00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 #include "magick/studio.h"
00049 #include "magick/annotate.h"
00050 #include "magick/artifact.h"
00051 #include "magick/blob.h"
00052 #include "magick/cache.h"
00053 #include "magick/cache-view.h"
00054 #include "magick/color.h"
00055 #include "magick/composite.h"
00056 #include "magick/composite-private.h"
00057 #include "magick/constitute.h"
00058 #include "magick/draw.h"
00059 #include "magick/draw-private.h"
00060 #include "magick/enhance.h"
00061 #include "magick/exception.h"
00062 #include "magick/exception-private.h"
00063 #include "magick/gem.h"
00064 #include "magick/geometry.h"
00065 #include "magick/image-private.h"
00066 #include "magick/list.h"
00067 #include "magick/log.h"
00068 #include "magick/monitor.h"
00069 #include "magick/monitor-private.h"
00070 #include "magick/option.h"
00071 #include "magick/paint.h"
00072 #include "magick/pixel-private.h"
00073 #include "magick/property.h"
00074 #include "magick/resample.h"
00075 #include "magick/resample-private.h"
00076 #include "magick/string_.h"
00077 #include "magick/string-private.h"
00078 #include "magick/thread-private.h"
00079 #include "magick/token.h"
00080 #include "magick/transform.h"
00081 #include "magick/utility.h"
00082
00083
00084
00085
00086 #define BezierQuantum 200
00087
00088
00089
00090
00091 typedef struct _EdgeInfo
00092 {
00093 SegmentInfo
00094 bounds;
00095
00096 MagickRealType
00097 scanline;
00098
00099 PointInfo
00100 *points;
00101
00102 unsigned long
00103 number_points;
00104
00105 long
00106 direction;
00107
00108 MagickBooleanType
00109 ghostline;
00110
00111 unsigned long
00112 highwater;
00113 } EdgeInfo;
00114
00115 typedef struct _ElementInfo
00116 {
00117 MagickRealType
00118 cx,
00119 cy,
00120 major,
00121 minor,
00122 angle;
00123 } ElementInfo;
00124
00125 typedef struct _PolygonInfo
00126 {
00127 EdgeInfo
00128 *edges;
00129
00130 unsigned long
00131 number_edges;
00132 } PolygonInfo;
00133
00134 typedef enum
00135 {
00136 MoveToCode,
00137 OpenCode,
00138 GhostlineCode,
00139 LineToCode,
00140 EndCode
00141 } PathInfoCode;
00142
00143 typedef struct _PathInfo
00144 {
00145 PointInfo
00146 point;
00147
00148 PathInfoCode
00149 code;
00150 } PathInfo;
00151
00152
00153
00154
00155 static MagickBooleanType
00156 DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *);
00157
00158 static PrimitiveInfo
00159 *TraceStrokePolygon(const DrawInfo *,const PrimitiveInfo *);
00160
00161 static unsigned long
00162 TracePath(PrimitiveInfo *,const char *);
00163
00164 static void
00165 TraceArc(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo),
00166 TraceArcPath(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo,
00167 const MagickRealType,const MagickBooleanType,const MagickBooleanType),
00168 TraceBezier(PrimitiveInfo *,const unsigned long),
00169 TraceCircle(PrimitiveInfo *,const PointInfo,const PointInfo),
00170 TraceEllipse(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo),
00171 TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo),
00172 TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo),
00173 TraceRoundRectangle(PrimitiveInfo *,const PointInfo,const PointInfo,
00174 PointInfo),
00175 TraceSquareLinecap(PrimitiveInfo *,const unsigned long,const MagickRealType);
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195 MagickExport DrawInfo *AcquireDrawInfo(void)
00196 {
00197 DrawInfo
00198 *draw_info;
00199
00200 draw_info=(DrawInfo *) AcquireAlignedMemory(1,sizeof(*draw_info));
00201 if (draw_info == (DrawInfo *) NULL)
00202 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00203 GetDrawInfo((ImageInfo *) NULL,draw_info);
00204 return(draw_info);
00205 }
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234 MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
00235 const DrawInfo *draw_info)
00236 {
00237 DrawInfo
00238 *clone_info;
00239
00240 clone_info=(DrawInfo *) AcquireAlignedMemory(1,sizeof(*clone_info));
00241 if (clone_info == (DrawInfo *) NULL)
00242 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00243 GetDrawInfo(image_info,clone_info);
00244 if (draw_info == (DrawInfo *) NULL)
00245 return(clone_info);
00246 if (clone_info->primitive != (char *) NULL)
00247 (void) CloneString(&clone_info->primitive,draw_info->primitive);
00248 if (draw_info->geometry != (char *) NULL)
00249 (void) CloneString(&clone_info->geometry,draw_info->geometry);
00250 clone_info->viewbox=draw_info->viewbox;
00251 clone_info->affine=draw_info->affine;
00252 clone_info->gravity=draw_info->gravity;
00253 clone_info->fill=draw_info->fill;
00254 clone_info->stroke=draw_info->stroke;
00255 clone_info->stroke_width=draw_info->stroke_width;
00256 if (draw_info->fill_pattern != (Image *) NULL)
00257 clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
00258 &draw_info->fill_pattern->exception);
00259 else
00260 if (draw_info->tile != (Image *) NULL)
00261 clone_info->fill_pattern=CloneImage(draw_info->tile,0,0,MagickTrue,
00262 &draw_info->tile->exception);
00263 clone_info->tile=NewImageList();
00264 if (draw_info->stroke_pattern != (Image *) NULL)
00265 clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
00266 MagickTrue,&draw_info->stroke_pattern->exception);
00267 clone_info->stroke_antialias=draw_info->stroke_antialias;
00268 clone_info->text_antialias=draw_info->text_antialias;
00269 clone_info->fill_rule=draw_info->fill_rule;
00270 clone_info->linecap=draw_info->linecap;
00271 clone_info->linejoin=draw_info->linejoin;
00272 clone_info->miterlimit=draw_info->miterlimit;
00273 clone_info->dash_offset=draw_info->dash_offset;
00274 clone_info->decorate=draw_info->decorate;
00275 clone_info->compose=draw_info->compose;
00276 if (draw_info->text != (char *) NULL)
00277 (void) CloneString(&clone_info->text,draw_info->text);
00278 if (draw_info->font != (char *) NULL)
00279 (void) CloneString(&clone_info->font,draw_info->font);
00280 if (draw_info->metrics != (char *) NULL)
00281 (void) CloneString(&clone_info->metrics,draw_info->metrics);
00282 if (draw_info->family != (char *) NULL)
00283 (void) CloneString(&clone_info->family,draw_info->family);
00284 clone_info->style=draw_info->style;
00285 clone_info->stretch=draw_info->stretch;
00286 clone_info->weight=draw_info->weight;
00287 if (draw_info->encoding != (char *) NULL)
00288 (void) CloneString(&clone_info->encoding,draw_info->encoding);
00289 clone_info->pointsize=draw_info->pointsize;
00290 clone_info->kerning=draw_info->kerning;
00291 clone_info->interline_spacing=draw_info->interline_spacing;
00292 clone_info->interword_spacing=draw_info->interword_spacing;
00293 if (draw_info->density != (char *) NULL)
00294 (void) CloneString(&clone_info->density,draw_info->density);
00295 clone_info->align=draw_info->align;
00296 clone_info->undercolor=draw_info->undercolor;
00297 clone_info->border_color=draw_info->border_color;
00298 if (draw_info->server_name != (char *) NULL)
00299 (void) CloneString(&clone_info->server_name,draw_info->server_name);
00300 if (draw_info->dash_pattern != (double *) NULL)
00301 {
00302 register long
00303 x;
00304
00305 for (x=0; draw_info->dash_pattern[x] != 0.0; x++) ;
00306 clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) x+1UL,
00307 sizeof(*clone_info->dash_pattern));
00308 if (clone_info->dash_pattern == (double *) NULL)
00309 ThrowFatalException(ResourceLimitFatalError,
00310 "UnableToAllocateDashPattern");
00311 (void) CopyMagickMemory(clone_info->dash_pattern,draw_info->dash_pattern,
00312 (size_t) (x+1)*sizeof(*clone_info->dash_pattern));
00313 }
00314 clone_info->gradient=draw_info->gradient;
00315 if (draw_info->gradient.stops != (StopInfo *) NULL)
00316 {
00317 unsigned long
00318 number_stops;
00319
00320 number_stops=clone_info->gradient.number_stops;
00321 clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t)
00322 number_stops,sizeof(*clone_info->gradient.stops));
00323 if (clone_info->gradient.stops == (StopInfo *) NULL)
00324 ThrowFatalException(ResourceLimitFatalError,
00325 "UnableToAllocateDashPattern");
00326 (void) CopyMagickMemory(clone_info->gradient.stops,
00327 draw_info->gradient.stops,(size_t) number_stops*
00328 sizeof(*clone_info->gradient.stops));
00329 }
00330 if (draw_info->clip_mask != (char *) NULL)
00331 (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
00332 clone_info->bounds=draw_info->bounds;
00333 clone_info->clip_units=draw_info->clip_units;
00334 clone_info->render=draw_info->render;
00335 clone_info->opacity=draw_info->opacity;
00336 clone_info->element_reference=draw_info->element_reference;
00337 clone_info->debug=IsEventLogging();
00338 return(clone_info);
00339 }
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372 #if defined(__cplusplus) || defined(c_plusplus)
00373 extern "C" {
00374 #endif
00375
00376 static int CompareEdges(const void *x,const void *y)
00377 {
00378 register const EdgeInfo
00379 *p,
00380 *q;
00381
00382
00383
00384
00385 p=(const EdgeInfo *) x;
00386 q=(const EdgeInfo *) y;
00387 if ((p->points[0].y-MagickEpsilon) > q->points[0].y)
00388 return(1);
00389 if ((p->points[0].y+MagickEpsilon) < q->points[0].y)
00390 return(-1);
00391 if ((p->points[0].x-MagickEpsilon) > q->points[0].x)
00392 return(1);
00393 if ((p->points[0].x+MagickEpsilon) < q->points[0].x)
00394 return(-1);
00395 if (((p->points[1].x-p->points[0].x)*(q->points[1].y-q->points[0].y)-
00396 (p->points[1].y-p->points[0].y)*(q->points[1].x-q->points[0].x)) > 0.0)
00397 return(1);
00398 return(-1);
00399 }
00400
00401 #if defined(__cplusplus) || defined(c_plusplus)
00402 }
00403 #endif
00404
00405 static void LogPolygonInfo(const PolygonInfo *polygon_info)
00406 {
00407 register EdgeInfo
00408 *p;
00409
00410 register long
00411 i,
00412 j;
00413
00414 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge");
00415 p=polygon_info->edges;
00416 for (i=0; i < (long) polygon_info->number_edges; i++)
00417 {
00418 (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %lu:",i);
00419 (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s",
00420 p->direction != MagickFalse ? "down" : "up");
00421 (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s",
00422 p->ghostline != MagickFalse ? "transparent" : "opaque");
00423 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
00424 " bounds: %g,%g - %g,%g",p->bounds.x1,p->bounds.y1,
00425 p->bounds.x2,p->bounds.y2);
00426 for (j=0; j < (long) p->number_points; j++)
00427 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g,%g",
00428 p->points[j].x,p->points[j].y);
00429 p++;
00430 }
00431 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge");
00432 }
00433
00434 static void ReversePoints(PointInfo *points,const unsigned long number_points)
00435 {
00436 PointInfo
00437 point;
00438
00439 register long
00440 i;
00441
00442 for (i=0; i < (long) (number_points >> 1); i++)
00443 {
00444 point=points[i];
00445 points[i]=points[number_points-(i+1)];
00446 points[number_points-(i+1)]=point;
00447 }
00448 }
00449
00450 static PolygonInfo *ConvertPathToPolygon(
00451 const DrawInfo *magick_unused(draw_info),const PathInfo *path_info)
00452 {
00453 long
00454 direction,
00455 next_direction;
00456
00457 PointInfo
00458 point,
00459 *points;
00460
00461 PolygonInfo
00462 *polygon_info;
00463
00464 SegmentInfo
00465 bounds;
00466
00467 register long
00468 i,
00469 n;
00470
00471 MagickBooleanType
00472 ghostline;
00473
00474 unsigned long
00475 edge,
00476 number_edges,
00477 number_points;
00478
00479
00480
00481
00482 polygon_info=(PolygonInfo *) AcquireAlignedMemory(1,sizeof(*polygon_info));
00483 if (polygon_info == (PolygonInfo *) NULL)
00484 return((PolygonInfo *) NULL);
00485 number_edges=16;
00486 polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory((size_t) number_edges,
00487 sizeof(*polygon_info->edges));
00488 if (polygon_info->edges == (EdgeInfo *) NULL)
00489 return((PolygonInfo *) NULL);
00490 direction=0;
00491 edge=0;
00492 ghostline=MagickFalse;
00493 n=0;
00494 number_points=0;
00495 points=(PointInfo *) NULL;
00496 (void) ResetMagickMemory(&point,0,sizeof(point));
00497 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
00498 for (i=0; path_info[i].code != EndCode; i++)
00499 {
00500 if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
00501 (path_info[i].code == GhostlineCode))
00502 {
00503
00504
00505
00506 if ((points != (PointInfo *) NULL) && (n >= 2))
00507 {
00508 if (edge == number_edges)
00509 {
00510 number_edges<<=1;
00511 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
00512 polygon_info->edges,(size_t) number_edges,
00513 sizeof(*polygon_info->edges));
00514 if (polygon_info->edges == (EdgeInfo *) NULL)
00515 return((PolygonInfo *) NULL);
00516 }
00517 polygon_info->edges[edge].number_points=(unsigned long) n;
00518 polygon_info->edges[edge].scanline=(-1.0);
00519 polygon_info->edges[edge].highwater=0;
00520 polygon_info->edges[edge].ghostline=ghostline;
00521 polygon_info->edges[edge].direction=(long) (direction > 0);
00522 if (direction < 0)
00523 ReversePoints(points,(unsigned long) n);
00524 polygon_info->edges[edge].points=points;
00525 polygon_info->edges[edge].bounds=bounds;
00526 polygon_info->edges[edge].bounds.y1=points[0].y;
00527 polygon_info->edges[edge].bounds.y2=points[n-1].y;
00528 points=(PointInfo *) NULL;
00529 ghostline=MagickFalse;
00530 edge++;
00531 }
00532 if (points == (PointInfo *) NULL)
00533 {
00534 number_points=16;
00535 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
00536 sizeof(*points));
00537 if (points == (PointInfo *) NULL)
00538 return((PolygonInfo *) NULL);
00539 }
00540 ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
00541 point=path_info[i].point;
00542 points[0]=point;
00543 bounds.x1=point.x;
00544 bounds.x2=point.x;
00545 direction=0;
00546 n=1;
00547 continue;
00548 }
00549
00550
00551
00552 next_direction=((path_info[i].point.y > point.y) ||
00553 ((path_info[i].point.y == point.y) &&
00554 (path_info[i].point.x > point.x))) ? 1 : -1;
00555 if ((direction != 0) && (direction != next_direction))
00556 {
00557
00558
00559
00560 point=points[n-1];
00561 if (edge == number_edges)
00562 {
00563 number_edges<<=1;
00564 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
00565 polygon_info->edges,(size_t) number_edges,
00566 sizeof(*polygon_info->edges));
00567 if (polygon_info->edges == (EdgeInfo *) NULL)
00568 return((PolygonInfo *) NULL);
00569 }
00570 polygon_info->edges[edge].number_points=(unsigned long) n;
00571 polygon_info->edges[edge].scanline=(-1.0);
00572 polygon_info->edges[edge].highwater=0;
00573 polygon_info->edges[edge].ghostline=ghostline;
00574 polygon_info->edges[edge].direction=(long) (direction > 0);
00575 if (direction < 0)
00576 ReversePoints(points,(unsigned long) n);
00577 polygon_info->edges[edge].points=points;
00578 polygon_info->edges[edge].bounds=bounds;
00579 polygon_info->edges[edge].bounds.y1=points[0].y;
00580 polygon_info->edges[edge].bounds.y2=points[n-1].y;
00581 number_points=16;
00582 points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
00583 sizeof(*points));
00584 if (points == (PointInfo *) NULL)
00585 return((PolygonInfo *) NULL);
00586 n=1;
00587 ghostline=MagickFalse;
00588 points[0]=point;
00589 bounds.x1=point.x;
00590 bounds.x2=point.x;
00591 edge++;
00592 }
00593 direction=next_direction;
00594 if (points == (PointInfo *) NULL)
00595 continue;
00596 if (n == (long) number_points)
00597 {
00598 number_points<<=1;
00599 points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
00600 sizeof(*points));
00601 if (points == (PointInfo *) NULL)
00602 return((PolygonInfo *) NULL);
00603 }
00604 point=path_info[i].point;
00605 points[n]=point;
00606 if (point.x < bounds.x1)
00607 bounds.x1=point.x;
00608 if (point.x > bounds.x2)
00609 bounds.x2=point.x;
00610 n++;
00611 }
00612 if (points != (PointInfo *) NULL)
00613 {
00614 if (n < 2)
00615 points=(PointInfo *) RelinquishMagickMemory(points);
00616 else
00617 {
00618 if (edge == number_edges)
00619 {
00620 number_edges<<=1;
00621 polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
00622 polygon_info->edges,(size_t) number_edges,
00623 sizeof(*polygon_info->edges));
00624 if (polygon_info->edges == (EdgeInfo *) NULL)
00625 return((PolygonInfo *) NULL);
00626 }
00627 polygon_info->edges[edge].number_points=(unsigned long) n;
00628 polygon_info->edges[edge].scanline=(-1.0);
00629 polygon_info->edges[edge].highwater=0;
00630 polygon_info->edges[edge].ghostline=ghostline;
00631 polygon_info->edges[edge].direction=(long) (direction > 0);
00632 if (direction < 0)
00633 ReversePoints(points,(unsigned long) n);
00634 polygon_info->edges[edge].points=points;
00635 polygon_info->edges[edge].bounds=bounds;
00636 polygon_info->edges[edge].bounds.y1=points[0].y;
00637 polygon_info->edges[edge].bounds.y2=points[n-1].y;
00638 ghostline=MagickFalse;
00639 edge++;
00640 }
00641 }
00642 polygon_info->number_edges=edge;
00643 qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
00644 sizeof(*polygon_info->edges),CompareEdges);
00645 if (IsEventLogging() != MagickFalse)
00646 LogPolygonInfo(polygon_info);
00647 return(polygon_info);
00648 }
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681 static void LogPathInfo(const PathInfo *path_info)
00682 {
00683 register const PathInfo
00684 *p;
00685
00686 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path");
00687 for (p=path_info; p->code != EndCode; p++)
00688 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
00689 " %g,%g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
00690 "moveto ghostline" : p->code == OpenCode ? "moveto open" :
00691 p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
00692 "?");
00693 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path");
00694 }
00695
00696 static PathInfo *ConvertPrimitiveToPath(
00697 const DrawInfo *magick_unused(draw_info),const PrimitiveInfo *primitive_info)
00698 {
00699 long
00700 coordinates,
00701 start;
00702
00703 PathInfo
00704 *path_info;
00705
00706 PathInfoCode
00707 code;
00708
00709 PointInfo
00710 p,
00711 q;
00712
00713 register long
00714 i,
00715 n;
00716
00717
00718
00719
00720 switch (primitive_info->primitive)
00721 {
00722 case PointPrimitive:
00723 case ColorPrimitive:
00724 case MattePrimitive:
00725 case TextPrimitive:
00726 case ImagePrimitive:
00727 return((PathInfo *) NULL);
00728 default:
00729 break;
00730 }
00731 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
00732 path_info=(PathInfo *) AcquireQuantumMemory((size_t) (2UL*i+3UL),
00733 sizeof(*path_info));
00734 if (path_info == (PathInfo *) NULL)
00735 return((PathInfo *) NULL);
00736 coordinates=0;
00737 n=0;
00738 p.x=(-1.0);
00739 p.y=(-1.0);
00740 q.x=(-1.0);
00741 q.y=(-1.0);
00742 start=0;
00743 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
00744 {
00745 code=LineToCode;
00746 if (coordinates <= 0)
00747 {
00748 coordinates=(long) primitive_info[i].coordinates;
00749 p=primitive_info[i].point;
00750 start=n;
00751 code=MoveToCode;
00752 }
00753 coordinates--;
00754
00755
00756
00757 if ((i == 0) || (fabs(q.x-primitive_info[i].point.x) > MagickEpsilon) ||
00758 (fabs(q.y-primitive_info[i].point.y) > MagickEpsilon))
00759 {
00760 path_info[n].code=code;
00761 path_info[n].point=primitive_info[i].point;
00762 q=primitive_info[i].point;
00763 n++;
00764 }
00765 if (coordinates > 0)
00766 continue;
00767 if ((fabs(p.x-primitive_info[i].point.x) <= MagickEpsilon) &&
00768 (fabs(p.y-primitive_info[i].point.y) <= MagickEpsilon))
00769 continue;
00770
00771
00772
00773 path_info[start].code=OpenCode;
00774 path_info[n].code=GhostlineCode;
00775 path_info[n].point=primitive_info[i].point;
00776 n++;
00777 path_info[n].code=LineToCode;
00778 path_info[n].point=p;
00779 n++;
00780 }
00781 path_info[n].code=EndCode;
00782 path_info[n].point.x=0.0;
00783 path_info[n].point.y=0.0;
00784 if (IsEventLogging() != MagickFalse)
00785 LogPathInfo(path_info);
00786 return(path_info);
00787 }
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812 MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
00813 {
00814 if (draw_info->debug != MagickFalse)
00815 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00816 assert(draw_info != (DrawInfo *) NULL);
00817 assert(draw_info->signature == MagickSignature);
00818 if (draw_info->primitive != (char *) NULL)
00819 draw_info->primitive=DestroyString(draw_info->primitive);
00820 if (draw_info->text != (char *) NULL)
00821 draw_info->text=DestroyString(draw_info->text);
00822 if (draw_info->geometry != (char *) NULL)
00823 draw_info->geometry=DestroyString(draw_info->geometry);
00824 if (draw_info->tile != (Image *) NULL)
00825 draw_info->tile=DestroyImage(draw_info->tile);
00826 if (draw_info->fill_pattern != (Image *) NULL)
00827 draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
00828 if (draw_info->stroke_pattern != (Image *) NULL)
00829 draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
00830 if (draw_info->font != (char *) NULL)
00831 draw_info->font=DestroyString(draw_info->font);
00832 if (draw_info->metrics != (char *) NULL)
00833 draw_info->metrics=DestroyString(draw_info->metrics);
00834 if (draw_info->family != (char *) NULL)
00835 draw_info->family=DestroyString(draw_info->family);
00836 if (draw_info->encoding != (char *) NULL)
00837 draw_info->encoding=DestroyString(draw_info->encoding);
00838 if (draw_info->density != (char *) NULL)
00839 draw_info->density=DestroyString(draw_info->density);
00840 if (draw_info->server_name != (char *) NULL)
00841 draw_info->server_name=(char *)
00842 RelinquishMagickMemory(draw_info->server_name);
00843 if (draw_info->dash_pattern != (double *) NULL)
00844 draw_info->dash_pattern=(double *) RelinquishMagickMemory(
00845 draw_info->dash_pattern);
00846 if (draw_info->gradient.stops != (StopInfo *) NULL)
00847 draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
00848 draw_info->gradient.stops);
00849 if (draw_info->clip_mask != (char *) NULL)
00850 draw_info->clip_mask=DestroyString(draw_info->clip_mask);
00851 draw_info->signature=(~MagickSignature);
00852 draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
00853 return(draw_info);
00854 }
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880 static unsigned long DestroyEdge(PolygonInfo *polygon_info,
00881 const unsigned long edge)
00882 {
00883 assert(edge < polygon_info->number_edges);
00884 polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
00885 polygon_info->edges[edge].points);
00886 polygon_info->number_edges--;
00887 if (edge < polygon_info->number_edges)
00888 (void) CopyMagickMemory(polygon_info->edges+edge,polygon_info->edges+edge+1,
00889 (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges));
00890 return(polygon_info->number_edges);
00891 }
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915 static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
00916 {
00917 register long
00918 i;
00919
00920 for (i=0; i < (long) polygon_info->number_edges; i++)
00921 polygon_info->edges[i].points=(PointInfo *)
00922 RelinquishMagickMemory(polygon_info->edges[i].points);
00923 polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges);
00924 return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
00925 }
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955 static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine,
00956 const double y,const SegmentInfo *edge)
00957 {
00958 double
00959 intercept,
00960 z;
00961
00962 register double
00963 x;
00964
00965 SegmentInfo
00966 inverse_edge;
00967
00968
00969
00970
00971 inverse_edge.x1=edge->x1;
00972 inverse_edge.y1=edge->y1;
00973 inverse_edge.x2=edge->x2;
00974 inverse_edge.y2=edge->y2;
00975 z=affine->ry*y+affine->tx;
00976 if (affine->sx > MagickEpsilon)
00977 {
00978 intercept=(-z/affine->sx);
00979 x=intercept+MagickEpsilon;
00980 if (x > inverse_edge.x1)
00981 inverse_edge.x1=x;
00982 intercept=(-z+(double) image->columns)/affine->sx;
00983 x=intercept-MagickEpsilon;
00984 if (x < inverse_edge.x2)
00985 inverse_edge.x2=x;
00986 }
00987 else
00988 if (affine->sx < -MagickEpsilon)
00989 {
00990 intercept=(-z+(double) image->columns)/affine->sx;
00991 x=intercept+MagickEpsilon;
00992 if (x > inverse_edge.x1)
00993 inverse_edge.x1=x;
00994 intercept=(-z/affine->sx);
00995 x=intercept-MagickEpsilon;
00996 if (x < inverse_edge.x2)
00997 inverse_edge.x2=x;
00998 }
00999 else
01000 if ((z < 0.0) || ((unsigned long) floor(z+0.5) >= image->columns))
01001 {
01002 inverse_edge.x2=edge->x1;
01003 return(inverse_edge);
01004 }
01005
01006
01007
01008 z=affine->sy*y+affine->ty;
01009 if (affine->rx > MagickEpsilon)
01010 {
01011 intercept=(-z/affine->rx);
01012 x=intercept+MagickEpsilon;
01013 if (x > inverse_edge.x1)
01014 inverse_edge.x1=x;
01015 intercept=(-z+(double) image->rows)/affine->rx;
01016 x=intercept-MagickEpsilon;
01017 if (x < inverse_edge.x2)
01018 inverse_edge.x2=x;
01019 }
01020 else
01021 if (affine->rx < -MagickEpsilon)
01022 {
01023 intercept=(-z+(double) image->rows)/affine->rx;
01024 x=intercept+MagickEpsilon;
01025 if (x > inverse_edge.x1)
01026 inverse_edge.x1=x;
01027 intercept=(-z/affine->rx);
01028 x=intercept-MagickEpsilon;
01029 if (x < inverse_edge.x2)
01030 inverse_edge.x2=x;
01031 }
01032 else
01033 if ((z < 0.0) || ((unsigned long) floor(z+0.5) >= image->rows))
01034 {
01035 inverse_edge.x2=edge->x2;
01036 return(inverse_edge);
01037 }
01038 return(inverse_edge);
01039 }
01040
01041 static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine)
01042 {
01043 AffineMatrix
01044 inverse_affine;
01045
01046 double
01047 determinant;
01048
01049 determinant=1.0/(affine->sx*affine->sy-affine->rx*affine->ry);
01050 inverse_affine.sx=determinant*affine->sy;
01051 inverse_affine.rx=determinant*(-affine->rx);
01052 inverse_affine.ry=determinant*(-affine->ry);
01053 inverse_affine.sy=determinant*affine->sx;
01054 inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
01055 inverse_affine.ry;
01056 inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
01057 inverse_affine.sy;
01058 return(inverse_affine);
01059 }
01060
01061 static inline long MagickAbsoluteValue(const long x)
01062 {
01063 if (x < 0)
01064 return(-x);
01065 return(x);
01066 }
01067
01068 static inline double MagickMax(const double x,const double y)
01069 {
01070 if (x > y)
01071 return(x);
01072 return(y);
01073 }
01074
01075 static inline double MagickMin(const double x,const double y)
01076 {
01077 if (x < y)
01078 return(x);
01079 return(y);
01080 }
01081
01082 MagickExport MagickBooleanType DrawAffineImage(Image *image,
01083 const Image *source,const AffineMatrix *affine)
01084 {
01085 AffineMatrix
01086 inverse_affine;
01087
01088 CacheView
01089 *image_view,
01090 *source_view;
01091
01092 ExceptionInfo
01093 *exception;
01094
01095 long
01096 y;
01097
01098 MagickBooleanType
01099 status;
01100
01101 MagickPixelPacket
01102 zero;
01103
01104 PointInfo
01105 extent[4],
01106 min,
01107 max,
01108 point;
01109
01110 register long
01111 i;
01112
01113 ResampleFilter
01114 **restrict resample_filter;
01115
01116 SegmentInfo
01117 edge;
01118
01119
01120
01121
01122 assert(image != (Image *) NULL);
01123 assert(image->signature == MagickSignature);
01124 if (image->debug != MagickFalse)
01125 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01126 assert(source != (const Image *) NULL);
01127 assert(source->signature == MagickSignature);
01128 assert(affine != (AffineMatrix *) NULL);
01129 extent[0].x=0.0;
01130 extent[0].y=0.0;
01131 extent[1].x=(double) source->columns-1.0;
01132 extent[1].y=0.0;
01133 extent[2].x=(double) source->columns-1.0;
01134 extent[2].y=(double) source->rows-1.0;
01135 extent[3].x=0.0;
01136 extent[3].y=(double) source->rows-1.0;
01137 for (i=0; i < 4; i++)
01138 {
01139 point=extent[i];
01140 extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
01141 extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
01142 }
01143 min=extent[0];
01144 max=extent[0];
01145 for (i=1; i < 4; i++)
01146 {
01147 if (min.x > extent[i].x)
01148 min.x=extent[i].x;
01149 if (min.y > extent[i].y)
01150 min.y=extent[i].y;
01151 if (max.x < extent[i].x)
01152 max.x=extent[i].x;
01153 if (max.y < extent[i].y)
01154 max.y=extent[i].y;
01155 }
01156
01157
01158
01159 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
01160 return(MagickFalse);
01161 status=MagickTrue;
01162 edge.x1=MagickMax(min.x,0.0);
01163 edge.y1=MagickMax(min.y,0.0);
01164 edge.x2=MagickMin(max.x,(double) image->columns-1.0);
01165 edge.y2=MagickMin(max.y,(double) image->rows-1.0);
01166 inverse_affine=InverseAffineMatrix(affine);
01167 GetMagickPixelPacket(image,&zero);
01168 exception=(&image->exception);
01169 resample_filter=AcquireResampleFilterThreadSet(source,
01170 UndefinedVirtualPixelMethod,MagickTrue,exception);
01171 image_view=AcquireCacheView(image);
01172 source_view=AcquireCacheView(source);
01173 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01174 #pragma omp parallel for schedule(dynamic,4) shared(status)
01175 #endif
01176 for (y=(long) ceil(edge.y1-0.5); y <= (long) floor(edge.y2+0.5); y++)
01177 {
01178 long
01179 x_offset;
01180
01181 MagickPixelPacket
01182 composite,
01183 pixel;
01184
01185 PointInfo
01186 point;
01187
01188 register IndexPacket
01189 *restrict indexes;
01190
01191 register long
01192 id,
01193 x;
01194
01195 register PixelPacket
01196 *restrict q;
01197
01198 SegmentInfo
01199 inverse_edge;
01200
01201 inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
01202 if (inverse_edge.x2 < inverse_edge.x1)
01203 continue;
01204 q=GetCacheViewAuthenticPixels(image_view,(long) ceil(inverse_edge.x1-0.5),y,
01205 (unsigned long) ((long) floor(inverse_edge.x2+0.5)-(long) floor(
01206 inverse_edge.x1+0.5)+1),1,exception);
01207 if (q == (PixelPacket *) NULL)
01208 continue;
01209 id=GetOpenMPThreadId();
01210 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01211 pixel=zero;
01212 composite=zero;
01213 x_offset=0;
01214 for (x=(long) ceil(inverse_edge.x1-0.5); x <= (long) floor(inverse_edge.x2+0.5); x++)
01215 {
01216 point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
01217 inverse_affine.tx;
01218 point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
01219 inverse_affine.ty;
01220 (void) ResamplePixelColor(resample_filter[id],point.x,point.y,&pixel);
01221 SetMagickPixelPacket(image,q,indexes+x_offset,&composite);
01222 MagickPixelCompositeOver(&pixel,pixel.opacity,&composite,
01223 composite.opacity,&composite);
01224 SetPixelPacket(image,&composite,q,indexes+x_offset);
01225 x_offset++;
01226 q++;
01227 }
01228 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01229 status=MagickFalse;
01230 }
01231 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
01232 source_view=DestroyCacheView(source_view);
01233 image_view=DestroyCacheView(image_view);
01234 return(status);
01235 }
01236
01237
01238
01239
01240
01241
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265 static void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
01266 const PolygonInfo *polygon_info)
01267 {
01268 DrawInfo
01269 *clone_info;
01270
01271 long
01272 coordinates;
01273
01274 MagickRealType
01275 mid;
01276
01277 PointInfo
01278 end,
01279 resolution,
01280 start;
01281
01282 PrimitiveInfo
01283 primitive_info[6];
01284
01285 register long
01286 i;
01287
01288 SegmentInfo
01289 bounds;
01290
01291 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
01292 (void) QueryColorDatabase("#0000",&clone_info->fill,&image->exception);
01293 resolution.x=DefaultResolution;
01294 resolution.y=DefaultResolution;
01295 if (clone_info->density != (char *) NULL)
01296 {
01297 GeometryInfo
01298 geometry_info;
01299
01300 MagickStatusType
01301 flags;
01302
01303 flags=ParseGeometry(clone_info->density,&geometry_info);
01304 resolution.x=geometry_info.rho;
01305 resolution.y=geometry_info.sigma;
01306 if ((flags & SigmaValue) == MagickFalse)
01307 resolution.y=resolution.x;
01308 }
01309 mid=(resolution.x/72.0)*ExpandAffine(&clone_info->affine)*
01310 clone_info->stroke_width/2.0;
01311 bounds.x1=0.0;
01312 bounds.y1=0.0;
01313 bounds.x2=0.0;
01314 bounds.y2=0.0;
01315 if (polygon_info != (PolygonInfo *) NULL)
01316 {
01317 bounds=polygon_info->edges[0].bounds;
01318 for (i=1; i < (long) polygon_info->number_edges; i++)
01319 {
01320 if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
01321 bounds.x1=polygon_info->edges[i].bounds.x1;
01322 if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
01323 bounds.y1=polygon_info->edges[i].bounds.y1;
01324 if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
01325 bounds.x2=polygon_info->edges[i].bounds.x2;
01326 if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
01327 bounds.y2=polygon_info->edges[i].bounds.y2;
01328 }
01329 bounds.x1-=mid;
01330 bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
01331 image->columns ? (double) image->columns-1 : bounds.x1;
01332 bounds.y1-=mid;
01333 bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
01334 image->rows ? (double) image->rows-1 : bounds.y1;
01335 bounds.x2+=mid;
01336 bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
01337 image->columns ? (double) image->columns-1 : bounds.x2;
01338 bounds.y2+=mid;
01339 bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
01340 image->rows ? (double) image->rows-1 : bounds.y2;
01341 for (i=0; i < (long) polygon_info->number_edges; i++)
01342 {
01343 if (polygon_info->edges[i].direction != 0)
01344 (void) QueryColorDatabase("red",&clone_info->stroke,
01345 &image->exception);
01346 else
01347 (void) QueryColorDatabase("green",&clone_info->stroke,
01348 &image->exception);
01349 start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
01350 start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
01351 end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
01352 end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
01353 primitive_info[0].primitive=RectanglePrimitive;
01354 TraceRectangle(primitive_info,start,end);
01355 primitive_info[0].method=ReplaceMethod;
01356 coordinates=(long) primitive_info[0].coordinates;
01357 primitive_info[coordinates].primitive=UndefinedPrimitive;
01358 (void) DrawPrimitive(image,clone_info,primitive_info);
01359 }
01360 }
01361 (void) QueryColorDatabase("blue",&clone_info->stroke,&image->exception);
01362 start.x=(double) (bounds.x1-mid);
01363 start.y=(double) (bounds.y1-mid);
01364 end.x=(double) (bounds.x2+mid);
01365 end.y=(double) (bounds.y2+mid);
01366 primitive_info[0].primitive=RectanglePrimitive;
01367 TraceRectangle(primitive_info,start,end);
01368 primitive_info[0].method=ReplaceMethod;
01369 coordinates=(long) primitive_info[0].coordinates;
01370 primitive_info[coordinates].primitive=UndefinedPrimitive;
01371 (void) DrawPrimitive(image,clone_info,primitive_info);
01372 clone_info=DestroyDrawInfo(clone_info);
01373 }
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400
01401
01402 MagickExport MagickBooleanType DrawClipPath(Image *image,
01403 const DrawInfo *draw_info,const char *name)
01404 {
01405 char
01406 clip_mask[MaxTextExtent];
01407
01408 const char
01409 *value;
01410
01411 DrawInfo
01412 *clone_info;
01413
01414 MagickStatusType
01415 status;
01416
01417 assert(image != (Image *) NULL);
01418 assert(image->signature == MagickSignature);
01419 if (image->debug != MagickFalse)
01420 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01421 assert(draw_info != (const DrawInfo *) NULL);
01422 (void) FormatMagickString(clip_mask,MaxTextExtent,"%s",name);
01423 value=GetImageArtifact(image,clip_mask);
01424 if (value == (const char *) NULL)
01425 return(MagickFalse);
01426 if (image->clip_mask == (Image *) NULL)
01427 {
01428 Image
01429 *clip_mask;
01430
01431 clip_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
01432 &image->exception);
01433 if (clip_mask == (Image *) NULL)
01434 return(MagickFalse);
01435 (void) SetImageClipMask(image,clip_mask);
01436 clip_mask=DestroyImage(clip_mask);
01437 }
01438 (void) QueryColorDatabase("#00000000",&image->clip_mask->background_color,
01439 &image->exception);
01440 image->clip_mask->background_color.opacity=(Quantum) TransparentOpacity;
01441 (void) SetImageBackgroundColor(image->clip_mask);
01442 if (image->debug != MagickFalse)
01443 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
01444 draw_info->clip_mask);
01445 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
01446 (void) CloneString(&clone_info->primitive,value);
01447 (void) QueryColorDatabase("#ffffff",&clone_info->fill,&image->exception);
01448 clone_info->clip_mask=(char *) NULL;
01449 status=DrawImage(image->clip_mask,clone_info);
01450 status|=NegateImage(image->clip_mask,MagickFalse);
01451 clone_info=DestroyDrawInfo(clone_info);
01452 if (image->debug != MagickFalse)
01453 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path");
01454 return(status != 0 ? MagickTrue : MagickFalse);
01455 }
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468
01469
01470
01471
01472
01473
01474
01475
01476
01477
01478
01479
01480
01481
01482
01483
01484
01485
01486 static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
01487 const PrimitiveInfo *primitive_info,Image *image)
01488 {
01489 DrawInfo
01490 *clone_info;
01491
01492 long
01493 j,
01494 n;
01495
01496 MagickRealType
01497 length,
01498 maximum_length,
01499 offset,
01500 scale,
01501 total_length;
01502
01503 MagickStatusType
01504 status;
01505
01506 PrimitiveInfo
01507 *dash_polygon;
01508
01509 register long
01510 i;
01511
01512 register MagickRealType
01513 dx,
01514 dy;
01515
01516 unsigned long
01517 number_vertices;
01518
01519 assert(draw_info != (const DrawInfo *) NULL);
01520 if (image->debug != MagickFalse)
01521 (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash");
01522 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
01523 clone_info->miterlimit=0;
01524 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
01525 number_vertices=(unsigned long) i;
01526 dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
01527 (2UL*number_vertices+1UL),sizeof(*dash_polygon));
01528 if (dash_polygon == (PrimitiveInfo *) NULL)
01529 return(MagickFalse);
01530 dash_polygon[0]=primitive_info[0];
01531 scale=ExpandAffine(&draw_info->affine);
01532 length=scale*(draw_info->dash_pattern[0]-0.5);
01533 offset=draw_info->dash_offset != 0.0 ? scale*draw_info->dash_offset : 0.0;
01534 j=1;
01535 for (n=0; offset > 0.0; j=0)
01536 {
01537 if (draw_info->dash_pattern[n] <= 0.0)
01538 break;
01539 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
01540 if (offset > length)
01541 {
01542 offset-=length;
01543 n++;
01544 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
01545 continue;
01546 }
01547 if (offset < length)
01548 {
01549 length-=offset;
01550 offset=0.0;
01551 break;
01552 }
01553 offset=0.0;
01554 n++;
01555 }
01556 status=MagickTrue;
01557 maximum_length=0.0;
01558 total_length=0.0;
01559 for (i=1; i < (long) number_vertices; i++)
01560 {
01561 dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
01562 dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
01563 maximum_length=hypot((double) dx,dy);
01564 if (length == 0.0)
01565 {
01566 n++;
01567 if (draw_info->dash_pattern[n] == 0.0)
01568 n=0;
01569 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
01570 }
01571 for (total_length=0.0; (total_length+length) < maximum_length; )
01572 {
01573 total_length+=length;
01574 if ((n & 0x01) != 0)
01575 {
01576 dash_polygon[0]=primitive_info[0];
01577 dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
01578 total_length/maximum_length);
01579 dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
01580 total_length/maximum_length);
01581 j=1;
01582 }
01583 else
01584 {
01585 if ((j+1) > (long) (2*number_vertices))
01586 break;
01587 dash_polygon[j]=primitive_info[i-1];
01588 dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
01589 total_length/maximum_length);
01590 dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
01591 total_length/maximum_length);
01592 dash_polygon[j].coordinates=1;
01593 j++;
01594 dash_polygon[0].coordinates=(unsigned long) j;
01595 dash_polygon[j].primitive=UndefinedPrimitive;
01596 status|=DrawStrokePolygon(image,clone_info,dash_polygon);
01597 }
01598 n++;
01599 if (draw_info->dash_pattern[n] == 0.0)
01600 n=0;
01601 length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
01602 }
01603 length-=(maximum_length-total_length);
01604 if ((n & 0x01) != 0)
01605 continue;
01606 dash_polygon[j]=primitive_info[i];
01607 dash_polygon[j].coordinates=1;
01608 j++;
01609 }
01610 if ((total_length < maximum_length) && ((n & 0x01) == 0) && (j > 1))
01611 {
01612 dash_polygon[j]=primitive_info[i-1];
01613 dash_polygon[j].point.x+=MagickEpsilon;
01614 dash_polygon[j].point.y+=MagickEpsilon;
01615 dash_polygon[j].coordinates=1;
01616 j++;
01617 dash_polygon[0].coordinates=(unsigned long) j;
01618 dash_polygon[j].primitive=UndefinedPrimitive;
01619 status|=DrawStrokePolygon(image,clone_info,dash_polygon);
01620 }
01621 dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
01622 clone_info=DestroyDrawInfo(clone_info);
01623 if (image->debug != MagickFalse)
01624 (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
01625 return(status != 0 ? MagickTrue : MagickFalse);
01626 }
01627
01628
01629
01630
01631
01632
01633
01634
01635
01636
01637
01638
01639
01640
01641
01642
01643
01644
01645
01646
01647
01648
01649
01650
01651
01652
01653
01654
01655
01656
01657 static inline MagickBooleanType IsPoint(const char *point)
01658 {
01659 char
01660 *p;
01661
01662 double
01663 value;
01664
01665 value=strtod(point,&p);
01666 return((value == 0.0) && (p == point) ? MagickFalse : MagickTrue);
01667 }
01668
01669 static inline void TracePoint(PrimitiveInfo *primitive_info,
01670 const PointInfo point)
01671 {
01672 primitive_info->coordinates=1;
01673 primitive_info->point=point;
01674 }
01675
01676 MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info)
01677 {
01678 #define RenderImageTag "Render/Image"
01679
01680 AffineMatrix
01681 affine,
01682 current;
01683
01684 char
01685 key[2*MaxTextExtent],
01686 keyword[MaxTextExtent],
01687 geometry[MaxTextExtent],
01688 name[MaxTextExtent],
01689 pattern[MaxTextExtent],
01690 *primitive,
01691 *token;
01692
01693 const char
01694 *q;
01695
01696 DrawInfo
01697 **graphic_context;
01698
01699 long
01700 j,
01701 k,
01702 n;
01703
01704 MagickBooleanType
01705 proceed,
01706 status;
01707
01708 MagickRealType
01709 angle,
01710 factor,
01711 primitive_extent;
01712
01713 PointInfo
01714 point;
01715
01716 PixelPacket
01717 start_color;
01718
01719 PrimitiveInfo
01720 *primitive_info;
01721
01722 PrimitiveType
01723 primitive_type;
01724
01725 register const char
01726 *p;
01727
01728 register long
01729 i,
01730 x;
01731
01732 SegmentInfo
01733 bounds;
01734
01735 size_t
01736 length;
01737
01738 unsigned long
01739 number_points;
01740
01741
01742
01743
01744 assert(image != (Image *) NULL);
01745 assert(image->signature == MagickSignature);
01746 if (image->debug != MagickFalse)
01747 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01748 assert(draw_info != (DrawInfo *) NULL);
01749 assert(draw_info->signature == MagickSignature);
01750 if (image->debug != MagickFalse)
01751 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
01752 if ((draw_info->primitive == (char *) NULL) ||
01753 (*draw_info->primitive == '\0'))
01754 return(MagickFalse);
01755 if (image->debug != MagickFalse)
01756 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
01757 if (*draw_info->primitive != '@')
01758 primitive=AcquireString(draw_info->primitive);
01759 else
01760 primitive=FileToString(draw_info->primitive+1,~0,&image->exception);
01761 if (primitive == (char *) NULL)
01762 return(MagickFalse);
01763 primitive_extent=(MagickRealType) strlen(primitive);
01764 (void) SetImageArtifact(image,"MVG",primitive);
01765 n=0;
01766
01767
01768
01769 graphic_context=(DrawInfo **) AcquireAlignedMemory(1,sizeof(*graphic_context));
01770 if (graphic_context == (DrawInfo **) NULL)
01771 {
01772 primitive=DestroyString(primitive);
01773 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
01774 image->filename);
01775 }
01776 number_points=2047;
01777 primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
01778 sizeof(*primitive_info));
01779 if (primitive_info == (PrimitiveInfo *) NULL)
01780 {
01781 primitive=DestroyString(primitive);
01782 for ( ; n >= 0; n--)
01783 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
01784 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
01785 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
01786 image->filename);
01787 }
01788 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
01789 graphic_context[n]->viewbox=image->page;
01790 if ((image->page.width == 0) || (image->page.height == 0))
01791 {
01792 graphic_context[n]->viewbox.width=image->columns;
01793 graphic_context[n]->viewbox.height=image->rows;
01794 }
01795 token=AcquireString(primitive);
01796 (void) QueryColorDatabase("#000000",&start_color,&image->exception);
01797 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
01798 return(MagickFalse);
01799 status=MagickTrue;
01800 for (q=primitive; *q != '\0'; )
01801 {
01802
01803
01804
01805 GetMagickToken(q,&q,keyword);
01806 if (*keyword == '\0')
01807 break;
01808 if (*keyword == '#')
01809 {
01810
01811
01812
01813 while ((*q != '\n') && (*q != '\0'))
01814 q++;
01815 continue;
01816 }
01817 p=q-strlen(keyword)-1;
01818 primitive_type=UndefinedPrimitive;
01819 current=graphic_context[n]->affine;
01820 GetAffineMatrix(&affine);
01821 switch (*keyword)
01822 {
01823 case ';':
01824 break;
01825 case 'a':
01826 case 'A':
01827 {
01828 if (LocaleCompare("affine",keyword) == 0)
01829 {
01830 GetMagickToken(q,&q,token);
01831 affine.sx=StringToDouble(token);
01832 GetMagickToken(q,&q,token);
01833 if (*token == ',')
01834 GetMagickToken(q,&q,token);
01835 affine.rx=StringToDouble(token);
01836 GetMagickToken(q,&q,token);
01837 if (*token == ',')
01838 GetMagickToken(q,&q,token);
01839 affine.ry=StringToDouble(token);
01840 GetMagickToken(q,&q,token);
01841 if (*token == ',')
01842 GetMagickToken(q,&q,token);
01843 affine.sy=StringToDouble(token);
01844 GetMagickToken(q,&q,token);
01845 if (*token == ',')
01846 GetMagickToken(q,&q,token);
01847 affine.tx=StringToDouble(token);
01848 GetMagickToken(q,&q,token);
01849 if (*token == ',')
01850 GetMagickToken(q,&q,token);
01851 affine.ty=StringToDouble(token);
01852 break;
01853 }
01854 if (LocaleCompare("arc",keyword) == 0)
01855 {
01856 primitive_type=ArcPrimitive;
01857 break;
01858 }
01859 status=MagickFalse;
01860 break;
01861 }
01862 case 'b':
01863 case 'B':
01864 {
01865 if (LocaleCompare("bezier",keyword) == 0)
01866 {
01867 primitive_type=BezierPrimitive;
01868 break;
01869 }
01870 if (LocaleCompare("border-color",keyword) == 0)
01871 {
01872 GetMagickToken(q,&q,token);
01873 (void) QueryColorDatabase(token,&graphic_context[n]->border_color,
01874 &image->exception);
01875 break;
01876 }
01877 status=MagickFalse;
01878 break;
01879 }
01880 case 'c':
01881 case 'C':
01882 {
01883 if (LocaleCompare("clip-path",keyword) == 0)
01884 {
01885
01886
01887
01888 GetMagickToken(q,&q,token);
01889 (void) CloneString(&graphic_context[n]->clip_mask,token);
01890 (void) DrawClipPath(image,graphic_context[n],
01891 graphic_context[n]->clip_mask);
01892 break;
01893 }
01894 if (LocaleCompare("clip-rule",keyword) == 0)
01895 {
01896 long
01897 fill_rule;
01898
01899 GetMagickToken(q,&q,token);
01900 fill_rule=ParseMagickOption(MagickFillRuleOptions,MagickFalse,
01901 token);
01902 if (fill_rule == -1)
01903 {
01904 status=MagickFalse;
01905 break;
01906 }
01907 graphic_context[n]->fill_rule=(FillRule) fill_rule;
01908 break;
01909 }
01910 if (LocaleCompare("clip-units",keyword) == 0)
01911 {
01912 long
01913 clip_units;
01914
01915 GetMagickToken(q,&q,token);
01916 clip_units=ParseMagickOption(MagickClipPathOptions,MagickFalse,
01917 token);
01918 if (clip_units == -1)
01919 {
01920 status=MagickFalse;
01921 break;
01922 }
01923 graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
01924 if (clip_units == ObjectBoundingBox)
01925 {
01926 GetAffineMatrix(¤t);
01927 affine.sx=draw_info->bounds.x2;
01928 affine.sy=draw_info->bounds.y2;
01929 affine.tx=draw_info->bounds.x1;
01930 affine.ty=draw_info->bounds.y1;
01931 break;
01932 }
01933 break;
01934 }
01935 if (LocaleCompare("circle",keyword) == 0)
01936 {
01937 primitive_type=CirclePrimitive;
01938 break;
01939 }
01940 if (LocaleCompare("color",keyword) == 0)
01941 {
01942 primitive_type=ColorPrimitive;
01943 break;
01944 }
01945 status=MagickFalse;
01946 break;
01947 }
01948 case 'd':
01949 case 'D':
01950 {
01951 if (LocaleCompare("decorate",keyword) == 0)
01952 {
01953 long
01954 decorate;
01955
01956 GetMagickToken(q,&q,token);
01957 decorate=ParseMagickOption(MagickDecorateOptions,MagickFalse,
01958 token);
01959 if (decorate == -1)
01960 {
01961 status=MagickFalse;
01962 break;
01963 }
01964 graphic_context[n]->decorate=(DecorationType) decorate;
01965 break;
01966 }
01967 status=MagickFalse;
01968 break;
01969 }
01970 case 'e':
01971 case 'E':
01972 {
01973 if (LocaleCompare("ellipse",keyword) == 0)
01974 {
01975 primitive_type=EllipsePrimitive;
01976 break;
01977 }
01978 if (LocaleCompare("encoding",keyword) == 0)
01979 {
01980 GetMagickToken(q,&q,token);
01981 (void) CloneString(&graphic_context[n]->encoding,token);
01982 break;
01983 }
01984 status=MagickFalse;
01985 break;
01986 }
01987 case 'f':
01988 case 'F':
01989 {
01990 if (LocaleCompare("fill",keyword) == 0)
01991 {
01992 GetMagickToken(q,&q,token);
01993 (void) FormatMagickString(pattern,MaxTextExtent,"%s",token);
01994 if (GetImageArtifact(image,pattern) != (const char *) NULL)
01995 (void) DrawPatternPath(image,draw_info,token,
01996 &graphic_context[n]->fill_pattern);
01997 else
01998 {
01999 status=QueryColorDatabase(token,&graphic_context[n]->fill,
02000 &image->exception);
02001 if (status == MagickFalse)
02002 {
02003 ImageInfo
02004 *pattern_info;
02005
02006 pattern_info=AcquireImageInfo();
02007 (void) CopyMagickString(pattern_info->filename,token,
02008 MaxTextExtent);
02009 graphic_context[n]->fill_pattern=
02010 ReadImage(pattern_info,&image->exception);
02011 CatchException(&image->exception);
02012 pattern_info=DestroyImageInfo(pattern_info);
02013 }
02014 }
02015 break;
02016 }
02017 if (LocaleCompare("fill-opacity",keyword) == 0)
02018 {
02019 GetMagickToken(q,&q,token);
02020 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
02021 graphic_context[n]->fill.opacity=ClampToQuantum((MagickRealType)
02022 QuantumRange*(1.0-factor*StringToDouble(token)));
02023 break;
02024 }
02025 if (LocaleCompare("fill-rule",keyword) == 0)
02026 {
02027 long
02028 fill_rule;
02029
02030 GetMagickToken(q,&q,token);
02031 fill_rule=ParseMagickOption(MagickFillRuleOptions,MagickFalse,
02032 token);
02033 if (fill_rule == -1)
02034 {
02035 status=MagickFalse;
02036 break;
02037 }
02038 graphic_context[n]->fill_rule=(FillRule) fill_rule;
02039 break;
02040 }
02041 if (LocaleCompare("font",keyword) == 0)
02042 {
02043 GetMagickToken(q,&q,token);
02044 (void) CloneString(&graphic_context[n]->font,token);
02045 if (LocaleCompare("none",token) == 0)
02046 graphic_context[n]->font=(char *)
02047 RelinquishMagickMemory(graphic_context[n]->font);
02048 break;
02049 }
02050 if (LocaleCompare("font-family",keyword) == 0)
02051 {
02052 GetMagickToken(q,&q,token);
02053 (void) CloneString(&graphic_context[n]->family,token);
02054 break;
02055 }
02056 if (LocaleCompare("font-size",keyword) == 0)
02057 {
02058 GetMagickToken(q,&q,token);
02059 graphic_context[n]->pointsize=StringToDouble(token);
02060 break;
02061 }
02062 if (LocaleCompare("font-stretch",keyword) == 0)
02063 {
02064 long
02065 stretch;
02066
02067 GetMagickToken(q,&q,token);
02068 stretch=ParseMagickOption(MagickStretchOptions,MagickFalse,token);
02069 if (stretch == -1)
02070 {
02071 status=MagickFalse;
02072 break;
02073 }
02074 graphic_context[n]->stretch=(StretchType) stretch;
02075 break;
02076 }
02077 if (LocaleCompare("font-style",keyword) == 0)
02078 {
02079 long
02080 style;
02081
02082 GetMagickToken(q,&q,token);
02083 style=ParseMagickOption(MagickStyleOptions,MagickFalse,token);
02084 if (style == -1)
02085 {
02086 status=MagickFalse;
02087 break;
02088 }
02089 graphic_context[n]->style=(StyleType) style;
02090 break;
02091 }
02092 if (LocaleCompare("font-weight",keyword) == 0)
02093 {
02094 GetMagickToken(q,&q,token);
02095 graphic_context[n]->weight=StringToUnsignedLong(token);
02096 if (LocaleCompare(token,"all") == 0)
02097 graphic_context[n]->weight=0;
02098 if (LocaleCompare(token,"bold") == 0)
02099 graphic_context[n]->weight=700;
02100 if (LocaleCompare(token,"bolder") == 0)
02101 if (graphic_context[n]->weight <= 800)
02102 graphic_context[n]->weight+=100;
02103 if (LocaleCompare(token,"lighter") == 0)
02104 if (graphic_context[n]->weight >= 100)
02105 graphic_context[n]->weight-=100;
02106 if (LocaleCompare(token,"normal") == 0)
02107 graphic_context[n]->weight=400;
02108 break;
02109 }
02110 status=MagickFalse;
02111 break;
02112 }
02113 case 'g':
02114 case 'G':
02115 {
02116 if (LocaleCompare("gradient-units",keyword) == 0)
02117 {
02118 GetMagickToken(q,&q,token);
02119 break;
02120 }
02121 if (LocaleCompare("gravity",keyword) == 0)
02122 {
02123 long
02124 gravity;
02125
02126 GetMagickToken(q,&q,token);
02127 gravity=ParseMagickOption(MagickGravityOptions,MagickFalse,token);
02128 if (gravity == -1)
02129 {
02130 status=MagickFalse;
02131 break;
02132 }
02133 graphic_context[n]->gravity=(GravityType) gravity;
02134 break;
02135 }
02136 status=MagickFalse;
02137 break;
02138 }
02139 case 'i':
02140 case 'I':
02141 {
02142 if (LocaleCompare("image",keyword) == 0)
02143 {
02144 long
02145 compose;
02146
02147 primitive_type=ImagePrimitive;
02148 GetMagickToken(q,&q,token);
02149 compose=ParseMagickOption(MagickComposeOptions,MagickFalse,token);
02150 if (compose == -1)
02151 {
02152 status=MagickFalse;
02153 break;
02154 }
02155 graphic_context[n]->compose=(CompositeOperator) compose;
02156 break;
02157 }
02158 if (LocaleCompare("interline-spacing",keyword) == 0)
02159 {
02160 GetMagickToken(q,&q,token);
02161 graphic_context[n]->interline_spacing=StringToDouble(token);
02162 break;
02163 }
02164 if (LocaleCompare("interword-spacing",keyword) == 0)
02165 {
02166 GetMagickToken(q,&q,token);
02167 graphic_context[n]->interword_spacing=StringToDouble(token);
02168 break;
02169 }
02170 status=MagickFalse;
02171 break;
02172 }
02173 case 'k':
02174 case 'K':
02175 {
02176 if (LocaleCompare("kerning",keyword) == 0)
02177 {
02178 GetMagickToken(q,&q,token);
02179 graphic_context[n]->kerning=StringToDouble(token);
02180 break;
02181 }
02182 status=MagickFalse;
02183 break;
02184 }
02185 case 'l':
02186 case 'L':
02187 {
02188 if (LocaleCompare("line",keyword) == 0)
02189 {
02190 primitive_type=LinePrimitive;
02191 break;
02192 }
02193 status=MagickFalse;
02194 break;
02195 }
02196 case 'm':
02197 case 'M':
02198 {
02199 if (LocaleCompare("matte",keyword) == 0)
02200 {
02201 primitive_type=MattePrimitive;
02202 break;
02203 }
02204 status=MagickFalse;
02205 break;
02206 }
02207 case 'o':
02208 case 'O':
02209 {
02210 if (LocaleCompare("offset",keyword) == 0)
02211 {
02212 GetMagickToken(q,&q,token);
02213 break;
02214 }
02215 if (LocaleCompare("opacity",keyword) == 0)
02216 {
02217 GetMagickToken(q,&q,token);
02218 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
02219 graphic_context[n]->opacity=ClampToQuantum((MagickRealType)
02220 QuantumRange*(1.0-((1.0-QuantumScale*graphic_context[n]->opacity)*
02221 factor*StringToDouble(token))));
02222 graphic_context[n]->fill.opacity=graphic_context[n]->opacity;
02223 graphic_context[n]->stroke.opacity=graphic_context[n]->opacity;
02224 break;
02225 }
02226 status=MagickFalse;
02227 break;
02228 }
02229 case 'p':
02230 case 'P':
02231 {
02232 if (LocaleCompare("path",keyword) == 0)
02233 {
02234 primitive_type=PathPrimitive;
02235 break;
02236 }
02237 if (LocaleCompare("point",keyword) == 0)
02238 {
02239 primitive_type=PointPrimitive;
02240 break;
02241 }
02242 if (LocaleCompare("polyline",keyword) == 0)
02243 {
02244 primitive_type=PolylinePrimitive;
02245 break;
02246 }
02247 if (LocaleCompare("polygon",keyword) == 0)
02248 {
02249 primitive_type=PolygonPrimitive;
02250 break;
02251 }
02252 if (LocaleCompare("pop",keyword) == 0)
02253 {
02254 GetMagickToken(q,&q,token);
02255 if (LocaleCompare("clip-path",token) == 0)
02256 break;
02257 if (LocaleCompare("defs",token) == 0)
02258 break;
02259 if (LocaleCompare("gradient",token) == 0)
02260 break;
02261 if (LocaleCompare("graphic-context",token) == 0)
02262 {
02263 if (n <= 0)
02264 {
02265 (void) ThrowMagickException(&image->exception,
02266 GetMagickModule(),DrawError,
02267 "UnbalancedGraphicContextPushPop","`%s'",token);
02268 n=0;
02269 break;
02270 }
02271 if (graphic_context[n]->clip_mask != (char *) NULL)
02272 if (LocaleCompare(graphic_context[n]->clip_mask,
02273 graphic_context[n-1]->clip_mask) != 0)
02274 (void) SetImageClipMask(image,(Image *) NULL);
02275 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
02276 n--;
02277 break;
02278 }
02279 if (LocaleCompare("pattern",token) == 0)
02280 break;
02281 status=MagickFalse;
02282 break;
02283 }
02284 if (LocaleCompare("push",keyword) == 0)
02285 {
02286 GetMagickToken(q,&q,token);
02287 if (LocaleCompare("clip-path",token) == 0)
02288 {
02289 char
02290 name[MaxTextExtent];
02291
02292 GetMagickToken(q,&q,token);
02293 (void) FormatMagickString(name,MaxTextExtent,"%s",token);
02294 for (p=q; *q != '\0'; )
02295 {
02296 GetMagickToken(q,&q,token);
02297 if (LocaleCompare(token,"pop") != 0)
02298 continue;
02299 GetMagickToken(q,(const char **) NULL,token);
02300 if (LocaleCompare(token,"clip-path") != 0)
02301 continue;
02302 break;
02303 }
02304 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
02305 (void) SetImageArtifact(image,name,token);
02306 GetMagickToken(q,&q,token);
02307 break;
02308 }
02309 if (LocaleCompare("gradient",token) == 0)
02310 {
02311 char
02312 key[2*MaxTextExtent],
02313 name[MaxTextExtent],
02314 type[MaxTextExtent];
02315
02316 ElementInfo
02317 element;
02318
02319 SegmentInfo
02320 segment;
02321
02322 GetMagickToken(q,&q,token);
02323 (void) CopyMagickString(name,token,MaxTextExtent);
02324 GetMagickToken(q,&q,token);
02325 (void) CopyMagickString(type,token,MaxTextExtent);
02326 GetMagickToken(q,&q,token);
02327 segment.x1=StringToDouble(token);
02328 element.cx=StringToDouble(token);
02329 GetMagickToken(q,&q,token);
02330 if (*token == ',')
02331 GetMagickToken(q,&q,token);
02332 segment.y1=StringToDouble(token);
02333 element.cy=StringToDouble(token);
02334 GetMagickToken(q,&q,token);
02335 if (*token == ',')
02336 GetMagickToken(q,&q,token);
02337 segment.x2=StringToDouble(token);
02338 element.major=StringToDouble(token);
02339 GetMagickToken(q,&q,token);
02340 if (*token == ',')
02341 GetMagickToken(q,&q,token);
02342 segment.y2=StringToDouble(token);
02343 element.minor=StringToDouble(token);
02344 if (LocaleCompare(type,"radial") == 0)
02345 {
02346 GetMagickToken(q,&q,token);
02347 if (*token == ',')
02348 GetMagickToken(q,&q,token);
02349 element.angle=StringToDouble(token);
02350 }
02351 for (p=q; *q != '\0'; )
02352 {
02353 GetMagickToken(q,&q,token);
02354 if (LocaleCompare(token,"pop") != 0)
02355 continue;
02356 GetMagickToken(q,(const char **) NULL,token);
02357 if (LocaleCompare(token,"gradient") != 0)
02358 continue;
02359 break;
02360 }
02361 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
02362 bounds.x1=graphic_context[n]->affine.sx*segment.x1+
02363 graphic_context[n]->affine.ry*segment.y1+
02364 graphic_context[n]->affine.tx;
02365 bounds.y1=graphic_context[n]->affine.rx*segment.x1+
02366 graphic_context[n]->affine.sy*segment.y1+
02367 graphic_context[n]->affine.ty;
02368 bounds.x2=graphic_context[n]->affine.sx*segment.x2+
02369 graphic_context[n]->affine.ry*segment.y2+
02370 graphic_context[n]->affine.tx;
02371 bounds.y2=graphic_context[n]->affine.rx*segment.x2+
02372 graphic_context[n]->affine.sy*segment.y2+
02373 graphic_context[n]->affine.ty;
02374 (void) FormatMagickString(key,MaxTextExtent,"%s",name);
02375 (void) SetImageArtifact(image,key,token);
02376 (void) FormatMagickString(key,MaxTextExtent,"%s-geometry",name);
02377 (void) FormatMagickString(geometry,MaxTextExtent,
02378 "%gx%g%+.15g%+.15g",
02379 MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
02380 MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
02381 bounds.x1,bounds.y1);
02382 (void) SetImageArtifact(image,key,geometry);
02383 GetMagickToken(q,&q,token);
02384 break;
02385 }
02386 if (LocaleCompare("pattern",token) == 0)
02387 {
02388 RectangleInfo
02389 bounds;
02390
02391 GetMagickToken(q,&q,token);
02392 (void) CopyMagickString(name,token,MaxTextExtent);
02393 GetMagickToken(q,&q,token);
02394 bounds.x=(long) ceil(StringToDouble(token)-0.5);
02395 GetMagickToken(q,&q,token);
02396 if (*token == ',')
02397 GetMagickToken(q,&q,token);
02398 bounds.y=(long) ceil(StringToDouble(token)-0.5);
02399 GetMagickToken(q,&q,token);
02400 if (*token == ',')
02401 GetMagickToken(q,&q,token);
02402 bounds.width=(unsigned long) floor(StringToDouble(token)+0.5);
02403 GetMagickToken(q,&q,token);
02404 if (*token == ',')
02405 GetMagickToken(q,&q,token);
02406 bounds.height=(unsigned long) floor(StringToDouble(token)+0.5);
02407 for (p=q; *q != '\0'; )
02408 {
02409 GetMagickToken(q,&q,token);
02410 if (LocaleCompare(token,"pop") != 0)
02411 continue;
02412 GetMagickToken(q,(const char **) NULL,token);
02413 if (LocaleCompare(token,"pattern") != 0)
02414 continue;
02415 break;
02416 }
02417 (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
02418 (void) FormatMagickString(key,MaxTextExtent,"%s",name);
02419 (void) SetImageArtifact(image,key,token);
02420 (void) FormatMagickString(key,MaxTextExtent,"%s-geometry",name);
02421 (void) FormatMagickString(geometry,MaxTextExtent,
02422 "%lux%lu%+ld%+ld",bounds.width,bounds.height,bounds.x,
02423 bounds.y);
02424 (void) SetImageArtifact(image,key,geometry);
02425 GetMagickToken(q,&q,token);
02426 break;
02427 }
02428 if (LocaleCompare("graphic-context",token) == 0)
02429 {
02430 n++;
02431 graphic_context=(DrawInfo **) ResizeQuantumMemory(
02432 graphic_context,(size_t) (n+1),sizeof(*graphic_context));
02433 if (graphic_context == (DrawInfo **) NULL)
02434 {
02435 (void) ThrowMagickException(&image->exception,
02436 GetMagickModule(),ResourceLimitError,
02437 "MemoryAllocationFailed","`%s'",image->filename);
02438 break;
02439 }
02440 graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
02441 graphic_context[n-1]);
02442 break;
02443 }
02444 if (LocaleCompare("defs",token) == 0)
02445 break;
02446 status=MagickFalse;
02447 break;
02448 }
02449 status=MagickFalse;
02450 break;
02451 }
02452 case 'r':
02453 case 'R':
02454 {
02455 if (LocaleCompare("rectangle",keyword) == 0)
02456 {
02457 primitive_type=RectanglePrimitive;
02458 break;
02459 }
02460 if (LocaleCompare("rotate",keyword) == 0)
02461 {
02462 GetMagickToken(q,&q,token);
02463 angle=StringToDouble(token);
02464 affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
02465 affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
02466 affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
02467 affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
02468 break;
02469 }
02470 if (LocaleCompare("roundRectangle",keyword) == 0)
02471 {
02472 primitive_type=RoundRectanglePrimitive;
02473 break;
02474 }
02475 status=MagickFalse;
02476 break;
02477 }
02478 case 's':
02479 case 'S':
02480 {
02481 if (LocaleCompare("scale",keyword) == 0)
02482 {
02483 GetMagickToken(q,&q,token);
02484 affine.sx=StringToDouble(token);
02485 GetMagickToken(q,&q,token);
02486 if (*token == ',')
02487 GetMagickToken(q,&q,token);
02488 affine.sy=StringToDouble(token);
02489 break;
02490 }
02491 if (LocaleCompare("skewX",keyword) == 0)
02492 {
02493 GetMagickToken(q,&q,token);
02494 angle=StringToDouble(token);
02495 affine.ry=sin(DegreesToRadians(angle));
02496 break;
02497 }
02498 if (LocaleCompare("skewY",keyword) == 0)
02499 {
02500 GetMagickToken(q,&q,token);
02501 angle=StringToDouble(token);
02502 affine.rx=(-tan(DegreesToRadians(angle)/2.0));
02503 break;
02504 }
02505 if (LocaleCompare("stop-color",keyword) == 0)
02506 {
02507 PixelPacket
02508 stop_color;
02509
02510 GetMagickToken(q,&q,token);
02511 (void) QueryColorDatabase(token,&stop_color,&image->exception);
02512 (void) GradientImage(image,LinearGradient,ReflectSpread,
02513 &start_color,&stop_color);
02514 start_color=stop_color;
02515 GetMagickToken(q,&q,token);
02516 break;
02517 }
02518 if (LocaleCompare("stroke",keyword) == 0)
02519 {
02520 GetMagickToken(q,&q,token);
02521 (void) FormatMagickString(pattern,MaxTextExtent,"%s",token);
02522 if (GetImageArtifact(image,pattern) != (const char *) NULL)
02523 (void) DrawPatternPath(image,draw_info,token,
02524 &graphic_context[n]->stroke_pattern);
02525 else
02526 {
02527 status=QueryColorDatabase(token,&graphic_context[n]->stroke,
02528 &image->exception);
02529 if (status == MagickFalse)
02530 {
02531 ImageInfo
02532 *pattern_info;
02533
02534 pattern_info=AcquireImageInfo();
02535 (void) CopyMagickString(pattern_info->filename,token,
02536 MaxTextExtent);
02537 graphic_context[n]->stroke_pattern=
02538 ReadImage(pattern_info,&image->exception);
02539 CatchException(&image->exception);
02540 pattern_info=DestroyImageInfo(pattern_info);
02541 }
02542 }
02543 break;
02544 }
02545 if (LocaleCompare("stroke-antialias",keyword) == 0)
02546 {
02547 GetMagickToken(q,&q,token);
02548 graphic_context[n]->stroke_antialias=
02549 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
02550 break;
02551 }
02552 if (LocaleCompare("stroke-dasharray",keyword) == 0)
02553 {
02554 if (graphic_context[n]->dash_pattern != (double *) NULL)
02555 graphic_context[n]->dash_pattern=(double *)
02556 RelinquishMagickMemory(graphic_context[n]->dash_pattern);
02557 if (IsPoint(q) != MagickFalse)
02558 {
02559 const char
02560 *p;
02561
02562 p=q;
02563 GetMagickToken(p,&p,token);
02564 if (*token == ',')
02565 GetMagickToken(p,&p,token);
02566 for (x=0; IsPoint(token) != MagickFalse; x++)
02567 {
02568 GetMagickToken(p,&p,token);
02569 if (*token == ',')
02570 GetMagickToken(p,&p,token);
02571 }
02572 graphic_context[n]->dash_pattern=(double *)
02573 AcquireQuantumMemory((size_t) (2UL*x+1UL),
02574 sizeof(*graphic_context[n]->dash_pattern));
02575 if (graphic_context[n]->dash_pattern == (double *) NULL)
02576 {
02577 (void) ThrowMagickException(&image->exception,
02578 GetMagickModule(),ResourceLimitError,
02579 "MemoryAllocationFailed","`%s'",image->filename);
02580 break;
02581 }
02582 for (j=0; j < x; j++)
02583 {
02584 GetMagickToken(q,&q,token);
02585 if (*token == ',')
02586 GetMagickToken(q,&q,token);
02587 graphic_context[n]->dash_pattern[j]=StringToDouble(token);
02588 }
02589 if ((x & 0x01) != 0)
02590 for ( ; j < (2*x); j++)
02591 graphic_context[n]->dash_pattern[j]=
02592 graphic_context[n]->dash_pattern[j-x];
02593 graphic_context[n]->dash_pattern[j]=0.0;
02594 break;
02595 }
02596 GetMagickToken(q,&q,token);
02597 break;
02598 }
02599 if (LocaleCompare("stroke-dashoffset",keyword) == 0)
02600 {
02601 GetMagickToken(q,&q,token);
02602 graphic_context[n]->dash_offset=StringToDouble(token);
02603 break;
02604 }
02605 if (LocaleCompare("stroke-linecap",keyword) == 0)
02606 {
02607 long
02608 linecap;
02609
02610 GetMagickToken(q,&q,token);
02611 linecap=ParseMagickOption(MagickLineCapOptions,MagickFalse,token);
02612 if (linecap == -1)
02613 {
02614 status=MagickFalse;
02615 break;
02616 }
02617 graphic_context[n]->linecap=(LineCap) linecap;
02618 break;
02619 }
02620 if (LocaleCompare("stroke-linejoin",keyword) == 0)
02621 {
02622 long
02623 linejoin;
02624
02625 GetMagickToken(q,&q,token);
02626 linejoin=ParseMagickOption(MagickLineJoinOptions,MagickFalse,token);
02627 if (linejoin == -1)
02628 {
02629 status=MagickFalse;
02630 break;
02631 }
02632 graphic_context[n]->linejoin=(LineJoin) linejoin;
02633 break;
02634 }
02635 if (LocaleCompare("stroke-miterlimit",keyword) == 0)
02636 {
02637 GetMagickToken(q,&q,token);
02638 graphic_context[n]->miterlimit=StringToUnsignedLong(token);
02639 break;
02640 }
02641 if (LocaleCompare("stroke-opacity",keyword) == 0)
02642 {
02643 GetMagickToken(q,&q,token);
02644 factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
02645 graphic_context[n]->stroke.opacity=ClampToQuantum((MagickRealType)
02646 QuantumRange*(1.0-factor*StringToDouble(token)));
02647 break;
02648 }
02649 if (LocaleCompare("stroke-width",keyword) == 0)
02650 {
02651 GetMagickToken(q,&q,token);
02652 graphic_context[n]->stroke_width=StringToDouble(token);
02653 break;
02654 }
02655 status=MagickFalse;
02656 break;
02657 }
02658 case 't':
02659 case 'T':
02660 {
02661 if (LocaleCompare("text",keyword) == 0)
02662 {
02663 primitive_type=TextPrimitive;
02664 break;
02665 }
02666 if (LocaleCompare("text-align",keyword) == 0)
02667 {
02668 long
02669 align;
02670
02671 GetMagickToken(q,&q,token);
02672 align=ParseMagickOption(MagickAlignOptions,MagickFalse,token);
02673 if (align == -1)
02674 {
02675 status=MagickFalse;
02676 break;
02677 }
02678 graphic_context[n]->align=(AlignType) align;
02679 break;
02680 }
02681 if (LocaleCompare("text-anchor",keyword) == 0)
02682 {
02683 long
02684 align;
02685
02686 GetMagickToken(q,&q,token);
02687 align=ParseMagickOption(MagickAlignOptions,MagickFalse,token);
02688 if (align == -1)
02689 {
02690 status=MagickFalse;
02691 break;
02692 }
02693 graphic_context[n]->align=(AlignType) align;
02694 break;
02695 }
02696 if (LocaleCompare("text-antialias",keyword) == 0)
02697 {
02698 GetMagickToken(q,&q,token);
02699 graphic_context[n]->text_antialias=
02700 StringToLong(token) != 0 ? MagickTrue : MagickFalse;
02701 break;
02702 }
02703 if (LocaleCompare("text-undercolor",keyword) == 0)
02704 {
02705 GetMagickToken(q,&q,token);
02706 (void) QueryColorDatabase(token,&graphic_context[n]->undercolor,
02707 &image->exception);
02708 break;
02709 }
02710 if (LocaleCompare("translate",keyword) == 0)
02711 {
02712 GetMagickToken(q,&q,token);
02713 affine.tx=StringToDouble(token);
02714 GetMagickToken(q,&q,token);
02715 if (*token == ',')
02716 GetMagickToken(q,&q,token);
02717 affine.ty=StringToDouble(token);
02718 break;
02719 }
02720 status=MagickFalse;
02721 break;
02722 }
02723 case 'v':
02724 case 'V':
02725 {
02726 if (LocaleCompare("viewbox",keyword) == 0)
02727 {
02728 GetMagickToken(q,&q,token);
02729 graphic_context[n]->viewbox.x=(long) ceil(StringToDouble(token)-
02730 0.5);
02731 GetMagickToken(q,&q,token);
02732 if (*token == ',')
02733 GetMagickToken(q,&q,token);
02734 graphic_context[n]->viewbox.y=(long) ceil(StringToDouble(token)-
02735 0.5);
02736 GetMagickToken(q,&q,token);
02737 if (*token == ',')
02738 GetMagickToken(q,&q,token);
02739 graphic_context[n]->viewbox.width=(unsigned long) floor(
02740 StringToDouble(token)+0.5);
02741 GetMagickToken(q,&q,token);
02742 if (*token == ',')
02743 GetMagickToken(q,&q,token);
02744 graphic_context[n]->viewbox.height=(unsigned long) floor(
02745 StringToDouble(token)+0.5);
02746 break;
02747 }
02748 status=MagickFalse;
02749 break;
02750 }
02751 default:
02752 {
02753 status=MagickFalse;
02754 break;
02755 }
02756 }
02757 if (status == MagickFalse)
02758 break;
02759 if ((affine.sx != 1.0) || (affine.rx != 0.0) || (affine.ry != 0.0) ||
02760 (affine.sy != 1.0) || (affine.tx != 0.0) || (affine.ty != 0.0))
02761 {
02762 graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
02763 graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
02764 graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
02765 graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
02766 graphic_context[n]->affine.tx=
02767 current.sx*affine.tx+current.ry*affine.ty+current.tx;
02768 graphic_context[n]->affine.ty=
02769 current.rx*affine.tx+current.sy*affine.ty+current.ty;
02770 }
02771 if (primitive_type == UndefinedPrimitive)
02772 {
02773 if (image->debug != MagickFalse)
02774 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",
02775 (int) (q-p),p);
02776 continue;
02777 }
02778
02779
02780
02781 i=0;
02782 j=0;
02783 primitive_info[0].point.x=0.0;
02784 primitive_info[0].point.y=0.0;
02785 for (x=0; *q != '\0'; x++)
02786 {
02787
02788
02789
02790 if (IsPoint(q) == MagickFalse)
02791 break;
02792 GetMagickToken(q,&q,token);
02793 point.x=StringToDouble(token);
02794 GetMagickToken(q,&q,token);
02795 if (*token == ',')
02796 GetMagickToken(q,&q,token);
02797 point.y=StringToDouble(token);
02798 GetMagickToken(q,(const char **) NULL,token);
02799 if (*token == ',')
02800 GetMagickToken(q,&q,token);
02801 primitive_info[i].primitive=primitive_type;
02802 primitive_info[i].point=point;
02803 primitive_info[i].coordinates=0;
02804 primitive_info[i].method=FloodfillMethod;
02805 i++;
02806 if (i < (long) number_points)
02807 continue;
02808 number_points<<=1;
02809 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
02810 (size_t) number_points,sizeof(*primitive_info));
02811 if (primitive_info == (PrimitiveInfo *) NULL)
02812 {
02813 (void) ThrowMagickException(&image->exception,GetMagickModule(),
02814 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
02815 break;
02816 }
02817 }
02818 primitive_info[j].primitive=primitive_type;
02819 primitive_info[j].coordinates=(unsigned long) x;
02820 primitive_info[j].method=FloodfillMethod;
02821 primitive_info[j].text=(char *) NULL;
02822
02823
02824
02825 bounds.x1=primitive_info[j].point.x;
02826 bounds.y1=primitive_info[j].point.y;
02827 bounds.x2=primitive_info[j].point.x;
02828 bounds.y2=primitive_info[j].point.y;
02829 for (k=1; k < (long) primitive_info[j].coordinates; k++)
02830 {
02831 point=primitive_info[j+k].point;
02832 if (point.x < bounds.x1)
02833 bounds.x1=point.x;
02834 if (point.y < bounds.y1)
02835 bounds.y1=point.y;
02836 if (point.x > bounds.x2)
02837 bounds.x2=point.x;
02838 if (point.y > bounds.y2)
02839 bounds.y2=point.y;
02840 }
02841
02842
02843
02844 length=primitive_info[j].coordinates;
02845 switch (primitive_type)
02846 {
02847 case RectanglePrimitive:
02848 {
02849 length*=5;
02850 break;
02851 }
02852 case RoundRectanglePrimitive:
02853 {
02854 length*=5+4*BezierQuantum;
02855 break;
02856 }
02857 case BezierPrimitive:
02858 {
02859 if (primitive_info[j].coordinates > 107)
02860 (void) ThrowMagickException(&image->exception,GetMagickModule(),
02861 DrawError,"TooManyBezierCoordinates","`%s'",token);
02862 length=BezierQuantum*primitive_info[j].coordinates;
02863 break;
02864 }
02865 case PathPrimitive:
02866 {
02867 char
02868 *s,
02869 *t;
02870
02871 GetMagickToken(q,&q,token);
02872 length=1;
02873 t=token;
02874 for (s=token; *s != '\0'; s=t)
02875 {
02876 double
02877 value;
02878
02879 value=strtod(s,&t);
02880 if (s == t)
02881 {
02882 t++;
02883 continue;
02884 }
02885 length++;
02886 }
02887 length=6*(3*length/2+BezierQuantum)+360+1;
02888 break;
02889 }
02890 case CirclePrimitive:
02891 case ArcPrimitive:
02892 case EllipsePrimitive:
02893 {
02894 MagickRealType
02895 alpha,
02896 beta,
02897 radius;
02898
02899 alpha=bounds.x2-bounds.x1;
02900 beta=bounds.y2-bounds.y1;
02901 radius=hypot((double) alpha,(double) beta);
02902 length=2*((size_t) (MagickPI*radius))+6*BezierQuantum+360+1;
02903 break;
02904 }
02905 default:
02906 break;
02907 }
02908 if ((unsigned long) (i+length) >= number_points)
02909 {
02910
02911
02912
02913 while ((unsigned long) (i+length) >= number_points)
02914 number_points<<=1;
02915 primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
02916 (size_t) number_points,sizeof(*primitive_info));
02917 if (primitive_info == (PrimitiveInfo *) NULL)
02918 {
02919 (void) ThrowMagickException(&image->exception,GetMagickModule(),
02920 ResourceLimitError,"MemoryAllocationFailed","`%s'",
02921 image->filename);
02922 break;
02923 }
02924 }
02925 switch (primitive_type)
02926 {
02927 case PointPrimitive:
02928 default:
02929 {
02930 if (primitive_info[j].coordinates != 1)
02931 {
02932 status=MagickFalse;
02933 break;
02934 }
02935 TracePoint(primitive_info+j,primitive_info[j].point);
02936 i=(long) (j+primitive_info[j].coordinates);
02937 break;
02938 }
02939 case LinePrimitive:
02940 {
02941 if (primitive_info[j].coordinates != 2)
02942 {
02943 status=MagickFalse;
02944 break;
02945 }
02946 TraceLine(primitive_info+j,primitive_info[j].point,
02947 primitive_info[j+1].point);
02948 i=(long) (j+primitive_info[j].coordinates);
02949 break;
02950 }
02951 case RectanglePrimitive:
02952 {
02953 if (primitive_info[j].coordinates != 2)
02954 {
02955 status=MagickFalse;
02956 break;
02957 }
02958 TraceRectangle(primitive_info+j,primitive_info[j].point,
02959 primitive_info[j+1].point);
02960 i=(long) (j+primitive_info[j].coordinates);
02961 break;
02962 }
02963 case RoundRectanglePrimitive:
02964 {
02965 if (primitive_info[j].coordinates != 3)
02966 {
02967 status=MagickFalse;
02968 break;
02969 }
02970 TraceRoundRectangle(primitive_info+j,primitive_info[j].point,
02971 primitive_info[j+1].point,primitive_info[j+2].point);
02972 i=(long) (j+primitive_info[j].coordinates);
02973 break;
02974 }
02975 case ArcPrimitive:
02976 {
02977 if (primitive_info[j].coordinates != 3)
02978 {
02979 primitive_type=UndefinedPrimitive;
02980 break;
02981 }
02982 TraceArc(primitive_info+j,primitive_info[j].point,
02983 primitive_info[j+1].point,primitive_info[j+2].point);
02984 i=(long) (j+primitive_info[j].coordinates);
02985 break;
02986 }
02987 case EllipsePrimitive:
02988 {
02989 if (primitive_info[j].coordinates != 3)
02990 {
02991 status=MagickFalse;
02992 break;
02993 }
02994 TraceEllipse(primitive_info+j,primitive_info[j].point,
02995 primitive_info[j+1].point,primitive_info[j+2].point);
02996 i=(long) (j+primitive_info[j].coordinates);
02997 break;
02998 }
02999 case CirclePrimitive:
03000 {
03001 if (primitive_info[j].coordinates != 2)
03002 {
03003 status=MagickFalse;
03004 break;
03005 }
03006 TraceCircle(primitive_info+j,primitive_info[j].point,
03007 primitive_info[j+1].point);
03008 i=(long) (j+primitive_info[j].coordinates);
03009 break;
03010 }
03011 case PolylinePrimitive:
03012 break;
03013 case PolygonPrimitive:
03014 {
03015 primitive_info[i]=primitive_info[j];
03016 primitive_info[i].coordinates=0;
03017 primitive_info[j].coordinates++;
03018 i++;
03019 break;
03020 }
03021 case BezierPrimitive:
03022 {
03023 if (primitive_info[j].coordinates < 3)
03024 {
03025 status=MagickFalse;
03026 break;
03027 }
03028 TraceBezier(primitive_info+j,primitive_info[j].coordinates);
03029 i=(long) (j+primitive_info[j].coordinates);
03030 break;
03031 }
03032 case PathPrimitive:
03033 {
03034 i=(long) (j+TracePath(primitive_info+j,token));
03035 break;
03036 }
03037 case ColorPrimitive:
03038 case MattePrimitive:
03039 {
03040 long
03041 method;
03042
03043 if (primitive_info[j].coordinates != 1)
03044 {
03045 status=MagickFalse;
03046 break;
03047 }
03048 GetMagickToken(q,&q,token);
03049 method=ParseMagickOption(MagickMethodOptions,MagickFalse,token);
03050 if (method == -1)
03051 {
03052 status=MagickFalse;
03053 break;
03054 }
03055 primitive_info[j].method=(PaintMethod) method;
03056 break;
03057 }
03058 case TextPrimitive:
03059 {
03060 if (primitive_info[j].coordinates != 1)
03061 {
03062 status=MagickFalse;
03063 break;
03064 }
03065 if (*token != ',')
03066 GetMagickToken(q,&q,token);
03067 primitive_info[j].text=AcquireString(token);
03068 break;
03069 }
03070 case ImagePrimitive:
03071 {
03072 if (primitive_info[j].coordinates != 2)
03073 {
03074 status=MagickFalse;
03075 break;
03076 }
03077 GetMagickToken(q,&q,token);
03078 primitive_info[j].text=AcquireString(token);
03079 break;
03080 }
03081 }
03082 if (primitive_info == (PrimitiveInfo *) NULL)
03083 break;
03084 if (image->debug != MagickFalse)
03085 (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p);
03086 if (status == MagickFalse)
03087 break;
03088 primitive_info[i].primitive=UndefinedPrimitive;
03089 if (i == 0)
03090 continue;
03091
03092
03093
03094 for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
03095 {
03096 point=primitive_info[i].point;
03097 primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
03098 graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
03099 primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
03100 graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
03101 point=primitive_info[i].point;
03102 if (point.x < graphic_context[n]->bounds.x1)
03103 graphic_context[n]->bounds.x1=point.x;
03104 if (point.y < graphic_context[n]->bounds.y1)
03105 graphic_context[n]->bounds.y1=point.y;
03106 if (point.x > graphic_context[n]->bounds.x2)
03107 graphic_context[n]->bounds.x2=point.x;
03108 if (point.y > graphic_context[n]->bounds.y2)
03109 graphic_context[n]->bounds.y2=point.y;
03110 if (primitive_info[i].primitive == ImagePrimitive)
03111 break;
03112 }
03113 if (i >= (long) number_points)
03114 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
03115 if (graphic_context[n]->render != MagickFalse)
03116 {
03117 if ((n != 0) && (graphic_context[n]->clip_mask != (char *) NULL) &&
03118 (LocaleCompare(graphic_context[n]->clip_mask,
03119 graphic_context[n-1]->clip_mask) != 0))
03120 (void) DrawClipPath(image,graphic_context[n],
03121 graphic_context[n]->clip_mask);
03122 (void) DrawPrimitive(image,graphic_context[n],primitive_info);
03123 }
03124 if (primitive_info->text != (char *) NULL)
03125 primitive_info->text=(char *) RelinquishMagickMemory(
03126 primitive_info->text);
03127 proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
03128 primitive_extent);
03129 if (proceed == MagickFalse)
03130 break;
03131 }
03132 if (image->debug != MagickFalse)
03133 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
03134
03135
03136
03137 token=DestroyString(token);
03138 if (primitive_info != (PrimitiveInfo *) NULL)
03139 primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
03140 primitive=DestroyString(primitive);
03141 for ( ; n >= 0; n--)
03142 graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
03143 graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
03144 if (status == MagickFalse)
03145 ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
03146 keyword);
03147 return(status);
03148 }
03149
03150
03151
03152
03153
03154
03155
03156
03157
03158
03159
03160
03161
03162
03163
03164
03165
03166
03167
03168
03169
03170
03171
03172
03173
03174
03175
03176 static inline MagickRealType GetStopColorOffset(const GradientInfo *gradient,
03177 const long x,const long y)
03178 {
03179 switch (gradient->type)
03180 {
03181 case UndefinedGradient:
03182 case LinearGradient:
03183 {
03184 MagickRealType
03185 gamma,
03186 length,
03187 offset,
03188 scale;
03189
03190 PointInfo
03191 p,
03192 q;
03193
03194 const SegmentInfo
03195 *gradient_vector;
03196
03197 gradient_vector=(&gradient->gradient_vector);
03198 p.x=gradient_vector->x2-gradient_vector->x1;
03199 p.y=gradient_vector->y2-gradient_vector->y1;
03200 q.x=(double) x-gradient_vector->x1;
03201 q.y=(double) y-gradient_vector->y1;
03202 length=sqrt(q.x*q.x+q.y*q.y);
03203 gamma=sqrt(p.x*p.x+p.y*p.y)*length;
03204 gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
03205 scale=p.x*q.x+p.y*q.y;
03206 offset=gamma*scale*length;
03207 return(offset);
03208 }
03209 case RadialGradient:
03210 {
03211 MagickRealType
03212 length,
03213 offset;
03214
03215 PointInfo
03216 v;
03217
03218 v.x=(double) x-gradient->center.x;
03219 v.y=(double) y-gradient->center.y;
03220 length=sqrt(v.x*v.x+v.y*v.y);
03221 if (gradient->spread == RepeatSpread)
03222 return(length);
03223 offset=length/gradient->radius;
03224 return(offset);
03225 }
03226 }
03227 return(0.0);
03228 }
03229
03230 MagickExport MagickBooleanType DrawGradientImage(Image *image,
03231 const DrawInfo *draw_info)
03232 {
03233 CacheView
03234 *image_view;
03235
03236 const GradientInfo
03237 *gradient;
03238
03239 const SegmentInfo
03240 *gradient_vector;
03241
03242 ExceptionInfo
03243 *exception;
03244
03245 long
03246 y;
03247
03248 MagickBooleanType
03249 status;
03250
03251 MagickPixelPacket
03252 zero;
03253
03254 MagickRealType
03255 length;
03256
03257 PointInfo
03258 point;
03259
03260 RectangleInfo
03261 bounding_box;
03262
03263
03264
03265
03266 assert(image != (Image *) NULL);
03267 assert(image->signature == MagickSignature);
03268 if (image->debug != MagickFalse)
03269 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03270 assert(draw_info != (const DrawInfo *) NULL);
03271 gradient=(&draw_info->gradient);
03272 gradient_vector=(&gradient->gradient_vector);
03273 point.x=gradient_vector->x2-gradient_vector->x1;
03274 point.y=gradient_vector->y2-gradient_vector->y1;
03275 length=sqrt(point.x*point.x+point.y*point.y);
03276 bounding_box=gradient->bounding_box;
03277 status=MagickTrue;
03278 exception=(&image->exception);
03279 GetMagickPixelPacket(image,&zero);
03280 image_view=AcquireCacheView(image);
03281 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03282 #pragma omp parallel for schedule(dynamic,4) shared(status)
03283 #endif
03284 for (y=bounding_box.y; y < (long) bounding_box.height; y++)
03285 {
03286 long
03287 j;
03288
03289 MagickPixelPacket
03290 composite,
03291 pixel;
03292
03293 MagickRealType
03294 alpha,
03295 offset;
03296
03297 register IndexPacket
03298 *restrict indexes;
03299
03300 register long
03301 i,
03302 x;
03303
03304 register PixelPacket
03305 *restrict q;
03306
03307 if (status == MagickFalse)
03308 continue;
03309 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
03310 if (q == (PixelPacket *) NULL)
03311 {
03312 status=MagickFalse;
03313 continue;
03314 }
03315 indexes=GetCacheViewAuthenticIndexQueue(image_view);
03316 pixel=zero;
03317 composite=zero;
03318 offset=GetStopColorOffset(gradient,0,y);
03319 if (gradient->type != RadialGradient)
03320 offset/=length;
03321 for (x=bounding_box.x; x < (long) bounding_box.width; x++)
03322 {
03323 SetMagickPixelPacket(image,q,indexes+x,&pixel);
03324 switch (gradient->spread)
03325 {
03326 case UndefinedSpread:
03327 case PadSpread:
03328 {
03329 if ((x != (long) ceil(gradient_vector->x1-0.5)) ||
03330 (y != (long) ceil(gradient_vector->y1-0.5)))
03331 {
03332 offset=GetStopColorOffset(gradient,x,y);
03333 if (gradient->type != RadialGradient)
03334 offset/=length;
03335 }
03336 for (i=0; i < (long) gradient->number_stops; i++)
03337 if (offset < gradient->stops[i].offset)
03338 break;
03339 if ((offset < 0.0) || (i == 0))
03340 composite=gradient->stops[0].color;
03341 else
03342 if ((offset > 1.0) || (i == (long) gradient->number_stops))
03343 composite=gradient->stops[gradient->number_stops-1].color;
03344 else
03345 {
03346 j=i;
03347 i--;
03348 alpha=(offset-gradient->stops[i].offset)/
03349 (gradient->stops[j].offset-gradient->stops[i].offset);
03350 MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
03351 &gradient->stops[j].color,alpha,&composite);
03352 }
03353 break;
03354 }
03355 case ReflectSpread:
03356 {
03357 if ((x != (long) ceil(gradient_vector->x1-0.5)) ||
03358 (y != (long) ceil(gradient_vector->y1-0.5)))
03359 {
03360 offset=GetStopColorOffset(gradient,x,y);
03361 if (gradient->type != RadialGradient)
03362 offset/=length;
03363 }
03364 if (offset < 0.0)
03365 offset=(-offset);
03366 if ((long) fmod(offset,2.0) == 0)
03367 offset=fmod(offset,1.0);
03368 else
03369 offset=1.0-fmod(offset,1.0);
03370 for (i=0; i < (long) gradient->number_stops; i++)
03371 if (offset < gradient->stops[i].offset)
03372 break;
03373 if (i == 0)
03374 composite=gradient->stops[0].color;
03375 else
03376 if (i == (long) gradient->number_stops)
03377 composite=gradient->stops[gradient->number_stops-1].color;
03378 else
03379 {
03380 j=i;
03381 i--;
03382 alpha=(offset-gradient->stops[i].offset)/
03383 (gradient->stops[j].offset-gradient->stops[i].offset);
03384 MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
03385 &gradient->stops[j].color,alpha,&composite);
03386 }
03387 break;
03388 }
03389 case RepeatSpread:
03390 {
03391 MagickBooleanType
03392 antialias;
03393
03394 MagickRealType
03395 repeat;
03396
03397 antialias=MagickFalse;
03398 repeat=0.0;
03399 if ((x != (long) ceil(gradient_vector->x1-0.5)) ||
03400 (y != (long) ceil(gradient_vector->y1-0.5)))
03401 {
03402 offset=GetStopColorOffset(gradient,x,y);
03403 if (gradient->type == LinearGradient)
03404 {
03405 repeat=fmod(offset,length);
03406 if (repeat < 0.0)
03407 repeat=length-fmod(-repeat,length);
03408 else
03409 repeat=fmod(offset,length);
03410 antialias=(repeat < length) && ((repeat+1.0) > length) ?
03411 MagickTrue : MagickFalse;
03412 offset=repeat/length;
03413 }
03414 else
03415 {
03416 repeat=fmod(offset,gradient->radius);
03417 if (repeat < 0.0)
03418 repeat=gradient->radius-fmod(-repeat,gradient->radius);
03419 else
03420 repeat=fmod(offset,gradient->radius);
03421 antialias=repeat+1.0 > gradient->radius ?
03422 MagickTrue : MagickFalse;
03423 offset=repeat/gradient->radius;
03424 }
03425 }
03426 for (i=0; i < (long) gradient->number_stops; i++)
03427 if (offset < gradient->stops[i].offset)
03428 break;
03429 if (i == 0)
03430 composite=gradient->stops[0].color;
03431 else
03432 if (i == (long) gradient->number_stops)
03433 composite=gradient->stops[gradient->number_stops-1].color;
03434 else
03435 {
03436 j=i;
03437 i--;
03438 alpha=(offset-gradient->stops[i].offset)/
03439 (gradient->stops[j].offset-gradient->stops[i].offset);
03440 if (antialias != MagickFalse)
03441 {
03442 if (gradient->type == LinearGradient)
03443 alpha=length-repeat;
03444 else
03445 alpha=gradient->radius-repeat;
03446 i=0;
03447 j=(long) gradient->number_stops-1L;
03448 }
03449 MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
03450 &gradient->stops[j].color,alpha,&composite);
03451 }
03452 break;
03453 }
03454 }
03455 MagickPixelCompositeOver(&composite,composite.opacity,&pixel,
03456 pixel.opacity,&pixel);
03457 SetPixelPacket(image,&pixel,q,indexes+x);
03458 q++;
03459 }
03460 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
03461 status=MagickFalse;
03462 }
03463 image_view=DestroyCacheView(image_view);
03464 return(status);
03465 }
03466
03467
03468
03469
03470
03471
03472
03473
03474
03475
03476
03477
03478
03479
03480
03481
03482
03483
03484
03485
03486
03487
03488
03489
03490
03491
03492
03493
03494
03495
03496 MagickExport MagickBooleanType DrawPatternPath(Image *image,
03497 const DrawInfo *draw_info,const char *name,Image **pattern)
03498 {
03499 char
03500 property[MaxTextExtent];
03501
03502 const char
03503 *geometry,
03504 *path;
03505
03506 DrawInfo
03507 *clone_info;
03508
03509 ImageInfo
03510 *image_info;
03511
03512 MagickBooleanType
03513 status;
03514
03515 assert(image != (Image *) NULL);
03516 assert(image->signature == MagickSignature);
03517 if (image->debug != MagickFalse)
03518 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03519 assert(draw_info != (const DrawInfo *) NULL);
03520 assert(name != (const char *) NULL);
03521 (void) FormatMagickString(property,MaxTextExtent,"%s",name);
03522 path=GetImageArtifact(image,property);
03523 if (path == (const char *) NULL)
03524 return(MagickFalse);
03525 (void) FormatMagickString(property,MaxTextExtent,"%s-geometry",name);
03526 geometry=GetImageArtifact(image,property);
03527 if (geometry == (const char *) NULL)
03528 return(MagickFalse);
03529 if ((*pattern) != (Image *) NULL)
03530 *pattern=DestroyImage(*pattern);
03531 image_info=AcquireImageInfo();
03532 image_info->size=AcquireString(geometry);
03533 *pattern=AcquireImage(image_info);
03534 image_info=DestroyImageInfo(image_info);
03535 (void) QueryColorDatabase("#00000000",&(*pattern)->background_color,
03536 &image->exception);
03537 (void) SetImageBackgroundColor(*pattern);
03538 if (image->debug != MagickFalse)
03539 (void) LogMagickEvent(DrawEvent,GetMagickModule(),
03540 "begin pattern-path %s %s",name,geometry);
03541 clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
03542 clone_info->fill_pattern=NewImageList();
03543 clone_info->stroke_pattern=NewImageList();
03544 (void) CloneString(&clone_info->primitive,path);
03545 status=DrawImage(*pattern,clone_info);
03546 clone_info=DestroyDrawInfo(clone_info);
03547 if (image->debug != MagickFalse)
03548 (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
03549 return(status);
03550 }
03551
03552
03553
03554
03555
03556
03557
03558
03559
03560
03561
03562
03563
03564
03565
03566
03567
03568
03569
03570
03571
03572
03573
03574
03575
03576
03577
03578
03579
03580 static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info)
03581 {
03582 register long
03583 i;
03584
03585 assert(polygon_info != (PolygonInfo **) NULL);
03586 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
03587 if (polygon_info[i] != (PolygonInfo *) NULL)
03588 polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
03589 polygon_info=(PolygonInfo **) RelinquishAlignedMemory(polygon_info);
03590 return(polygon_info);
03591 }
03592
03593 static PolygonInfo **AcquirePolygonThreadSet(const DrawInfo *draw_info,
03594 const PrimitiveInfo *primitive_info)
03595 {
03596 PathInfo
03597 *restrict path_info;
03598
03599 register long
03600 i;
03601
03602 PolygonInfo
03603 **polygon_info;
03604
03605 unsigned long
03606 number_threads;
03607
03608 number_threads=GetOpenMPMaximumThreads();
03609 polygon_info=(PolygonInfo **) AcquireAlignedMemory(number_threads,
03610 sizeof(*polygon_info));
03611 if (polygon_info == (PolygonInfo **) NULL)
03612 return((PolygonInfo **) NULL);
03613 (void) ResetMagickMemory(polygon_info,0,GetOpenMPMaximumThreads()*
03614 sizeof(*polygon_info));
03615 path_info=ConvertPrimitiveToPath(draw_info,primitive_info);
03616 if (path_info == (PathInfo *) NULL)
03617 return(DestroyPolygonThreadSet(polygon_info));
03618 for (i=0; i < (long) number_threads; i++)
03619 {
03620 polygon_info[i]=ConvertPathToPolygon(draw_info,path_info);
03621 if (polygon_info[i] == (PolygonInfo *) NULL)
03622 return(DestroyPolygonThreadSet(polygon_info));
03623 }
03624 path_info=(PathInfo *) RelinquishMagickMemory(path_info);
03625 return(polygon_info);
03626 }
03627
03628 static MagickRealType GetPixelOpacity(PolygonInfo *polygon_info,
03629 const MagickRealType mid,const MagickBooleanType fill,
03630 const FillRule fill_rule,const long x,const long y,
03631 MagickRealType *stroke_opacity)
03632 {
03633 int
03634 winding_number;
03635
03636 long
03637 j;
03638
03639 MagickRealType
03640 alpha,
03641 beta,
03642 distance,
03643 subpath_opacity;
03644
03645 PointInfo
03646 delta;
03647
03648 register EdgeInfo
03649 *p;
03650
03651 register const PointInfo
03652 *q;
03653
03654 register long
03655 i;
03656
03657
03658
03659
03660 *stroke_opacity=0.0;
03661 subpath_opacity=0.0;
03662 p=polygon_info->edges;
03663 for (j=0; j < (long) polygon_info->number_edges; j++, p++)
03664 {
03665 if (y <= (p->bounds.y1-mid-0.5))
03666 break;
03667 if (y > (p->bounds.y2+mid+0.5))
03668 {
03669 (void) DestroyEdge(polygon_info,j);
03670 continue;
03671 }
03672 if ((x <= (p->bounds.x1-mid-0.5)) || (x > (p->bounds.x2+mid+0.5)))
03673 continue;
03674 for (i=MagickMax(p->highwater,1); i < (long) p->number_points; i++)
03675 {
03676 if (y <= (p->points[i-1].y-mid-0.5))
03677 break;
03678 if (y > (p->points[i].y+mid+0.5))
03679 continue;
03680 if (p->scanline != y)
03681 {
03682 p->scanline=y;
03683 p->highwater=i;
03684 }
03685
03686
03687
03688 q=p->points+i-1;
03689 delta.x=(q+1)->x-q->x;
03690 delta.y=(q+1)->y-q->y;
03691 beta=delta.x*(x-q->x)+delta.y*(y-q->y);
03692 if (beta < 0.0)
03693 {
03694 delta.x=x-q->x;
03695 delta.y=y-q->y;
03696 distance=delta.x*delta.x+delta.y*delta.y;
03697 }
03698 else
03699 {
03700 alpha=delta.x*delta.x+delta.y*delta.y;
03701 if (beta > alpha)
03702 {
03703 delta.x=x-(q+1)->x;
03704 delta.y=y-(q+1)->y;
03705 distance=delta.x*delta.x+delta.y*delta.y;
03706 }
03707 else
03708 {
03709 alpha=1.0/alpha;
03710 beta=delta.x*(y-q->y)-delta.y*(x-q->x);
03711 distance=alpha*beta*beta;
03712 }
03713 }
03714
03715
03716
03717 beta=0.0;
03718 if (p->ghostline == MagickFalse)
03719 {
03720 alpha=mid+0.5;
03721 if ((*stroke_opacity < 1.0) &&
03722 (distance <= ((alpha+0.25)*(alpha+0.25))))
03723 {
03724 alpha=mid-0.5;
03725 if (distance <= ((alpha+0.25)*(alpha+0.25)))
03726 *stroke_opacity=1.0;
03727 else
03728 {
03729 beta=1.0;
03730 if (distance != 1.0)
03731 beta=sqrt((double) distance);
03732 alpha=beta-mid-0.5;
03733 if (*stroke_opacity < ((alpha-0.25)*(alpha-0.25)))
03734 *stroke_opacity=(alpha-0.25)*(alpha-0.25);
03735 }
03736 }
03737 }
03738 if ((fill == MagickFalse) || (distance > 1.0) || (subpath_opacity >= 1.0))
03739 continue;
03740 if (distance <= 0.0)
03741 {
03742 subpath_opacity=1.0;
03743 continue;
03744 }
03745 if (distance > 1.0)
03746 continue;
03747 if (beta == 0.0)
03748 {
03749 beta=1.0;
03750 if (distance != 1.0)
03751 beta=sqrt(distance);
03752 }
03753 alpha=beta-1.0;
03754 if (subpath_opacity < (alpha*alpha))
03755 subpath_opacity=alpha*alpha;
03756 }
03757 }
03758
03759
03760
03761 if (fill == MagickFalse)
03762 return(0.0);
03763 if (subpath_opacity >= 1.0)
03764 return(1.0);
03765
03766
03767
03768 winding_number=0;
03769 p=polygon_info->edges;
03770 for (j=0; j < (long) polygon_info->number_edges; j++, p++)
03771 {
03772 if (y <= p->bounds.y1)
03773 break;
03774 if ((y > p->bounds.y2) || (x <= p->bounds.x1))
03775 continue;
03776 if (x > p->bounds.x2)
03777 {
03778 winding_number+=p->direction ? 1 : -1;
03779 continue;
03780 }
03781 for (i=MagickMax(p->highwater,1); i < (long) p->number_points; i++)
03782 if (y <= p->points[i].y)
03783 break;
03784 q=p->points+i-1;
03785 if ((((q+1)->x-q->x)*(y-q->y)) <= (((q+1)->y-q->y)*(x-q->x)))
03786 winding_number+=p->direction ? 1 : -1;
03787 }
03788 if (fill_rule != NonZeroRule)
03789 {
03790 if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
03791 return(1.0);
03792 }
03793 else
03794 if (MagickAbsoluteValue(winding_number) != 0)
03795 return(1.0);
03796 return(subpath_opacity);
03797 }
03798
03799 static MagickBooleanType DrawPolygonPrimitive(Image *image,
03800 const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
03801 {
03802 CacheView
03803 *image_view;
03804
03805 ExceptionInfo
03806 *exception;
03807
03808 long
03809 start,
03810 stop,
03811 y;
03812
03813