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