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