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