]> granicus.if.org Git - imagemagick/commitdiff
...
authorCristy <urban-warrior@imagemagick.org>
Sun, 6 May 2018 12:32:23 +0000 (08:32 -0400)
committerCristy <urban-warrior@imagemagick.org>
Sun, 6 May 2018 12:32:23 +0000 (08:32 -0400)
MagickCore/draw.c

index af8f59b132fb957f2021d1aa0879e467f235f3d1..9e98ca0738c68cc330165beb574479fe37ff3221 100644 (file)
@@ -1706,22 +1706,18 @@ static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
 %                                                                             %
 %                                                                             %
 %                                                                             %
-%   D r a w I m a g e                                                         %
+%     D r a w G r a d i e n t I m a g e                                       %
 %                                                                             %
 %                                                                             %
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  DrawImage() draws a graphic primitive on your image.  The primitive
-%  may be represented as a string or filename.  Precede the filename with an
-%  "at" sign (@) and the contents of the file are drawn on the image.  You
-%  can affect how text is drawn by setting one or more members of the draw
-%  info structure.
+%  DrawGradientImage() draws a linear gradient on the image.
 %
-%  The format of the DrawImage method is:
+%  The format of the DrawGradientImage method is:
 %
-%      MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
-%        ExceptionInfo *exception)
+%      MagickBooleanType DrawGradientImage(Image *image,
+%        const DrawInfo *draw_info,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -1733,722 +1729,1062 @@ static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
 %
 */
 
-static char *GetNodeByURL(const char *primitive,const char *url)
+static inline double GetStopColorOffset(const GradientInfo *gradient,
+  const ssize_t x,const ssize_t y)
 {
-  char
-    *token;
-
-  const char
-    *q,
-    *start;
+  switch (gradient->type)
+  {
+    case UndefinedGradient:
+    case LinearGradient:
+    {
+      double
+        gamma,
+        length,
+        offset,
+        scale;
 
-  register const char
-    *p;
+      PointInfo
+        p,
+        q;
 
-  size_t
-    extent,
-    length;
+      const SegmentInfo
+        *gradient_vector;
 
-  ssize_t
-    n;
+      gradient_vector=(&gradient->gradient_vector);
+      p.x=gradient_vector->x2-gradient_vector->x1;
+      p.y=gradient_vector->y2-gradient_vector->y1;
+      q.x=(double) x-gradient_vector->x1;
+      q.y=(double) y-gradient_vector->y1;
+      length=sqrt(q.x*q.x+q.y*q.y);
+      gamma=sqrt(p.x*p.x+p.y*p.y)*length;
+      gamma=PerceptibleReciprocal(gamma);
+      scale=p.x*q.x+p.y*q.y;
+      offset=gamma*scale*length;
+      return(offset);
+    }
+    case RadialGradient:
+    {
+      PointInfo
+        v;
 
-  /*
-    Find and return node by ID.
-  */
-  token=AcquireString(primitive);
-  extent=strlen(token)+MagickPathExtent;
-  length=0;
-  n=0;
-  start=(const char *) NULL;
-  p=(const char *) NULL;
-  for (q=primitive; (*q != '\0') && (length == 0); )
-  {
-    p=q;
-    GetNextToken(q,&q,extent,token);
-    if (*token == '\0')
-      break;
-    if (*token == '#')
-      {
-        /*
-          Comment.
-        */
-        while ((*q != '\n') && (*q != '\0'))
-          q++;
-        continue;
-      }
-    if (LocaleCompare("pop",token) == 0)
-      {
-        GetNextToken(q,&q,extent,token);
-        if ((n == 0) && (start != (const char *) NULL))
-          {
-            /*
-              End of node by ID.
-            */
-            length=(size_t) (p-start+1);
-            break;
-          }
-        n--;
-      }
-    if (LocaleCompare("push",token) == 0)
-      {
-        GetNextToken(q,&q,extent,token);
-        n++;
-        if (*q == '"')
-          {
-            GetNextToken(q,&q,extent,token);
-            if (LocaleCompare(url,token) == 0)
-              {
-                /*
-                  Start of node by ID.
-                */
-                n=0;
-                start=q;
-              }
-          }
-      }
+      if (gradient->spread == RepeatSpread)
+        {
+          v.x=(double) x-gradient->center.x;
+          v.y=(double) y-gradient->center.y;
+          return(sqrt(v.x*v.x+v.y*v.y));
+        }
+      v.x=(double) (((x-gradient->center.x)*cos(DegreesToRadians(
+        gradient->angle)))+((y-gradient->center.y)*sin(DegreesToRadians(
+        gradient->angle))))*PerceptibleReciprocal(gradient->radii.x);
+      v.y=(double) (((x-gradient->center.x)*sin(DegreesToRadians(
+        gradient->angle)))-((y-gradient->center.y)*cos(DegreesToRadians(
+        gradient->angle))))*PerceptibleReciprocal(gradient->radii.y);
+      return(sqrt(v.x*v.x+v.y*v.y));
+    }
   }
-  if (start == (const char *) NULL)
-    return(DestroyString(token));
-  (void) CopyMagickString(token,start,length);
-  return(token);
-}
-
-static inline MagickBooleanType IsPoint(const char *point)
-{
-  char
-    *p;
-
-  double
-    value;
-
-  value=StringToDouble(point,&p);
-  return((fabs(value) < DrawEpsilon) && (p == point) ? MagickFalse : MagickTrue);
+  return(0.0);
 }
 
-static size_t ReckonEllipseCoordinates(const PointInfo radii,
-  const PointInfo arc)
+static int StopInfoCompare(const void *x,const void *y)
 {
-  double
-    delta,
-    step,
-    y;
-
-  PointInfo
-    angle;
+  StopInfo
+    *stop_1,
+    *stop_2;
 
-  /*
-    Ellipses are just short segmented polys.
-  */
-  if ((fabs(radii.x) < DrawEpsilon) || (fabs(radii.y) < DrawEpsilon))
+  stop_1=(StopInfo *) x;
+  stop_2=(StopInfo *) y;
+  if (stop_1->offset > stop_2->offset)
+    return(1);
+  if (fabs(stop_1->offset-stop_2->offset) <= DrawEpsilon)
     return(0);
-  delta=2.0*PerceptibleReciprocal(MagickMax(radii.x,radii.y));
-  step=MagickPI/8.0;
-  if ((delta >= 0.0) && (delta < (MagickPI/8.0)))
-    step=MagickPI/(4.0*(MagickPI*PerceptibleReciprocal(delta)/2.0));
-  angle.x=DegreesToRadians(arc.x);
-  y=arc.y;
-  while (y < arc.x)
-    y+=360.0;
-  angle.y=DegreesToRadians(y);
-  return((size_t) floor((angle.y-angle.x)/step+0.5)+3);
+  return(-1);
 }
 
-static size_t ReckonRoundRectangleCoordinates(const PointInfo start,
-  const PointInfo end,PointInfo arc)
+MagickExport MagickBooleanType DrawGradientImage(Image *image,
+  const DrawInfo *draw_info,ExceptionInfo *exception)
 {
-  PointInfo
-    degrees,
-    offset;
+  CacheView
+    *image_view;
 
-  size_t
-    coordinates;
+  const GradientInfo
+    *gradient;
 
-  offset.x=fabs(end.x-start.x);
-  offset.y=fabs(end.y-start.y);
-  if ((offset.x < DrawEpsilon) || (offset.y < DrawEpsilon))
-    return(0);
-  coordinates=0;
-  if (arc.x > (0.5*offset.x))
-    arc.x=0.5*offset.x;
-  if (arc.y > (0.5*offset.y))
-    arc.y=0.5*offset.y;
-  degrees.x=270.0;
-  degrees.y=360.0;
-  coordinates+=ReckonEllipseCoordinates(arc,degrees);
-  degrees.x=0.0;
-  degrees.y=90.0;
-  coordinates+=ReckonEllipseCoordinates(arc,degrees);
-  degrees.x=90.0;
-  degrees.y=180.0;
-  coordinates+=ReckonEllipseCoordinates(arc,degrees);
-  degrees.x=180.0;
-  degrees.y=270.0;
-  coordinates+=ReckonEllipseCoordinates(arc,degrees);
-  return(coordinates+1);
-}
+  const SegmentInfo
+    *gradient_vector;
 
-static inline void TracePoint(PrimitiveInfo *primitive_info,
-  const PointInfo point)
-{
-  primitive_info->coordinates=1;
-  primitive_info->closed_subpath=MagickFalse;
-  primitive_info->point=point;
-}
+  double
+    length;
 
-MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
-  ExceptionInfo *exception)
-{
-#define RenderImageTag  "Render/Image"
+  MagickBooleanType
+    status;
 
-  AffineMatrix
-    affine,
-    current;
+  PixelInfo
+    zero;
 
-  char
-    keyword[MagickPathExtent],
-    geometry[MagickPathExtent],
-    *next_token,
-    pattern[MagickPathExtent],
-    *primitive,
-    *token;
+  PointInfo
+    point;
 
-  const char
-    *q;
+  RectangleInfo
+    bounding_box;
 
-  double
-    angle,
-    factor,
-    primitive_extent;
-
-  DrawInfo
-    **graphic_context;
-
-  MagickBooleanType
-    proceed;
-
-  MagickSizeType
-    number_points;
-
-  MagickStatusType
-    status;
-
-  PointInfo
-    point;
-
-  PrimitiveInfo
-    *primitive_info;
-
-  PrimitiveType
-    primitive_type;
-
-  register const char
-    *p;
-
-  register ssize_t
-    i,
-    x;
-
-  SegmentInfo
-    bounds;
-
-  size_t
-    coordinates,
-    extent,
-    number_stops;
-
-  ssize_t
-    defsDepth,
-    j,
-    k,
-    n,
-    symbolDepth;
-
-  StopInfo
-    *stops;
+  ssize_t
+    y;
 
+  /*
+    Draw linear or radial gradient on image.
+  */
   assert(image != (Image *) NULL);
   assert(image->signature == MagickCoreSignature);
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  assert(draw_info != (DrawInfo *) NULL);
-  assert(draw_info->signature == MagickCoreSignature);
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
-  if ((draw_info->primitive == (char *) NULL) ||
-      (*draw_info->primitive == '\0'))
-    return(MagickFalse);
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
-  primitive=(char *) NULL;
-  if (*draw_info->primitive != '@')
-    primitive=AcquireString(draw_info->primitive);
-  else
-    if ((strlen(draw_info->primitive) > 1) &&
-        (*(draw_info->primitive+1) != '-'))
-      primitive=FileToString(draw_info->primitive+1,~0UL,exception);
-  if (primitive == (char *) NULL)
-    return(MagickFalse);
-  primitive_extent=(double) strlen(primitive);
-  (void) SetImageArtifact(image,"MVG",primitive);
-  n=0;
-  number_stops=0;
-  stops=(StopInfo *) NULL;
-  /*
-    Allocate primitive info memory.
-  */
-  graphic_context=(DrawInfo **) AcquireMagickMemory(sizeof(*graphic_context));
-  if (graphic_context == (DrawInfo **) NULL)
-    {
-      primitive=DestroyString(primitive);
-      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
-        image->filename);
-    }
-  number_points=65536;
-  primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
-    sizeof(*primitive_info));
-  if (primitive_info == (PrimitiveInfo *) NULL)
-    {
-      primitive=DestroyString(primitive);
-      for ( ; n >= 0; n--)
-        graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
-      graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
-      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
-        image->filename);
-    }
-  (void) memset(primitive_info,0,(size_t) number_points*
-    sizeof(*primitive_info));
-  graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
-  graphic_context[n]->viewbox=image->page;
-  if ((image->page.width == 0) || (image->page.height == 0))
-    {
-      graphic_context[n]->viewbox.width=image->columns;
-      graphic_context[n]->viewbox.height=image->rows;
-    }
-  token=AcquireString(primitive);
-  extent=strlen(token)+MagickPathExtent;
-  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
-    return(MagickFalse);
-  defsDepth=0;
-  symbolDepth=0;
+  assert(draw_info != (const DrawInfo *) NULL);
+  gradient=(&draw_info->gradient);
+  qsort(gradient->stops,gradient->number_stops,sizeof(StopInfo),
+    StopInfoCompare);
+  gradient_vector=(&gradient->gradient_vector);
+  point.x=gradient_vector->x2-gradient_vector->x1;
+  point.y=gradient_vector->y2-gradient_vector->y1;
+  length=sqrt(point.x*point.x+point.y*point.y);
+  bounding_box=gradient->bounding_box;
   status=MagickTrue;
