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