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