% %
% %
% %
-% 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:
%
%
*/
-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(¤t);
- 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(¤t);
+ 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;
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
/*