1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % DDDD RRRR AAA W W % 7 % D D R R A A W W % 8 % D D RRRR AAAAA W W W % 9 % D D R RN A A WW WW % 10 % DDDD R R A A W W % 11 % % 12 % % 13 % MagickCore Image Drawing Methods % 14 % % 15 % % 16 % Software Design % 17 % Cristy % 18 % July 1998 % 19 % % 20 % % 21 % Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 22 % dedicated to making software imaging solutions freely available. % 23 % % 24 % You may not use this file except in compliance with the License. You may % 25 % obtain a copy of the License at % 26 % % 27 % http://www.imagemagick.org/script/license.php % 28 % % 29 % Unless required by applicable law or agreed to in writing, software % 30 % distributed under the License is distributed on an "AS IS" BASIS, % 31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 32 % See the License for the specific language governing permissions and % 33 % limitations under the License. % 34 % % 35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 36 % 37 % Bill Radcliffe of Corbis (www.corbis.com) contributed the polygon 38 % rendering code based on Paul Heckbert's "Concave Polygon Scan Conversion", 39 % Graphics Gems, 1990. Leonard Rosenthal and David Harr of Appligent 40 % (www.appligent.com) contributed the dash pattern, linecap stroking 41 % algorithm, and minor rendering improvements. 42 % 43 */ 44 45 /* 47 Include declarations. 48 */ 49 #include "MagickCore/studio.h" 50 #include "MagickCore/annotate.h" 51 #include "MagickCore/artifact.h" 52 #include "MagickCore/blob.h" 53 #include "MagickCore/cache.h" 54 #include "MagickCore/cache-private.h" 55 #include "MagickCore/cache-view.h" 56 #include "MagickCore/channel.h" 57 #include "MagickCore/color.h" 58 #include "MagickCore/colorspace-private.h" 59 #include "MagickCore/composite.h" 60 #include "MagickCore/composite-private.h" 61 #include "MagickCore/constitute.h" 62 #include "MagickCore/draw.h" 63 #include "MagickCore/draw-private.h" 64 #include "MagickCore/enhance.h" 65 #include "MagickCore/exception.h" 66 #include "MagickCore/exception-private.h" 67 #include "MagickCore/gem.h" 68 #include "MagickCore/geometry.h" 69 #include "MagickCore/image-private.h" 70 #include "MagickCore/list.h" 71 #include "MagickCore/log.h" 72 #include "MagickCore/monitor.h" 73 #include "MagickCore/monitor-private.h" 74 #include "MagickCore/option.h" 75 #include "MagickCore/paint.h" 76 #include "MagickCore/pixel-accessor.h" 77 #include "MagickCore/pixel-private.h" 78 #include "MagickCore/property.h" 79 #include "MagickCore/resample.h" 80 #include "MagickCore/resample-private.h" 81 #include "MagickCore/resource_.h" 82 #include "MagickCore/string_.h" 83 #include "MagickCore/string-private.h" 84 #include "MagickCore/thread-private.h" 85 #include "MagickCore/token.h" 86 #include "MagickCore/transform-private.h" 87 #include "MagickCore/utility.h" 88 89 /* 91 Define declarations. 92 */ 93 #define BezierQuantum 200 94 #define DrawEpsilon (1.0e-10) 95 96 97 /* 99 Typedef declarations. 100 */ 101 typedef struct _EdgeInfo 102 { 103 SegmentInfo 104 bounds; 105 106 double 107 scanline; 108 109 PointInfo 110 *points; 111 112 size_t 113 number_points; 114 115 ssize_t 116 direction; 117 118 MagickBooleanType 119 ghostline; 120 121 size_t 122 highwater; 123 } EdgeInfo; 124 125 typedef struct _ElementInfo 126 { 127 double 128 cx, 129 cy, 130 major, 131 minor, 132 angle; 133 } ElementInfo; 134 135 typedef struct _PolygonInfo 136 { 137 EdgeInfo 138 *edges; 139 140 size_t 141 number_edges; 142 } PolygonInfo; 143 144 typedef enum 145 { 146 MoveToCode, 147 OpenCode, 148 GhostlineCode, 149 LineToCode, 150 EndCode 151 } PathInfoCode; 152 153 typedef struct _PathInfo 154 { 155 PointInfo 156 point; 157 158 PathInfoCode 159 code; 160 } PathInfo; 161 162 /* 164 Forward declarations. 165 */ 166 static MagickBooleanType 167 DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *, 168 ExceptionInfo *); 169 170 static PrimitiveInfo 171 *TraceStrokePolygon(const DrawInfo *,const PrimitiveInfo *); 172 173 static size_t 174 TracePath(PrimitiveInfo *,const char *); 175 176 static void 177 TraceArc(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo), 178 TraceArcPath(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo, 179 const double,const MagickBooleanType,const MagickBooleanType), 180 TraceBezier(PrimitiveInfo *,const size_t), 181 TraceCircle(PrimitiveInfo *,const PointInfo,const PointInfo), 182 TraceEllipse(PrimitiveInfo *,const PointInfo,const PointInfo, 183 const PointInfo), 184 TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo), 185 TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo), 186 TraceRoundRectangle(PrimitiveInfo *,const PointInfo,const PointInfo, 187 PointInfo), 188 TraceSquareLinecap(PrimitiveInfo *,const size_t,const double); 189 190 /* 192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 193 % % 194 % % 195 % % 196 % A c q u i r e D r a w I n f o % 197 % % 198 % % 199 % % 200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 201 % 202 % AcquireDrawInfo() returns a DrawInfo structure properly initialized. 203 % 204 % The format of the AcquireDrawInfo method is: 205 % 206 % DrawInfo *AcquireDrawInfo(void) 207 % 208 */ 209 MagickExport DrawInfo *AcquireDrawInfo(void) 210 { 211 DrawInfo 212 *draw_info; 213 214 draw_info=(DrawInfo *) AcquireMagickMemory(sizeof(*draw_info)); 215 if (draw_info == (DrawInfo *) NULL) 216 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 217 GetDrawInfo((ImageInfo *) NULL,draw_info); 218 return(draw_info); 219 } 220 221 /* 223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 224 % % 225 % % 226 % % 227 % C l o n e D r a w I n f o % 228 % % 229 % % 230 % % 231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 232 % 233 % CloneDrawInfo() makes a copy of the given draw_info structure. If NULL 234 % is specified, a new DrawInfo structure is created initialized to default 235 % values. 236 % 237 % The format of the CloneDrawInfo method is: 238 % 239 % DrawInfo *CloneDrawInfo(const ImageInfo *image_info, 240 % const DrawInfo *draw_info) 241 % 242 % A description of each parameter follows: 243 % 244 % o image_info: the image info. 245 % 246 % o draw_info: the draw info. 247 % 248 */ 249 MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info, 250 const DrawInfo *draw_info) 251 { 252 DrawInfo 253 *clone_info; 254 255 ExceptionInfo 256 *exception; 257 258 clone_info=(DrawInfo *) AcquireMagickMemory(sizeof(*clone_info)); 259 if (clone_info == (DrawInfo *) NULL) 260 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 261 GetDrawInfo(image_info,clone_info); 262 if (draw_info == (DrawInfo *) NULL) 263 return(clone_info); 264 exception=AcquireExceptionInfo(); 265 if (clone_info->primitive != (char *) NULL) 266 (void) CloneString(&clone_info->primitive,draw_info->primitive); 267 if (draw_info->geometry != (char *) NULL) 268 (void) CloneString(&clone_info->geometry,draw_info->geometry); 269 clone_info->viewbox=draw_info->viewbox; 270 clone_info->affine=draw_info->affine; 271 clone_info->gravity=draw_info->gravity; 272 clone_info->fill=draw_info->fill; 273 clone_info->stroke=draw_info->stroke; 274 clone_info->stroke_width=draw_info->stroke_width; 275 if (draw_info->fill_pattern != (Image *) NULL) 276 clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue, 277 exception); 278 if (draw_info->stroke_pattern != (Image *) NULL) 279 clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0, 280 MagickTrue,exception); 281 clone_info->stroke_antialias=draw_info->stroke_antialias; 282 clone_info->text_antialias=draw_info->text_antialias; 283 clone_info->fill_rule=draw_info->fill_rule; 284 clone_info->linecap=draw_info->linecap; 285 clone_info->linejoin=draw_info->linejoin; 286 clone_info->miterlimit=draw_info->miterlimit; 287 clone_info->dash_offset=draw_info->dash_offset; 288 clone_info->decorate=draw_info->decorate; 289 clone_info->compose=draw_info->compose; 290 if (draw_info->text != (char *) NULL) 291 (void) CloneString(&clone_info->text,draw_info->text); 292 if (draw_info->font != (char *) NULL) 293 (void) CloneString(&clone_info->font,draw_info->font); 294 if (draw_info->metrics != (char *) NULL) 295 (void) CloneString(&clone_info->metrics,draw_info->metrics); 296 if (draw_info->family != (char *) NULL) 297 (void) CloneString(&clone_info->family,draw_info->family); 298 clone_info->style=draw_info->style; 299 clone_info->stretch=draw_info->stretch; 300 clone_info->weight=draw_info->weight; 301 if (draw_info->encoding != (char *) NULL) 302 (void) CloneString(&clone_info->encoding,draw_info->encoding); 303 clone_info->pointsize=draw_info->pointsize; 304 clone_info->kerning=draw_info->kerning; 305 clone_info->interline_spacing=draw_info->interline_spacing; 306 clone_info->interword_spacing=draw_info->interword_spacing; 307 clone_info->direction=draw_info->direction; 308 if (draw_info->density != (char *) NULL) 309 (void) CloneString(&clone_info->density,draw_info->density); 310 clone_info->align=draw_info->align; 311 clone_info->undercolor=draw_info->undercolor; 312 clone_info->border_color=draw_info->border_color; 313 if (draw_info->server_name != (char *) NULL) 314 (void) CloneString(&clone_info->server_name,draw_info->server_name); 315 if (draw_info->dash_pattern != (double *) NULL) 316 { 317 register ssize_t 318 x; 319 320 for (x=0; fabs(draw_info->dash_pattern[x]) >= DrawEpsilon; x++) ; 321 clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) x+1UL, 322 sizeof(*clone_info->dash_pattern)); 323 if (clone_info->dash_pattern == (double *) NULL) 324 ThrowFatalException(ResourceLimitFatalError, 325 "UnableToAllocateDashPattern"); 326 (void) CopyMagickMemory(clone_info->dash_pattern,draw_info->dash_pattern, 327 (size_t) (x+1)*sizeof(*clone_info->dash_pattern)); 328 } 329 clone_info->gradient=draw_info->gradient; 330 if (draw_info->gradient.stops != (StopInfo *) NULL) 331 { 332 size_t 333 number_stops; 334 335 number_stops=clone_info->gradient.number_stops; 336 clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t) 337 number_stops,sizeof(*clone_info->gradient.stops)); 338 if (clone_info->gradient.stops == (StopInfo *) NULL) 339 ThrowFatalException(ResourceLimitFatalError, 340 "UnableToAllocateDashPattern"); 341 (void) CopyMagickMemory(clone_info->gradient.stops, 342 draw_info->gradient.stops,(size_t) number_stops* 343 sizeof(*clone_info->gradient.stops)); 344 } 345 if (draw_info->clip_mask != (char *) NULL) 346 (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask); 347 clone_info->bounds=draw_info->bounds; 348 clone_info->clip_units=draw_info->clip_units; 349 clone_info->render=draw_info->render; 350 clone_info->fill_alpha=draw_info->fill_alpha; 351 clone_info->stroke_alpha=draw_info->stroke_alpha; 352 clone_info->element_reference=draw_info->element_reference; 353 clone_info->debug=IsEventLogging(); 354 exception=DestroyExceptionInfo(exception); 355 return(clone_info); 356 } 357 358 /* 360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 361 % % 362 % % 363 % % 364 + C o n v e r t P a t h T o P o l y g o n % 365 % % 366 % % 367 % % 368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 369 % 370 % ConvertPathToPolygon() converts a path to the more efficient sorted 371 % rendering form. 372 % 373 % The format of the ConvertPathToPolygon method is: 374 % 375 % PolygonInfo *ConvertPathToPolygon(const DrawInfo *draw_info, 376 % const PathInfo *path_info) 377 % 378 % A description of each parameter follows: 379 % 380 % o Method ConvertPathToPolygon returns the path in a more efficient sorted 381 % rendering form of type PolygonInfo. 382 % 383 % o draw_info: Specifies a pointer to an DrawInfo structure. 384 % 385 % o path_info: Specifies a pointer to an PathInfo structure. 386 % 387 % 388 */ 389 390 #if defined(__cplusplus) || defined(c_plusplus) 391 extern "C" { 392 #endif 393 394 static int CompareEdges(const void *x,const void *y) 395 { 396 register const EdgeInfo 397 *p, 398 *q; 399 400 /* 401 Compare two edges. 402 */ 403 p=(const EdgeInfo *) x; 404 q=(const EdgeInfo *) y; 405 if ((p->points[0].y-DrawEpsilon) > q->points[0].y) 406 return(1); 407 if ((p->points[0].y+DrawEpsilon) < q->points[0].y) 408 return(-1); 409 if ((p->points[0].x-DrawEpsilon) > q->points[0].x) 410 return(1); 411 if ((p->points[0].x+DrawEpsilon) < q->points[0].x) 412 return(-1); 413 if (((p->points[1].x-p->points[0].x)*(q->points[1].y-q->points[0].y)- 414 (p->points[1].y-p->points[0].y)*(q->points[1].x-q->points[0].x)) > 0.0) 415 return(1); 416 return(-1); 417 } 418 419 #if defined(__cplusplus) || defined(c_plusplus) 420 } 421 #endif 422 423 static void LogPolygonInfo(const PolygonInfo *polygon_info) 424 { 425 register EdgeInfo 426 *p; 427 428 register ssize_t 429 i, 430 j; 431 432 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge"); 433 p=polygon_info->edges; 434 for (i=0; i < (ssize_t) polygon_info->number_edges; i++) 435 { 436 (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %.20g:", 437 (double) i); 438 (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s", 439 p->direction != MagickFalse ? "down" : "up"); 440 (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s", 441 p->ghostline != MagickFalse ? "transparent" : "opaque"); 442 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 443 " bounds: %g,%g - %g,%g",p->bounds.x1,p->bounds.y1, 444 p->bounds.x2,p->bounds.y2); 445 for (j=0; j < (ssize_t) p->number_points; j++) 446 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g,%g", 447 p->points[j].x,p->points[j].y); 448 p++; 449 } 450 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge"); 451 } 452 453 static void ReversePoints(PointInfo *points,const size_t number_points) 454 { 455 PointInfo 456 point; 457 458 register ssize_t 459 i; 460 461 for (i=0; i < (ssize_t) (number_points >> 1); i++) 462 { 463 point=points[i]; 464 points[i]=points[number_points-(i+1)]; 465 points[number_points-(i+1)]=point; 466 } 467 } 468 469 static PolygonInfo *ConvertPathToPolygon(const PathInfo *path_info) 470 { 471 long 472 direction, 473 next_direction; 474 475 PointInfo 476 point, 477 *points; 478 479 PolygonInfo 480 *polygon_info; 481 482 SegmentInfo 483 bounds; 484 485 register ssize_t 486 i, 487 n; 488 489 MagickBooleanType 490 ghostline; 491 492 size_t 493 edge, 494 number_edges, 495 number_points; 496 497 /* 498 Convert a path to the more efficient sorted rendering form. 499 */ 500 polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info)); 501 if (polygon_info == (PolygonInfo *) NULL) 502 return((PolygonInfo *) NULL); 503 number_edges=16; 504 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory(number_edges, 505 sizeof(*polygon_info->edges)); 506 if (polygon_info->edges == (EdgeInfo *) NULL) 507 return((PolygonInfo *) NULL); 508 (void) ResetMagickMemory(polygon_info->edges,0,number_edges* 509 sizeof(*polygon_info->edges)); 510 direction=0; 511 edge=0; 512 ghostline=MagickFalse; 513 n=0; 514 number_points=0; 515 points=(PointInfo *) NULL; 516 (void) ResetMagickMemory(&point,0,sizeof(point)); 517 (void) ResetMagickMemory(&bounds,0,sizeof(bounds)); 518 for (i=0; path_info[i].code != EndCode; i++) 519 { 520 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) || 521 (path_info[i].code == GhostlineCode)) 522 { 523 /* 524 Move to. 525 */ 526 if ((points != (PointInfo *) NULL) && (n >= 2)) 527 { 528 if (edge == number_edges) 529 { 530 number_edges<<=1; 531 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory( 532 polygon_info->edges,(size_t) number_edges, 533 sizeof(*polygon_info->edges)); 534 if (polygon_info->edges == (EdgeInfo *) NULL) 535 return((PolygonInfo *) NULL); 536 } 537 polygon_info->edges[edge].number_points=(size_t) n; 538 polygon_info->edges[edge].scanline=(-1.0); 539 polygon_info->edges[edge].highwater=0; 540 polygon_info->edges[edge].ghostline=ghostline; 541 polygon_info->edges[edge].direction=(ssize_t) (direction > 0); 542 if (direction < 0) 543 ReversePoints(points,(size_t) n); 544 polygon_info->edges[edge].points=points; 545 polygon_info->edges[edge].bounds=bounds; 546 polygon_info->edges[edge].bounds.y1=points[0].y; 547 polygon_info->edges[edge].bounds.y2=points[n-1].y; 548 points=(PointInfo *) NULL; 549 ghostline=MagickFalse; 550 edge++; 551 } 552 if (points == (PointInfo *) NULL) 553 { 554 number_points=16; 555 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points, 556 sizeof(*points)); 557 if (points == (PointInfo *) NULL) 558 return((PolygonInfo *) NULL); 559 } 560 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse; 561 point=path_info[i].point; 562 points[0]=point; 563 bounds.x1=point.x; 564 bounds.x2=point.x; 565 direction=0; 566 n=1; 567 continue; 568 } 569 /* 570 Line to. 571 */ 572 next_direction=((path_info[i].point.y > point.y) || 573 ((fabs(path_info[i].point.y-point.y) < DrawEpsilon) && 574 (path_info[i].point.x > point.x))) ? 1 : -1; 575 if ((points != (PointInfo *) NULL) && (direction != 0) && 576 (direction != next_direction)) 577 { 578 /* 579 New edge. 580 */ 581 point=points[n-1]; 582 if (edge == number_edges) 583 { 584 number_edges<<=1; 585 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory( 586 polygon_info->edges,(size_t) number_edges, 587 sizeof(*polygon_info->edges)); 588 if (polygon_info->edges == (EdgeInfo *) NULL) 589 return((PolygonInfo *) NULL); 590 } 591 polygon_info->edges[edge].number_points=(size_t) n; 592 polygon_info->edges[edge].scanline=(-1.0); 593 polygon_info->edges[edge].highwater=0; 594 polygon_info->edges[edge].ghostline=ghostline; 595 polygon_info->edges[edge].direction=(ssize_t) (direction > 0); 596 if (direction < 0) 597 ReversePoints(points,(size_t) n); 598 polygon_info->edges[edge].points=points; 599 polygon_info->edges[edge].bounds=bounds; 600 polygon_info->edges[edge].bounds.y1=points[0].y; 601 polygon_info->edges[edge].bounds.y2=points[n-1].y; 602 number_points=16; 603 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points, 604 sizeof(*points)); 605 if (points == (PointInfo *) NULL) 606 return((PolygonInfo *) NULL); 607 n=1; 608 ghostline=MagickFalse; 609 points[0]=point; 610 bounds.x1=point.x; 611 bounds.x2=point.x; 612 edge++; 613 } 614 direction=next_direction; 615 if (points == (PointInfo *) NULL) 616 continue; 617 if (n == (ssize_t) number_points) 618 { 619 number_points<<=1; 620 points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points, 621 sizeof(*points)); 622 if (points == (PointInfo *) NULL) 623 return((PolygonInfo *) NULL); 624 } 625 point=path_info[i].point; 626 points[n]=point; 627 if (point.x < bounds.x1) 628 bounds.x1=point.x; 629 if (point.x > bounds.x2) 630 bounds.x2=point.x; 631 n++; 632 } 633 if (points != (PointInfo *) NULL) 634 { 635 if (n < 2) 636 points=(PointInfo *) RelinquishMagickMemory(points); 637 else 638 { 639 if (edge == number_edges) 640 { 641 number_edges<<=1; 642 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory( 643 polygon_info->edges,(size_t) number_edges, 644 sizeof(*polygon_info->edges)); 645 if (polygon_info->edges == (EdgeInfo *) NULL) 646 return((PolygonInfo *) NULL); 647 } 648 polygon_info->edges[edge].number_points=(size_t) n; 649 polygon_info->edges[edge].scanline=(-1.0); 650 polygon_info->edges[edge].highwater=0; 651 polygon_info->edges[edge].ghostline=ghostline; 652 polygon_info->edges[edge].direction=(ssize_t) (direction > 0); 653 if (direction < 0) 654 ReversePoints(points,(size_t) n); 655 polygon_info->edges[edge].points=points; 656 polygon_info->edges[edge].bounds=bounds; 657 polygon_info->edges[edge].bounds.y1=points[0].y; 658 polygon_info->edges[edge].bounds.y2=points[n-1].y; 659 ghostline=MagickFalse; 660 edge++; 661 } 662 } 663 polygon_info->number_edges=edge; 664 qsort(polygon_info->edges,(size_t) polygon_info->number_edges, 665 sizeof(*polygon_info->edges),CompareEdges); 666 if (IsEventLogging() != MagickFalse) 667 LogPolygonInfo(polygon_info); 668 return(polygon_info); 669 } 670 671 /* 673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 674 % % 675 % % 676 % % 677 + C o n v e r t P r i m i t i v e T o P a t h % 678 % % 679 % % 680 % % 681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 682 % 683 % ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector 684 % path structure. 685 % 686 % The format of the ConvertPrimitiveToPath method is: 687 % 688 % PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info, 689 % const PrimitiveInfo *primitive_info) 690 % 691 % A description of each parameter follows: 692 % 693 % o Method ConvertPrimitiveToPath returns a vector path structure of type 694 % PathInfo. 695 % 696 % o draw_info: a structure of type DrawInfo. 697 % 698 % o primitive_info: Specifies a pointer to an PrimitiveInfo structure. 699 % 700 % 701 */ 702 703 static void LogPathInfo(const PathInfo *path_info) 704 { 705 register const PathInfo 706 *p; 707 708 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path"); 709 for (p=path_info; p->code != EndCode; p++) 710 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 711 " %g,%g %s",p->point.x,p->point.y,p->code == GhostlineCode ? 712 "moveto ghostline" : p->code == OpenCode ? "moveto open" : 713 p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" : 714 "?"); 715 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path"); 716 } 717 718 static PathInfo *ConvertPrimitiveToPath(const PrimitiveInfo *primitive_info) 719 { 720 PathInfo 721 *path_info; 722 723 PathInfoCode 724 code; 725 726 PointInfo 727 p, 728 q; 729 730 register ssize_t 731 i, 732 n; 733 734 ssize_t 735 coordinates, 736 start; 737 738 /* 739 Converts a PrimitiveInfo structure into a vector path structure. 740 */ 741 switch (primitive_info->primitive) 742 { 743 case AlphaPrimitive: 744 case ColorPrimitive: 745 case ImagePrimitive: 746 case PointPrimitive: 747 case TextPrimitive: 748 return((PathInfo *) NULL); 749 default: 750 break; 751 } 752 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ; 753 path_info=(PathInfo *) AcquireQuantumMemory((size_t) (2UL*i+3UL), 754 sizeof(*path_info)); 755 if (path_info == (PathInfo *) NULL) 756 return((PathInfo *) NULL); 757 coordinates=0; 758 n=0; 759 p.x=(-1.0); 760 p.y=(-1.0); 761 q.x=(-1.0); 762 q.y=(-1.0); 763 start=0; 764 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) 765 { 766 code=LineToCode; 767 if (coordinates <= 0) 768 { 769 coordinates=(ssize_t) primitive_info[i].coordinates; 770 p=primitive_info[i].point; 771 start=n; 772 code=MoveToCode; 773 } 774 coordinates--; 775 /* 776 Eliminate duplicate points. 777 */ 778 if ((i == 0) || (fabs(q.x-primitive_info[i].point.x) >= DrawEpsilon) || 779 (fabs(q.y-primitive_info[i].point.y) >= DrawEpsilon)) 780 { 781 path_info[n].code=code; 782 path_info[n].point=primitive_info[i].point; 783 q=primitive_info[i].point; 784 n++; 785 } 786 if (coordinates > 0) 787 continue; 788 if ((fabs(p.x-primitive_info[i].point.x) < DrawEpsilon) && 789 (fabs(p.y-primitive_info[i].point.y) < DrawEpsilon)) 790 continue; 791 /* 792 Mark the p point as open if it does not match the q. 793 */ 794 path_info[start].code=OpenCode; 795 path_info[n].code=GhostlineCode; 796 path_info[n].point=primitive_info[i].point; 797 n++; 798 path_info[n].code=LineToCode; 799 path_info[n].point=p; 800 n++; 801 } 802 path_info[n].code=EndCode; 803 path_info[n].point.x=0.0; 804 path_info[n].point.y=0.0; 805 if (IsEventLogging() != MagickFalse) 806 LogPathInfo(path_info); 807 return(path_info); 808 } 809 810 /* 812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 813 % % 814 % % 815 % % 816 % D e s t r o y D r a w I n f o % 817 % % 818 % % 819 % % 820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 821 % 822 % DestroyDrawInfo() deallocates memory associated with an DrawInfo 823 % structure. 824 % 825 % The format of the DestroyDrawInfo method is: 826 % 827 % DrawInfo *DestroyDrawInfo(DrawInfo *draw_info) 828 % 829 % A description of each parameter follows: 830 % 831 % o draw_info: the draw info. 832 % 833 */ 834 MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info) 835 { 836 if (draw_info->debug != MagickFalse) 837 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 838 assert(draw_info != (DrawInfo *) NULL); 839 assert(draw_info->signature == MagickCoreSignature); 840 if (draw_info->primitive != (char *) NULL) 841 draw_info->primitive=DestroyString(draw_info->primitive); 842 if (draw_info->text != (char *) NULL) 843 draw_info->text=DestroyString(draw_info->text); 844 if (draw_info->geometry != (char *) NULL) 845 draw_info->geometry=DestroyString(draw_info->geometry); 846 if (draw_info->fill_pattern != (Image *) NULL) 847 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern); 848 if (draw_info->stroke_pattern != (Image *) NULL) 849 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern); 850 if (draw_info->font != (char *) NULL) 851 draw_info->font=DestroyString(draw_info->font); 852 if (draw_info->metrics != (char *) NULL) 853 draw_info->metrics=DestroyString(draw_info->metrics); 854 if (draw_info->family != (char *) NULL) 855 draw_info->family=DestroyString(draw_info->family); 856 if (draw_info->encoding != (char *) NULL) 857 draw_info->encoding=DestroyString(draw_info->encoding); 858 if (draw_info->density != (char *) NULL) 859 draw_info->density=DestroyString(draw_info->density); 860 if (draw_info->server_name != (char *) NULL) 861 draw_info->server_name=(char *) 862 RelinquishMagickMemory(draw_info->server_name); 863 if (draw_info->dash_pattern != (double *) NULL) 864 draw_info->dash_pattern=(double *) RelinquishMagickMemory( 865 draw_info->dash_pattern); 866 if (draw_info->gradient.stops != (StopInfo *) NULL) 867 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory( 868 draw_info->gradient.stops); 869 if (draw_info->clip_mask != (char *) NULL) 870 draw_info->clip_mask=DestroyString(draw_info->clip_mask); 871 draw_info->signature=(~MagickCoreSignature); 872 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info); 873 return(draw_info); 874 } 875 876 /* 878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 879 % % 880 % % 881 % % 882 + D e s t r o y E d g e % 883 % % 884 % % 885 % % 886 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 887 % 888 % DestroyEdge() destroys the specified polygon edge. 889 % 890 % The format of the DestroyEdge method is: 891 % 892 % ssize_t DestroyEdge(PolygonInfo *polygon_info,const int edge) 893 % 894 % A description of each parameter follows: 895 % 896 % o polygon_info: Specifies a pointer to an PolygonInfo structure. 897 % 898 % o edge: the polygon edge number to destroy. 899 % 900 */ 901 static size_t DestroyEdge(PolygonInfo *polygon_info, 902 const size_t edge) 903 { 904 assert(edge < polygon_info->number_edges); 905 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory( 906 polygon_info->edges[edge].points); 907 polygon_info->number_edges--; 908 if (edge < polygon_info->number_edges) 909 (void) CopyMagickMemory(polygon_info->edges+edge,polygon_info->edges+edge+1, 910 (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges)); 911 return(polygon_info->number_edges); 912 } 913 914 /* 916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 917 % % 918 % % 919 % % 920 + D e s t r o y P o l y g o n I n f o % 921 % % 922 % % 923 % % 924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 925 % 926 % DestroyPolygonInfo() destroys the PolygonInfo data structure. 927 % 928 % The format of the DestroyPolygonInfo method is: 929 % 930 % PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info) 931 % 932 % A description of each parameter follows: 933 % 934 % o polygon_info: Specifies a pointer to an PolygonInfo structure. 935 % 936 */ 937 static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info) 938 { 939 register ssize_t 940 i; 941 942 for (i=0; i < (ssize_t) polygon_info->number_edges; i++) 943 polygon_info->edges[i].points=(PointInfo *) 944 RelinquishMagickMemory(polygon_info->edges[i].points); 945 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges); 946 return((PolygonInfo *) RelinquishMagickMemory(polygon_info)); 947 } 948 949 /* 951 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 952 % % 953 % % 954 % % 955 % D r a w A f f i n e I m a g e % 956 % % 957 % % 958 % % 959 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 960 % 961 % DrawAffineImage() composites the source over the destination image as 962 % dictated by the affine transform. 963 % 964 % The format of the DrawAffineImage method is: 965 % 966 % MagickBooleanType DrawAffineImage(Image *image,const Image *source, 967 % const AffineMatrix *affine,ExceptionInfo *exception) 968 % 969 % A description of each parameter follows: 970 % 971 % o image: the image. 972 % 973 % o source: the source image. 974 % 975 % o affine: the affine transform. 976 % 977 % o exception: return any errors or warnings in this structure. 978 % 979 */ 980 981 static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine, 982 const double y,const SegmentInfo *edge) 983 { 984 double 985 intercept, 986 z; 987 988 register double 989 x; 990 991 SegmentInfo 992 inverse_edge; 993 994 /* 995 Determine left and right edges. 996 */ 997 inverse_edge.x1=edge->x1; 998 inverse_edge.y1=edge->y1; 999 inverse_edge.x2=edge->x2; 1000 inverse_edge.y2=edge->y2; 1001 z=affine->ry*y+affine->tx; 1002 if (affine->sx >= DrawEpsilon) 1003 { 1004 intercept=(-z/affine->sx); 1005 x=intercept; 1006 if (x > inverse_edge.x1) 1007 inverse_edge.x1=x; 1008 intercept=(-z+(double) image->columns)/affine->sx; 1009 x=intercept; 1010 if (x < inverse_edge.x2) 1011 inverse_edge.x2=x; 1012 } 1013 else 1014 if (affine->sx < -DrawEpsilon) 1015 { 1016 intercept=(-z+(double) image->columns)/affine->sx; 1017 x=intercept; 1018 if (x > inverse_edge.x1) 1019 inverse_edge.x1=x; 1020 intercept=(-z/affine->sx); 1021 x=intercept; 1022 if (x < inverse_edge.x2) 1023 inverse_edge.x2=x; 1024 } 1025 else 1026 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->columns)) 1027 { 1028 inverse_edge.x2=edge->x1; 1029 return(inverse_edge); 1030 } 1031 /* 1032 Determine top and bottom edges. 1033 */ 1034 z=affine->sy*y+affine->ty; 1035 if (affine->rx >= DrawEpsilon) 1036 { 1037 intercept=(-z/affine->rx); 1038 x=intercept; 1039 if (x > inverse_edge.x1) 1040 inverse_edge.x1=x; 1041 intercept=(-z+(double) image->rows)/affine->rx; 1042 x=intercept; 1043 if (x < inverse_edge.x2) 1044 inverse_edge.x2=x; 1045 } 1046 else 1047 if (affine->rx < -DrawEpsilon) 1048 { 1049 intercept=(-z+(double) image->rows)/affine->rx; 1050 x=intercept; 1051 if (x > inverse_edge.x1) 1052 inverse_edge.x1=x; 1053 intercept=(-z/affine->rx); 1054 x=intercept; 1055 if (x < inverse_edge.x2) 1056 inverse_edge.x2=x; 1057 } 1058 else 1059 if ((z < 0.0) || ((size_t) floor(z+0.5) >= image->rows)) 1060 { 1061 inverse_edge.x2=edge->x2; 1062 return(inverse_edge); 1063 } 1064 return(inverse_edge); 1065 } 1066 1067 static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine) 1068 { 1069 AffineMatrix 1070 inverse_affine; 1071 1072 double 1073 determinant; 1074 1075 determinant=PerceptibleReciprocal(affine->sx*affine->sy-affine->rx* 1076 affine->ry); 1077 inverse_affine.sx=determinant*affine->sy; 1078 inverse_affine.rx=determinant*(-affine->rx); 1079 inverse_affine.ry=determinant*(-affine->ry); 1080 inverse_affine.sy=determinant*affine->sx; 1081 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty* 1082 inverse_affine.ry; 1083 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty* 1084 inverse_affine.sy; 1085 return(inverse_affine); 1086 } 1087 1088 MagickExport MagickBooleanType DrawAffineImage(Image *image, 1089 const Image *source,const AffineMatrix *affine,ExceptionInfo *exception) 1090 { 1091 AffineMatrix 1092 inverse_affine; 1093 1094 CacheView 1095 *image_view, 1096 *source_view; 1097 1098 MagickBooleanType 1099 status; 1100 1101 PixelInfo 1102 zero; 1103 1104 PointInfo 1105 extent[4], 1106 min, 1107 max; 1108 1109 register ssize_t 1110 i; 1111 1112 SegmentInfo 1113 edge; 1114 1115 ssize_t 1116 start, 1117 stop, 1118 y; 1119 1120 /* 1121 Determine bounding box. 1122 */ 1123 assert(image != (Image *) NULL); 1124 assert(image->signature == MagickCoreSignature); 1125 if (image->debug != MagickFalse) 1126 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1127 assert(source != (const Image *) NULL); 1128 assert(source->signature == MagickCoreSignature); 1129 assert(affine != (AffineMatrix *) NULL); 1130 extent[0].x=0.0; 1131 extent[0].y=0.0; 1132 extent[1].x=(double) source->columns-1.0; 1133 extent[1].y=0.0; 1134 extent[2].x=(double) source->columns-1.0; 1135 extent[2].y=(double) source->rows-1.0; 1136 extent[3].x=0.0; 1137 extent[3].y=(double) source->rows-1.0; 1138 for (i=0; i < 4; i++) 1139 { 1140 PointInfo 1141 point; 1142 1143 point=extent[i]; 1144 extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx; 1145 extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty; 1146 } 1147 min=extent[0]; 1148 max=extent[0]; 1149 for (i=1; i < 4; i++) 1150 { 1151 if (min.x > extent[i].x) 1152 min.x=extent[i].x; 1153 if (min.y > extent[i].y) 1154 min.y=extent[i].y; 1155 if (max.x < extent[i].x) 1156 max.x=extent[i].x; 1157 if (max.y < extent[i].y) 1158 max.y=extent[i].y; 1159 } 1160 /* 1161 Affine transform image. 1162 */ 1163 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 1164 return(MagickFalse); 1165 status=MagickTrue; 1166 edge.x1=MagickMax(min.x,0.0); 1167 edge.y1=MagickMax(min.y,0.0); 1168 edge.x2=MagickMin(max.x,(double) image->columns-1.0); 1169 edge.y2=MagickMin(max.y,(double) image->rows-1.0); 1170 inverse_affine=InverseAffineMatrix(affine); 1171 GetPixelInfo(image,&zero); 1172 start=(ssize_t) ceil(edge.y1-0.5); 1173 stop=(ssize_t) floor(edge.y2+0.5); 1174 source_view=AcquireVirtualCacheView(source,exception); 1175 image_view=AcquireAuthenticCacheView(image,exception); 1176 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1177 #pragma omp parallel for schedule(static,4) shared(status) \ 1178 magick_threads(source,image,1,1) 1179 #endif 1180 for (y=start; y <= stop; y++) 1181 { 1182 PixelInfo 1183 composite, 1184 pixel; 1185 1186 PointInfo 1187 point; 1188 1189 register ssize_t 1190 x; 1191 1192 register Quantum 1193 *magick_restrict q; 1194 1195 SegmentInfo 1196 inverse_edge; 1197 1198 ssize_t 1199 x_offset; 1200 1201 inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge); 1202 if (inverse_edge.x2 < inverse_edge.x1) 1203 continue; 1204 q=GetCacheViewAuthenticPixels(image_view,(ssize_t) ceil(inverse_edge.x1- 1205 0.5),y,(size_t) (floor(inverse_edge.x2+0.5)-ceil(inverse_edge.x1-0.5)+1), 1206 1,exception); 1207 if (q == (Quantum *) NULL) 1208 continue; 1209 pixel=zero; 1210 composite=zero; 1211 x_offset=0; 1212 for (x=(ssize_t) ceil(inverse_edge.x1-0.5); x <= (ssize_t) floor(inverse_edge.x2+0.5); x++) 1213 { 1214 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+ 1215 inverse_affine.tx; 1216 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+ 1217 inverse_affine.ty; 1218 (void) InterpolatePixelInfo(source,source_view,UndefinedInterpolatePixel, 1219 point.x,point.y,&pixel,exception); 1220 GetPixelInfoPixel(image,q,&composite); 1221 CompositePixelInfoOver(&pixel,pixel.alpha,&composite,composite.alpha, 1222 &composite); 1223 SetPixelViaPixelInfo(image,&composite,q); 1224 x_offset++; 1225 q+=GetPixelChannels(image); 1226 } 1227 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 1228 status=MagickFalse; 1229 } 1230 source_view=DestroyCacheView(source_view); 1231 image_view=DestroyCacheView(image_view); 1232 return(status); 1233 } 1234 1235 /* 1237 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1238 % % 1239 % % 1240 % % 1241 + D r a w B o u n d i n g R e c t a n g l e s % 1242 % % 1243 % % 1244 % % 1245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1246 % 1247 % DrawBoundingRectangles() draws the bounding rectangles on the image. This 1248 % is only useful for developers debugging the rendering algorithm. 1249 % 1250 % The format of the DrawBoundingRectangles method is: 1251 % 1252 % void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info, 1253 % PolygonInfo *polygon_info,ExceptionInfo *exception) 1254 % 1255 % A description of each parameter follows: 1256 % 1257 % o image: the image. 1258 % 1259 % o draw_info: the draw info. 1260 % 1261 % o polygon_info: Specifies a pointer to a PolygonInfo structure. 1262 % 1263 % o exception: return any errors or warnings in this structure. 1264 % 1265 */ 1266 static void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info, 1267 const PolygonInfo *polygon_info,ExceptionInfo *exception) 1268 { 1269 DrawInfo 1270 *clone_info; 1271 1272 double 1273 mid; 1274 1275 PointInfo 1276 end, 1277 resolution, 1278 start; 1279 1280 PrimitiveInfo 1281 primitive_info[6]; 1282 1283 register ssize_t 1284 i; 1285 1286 SegmentInfo 1287 bounds; 1288 1289 ssize_t 1290 coordinates; 1291 1292 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 1293 (void) QueryColorCompliance("#0000",AllCompliance,&clone_info->fill, 1294 exception); 1295 resolution.x=DefaultResolution; 1296 resolution.y=DefaultResolution; 1297 if (clone_info->density != (char *) NULL) 1298 { 1299 GeometryInfo 1300 geometry_info; 1301 1302 MagickStatusType 1303 flags; 1304 1305 flags=ParseGeometry(clone_info->density,&geometry_info); 1306 resolution.x=geometry_info.rho; 1307 resolution.y=geometry_info.sigma; 1308 if ((flags & SigmaValue) == MagickFalse) 1309 resolution.y=resolution.x; 1310 } 1311 mid=(resolution.x/72.0)*ExpandAffine(&clone_info->affine)* 1312 clone_info->stroke_width/2.0; 1313 bounds.x1=0.0; 1314 bounds.y1=0.0; 1315 bounds.x2=0.0; 1316 bounds.y2=0.0; 1317 if (polygon_info != (PolygonInfo *) NULL) 1318 { 1319 bounds=polygon_info->edges[0].bounds; 1320 for (i=1; i < (ssize_t) polygon_info->number_edges; i++) 1321 { 1322 if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1) 1323 bounds.x1=polygon_info->edges[i].bounds.x1; 1324 if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1) 1325 bounds.y1=polygon_info->edges[i].bounds.y1; 1326 if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2) 1327 bounds.x2=polygon_info->edges[i].bounds.x2; 1328 if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2) 1329 bounds.y2=polygon_info->edges[i].bounds.y2; 1330 } 1331 bounds.x1-=mid; 1332 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double) 1333 image->columns ? (double) image->columns-1 : bounds.x1; 1334 bounds.y1-=mid; 1335 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double) 1336 image->rows ? (double) image->rows-1 : bounds.y1; 1337 bounds.x2+=mid; 1338 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double) 1339 image->columns ? (double) image->columns-1 : bounds.x2; 1340 bounds.y2+=mid; 1341 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double) 1342 image->rows ? (double) image->rows-1 : bounds.y2; 1343 for (i=0; i < (ssize_t) polygon_info->number_edges; i++) 1344 { 1345 if (polygon_info->edges[i].direction != 0) 1346 (void) QueryColorCompliance("red",AllCompliance,&clone_info->stroke, 1347 exception); 1348 else 1349 (void) QueryColorCompliance("green",AllCompliance,&clone_info->stroke, 1350 exception); 1351 start.x=(double) (polygon_info->edges[i].bounds.x1-mid); 1352 start.y=(double) (polygon_info->edges[i].bounds.y1-mid); 1353 end.x=(double) (polygon_info->edges[i].bounds.x2+mid); 1354 end.y=(double) (polygon_info->edges[i].bounds.y2+mid); 1355 primitive_info[0].primitive=RectanglePrimitive; 1356 TraceRectangle(primitive_info,start,end); 1357 primitive_info[0].method=ReplaceMethod; 1358 coordinates=(ssize_t) primitive_info[0].coordinates; 1359 primitive_info[coordinates].primitive=UndefinedPrimitive; 1360 (void) DrawPrimitive(image,clone_info,primitive_info,exception); 1361 } 1362 } 1363 (void) QueryColorCompliance("blue",AllCompliance,&clone_info->stroke, 1364 exception); 1365 start.x=(double) (bounds.x1-mid); 1366 start.y=(double) (bounds.y1-mid); 1367 end.x=(double) (bounds.x2+mid); 1368 end.y=(double) (bounds.y2+mid); 1369 primitive_info[0].primitive=RectanglePrimitive; 1370 TraceRectangle(primitive_info,start,end); 1371 primitive_info[0].method=ReplaceMethod; 1372 coordinates=(ssize_t) primitive_info[0].coordinates; 1373 primitive_info[coordinates].primitive=UndefinedPrimitive; 1374 (void) DrawPrimitive(image,clone_info,primitive_info,exception); 1375 clone_info=DestroyDrawInfo(clone_info); 1376 } 1377 1378 /* 1380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1381 % % 1382 % % 1383 % % 1384 % D r a w C l i p P a t h % 1385 % % 1386 % % 1387 % % 1388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1389 % 1390 % DrawClipPath() draws the clip path on the image mask. 1391 % 1392 % The format of the DrawClipPath method is: 1393 % 1394 % MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info, 1395 % const char *name,ExceptionInfo *exception) 1396 % 1397 % A description of each parameter follows: 1398 % 1399 % o image: the image. 1400 % 1401 % o draw_info: the draw info. 1402 % 1403 % o name: the name of the clip path. 1404 % 1405 % o exception: return any errors or warnings in this structure. 1406 % 1407 */ 1408 MagickExport MagickBooleanType DrawClipPath(Image *image, 1409 const DrawInfo *draw_info,const char *name,ExceptionInfo *exception) 1410 { 1411 char 1412 filename[MagickPathExtent]; 1413 1414 Image 1415 *clip_mask; 1416 1417 const char 1418 *value; 1419 1420 DrawInfo 1421 *clone_info; 1422 1423 MagickStatusType 1424 status; 1425 1426 assert(image != (Image *) NULL); 1427 assert(image->signature == MagickCoreSignature); 1428 if (image->debug != MagickFalse) 1429 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1430 assert(draw_info != (const DrawInfo *) NULL); 1431 (void) FormatLocaleString(filename,MagickPathExtent,"%s",name); 1432 value=GetImageArtifact(image,filename); 1433 if (value == (const char *) NULL) 1434 return(MagickFalse); 1435 clip_mask=CloneImage(image,image->columns,image->rows,MagickTrue,exception); 1436 if (clip_mask == (Image *) NULL) 1437 return(MagickFalse); 1438 (void) QueryColorCompliance("#0000",AllCompliance, 1439 &clip_mask->background_color,exception); 1440 clip_mask->background_color.alpha=(MagickRealType) TransparentAlpha; 1441 (void) SetImageBackgroundColor(clip_mask,exception); 1442 if (image->debug != MagickFalse) 1443 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s", 1444 draw_info->clip_mask); 1445 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 1446 (void) CloneString(&clone_info->primitive,value); 1447 (void) QueryColorCompliance("#ffffff",AllCompliance,&clone_info->fill, 1448 exception); 1449 clone_info->clip_mask=(char *) NULL; 1450 status=NegateImage(clip_mask,MagickFalse,exception); 1451 (void) SetImageMask(image,ReadPixelMask,clip_mask,exception); 1452 clip_mask=DestroyImage(clip_mask); 1453 status&=DrawImage(image,clone_info,exception); 1454 clone_info=DestroyDrawInfo(clone_info); 1455 if (image->debug != MagickFalse) 1456 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path"); 1457 return(status != 0 ? MagickTrue : MagickFalse); 1458 } 1459 1460 /* 1462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1463 % % 1464 % % 1465 % % 1466 + D r a w D a s h P o l y g o n % 1467 % % 1468 % % 1469 % % 1470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1471 % 1472 % DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the 1473 % image while respecting the dash offset and dash pattern attributes. 1474 % 1475 % The format of the DrawDashPolygon method is: 1476 % 1477 % MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info, 1478 % const PrimitiveInfo *primitive_info,Image *image, 1479 % ExceptionInfo *exception) 1480 % 1481 % A description of each parameter follows: 1482 % 1483 % o draw_info: the draw info. 1484 % 1485 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure. 1486 % 1487 % o image: the image. 1488 % 1489 % o exception: return any errors or warnings in this structure. 1490 % 1491 */ 1492 static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info, 1493 const PrimitiveInfo *primitive_info,Image *image,ExceptionInfo *exception) 1494 { 1495 DrawInfo 1496 *clone_info; 1497 1498 double 1499 length, 1500 maximum_length, 1501 offset, 1502 scale, 1503 total_length; 1504 1505 MagickStatusType 1506 status; 1507 1508 PrimitiveInfo 1509 *dash_polygon; 1510 1511 register ssize_t 1512 i; 1513 1514 register double 1515 dx, 1516 dy; 1517 1518 size_t 1519 number_vertices; 1520 1521 ssize_t 1522 j, 1523 n; 1524 1525 assert(draw_info != (const DrawInfo *) NULL); 1526 if (image->debug != MagickFalse) 1527 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash"); 1528 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ; 1529 number_vertices=(size_t) i; 1530 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t) 1531 (2UL*number_vertices+1UL),sizeof(*dash_polygon)); 1532 if (dash_polygon == (PrimitiveInfo *) NULL) 1533 return(MagickFalse); 1534 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 1535 clone_info->miterlimit=0; 1536 dash_polygon[0]=primitive_info[0]; 1537 scale=ExpandAffine(&draw_info->affine); 1538 length=scale*(draw_info->dash_pattern[0]-0.5); 1539 offset=fabs(draw_info->dash_offset) >= DrawEpsilon ? 1540 scale*draw_info->dash_offset : 0.0; 1541 j=1; 1542 for (n=0; offset > 0.0; j=0) 1543 { 1544 if (draw_info->dash_pattern[n] <= 0.0) 1545 break; 1546 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5)); 1547 if (offset > length) 1548 { 1549 offset-=length; 1550 n++; 1551 length=scale*(draw_info->dash_pattern[n]+0.5); 1552 continue; 1553 } 1554 if (offset < length) 1555 { 1556 length-=offset; 1557 offset=0.0; 1558 break; 1559 } 1560 offset=0.0; 1561 n++; 1562 } 1563 status=MagickTrue; 1564 maximum_length=0.0; 1565 total_length=0.0; 1566 for (i=1; (i < (ssize_t) number_vertices) && (length >= 0.0); i++) 1567 { 1568 dx=primitive_info[i].point.x-primitive_info[i-1].point.x; 1569 dy=primitive_info[i].point.y-primitive_info[i-1].point.y; 1570 maximum_length=hypot((double) dx,dy); 1571 if (fabs(length) < DrawEpsilon) 1572 { 1573 n++; 1574 if (fabs(draw_info->dash_pattern[n]) < DrawEpsilon) 1575 n=0; 1576 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5)); 1577 } 1578 for (total_length=0.0; (length >= 0.0) && (maximum_length >= (total_length+length)); ) 1579 { 1580 total_length+=length; 1581 if ((n & 0x01) != 0) 1582 { 1583 dash_polygon[0]=primitive_info[0]; 1584 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx* 1585 total_length/maximum_length); 1586 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy* 1587 total_length/maximum_length); 1588 j=1; 1589 } 1590 else 1591 { 1592 if ((j+1) > (ssize_t) (2*number_vertices)) 1593 break; 1594 dash_polygon[j]=primitive_info[i-1]; 1595 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx* 1596 total_length/maximum_length); 1597 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy* 1598 total_length/maximum_length); 1599 dash_polygon[j].coordinates=1; 1600 j++; 1601 dash_polygon[0].coordinates=(size_t) j; 1602 dash_polygon[j].primitive=UndefinedPrimitive; 1603 status&=DrawStrokePolygon(image,clone_info,dash_polygon,exception); 1604 } 1605 n++; 1606 if (fabs(draw_info->dash_pattern[n]) < DrawEpsilon) 1607 n=0; 1608 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5)); 1609 } 1610 length-=(maximum_length-total_length); 1611 if ((n & 0x01) != 0) 1612 continue; 1613 dash_polygon[j]=primitive_info[i]; 1614 dash_polygon[j].coordinates=1; 1615 j++; 1616 } 1617 if ((total_length <= maximum_length) && ((n & 0x01) == 0) && (j > 1)) 1618 { 1619 dash_polygon[j]=primitive_info[i-1]; 1620 dash_polygon[j].point.x+=DrawEpsilon; 1621 dash_polygon[j].point.y+=DrawEpsilon; 1622 dash_polygon[j].coordinates=1; 1623 j++; 1624 dash_polygon[0].coordinates=(size_t) j; 1625 dash_polygon[j].primitive=UndefinedPrimitive; 1626 status&=DrawStrokePolygon(image,clone_info,dash_polygon,exception); 1627 } 1628 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon); 1629 clone_info=DestroyDrawInfo(clone_info); 1630 if (image->debug != MagickFalse) 1631 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash"); 1632 return(status != 0 ? MagickTrue : MagickFalse); 1633 } 1634 1635 /* 1637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1638 % % 1639 % % 1640 % % 1641 % D r a w I m a g e % 1642 % % 1643 % % 1644 % % 1645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1646 % 1647 % DrawImage() draws a graphic primitive on your image. The primitive 1648 % may be represented as a string or filename. Precede the filename with an 1649 % "at" sign (@) and the contents of the file are drawn on the image. You 1650 % can affect how text is drawn by setting one or more members of the draw 1651 % info structure. 1652 % 1653 % The format of the DrawImage method is: 1654 % 1655 % MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info, 1656 % ExceptionInfo *exception) 1657 % 1658 % A description of each parameter follows: 1659 % 1660 % o image: the image. 1661 % 1662 % o draw_info: the draw info. 1663 % 1664 % o exception: return any errors or warnings in this structure. 1665 % 1666 */ 1667 1668 static inline MagickBooleanType IsPoint(const char *point) 1669 { 1670 char 1671 *p; 1672 1673 double 1674 value; 1675 1676 value=StringToDouble(point,&p); 1677 return((fabs(value) < DrawEpsilon) && (p == point) ? MagickFalse : MagickTrue); 1678 } 1679 1680 static inline void TracePoint(PrimitiveInfo *primitive_info, 1681 const PointInfo point) 1682 { 1683 primitive_info->coordinates=1; 1684 primitive_info->point=point; 1685 } 1686 1687 MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info, 1688 ExceptionInfo *exception) 1689 { 1690 #define RenderImageTag "Render/Image" 1691 1692 AffineMatrix 1693 affine, 1694 current; 1695 1696 char 1697 keyword[MagickPathExtent], 1698 geometry[MagickPathExtent], 1699 *next_token, 1700 pattern[MagickPathExtent], 1701 *primitive, 1702 *token; 1703 1704 const char 1705 *q; 1706 1707 DrawInfo 1708 **graphic_context; 1709 1710 MagickBooleanType 1711 proceed; 1712 1713 MagickSizeType 1714 length, 1715 number_points; 1716 1717 MagickStatusType 1718 status; 1719 1720 double 1721 angle, 1722 factor, 1723 primitive_extent; 1724 1725 PointInfo 1726 point; 1727 1728 PrimitiveInfo 1729 *primitive_info; 1730 1731 PrimitiveType 1732 primitive_type; 1733 1734 register const char 1735 *p; 1736 1737 register ssize_t 1738 i, 1739 x; 1740 1741 SegmentInfo 1742 bounds; 1743 1744 size_t 1745 extent, 1746 number_stops; 1747 1748 ssize_t 1749 j, 1750 k, 1751 n; 1752 1753 StopInfo 1754 *stops; 1755 1756 /* 1757 Ensure the annotation info is valid. 1758 */ 1759 assert(image != (Image *) NULL); 1760 assert(image->signature == MagickCoreSignature); 1761 if (image->debug != MagickFalse) 1762 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1763 assert(draw_info != (DrawInfo *) NULL); 1764 assert(draw_info->signature == MagickCoreSignature); 1765 if (image->debug != MagickFalse) 1766 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 1767 if ((draw_info->primitive == (char *) NULL) || 1768 (*draw_info->primitive == '\0')) 1769 return(MagickFalse); 1770 if (image->debug != MagickFalse) 1771 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image"); 1772 if (*draw_info->primitive != '@') 1773 primitive=AcquireString(draw_info->primitive); 1774 else 1775 primitive=FileToString(draw_info->primitive+1,~0UL,exception); 1776 if (primitive == (char *) NULL) 1777 return(MagickFalse); 1778 primitive_extent=(double) strlen(primitive); 1779 (void) SetImageArtifact(image,"MVG",primitive); 1780 n=0; 1781 number_stops=0; 1782 stops=(StopInfo *) NULL; 1783 /* 1784 Allocate primitive info memory. 1785 */ 1786 graphic_context=(DrawInfo **) AcquireMagickMemory( 1787 sizeof(*graphic_context)); 1788 if (graphic_context == (DrawInfo **) NULL) 1789 { 1790 primitive=DestroyString(primitive); 1791 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 1792 image->filename); 1793 } 1794 number_points=6553; 1795 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points, 1796 sizeof(*primitive_info)); 1797 if (primitive_info == (PrimitiveInfo *) NULL) 1798 { 1799 primitive=DestroyString(primitive); 1800 for ( ; n >= 0; n--) 1801 graphic_context[n]=DestroyDrawInfo(graphic_context[n]); 1802 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context); 1803 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 1804 image->filename); 1805 } 1806 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info); 1807 graphic_context[n]->viewbox=image->page; 1808 if ((image->page.width == 0) || (image->page.height == 0)) 1809 { 1810 graphic_context[n]->viewbox.width=image->columns; 1811 graphic_context[n]->viewbox.height=image->rows; 1812 } 1813 token=AcquireString(primitive); 1814 extent=strlen(token)+MagickPathExtent; 1815 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 1816 return(MagickFalse); 1817 status=MagickTrue; 1818 for (q=primitive; *q != '\0'; ) 1819 { 1820 /* 1821 Interpret graphic primitive. 1822 */ 1823 GetNextToken(q,&q,MagickPathExtent,keyword); 1824 if (*keyword == '\0') 1825 break; 1826 if (*keyword == '#') 1827 { 1828 /* 1829 Comment. 1830 */ 1831 while ((*q != '\n') && (*q != '\0')) 1832 q++; 1833 continue; 1834 } 1835 p=q-strlen(keyword)-1; 1836 primitive_type=UndefinedPrimitive; 1837 current=graphic_context[n]->affine; 1838 GetAffineMatrix(&affine); 1839 switch (*keyword) 1840 { 1841 case ';': 1842 break; 1843 case 'a': 1844 case 'A': 1845 { 1846 if (LocaleCompare("affine",keyword) == 0) 1847 { 1848 GetNextToken(q,&q,extent,token); 1849 affine.sx=StringToDouble(token,&next_token); 1850 if (token == next_token) 1851 status=MagickFalse; 1852 GetNextToken(q,&q,extent,token); 1853 if (*token == ',') 1854 GetNextToken(q,&q,extent,token); 1855 affine.rx=StringToDouble(token,&next_token); 1856 if (token == next_token) 1857 status=MagickFalse; 1858 GetNextToken(q,&q,extent,token); 1859 if (*token == ',') 1860 GetNextToken(q,&q,extent,token); 1861 affine.ry=StringToDouble(token,&next_token); 1862 if (token == next_token) 1863 status=MagickFalse; 1864 GetNextToken(q,&q,extent,token); 1865 if (*token == ',') 1866 GetNextToken(q,&q,extent,token); 1867 affine.sy=StringToDouble(token,&next_token); 1868 if (token == next_token) 1869 status=MagickFalse; 1870 GetNextToken(q,&q,extent,token); 1871 if (*token == ',') 1872 GetNextToken(q,&q,extent,token); 1873 affine.tx=StringToDouble(token,&next_token); 1874 if (token == next_token) 1875 status=MagickFalse; 1876 GetNextToken(q,&q,extent,token); 1877 if (*token == ',') 1878 GetNextToken(q,&q,extent,token); 1879 affine.ty=StringToDouble(token,&next_token); 1880 if (token == next_token) 1881 status=MagickFalse; 1882 break; 1883 } 1884 if (LocaleCompare("alpha",keyword) == 0) 1885 { 1886 primitive_type=AlphaPrimitive; 1887 break; 1888 } 1889 if (LocaleCompare("arc",keyword) == 0) 1890 { 1891 primitive_type=ArcPrimitive; 1892 break; 1893 } 1894 status=MagickFalse; 1895 break; 1896 } 1897 case 'b': 1898 case 'B': 1899 { 1900 if (LocaleCompare("bezier",keyword) == 0) 1901 { 1902 primitive_type=BezierPrimitive; 1903 break; 1904 } 1905 if (LocaleCompare("border-color",keyword) == 0) 1906 { 1907 GetNextToken(q,&q,extent,token); 1908 (void) QueryColorCompliance(token,AllCompliance, 1909 &graphic_context[n]->border_color,exception); 1910 break; 1911 } 1912 status=MagickFalse; 1913 break; 1914 } 1915 case 'c': 1916 case 'C': 1917 { 1918 if (LocaleCompare("clip-path",keyword) == 0) 1919 { 1920 /* 1921 Create clip mask. 1922 */ 1923 GetNextToken(q,&q,extent,token); 1924 (void) CloneString(&graphic_context[n]->clip_mask,token); 1925 (void) DrawClipPath(image,graphic_context[n], 1926 graphic_context[n]->clip_mask,exception); 1927 break; 1928 } 1929 if (LocaleCompare("clip-rule",keyword) == 0) 1930 { 1931 ssize_t 1932 fill_rule; 1933 1934 GetNextToken(q,&q,extent,token); 1935 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse, 1936 token); 1937 if (fill_rule == -1) 1938 status=MagickFalse; 1939 else 1940 graphic_context[n]->fill_rule=(FillRule) fill_rule; 1941 break; 1942 } 1943 if (LocaleCompare("clip-units",keyword) == 0) 1944 { 1945 ssize_t 1946 clip_units; 1947 1948 GetNextToken(q,&q,extent,token); 1949 clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse, 1950 token); 1951 if (clip_units == -1) 1952 { 1953 status=MagickFalse; 1954 break; 1955 } 1956 graphic_context[n]->clip_units=(ClipPathUnits) clip_units; 1957 if (clip_units == ObjectBoundingBox) 1958 { 1959 GetAffineMatrix(¤t); 1960 affine.sx=draw_info->bounds.x2; 1961 affine.sy=draw_info->bounds.y2; 1962 affine.tx=draw_info->bounds.x1; 1963 affine.ty=draw_info->bounds.y1; 1964 break; 1965 } 1966 break; 1967 } 1968 if (LocaleCompare("circle",keyword) == 0) 1969 { 1970 primitive_type=CirclePrimitive; 1971 break; 1972 } 1973 if (LocaleCompare("color",keyword) == 0) 1974 { 1975 primitive_type=ColorPrimitive; 1976 break; 1977 } 1978 status=MagickFalse; 1979 break; 1980 } 1981 case 'd': 1982 case 'D': 1983 { 1984 if (LocaleCompare("decorate",keyword) == 0) 1985 { 1986 ssize_t 1987 decorate; 1988 1989 GetNextToken(q,&q,extent,token); 1990 decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse, 1991 token); 1992 if (decorate == -1) 1993 status=MagickFalse; 1994 else 1995 graphic_context[n]->decorate=(DecorationType) decorate; 1996 break; 1997 } 1998 if (LocaleCompare("density",keyword) == 0) 1999 { 2000 GetNextToken(q,&q,extent,token); 2001 (void) CloneString(&graphic_context[n]->density,token); 2002 break; 2003 } 2004 if (LocaleCompare("direction",keyword) == 0) 2005 { 2006 ssize_t 2007 direction; 2008 2009 GetNextToken(q,&q,extent,token); 2010 direction=ParseCommandOption(MagickDirectionOptions,MagickFalse, 2011 token); 2012 if (direction == -1) 2013 status=MagickFalse; 2014 else 2015 graphic_context[n]->direction=(DirectionType) direction; 2016 break; 2017 } 2018 status=MagickFalse; 2019 break; 2020 } 2021 case 'e': 2022 case 'E': 2023 { 2024 if (LocaleCompare("ellipse",keyword) == 0) 2025 { 2026 primitive_type=EllipsePrimitive; 2027 break; 2028 } 2029 if (LocaleCompare("encoding",keyword) == 0) 2030 { 2031 GetNextToken(q,&q,extent,token); 2032 (void) CloneString(&graphic_context[n]->encoding,token); 2033 break; 2034 } 2035 status=MagickFalse; 2036 break; 2037 } 2038 case 'f': 2039 case 'F': 2040 { 2041 if (LocaleCompare("fill",keyword) == 0) 2042 { 2043 GetNextToken(q,&q,extent,token); 2044 (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token); 2045 if (GetImageArtifact(image,pattern) != (const char *) NULL) 2046 (void) DrawPatternPath(image,draw_info,token, 2047 &graphic_context[n]->fill_pattern,exception); 2048 else 2049 { 2050 status&=QueryColorCompliance(token,AllCompliance, 2051 &graphic_context[n]->fill,exception); 2052 if (graphic_context[n]->fill_alpha != OpaqueAlpha) 2053 graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha; 2054 if (status == MagickFalse) 2055 { 2056 ImageInfo 2057 *pattern_info; 2058 2059 pattern_info=AcquireImageInfo(); 2060 (void) CopyMagickString(pattern_info->filename,token, 2061 MagickPathExtent); 2062 graphic_context[n]->fill_pattern=ReadImage(pattern_info, 2063 exception); 2064 CatchException(exception); 2065 pattern_info=DestroyImageInfo(pattern_info); 2066 } 2067 } 2068 break; 2069 } 2070 if (LocaleCompare("fill-opacity",keyword) == 0) 2071 { 2072 GetNextToken(q,&q,extent,token); 2073 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0; 2074 graphic_context[n]->fill_alpha=(double) QuantumRange*(1.0-factor* 2075 StringToDouble(token,&next_token)); 2076 if (token == next_token) 2077 status=MagickFalse; 2078 break; 2079 } 2080 if (LocaleCompare("fill-rule",keyword) == 0) 2081 { 2082 ssize_t 2083 fill_rule; 2084 2085 GetNextToken(q,&q,extent,token); 2086 fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse, 2087 token); 2088 if (fill_rule == -1) 2089 status=MagickFalse; 2090 else 2091 graphic_context[n]->fill_rule=(FillRule) fill_rule; 2092 break; 2093 } 2094 if (LocaleCompare("font",keyword) == 0) 2095 { 2096 GetNextToken(q,&q,extent,token); 2097 (void) CloneString(&graphic_context[n]->font,token); 2098 if (LocaleCompare("none",token) == 0) 2099 graphic_context[n]->font=(char *) RelinquishMagickMemory( 2100 graphic_context[n]->font); 2101 break; 2102 } 2103 if (LocaleCompare("font-family",keyword) == 0) 2104 { 2105 GetNextToken(q,&q,extent,token); 2106 (void) CloneString(&graphic_context[n]->family,token); 2107 break; 2108 } 2109 if (LocaleCompare("font-size",keyword) == 0) 2110 { 2111 GetNextToken(q,&q,extent,token); 2112 graphic_context[n]->pointsize=StringToDouble(token,&next_token); 2113 if (token == next_token) 2114 status=MagickFalse; 2115 break; 2116 } 2117 if (LocaleCompare("font-stretch",keyword) == 0) 2118 { 2119 ssize_t 2120 stretch; 2121 2122 GetNextToken(q,&q,extent,token); 2123 stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token); 2124 if (stretch == -1) 2125 status=MagickFalse; 2126 else 2127 graphic_context[n]->stretch=(StretchType) stretch; 2128 break; 2129 } 2130 if (LocaleCompare("font-style",keyword) == 0) 2131 { 2132 ssize_t 2133 style; 2134 2135 GetNextToken(q,&q,extent,token); 2136 style=ParseCommandOption(MagickStyleOptions,MagickFalse,token); 2137 if (style == -1) 2138 status=MagickFalse; 2139 else 2140 graphic_context[n]->style=(StyleType) style; 2141 break; 2142 } 2143 if (LocaleCompare("font-weight",keyword) == 0) 2144 { 2145 ssize_t 2146 weight; 2147 2148 GetNextToken(q,&q,extent,token); 2149 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token); 2150 if (weight == -1) 2151 weight=(ssize_t) StringToUnsignedLong(token); 2152 graphic_context[n]->weight=(size_t) weight; 2153 break; 2154 } 2155 status=MagickFalse; 2156 break; 2157 } 2158 case 'g': 2159 case 'G': 2160 { 2161 if (LocaleCompare("gradient-units",keyword) == 0) 2162 { 2163 GetNextToken(q,&q,extent,token); 2164 break; 2165 } 2166 if (LocaleCompare("gravity",keyword) == 0) 2167 { 2168 ssize_t 2169 gravity; 2170 2171 GetNextToken(q,&q,extent,token); 2172 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token); 2173 if (gravity == -1) 2174 status=MagickFalse; 2175 else 2176 graphic_context[n]->gravity=(GravityType) gravity; 2177 break; 2178 } 2179 status=MagickFalse; 2180 break; 2181 } 2182 case 'i': 2183 case 'I': 2184 { 2185 if (LocaleCompare("image",keyword) == 0) 2186 { 2187 ssize_t 2188 compose; 2189 2190 primitive_type=ImagePrimitive; 2191 GetNextToken(q,&q,extent,token); 2192 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token); 2193 if (compose == -1) 2194 status=MagickFalse; 2195 else 2196 graphic_context[n]->compose=(CompositeOperator) compose; 2197 break; 2198 } 2199 if (LocaleCompare("interline-spacing",keyword) == 0) 2200 { 2201 GetNextToken(q,&q,extent,token); 2202 graphic_context[n]->interline_spacing=StringToDouble(token, 2203 &next_token); 2204 if (token == next_token) 2205 status=MagickFalse; 2206 break; 2207 } 2208 if (LocaleCompare("interword-spacing",keyword) == 0) 2209 { 2210 GetNextToken(q,&q,extent,token); 2211 graphic_context[n]->interword_spacing=StringToDouble(token, 2212 &next_token); 2213 if (token == next_token) 2214 status=MagickFalse; 2215 break; 2216 } 2217 status=MagickFalse; 2218 break; 2219 } 2220 case 'k': 2221 case 'K': 2222 { 2223 if (LocaleCompare("kerning",keyword) == 0) 2224 { 2225 GetNextToken(q,&q,extent,token); 2226 graphic_context[n]->kerning=StringToDouble(token,&next_token); 2227 if (token == next_token) 2228 status=MagickFalse; 2229 break; 2230 } 2231 status=MagickFalse; 2232 break; 2233 } 2234 case 'l': 2235 case 'L': 2236 { 2237 if (LocaleCompare("line",keyword) == 0) 2238 primitive_type=LinePrimitive; 2239 else 2240 status=MagickFalse; 2241 break; 2242 } 2243 case 'o': 2244 case 'O': 2245 { 2246 if (LocaleCompare("offset",keyword) == 0) 2247 { 2248 GetNextToken(q,&q,extent,token); 2249 break; 2250 } 2251 if (LocaleCompare("opacity",keyword) == 0) 2252 { 2253 GetNextToken(q,&q,extent,token); 2254 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0; 2255 graphic_context[n]->fill_alpha=QuantumRange*(1.0-(QuantumScale* 2256 graphic_context[n]->fill_alpha*(1.0-factor*StringToDouble(token, 2257 &next_token)))); 2258 graphic_context[n]->stroke_alpha=QuantumRange*(1.0-(QuantumScale* 2259 graphic_context[n]->stroke_alpha*(1.0-factor*StringToDouble(token, 2260 &next_token)))); 2261 if (token == next_token) 2262 status=MagickFalse; 2263 break; 2264 } 2265 status=MagickFalse; 2266 break; 2267 } 2268 case 'p': 2269 case 'P': 2270 { 2271 if (LocaleCompare("path",keyword) == 0) 2272 { 2273 primitive_type=PathPrimitive; 2274 break; 2275 } 2276 if (LocaleCompare("point",keyword) == 0) 2277 { 2278 primitive_type=PointPrimitive; 2279 break; 2280 } 2281 if (LocaleCompare("polyline",keyword) == 0) 2282 { 2283 primitive_type=PolylinePrimitive; 2284 break; 2285 } 2286 if (LocaleCompare("polygon",keyword) == 0) 2287 { 2288 primitive_type=PolygonPrimitive; 2289 break; 2290 } 2291 if (LocaleCompare("pop",keyword) == 0) 2292 { 2293 GetNextToken(q,&q,extent,token); 2294 if (LocaleCompare("clip-path",token) == 0) 2295 break; 2296 if (LocaleCompare("defs",token) == 0) 2297 break; 2298 if (LocaleCompare("gradient",token) == 0) 2299 break; 2300 if (LocaleCompare("graphic-context",token) == 0) 2301 { 2302 if (n <= 0) 2303 { 2304 (void) ThrowMagickException(exception,GetMagickModule(), 2305 DrawError,"UnbalancedGraphicContextPushPop","`%s'",token); 2306 status=MagickFalse; 2307 n=0; 2308 break; 2309 } 2310 if (graphic_context[n]->clip_mask != (char *) NULL) 2311 if (LocaleCompare(graphic_context[n]->clip_mask, 2312 graphic_context[n-1]->clip_mask) != 0) 2313 (void) SetImageMask(image,ReadPixelMask,(Image *) NULL, 2314 exception); 2315 graphic_context[n]=DestroyDrawInfo(graphic_context[n]); 2316 n--; 2317 break; 2318 } 2319 if (LocaleCompare("pattern",token) == 0) 2320 break; 2321 status=MagickFalse; 2322 break; 2323 } 2324 if (LocaleCompare("push",keyword) == 0) 2325 { 2326 GetNextToken(q,&q,extent,token); 2327 if (LocaleCompare("clip-path",token) == 0) 2328 { 2329 char 2330 name[MagickPathExtent]; 2331 2332 GetNextToken(q,&q,extent,token); 2333 (void) FormatLocaleString(name,MagickPathExtent,"%s",token); 2334 for (p=q; *q != '\0'; ) 2335 { 2336 GetNextToken(q,&q,extent,token); 2337 if (LocaleCompare(token,"pop") != 0) 2338 continue; 2339 GetNextToken(q,(const char **) NULL,extent,token); 2340 if (LocaleCompare(token,"clip-path") != 0) 2341 continue; 2342 break; 2343 } 2344 (void) CopyMagickString(token,p,(size_t) (q-p-4+1)); 2345 (void) SetImageArtifact(image,name,token); 2346 GetNextToken(q,&q,extent,token); 2347 break; 2348 } 2349 if (LocaleCompare("gradient",token) == 0) 2350 { 2351 char 2352 key[2*MagickPathExtent], 2353 name[MagickPathExtent], 2354 type[MagickPathExtent]; 2355 2356 SegmentInfo 2357 segment; 2358 2359 GetNextToken(q,&q,extent,token); 2360 (void) CopyMagickString(name,token,MagickPathExtent); 2361 GetNextToken(q,&q,extent,token); 2362 (void) CopyMagickString(type,token,MagickPathExtent); 2363 GetNextToken(q,&q,extent,token); 2364 segment.x1=StringToDouble(token,&next_token); 2365 if (token == next_token) 2366 status=MagickFalse; 2367 GetNextToken(q,&q,extent,token); 2368 if (*token == ',') 2369 GetNextToken(q,&q,extent,token); 2370 segment.y1=StringToDouble(token,&next_token); 2371 if (token == next_token) 2372 status=MagickFalse; 2373 GetNextToken(q,&q,extent,token); 2374 if (*token == ',') 2375 GetNextToken(q,&q,extent,token); 2376 segment.x2=StringToDouble(token,&next_token); 2377 if (token == next_token) 2378 status=MagickFalse; 2379 GetNextToken(q,&q,extent,token); 2380 if (*token == ',') 2381 GetNextToken(q,&q,extent,token); 2382 segment.y2=StringToDouble(token,&next_token); 2383 if (token == next_token) 2384 status=MagickFalse; 2385 if (LocaleCompare(type,"radial") == 0) 2386 { 2387 GetNextToken(q,&q,extent,token); 2388 if (*token == ',') 2389 GetNextToken(q,&q,extent,token); 2390 } 2391 for (p=q; *q != '\0'; ) 2392 { 2393 GetNextToken(q,&q,extent,token); 2394 if (LocaleCompare(token,"pop") != 0) 2395 continue; 2396 GetNextToken(q,(const char **) NULL,extent,token); 2397 if (LocaleCompare(token,"gradient") != 0) 2398 continue; 2399 break; 2400 } 2401 (void) CopyMagickString(token,p,(size_t) (q-p-4+1)); 2402 bounds.x1=graphic_context[n]->affine.sx*segment.x1+ 2403 graphic_context[n]->affine.ry*segment.y1+ 2404 graphic_context[n]->affine.tx; 2405 bounds.y1=graphic_context[n]->affine.rx*segment.x1+ 2406 graphic_context[n]->affine.sy*segment.y1+ 2407 graphic_context[n]->affine.ty; 2408 bounds.x2=graphic_context[n]->affine.sx*segment.x2+ 2409 graphic_context[n]->affine.ry*segment.y2+ 2410 graphic_context[n]->affine.tx; 2411 bounds.y2=graphic_context[n]->affine.rx*segment.x2+ 2412 graphic_context[n]->affine.sy*segment.y2+ 2413 graphic_context[n]->affine.ty; 2414 (void) FormatLocaleString(key,MagickPathExtent,"%s",name); 2415 (void) SetImageArtifact(image,key,token); 2416 (void) FormatLocaleString(key,MagickPathExtent,"%s-type",name); 2417 (void) SetImageArtifact(image,key,type); 2418 (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry", 2419 name); 2420 (void) FormatLocaleString(geometry,MagickPathExtent, 2421 "%gx%g%+.15g%+.15g", 2422 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0), 2423 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0), 2424 bounds.x1,bounds.y1); 2425 (void) SetImageArtifact(image,key,geometry); 2426 GetNextToken(q,&q,extent,token); 2427 break; 2428 } 2429 if (LocaleCompare("pattern",token) == 0) 2430 { 2431 char 2432 key[2*MagickPathExtent], 2433 name[MagickPathExtent]; 2434 2435 RectangleInfo 2436 pattern_bounds; 2437 2438 GetNextToken(q,&q,extent,token); 2439 (void) CopyMagickString(name,token,MagickPathExtent); 2440 GetNextToken(q,&q,extent,token); 2441 pattern_bounds.x=(ssize_t) ceil(StringToDouble(token, 2442 &next_token)-0.5); 2443 if (token == next_token) 2444 status=MagickFalse; 2445 GetNextToken(q,&q,extent,token); 2446 if (*token == ',') 2447 GetNextToken(q,&q,extent,token); 2448 pattern_bounds.y=(ssize_t) ceil(StringToDouble(token, 2449 &next_token)-0.5); 2450 if (token == next_token) 2451 status=MagickFalse; 2452 GetNextToken(q,&q,extent,token); 2453 if (*token == ',') 2454 GetNextToken(q,&q,extent,token); 2455 pattern_bounds.width=(size_t) floor(StringToDouble(token, 2456 &next_token)+0.5); 2457 if (token == next_token) 2458 status=MagickFalse; 2459 GetNextToken(q,&q,extent,token); 2460 if (*token == ',') 2461 GetNextToken(q,&q,extent,token); 2462 pattern_bounds.height=(size_t) floor(StringToDouble(token, 2463 &next_token)+0.5); 2464 if (token == next_token) 2465 status=MagickFalse; 2466 for (p=q; *q != '\0'; ) 2467 { 2468 GetNextToken(q,&q,extent,token); 2469 if (LocaleCompare(token,"pop") != 0) 2470 continue; 2471 GetNextToken(q,(const char **) NULL,extent,token); 2472 if (LocaleCompare(token,"pattern") != 0) 2473 continue; 2474 break; 2475 } 2476 (void) CopyMagickString(token,p,(size_t) (q-p-4+1)); 2477 (void) FormatLocaleString(key,MagickPathExtent,"%s",name); 2478 (void) SetImageArtifact(image,key,token); 2479 (void) FormatLocaleString(key,MagickPathExtent,"%s-geometry", 2480 name); 2481 (void) FormatLocaleString(geometry,MagickPathExtent, 2482 "%.20gx%.20g%+.20g%+.20g",(double)pattern_bounds.width, 2483 (double)pattern_bounds.height,(double)pattern_bounds.x, 2484 (double)pattern_bounds.y); 2485 (void) SetImageArtifact(image,key,geometry); 2486 GetNextToken(q,&q,extent,token); 2487 break; 2488 } 2489 if (LocaleCompare("graphic-context",token) == 0) 2490 { 2491 n++; 2492 graphic_context=(DrawInfo **) ResizeQuantumMemory( 2493 graphic_context,(size_t) (n+1),sizeof(*graphic_context)); 2494 if (graphic_context == (DrawInfo **) NULL) 2495 { 2496 (void) ThrowMagickException(exception,GetMagickModule(), 2497 ResourceLimitError,"MemoryAllocationFailed","`%s'", 2498 image->filename); 2499 break; 2500 } 2501 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL, 2502 graphic_context[n-1]); 2503 break; 2504 } 2505 if (LocaleCompare("defs",token) == 0) 2506 break; 2507 status=MagickFalse; 2508 break; 2509 } 2510 status=MagickFalse; 2511 break; 2512 } 2513 case 'r': 2514 case 'R': 2515 { 2516 if (LocaleCompare("rectangle",keyword) == 0) 2517 { 2518 primitive_type=RectanglePrimitive; 2519 break; 2520 } 2521 if (LocaleCompare("rotate",keyword) == 0) 2522 { 2523 GetNextToken(q,&q,extent,token); 2524 angle=StringToDouble(token,&next_token); 2525 if (token == next_token) 2526 status=MagickFalse; 2527 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0))); 2528 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0))); 2529 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0)))); 2530 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0))); 2531 break; 2532 } 2533 if (LocaleCompare("roundRectangle",keyword) == 0) 2534 { 2535 primitive_type=RoundRectanglePrimitive; 2536 break; 2537 } 2538 status=MagickFalse; 2539 break; 2540 } 2541 case 's': 2542 case 'S': 2543 { 2544 if (LocaleCompare("scale",keyword) == 0) 2545 { 2546 GetNextToken(q,&q,extent,token); 2547 affine.sx=StringToDouble(token,&next_token); 2548 if (token == next_token) 2549 status=MagickFalse; 2550 GetNextToken(q,&q,extent,token); 2551 if (*token == ',') 2552 GetNextToken(q,&q,extent,token); 2553 affine.sy=StringToDouble(token,&next_token); 2554 if (token == next_token) 2555 status=MagickFalse; 2556 break; 2557 } 2558 if (LocaleCompare("skewX",keyword) == 0) 2559 { 2560 GetNextToken(q,&q,extent,token); 2561 angle=StringToDouble(token,&next_token); 2562 if (token == next_token) 2563 status=MagickFalse; 2564 affine.ry=sin(DegreesToRadians(angle)); 2565 break; 2566 } 2567 if (LocaleCompare("skewY",keyword) == 0) 2568 { 2569 GetNextToken(q,&q,extent,token); 2570 angle=StringToDouble(token,&next_token); 2571 if (token == next_token) 2572 status=MagickFalse; 2573 affine.rx=(-tan(DegreesToRadians(angle)/2.0)); 2574 break; 2575 } 2576 if (LocaleCompare("stop-color",keyword) == 0) 2577 { 2578 PixelInfo 2579 stop_color; 2580 2581 number_stops++; 2582 if (number_stops == 1) 2583 stops=(StopInfo *) AcquireQuantumMemory(2,sizeof(*stops)); 2584 else if (number_stops > 2) 2585 stops=(StopInfo *) ResizeQuantumMemory(stops,number_stops, 2586 sizeof(*stops)); 2587 if (stops == (StopInfo *) NULL) 2588 { 2589 (void) ThrowMagickException(exception,GetMagickModule(), 2590 ResourceLimitError,"MemoryAllocationFailed","`%s'", 2591 image->filename); 2592 break; 2593 } 2594 GetNextToken(q,&q,extent,token); 2595 (void) QueryColorCompliance(token,AllCompliance,&stop_color, 2596 exception); 2597 stops[number_stops-1].color=stop_color; 2598 GetNextToken(q,&q,extent,token); 2599 stops[number_stops-1].offset=StringToDouble(token,&next_token); 2600 if (token == next_token) 2601 status=MagickFalse; 2602 break; 2603 } 2604 if (LocaleCompare("stroke",keyword) == 0) 2605 { 2606 GetNextToken(q,&q,extent,token); 2607 (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token); 2608 if (GetImageArtifact(image,pattern) != (const char *) NULL) 2609 (void) DrawPatternPath(image,draw_info,token, 2610 &graphic_context[n]->stroke_pattern,exception); 2611 else 2612 { 2613 status&=QueryColorCompliance(token,AllCompliance, 2614 &graphic_context[n]->stroke,exception); 2615 if (graphic_context[n]->stroke_alpha != OpaqueAlpha) 2616 graphic_context[n]->stroke.alpha= 2617 graphic_context[n]->stroke_alpha; 2618 if (status == MagickFalse) 2619 { 2620 ImageInfo 2621 *pattern_info; 2622 2623 pattern_info=AcquireImageInfo(); 2624 (void) CopyMagickString(pattern_info->filename,token, 2625 MagickPathExtent); 2626 graphic_context[n]->stroke_pattern=ReadImage(pattern_info, 2627 exception); 2628 CatchException(exception); 2629 pattern_info=DestroyImageInfo(pattern_info); 2630 } 2631 } 2632 break; 2633 } 2634 if (LocaleCompare("stroke-antialias",keyword) == 0) 2635 { 2636 GetNextToken(q,&q,extent,token); 2637 graphic_context[n]->stroke_antialias= 2638 StringToLong(token) != 0 ? MagickTrue : MagickFalse; 2639 break; 2640 } 2641 if (LocaleCompare("stroke-dasharray",keyword) == 0) 2642 { 2643 if (graphic_context[n]->dash_pattern != (double *) NULL) 2644 graphic_context[n]->dash_pattern=(double *) 2645 RelinquishMagickMemory(graphic_context[n]->dash_pattern); 2646 if (IsPoint(q) != MagickFalse) 2647 { 2648 const char 2649 *r; 2650 2651 r=q; 2652 GetNextToken(r,&r,extent,token); 2653 if (*token == ',') 2654 GetNextToken(r,&r,extent,token); 2655 for (x=0; IsPoint(token) != MagickFalse; x++) 2656 { 2657 GetNextToken(r,&r,extent,token); 2658 if (*token == ',') 2659 GetNextToken(r,&r,extent,token); 2660 } 2661 graphic_context[n]->dash_pattern=(double *) 2662 AcquireQuantumMemory((size_t) (2UL*x+1UL), 2663 sizeof(*graphic_context[n]->dash_pattern)); 2664 if (graphic_context[n]->dash_pattern == (double *) NULL) 2665 { 2666 (void) ThrowMagickException(exception,GetMagickModule(), 2667 ResourceLimitError,"MemoryAllocationFailed","`%s'", 2668 image->filename); 2669 status=MagickFalse; 2670 break; 2671 } 2672 for (j=0; j < x; j++) 2673 { 2674 GetNextToken(q,&q,extent,token); 2675 if (*token == ',') 2676 GetNextToken(q,&q,extent,token); 2677 graphic_context[n]->dash_pattern[j]=StringToDouble(token, 2678 &next_token); 2679 if (token == next_token) 2680 status=MagickFalse; 2681 if (graphic_context[n]->dash_pattern[j] < 0.0) 2682 status=MagickFalse; 2683 } 2684 if ((x & 0x01) != 0) 2685 for ( ; j < (2*x); j++) 2686 graphic_context[n]->dash_pattern[j]= 2687 graphic_context[n]->dash_pattern[j-x]; 2688 graphic_context[n]->dash_pattern[j]=0.0; 2689 break; 2690 } 2691 GetNextToken(q,&q,extent,token); 2692 break; 2693 } 2694 if (LocaleCompare("stroke-dashoffset",keyword) == 0) 2695 { 2696 GetNextToken(q,&q,extent,token); 2697 graphic_context[n]->dash_offset=StringToDouble(token, 2698 &next_token); 2699 if (token == next_token) 2700 status=MagickFalse; 2701 break; 2702 } 2703 if (LocaleCompare("stroke-linecap",keyword) == 0) 2704 { 2705 ssize_t 2706 linecap; 2707 2708 GetNextToken(q,&q,extent,token); 2709 linecap=ParseCommandOption(MagickLineCapOptions,MagickFalse,token); 2710 if (linecap == -1) 2711 status=MagickFalse; 2712 else 2713 graphic_context[n]->linecap=(LineCap) linecap; 2714 break; 2715 } 2716 if (LocaleCompare("stroke-linejoin",keyword) == 0) 2717 { 2718 ssize_t 2719 linejoin; 2720 2721 GetNextToken(q,&q,extent,token); 2722 linejoin=ParseCommandOption(MagickLineJoinOptions,MagickFalse, 2723 token); 2724 if (linejoin == -1) 2725 status=MagickFalse; 2726 else 2727 graphic_context[n]->linejoin=(LineJoin) linejoin; 2728 break; 2729 } 2730 if (LocaleCompare("stroke-miterlimit",keyword) == 0) 2731 { 2732 GetNextToken(q,&q,extent,token); 2733 graphic_context[n]->miterlimit=StringToUnsignedLong(token); 2734 break; 2735 } 2736 if (LocaleCompare("stroke-opacity",keyword) == 0) 2737 { 2738 GetNextToken(q,&q,extent,token); 2739 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0; 2740 graphic_context[n]->stroke_alpha=(double) QuantumRange*(1.0-factor* 2741 StringToDouble(token,&next_token)); 2742 if (token == next_token) 2743 status=MagickFalse; 2744 break; 2745 } 2746 if (LocaleCompare("stroke-width",keyword) == 0) 2747 { 2748 GetNextToken(q,&q,extent,token); 2749 graphic_context[n]->stroke_width=StringToDouble(token,&next_token); 2750 if (token == next_token) 2751 status=MagickFalse; 2752 break; 2753 } 2754 status=MagickFalse; 2755 break; 2756 } 2757 case 't': 2758 case 'T': 2759 { 2760 if (LocaleCompare("text",keyword) == 0) 2761 { 2762 primitive_type=TextPrimitive; 2763 break; 2764 } 2765 if (LocaleCompare("text-align",keyword) == 0) 2766 { 2767 ssize_t 2768 align; 2769 2770 GetNextToken(q,&q,extent,token); 2771 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token); 2772 if (align == -1) 2773 status=MagickFalse; 2774 else 2775 graphic_context[n]->align=(AlignType) align; 2776 break; 2777 } 2778 if (LocaleCompare("text-anchor",keyword) == 0) 2779 { 2780 ssize_t 2781 align; 2782 2783 GetNextToken(q,&q,extent,token); 2784 align=ParseCommandOption(MagickAlignOptions,MagickFalse,token); 2785 if (align == -1) 2786 status=MagickFalse; 2787 else 2788 graphic_context[n]->align=(AlignType) align; 2789 break; 2790 } 2791 if (LocaleCompare("text-antialias",keyword) == 0) 2792 { 2793 GetNextToken(q,&q,extent,token); 2794 graphic_context[n]->text_antialias=StringToLong(token) != 0 ? 2795 MagickTrue : MagickFalse; 2796 break; 2797 } 2798 if (LocaleCompare("text-undercolor",keyword) == 0) 2799 { 2800 GetNextToken(q,&q,extent,token); 2801 (void) QueryColorCompliance(token,AllCompliance, 2802 &graphic_context[n]->undercolor,exception); 2803 break; 2804 } 2805 if (LocaleCompare("translate",keyword) == 0) 2806 { 2807 GetNextToken(q,&q,extent,token); 2808 affine.tx=StringToDouble(token,&next_token); 2809 if (token == next_token) 2810 status=MagickFalse; 2811 GetNextToken(q,&q,extent,token); 2812 if (*token == ',') 2813 GetNextToken(q,&q,extent,token); 2814 affine.ty=StringToDouble(token,&next_token); 2815 if (token == next_token) 2816 status=MagickFalse; 2817 break; 2818 } 2819 status=MagickFalse; 2820 break; 2821 } 2822 case 'v': 2823 case 'V': 2824 { 2825 if (LocaleCompare("viewbox",keyword) == 0) 2826 { 2827 GetNextToken(q,&q,extent,token); 2828 graphic_context[n]->viewbox.x=(ssize_t) ceil(StringToDouble(token, 2829 &next_token)-0.5); 2830 if (token == next_token) 2831 status=MagickFalse; 2832 GetNextToken(q,&q,extent,token); 2833 if (*token == ',') 2834 GetNextToken(q,&q,extent,token); 2835 graphic_context[n]->viewbox.y=(ssize_t) ceil(StringToDouble(token, 2836 &next_token)-0.5); 2837 if (token == next_token) 2838 status=MagickFalse; 2839 GetNextToken(q,&q,extent,token); 2840 if (*token == ',') 2841 GetNextToken(q,&q,extent,token); 2842 graphic_context[n]->viewbox.width=(size_t) floor(StringToDouble( 2843 token,&next_token)+0.5); 2844 if (token == next_token) 2845 status=MagickFalse; 2846 GetNextToken(q,&q,extent,token); 2847 if (*token == ',') 2848 GetNextToken(q,&q,extent,token); 2849 graphic_context[n]->viewbox.height=(size_t) floor(StringToDouble( 2850 token,&next_token)+0.5); 2851 if (token == next_token) 2852 status=MagickFalse; 2853 break; 2854 } 2855 status=MagickFalse; 2856 break; 2857 } 2858 default: 2859 { 2860 status=MagickFalse; 2861 break; 2862 } 2863 } 2864 if (status == MagickFalse) 2865 break; 2866 if ((fabs(affine.sx-1.0) >= DrawEpsilon) || 2867 (fabs(affine.rx) >= DrawEpsilon) || (fabs(affine.ry) >= DrawEpsilon) || 2868 (fabs(affine.sy-1.0) >= DrawEpsilon) || 2869 (fabs(affine.tx) >= DrawEpsilon) || (fabs(affine.ty) >= DrawEpsilon)) 2870 { 2871 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx; 2872 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx; 2873 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy; 2874 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy; 2875 graphic_context[n]->affine.tx=current.sx*affine.tx+current.ry*affine.ty+ 2876 current.tx; 2877 graphic_context[n]->affine.ty=current.rx*affine.tx+current.sy*affine.ty+ 2878 current.ty; 2879 } 2880 if (primitive_type == UndefinedPrimitive) 2881 { 2882 if (*q == '\0') 2883 { 2884 if (number_stops > 1) 2885 { 2886 GradientType 2887 type; 2888 2889 type=LinearGradient; 2890 if (draw_info->gradient.type == RadialGradient) 2891 type=RadialGradient; 2892 (void) GradientImage(image,type,PadSpread,stops,number_stops, 2893 exception); 2894 } 2895 if (number_stops > 0) 2896 stops=(StopInfo *) RelinquishMagickMemory(stops); 2897 } 2898 if (image->debug != MagickFalse) 2899 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) 2900 (q-p),p); 2901 continue; 2902 } 2903 /* 2904 Parse the primitive attributes. 2905 */ 2906 i=0; 2907 j=0; 2908 primitive_info[0].point.x=0.0; 2909 primitive_info[0].point.y=0.0; 2910 for (x=0; *q != '\0'; x++) 2911 { 2912 /* 2913 Define points. 2914 */ 2915 if (IsPoint(q) == MagickFalse) 2916 break; 2917 GetNextToken(q,&q,extent,token); 2918 point.x=StringToDouble(token,&next_token); 2919 if (token == next_token) 2920 status=MagickFalse; 2921 GetNextToken(q,&q,extent,token); 2922 if (*token == ',') 2923 GetNextToken(q,&q,extent,token); 2924 point.y=StringToDouble(token,&next_token); 2925 if (token == next_token) 2926 status=MagickFalse; 2927 GetNextToken(q,(const char **) NULL,extent,token); 2928 if (*token == ',') 2929 GetNextToken(q,&q,extent,token); 2930 primitive_info[i].primitive=primitive_type; 2931 primitive_info[i].point=point; 2932 primitive_info[i].coordinates=0; 2933 primitive_info[i].method=FloodfillMethod; 2934 i++; 2935 if (i < (ssize_t) number_points) 2936 continue; 2937 number_points<<=1; 2938 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info, 2939 (size_t) number_points,sizeof(*primitive_info)); 2940 if ((primitive_info == (PrimitiveInfo *) NULL) || 2941 (number_points != (MagickSizeType) ((size_t) number_points))) 2942 { 2943 (void) ThrowMagickException(exception,GetMagickModule(), 2944 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 2945 break; 2946 } 2947 } 2948 primitive_info[j].primitive=primitive_type; 2949 primitive_info[j].coordinates=(size_t) x; 2950 primitive_info[j].method=FloodfillMethod; 2951 primitive_info[j].text=(char *) NULL; 2952 /* 2953 Circumscribe primitive within a circle. 2954 */ 2955 bounds.x1=primitive_info[j].point.x; 2956 bounds.y1=primitive_info[j].point.y; 2957 bounds.x2=primitive_info[j].point.x; 2958 bounds.y2=primitive_info[j].point.y; 2959 for (k=1; k < (ssize_t) primitive_info[j].coordinates; k++) 2960 { 2961 point=primitive_info[j+k].point; 2962 if (point.x < bounds.x1) 2963 bounds.x1=point.x; 2964 if (point.y < bounds.y1) 2965 bounds.y1=point.y; 2966 if (point.x > bounds.x2) 2967 bounds.x2=point.x; 2968 if (point.y > bounds.y2) 2969 bounds.y2=point.y; 2970 } 2971 /* 2972 Speculate how many points our primitive might consume. 2973 */ 2974 length=primitive_info[j].coordinates; 2975 switch (primitive_type) 2976 { 2977 case RectanglePrimitive: 2978 { 2979 length*=5; 2980 break; 2981 } 2982 case RoundRectanglePrimitive: 2983 { 2984 double 2985 alpha, 2986 beta, 2987 radius; 2988 2989 alpha=bounds.x2-bounds.x1; 2990 beta=bounds.y2-bounds.y1; 2991 radius=hypot((double) alpha,(double) beta); 2992 length*=5; 2993 length+=2*((size_t) ceil((double) MagickPI*radius))+6*BezierQuantum+360; 2994 break; 2995 } 2996 case BezierPrimitive: 2997 { 2998 if (primitive_info[j].coordinates > 107) 2999 (void) ThrowMagickException(exception,GetMagickModule(),DrawError, 3000 "TooManyBezierCoordinates","`%s'",token); 3001 length=BezierQuantum*primitive_info[j].coordinates; 3002 break; 3003 } 3004 case PathPrimitive: 3005 { 3006 char 3007 *s, 3008 *t; 3009 3010 GetNextToken(q,&q,extent,token); 3011 length=1; 3012 t=token; 3013 for (s=token; *s != '\0'; s=t) 3014 { 3015 double 3016 value; 3017 3018 value=StringToDouble(s,&t); 3019 (void) value; 3020 if (s == t) 3021 { 3022 t++; 3023 continue; 3024 } 3025 length++; 3026 } 3027 length=length*BezierQuantum/2; 3028 break; 3029 } 3030 case CirclePrimitive: 3031 case ArcPrimitive: 3032 case EllipsePrimitive: 3033 { 3034 double 3035 alpha, 3036 beta, 3037 radius; 3038 3039 alpha=bounds.x2-bounds.x1; 3040 beta=bounds.y2-bounds.y1; 3041 radius=hypot((double) alpha,(double) beta); 3042 length=2*((size_t) ceil((double) MagickPI*radius))+6*BezierQuantum+360; 3043 break; 3044 } 3045 default: 3046 break; 3047 } 3048 if ((i+length) >= number_points) 3049 { 3050 /* 3051 Resize based on speculative points required by primitive. 3052 */ 3053 number_points+=length+1; 3054 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info, 3055 (size_t) number_points,sizeof(*primitive_info)); 3056 if ((primitive_info == (PrimitiveInfo *) NULL) || 3057 (number_points != (MagickSizeType) ((size_t) number_points))) 3058 { 3059 (void) ThrowMagickException(exception,GetMagickModule(), 3060 ResourceLimitError,"MemoryAllocationFailed","`%s'", 3061 image->filename); 3062 break; 3063 } 3064 } 3065 switch (primitive_type) 3066 { 3067 case PointPrimitive: 3068 default: 3069 { 3070 if (primitive_info[j].coordinates != 1) 3071 { 3072 status=MagickFalse; 3073 break; 3074 } 3075 TracePoint(primitive_info+j,primitive_info[j].point); 3076 i=(ssize_t) (j+primitive_info[j].coordinates); 3077 break; 3078 } 3079 case LinePrimitive: 3080 { 3081 if (primitive_info[j].coordinates != 2) 3082 { 3083 status=MagickFalse; 3084 break; 3085 } 3086 TraceLine(primitive_info+j,primitive_info[j].point, 3087 primitive_info[j+1].point); 3088 i=(ssize_t) (j+primitive_info[j].coordinates); 3089 break; 3090 } 3091 case RectanglePrimitive: 3092 { 3093 if (primitive_info[j].coordinates != 2) 3094 { 3095 status=MagickFalse; 3096 break; 3097 } 3098 TraceRectangle(primitive_info+j,primitive_info[j].point, 3099 primitive_info[j+1].point); 3100 i=(ssize_t) (j+primitive_info[j].coordinates); 3101 break; 3102 } 3103 case RoundRectanglePrimitive: 3104 { 3105 if (primitive_info[j].coordinates != 3) 3106 { 3107 status=MagickFalse; 3108 break; 3109 } 3110 TraceRoundRectangle(primitive_info+j,primitive_info[j].point, 3111 primitive_info[j+1].point,primitive_info[j+2].point); 3112 i=(ssize_t) (j+primitive_info[j].coordinates); 3113 break; 3114 } 3115 case ArcPrimitive: 3116 { 3117 if (primitive_info[j].coordinates != 3) 3118 { 3119 primitive_type=UndefinedPrimitive; 3120 break; 3121 } 3122 TraceArc(primitive_info+j,primitive_info[j].point, 3123 primitive_info[j+1].point,primitive_info[j+2].point); 3124 i=(ssize_t) (j+primitive_info[j].coordinates); 3125 break; 3126 } 3127 case EllipsePrimitive: 3128 { 3129 if (primitive_info[j].coordinates != 3) 3130 { 3131 status=MagickFalse; 3132 break; 3133 } 3134 TraceEllipse(primitive_info+j,primitive_info[j].point, 3135 primitive_info[j+1].point,primitive_info[j+2].point); 3136 i=(ssize_t) (j+primitive_info[j].coordinates); 3137 break; 3138 } 3139 case CirclePrimitive: 3140 { 3141 if (primitive_info[j].coordinates != 2) 3142 { 3143 status=MagickFalse; 3144 break; 3145 } 3146 TraceCircle(primitive_info+j,primitive_info[j].point, 3147 primitive_info[j+1].point); 3148 i=(ssize_t) (j+primitive_info[j].coordinates); 3149 break; 3150 } 3151 case PolylinePrimitive: 3152 break; 3153 case PolygonPrimitive: 3154 { 3155 primitive_info[i]=primitive_info[j]; 3156 primitive_info[i].coordinates=0; 3157 primitive_info[j].coordinates++; 3158 i++; 3159 break; 3160 } 3161 case BezierPrimitive: 3162 { 3163 if (primitive_info[j].coordinates < 3) 3164 { 3165 status=MagickFalse; 3166 break; 3167 } 3168 TraceBezier(primitive_info+j,primitive_info[j].coordinates); 3169 i=(ssize_t) (j+primitive_info[j].coordinates); 3170 break; 3171 } 3172 case PathPrimitive: 3173 { 3174 i=(ssize_t) (j+TracePath(primitive_info+j,token)); 3175 break; 3176 } 3177 case AlphaPrimitive: 3178 case ColorPrimitive: 3179 { 3180 ssize_t 3181 method; 3182 3183 if (primitive_info[j].coordinates != 1) 3184 { 3185 status=MagickFalse; 3186 break; 3187 } 3188 GetNextToken(q,&q,extent,token); 3189 method=ParseCommandOption(MagickMethodOptions,MagickFalse,token); 3190 if (method == -1) 3191 status=MagickFalse; 3192 else 3193 primitive_info[j].method=(PaintMethod) method; 3194 break; 3195 } 3196 case TextPrimitive: 3197 { 3198 if (primitive_info[j].coordinates != 1) 3199 { 3200 status=MagickFalse; 3201 break; 3202 } 3203 if (*token != ',') 3204 GetNextToken(q,&q,extent,token); 3205 primitive_info[j].text=AcquireString(token); 3206 break; 3207 } 3208 case ImagePrimitive: 3209 { 3210 if (primitive_info[j].coordinates != 2) 3211 { 3212 status=MagickFalse; 3213 break; 3214 } 3215 GetNextToken(q,&q,extent,token); 3216 primitive_info[j].text=AcquireString(token); 3217 break; 3218 } 3219 } 3220 if (primitive_info == (PrimitiveInfo *) NULL) 3221 break; 3222 if (image->debug != MagickFalse) 3223 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p); 3224 if (status == MagickFalse) 3225 break; 3226 primitive_info[i].primitive=UndefinedPrimitive; 3227 if (i == 0) 3228 continue; 3229 /* 3230 Transform points. 3231 */ 3232 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) 3233 { 3234 point=primitive_info[i].point; 3235 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+ 3236 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx; 3237 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+ 3238 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty; 3239 point=primitive_info[i].point; 3240 if (point.x < graphic_context[n]->bounds.x1) 3241 graphic_context[n]->bounds.x1=point.x; 3242 if (point.y < graphic_context[n]->bounds.y1) 3243 graphic_context[n]->bounds.y1=point.y; 3244 if (point.x > graphic_context[n]->bounds.x2) 3245 graphic_context[n]->bounds.x2=point.x; 3246 if (point.y > graphic_context[n]->bounds.y2) 3247 graphic_context[n]->bounds.y2=point.y; 3248 if (primitive_info[i].primitive == ImagePrimitive) 3249 break; 3250 if (i >= (ssize_t) number_points) 3251 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 3252 } 3253 if (graphic_context[n]->render != MagickFalse) 3254 { 3255 if ((n != 0) && (graphic_context[n]->clip_mask != (char *) NULL) && 3256 (LocaleCompare(graphic_context[n]->clip_mask, 3257 graphic_context[n-1]->clip_mask) != 0)) 3258 status&=DrawClipPath(image,graphic_context[n], 3259 graphic_context[n]->clip_mask,exception); 3260 status&=DrawPrimitive(image,graphic_context[n],primitive_info, 3261 exception); 3262 } 3263 if (primitive_info->text != (char *) NULL) 3264 primitive_info->text=(char *) RelinquishMagickMemory( 3265 primitive_info->text); 3266 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType) 3267 primitive_extent); 3268 if (proceed == MagickFalse) 3269 break; 3270 if (status == 0) 3271 break; 3272 } 3273 if (image->debug != MagickFalse) 3274 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image"); 3275 /* 3276 Relinquish resources. 3277 */ 3278 token=DestroyString(token); 3279 if (primitive_info != (PrimitiveInfo *) NULL) 3280 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info); 3281 primitive=DestroyString(primitive); 3282 for ( ; n >= 0; n--) 3283 graphic_context[n]=DestroyDrawInfo(graphic_context[n]); 3284 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context); 3285 if (status == MagickFalse) 3286 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition", 3287 keyword); 3288 return(status != 0 ? MagickTrue : MagickFalse); 3289 } 3290 3291 /* 3293 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3294 % % 3295 % % 3296 % % 3297 % D r a w G r a d i e n t I m a g e % 3298 % % 3299 % % 3300 % % 3301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3302 % 3303 % DrawGradientImage() draws a linear gradient on the image. 3304 % 3305 % The format of the DrawGradientImage method is: 3306 % 3307 % MagickBooleanType DrawGradientImage(Image *image, 3308 % const DrawInfo *draw_info,ExceptionInfo *exception) 3309 % 3310 % A description of each parameter follows: 3311 % 3312 % o image: the image. 3313 % 3314 % o draw_info: the draw info. 3315 % 3316 % o exception: return any errors or warnings in this structure. 3317 % 3318 */ 3319 3320 static inline double GetStopColorOffset(const GradientInfo *gradient, 3321 const ssize_t x,const ssize_t y) 3322 { 3323 switch (gradient->type) 3324 { 3325 case UndefinedGradient: 3326 case LinearGradient: 3327 { 3328 double 3329 gamma, 3330 length, 3331 offset, 3332 scale; 3333 3334 PointInfo 3335 p, 3336 q; 3337 3338 const SegmentInfo 3339 *gradient_vector; 3340 3341 gradient_vector=(&gradient->gradient_vector); 3342 p.x=gradient_vector->x2-gradient_vector->x1; 3343 p.y=gradient_vector->y2-gradient_vector->y1; 3344 q.x=(double) x-gradient_vector->x1; 3345 q.y=(double) y-gradient_vector->y1; 3346 length=sqrt(q.x*q.x+q.y*q.y); 3347 gamma=sqrt(p.x*p.x+p.y*p.y)*length; 3348 gamma=PerceptibleReciprocal(gamma); 3349 scale=p.x*q.x+p.y*q.y; 3350 offset=gamma*scale*length; 3351 return(offset); 3352 } 3353 case RadialGradient: 3354 { 3355 PointInfo 3356 v; 3357 3358 if (gradient->spread == RepeatSpread) 3359 { 3360 v.x=(double) x-gradient->center.x; 3361 v.y=(double) y-gradient->center.y; 3362 return(sqrt(v.x*v.x+v.y*v.y)); 3363 } 3364 v.x=(double) (((x-gradient->center.x)*cos(DegreesToRadians( 3365 gradient->angle)))+((y-gradient->center.y)*sin(DegreesToRadians( 3366 gradient->angle))))/gradient->radii.x; 3367 v.y=(double) (((x-gradient->center.x)*sin(DegreesToRadians( 3368 gradient->angle)))-((y-gradient->center.y)*cos(DegreesToRadians( 3369 gradient->angle))))/gradient->radii.y; 3370 return(sqrt(v.x*v.x+v.y*v.y)); 3371 } 3372 } 3373 return(0.0); 3374 } 3375 3376 static int StopInfoCompare(const void *x,const void *y) 3377 { 3378 StopInfo 3379 *stop_1, 3380 *stop_2; 3381 3382 stop_1=(StopInfo *) x; 3383 stop_2=(StopInfo *) y; 3384 if (stop_1->offset > stop_2->offset) 3385 return(1); 3386 if (fabs(stop_1->offset-stop_2->offset) <= DrawEpsilon) 3387 return(0); 3388 return(-1); 3389 } 3390 3391 MagickExport MagickBooleanType DrawGradientImage(Image *image, 3392 const DrawInfo *draw_info,ExceptionInfo *exception) 3393 { 3394 CacheView 3395 *image_view; 3396 3397 const GradientInfo 3398 *gradient; 3399 3400 const SegmentInfo 3401 *gradient_vector; 3402 3403 double 3404 length; 3405 3406 MagickBooleanType 3407 status; 3408 3409 PixelInfo 3410 zero; 3411 3412 PointInfo 3413 point; 3414 3415 RectangleInfo 3416 bounding_box; 3417 3418 ssize_t 3419 y; 3420 3421 /* 3422 Draw linear or radial gradient on image. 3423 */ 3424 assert(image != (Image *) NULL); 3425 assert(image->signature == MagickCoreSignature); 3426 if (image->debug != MagickFalse) 3427 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3428 assert(draw_info != (const DrawInfo *) NULL); 3429 gradient=(&draw_info->gradient); 3430 qsort(gradient->stops,gradient->number_stops,sizeof(StopInfo), 3431 StopInfoCompare); 3432 gradient_vector=(&gradient->gradient_vector); 3433 point.x=gradient_vector->x2-gradient_vector->x1; 3434 point.y=gradient_vector->y2-gradient_vector->y1; 3435 length=sqrt(point.x*point.x+point.y*point.y); 3436 bounding_box=gradient->bounding_box; 3437 status=MagickTrue; 3438 GetPixelInfo(image,&zero); 3439 image_view=AcquireAuthenticCacheView(image,exception); 3440 #if defined(MAGICKCORE_OPENMP_SUPPORT) 3441 #pragma omp parallel for schedule(static,4) shared(status) \ 3442 magick_threads(image,image,1,1) 3443 #endif 3444 for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++) 3445 { 3446 PixelInfo 3447 composite, 3448 pixel; 3449 3450 double 3451 alpha, 3452 offset; 3453 3454 register Quantum 3455 *magick_restrict q; 3456 3457 register ssize_t 3458 i, 3459 x; 3460 3461 ssize_t 3462 j; 3463 3464 if (status == MagickFalse) 3465 continue; 3466 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 3467 if (q == (Quantum *) NULL) 3468 { 3469 status=MagickFalse; 3470 continue; 3471 } 3472 pixel=zero; 3473 composite=zero; 3474 offset=GetStopColorOffset(gradient,0,y); 3475 if (gradient->type != RadialGradient) 3476 offset/=length; 3477 for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++) 3478 { 3479 GetPixelInfoPixel(image,q,&pixel); 3480 switch (gradient->spread) 3481 { 3482 case UndefinedSpread: 3483 case PadSpread: 3484 { 3485 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) || 3486 (y != (ssize_t) ceil(gradient_vector->y1-0.5))) 3487 { 3488 offset=GetStopColorOffset(gradient,x,y); 3489 if (gradient->type != RadialGradient) 3490 offset/=length; 3491 } 3492 for (i=0; i < (ssize_t) gradient->number_stops; i++) 3493 if (offset < gradient->stops[i].offset) 3494 break; 3495 if ((offset < 0.0) || (i == 0)) 3496 composite=gradient->stops[0].color; 3497 else 3498 if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops)) 3499 composite=gradient->stops[gradient->number_stops-1].color; 3500 else 3501 { 3502 j=i; 3503 i--; 3504 alpha=(offset-gradient->stops[i].offset)/ 3505 (gradient->stops[j].offset-gradient->stops[i].offset); 3506 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha, 3507 &gradient->stops[j].color,alpha,&composite); 3508 } 3509 break; 3510 } 3511 case ReflectSpread: 3512 { 3513 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) || 3514 (y != (ssize_t) ceil(gradient_vector->y1-0.5))) 3515 { 3516 offset=GetStopColorOffset(gradient,x,y); 3517 if (gradient->type != RadialGradient) 3518 offset/=length; 3519 } 3520 if (offset < 0.0) 3521 offset=(-offset); 3522 if ((ssize_t) fmod(offset,2.0) == 0) 3523 offset=fmod(offset,1.0); 3524 else 3525 offset=1.0-fmod(offset,1.0); 3526 for (i=0; i < (ssize_t) gradient->number_stops; i++) 3527 if (offset < gradient->stops[i].offset) 3528 break; 3529 if (i == 0) 3530 composite=gradient->stops[0].color; 3531 else 3532 if (i == (ssize_t) gradient->number_stops) 3533 composite=gradient->stops[gradient->number_stops-1].color; 3534 else 3535 { 3536 j=i; 3537 i--; 3538 alpha=(offset-gradient->stops[i].offset)/ 3539 (gradient->stops[j].offset-gradient->stops[i].offset); 3540 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha, 3541 &gradient->stops[j].color,alpha,&composite); 3542 } 3543 break; 3544 } 3545 case RepeatSpread: 3546 { 3547 MagickBooleanType 3548 antialias; 3549 3550 double 3551 repeat; 3552 3553 antialias=MagickFalse; 3554 repeat=0.0; 3555 if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) || 3556 (y != (ssize_t) ceil(gradient_vector->y1-0.5))) 3557 { 3558 offset=GetStopColorOffset(gradient,x,y); 3559 if (gradient->type == LinearGradient) 3560 { 3561 repeat=fmod(offset,length); 3562 if (repeat < 0.0) 3563 repeat=length-fmod(-repeat,length); 3564 else 3565 repeat=fmod(offset,length); 3566 antialias=(repeat < length) && ((repeat+1.0) > length) ? 3567 MagickTrue : MagickFalse; 3568 offset=repeat/length; 3569 } 3570 else 3571 { 3572 repeat=fmod(offset,gradient->radius); 3573 if (repeat < 0.0) 3574 repeat=gradient->radius-fmod(-repeat,gradient->radius); 3575 else 3576 repeat=fmod(offset,gradient->radius); 3577 antialias=repeat+1.0 > gradient->radius ? MagickTrue : 3578 MagickFalse; 3579 offset=repeat/gradient->radius; 3580 } 3581 } 3582 for (i=0; i < (ssize_t) gradient->number_stops; i++) 3583 if (offset < gradient->stops[i].offset) 3584 break; 3585 if (i == 0) 3586 composite=gradient->stops[0].color; 3587 else 3588 if (i == (ssize_t) gradient->number_stops) 3589 composite=gradient->stops[gradient->number_stops-1].color; 3590 else 3591 { 3592 j=i; 3593 i--; 3594 alpha=(offset-gradient->stops[i].offset)/ 3595 (gradient->stops[j].offset-gradient->stops[i].offset); 3596 if (antialias != MagickFalse) 3597 { 3598 if (gradient->type == LinearGradient) 3599 alpha=length-repeat; 3600 else 3601 alpha=gradient->radius-repeat; 3602 i=0; 3603 j=(ssize_t) gradient->number_stops-1L; 3604 } 3605 CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha, 3606 &gradient->stops[j].color,alpha,&composite); 3607 } 3608 break; 3609 } 3610 } 3611 CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha, 3612 &pixel); 3613 SetPixelViaPixelInfo(image,&pixel,q); 3614 q+=GetPixelChannels(image); 3615 } 3616 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 3617 status=MagickFalse; 3618 } 3619 image_view=DestroyCacheView(image_view); 3620 return(status); 3621 } 3622 3623 /* 3625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3626 % % 3627 % % 3628 % % 3629 % D r a w P a t t e r n P a t h % 3630 % % 3631 % % 3632 % % 3633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3634 % 3635 % DrawPatternPath() draws a pattern. 3636 % 3637 % The format of the DrawPatternPath method is: 3638 % 3639 % MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info, 3640 % const char *name,Image **pattern,ExceptionInfo *exception) 3641 % 3642 % A description of each parameter follows: 3643 % 3644 % o image: the image. 3645 % 3646 % o draw_info: the draw info. 3647 % 3648 % o name: the pattern name. 3649 % 3650 % o image: the image. 3651 % 3652 % o exception: return any errors or warnings in this structure. 3653 % 3654 */ 3655 MagickExport MagickBooleanType DrawPatternPath(Image *image, 3656 const DrawInfo *draw_info,const char *name,Image **pattern, 3657 ExceptionInfo *exception) 3658 { 3659 char 3660 property[MagickPathExtent]; 3661 3662 const char 3663 *geometry, 3664 *path, 3665 *type; 3666 3667 DrawInfo 3668 *clone_info; 3669 3670 ImageInfo 3671 *image_info; 3672 3673 MagickBooleanType 3674 status; 3675 3676 assert(image != (Image *) NULL); 3677 assert(image->signature == MagickCoreSignature); 3678 if (image->debug != MagickFalse) 3679 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3680 assert(draw_info != (const DrawInfo *) NULL); 3681 assert(name != (const char *) NULL); 3682 (void) FormatLocaleString(property,MagickPathExtent,"%s",name); 3683 path=GetImageArtifact(image,property); 3684 if (path == (const char *) NULL) 3685 return(MagickFalse); 3686 (void) FormatLocaleString(property,MagickPathExtent,"%s-geometry",name); 3687 geometry=GetImageArtifact(image,property); 3688 if (geometry == (const char *) NULL) 3689 return(MagickFalse); 3690 if ((*pattern) != (Image *) NULL) 3691 *pattern=DestroyImage(*pattern); 3692 image_info=AcquireImageInfo(); 3693 image_info->size=AcquireString(geometry); 3694 *pattern=AcquireImage(image_info,exception); 3695 image_info=DestroyImageInfo(image_info); 3696 (void) QueryColorCompliance("#000000ff",AllCompliance, 3697 &(*pattern)->background_color,exception); 3698 (void) SetImageBackgroundColor(*pattern,exception); 3699 if (image->debug != MagickFalse) 3700 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 3701 "begin pattern-path %s %s",name,geometry); 3702 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 3703 clone_info->fill_pattern=NewImageList(); 3704 clone_info->stroke_pattern=NewImageList(); 3705 (void) FormatLocaleString(property,MagickPathExtent,"%s-type",name); 3706 type=GetImageArtifact(image,property); 3707 if (type != (const char *) NULL) 3708 clone_info->gradient.type=(GradientType) ParseCommandOption( 3709 MagickGradientOptions,MagickFalse,type); 3710 (void) CloneString(&clone_info->primitive,path); 3711 status=DrawImage(*pattern,clone_info,exception); 3712 clone_info=DestroyDrawInfo(clone_info); 3713 if (image->debug != MagickFalse) 3714 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path"); 3715 return(status); 3716 } 3717 3718 /* 3720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3721 % % 3722 % % 3723 % % 3724 + D r a w P o l y g o n P r i m i t i v e % 3725 % % 3726 % % 3727 % % 3728 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3729 % 3730 % DrawPolygonPrimitive() draws a polygon on the image. 3731 % 3732 % The format of the DrawPolygonPrimitive method is: 3733 % 3734 % MagickBooleanType DrawPolygonPrimitive(Image *image, 3735 % const DrawInfo *draw_info,const PrimitiveInfo *primitive_info, 3736 % ExceptionInfo *exception) 3737 % 3738 % A description of each parameter follows: 3739 % 3740 % o image: the image. 3741 % 3742 % o draw_info: the draw info. 3743 % 3744 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure. 3745 % 3746 % o exception: return any errors or warnings in this structure. 3747 % 3748 */ 3749 3750 static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info) 3751 { 3752 register ssize_t 3753 i; 3754 3755 assert(polygon_info != (PolygonInfo **) NULL); 3756 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++) 3757 if (polygon_info[i] != (PolygonInfo *) NULL) 3758 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]); 3759 polygon_info=(PolygonInfo **) RelinquishMagickMemory(polygon_info); 3760 return(polygon_info); 3761 } 3762 3763 static PolygonInfo **AcquirePolygonThreadSet( 3764 const PrimitiveInfo *primitive_info) 3765 { 3766 PathInfo 3767 *magick_restrict path_info; 3768 3769 PolygonInfo 3770 **polygon_info; 3771 3772 register ssize_t 3773 i; 3774 3775 size_t 3776 number_threads; 3777 3778 number_threads=(size_t) GetMagickResourceLimit(ThreadResource); 3779 polygon_info=(PolygonInfo **) AcquireQuantumMemory(number_threads, 3780 sizeof(*polygon_info)); 3781 if (polygon_info == (PolygonInfo **) NULL) 3782 return((PolygonInfo **) NULL); 3783 (void) ResetMagickMemory(polygon_info,0,number_threads*sizeof(*polygon_info)); 3784 path_info=ConvertPrimitiveToPath(primitive_info); 3785 if (path_info == (PathInfo *) NULL) 3786 return(DestroyPolygonThreadSet(polygon_info)); 3787 for (i=0; i < (ssize_t) number_threads; i++) 3788 { 3789 polygon_info[i]=ConvertPathToPolygon(path_info); 3790 if (polygon_info[i] == (PolygonInfo *) NULL) 3791 return(DestroyPolygonThreadSet(polygon_info)); 3792 } 3793 path_info=(PathInfo *) RelinquishMagickMemory(path_info); 3794 return(polygon_info); 3795 } 3796 3797 static double GetFillAlpha(PolygonInfo *polygon_info,const double mid, 3798 const MagickBooleanType fill,const FillRule fill_rule,const ssize_t x, 3799 const ssize_t y,double *stroke_alpha) 3800 { 3801 double 3802 alpha, 3803 beta, 3804 distance, 3805 subpath_alpha; 3806 3807 PointInfo 3808 delta; 3809 3810 register const PointInfo 3811 *q; 3812 3813 register EdgeInfo 3814 *p; 3815 3816 register ssize_t 3817 i; 3818 3819 ssize_t 3820 j, 3821 winding_number; 3822 3823 /* 3824 Compute fill & stroke opacity for this (x,y) point. 3825 */ 3826 *stroke_alpha=0.0; 3827 subpath_alpha=0.0; 3828 p=polygon_info->edges; 3829 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++) 3830 { 3831 if ((double) y <= (p->bounds.y1-mid-0.5)) 3832 break; 3833 if ((double) y > (p->bounds.y2+mid+0.5)) 3834 { 3835 (void) DestroyEdge(polygon_info,(size_t) j); 3836 continue; 3837 } 3838 if (((double) x <= (p->bounds.x1-mid-0.5)) || 3839 ((double) x > (p->bounds.x2+mid+0.5))) 3840 continue; 3841 i=(ssize_t) MagickMax((double) p->highwater,1.0); 3842 for ( ; i < (ssize_t) p->number_points; i++) 3843 { 3844 if ((double) y <= (p->points[i-1].y-mid-0.5)) 3845 break; 3846 if ((double) y > (p->points[i].y+mid+0.5)) 3847 continue; 3848 if (p->scanline != (double) y) 3849 { 3850 p->scanline=(double) y; 3851 p->highwater=(size_t) i; 3852 } 3853 /* 3854 Compute distance between a point and an edge. 3855 */ 3856 q=p->points+i-1; 3857 delta.x=(q+1)->x-q->x; 3858 delta.y=(q+1)->y-q->y; 3859 beta=delta.x*(x-q->x)+delta.y*(y-q->y); 3860 if (beta < 0.0) 3861 { 3862 delta.x=(double) x-q->x; 3863 delta.y=(double) y-q->y; 3864 distance=delta.x*delta.x+delta.y*delta.y; 3865 } 3866 else 3867 { 3868 alpha=delta.x*delta.x+delta.y*delta.y; 3869 if (beta > alpha) 3870 { 3871 delta.x=(double) x-(q+1)->x; 3872 delta.y=(double) y-(q+1)->y; 3873 distance=delta.x*delta.x+delta.y*delta.y; 3874 } 3875 else 3876 { 3877 alpha=1.0/alpha; 3878 beta=delta.x*(y-q->y)-delta.y*(x-q->x); 3879 distance=alpha*beta*beta; 3880 } 3881 } 3882 /* 3883 Compute stroke & subpath opacity. 3884 */ 3885 beta=0.0; 3886 if (p->ghostline == MagickFalse) 3887 { 3888 alpha=mid+0.5; 3889 if ((*stroke_alpha < 1.0) && 3890 (distance <= ((alpha+0.25)*(alpha+0.25)))) 3891 { 3892 alpha=mid-0.5; 3893 if (distance <= ((alpha+0.25)*(alpha+0.25))) 3894 *stroke_alpha=1.0; 3895 else 3896 { 3897 beta=1.0; 3898 if (fabs(distance-1.0) >= DrawEpsilon) 3899 beta=sqrt((double) distance); 3900 alpha=beta-mid-0.5; 3901 if (*stroke_alpha < ((alpha-0.25)*(alpha-0.25))) 3902 *stroke_alpha=(alpha-0.25)*(alpha-0.25); 3903 } 3904 } 3905 } 3906 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_alpha >= 1.0)) 3907 continue; 3908 if (distance <= 0.0) 3909 { 3910 subpath_alpha=1.0; 3911 continue; 3912 } 3913 if (distance > 1.0) 3914 continue; 3915 if (fabs(beta) < DrawEpsilon) 3916 { 3917 beta=1.0; 3918 if (fabs(distance-1.0) >= DrawEpsilon) 3919 beta=sqrt(distance); 3920 } 3921 alpha=beta-1.0; 3922 if (subpath_alpha < (alpha*alpha)) 3923 subpath_alpha=alpha*alpha; 3924 } 3925 } 3926 /* 3927 Compute fill opacity. 3928 */ 3929 if (fill == MagickFalse) 3930 return(0.0); 3931 if (subpath_alpha >= 1.0) 3932 return(1.0); 3933 /* 3934 Determine winding number. 3935 */ 3936 winding_number=0; 3937 p=polygon_info->edges; 3938 for (j=0; j < (ssize_t) polygon_info->number_edges; j++, p++) 3939 { 3940 if ((double) y <= p->bounds.y1) 3941 break; 3942 if (((double) y > p->bounds.y2) || ((double) x <= p->bounds.x1)) 3943 continue; 3944 if ((double) x > p->bounds.x2) 3945 { 3946 winding_number+=p->direction ? 1 : -1; 3947 continue; 3948 } 3949 i=(ssize_t) MagickMax((double) p->highwater,1.0); 3950 for ( ; i < (ssize_t) p->number_points; i++) 3951 if ((double) y <= p->points[i].y) 3952 break; 3953 q=p->points+i-1; 3954 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x))) 3955 winding_number+=p->direction ? 1 : -1; 3956 } 3957 if (fill_rule != NonZeroRule) 3958 { 3959 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0) 3960 return(1.0); 3961 } 3962 else 3963 if (MagickAbsoluteValue(winding_number) != 0) 3964 return(1.0); 3965 return(subpath_alpha); 3966 } 3967 3968 static MagickBooleanType DrawPolygonPrimitive(Image *image, 3969 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info, 3970 ExceptionInfo *exception) 3971 { 3972 CacheView 3973 *image_view; 3974 3975 MagickBooleanType 3976 fill, 3977 status; 3978 3979 double 3980 mid; 3981 3982 PolygonInfo 3983 **magick_restrict polygon_info; 3984 3985 register EdgeInfo 3986 *p; 3987 3988 register ssize_t 3989 i; 3990 3991 SegmentInfo 3992 bounds; 3993 3994 ssize_t 3995 start_y, 3996 stop_y, 3997 y; 3998 3999 /* 4000 Compute bounding box. 4001 */ 4002 assert(image != (Image *) NULL); 4003 assert(image->signature == MagickCoreSignature); 4004 if (image->debug != MagickFalse) 4005 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 4006 assert(draw_info != (DrawInfo *) NULL); 4007 assert(draw_info->signature == MagickCoreSignature); 4008 assert(primitive_info != (PrimitiveInfo *) NULL); 4009 if (primitive_info->coordinates == 0) 4010 return(MagickTrue); 4011 polygon_info=AcquirePolygonThreadSet(primitive_info); 4012 if (polygon_info == (PolygonInfo **) NULL) 4013 return(MagickFalse); 4014 DisableMSCWarning(4127) 4015 if (0) 4016 DrawBoundingRectangles(image,draw_info,polygon_info[0],exception); 4017 RestoreMSCWarning 4018 if (image->debug != MagickFalse) 4019 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon"); 4020 fill=(primitive_info->method == FillToBorderMethod) || 4021 (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse; 4022 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0; 4023 bounds=polygon_info[0]->edges[0].bounds; 4024 for (i=1; i < (ssize_t) polygon_info[0]->number_edges; i++) 4025 { 4026 p=polygon_info[0]->edges+i; 4027 if (p->bounds.x1 < bounds.x1) 4028 bounds.x1=p->bounds.x1; 4029 if (p->bounds.y1 < bounds.y1) 4030 bounds.y1=p->bounds.y1; 4031 if (p->bounds.x2 > bounds.x2) 4032 bounds.x2=p->bounds.x2; 4033 if (p->bounds.y2 > bounds.y2) 4034 bounds.y2=p->bounds.y2; 4035 } 4036 bounds.x1-=(mid+1.0); 4037 bounds.x1=bounds.x1 < 0.0 ? 0.0 : (size_t) ceil(bounds.x1-0.5) >= 4038 image->columns ? (double) image->columns-1 : bounds.x1; 4039 bounds.y1-=(mid+1.0); 4040 bounds.y1=bounds.y1 < 0.0 ? 0.0 : (size_t) ceil(bounds.y1-0.5) >= 4041 image->rows ? (double) image->rows-1 : bounds.y1; 4042 bounds.x2+=(mid+1.0); 4043 bounds.x2=bounds.x2 < 0.0 ? 0.0 : (size_t) floor(bounds.x2+0.5) >= 4044 image->columns ? (double) image->columns-1 : bounds.x2; 4045 bounds.y2+=(mid+1.0); 4046 bounds.y2=bounds.y2 < 0.0 ? 0.0 : (size_t) floor(bounds.y2+0.5) >= 4047 image->rows ? (double) image->rows-1 : bounds.y2; 4048 status=MagickTrue; 4049 image_view=AcquireAuthenticCacheView(image,exception); 4050 if ((primitive_info->coordinates == 1) || 4051 (polygon_info[0]->number_edges == 0)) 4052 { 4053 /* 4054 Draw point. 4055 */ 4056 start_y=(ssize_t) ceil(bounds.y1-0.5); 4057 stop_y=(ssize_t) floor(bounds.y2+0.5); 4058 #if defined(MAGICKCORE_OPENMP_SUPPORT) 4059 #pragma omp parallel for schedule(static,4) shared(status) \ 4060 magick_threads(image,image,1,1) 4061 #endif 4062 for (y=start_y; y <= stop_y; y++) 4063 { 4064 MagickBooleanType 4065 sync; 4066 4067 PixelInfo 4068 pixel; 4069 4070 register ssize_t 4071 x; 4072 4073 register Quantum 4074 *magick_restrict q; 4075 4076 ssize_t 4077 start_x, 4078 stop_x; 4079 4080 if (status == MagickFalse) 4081 continue; 4082 start_x=(ssize_t) ceil(bounds.x1-0.5); 4083 stop_x=(ssize_t) floor(bounds.x2+0.5); 4084 x=start_x; 4085 q=GetCacheViewAuthenticPixels(image_view,x,y,(size_t) (stop_x-x+1),1, 4086 exception); 4087 if (q == (Quantum *) NULL) 4088 { 4089 status=MagickFalse; 4090 continue; 4091 } 4092 GetPixelInfo(image,&pixel); 4093 for ( ; x <= stop_x; x++) 4094 { 4095 if ((x == (ssize_t) ceil(primitive_info->point.x-0.5)) && 4096 (y == (ssize_t) ceil(primitive_info->point.y-0.5))) 4097 { 4098 GetFillColor(draw_info,x-start_x,y-start_y,&pixel,exception); 4099 SetPixelViaPixelInfo(image,&pixel,q); 4100 } 4101 q+=GetPixelChannels(image); 4102 } 4103 sync=SyncCacheViewAuthenticPixels(image_view,exception); 4104 if (sync == MagickFalse) 4105 status=MagickFalse; 4106 } 4107 image_view=DestroyCacheView(image_view); 4108 polygon_info=DestroyPolygonThreadSet(polygon_info); 4109 if (image->debug != MagickFalse) 4110 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4111 " end draw-polygon"); 4112 return(status); 4113 } 4114 /* 4115 Draw polygon or line. 4116 */ 4117 if (image->alpha_trait == UndefinedPixelTrait) 4118 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); 4119 start_y=(ssize_t) ceil(bounds.y1-0.5); 4120 stop_y=(ssize_t) floor(bounds.y2+0.5); 4121 #if defined(MAGICKCORE_OPENMP_SUPPORT) 4122 #pragma omp parallel for schedule(static,4) shared(status) \ 4123 magick_threads(image,image,1,1) 4124 #endif 4125 for (y=start_y; y <= stop_y; y++) 4126 { 4127 const int 4128 id = GetOpenMPThreadId(); 4129 4130 double 4131 fill_alpha, 4132 stroke_alpha; 4133 4134 PixelInfo 4135 fill_color, 4136 stroke_color; 4137 4138 register Quantum 4139 *magick_restrict q; 4140 4141 register ssize_t 4142 x; 4143 4144 ssize_t 4145 start_x, 4146 stop_x; 4147 4148 if (status == MagickFalse) 4149 continue; 4150 start_x=(ssize_t) ceil(bounds.x1-0.5); 4151 stop_x=(ssize_t) floor(bounds.x2+0.5); 4152 q=GetCacheViewAuthenticPixels(image_view,start_x,y,(size_t) (stop_x-start_x+1),1, 4153 exception); 4154 if (q == (Quantum *) NULL) 4155 { 4156 status=MagickFalse; 4157 continue; 4158 } 4159 for (x=start_x; x <= stop_x; x++) 4160 { 4161 /* 4162 Fill and/or stroke. 4163 */ 4164 fill_alpha=GetFillAlpha(polygon_info[id],mid,fill,draw_info->fill_rule, 4165 x,y,&stroke_alpha); 4166 if (draw_info->stroke_antialias == MagickFalse) 4167 { 4168 fill_alpha=fill_alpha > 0.25 ? 1.0 : 0.0; 4169 stroke_alpha=stroke_alpha > 0.25 ? 1.0 : 0.0; 4170 } 4171 GetFillColor(draw_info,x-start_x,y-start_y,&fill_color,exception); 4172 fill_alpha=fill_alpha*fill_color.alpha; 4173 CompositePixelOver(image,&fill_color,fill_alpha,q,(double) 4174 GetPixelAlpha(image,q),q); 4175 GetStrokeColor(draw_info,x-start_x,y-start_y,&stroke_color,exception); 4176 stroke_alpha=stroke_alpha*stroke_color.alpha; 4177 CompositePixelOver(image,&stroke_color,stroke_alpha,q,(double) 4178 GetPixelAlpha(image,q),q); 4179 q+=GetPixelChannels(image); 4180 } 4181 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 4182 status=MagickFalse; 4183 } 4184 image_view=DestroyCacheView(image_view); 4185 polygon_info=DestroyPolygonThreadSet(polygon_info); 4186 if (image->debug != MagickFalse) 4187 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon"); 4188 return(status); 4189 } 4190 4191 /* 4193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4194 % % 4195 % % 4196 % % 4197 % D r a w P r i m i t i v e % 4198 % % 4199 % % 4200 % % 4201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4202 % 4203 % DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image. 4204 % 4205 % The format of the DrawPrimitive method is: 4206 % 4207 % MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info, 4208 % PrimitiveInfo *primitive_info,ExceptionInfo *exception) 4209 % 4210 % A description of each parameter follows: 4211 % 4212 % o image: the image. 4213 % 4214 % o draw_info: the draw info. 4215 % 4216 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure. 4217 % 4218 % o exception: return any errors or warnings in this structure. 4219 % 4220 */ 4221 4222 static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info) 4223 { 4224 const char 4225 *methods[] = 4226 { 4227 "point", 4228 "replace", 4229 "floodfill", 4230 "filltoborder", 4231 "reset", 4232 "?" 4233 }; 4234 4235 PointInfo 4236 p, 4237 q, 4238 point; 4239 4240 register ssize_t 4241 i, 4242 x; 4243 4244 ssize_t 4245 coordinates, 4246 y; 4247 4248 x=(ssize_t) ceil(primitive_info->point.x-0.5); 4249 y=(ssize_t) ceil(primitive_info->point.y-0.5); 4250 switch (primitive_info->primitive) 4251 { 4252 case AlphaPrimitive: 4253 { 4254 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4255 "AlphaPrimitive %.20g,%.20g %s",(double) x,(double) y, 4256 methods[primitive_info->method]); 4257 return; 4258 } 4259 case ColorPrimitive: 4260 { 4261 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4262 "ColorPrimitive %.20g,%.20g %s",(double) x,(double) y, 4263 methods[primitive_info->method]); 4264 return; 4265 } 4266 case ImagePrimitive: 4267 { 4268 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4269 "ImagePrimitive %.20g,%.20g",(double) x,(double) y); 4270 return; 4271 } 4272 case PointPrimitive: 4273 { 4274 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4275 "PointPrimitive %.20g,%.20g %s",(double) x,(double) y, 4276 methods[primitive_info->method]); 4277 return; 4278 } 4279 case TextPrimitive: 4280 { 4281 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4282 "TextPrimitive %.20g,%.20g",(double) x,(double) y); 4283 return; 4284 } 4285 default: 4286 break; 4287 } 4288 coordinates=0; 4289 p=primitive_info[0].point; 4290 q.x=(-1.0); 4291 q.y=(-1.0); 4292 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) 4293 { 4294 point=primitive_info[i].point; 4295 if (coordinates <= 0) 4296 { 4297 coordinates=(ssize_t) primitive_info[i].coordinates; 4298 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4299 " begin open (%.20g)",(double) coordinates); 4300 p=point; 4301 } 4302 point=primitive_info[i].point; 4303 if ((fabs(q.x-point.x) >= DrawEpsilon) || 4304 (fabs(q.y-point.y) >= DrawEpsilon)) 4305 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4306 " %.20g: %.18g,%.18g",(double) coordinates,point.x,point.y); 4307 else 4308 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4309 " %.20g: %g %g (duplicate)",(double) coordinates,point.x,point.y); 4310 q=point; 4311 coordinates--; 4312 if (coordinates > 0) 4313 continue; 4314 if ((fabs(p.x-point.x) >= DrawEpsilon) || 4315 (fabs(p.y-point.y) >= DrawEpsilon)) 4316 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%.20g)", 4317 (double) coordinates); 4318 else 4319 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%.20g)", 4320 (double) coordinates); 4321 } 4322 } 4323 4324 MagickExport MagickBooleanType DrawPrimitive(Image *image, 4325 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info, 4326 ExceptionInfo *exception) 4327 { 4328 CacheView 4329 *image_view; 4330 4331 MagickStatusType 4332 status; 4333 4334 register ssize_t 4335 i, 4336 x; 4337 4338 ssize_t 4339 y; 4340 4341 if (image->debug != MagickFalse) 4342 { 4343 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4344 " begin draw-primitive"); 4345 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4346 " affine: %g,%g,%g,%g,%g,%g",draw_info->affine.sx, 4347 draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy, 4348 draw_info->affine.tx,draw_info->affine.ty); 4349 } 4350 if ((IsGrayColorspace(image->colorspace) != MagickFalse) && 4351 ((IsPixelInfoGray(&draw_info->fill) == MagickFalse) || 4352 (IsPixelInfoGray(&draw_info->stroke) == MagickFalse))) 4353 (void) SetImageColorspace(image,sRGBColorspace,exception); 4354 status=MagickTrue; 4355 x=(ssize_t) ceil(primitive_info->point.x-0.5); 4356 y=(ssize_t) ceil(primitive_info->point.y-0.5); 4357 image_view=AcquireAuthenticCacheView(image,exception); 4358 switch (primitive_info->primitive) 4359 { 4360 case AlphaPrimitive: 4361 { 4362 if (image->alpha_trait == UndefinedPixelTrait) 4363 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); 4364 switch (primitive_info->method) 4365 { 4366 case PointMethod: 4367 default: 4368 { 4369 PixelInfo 4370 pixel; 4371 4372 register Quantum 4373 *q; 4374 4375 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception); 4376 if (q == (Quantum *) NULL) 4377 break; 4378 GetFillColor(draw_info,x,y,&pixel,exception); 4379 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q); 4380 (void) SyncCacheViewAuthenticPixels(image_view,exception); 4381 break; 4382 } 4383 case ReplaceMethod: 4384 { 4385 MagickBooleanType 4386 sync; 4387 4388 PixelInfo 4389 pixel, 4390 target; 4391 4392 (void) GetOneCacheViewVirtualPixelInfo(image_view,x,y,&target, 4393 exception); 4394 GetPixelInfo(image,&pixel); 4395 for (y=0; y < (ssize_t) image->rows; y++) 4396 { 4397 register Quantum 4398 *magick_restrict q; 4399 4400 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 4401 exception); 4402 if (q == (Quantum *) NULL) 4403 break; 4404 for (x=0; x < (ssize_t) image->columns; x++) 4405 { 4406 GetPixelInfoPixel(image,q,&pixel); 4407 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse) 4408 { 4409 q+=GetPixelChannels(image); 4410 continue; 4411 } 4412 GetFillColor(draw_info,x,y,&pixel,exception); 4413 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q); 4414 q+=GetPixelChannels(image); 4415 } 4416 sync=SyncCacheViewAuthenticPixels(image_view,exception); 4417 if (sync == MagickFalse) 4418 break; 4419 } 4420 break; 4421 } 4422 case FloodfillMethod: 4423 case FillToBorderMethod: 4424 { 4425 ChannelType 4426 channel_mask; 4427 4428 PixelInfo 4429 target; 4430 4431 (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y, 4432 &target,exception); 4433 if (primitive_info->method == FillToBorderMethod) 4434 { 4435 target.red=(double) draw_info->border_color.red; 4436 target.green=(double) draw_info->border_color.green; 4437 target.blue=(double) draw_info->border_color.blue; 4438 } 4439 channel_mask=SetImageChannelMask(image,AlphaChannel); 4440 status&=FloodfillPaintImage(image,draw_info,&target,x,y, 4441 primitive_info->method == FloodfillMethod ? MagickFalse : 4442 MagickTrue,exception); 4443 (void) SetImageChannelMask(image,channel_mask); 4444 break; 4445 } 4446 case ResetMethod: 4447 { 4448 MagickBooleanType 4449 sync; 4450 4451 PixelInfo 4452 pixel; 4453 4454 for (y=0; y < (ssize_t) image->rows; y++) 4455 { 4456 register Quantum 4457 *magick_restrict q; 4458 4459 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 4460 exception); 4461 if (q == (Quantum *) NULL) 4462 break; 4463 for (x=0; x < (ssize_t) image->columns; x++) 4464 { 4465 GetFillColor(draw_info,x,y,&pixel,exception); 4466 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q); 4467 q+=GetPixelChannels(image); 4468 } 4469 sync=SyncCacheViewAuthenticPixels(image_view,exception); 4470 if (sync == MagickFalse) 4471 break; 4472 } 4473 break; 4474 } 4475 } 4476 break; 4477 } 4478 case ColorPrimitive: 4479 { 4480 switch (primitive_info->method) 4481 { 4482 case PointMethod: 4483 default: 4484 { 4485 PixelInfo 4486 pixel; 4487 4488 register Quantum 4489 *q; 4490 4491 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception); 4492 if (q == (Quantum *) NULL) 4493 break; 4494 GetPixelInfo(image,&pixel); 4495 GetFillColor(draw_info,x,y,&pixel,exception); 4496 SetPixelViaPixelInfo(image,&pixel,q); 4497 (void) SyncCacheViewAuthenticPixels(image_view,exception); 4498 break; 4499 } 4500 case ReplaceMethod: 4501 { 4502 MagickBooleanType 4503 sync; 4504 4505 PixelInfo 4506 pixel, 4507 target; 4508 4509 (void) GetOneCacheViewVirtualPixelInfo(image_view,x,y,&target, 4510 exception); 4511 for (y=0; y < (ssize_t) image->rows; y++) 4512 { 4513 register Quantum 4514 *magick_restrict q; 4515 4516 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 4517 exception); 4518 if (q == (Quantum *) NULL) 4519 break; 4520 for (x=0; x < (ssize_t) image->columns; x++) 4521 { 4522 GetPixelInfoPixel(image,q,&pixel); 4523 if (IsFuzzyEquivalencePixelInfo(&pixel,&target) == MagickFalse) 4524 { 4525 q+=GetPixelChannels(image); 4526 continue; 4527 } 4528 GetFillColor(draw_info,x,y,&pixel,exception); 4529 SetPixelViaPixelInfo(image,&pixel,q); 4530 q+=GetPixelChannels(image); 4531 } 4532 sync=SyncCacheViewAuthenticPixels(image_view,exception); 4533 if (sync == MagickFalse) 4534 break; 4535 } 4536 break; 4537 } 4538 case FloodfillMethod: 4539 case FillToBorderMethod: 4540 { 4541 PixelInfo 4542 target; 4543 4544 (void) GetOneVirtualPixelInfo(image,TileVirtualPixelMethod,x,y, 4545 &target,exception); 4546 if (primitive_info->method == FillToBorderMethod) 4547 { 4548 target.red=(double) draw_info->border_color.red; 4549 target.green=(double) draw_info->border_color.green; 4550 target.blue=(double) draw_info->border_color.blue; 4551 } 4552 status&=FloodfillPaintImage(image,draw_info,&target,x,y, 4553 primitive_info->method == FloodfillMethod ? MagickFalse : 4554 MagickTrue,exception); 4555 break; 4556 } 4557 case ResetMethod: 4558 { 4559 MagickBooleanType 4560 sync; 4561 4562 PixelInfo 4563 pixel; 4564 4565 GetPixelInfo(image,&pixel); 4566 for (y=0; y < (ssize_t) image->rows; y++) 4567 { 4568 register Quantum 4569 *magick_restrict q; 4570 4571 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 4572 exception); 4573 if (q == (Quantum *) NULL) 4574 break; 4575 for (x=0; x < (ssize_t) image->columns; x++) 4576 { 4577 GetFillColor(draw_info,x,y,&pixel,exception); 4578 SetPixelViaPixelInfo(image,&pixel,q); 4579 q+=GetPixelChannels(image); 4580 } 4581 sync=SyncCacheViewAuthenticPixels(image_view,exception); 4582 if (sync == MagickFalse) 4583 break; 4584 } 4585 break; 4586 } 4587 } 4588 break; 4589 } 4590 case ImagePrimitive: 4591 { 4592 AffineMatrix 4593 affine; 4594 4595 char 4596 composite_geometry[MagickPathExtent]; 4597 4598 Image 4599 *composite_image; 4600 4601 ImageInfo 4602 *clone_info; 4603 4604 RectangleInfo 4605 geometry; 4606 4607 ssize_t 4608 x1, 4609 y1; 4610 4611 if (primitive_info->text == (char *) NULL) 4612 break; 4613 clone_info=AcquireImageInfo(); 4614 if (LocaleNCompare(primitive_info->text,"data:",5) == 0) 4615 composite_image=ReadInlineImage(clone_info,primitive_info->text, 4616 exception); 4617 else 4618 { 4619 (void) CopyMagickString(clone_info->filename,primitive_info->text, 4620 MagickPathExtent); 4621 composite_image=ReadImage(clone_info,exception); 4622 } 4623 clone_info=DestroyImageInfo(clone_info); 4624 if (composite_image == (Image *) NULL) 4625 break; 4626 (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor) 4627 NULL,(void *) NULL); 4628 x1=(ssize_t) ceil(primitive_info[1].point.x-0.5); 4629 y1=(ssize_t) ceil(primitive_info[1].point.y-0.5); 4630 if (((x1 != 0L) && (x1 != (ssize_t) composite_image->columns)) || 4631 ((y1 != 0L) && (y1 != (ssize_t) composite_image->rows))) 4632 { 4633 /* 4634 Resize image. 4635 */ 4636 (void) FormatLocaleString(composite_geometry,MagickPathExtent, 4637 "%gx%g!",primitive_info[1].point.x,primitive_info[1].point.y); 4638 composite_image->filter=image->filter; 4639 (void) TransformImage(&composite_image,(char *) NULL, 4640 composite_geometry,exception); 4641 } 4642 if (composite_image->alpha_trait == UndefinedPixelTrait) 4643 (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel, 4644 exception); 4645 if (draw_info->alpha != OpaqueAlpha) 4646 (void) SetImageAlpha(composite_image,draw_info->alpha,exception); 4647 SetGeometry(image,&geometry); 4648 image->gravity=draw_info->gravity; 4649 geometry.x=x; 4650 geometry.y=y; 4651 (void) FormatLocaleString(composite_geometry,MagickPathExtent, 4652 "%.20gx%.20g%+.20g%+.20g",(double) composite_image->columns,(double) 4653 composite_image->rows,(double) geometry.x,(double) geometry.y); 4654 (void) ParseGravityGeometry(image,composite_geometry,&geometry,exception); 4655 affine=draw_info->affine; 4656 affine.tx=(double) geometry.x; 4657 affine.ty=(double) geometry.y; 4658 composite_image->interpolate=image->interpolate; 4659 if (draw_info->compose == OverCompositeOp) 4660 (void) DrawAffineImage(image,composite_image,&affine,exception); 4661 else 4662 (void) CompositeImage(image,composite_image,draw_info->compose, 4663 MagickTrue,geometry.x,geometry.y,exception); 4664 composite_image=DestroyImage(composite_image); 4665 break; 4666 } 4667 case PointPrimitive: 4668 { 4669 PixelInfo 4670 fill_color; 4671 4672 register Quantum 4673 *q; 4674 4675 if ((y < 0) || (y >= (ssize_t) image->rows)) 4676 break; 4677 if ((x < 0) || (x >= (ssize_t) image->columns)) 4678 break; 4679 q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception); 4680 if (q == (Quantum *) NULL) 4681 break; 4682 GetFillColor(draw_info,x,y,&fill_color,exception); 4683 CompositePixelOver(image,&fill_color,(double) fill_color.alpha,q, 4684 (double) GetPixelAlpha(image,q),q); 4685 (void) SyncCacheViewAuthenticPixels(image_view,exception); 4686 break; 4687 } 4688 case TextPrimitive: 4689 { 4690 char 4691 geometry[MagickPathExtent]; 4692 4693 DrawInfo 4694 *clone_info; 4695 4696 if (primitive_info->text == (char *) NULL) 4697 break; 4698 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 4699 (void) CloneString(&clone_info->text,primitive_info->text); 4700 (void) FormatLocaleString(geometry,MagickPathExtent,"%+f%+f", 4701 primitive_info->point.x,primitive_info->point.y); 4702 (void) CloneString(&clone_info->geometry,geometry); 4703 status&=AnnotateImage(image,clone_info,exception); 4704 clone_info=DestroyDrawInfo(clone_info); 4705 break; 4706 } 4707 default: 4708 { 4709 double 4710 mid, 4711 scale; 4712 4713 DrawInfo 4714 *clone_info; 4715 4716 if (IsEventLogging() != MagickFalse) 4717 LogPrimitiveInfo(primitive_info); 4718 scale=ExpandAffine(&draw_info->affine); 4719 if ((draw_info->dash_pattern != (double *) NULL) && 4720 (fabs(draw_info->dash_pattern[0]) >= DrawEpsilon) && 4721 (fabs(scale*draw_info->stroke_width) >= DrawEpsilon) && 4722 (draw_info->stroke.alpha != (Quantum) TransparentAlpha)) 4723 { 4724 /* 4725 Draw dash polygon. 4726 */ 4727 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 4728 clone_info->stroke_width=0.0; 4729 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha; 4730 status&=DrawPolygonPrimitive(image,clone_info,primitive_info, 4731 exception); 4732 clone_info=DestroyDrawInfo(clone_info); 4733 (void) DrawDashPolygon(draw_info,primitive_info,image,exception); 4734 break; 4735 } 4736 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0; 4737 if ((mid > 1.0) && 4738 ((draw_info->stroke.alpha != (Quantum) TransparentAlpha) || 4739 (draw_info->stroke_pattern != (Image *) NULL))) 4740 { 4741 MagickBooleanType 4742 closed_path; 4743 4744 /* 4745 Draw strokes while respecting line cap/join attributes. 4746 */ 4747 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ; 4748 closed_path= 4749 (fabs(primitive_info[i-1].point.x-primitive_info[0].point.x) < DrawEpsilon) && 4750 (fabs(primitive_info[i-1].point.y-primitive_info[0].point.y) < DrawEpsilon) ? 4751 MagickTrue : MagickFalse; 4752 i=(ssize_t) primitive_info[0].coordinates; 4753 if (((closed_path != MagickFalse) && 4754 (draw_info->linejoin == RoundJoin)) || 4755 (primitive_info[i].primitive != UndefinedPrimitive)) 4756 { 4757 (void) DrawPolygonPrimitive(image,draw_info,primitive_info, 4758 exception); 4759 break; 4760 } 4761 if (draw_info->linecap == RoundCap) 4762 { 4763 (void) DrawPolygonPrimitive(image,draw_info,primitive_info, 4764 exception); 4765 break; 4766 } 4767 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 4768 clone_info->stroke_width=0.0; 4769 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha; 4770 status&=DrawPolygonPrimitive(image,clone_info,primitive_info, 4771 exception); 4772 clone_info=DestroyDrawInfo(clone_info); 4773 status&=DrawStrokePolygon(image,draw_info,primitive_info,exception); 4774 break; 4775 } 4776 status&=DrawPolygonPrimitive(image,draw_info,primitive_info,exception); 4777 break; 4778 } 4779 } 4780 image_view=DestroyCacheView(image_view); 4781 if (image->debug != MagickFalse) 4782 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive"); 4783 return(status != 0 ? MagickTrue : MagickFalse); 4784 } 4785 4786 /* 4788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4789 % % 4790 % % 4791 % % 4792 + D r a w S t r o k e P o l y g o n % 4793 % % 4794 % % 4795 % % 4796 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4797 % 4798 % DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on 4799 % the image while respecting the line cap and join attributes. 4800 % 4801 % The format of the DrawStrokePolygon method is: 4802 % 4803 % MagickBooleanType DrawStrokePolygon(Image *image, 4804 % const DrawInfo *draw_info,const PrimitiveInfo *primitive_info) 4805 % 4806 % A description of each parameter follows: 4807 % 4808 % o image: the image. 4809 % 4810 % o draw_info: the draw info. 4811 % 4812 % o primitive_info: Specifies a pointer to a PrimitiveInfo structure. 4813 % 4814 % 4815 */ 4816 4817 static void DrawRoundLinecap(Image *image,const DrawInfo *draw_info, 4818 const PrimitiveInfo *primitive_info,ExceptionInfo *exception) 4819 { 4820 PrimitiveInfo 4821 linecap[5]; 4822 4823 register ssize_t 4824 i; 4825 4826 for (i=0; i < 4; i++) 4827 linecap[i]=(*primitive_info); 4828 linecap[0].coordinates=4; 4829 linecap[1].point.x+=2.0*DrawEpsilon; 4830 linecap[2].point.x+=2.0*DrawEpsilon; 4831 linecap[2].point.y+=2.0*DrawEpsilon; 4832 linecap[3].point.y+=2.0*DrawEpsilon; 4833 linecap[4].primitive=UndefinedPrimitive; 4834 (void) DrawPolygonPrimitive(image,draw_info,linecap,exception); 4835 } 4836 4837 static MagickBooleanType DrawStrokePolygon(Image *image, 4838 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info, 4839 ExceptionInfo *exception) 4840 { 4841 DrawInfo 4842 *clone_info; 4843 4844 MagickBooleanType 4845 closed_path; 4846 4847 MagickStatusType 4848 status; 4849 4850 PrimitiveInfo 4851 *stroke_polygon; 4852 4853 register const PrimitiveInfo 4854 *p, 4855 *q; 4856 4857 /* 4858 Draw stroked polygon. 4859 */ 4860 if (image->debug != MagickFalse) 4861 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4862 " begin draw-stroke-polygon"); 4863 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 4864 clone_info->fill=draw_info->stroke; 4865 if (clone_info->fill_pattern != (Image *) NULL) 4866 clone_info->fill_pattern=DestroyImage(clone_info->fill_pattern); 4867 if (clone_info->stroke_pattern != (Image *) NULL) 4868 clone_info->fill_pattern=CloneImage(clone_info->stroke_pattern,0,0, 4869 MagickTrue,exception); 4870 clone_info->stroke.alpha=(MagickRealType) TransparentAlpha; 4871 clone_info->stroke_width=0.0; 4872 clone_info->fill_rule=NonZeroRule; 4873 status=MagickTrue; 4874 for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates) 4875 { 4876 stroke_polygon=TraceStrokePolygon(draw_info,p); 4877 status&=DrawPolygonPrimitive(image,clone_info,stroke_polygon,exception); 4878 if (status == 0) 4879 break; 4880 stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon); 4881 q=p+p->coordinates-1; 4882 closed_path=(fabs(q->point.x-p->point.x) < DrawEpsilon) && 4883 (fabs(q->point.y-p->point.y) < DrawEpsilon) ? MagickTrue : MagickFalse; 4884 if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse)) 4885 { 4886 DrawRoundLinecap(image,draw_info,p,exception); 4887 DrawRoundLinecap(image,draw_info,q,exception); 4888 } 4889 } 4890 clone_info=DestroyDrawInfo(clone_info); 4891 if (image->debug != MagickFalse) 4892 (void) LogMagickEvent(DrawEvent,GetMagickModule(), 4893 " end draw-stroke-polygon"); 4894 return(status != 0 ? MagickTrue : MagickFalse); 4895 } 4896 4897 /* 4899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4900 % % 4901 % % 4902 % % 4903 % G e t A f f i n e M a t r i x % 4904 % % 4905 % % 4906 % % 4907 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4908 % 4909 % GetAffineMatrix() returns an AffineMatrix initialized to the identity 4910 % matrix. 4911 % 4912 % The format of the GetAffineMatrix method is: 4913 % 4914 % void GetAffineMatrix(AffineMatrix *affine_matrix) 4915 % 4916 % A description of each parameter follows: 4917 % 4918 % o affine_matrix: the affine matrix. 4919 % 4920 */ 4921 MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix) 4922 { 4923 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 4924 assert(affine_matrix != (AffineMatrix *) NULL); 4925 (void) ResetMagickMemory(affine_matrix,0,sizeof(*affine_matrix)); 4926 affine_matrix->sx=1.0; 4927 affine_matrix->sy=1.0; 4928 } 4929 4930 /* 4932 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4933 % % 4934 % % 4935 % % 4936 + G e t D r a w I n f o % 4937 % % 4938 % % 4939 % % 4940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4941 % 4942 % GetDrawInfo() initializes draw_info to default values from image_info. 4943 % 4944 % The format of the GetDrawInfo method is: 4945 % 4946 % void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info) 4947 % 4948 % A description of each parameter follows: 4949 % 4950 % o image_info: the image info.. 4951 % 4952 % o draw_info: the draw info. 4953 % 4954 */ 4955 MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info) 4956 { 4957 char 4958 *next_token; 4959 4960 const char 4961 *option; 4962 4963 ExceptionInfo 4964 *exception; 4965 4966 ImageInfo 4967 *clone_info; 4968 4969 /* 4970 Initialize draw attributes. 4971 */ 4972 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 4973 assert(draw_info != (DrawInfo *) NULL); 4974 (void) ResetMagickMemory(draw_info,0,sizeof(*draw_info)); 4975 clone_info=CloneImageInfo(image_info); 4976 GetAffineMatrix(&draw_info->affine); 4977 exception=AcquireExceptionInfo(); 4978 (void) QueryColorCompliance("#000F",AllCompliance,&draw_info->fill, 4979 exception); 4980 (void) QueryColorCompliance("#0000",AllCompliance,&draw_info->stroke, 4981 exception); 4982 draw_info->stroke_width=1.0; 4983 draw_info->fill_rule=EvenOddRule; 4984 draw_info->fill_alpha=OpaqueAlpha; 4985 draw_info->stroke_alpha=OpaqueAlpha; 4986 draw_info->linecap=ButtCap; 4987 draw_info->linejoin=MiterJoin; 4988 draw_info->miterlimit=10; 4989 draw_info->decorate=NoDecoration; 4990 draw_info->pointsize=12.0; 4991 draw_info->undercolor.alpha=(MagickRealType) TransparentAlpha; 4992 draw_info->compose=OverCompositeOp; 4993 draw_info->render=MagickTrue; 4994 draw_info->debug=IsEventLogging(); 4995 draw_info->stroke_antialias=clone_info->antialias; 4996 if (clone_info->font != (char *) NULL) 4997 draw_info->font=AcquireString(clone_info->font); 4998 if (clone_info->density != (char *) NULL) 4999 draw_info->density=AcquireString(clone_info->density); 5000 draw_info->text_antialias=clone_info->antialias; 5001 if (fabs(clone_info->pointsize) >= DrawEpsilon) 5002 draw_info->pointsize=clone_info->pointsize; 5003 draw_info->border_color=clone_info->border_color; 5004 if (clone_info->server_name != (char *) NULL) 5005 draw_info->server_name=AcquireString(clone_info->server_name); 5006 option=GetImageOption(clone_info,"direction"); 5007 if (option != (const char *) NULL) 5008 draw_info->direction=(DirectionType) ParseCommandOption( 5009 MagickDirectionOptions,MagickFalse,option); 5010 else 5011 draw_info->direction=UndefinedDirection; 5012 option=GetImageOption(clone_info,"encoding"); 5013 if (option != (const char *) NULL) 5014 (void) CloneString(&draw_info->encoding,option); 5015 option=GetImageOption(clone_info,"family"); 5016 if (option != (const char *) NULL) 5017 (void) CloneString(&draw_info->family,option); 5018 option=GetImageOption(clone_info,"fill"); 5019 if (option != (const char *) NULL) 5020 (void) QueryColorCompliance(option,AllCompliance,&draw_info->fill, 5021 exception); 5022 option=GetImageOption(clone_info,"gravity"); 5023 if (option != (const char *) NULL) 5024 draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions, 5025 MagickFalse,option); 5026 option=GetImageOption(clone_info,"interline-spacing"); 5027 if (option != (const char *) NULL) 5028 draw_info->interline_spacing=StringToDouble(option,&next_token); 5029 option=GetImageOption(clone_info,"interword-spacing"); 5030 if (option != (const char *) NULL) 5031 draw_info->interword_spacing=StringToDouble(option,&next_token); 5032 option=GetImageOption(clone_info,"kerning"); 5033 if (option != (const char *) NULL) 5034 draw_info->kerning=StringToDouble(option,&next_token); 5035 option=GetImageOption(clone_info,"stroke"); 5036 if (option != (const char *) NULL) 5037 (void) QueryColorCompliance(option,AllCompliance,&draw_info->stroke, 5038 exception); 5039 option=GetImageOption(clone_info,"strokewidth"); 5040 if (option != (const char *) NULL) 5041 draw_info->stroke_width=StringToDouble(option,&next_token); 5042 option=GetImageOption(clone_info,"style"); 5043 if (option != (const char *) NULL) 5044 draw_info->style=(StyleType) ParseCommandOption(MagickStyleOptions, 5045 MagickFalse,option); 5046 option=GetImageOption(clone_info,"undercolor"); 5047 if (option != (const char *) NULL) 5048 (void) QueryColorCompliance(option,AllCompliance,&draw_info->undercolor, 5049 exception); 5050 option=GetImageOption(clone_info,"weight"); 5051 if (option != (const char *) NULL) 5052 { 5053 ssize_t 5054 weight; 5055 5056 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,option); 5057 if (weight == -1) 5058 weight=(ssize_t) StringToUnsignedLong(option); 5059 draw_info->weight=(size_t) weight; 5060 } 5061 exception=DestroyExceptionInfo(exception); 5062 draw_info->signature=MagickCoreSignature; 5063 clone_info=DestroyImageInfo(clone_info); 5064 } 5065 5066 /* 5068 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5069 % % 5070 % % 5071 % % 5072 + P e r m u t a t e % 5073 % % 5074 % % 5075 % % 5076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5077 % 5078 % Permutate() returns the permuation of the (n,k). 5079 % 5080 % The format of the Permutate method is: 5081 % 5082 % void Permutate(ssize_t n,ssize_t k) 5083 % 5084 % A description of each parameter follows: 5085 % 5086 % o n: 5087 % 5088 % o k: 5089 % 5090 % 5091 */ 5092 static inline double Permutate(const ssize_t n,const ssize_t k) 5093 { 5094 double 5095 r; 5096 5097 register ssize_t 5098 i; 5099 5100 r=1.0; 5101 for (i=k+1; i <= n; i++) 5102 r*=i; 5103 for (i=1; i <= (n-k); i++) 5104 r/=i; 5105 return(r); 5106 } 5107 5108 /* 5110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5111 % % 5112 % % 5113 % % 5114 + T r a c e P r i m i t i v e % 5115 % % 5116 % % 5117 % % 5118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5119 % 5120 % TracePrimitive is a collection of methods for generating graphic 5121 % primitives such as arcs, ellipses, paths, etc. 5122 % 5123 */ 5124 5125 static void TraceArc(PrimitiveInfo *primitive_info,const PointInfo start, 5126 const PointInfo end,const PointInfo degrees) 5127 { 5128 PointInfo 5129 center, 5130 radii; 5131 5132 center.x=0.5*(end.x+start.x); 5133 center.y=0.5*(end.y+start.y); 5134 radii.x=fabs(center.x-start.x); 5135 radii.y=fabs(center.y-start.y); 5136 TraceEllipse(primitive_info,center,radii,degrees); 5137 } 5138 5139 static void TraceArcPath(PrimitiveInfo *primitive_info,const PointInfo start, 5140 const PointInfo end,const PointInfo arc,const double angle, 5141 const MagickBooleanType large_arc,const MagickBooleanType sweep) 5142 { 5143 double 5144 alpha, 5145 beta, 5146 delta, 5147 factor, 5148 gamma, 5149 theta; 5150 5151 PointInfo 5152 center, 5153 points[3], 5154 radii; 5155 5156 register double 5157 cosine, 5158 sine; 5159 5160 register PrimitiveInfo 5161 *p; 5162 5163 register ssize_t 5164 i; 5165 5166 size_t 5167 arc_segments; 5168 5169 if ((fabs(start.x-end.x) < DrawEpsilon) && 5170 (fabs(start.y-end.y) < DrawEpsilon)) 5171 { 5172 TracePoint(primitive_info,end); 5173 return; 5174 } 5175 radii.x=fabs(arc.x); 5176 radii.y=fabs(arc.y); 5177 if ((fabs(radii.x) < DrawEpsilon) || (fabs(radii.y) < DrawEpsilon)) 5178 { 5179 TraceLine(primitive_info,start,end); 5180 return; 5181 } 5182 cosine=cos(DegreesToRadians(fmod((double) angle,360.0))); 5183 sine=sin(DegreesToRadians(fmod((double) angle,360.0))); 5184 center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2); 5185 center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2); 5186 delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/ 5187 (radii.y*radii.y); 5188 if (delta < DrawEpsilon) 5189 { 5190 TraceLine(primitive_info,start,end); 5191 return; 5192 } 5193 if (delta > 1.0) 5194 { 5195 radii.x*=sqrt((double) delta); 5196 radii.y*=sqrt((double) delta); 5197 } 5198 points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x); 5199 points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y); 5200 points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x); 5201 points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y); 5202 alpha=points[1].x-points[0].x; 5203 beta=points[1].y-points[0].y; 5204 factor=PerceptibleReciprocal(alpha*alpha+beta*beta)-0.25; 5205 if (factor <= 0.0) 5206 factor=0.0; 5207 else 5208 { 5209 factor=sqrt((double) factor); 5210 if (sweep == large_arc) 5211 factor=(-factor); 5212 } 5213 center.x=(double) ((points[0].x+points[1].x)/2-factor*beta); 5214 center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha); 5215 alpha=atan2(points[0].y-center.y,points[0].x-center.x); 5216 theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha; 5217 if ((theta < 0.0) && (sweep != MagickFalse)) 5218 theta+=(double) (2.0*MagickPI); 5219 else 5220 if ((theta > 0.0) && (sweep == MagickFalse)) 5221 theta-=(double) (2.0*MagickPI); 5222 arc_segments=(size_t) ceil(fabs((double) (theta/(0.5*MagickPI+ 5223 DrawEpsilon)))); 5224 p=primitive_info; 5225 for (i=0; i < (ssize_t) arc_segments; i++) 5226 { 5227 beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments)); 5228 gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))* 5229 sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/ 5230 sin(fmod((double) beta,DegreesToRadians(360.0))); 5231 points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/ 5232 arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+ 5233 (double) i*theta/arc_segments),DegreesToRadians(360.0)))); 5234 points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/ 5235 arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+ 5236 (double) i*theta/arc_segments),DegreesToRadians(360.0)))); 5237 points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)* 5238 theta/arc_segments),DegreesToRadians(360.0)))); 5239 points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)* 5240 theta/arc_segments),DegreesToRadians(360.0)))); 5241 points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double) 5242 (i+1)*theta/arc_segments),DegreesToRadians(360.0)))); 5243 points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double) 5244 (i+1)*theta/arc_segments),DegreesToRadians(360.0)))); 5245 p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x; 5246 p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y; 5247 (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y* 5248 points[0].y); 5249 (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y* 5250 points[0].y); 5251 (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y* 5252 points[1].y); 5253 (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y* 5254 points[1].y); 5255 (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y* 5256 points[2].y); 5257 (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y* 5258 points[2].y); 5259 if (i == (ssize_t) (arc_segments-1)) 5260 (p+3)->point=end; 5261 TraceBezier(p,4); 5262 p+=p->coordinates; 5263 } 5264 primitive_info->coordinates=(size_t) (p-primitive_info); 5265 for (i=0; i < (ssize_t) primitive_info->coordinates; i++) 5266 { 5267 p->primitive=primitive_info->primitive; 5268 p--; 5269 } 5270 } 5271 5272 static void TraceBezier(PrimitiveInfo *primitive_info, 5273 const size_t number_coordinates) 5274 { 5275 double 5276 alpha, 5277 *coefficients, 5278 weight; 5279 5280 PointInfo 5281 end, 5282 point, 5283 *points; 5284 5285 register PrimitiveInfo 5286 *p; 5287 5288 register ssize_t 5289 i, 5290 j; 5291 5292 size_t 5293 control_points, 5294 quantum; 5295 5296 /* 5297 Allocate coeficients. 5298 */ 5299 quantum=number_coordinates; 5300 for (i=0; i < (ssize_t) number_coordinates; i++) 5301 { 5302 for (j=i+1; j < (ssize_t) number_coordinates; j++) 5303 { 5304 alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x); 5305 if (alpha > (double) quantum) 5306 quantum=(size_t) alpha; 5307 alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y); 5308 if (alpha > (double) quantum) 5309 quantum=(size_t) alpha; 5310 } 5311 } 5312 quantum=(size_t) MagickMin((double) quantum/number_coordinates, 5313 (double) BezierQuantum); 5314 control_points=quantum*number_coordinates; 5315 coefficients=(double *) AcquireQuantumMemory((size_t) 5316 number_coordinates,sizeof(*coefficients)); 5317 points=(PointInfo *) AcquireQuantumMemory((size_t) control_points, 5318 sizeof(*points)); 5319 if ((coefficients == (double *) NULL) || (points == (PointInfo *) NULL)) 5320 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 5321 /* 5322 Compute bezier points. 5323 */ 5324 end=primitive_info[number_coordinates-1].point; 5325 for (i=0; i < (ssize_t) number_coordinates; i++) 5326 coefficients[i]=Permutate((ssize_t) number_coordinates-1,i); 5327 weight=0.0; 5328 for (i=0; i < (ssize_t) control_points; i++) 5329 { 5330 p=primitive_info; 5331 point.x=0.0; 5332 point.y=0.0; 5333 alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0); 5334 for (j=0; j < (ssize_t) number_coordinates; j++) 5335 { 5336 point.x+=alpha*coefficients[j]*p->point.x; 5337 point.y+=alpha*coefficients[j]*p->point.y; 5338 alpha*=weight/(1.0-weight); 5339 p++; 5340 } 5341 points[i]=point; 5342 weight+=1.0/control_points; 5343 } 5344 /* 5345 Bezier curves are just short segmented polys. 5346 */ 5347 p=primitive_info; 5348 for (i=0; i < (ssize_t) control_points; i++) 5349 { 5350 TracePoint(p,points[i]); 5351 p+=p->coordinates; 5352 } 5353 TracePoint(p,end); 5354 p+=p->coordinates; 5355 primitive_info->coordinates=(size_t) (p-primitive_info); 5356 for (i=0; i < (ssize_t) primitive_info->coordinates; i++) 5357 { 5358 p->primitive=primitive_info->primitive; 5359 p--; 5360 } 5361 points=(PointInfo *) RelinquishMagickMemory(points); 5362 coefficients=(double *) RelinquishMagickMemory(coefficients); 5363 } 5364 5365 static void TraceCircle(PrimitiveInfo *primitive_info,const PointInfo start, 5366 const PointInfo end) 5367 { 5368 double 5369 alpha, 5370 beta, 5371 radius; 5372 5373 PointInfo 5374 offset, 5375 degrees; 5376 5377 alpha=end.x-start.x; 5378 beta=end.y-start.y; 5379 radius=hypot((double) alpha,(double) beta); 5380 offset.x=(double) radius; 5381 offset.y=(double) radius; 5382 degrees.x=0.0; 5383 degrees.y=360.0; 5384 TraceEllipse(primitive_info,start,offset,degrees); 5385 } 5386 5387 static void TraceEllipse(PrimitiveInfo *primitive_info,const PointInfo start, 5388 const PointInfo stop,const PointInfo degrees) 5389 { 5390 double 5391 delta, 5392 step, 5393 y; 5394 5395 PointInfo 5396 angle, 5397 point; 5398 5399 register PrimitiveInfo 5400 *p; 5401 5402 register ssize_t 5403 i; 5404 5405 /* 5406 Ellipses are just short segmented polys. 5407 */ 5408 if ((fabs(stop.x) < DrawEpsilon) && (fabs(stop.y) < DrawEpsilon)) 5409 { 5410 TracePoint(primitive_info,start); 5411 return; 5412 } 5413 delta=2.0/MagickMax(stop.x,stop.y); 5414 step=(double) (MagickPI/8.0); 5415 if ((delta >= 0.0) && (delta < (double) (MagickPI/8.0))) 5416 step=(double) (MagickPI/(4*(MagickPI/delta/2+0.5))); 5417 angle.x=DegreesToRadians(degrees.x); 5418 y=degrees.y; 5419 while (y < degrees.x) 5420 y+=360.0; 5421 angle.y=(double) DegreesToRadians(y); 5422 for (p=primitive_info; angle.x < angle.y; angle.x+=step) 5423 { 5424 point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*stop.x+start.x; 5425 point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*stop.y+start.y; 5426 TracePoint(p,point); 5427 p+=p->coordinates; 5428 } 5429 point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*stop.x+start.x; 5430 point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*stop.y+start.y; 5431 TracePoint(p,point); 5432 p+=p->coordinates; 5433 primitive_info->coordinates=(size_t) (p-primitive_info); 5434 for (i=0; i < (ssize_t) primitive_info->coordinates; i++) 5435 { 5436 p->primitive=primitive_info->primitive; 5437 p--; 5438 } 5439 } 5440 5441 static void TraceLine(PrimitiveInfo *primitive_info,const PointInfo start, 5442 const PointInfo end) 5443 { 5444 TracePoint(primitive_info,start); 5445 if ((fabs(start.x-end.x) < DrawEpsilon) && 5446 (fabs(start.y-end.y) < DrawEpsilon)) 5447 { 5448 primitive_info->primitive=PointPrimitive; 5449 primitive_info->coordinates=1; 5450 return; 5451 } 5452 TracePoint(primitive_info+1,end); 5453 (primitive_info+1)->primitive=primitive_info->primitive; 5454 primitive_info->coordinates=2; 5455 } 5456 5457 static size_t TracePath(PrimitiveInfo *primitive_info,const char *path) 5458 { 5459 char 5460 *next_token, 5461 token[MagickPathExtent]; 5462 5463 const char 5464 *p; 5465 5466 int 5467 attribute, 5468 last_attribute; 5469 5470 double 5471 x, 5472 y; 5473 5474 PointInfo 5475 end = {0.0, 0.0}, 5476 points[4] = { {0.0,0.0}, {0.0,0.0}, {0.0,0.0}, {0.0,0.0} }, 5477 point = {0.0, 0.0}, 5478 start = {0.0, 0.0}; 5479 5480 PrimitiveType 5481 primitive_type; 5482 5483 register PrimitiveInfo 5484 *q; 5485 5486 register ssize_t 5487 i; 5488 5489 size_t 5490 number_coordinates, 5491 z_count; 5492 5493 attribute=0; 5494 number_coordinates=0; 5495 z_count=0; 5496 primitive_type=primitive_info->primitive; 5497 q=primitive_info; 5498 for (p=path; *p != '\0'; ) 5499 { 5500 while (isspace((int) ((unsigned char) *p)) != 0) 5501 p++; 5502 if (*p == '\0') 5503 break; 5504 last_attribute=attribute; 5505 attribute=(int) (*p++); 5506 switch (attribute) 5507 { 5508 case 'a': 5509 case 'A': 5510 { 5511 MagickBooleanType 5512 large_arc, 5513 sweep; 5514 5515 double 5516 angle; 5517 5518 PointInfo 5519 arc; 5520 5521 /* 5522 Compute arc points. 5523 */ 5524 do 5525 { 5526 GetNextToken(p,&p,MagickPathExtent,token); 5527 if (*token == ',') 5528 GetNextToken(p,&p,MagickPathExtent,token); 5529 arc.x=StringToDouble(token,&next_token); 5530 GetNextToken(p,&p,MagickPathExtent,token); 5531 if (*token == ',') 5532 GetNextToken(p,&p,MagickPathExtent,token); 5533 arc.y=StringToDouble(token,&next_token); 5534 GetNextToken(p,&p,MagickPathExtent,token); 5535 if (*token == ',') 5536 GetNextToken(p,&p,MagickPathExtent,token); 5537 angle=StringToDouble(token,&next_token); 5538 GetNextToken(p,&p,MagickPathExtent,token); 5539 if (*token == ',') 5540 GetNextToken(p,&p,MagickPathExtent,token); 5541 large_arc=StringToLong(token) != 0 ? MagickTrue : MagickFalse; 5542 GetNextToken(p,&p,MagickPathExtent,token); 5543 if (*token == ',') 5544 GetNextToken(p,&p,MagickPathExtent,token); 5545 sweep=StringToLong(token) != 0 ? MagickTrue : MagickFalse; 5546 GetNextToken(p,&p,MagickPathExtent,token); 5547 if (*token == ',') 5548 GetNextToken(p,&p,MagickPathExtent,token); 5549 x=StringToDouble(token,&next_token); 5550 GetNextToken(p,&p,MagickPathExtent,token); 5551 if (*token == ',') 5552 GetNextToken(p,&p,MagickPathExtent,token); 5553 y=StringToDouble(token,&next_token); 5554 end.x=(double) (attribute == (int) 'A' ? x : point.x+x); 5555 end.y=(double) (attribute == (int) 'A' ? y : point.y+y); 5556 TraceArcPath(q,point,end,arc,angle,large_arc,sweep); 5557 q+=q->coordinates; 5558 point=end; 5559 while (isspace((int) ((unsigned char) *p)) != 0) 5560 p++; 5561 if (*p == ',') 5562 p++; 5563 } while (IsPoint(p) != MagickFalse); 5564 break; 5565 } 5566 case 'c': 5567 case 'C': 5568 { 5569 /* 5570 Compute bezier points. 5571 */ 5572 do 5573 { 5574 points[0]=point; 5575 for (i=1; i < 4; i++) 5576 { 5577 GetNextToken(p,&p,MagickPathExtent,token); 5578 if (*token == ',') 5579 GetNextToken(p,&p,MagickPathExtent,token); 5580 x=StringToDouble(token,&next_token); 5581 GetNextToken(p,&p,MagickPathExtent,token); 5582 if (*token == ',') 5583 GetNextToken(p,&p,MagickPathExtent,token); 5584 y=StringToDouble(token,&next_token); 5585 end.x=(double) (attribute == (int) 'C' ? x : point.x+x); 5586 end.y=(double) (attribute == (int) 'C' ? y : point.y+y); 5587 points[i]=end; 5588 } 5589 for (i=0; i < 4; i++) 5590 (q+i)->point=points[i]; 5591 TraceBezier(q,4); 5592 q+=q->coordinates; 5593 point=end; 5594 } while (IsPoint(p) != MagickFalse); 5595 break; 5596 } 5597 case 'H': 5598 case 'h': 5599 { 5600 do 5601 { 5602 GetNextToken(p,&p,MagickPathExtent,token); 5603 if (*token == ',') 5604 GetNextToken(p,&p,MagickPathExtent,token); 5605 x=StringToDouble(token,&next_token); 5606 point.x=(double) (attribute == (int) 'H' ? x: point.x+x); 5607 TracePoint(q,point); 5608 q+=q->coordinates; 5609 } while (IsPoint(p) != MagickFalse); 5610 break; 5611 } 5612 case 'l': 5613 case 'L': 5614 { 5615 do 5616 { 5617 GetNextToken(p,&p,MagickPathExtent,token); 5618 if (*token == ',') 5619 GetNextToken(p,&p,MagickPathExtent,token); 5620 x=StringToDouble(token,&next_token); 5621 GetNextToken(p,&p,MagickPathExtent,token); 5622 if (*token == ',') 5623 GetNextToken(p,&p,MagickPathExtent,token); 5624 y=StringToDouble(token,&next_token); 5625 point.x=(double) (attribute == (int) 'L' ? x : point.x+x); 5626 point.y=(double) (attribute == (int) 'L' ? y : point.y+y); 5627 TracePoint(q,point); 5628 q+=q->coordinates; 5629 } while (IsPoint(p) != MagickFalse); 5630 break; 5631 } 5632 case 'M': 5633 case 'm': 5634 { 5635 if (q != primitive_info) 5636 { 5637 primitive_info->coordinates=(size_t) (q-primitive_info); 5638 number_coordinates+=primitive_info->coordinates; 5639 primitive_info=q; 5640 } 5641 i=0; 5642 do 5643 { 5644 GetNextToken(p,&p,MagickPathExtent,token); 5645 if (*token == ',') 5646 GetNextToken(p,&p,MagickPathExtent,token); 5647 x=StringToDouble(token,&next_token); 5648 GetNextToken(p,&p,MagickPathExtent,token); 5649 if (*token == ',') 5650 GetNextToken(p,&p,MagickPathExtent,token); 5651 y=StringToDouble(token,&next_token); 5652 point.x=(double) (attribute == (int) 'M' ? x : point.x+x); 5653 point.y=(double) (attribute == (int) 'M' ? y : point.y+y); 5654 if (i == 0) 5655 start=point; 5656 i++; 5657 TracePoint(q,point); 5658 q+=q->coordinates; 5659 if ((i != 0) && (attribute == (int) 'M')) 5660 { 5661 TracePoint(q,point); 5662 q+=q->coordinates; 5663 } 5664 } while (IsPoint(p) != MagickFalse); 5665 break; 5666 } 5667 case 'q': 5668 case 'Q': 5669 { 5670 /* 5671 Compute bezier points. 5672 */ 5673 do 5674 { 5675 points[0]=point; 5676 for (i=1; i < 3; i++) 5677 { 5678 GetNextToken(p,&p,MagickPathExtent,token); 5679 if (*token == ',') 5680 GetNextToken(p,&p,MagickPathExtent,token); 5681 x=StringToDouble(token,&next_token); 5682 GetNextToken(p,&p,MagickPathExtent,token); 5683 if (*token == ',') 5684 GetNextToken(p,&p,MagickPathExtent,token); 5685 y=StringToDouble(token,&next_token); 5686 if (*p == ',') 5687 p++; 5688 end.x=(double) (attribute == (int) 'Q' ? x : point.x+x); 5689 end.y=(double) (attribute == (int) 'Q' ? y : point.y+y); 5690 points[i]=end; 5691 } 5692 for (i=0; i < 3; i++) 5693 (q+i)->point=points[i]; 5694 TraceBezier(q,3); 5695 q+=q->coordinates; 5696 point=end; 5697 } while (IsPoint(p) != MagickFalse); 5698 break; 5699 } 5700 case 's': 5701 case 'S': 5702 { 5703 /* 5704 Compute bezier points. 5705 */ 5706 do 5707 { 5708 points[0]=points[3]; 5709 points[1].x=2.0*points[3].x-points[2].x; 5710 points[1].y=2.0*points[3].y-points[2].y; 5711 for (i=2; i < 4; i++) 5712 { 5713 GetNextToken(p,&p,MagickPathExtent,token); 5714 if (*token == ',') 5715 GetNextToken(p,&p,MagickPathExtent,token); 5716 x=StringToDouble(token,&next_token); 5717 GetNextToken(p,&p,MagickPathExtent,token); 5718 if (*token == ',') 5719 GetNextToken(p,&p,MagickPathExtent,token); 5720 y=StringToDouble(token,&next_token); 5721 if (*p == ',') 5722 p++; 5723 end.x=(double) (attribute == (int) 'S' ? x : point.x+x); 5724 end.y=(double) (attribute == (int) 'S' ? y : point.y+y); 5725 points[i]=end; 5726 } 5727 if (strchr("CcSs",last_attribute) == (char *) NULL) 5728 { 5729 points[0]=point; 5730 points[1]=point; 5731 } 5732 for (i=0; i < 4; i++) 5733 (q+i)->point=points[i]; 5734 TraceBezier(q,4); 5735 q+=q->coordinates; 5736 point=end; 5737 } while (IsPoint(p) != MagickFalse); 5738 break; 5739 } 5740 case 't': 5741 case 'T': 5742 { 5743 /* 5744 Compute bezier points. 5745 */ 5746 do 5747 { 5748 points[0]=points[2]; 5749 points[1].x=2.0*points[2].x-points[1].x; 5750 points[1].y=2.0*points[2].y-points[1].y; 5751 for (i=2; i < 3; i++) 5752 { 5753 GetNextToken(p,&p,MagickPathExtent,token); 5754 if (*token == ',') 5755 GetNextToken(p,&p,MagickPathExtent,token); 5756 x=StringToDouble(token,&next_token); 5757 GetNextToken(p,&p,MagickPathExtent,token); 5758 if (*token == ',') 5759 GetNextToken(p,&p,MagickPathExtent,token); 5760 y=StringToDouble(token,&next_token); 5761 end.x=(double) (attribute == (int) 'T' ? x : point.x+x); 5762 end.y=(double) (attribute == (int) 'T' ? y : point.y+y); 5763 points[i]=end; 5764 } 5765 if (strchr("QqTt",last_attribute) == (char *) NULL) 5766 { 5767 points[0]=point; 5768 points[1]=point; 5769 } 5770 for (i=0; i < 3; i++) 5771 (q+i)->point=points[i]; 5772 TraceBezier(q,3); 5773 q+=q->coordinates; 5774 point=end; 5775 } while (IsPoint(p) != MagickFalse); 5776 break; 5777 } 5778 case 'v': 5779 case 'V': 5780 { 5781 do 5782 { 5783 GetNextToken(p,&p,MagickPathExtent,token); 5784 if (*token == ',') 5785 GetNextToken(p,&p,MagickPathExtent,token); 5786 y=StringToDouble(token,&next_token); 5787 point.y=(double) (attribute == (int) 'V' ? y : point.y+y); 5788 TracePoint(q,point); 5789 q+=q->coordinates; 5790 } while (IsPoint(p) != MagickFalse); 5791 break; 5792 } 5793 case 'z': 5794 case 'Z': 5795 { 5796 point=start; 5797 TracePoint(q,point); 5798 q+=q->coordinates; 5799 primitive_info->coordinates=(size_t) (q-primitive_info); 5800 number_coordinates+=primitive_info->coordinates; 5801 primitive_info=q; 5802 z_count++; 5803 break; 5804 } 5805 default: 5806 { 5807 if (isalpha((int) ((unsigned char) attribute)) != 0) 5808 (void) FormatLocaleFile(stderr,"attribute not recognized: %c\n", 5809 attribute); 5810 break; 5811 } 5812 } 5813 } 5814 primitive_info->coordinates=(size_t) (q-primitive_info); 5815 number_coordinates+=primitive_info->coordinates; 5816 for (i=0; i < (ssize_t) number_coordinates; i++) 5817 { 5818 q--; 5819 q->primitive=primitive_type; 5820 if (z_count > 1) 5821 q->method=FillToBorderMethod; 5822 } 5823 q=primitive_info; 5824 return(number_coordinates); 5825 } 5826 5827 static void TraceRectangle(PrimitiveInfo *primitive_info,const PointInfo start, 5828 const PointInfo end) 5829 { 5830 PointInfo 5831 point; 5832 5833 register PrimitiveInfo 5834 *p; 5835 5836 register ssize_t 5837 i; 5838 5839 p=primitive_info; 5840 TracePoint(p,start); 5841 p+=p->coordinates; 5842 point.x=start.x; 5843 point.y=end.y; 5844 TracePoint(p,point); 5845 p+=p->coordinates; 5846 TracePoint(p,end); 5847 p+=p->coordinates; 5848 point.x=end.x; 5849 point.y=start.y; 5850 TracePoint(p,point); 5851 p+=p->coordinates; 5852 TracePoint(p,start); 5853 p+=p->coordinates; 5854 primitive_info->coordinates=(size_t) (p-primitive_info); 5855 for (i=0; i < (ssize_t) primitive_info->coordinates; i++) 5856 { 5857 p->primitive=primitive_info->primitive; 5858 p--; 5859 } 5860 } 5861 5862 static void TraceRoundRectangle(PrimitiveInfo *primitive_info, 5863 const PointInfo start,const PointInfo end,PointInfo arc) 5864 { 5865 PointInfo 5866 degrees, 5867 offset, 5868 point; 5869 5870 register PrimitiveInfo 5871 *p; 5872 5873 register ssize_t 5874 i; 5875 5876 p=primitive_info; 5877 offset.x=fabs(end.x-start.x); 5878 offset.y=fabs(end.y-start.y); 5879 if (arc.x > (0.5*offset.x)) 5880 arc.x=0.5*offset.x; 5881 if (arc.y > (0.5*offset.y)) 5882 arc.y=0.5*offset.y; 5883 point.x=start.x+offset.x-arc.x; 5884 point.y=start.y+arc.y; 5885 degrees.x=270.0; 5886 degrees.y=360.0; 5887 TraceEllipse(p,point,arc,degrees); 5888 p+=p->coordinates; 5889 point.x=start.x+offset.x-arc.x; 5890 point.y=start.y+offset.y-arc.y; 5891 degrees.x=0.0; 5892 degrees.y=90.0; 5893 TraceEllipse(p,point,arc,degrees); 5894 p+=p->coordinates; 5895 point.x=start.x+arc.x; 5896 point.y=start.y+offset.y-arc.y; 5897 degrees.x=90.0; 5898 degrees.y=180.0; 5899 TraceEllipse(p,point,arc,degrees); 5900 p+=p->coordinates; 5901 point.x=start.x+arc.x; 5902 point.y=start.y+arc.y; 5903 degrees.x=180.0; 5904 degrees.y=270.0; 5905 TraceEllipse(p,point,arc,degrees); 5906 p+=p->coordinates; 5907 TracePoint(p,primitive_info->point); 5908 p+=p->coordinates; 5909 primitive_info->coordinates=(size_t) (p-primitive_info); 5910 for (i=0; i < (ssize_t) primitive_info->coordinates; i++) 5911 { 5912 p->primitive=primitive_info->primitive; 5913 p--; 5914 } 5915 } 5916 5917 static void TraceSquareLinecap(PrimitiveInfo *primitive_info, 5918 const size_t number_vertices,const double offset) 5919 { 5920 double 5921 distance; 5922 5923 register double 5924 dx, 5925 dy; 5926 5927 register ssize_t 5928 i; 5929 5930 ssize_t 5931 j; 5932 5933 dx=0.0; 5934 dy=0.0; 5935 for (i=1; i < (ssize_t) number_vertices; i++) 5936 { 5937 dx=primitive_info[0].point.x-primitive_info[i].point.x; 5938 dy=primitive_info[0].point.y-primitive_info[i].point.y; 5939 if ((fabs((double) dx) >= DrawEpsilon) || 5940 (fabs((double) dy) >= DrawEpsilon)) 5941 break; 5942 } 5943 if (i == (ssize_t) number_vertices) 5944 i=(ssize_t) number_vertices-1L; 5945 distance=hypot((double) dx,(double) dy); 5946 primitive_info[0].point.x=(double) (primitive_info[i].point.x+ 5947 dx*(distance+offset)/distance); 5948 primitive_info[0].point.y=(double) (primitive_info[i].point.y+ 5949 dy*(distance+offset)/distance); 5950 for (j=(ssize_t) number_vertices-2; j >= 0; j--) 5951 { 5952 dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x; 5953 dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y; 5954 if ((fabs((double) dx) >= DrawEpsilon) || 5955 (fabs((double) dy) >= DrawEpsilon)) 5956 break; 5957 } 5958 distance=hypot((double) dx,(double) dy); 5959 primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+ 5960 dx*(distance+offset)/distance); 5961 primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+ 5962 dy*(distance+offset)/distance); 5963 } 5964 5965 static inline double DrawEpsilonReciprocal(const double x) 5966 { 5967 double sign = x < 0.0 ? -1.0 : 1.0; 5968 return((sign*x) >= DrawEpsilon ? 1.0/x : sign*(1.0/DrawEpsilon)); 5969 } 5970 5971 static PrimitiveInfo *TraceStrokePolygon(const DrawInfo *draw_info, 5972 const PrimitiveInfo *primitive_info) 5973 { 5974 typedef struct _LineSegment 5975 { 5976 double 5977 p, 5978 q; 5979 } LineSegment; 5980 5981 LineSegment 5982 dx, 5983 dy, 5984 inverse_slope, 5985 slope, 5986 theta; 5987 5988 MagickBooleanType 5989 closed_path; 5990 5991 double 5992 delta_theta, 5993 dot_product, 5994 mid, 5995 miterlimit; 5996 5997 PointInfo 5998 box_p[5], 5999 box_q[5], 6000 center, 6001 offset, 6002 *path_p, 6003 *path_q; 6004 6005 PrimitiveInfo 6006 *polygon_primitive, 6007 *stroke_polygon; 6008 6009 register ssize_t 6010 i; 6011 6012 size_t 6013 arc_segments, 6014 max_strokes, 6015 number_vertices; 6016 6017 ssize_t 6018 j, 6019 n, 6020 p, 6021 q; 6022 6023 /* 6024 Allocate paths. 6025 */ 6026 number_vertices=primitive_info->coordinates; 6027 max_strokes=2*number_vertices+6*BezierQuantum+360; 6028 path_p=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes, 6029 sizeof(*path_p)); 6030 path_q=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes, 6031 sizeof(*path_q)); 6032 polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t) 6033 number_vertices+2UL,sizeof(*polygon_primitive)); 6034 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL) || 6035 (polygon_primitive == (PrimitiveInfo *) NULL)) 6036 return((PrimitiveInfo *) NULL); 6037 (void) CopyMagickMemory(polygon_primitive,primitive_info,(size_t) 6038 number_vertices*sizeof(*polygon_primitive)); 6039 closed_path= 6040 (primitive_info[number_vertices-1].point.x == primitive_info[0].point.x) && 6041 (primitive_info[number_vertices-1].point.y == primitive_info[0].point.y) ? 6042 MagickTrue : MagickFalse; 6043 if ((draw_info->linejoin == RoundJoin) || 6044 ((draw_info->linejoin == MiterJoin) && (closed_path != MagickFalse))) 6045 { 6046 polygon_primitive[number_vertices]=primitive_info[1]; 6047 number_vertices++; 6048 } 6049 polygon_primitive[number_vertices].primitive=UndefinedPrimitive; 6050 /* 6051 Compute the slope for the first line segment, p. 6052 */ 6053 dx.p=0.0; 6054 dy.p=0.0; 6055 for (n=1; n < (ssize_t) number_vertices; n++) 6056 { 6057 dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x; 6058 dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y; 6059 if ((fabs(dx.p) >= DrawEpsilon) || (fabs(dy.p) >= DrawEpsilon)) 6060 break; 6061 } 6062 if (n == (ssize_t) number_vertices) 6063 n=(ssize_t) number_vertices-1L; 6064 slope.p=0.0; 6065 inverse_slope.p=0.0; 6066 if (fabs(dx.p) < DrawEpsilon) 6067 { 6068 if (dx.p >= 0.0) 6069 slope.p=dy.p < 0.0 ? -1.0/DrawEpsilon : 1.0/DrawEpsilon; 6070 else 6071 slope.p=dy.p < 0.0 ? 1.0/DrawEpsilon : -1.0/DrawEpsilon; 6072 } 6073 else 6074 if (fabs(dy.p) < DrawEpsilon) 6075 { 6076 if (dy.p >= 0.0) 6077 inverse_slope.p=dx.p < 0.0 ? -1.0/DrawEpsilon : 1.0/DrawEpsilon; 6078 else 6079 inverse_slope.p=dx.p < 0.0 ? 1.0/DrawEpsilon : -1.0/DrawEpsilon; 6080 } 6081 else 6082 { 6083 slope.p=dy.p/dx.p; 6084 inverse_slope.p=(-1.0/slope.p); 6085 } 6086 mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0; 6087 miterlimit=(double) (draw_info->miterlimit*draw_info->miterlimit* 6088 mid*mid); 6089 if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse)) 6090 TraceSquareLinecap(polygon_primitive,number_vertices,mid); 6091 offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0))); 6092 offset.y=(double) (offset.x*inverse_slope.p); 6093 if ((dy.p*offset.x-dx.p*offset.y) > 0.0) 6094 { 6095 box_p[0].x=polygon_primitive[0].point.x-offset.x; 6096 box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p; 6097 box_p[1].x=polygon_primitive[n].point.x-offset.x; 6098 box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p; 6099 box_q[0].x=polygon_primitive[0].point.x+offset.x; 6100 box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p; 6101 box_q[1].x=polygon_primitive[n].point.x+offset.x; 6102 box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p; 6103 } 6104 else 6105 { 6106 box_p[0].x=polygon_primitive[0].point.x+offset.x; 6107 box_p[0].y=polygon_primitive[0].point.y+offset.y; 6108 box_p[1].x=polygon_primitive[n].point.x+offset.x; 6109 box_p[1].y=polygon_primitive[n].point.y+offset.y; 6110 box_q[0].x=polygon_primitive[0].point.x-offset.x; 6111 box_q[0].y=polygon_primitive[0].point.y-offset.y; 6112 box_q[1].x=polygon_primitive[n].point.x-offset.x; 6113 box_q[1].y=polygon_primitive[n].point.y-offset.y; 6114 } 6115 /* 6116 Create strokes for the line join attribute: bevel, miter, round. 6117 */ 6118 p=0; 6119 q=0; 6120 path_q[p++]=box_q[0]; 6121 path_p[q++]=box_p[0]; 6122 for (i=(ssize_t) n+1; i < (ssize_t) number_vertices; i++) 6123 { 6124 /* 6125 Compute the slope for this line segment, q. 6126 */ 6127 dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x; 6128 dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y; 6129 dot_product=dx.q*dx.q+dy.q*dy.q; 6130 if (dot_product < 0.25) 6131 continue; 6132 slope.q=0.0; 6133 inverse_slope.q=0.0; 6134 if (fabs(dx.q) < DrawEpsilon) 6135 { 6136 if (dx.q >= 0.0) 6137 slope.q=dy.q < 0.0 ? -1.0/DrawEpsilon : 1.0/DrawEpsilon; 6138 else 6139 slope.q=dy.q < 0.0 ? 1.0/DrawEpsilon : -1.0/DrawEpsilon; 6140 } 6141 else 6142 if (fabs(dy.q) < DrawEpsilon) 6143 { 6144 if (dy.q >= 0.0) 6145 inverse_slope.q=dx.q < 0.0 ? -1.0/DrawEpsilon : 1.0/DrawEpsilon; 6146 else 6147 inverse_slope.q=dx.q < 0.0 ? 1.0/DrawEpsilon : -1.0/DrawEpsilon; 6148 } 6149 else 6150 { 6151 slope.q=dy.q/dx.q; 6152 inverse_slope.q=(-1.0/slope.q); 6153 } 6154 offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0))); 6155 offset.y=(double) (offset.x*inverse_slope.q); 6156 dot_product=dy.q*offset.x-dx.q*offset.y; 6157 if (dot_product > 0.0) 6158 { 6159 box_p[2].x=polygon_primitive[n].point.x-offset.x; 6160 box_p[2].y=polygon_primitive[n].point.y-offset.y; 6161 box_p[3].x=polygon_primitive[i].point.x-offset.x; 6162 box_p[3].y=polygon_primitive[i].point.y-offset.y; 6163 box_q[2].x=polygon_primitive[n].point.x+offset.x; 6164 box_q[2].y=polygon_primitive[n].point.y+offset.y; 6165 box_q[3].x=polygon_primitive[i].point.x+offset.x; 6166 box_q[3].y=polygon_primitive[i].point.y+offset.y; 6167 } 6168 else 6169 { 6170 box_p[2].x=polygon_primitive[n].point.x+offset.x; 6171 box_p[2].y=polygon_primitive[n].point.y+offset.y; 6172 box_p[3].x=polygon_primitive[i].point.x+offset.x; 6173 box_p[3].y=polygon_primitive[i].point.y+offset.y; 6174 box_q[2].x=polygon_primitive[n].point.x-offset.x; 6175 box_q[2].y=polygon_primitive[n].point.y-offset.y; 6176 box_q[3].x=polygon_primitive[i].point.x-offset.x; 6177 box_q[3].y=polygon_primitive[i].point.y-offset.y; 6178 } 6179 if (fabs((double) (slope.p-slope.q)) < DrawEpsilon) 6180 { 6181 box_p[4]=box_p[1]; 6182 box_q[4]=box_q[1]; 6183 } 6184 else 6185 { 6186 box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+ 6187 box_p[3].y)/(slope.p-slope.q)); 6188 box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y); 6189 box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+ 6190 box_q[3].y)/(slope.p-slope.q)); 6191 box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y); 6192 } 6193 if (q >= (ssize_t) (max_strokes-6*BezierQuantum-360)) 6194 { 6195 if (~max_strokes < (6*BezierQuantum+360)) 6196 { 6197 path_p=(PointInfo *) RelinquishMagickMemory(path_p); 6198 path_q=(PointInfo *) RelinquishMagickMemory(path_q); 6199 } 6200 else 6201 { 6202 max_strokes+=6*BezierQuantum+360; 6203 path_p=(PointInfo *) ResizeQuantumMemory(path_p,max_strokes, 6204 sizeof(*path_p)); 6205 path_q=(PointInfo *) ResizeQuantumMemory(path_q,max_strokes, 6206 sizeof(*path_q)); 6207 } 6208 if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL)) 6209 { 6210 if (path_p != (PointInfo *) NULL) 6211 path_p=(PointInfo *) RelinquishMagickMemory(path_p); 6212 if (path_q != (PointInfo *) NULL) 6213 path_q=(PointInfo *) RelinquishMagickMemory(path_q); 6214 polygon_primitive=(PrimitiveInfo *) 6215 RelinquishMagickMemory(polygon_primitive); 6216 return((PrimitiveInfo *) NULL); 6217 } 6218 } 6219 dot_product=dx.q*dy.p-dx.p*dy.q; 6220 if (dot_product <= 0.0) 6221 switch (draw_info->linejoin) 6222 { 6223 case BevelJoin: 6224 { 6225 path_q[q++]=box_q[1]; 6226 path_q[q++]=box_q[2]; 6227 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+ 6228 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y); 6229 if (dot_product <= miterlimit) 6230 path_p[p++]=box_p[4]; 6231 else 6232 { 6233 path_p[p++]=box_p[1]; 6234 path_p[p++]=box_p[2]; 6235 } 6236 break; 6237 } 6238 case MiterJoin: 6239 { 6240 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+ 6241 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y); 6242 if (dot_product <= miterlimit) 6243 { 6244 path_q[q++]=box_q[4]; 6245 path_p[p++]=box_p[4]; 6246 } 6247 else 6248 { 6249 path_q[q++]=box_q[1]; 6250 path_q[q++]=box_q[2]; 6251 path_p[p++]=box_p[1]; 6252 path_p[p++]=box_p[2]; 6253 } 6254 break; 6255 } 6256 case RoundJoin: 6257 { 6258 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+ 6259 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y); 6260 if (dot_product <= miterlimit) 6261 path_p[p++]=box_p[4]; 6262 else 6263 { 6264 path_p[p++]=box_p[1]; 6265 path_p[p++]=box_p[2]; 6266 } 6267 center=polygon_primitive[n].point; 6268 theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x); 6269 theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x); 6270 if (theta.q < theta.p) 6271 theta.q+=(double) (2.0*MagickPI); 6272 arc_segments=(size_t) ceil((double) ((theta.q-theta.p)/ 6273 (2.0*sqrt((double) (1.0/mid))))); 6274 path_q[q].x=box_q[1].x; 6275 path_q[q].y=box_q[1].y; 6276 q++; 6277 for (j=1; j < (ssize_t) arc_segments; j++) 6278 { 6279 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments); 6280 path_q[q].x=(double) (center.x+mid*cos(fmod((double) 6281 (theta.p+delta_theta),DegreesToRadians(360.0)))); 6282 path_q[q].y=(double) (center.y+mid*sin(fmod((double) 6283 (theta.p+delta_theta),DegreesToRadians(360.0)))); 6284 q++; 6285 } 6286 path_q[q++]=box_q[2]; 6287 break; 6288 } 6289 default: 6290 break; 6291 } 6292 else 6293 switch (draw_info->linejoin) 6294 { 6295 case BevelJoin: 6296 { 6297 path_p[p++]=box_p[1]; 6298 path_p[p++]=box_p[2]; 6299 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+ 6300 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y); 6301 if (dot_product <= miterlimit) 6302 path_q[q++]=box_q[4]; 6303 else 6304 { 6305 path_q[q++]=box_q[1]; 6306 path_q[q++]=box_q[2]; 6307 } 6308 break; 6309 } 6310 case MiterJoin: 6311 { 6312 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+ 6313 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y); 6314 if (dot_product <= miterlimit) 6315 { 6316 path_q[q++]=box_q[4]; 6317 path_p[p++]=box_p[4]; 6318 } 6319 else 6320 { 6321 path_q[q++]=box_q[1]; 6322 path_q[q++]=box_q[2]; 6323 path_p[p++]=box_p[1]; 6324 path_p[p++]=box_p[2]; 6325 } 6326 break; 6327 } 6328 case RoundJoin: 6329 { 6330 dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+ 6331 (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y); 6332 if (dot_product <= miterlimit) 6333 path_q[q++]=box_q[4]; 6334 else 6335 { 6336 path_q[q++]=box_q[1]; 6337 path_q[q++]=box_q[2]; 6338 } 6339 center=polygon_primitive[n].point; 6340 theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x); 6341 theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x); 6342 if (theta.p < theta.q) 6343 theta.p+=(double) (2.0*MagickPI); 6344 arc_segments=(size_t) ceil((double) ((theta.p-theta.q)/ 6345 (2.0*sqrt((double) (1.0/mid))))); 6346 path_p[p++]=box_p[1]; 6347 for (j=1; j < (ssize_t) arc_segments; j++) 6348 { 6349 delta_theta=(double) (j*(theta.q-theta.p)/arc_segments); 6350 path_p[p].x=(double) (center.x+mid*cos(fmod((double) 6351 (theta.p+delta_theta),DegreesToRadians(360.0)))); 6352 path_p[p].y=(double) (center.y+mid*sin(fmod((double) 6353 (theta.p+delta_theta),DegreesToRadians(360.0)))); 6354 p++; 6355 } 6356 path_p[p++]=box_p[2]; 6357 break; 6358 } 6359 default: 6360 break; 6361 } 6362 slope.p=slope.q; 6363 inverse_slope.p=inverse_slope.q; 6364 box_p[0]=box_p[2]; 6365 box_p[1]=box_p[3]; 6366 box_q[0]=box_q[2]; 6367 box_q[1]=box_q[3]; 6368 dx.p=dx.q; 6369 dy.p=dy.q; 6370 n=i; 6371 } 6372 path_p[p++]=box_p[1]; 6373 path_q[q++]=box_q[1]; 6374 /* 6375 Trace stroked polygon. 6376 */ 6377 stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t) 6378 (p+q+2UL*closed_path+2UL),sizeof(*stroke_polygon)); 6379 if (stroke_polygon != (PrimitiveInfo *) NULL) 6380 { 6381 for (i=0; i < (ssize_t) p; i++) 6382 { 6383 stroke_polygon[i]=polygon_primitive[0]; 6384 stroke_polygon[i].point=path_p[i]; 6385 } 6386 if (closed_path != MagickFalse) 6387 { 6388 stroke_polygon[i]=polygon_primitive[0]; 6389 stroke_polygon[i].point=stroke_polygon[0].point; 6390 i++; 6391 } 6392 for ( ; i < (ssize_t) (p+q+closed_path); i++) 6393 { 6394 stroke_polygon[i]=polygon_primitive[0]; 6395 stroke_polygon[i].point=path_q[p+q+closed_path-(i+1)]; 6396 } 6397 if (closed_path != MagickFalse) 6398 { 6399 stroke_polygon[i]=polygon_primitive[0]; 6400 stroke_polygon[i].point=stroke_polygon[p+closed_path].point; 6401 i++; 6402 } 6403 stroke_polygon[i]=polygon_primitive[0]; 6404 stroke_polygon[i].point=stroke_polygon[0].point; 6405 i++; 6406 stroke_polygon[i].primitive=UndefinedPrimitive; 6407 stroke_polygon[0].coordinates=(size_t) (p+q+2*closed_path+1); 6408 } 6409 path_p=(PointInfo *) RelinquishMagickMemory(path_p); 6410 path_q=(PointInfo *) RelinquishMagickMemory(path_q); 6411 polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive); 6412 return(stroke_polygon); 6413 } 6414