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