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