From: Cristy Date: Sun, 6 May 2018 12:32:23 +0000 (-0400) Subject: ... X-Git-Tag: 7.0.7-31~8 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=301b8fdf021216ec0f74caa321d0d465aba316b4;p=imagemagick ... --- diff --git a/MagickCore/draw.c b/MagickCore/draw.c index af8f59b13..9e98ca073 100644 --- a/MagickCore/draw.c +++ b/MagickCore/draw.c @@ -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(¤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); +} + +/* +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% % +% % +% 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; @@ -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); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -% 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); } /*