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