-  for (q=primitive; *q != '\0'; )
+  GetPixelInfo(image,&zero);
+  image_view=AcquireAuthenticCacheView(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(static) shared(status) \
+    magick_number_threads(image,image,bounding_box.height-bounding_box.y,1)
+#endif
+  for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++)
   {
-    /*
-      Interpret graphic primitive.
-    */
-    GetNextToken(q,&q,MagickPathExtent,keyword);
-    if (*keyword == '\0')
-      break;
-    if (*keyword == '#')
+    PixelInfo
+      composite,
+      pixel;
+
+    double
+      alpha,
+      offset;
+
+    register Quantum
+      *magick_restrict q;
+
+    register ssize_t
+      i,
+      x;
+
+    ssize_t
+      j;
+
+    if (status == MagickFalse)
+      continue;
+    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+    if (q == (Quantum *) NULL)
       {
-        /*
-          Comment.
-        */
-        while ((*q != '\n') && (*q != '\0'))
-          q++;
+        status=MagickFalse;
         continue;
       }
-    p=q-strlen(keyword)-1;
-    primitive_type=UndefinedPrimitive;
-    current=graphic_context[n]->affine;
-    GetAffineMatrix(&affine);
-    switch (*keyword)
+    pixel=zero;
+    composite=zero;
+    offset=GetStopColorOffset(gradient,0,y);
+    if (gradient->type != RadialGradient)
+      offset*=PerceptibleReciprocal(length);
+    for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++)
     {
-      case ';':
-        break;
-      case 'a':
-      case 'A':
-      {
-        if (LocaleCompare("affine",keyword) == 0)
-          {
-            GetNextToken(q,&q,extent,token);
-            affine.sx=StringToDouble(token,&next_token);
-            if (token == next_token)
-              ThrowPointExpectedException(token,exception);
-            GetNextToken(q,&q,extent,token);
-            if (*token == ',')
-              GetNextToken(q,&q,extent,token);
-            affine.rx=StringToDouble(token,&next_token);
-            if (token == next_token)
-              ThrowPointExpectedException(token,exception);
-            GetNextToken(q,&q,extent,token);
-            if (*token == ',')
-              GetNextToken(q,&q,extent,token);
-            affine.ry=StringToDouble(token,&next_token);
-            if (token == next_token)
-              ThrowPointExpectedException(token,exception);
-            GetNextToken(q,&q,extent,token);
-            if (*token == ',')
-              GetNextToken(q,&q,extent,token);
-            affine.sy=StringToDouble(token,&next_token);
-            if (token == next_token)
-              ThrowPointExpectedException(token,exception);
-            GetNextToken(q,&q,extent,token);
-            if (*token == ',')
-              GetNextToken(q,&q,extent,token);
-            affine.tx=StringToDouble(token,&next_token);
-            if (token == next_token)
-              ThrowPointExpectedException(token,exception);
-            GetNextToken(q,&q,extent,token);
-            if (*token == ',')
-              GetNextToken(q,&q,extent,token);
-            affine.ty=StringToDouble(token,&next_token);
-            if (token == next_token)
-              ThrowPointExpectedException(token,exception);
-            break;
-          }
-        if (LocaleCompare("alpha",keyword) == 0)
-          {
-            primitive_type=AlphaPrimitive;
-            break;
-          }
-        if (LocaleCompare("arc",keyword) == 0)
-          {
-            primitive_type=ArcPrimitive;
-            break;
-          }
-        status=MagickFalse;
-        break;
-      }
-      case 'b':
-      case 'B':
-      {
-        if (LocaleCompare("bezier",keyword) == 0)
-          {
-            primitive_type=BezierPrimitive;
-            break;
-          }
-        if (LocaleCompare("border-color",keyword) == 0)
-          {
-            GetNextToken(q,&q,extent,token);
-            status&=QueryColorCompliance(token,AllCompliance,
-              &graphic_context[n]->border_color,exception);
-            break;
-          }
-        status=MagickFalse;
-        break;
-      }
-      case 'c':
-      case 'C':
+      if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
+        {
+          q+=GetPixelChannels(image);
+          continue;
+        }
+      GetPixelInfoPixel(image,q,&pixel);
+      switch (gradient->spread)
       {
-        if (LocaleCompare("clip-path",keyword) == 0)
-          {
-            char
-              *clip_path;
-
-            /*
-              Take a node from within the MVG document, and duplicate it here.
-            */
-            GetNextToken(q,&q,extent,token);
-            clip_path=GetNodeByURL(primitive,token);
-            if (clip_path != (char *) NULL)
-              {
-                if (graphic_context[n]->clipping_mask != (Image *) NULL)
-                  graphic_context[n]->clipping_mask=
-                    DestroyImage(graphic_context[n]->clipping_mask);
-                graphic_context[n]->clipping_mask=DrawClippingMask(image,
-                  graphic_context[n],clip_path,exception);
-                clip_path=DestroyString(clip_path);
-                if (draw_info->compliance != SVGCompliance)
-                  status=SetImageMask(image,WritePixelMask,
-                    graphic_context[n]->clipping_mask,exception);
-              }
-            break;
-          }
-        if (LocaleCompare("clip-rule",keyword) == 0)
-          {
-            ssize_t
-              fill_rule;
-
-            GetNextToken(q,&q,extent,token);
-            fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
-              token);
-            if (fill_rule == -1)
-              status=MagickFalse;
+        case UndefinedSpread:
+        case PadSpread:
+        {
+          if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
+              (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
+            {
+              offset=GetStopColorOffset(gradient,x,y);
+              if (gradient->type != RadialGradient)
+                offset*=PerceptibleReciprocal(length);
+            }
+          for (i=0; i < (ssize_t) gradient->number_stops; i++)
+            if (offset < gradient->stops[i].offset)
+              break;
+          if ((offset < 0.0) || (i == 0))
+            composite=gradient->stops[0].color;
+          else
+            if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
+              composite=gradient->stops[gradient->number_stops-1].color;
             else
-              graphic_context[n]->fill_rule=(FillRule) fill_rule;
-            break;
-          }
-        if (LocaleCompare("clip-units",keyword) == 0)
-          {
-            ssize_t
-              clip_units;
-
-            GetNextToken(q,&q,extent,token);
-            clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
-              token);
-            if (clip_units == -1)
               {
-                status=MagickFalse;
-                break;
+                j=i;
+                i--;
+                alpha=(offset-gradient->stops[i].offset)/
+                  (gradient->stops[j].offset-gradient->stops[i].offset);
+                CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
+                  &gradient->stops[j].color,alpha,&composite);
               }
-            graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
-            if (clip_units == ObjectBoundingBox)
+          break;
+        }
+        case ReflectSpread:
+        {
+          if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
+              (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
+            {
+              offset=GetStopColorOffset(gradient,x,y);
+              if (gradient->type != RadialGradient)
+                                                               offset*=PerceptibleReciprocal(length);
+            }
+          if (offset < 0.0)
+            offset=(-offset);
+          if ((ssize_t) fmod(offset,2.0) == 0)
+            offset=fmod(offset,1.0);
+          else
+            offset=1.0-fmod(offset,1.0);
+          for (i=0; i < (ssize_t) gradient->number_stops; i++)
+            if (offset < gradient->stops[i].offset)
+              break;
+          if (i == 0)
+            composite=gradient->stops[0].color;
+          else
+            if (i == (ssize_t) gradient->number_stops)
+              composite=gradient->stops[gradient->number_stops-1].color;
+            else
               {
-                GetAffineMatrix(&current);
-                affine.sx=draw_info->bounds.x2;
-                affine.sy=draw_info->bounds.y2;
-                affine.tx=draw_info->bounds.x1;
-                affine.ty=draw_info->bounds.y1;
-                break;
+                j=i;
+                i--;
+                alpha=(offset-gradient->stops[i].offset)/
+                  (gradient->stops[j].offset-gradient->stops[i].offset);
+                CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
+                  &gradient->stops[j].color,alpha,&composite);
               }
-            break;
-          }
-        if (LocaleCompare("circle",keyword) == 0)
-          {
-            primitive_type=CirclePrimitive;
-            break;
-          }
-        if (LocaleCompare("color",keyword) == 0)
-          {
-            primitive_type=ColorPrimitive;
-            break;
-          }
-        if (LocaleCompare("compliance",keyword) == 0)
-          {
-            /*
-              MVG compliance associates a clipping mask with an image; SVG
-              compliance associates a clipping mask with a graphics context.
-            */
-            GetNextToken(q,&q,extent,token);
-            graphic_context[n]->compliance=(ComplianceType) ParseCommandOption(
-              MagickComplianceOptions,MagickFalse,token);
-            break;
-          }
-        status=MagickFalse;
-        break;
-      }
-      case 'd':
-      case 'D':
-      {
-        if (LocaleCompare("decorate",keyword) == 0)
-          {
-            ssize_t
-              decorate;
+          break;
+        }
+        case RepeatSpread:
+        {
+          MagickBooleanType
+            antialias;
 
-            GetNextToken(q,&q,extent,token);
-            decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
-              token);
-            if (decorate == -1)
-              status=MagickFalse;
-            else
-              graphic_context[n]->decorate=(DecorationType) decorate;
-            break;
-          }
-        if (LocaleCompare("density",keyword) == 0)
-          {
-            GetNextToken(q,&q,extent,token);
-            (void) CloneString(&graphic_context[n]->density,token);
-            break;
-          }
-        if (LocaleCompare("direction",keyword) == 0)
-          {
-            ssize_t
-              direction;
+          double
+            repeat;
 
-            GetNextToken(q,&q,extent,token);
-            direction=ParseCommandOption(MagickDirectionOptions,MagickFalse,
-              token);
-            if (direction == -1)
-              status=MagickFalse;
-            else
-              graphic_context[n]->direction=(DirectionType) direction;
-            break;
-          }
-        status=MagickFalse;
-        break;
-      }
-      case 'e':
-      case 'E':
-      {
-        if (LocaleCompare("ellipse",keyword) == 0)
-          {
-            primitive_type=EllipsePrimitive;
-            break;
-          }
-        if (LocaleCompare("encoding",keyword) == 0)
-          {
-            GetNextToken(q,&q,extent,token);
-            (void) CloneString(&graphic_context[n]->encoding,token);
-            break;
-          }
-        status=MagickFalse;
-        break;
-      }
-      case 'f':
-      case 'F':
-      {
-        if (LocaleCompare("fill",keyword) == 0)
-          {
-            GetNextToken(q,&q,extent,token);
-            if (graphic_context[n]->clip_path != MagickFalse)
+          antialias=MagickFalse;
+          repeat=0.0;
+          if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
+              (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
+            {
+              offset=GetStopColorOffset(gradient,x,y);
+              if (gradient->type == LinearGradient)
+                {
+                  repeat=fmod(offset,length);
+                  if (repeat < 0.0)
+                    repeat=length-fmod(-repeat,length);
+                  else
+                    repeat=fmod(offset,length);
+                  antialias=(repeat < length) && ((repeat+1.0) > length) ?
+                    MagickTrue : MagickFalse;
+                                                                 offset=PerceptibleReciprocal(length)*repeat;
+                }
+              else
+                {
+                  repeat=fmod(offset,gradient->radius);
+                  if (repeat < 0.0)
+                    repeat=gradient->radius-fmod(-repeat,gradient->radius);
+                  else
+                    repeat=fmod(offset,gradient->radius);
+                  antialias=repeat+1.0 > gradient->radius ? MagickTrue :
+                    MagickFalse;
+                  offset=repeat/gradient->radius;
+                }
+            }
+          for (i=0; i < (ssize_t) gradient->number_stops; i++)
+            if (offset < gradient->stops[i].offset)
               break;
-            (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
-            if (GetImageArtifact(image,pattern) != (const char *) NULL)
-              (void) DrawPatternPath(image,draw_info,token,
-                &graphic_context[n]->fill_pattern,exception);
+          if (i == 0)
+            composite=gradient->stops[0].color;
+          else
+            if (i == (ssize_t) gradient->number_stops)
+              composite=gradient->stops[gradient->number_stops-1].color;
             else
               {
-                status&=QueryColorCompliance(token,AllCompliance,
-                  &graphic_context[n]->fill,exception);
-                if (graphic_context[n]->fill_alpha != OpaqueAlpha)
-                  graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
-              }
-            break;
-          }
-        if (LocaleCompare("fill-opacity",keyword) == 0)
-          {
-            double
-              opacity;
+                j=i;
+                i--;
+                alpha=(offset-gradient->stops[i].offset)/
+                  (gradient->stops[j].offset-gradient->stops[i].offset);
+                if (antialias != MagickFalse)
+                  {
+                    if (gradient->type == LinearGradient)
+                      alpha=length-repeat;
+                    else
+                      alpha=gradient->radius-repeat;
+                    i=0;
+                    j=(ssize_t) gradient->number_stops-1L;
+                  }
+                CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
+                  &gradient->stops[j].color,alpha,&composite);
+              }
+          break;
+        }
+      }
+      CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
+        &pixel);
+      SetPixelViaPixelInfo(image,&pixel,q);
+      q+=GetPixelChannels(image);
+    }
+    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+      status=MagickFalse;
+  }
+  image_view=DestroyCacheView(image_view);
+  return(status);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%   D r a w I m a g e                                                         %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  DrawImage() draws a graphic primitive on your image.  The primitive
+%  may be represented as a string or filename.  Precede the filename with an
+%  "at" sign (@) and the contents of the file are drawn on the image.  You
+%  can affect how text is drawn by setting one or more members of the draw
+%  info structure.
+%
+%  The format of the DrawImage method is:
+%
+%      MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
+%        ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o image: the image.
+%
+%    o draw_info: the draw info.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
 
