draw.c

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