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