-            GetNextToken(q,&q,extent,token);
-            if (graphic_context[n]->clip_path != MagickFalse)
-              break;
-            factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
-            opacity=MagickMin(MagickMax(factor*
-              StringToDouble(token,&next_token),0.0),1.0);
-            if (token == next_token)
-              ThrowPointExpectedException(token,exception);
-            graphic_context[n]->fill_alpha=(MagickRealType) (QuantumRange-
-              QuantumRange*(1.0-opacity));
-            break;
-          }
-        if (LocaleCompare("fill-rule",keyword) == 0)
-          {
-            ssize_t
-              fill_rule;
+static char *GetNodeByURL(const char *primitive,const char *url)
+{
+  char
+    *token;
 
-            GetNextToken(q,&q,extent,token);
-            fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
-              token);
-            if (fill_rule == -1)
-              status=MagickFalse;
-            else
-              graphic_context[n]->fill_rule=(FillRule) fill_rule;
-            break;
-          }
-        if (LocaleCompare("font",keyword) == 0)
-          {
-            GetNextToken(q,&q,extent,token);
-            (void) CloneString(&graphic_context[n]->font,token);
-            if (LocaleCompare("none",token) == 0)
-              graphic_context[n]->font=(char *) RelinquishMagickMemory(
-                graphic_context[n]->font);
-            break;
-          }
-        if (LocaleCompare("font-family",keyword) == 0)
-          {
-            GetNextToken(q,&q,extent,token);
-            (void) CloneString(&graphic_context[n]->family,token);
-            break;
-          }
-        if (LocaleCompare("font-size",keyword) == 0)
-          {
-            GetNextToken(q,&q,extent,token);
-            graphic_context[n]->pointsize=StringToDouble(token,&next_token);
-            if (token == next_token)
-              ThrowPointExpectedException(token,exception);
-            break;
-          }
-        if (LocaleCompare("font-stretch",keyword) == 0)
-          {
-            ssize_t
-              stretch;
+  const char
+    *q,
+    *start;
 
-            GetNextToken(q,&q,extent,token);
-            stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
-            if (stretch == -1)
-              status=MagickFalse;
-            else
-              graphic_context[n]->stretch=(StretchType) stretch;
-            break;
-          }
-        if (LocaleCompare("font-style",keyword) == 0)
-          {
-            ssize_t
-              style;
+  register const char
+    *p;
 
-            GetNextToken(q,&q,extent,token);
-            style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
-            if (style == -1)
-              status=MagickFalse;
-            else
-              graphic_context[n]->style=(StyleType) style;
-            break;
-          }
-        if (LocaleCompare("font-weight",keyword) == 0)
-          {
-            ssize_t
-              weight;
+  size_t
+    extent,
+    length;
 
-            GetNextToken(q,&q,extent,token);
-            weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token);
-            if (weight == -1)
-              weight=(ssize_t) StringToUnsignedLong(token);
-            graphic_context[n]->weight=(size_t) weight;
-            break;
-          }
-        status=MagickFalse;
-        break;
+  ssize_t
+    n;
+
+  /*
+    Find and return node by ID.
+  */
+  token=AcquireString(primitive);
+  extent=strlen(token)+MagickPathExtent;
+  length=0;
+  n=0;
+  start=(const char *) NULL;
+  p=(const char *) NULL;
+  for (q=primitive; (*q != '\0') && (length == 0); )
+  {
+    p=q;
+    GetNextToken(q,&q,extent,token);
+    if (*token == '\0')
+      break;
+    if (*token == '#')
+      {
+        /*
+          Comment.
+        */
+        while ((*q != '\n') && (*q != '\0'))
+          q++;
+        continue;
       }
-      case 'g':
-      case 'G':
+    if (LocaleCompare("pop",token) == 0)
       {
-        if (LocaleCompare("gradient-units",keyword) == 0)
-          {
-            GetNextToken(q,&q,extent,token);
-            break;
-          }
-        if (LocaleCompare("gravity",keyword) == 0)
+        GetNextToken(q,&q,extent,token);
+        if ((n == 0) && (start != (const char *) NULL))
           {
-            ssize_t
-              gravity;
-
-            GetNextToken(q,&q,extent,token);
-            gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
-            if (gravity == -1)
-              status=MagickFalse;
-            else
-              graphic_context[n]->gravity=(GravityType) gravity;
+            /*
+              End of node by ID.
+            */
+            length=(size_t) (p-start+1);
             break;
           }
-        status=MagickFalse;
-        break;
+        n--;
       }
-      case 'i':
-      case 'I':
+    if (LocaleCompare("push",token) == 0)
       {
-        if (LocaleCompare("image",keyword) == 0)
-          {
-            ssize_t
-              compose;
-
-            primitive_type=ImagePrimitive;
-            GetNextToken(q,&q,extent,token);
-            compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
-            if (compose == -1)
-              status=MagickFalse;
-            else
-              graphic_context[n]->compose=(CompositeOperator) compose;
-            break;
-          }
-        if (LocaleCompare("interline-spacing",keyword) == 0)
+        GetNextToken(q,&q,extent,token);
+        n++;
+        if (*q == '"')
           {
             GetNextToken(q,&q,extent,token);
-            graphic_context[n]->interline_spacing=StringToDouble(token,
-              &next_token);
-            if (token == next_token)
-              ThrowPointExpectedException(token,exception);
-            break;
+            if (LocaleCompare(url,token) == 0)
+              {
+                /*
+                  Start of node by ID.
+                */
+                n=0;
+                start=q;
+              }
           }
-        if (LocaleCompare("interword-spacing",keyword) == 0)
-          {
-            GetNextToken(q,&q,extent,token);
-            graphic_context[n]->interword_spacing=StringToDouble(token,
-              &next_token);
-            if (token == next_token)
-              ThrowPointExpectedException(token,exception);
-            break;
+      }
+  }
+  if (start == (const char *) NULL)
+    return(DestroyString(token));
+  (void) CopyMagickString(token,start,length);
+  return(token);
+}
+
+static inline MagickBooleanType IsPoint(const char *point)
+{
+  char
+    *p;
+
+  double
+    value;
+
+  value=StringToDouble(point,&p);
+  return((fabs(value) < DrawEpsilon) && (p == point) ? MagickFalse : MagickTrue);
+}
+
+static size_t ReckonEllipseCoordinates(const PointInfo radii,
+  const PointInfo arc)
+{
+  double
+    delta,
+    step,
+    y;
+
+  PointInfo
+    angle;
+
+  /*
+    Ellipses are just short segmented polys.
+  */
+  if ((fabs(radii.x) < DrawEpsilon) || (fabs(radii.y) < DrawEpsilon))
+    return(0);
+  delta=2.0*PerceptibleReciprocal(MagickMax(radii.x,radii.y));
+  step=MagickPI/8.0;
+  if ((delta >= 0.0) && (delta < (MagickPI/8.0)))
+    step=MagickPI/(4.0*(MagickPI*PerceptibleReciprocal(delta)/2.0));
+  angle.x=DegreesToRadians(arc.x);
+  y=arc.y;
+  while (y < arc.x)
+    y+=360.0;
+  angle.y=DegreesToRadians(y);
+  return((size_t) floor((angle.y-angle.x)/step+0.5)+3);
+}
+
+static size_t ReckonRoundRectangleCoordinates(const PointInfo start,
+  const PointInfo end,PointInfo arc)
+{
+  PointInfo
+    degrees,
+    offset;
+
+  size_t
+    coordinates;
+
+  offset.x=fabs(end.x-start.x);
+  offset.y=fabs(end.y-start.y);
+  if ((offset.x < DrawEpsilon) || (offset.y < DrawEpsilon))
+    return(0);
+  coordinates=0;
+  if (arc.x > (0.5*offset.x))
+    arc.x=0.5*offset.x;
+  if (arc.y > (0.5*offset.y))
+    arc.y=0.5*offset.y;
+  degrees.x=270.0;
+  degrees.y=360.0;
+  coordinates+=ReckonEllipseCoordinates(arc,degrees);
+  degrees.x=0.0;
+  degrees.y=90.0;
+  coordinates+=ReckonEllipseCoordinates(arc,degrees);
+  degrees.x=90.0;
+  degrees.y=180.0;
+  coordinates+=ReckonEllipseCoordinates(arc,degrees);
+  degrees.x=180.0;
+  degrees.y=270.0;
+  coordinates+=ReckonEllipseCoordinates(arc,degrees);
+  return(coordinates+1);
+}
+
+static inline void TracePoint(PrimitiveInfo *primitive_info,
+  const PointInfo point)
+{
+  primitive_info->coordinates=1;
+  primitive_info->closed_subpath=MagickFalse;
+  primitive_info->point=point;
+}
+
+MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
+  ExceptionInfo *exception)
+{
+#define RenderImageTag  "Render/Image"
+
+  AffineMatrix
+    affine,
+    current;
+
+  char
+    keyword[MagickPathExtent],
+    geometry[MagickPathExtent],
+    *next_token,
+    pattern[MagickPathExtent],
+    *primitive,
+    *token;
+
+  const char
+    *q;
+
+  double
+    angle,
+    factor,
+    primitive_extent;
+
+  DrawInfo
+    **graphic_context;
+
+  MagickBooleanType
+    proceed;
+
+  MagickSizeType
+    number_points;
+
+  MagickStatusType
+    status;
+
+  PointInfo
+    point;
+
+  PrimitiveInfo
+    *primitive_info;
+
+  PrimitiveType
+    primitive_type;
+
+  register const char
+    *p;
+
+  register ssize_t
+    i,
+    x;
+
+  SegmentInfo
+    bounds;
+
+  size_t
+    coordinates,
+    extent,
+    number_stops;
+
+  ssize_t
+    defsDepth,
+    j,
+    k,
+    n,
+    symbolDepth;
+
+  StopInfo
+    *stops;
+
+  assert(image != (Image *) NULL);
+  assert(image->signature == MagickCoreSignature);
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  assert(draw_info != (DrawInfo *) NULL);
+  assert(draw_info->signature == MagickCoreSignature);
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+  if ((draw_info->primitive == (char *) NULL) ||
+      (*draw_info->primitive == '\0'))
+    return(MagickFalse);
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
+  primitive=(char *) NULL;
+  if (*draw_info->primitive != '@')
+    primitive=AcquireString(draw_info->primitive);
+  else
+    if ((strlen(draw_info->primitive) > 1) &&
+        (*(draw_info->primitive+1) != '-'))
+      primitive=FileToString(draw_info->primitive+1,~0UL,exception);
+  if (primitive == (char *) NULL)
+    return(MagickFalse);
+  primitive_extent=(double) strlen(primitive);
+  (void) SetImageArtifact(image,"MVG",primitive);
+  n=0;
+  number_stops=0;
+  stops=(StopInfo *) NULL;
+  /*
+    Allocate primitive info memory.
+  */
+  graphic_context=(DrawInfo **) AcquireMagickMemory(sizeof(*graphic_context));
+  if (graphic_context == (DrawInfo **) NULL)
+    {
+      primitive=DestroyString(primitive);
+      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+        image->filename);
+    }
+  number_points=65536;
+  primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
+    sizeof(*primitive_info));
+  if (primitive_info == (PrimitiveInfo *) NULL)
+    {
+      primitive=DestroyString(primitive);
+      for ( ; n >= 0; n--)
+        graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
+      graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
+      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+        image->filename);
+    }
+  (void) memset(primitive_info,0,(size_t) number_points*
+    sizeof(*primitive_info));
+  graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+  graphic_context[n]->viewbox=image->page;
+  if ((image->page.width == 0) || (image->page.height == 0))
+    {
+      graphic_context[n]->viewbox.width=image->columns;
+      graphic_context[n]->viewbox.height=image->rows;
+    }
+  token=AcquireString(primitive);
+  extent=strlen(token)+MagickPathExtent;
+  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
+    return(MagickFalse);
+  defsDepth=0;
+  symbolDepth=0;
+  status=MagickTrue;
+  for (q=primitive; *q != '\0'; )
+  {
+    /*
+      Interpret graphic primitive.
+    */
+    GetNextToken(q,&q,MagickPathExtent,keyword);
+    if (*keyword == '\0')
+      break;
+    if (*keyword == '#')
+      {
+        /*
+          Comment.
+        */
+        while ((*q != '\n') && (*q != '\0'))
+          q++;
+        continue;
+      }
+    p=q-strlen(keyword)-1;
+    primitive_type=UndefinedPrimitive;
+    current=graphic_context[n]->affine;
+    GetAffineMatrix(&affine);
+    switch (*keyword)
+    {
+      case ';':
+        break;
+      case 'a':
+      case 'A':
+      {
+        if (LocaleCompare("affine",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            affine.sx=StringToDouble(token,&next_token);
+            if (token == next_token)
+              ThrowPointExpectedException(token,exception);
+            GetNextToken(q,&q,extent,token);
+            if (*token == ',')
+              GetNextToken(q,&q,extent,token);
+            affine.rx=StringToDouble(token,&next_token);
+            if (token == next_token)
+              ThrowPointExpectedException(token,exception);
+            GetNextToken(q,&q,extent,token);
+            if (*token == ',')
+              GetNextToken(q,&q,extent,token);
+            affine.ry=StringToDouble(token,&next_token);
+            if (token == next_token)
+              ThrowPointExpectedException(token,exception);
+            GetNextToken(q,&q,extent,token);
+            if (*token == ',')
+              GetNextToken(q,&q,extent,token);
+            affine.sy=StringToDouble(token,&next_token);
+            if (token == next_token)
+              ThrowPointExpectedException(token,exception);
+            GetNextToken(q,&q,extent,token);
+            if (*token == ',')
+              GetNextToken(q,&q,extent,token);
+            affine.tx=StringToDouble(token,&next_token);
+            if (token == next_token)
+              ThrowPointExpectedException(token,exception);
+            GetNextToken(q,&q,extent,token);
+            if (*token == ',')
+              GetNextToken(q,&q,extent,token);
+            affine.ty=StringToDouble(token,&next_token);
+            if (token == next_token)
+              ThrowPointExpectedException(token,exception);
+            break;
+          }
+        if (LocaleCompare("alpha",keyword) == 0)
+          {
+            primitive_type=AlphaPrimitive;
+            break;
+          }
+        if (LocaleCompare("arc",keyword) == 0)
+          {
+            primitive_type=ArcPrimitive;
+            break;
+          }
+        status=MagickFalse;
+        break;
+      }
+      case 'b':
+      case 'B':
+      {
+        if (LocaleCompare("bezier",keyword) == 0)
+          {
+            primitive_type=BezierPrimitive;
+            break;
+          }
+        if (LocaleCompare("border-color",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            status&=QueryColorCompliance(token,AllCompliance,
+              &graphic_context[n]->border_color,exception);
+            break;
+          }
+        status=MagickFalse;
+        break;
+      }
+      case 'c':
+      case 'C':
+      {
+        if (LocaleCompare("clip-path",keyword) == 0)
+          {
+            char
+              *clip_path;
+
+            /*
+              Take a node from within the MVG document, and duplicate it here.
+            */
+            GetNextToken(q,&q,extent,token);
+            clip_path=GetNodeByURL(primitive,token);
+            if (clip_path != (char *) NULL)
+              {
+                if (graphic_context[n]->clipping_mask != (Image *) NULL)
+                  graphic_context[n]->clipping_mask=
+                    DestroyImage(graphic_context[n]->clipping_mask);
+                graphic_context[n]->clipping_mask=DrawClippingMask(image,
+                  graphic_context[n],clip_path,exception);
+                clip_path=DestroyString(clip_path);
+                if (draw_info->compliance != SVGCompliance)
+                  status=SetImageMask(image,WritePixelMask,
+                    graphic_context[n]->clipping_mask,exception);
+              }
+            break;
+          }
+        if (LocaleCompare("clip-rule",keyword) == 0)
+          {
+            ssize_t
+              fill_rule;
+
+            GetNextToken(q,&q,extent,token);
+            fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
+              token);
+            if (fill_rule == -1)
+              status=MagickFalse;
+            else
+              graphic_context[n]->fill_rule=(FillRule) fill_rule;
+            break;
+          }
+        if (LocaleCompare("clip-units",keyword) == 0)
+          {
+            ssize_t
+              clip_units;
+
+            GetNextToken(q,&q,extent,token);
+            clip_units=ParseCommandOption(MagickClipPathOptions,MagickFalse,
+              token);
+            if (clip_units == -1)
+              {
+                status=MagickFalse;
+                break;
+              }
+            graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
+            if (clip_units == ObjectBoundingBox)
+              {
+                GetAffineMatrix(&current);
+                affine.sx=draw_info->bounds.x2;
+                affine.sy=draw_info->bounds.y2;
+                affine.tx=draw_info->bounds.x1;
+                affine.ty=draw_info->bounds.y1;
+                break;
+              }
+            break;
+          }
+        if (LocaleCompare("circle",keyword) == 0)
+          {
+            primitive_type=CirclePrimitive;
+            break;
+          }
+        if (LocaleCompare("color",keyword) == 0)
+          {
+            primitive_type=ColorPrimitive;
+            break;
+          }
+        if (LocaleCompare("compliance",keyword) == 0)
+          {
+            /*
+              MVG compliance associates a clipping mask with an image; SVG
+              compliance associates a clipping mask with a graphics context.
+            */
+            GetNextToken(q,&q,extent,token);
+            graphic_context[n]->compliance=(ComplianceType) ParseCommandOption(
+              MagickComplianceOptions,MagickFalse,token);
+            break;
+          }
+        status=MagickFalse;
+        break;
+      }
+      case 'd':
+      case 'D':
+      {
+        if (LocaleCompare("decorate",keyword) == 0)
+          {
+            ssize_t
+              decorate;
+
+            GetNextToken(q,&q,extent,token);
+            decorate=ParseCommandOption(MagickDecorateOptions,MagickFalse,
+              token);
+            if (decorate == -1)
+              status=MagickFalse;
+            else
+              graphic_context[n]->decorate=(DecorationType) decorate;
+            break;
+          }
+        if (LocaleCompare("density",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            (void) CloneString(&graphic_context[n]->density,token);
+            break;
+          }
+        if (LocaleCompare("direction",keyword) == 0)
+          {
+            ssize_t
+              direction;
+
+            GetNextToken(q,&q,extent,token);
+            direction=ParseCommandOption(MagickDirectionOptions,MagickFalse,
+              token);
+            if (direction == -1)
+              status=MagickFalse;
+            else
+              graphic_context[n]->direction=(DirectionType) direction;
+            break;
+          }
+        status=MagickFalse;
+        break;
+      }
+      case 'e':
+      case 'E':
+      {
+        if (LocaleCompare("ellipse",keyword) == 0)
+          {
+            primitive_type=EllipsePrimitive;
+            break;
+          }
+        if (LocaleCompare("encoding",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            (void) CloneString(&graphic_context[n]->encoding,token);
+            break;
+          }
+        status=MagickFalse;
+        break;
+      }
+      case 'f':
+      case 'F':
+      {
+        if (LocaleCompare("fill",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            if (graphic_context[n]->clip_path != MagickFalse)
+              break;
+            (void) FormatLocaleString(pattern,MagickPathExtent,"%s",token);
+            if (GetImageArtifact(image,pattern) != (const char *) NULL)
+              (void) DrawPatternPath(image,draw_info,token,
+                &graphic_context[n]->fill_pattern,exception);
+            else
+              {
+                status&=QueryColorCompliance(token,AllCompliance,
+                  &graphic_context[n]->fill,exception);
+                if (graphic_context[n]->fill_alpha != OpaqueAlpha)
+                  graphic_context[n]->fill.alpha=graphic_context[n]->fill_alpha;
+              }
+            break;
+          }
+        if (LocaleCompare("fill-opacity",keyword) == 0)
+          {
+            double
+              opacity;
+
+            GetNextToken(q,&q,extent,token);
+            if (graphic_context[n]->clip_path != MagickFalse)
+              break;
+            factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
+            opacity=MagickMin(MagickMax(factor*
+              StringToDouble(token,&next_token),0.0),1.0);
+            if (token == next_token)
+              ThrowPointExpectedException(token,exception);
+            graphic_context[n]->fill_alpha=(MagickRealType) (QuantumRange-
+              QuantumRange*(1.0-opacity));
+            break;
+          }
+        if (LocaleCompare("fill-rule",keyword) == 0)
+          {
+            ssize_t
+              fill_rule;
+
+            GetNextToken(q,&q,extent,token);
+            fill_rule=ParseCommandOption(MagickFillRuleOptions,MagickFalse,
+              token);
+            if (fill_rule == -1)
+              status=MagickFalse;
+            else
+              graphic_context[n]->fill_rule=(FillRule) fill_rule;
+            break;
+          }
+        if (LocaleCompare("font",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            (void) CloneString(&graphic_context[n]->font,token);
+            if (LocaleCompare("none",token) == 0)
+              graphic_context[n]->font=(char *) RelinquishMagickMemory(
+                graphic_context[n]->font);
+            break;
+          }
+        if (LocaleCompare("font-family",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            (void) CloneString(&graphic_context[n]->family,token);
+            break;
+          }
+        if (LocaleCompare("font-size",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            graphic_context[n]->pointsize=StringToDouble(token,&next_token);
+            if (token == next_token)
+              ThrowPointExpectedException(token,exception);
+            break;
+          }
+        if (LocaleCompare("font-stretch",keyword) == 0)
+          {
+            ssize_t
+              stretch;
+
+            GetNextToken(q,&q,extent,token);
+            stretch=ParseCommandOption(MagickStretchOptions,MagickFalse,token);
+            if (stretch == -1)
+              status=MagickFalse;
+            else
+              graphic_context[n]->stretch=(StretchType) stretch;
+            break;
+          }
+        if (LocaleCompare("font-style",keyword) == 0)
+          {
+            ssize_t
+              style;
+
+            GetNextToken(q,&q,extent,token);
+            style=ParseCommandOption(MagickStyleOptions,MagickFalse,token);
+            if (style == -1)
+              status=MagickFalse;
+            else
+              graphic_context[n]->style=(StyleType) style;
+            break;
+          }
+        if (LocaleCompare("font-weight",keyword) == 0)
+          {
+            ssize_t
+              weight;
+
+            GetNextToken(q,&q,extent,token);
+            weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token);
+            if (weight == -1)
+              weight=(ssize_t) StringToUnsignedLong(token);
+            graphic_context[n]->weight=(size_t) weight;
+            break;
+          }
+        status=MagickFalse;
+        break;
+      }
+      case 'g':
+      case 'G':
+      {
+        if (LocaleCompare("gradient-units",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            break;
+          }
+        if (LocaleCompare("gravity",keyword) == 0)
+          {
+            ssize_t
+              gravity;
+
+            GetNextToken(q,&q,extent,token);
+            gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,token);
+            if (gravity == -1)
+              status=MagickFalse;
+            else
+              graphic_context[n]->gravity=(GravityType) gravity;
+            break;
+          }
+        status=MagickFalse;
+        break;
+      }
+      case 'i':
+      case 'I':
+      {
+        if (LocaleCompare("image",keyword) == 0)
+          {
+            ssize_t
+              compose;
+
+            primitive_type=ImagePrimitive;
+            GetNextToken(q,&q,extent,token);
+            compose=ParseCommandOption(MagickComposeOptions,MagickFalse,token);
+            if (compose == -1)
+              status=MagickFalse;
+            else
+              graphic_context[n]->compose=(CompositeOperator) compose;
+            break;
+          }
+        if (LocaleCompare("interline-spacing",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            graphic_context[n]->interline_spacing=StringToDouble(token,
+              &next_token);
+            if (token == next_token)
+              ThrowPointExpectedException(token,exception);
+            break;
+          }
+        if (LocaleCompare("interword-spacing",keyword) == 0)
+          {
+            GetNextToken(q,&q,extent,token);
+            graphic_context[n]->interword_spacing=StringToDouble(token,
+              &next_token);
+            if (token == next_token)
+              ThrowPointExpectedException(token,exception);
+            break;
           }
         status=MagickFalse;
         break;
@@ -3545,474 +3881,138 @@ MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info,
         primitive_info[i].coordinates=0;
         primitive_info[j].coordinates++;
         primitive_info[j].closed_subpath=MagickTrue;
-        i++;
-        break;
-      }
-      case BezierPrimitive:
-      {
-        if (primitive_info[j].coordinates < 3)
-          {
-            status=MagickFalse;
-            break;
-          }
-        TraceBezier(primitive_info+j,primitive_info[j].coordinates);
-        i=(ssize_t) (j+primitive_info[j].coordinates);
-        break;
-      }
-      case PathPrimitive:
-      {
-        coordinates=TracePath(primitive_info+j,token,exception);
-        if (coordinates == 0)
-          {
-            status=MagickFalse;
-            break;
-          }
-        i=(ssize_t) (j+coordinates);
-        break;
-      }
-      case AlphaPrimitive:
-      case ColorPrimitive:
-      {
-        ssize_t
-          method;
-
-        if (primitive_info[j].coordinates != 1)
-          {
-            status=MagickFalse;
-            break;
-          }
-        GetNextToken(q,&q,extent,token);
-        method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
-        if (method == -1)
-          status=MagickFalse;
-        else
-          primitive_info[j].method=(PaintMethod) method;
+        i++;
         break;
       }
-      case TextPrimitive:
+      case BezierPrimitive:
       {
-        if (primitive_info[j].coordinates != 1)
+        if (primitive_info[j].coordinates < 3)
           {
             status=MagickFalse;
             break;
           }
-        if (*token != ',')
-          GetNextToken(q,&q,extent,token);
-        primitive_info[j].text=AcquireString(token);
+        TraceBezier(primitive_info+j,primitive_info[j].coordinates);
+        i=(ssize_t) (j+primitive_info[j].coordinates);
         break;
       }
-      case ImagePrimitive:
+      case PathPrimitive:
       {
-        if (primitive_info[j].coordinates != 2)
+        coordinates=TracePath(primitive_info+j,token,exception);
+        if (coordinates == 0)
           {
             status=MagickFalse;
             break;
           }
-        GetNextToken(q,&q,extent,token);
-        primitive_info[j].text=AcquireString(token);
+        i=(ssize_t) (j+coordinates);
         break;
       }
-    }
-    if (primitive_info == (PrimitiveInfo *) NULL)
-      break;
-    if (image->debug != MagickFalse)
-      (void) LogMagickEvent(DrawEvent,GetMagickModule(),"  %.*s",(int) (q-p),p);
-    if (status == MagickFalse)
-      break;
-    primitive_info[i].primitive=UndefinedPrimitive;
-    if (i == 0)
-      continue;
-    /*
-      Transform points.
-    */
-    for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
-    {
-      point=primitive_info[i].point;
-      primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
-        graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
-      primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
-        graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
-      point=primitive_info[i].point;
-      if (point.x < graphic_context[n]->bounds.x1)
-        graphic_context[n]->bounds.x1=point.x;
-      if (point.y < graphic_context[n]->bounds.y1)
-        graphic_context[n]->bounds.y1=point.y;
-      if (point.x > graphic_context[n]->bounds.x2)
-        graphic_context[n]->bounds.x2=point.x;
-      if (point.y > graphic_context[n]->bounds.y2)
-        graphic_context[n]->bounds.y2=point.y;
-      if (primitive_info[i].primitive == ImagePrimitive)
-        break;
-      if (i >= (ssize_t) number_points)
-        ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
-    }
-    if (graphic_context[n]->render != MagickFalse)
-      status&=DrawPrimitive(image,graphic_context[n],primitive_info,
-        exception);
-    if (primitive_info->text != (char *) NULL)
-      primitive_info->text=(char *) RelinquishMagickMemory(
-        primitive_info->text);
-    proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
-      primitive_extent);
-    if (proceed == MagickFalse)
-      break;
-    if (status == 0)
-      break;
-  }
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
-  /*
-    Relinquish resources.
-  */
-  token=DestroyString(token);
-  if (primitive_info != (PrimitiveInfo *) NULL)
-    primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
-  primitive=DestroyString(primitive);
-  if (stops != (StopInfo *) NULL)
-    stops=(StopInfo *) RelinquishMagickMemory(stops);
-  for ( ; n >= 0; n--)
-    graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
-  graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
-  if (status == MagickFalse)
-    ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
-      keyword);
-  return(status != 0 ? MagickTrue : MagickFalse);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%     D r a w G r a d i e n t I m a g e                                       %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  DrawGradientImage() draws a linear gradient on the image.
-%
-%  The format of the DrawGradientImage method is:
-%
-%      MagickBooleanType DrawGradientImage(Image *image,
-%        const DrawInfo *draw_info,ExceptionInfo *exception)
-%
-%  A description of each parameter follows:
-%
-%    o image: the image.
-%
-%    o draw_info: the draw info.
-%
-%    o exception: return any errors or warnings in this structure.
-%
-*/
-
-static inline double GetStopColorOffset(const GradientInfo *gradient,
-  const ssize_t x,const ssize_t y)
-{
-  switch (gradient->type)
-  {
-    case UndefinedGradient:
-    case LinearGradient:
-    {
-      double
-        gamma,
-        length,
-        offset,
-        scale;
-
-      PointInfo
-        p,
-        q;
-
-      const SegmentInfo
-        *gradient_vector;
-
-      gradient_vector=(&gradient->gradient_vector);
-      p.x=gradient_vector->x2-gradient_vector->x1;
-      p.y=gradient_vector->y2-gradient_vector->y1;
-      q.x=(double) x-gradient_vector->x1;
-      q.y=(double) y-gradient_vector->y1;
-      length=sqrt(q.x*q.x+q.y*q.y);
-      gamma=sqrt(p.x*p.x+p.y*p.y)*length;
-      gamma=PerceptibleReciprocal(gamma);
-      scale=p.x*q.x+p.y*q.y;
-      offset=gamma*scale*length;
-      return(offset);
-    }
-    case RadialGradient:
-    {
-      PointInfo
-        v;
-
-      if (gradient->spread == RepeatSpread)
-        {
-          v.x=(double) x-gradient->center.x;
-          v.y=(double) y-gradient->center.y;
-          return(sqrt(v.x*v.x+v.y*v.y));
-        }
-      v.x=(double) (((x-gradient->center.x)*cos(DegreesToRadians(
-        gradient->angle)))+((y-gradient->center.y)*sin(DegreesToRadians(
-        gradient->angle))))*PerceptibleReciprocal(gradient->radii.x);
-      v.y=(double) (((x-gradient->center.x)*sin(DegreesToRadians(
-        gradient->angle)))-((y-gradient->center.y)*cos(DegreesToRadians(
-        gradient->angle))))*PerceptibleReciprocal(gradient->radii.y);
-      return(sqrt(v.x*v.x+v.y*v.y));
-    }
-  }
-  return(0.0);
-}
-
-static int StopInfoCompare(const void *x,const void *y)
-{
-  StopInfo
-    *stop_1,
-    *stop_2;
-
-  stop_1=(StopInfo *) x;
-  stop_2=(StopInfo *) y;
-  if (stop_1->offset > stop_2->offset)
-    return(1);
-  if (fabs(stop_1->offset-stop_2->offset) <= DrawEpsilon)
-    return(0);
-  return(-1);
-}
-
-MagickExport MagickBooleanType DrawGradientImage(Image *image,
-  const DrawInfo *draw_info,ExceptionInfo *exception)
-{
-  CacheView
-    *image_view;
-
-  const GradientInfo
-    *gradient;
-
-  const SegmentInfo
-    *gradient_vector;
-
-  double
-    length;
-
-  MagickBooleanType
-    status;
-
-  PixelInfo
-    zero;
-
-  PointInfo
-    point;
-
-  RectangleInfo
-    bounding_box;
-
-  ssize_t
-    y;
-
-  /*
-    Draw linear or radial gradient on image.
-  */
-  assert(image != (Image *) NULL);
-  assert(image->signature == MagickCoreSignature);
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  assert(draw_info != (const DrawInfo *) NULL);
-  gradient=(&draw_info->gradient);
-  qsort(gradient->stops,gradient->number_stops,sizeof(StopInfo),
-    StopInfoCompare);
-  gradient_vector=(&gradient->gradient_vector);
-  point.x=gradient_vector->x2-gradient_vector->x1;
-  point.y=gradient_vector->y2-gradient_vector->y1;
-  length=sqrt(point.x*point.x+point.y*point.y);
-  bounding_box=gradient->bounding_box;
-  status=MagickTrue;
-  GetPixelInfo(image,&zero);
-  image_view=AcquireAuthenticCacheView(image,exception);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(static) shared(status) \
-    magick_number_threads(image,image,bounding_box.height-bounding_box.y,1)
-#endif
-  for (y=bounding_box.y; y < (ssize_t) bounding_box.height; y++)
-  {
-    PixelInfo
-      composite,
-      pixel;
-
-    double
-      alpha,
-      offset;
-
-    register Quantum
-      *magick_restrict q;
-
-    register ssize_t
-      i,
-      x;
-
-    ssize_t
-      j;
+      case AlphaPrimitive:
+      case ColorPrimitive:
+      {
+        ssize_t
+          method;
 
-    if (status == MagickFalse)
-      continue;
-    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
-    if (q == (Quantum *) NULL)
+        if (primitive_info[j].coordinates != 1)
+          {
+            status=MagickFalse;
+            break;
+          }
+        GetNextToken(q,&q,extent,token);
+        method=ParseCommandOption(MagickMethodOptions,MagickFalse,token);
+        if (method == -1)
+          status=MagickFalse;
+        else
+          primitive_info[j].method=(PaintMethod) method;
+        break;
+      }
+      case TextPrimitive:
       {
-        status=MagickFalse;
-        continue;
+        if (primitive_info[j].coordinates != 1)
+          {
+            status=MagickFalse;
+            break;
+          }
+        if (*token != ',')
+          GetNextToken(q,&q,extent,token);
+        primitive_info[j].text=AcquireString(token);
+        break;
       }
-    pixel=zero;
-    composite=zero;
-    offset=GetStopColorOffset(gradient,0,y);
-    if (gradient->type != RadialGradient)
-      offset*=PerceptibleReciprocal(length);
-    for (x=bounding_box.x; x < (ssize_t) bounding_box.width; x++)
-    {
-      if (GetPixelWriteMask(image,q) <= (QuantumRange/2))
-        {
-          q+=GetPixelChannels(image);
-          continue;
-        }
-      GetPixelInfoPixel(image,q,&pixel);
-      switch (gradient->spread)
+      case ImagePrimitive:
       {
-        case UndefinedSpread:
-        case PadSpread:
-        {
-          if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
-              (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
-            {
-              offset=GetStopColorOffset(gradient,x,y);
-              if (gradient->type != RadialGradient)
-                offset*=PerceptibleReciprocal(length);
-            }
-          for (i=0; i < (ssize_t) gradient->number_stops; i++)
-            if (offset < gradient->stops[i].offset)
-              break;
-          if ((offset < 0.0) || (i == 0))
-            composite=gradient->stops[0].color;
-          else
-            if ((offset > 1.0) || (i == (ssize_t) gradient->number_stops))
-              composite=gradient->stops[gradient->number_stops-1].color;
-            else
-              {
-                j=i;
-                i--;
-                alpha=(offset-gradient->stops[i].offset)/
-                  (gradient->stops[j].offset-gradient->stops[i].offset);
-                CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
-                  &gradient->stops[j].color,alpha,&composite);
-              }
-          break;
-        }
-        case ReflectSpread:
-        {
-          if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
-              (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
-            {
-              offset=GetStopColorOffset(gradient,x,y);
-              if (gradient->type != RadialGradient)
-                                                               offset*=PerceptibleReciprocal(length);
-            }
-          if (offset < 0.0)
-            offset=(-offset);
-          if ((ssize_t) fmod(offset,2.0) == 0)
-            offset=fmod(offset,1.0);
-          else
-            offset=1.0-fmod(offset,1.0);
-          for (i=0; i < (ssize_t) gradient->number_stops; i++)
-            if (offset < gradient->stops[i].offset)
-              break;
-          if (i == 0)
-            composite=gradient->stops[0].color;
-          else
-            if (i == (ssize_t) gradient->number_stops)
-              composite=gradient->stops[gradient->number_stops-1].color;
-            else
-              {
-                j=i;
-                i--;
-                alpha=(offset-gradient->stops[i].offset)/
-                  (gradient->stops[j].offset-gradient->stops[i].offset);
-                CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
-                  &gradient->stops[j].color,alpha,&composite);
-              }
-          break;
-        }
-        case RepeatSpread:
-        {
-          MagickBooleanType
-            antialias;
-
-          double
-            repeat;
-
-          antialias=MagickFalse;
-          repeat=0.0;
-          if ((x != (ssize_t) ceil(gradient_vector->x1-0.5)) ||
-              (y != (ssize_t) ceil(gradient_vector->y1-0.5)))
-            {
-              offset=GetStopColorOffset(gradient,x,y);
-              if (gradient->type == LinearGradient)
-                {
-                  repeat=fmod(offset,length);
-                  if (repeat < 0.0)
-                    repeat=length-fmod(-repeat,length);
-                  else
-                    repeat=fmod(offset,length);
-                  antialias=(repeat < length) && ((repeat+1.0) > length) ?
-                    MagickTrue : MagickFalse;
-                                                                 offset=PerceptibleReciprocal(length)*repeat;
-                }
-              else
-                {
-                  repeat=fmod(offset,gradient->radius);
-                  if (repeat < 0.0)
-                    repeat=gradient->radius-fmod(-repeat,gradient->radius);
-                  else
-                    repeat=fmod(offset,gradient->radius);
-                  antialias=repeat+1.0 > gradient->radius ? MagickTrue :
-                    MagickFalse;
-                  offset=repeat/gradient->radius;
-                }
-            }
-          for (i=0; i < (ssize_t) gradient->number_stops; i++)
-            if (offset < gradient->stops[i].offset)
-              break;
-          if (i == 0)
-            composite=gradient->stops[0].color;
-          else
-            if (i == (ssize_t) gradient->number_stops)
-              composite=gradient->stops[gradient->number_stops-1].color;
-            else
-              {
-                j=i;
-                i--;
-                alpha=(offset-gradient->stops[i].offset)/
-                  (gradient->stops[j].offset-gradient->stops[i].offset);
-                if (antialias != MagickFalse)
-                  {
-                    if (gradient->type == LinearGradient)
-                      alpha=length-repeat;
-                    else
-                      alpha=gradient->radius-repeat;
-                    i=0;
-                    j=(ssize_t) gradient->number_stops-1L;
-                  }
-                CompositePixelInfoBlend(&gradient->stops[i].color,1.0-alpha,
-                  &gradient->stops[j].color,alpha,&composite);
-              }
-          break;
-        }
+        if (primitive_info[j].coordinates != 2)
+          {
+            status=MagickFalse;
+            break;
+          }
+        GetNextToken(q,&q,extent,token);
+        primitive_info[j].text=AcquireString(token);
+        break;
       }
-      CompositePixelInfoOver(&composite,composite.alpha,&pixel,pixel.alpha,
-        &pixel);
-      SetPixelViaPixelInfo(image,&pixel,q);
-      q+=GetPixelChannels(image);
     }
-    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
-      status=MagickFalse;
+    if (primitive_info == (PrimitiveInfo *) NULL)
+      break;
+    if (image->debug != MagickFalse)
+      (void) LogMagickEvent(DrawEvent,GetMagickModule(),"  %.*s",(int) (q-p),p);
+    if (status == MagickFalse)
+      break;
+    primitive_info[i].primitive=UndefinedPrimitive;
+    if (i == 0)
+      continue;
+    /*
+      Transform points.
+    */
+    for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
+    {
+      point=primitive_info[i].point;
+      primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
+        graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
+      primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
+        graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
+      point=primitive_info[i].point;
+      if (point.x < graphic_context[n]->bounds.x1)
+        graphic_context[n]->bounds.x1=point.x;
+      if (point.y < graphic_context[n]->bounds.y1)
+        graphic_context[n]->bounds.y1=point.y;
+      if (point.x > graphic_context[n]->bounds.x2)
+        graphic_context[n]->bounds.x2=point.x;
+      if (point.y > graphic_context[n]->bounds.y2)
+        graphic_context[n]->bounds.y2=point.y;
+      if (primitive_info[i].primitive == ImagePrimitive)
+        break;
+      if (i >= (ssize_t) number_points)
+        ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+    }
+    if (graphic_context[n]->render != MagickFalse)
+      status&=DrawPrimitive(image,graphic_context[n],primitive_info,
+        exception);
+    if (primitive_info->text != (char *) NULL)
+      primitive_info->text=(char *) RelinquishMagickMemory(
+        primitive_info->text);
+    proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
+      primitive_extent);
+    if (proceed == MagickFalse)
+      break;
+    if (status == 0)
+      break;
   }
-  image_view=DestroyCacheView(image_view);
-  return(status);
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
+  /*
+    Relinquish resources.
+  */
+  token=DestroyString(token);
+  if (primitive_info != (PrimitiveInfo *) NULL)
+    primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
+  primitive=DestroyString(primitive);
+  if (stops != (StopInfo *) NULL)
+    stops=(StopInfo *) RelinquishMagickMemory(stops);
+  for ( ; n >= 0; n--)
+    graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
+  graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
+  if (status == MagickFalse)
+    ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
+      keyword);
+  return(status != 0 ? MagickTrue : MagickFalse);
 }
 \f
 /*