Hi, I don't know if you remember, I sent code related to linear gradient in january 2006. In fact I did that code first in OCaml, and did both the linear and the radial gradients (which I posted on the list), but only rewrote the linear one to C. I've just seen yesterday that you included that C code in IM release 6.3.1-0, so I have just made the rewrite of the radial gradial to C too. Here is the code below, which is a modification against IM 6.4.4-1, I've also made a simple command line program to test it, see it below too:
======================== (License Notice) ======================== Copyright 2008 Florent Monnier Licensed under the ImageMagick License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.imagemagick.org/script/license.php Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ======================== (part of draw.h) ======================== /* PointInfo moved to be available for GradientInfo */ /* radius and center members added to GradientInfo for radial gradients */ typedef struct _PointInfo { double x, y; } PointInfo; typedef enum { UndefinedSpread, PadSpread, ReflectSpread, RepeatSpread } SpreadMethod; typedef struct _StopInfo { MagickPixelPacket color; MagickRealType offset; } StopInfo; typedef struct _GradientInfo { GradientType type; RectangleInfo bounding_box; SegmentInfo gradient_vector; PointInfo center; MagickRealType radius; StopInfo *stops; unsigned long number_stops; SpreadMethod spread; MagickBooleanType debug; unsigned long signature; } GradientInfo; ======================== (part of draw.c) ======================== static inline MagickRealType GetStopColorOffset( const GradientInfo *gradient,const long x,const long y) { switch (gradient->type) { case UndefinedGradient: case LinearGradient: { MagickRealType 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=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma); scale=p.x*q.x+p.y*q.y; offset=gamma*scale*length; return(offset); } case RadialGradient: { MagickRealType length, offset; PointInfo v; v.x=(double) x-gradient->center.x; v.y=(double) y-gradient->center.y; length=sqrt(v.x*v.x+v.y*v.y); if (gradient->spread == RepeatSpread) return(length); offset=length/gradient->radius; return(offset); } } } MagickExport MagickBooleanType DrawGradientImage(Image *image, const DrawInfo *draw_info) { const GradientInfo *gradient; long y; MagickPixelPacket zero; MagickRealType length; PointInfo point; RectangleInfo bounding_box; ViewInfo **image_view; volatile MagickBooleanType status; /* Draw linear or radial gradient on image. */ assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(draw_info != (const DrawInfo *) NULL); gradient=(&draw_info->gradient); point.x=gradient->gradient_vector.x2-gradient->gradient_vector.x1; point.y=gradient->gradient_vector.y2-gradient->gradient_vector.y1; length=sqrt(point.x*point.x+point.y*point.y); bounding_box=gradient->bounding_box; status=MagickTrue; GetMagickPixelPacket(image,&zero); image_view=AcquireCacheViewThreadSet(image); #pragma omp parallel for schedule(static,64) for (y=bounding_box.y; y < (long) bounding_box.height; y++) { IndexPacket *indexes; long j; MagickPixelPacket composite, pixel; MagickRealType alpha, beta, offset; register long i, id, x; register PixelPacket *q; if (status == MagickFalse) continue; id=GetCacheViewThreadId(); q=GetCacheViewPixels(image_view[id],0,y,image->columns,1); if (q == (PixelPacket *) NULL) { status=MagickFalse; continue; } pixel=zero; composite=zero; indexes=GetCacheViewIndexes(image_view[id]); for (x=bounding_box.x; x < (long) bounding_box.width; x++) { SetMagickPixelPacket(image,q,indexes+x,&pixel); offset=0.0; switch (gradient->spread) { case UndefinedSpread: case PadSpread: { if ((x != (long) (gradient->gradient_vector.x1+0.5)) || (y != (long) (gradient->gradient_vector.y1+0.5))) offset=GetStopColorOffset(gradient,x,y); if (gradient->type == LinearGradient) offset/=length; for (i=0; i < (long) 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 == (long) gradient->number_stops)) composite=gradient->stops[gradient->number_stops-1].color; else { j=i; i--; beta=(offset-gradient->stops[i].offset)/ (gradient->stops[j].offset-gradient->stops[i].offset); alpha=1.0-beta; composite.red=((alpha*gradient->stops[i].color.red)+ (beta*gradient->stops[j].color.red)); composite.green=((alpha*gradient->stops[i].color.green)+ (beta*gradient->stops[j].color.green)); composite.blue=((alpha*gradient->stops[i].color.blue)+ (beta*gradient->stops[j].color.blue)); composite.opacity=((alpha*gradient->stops[i].color.opacity)+ (beta*gradient->stops[j].color.opacity)); } break; } case ReflectSpread: { if ((x != (long) (gradient->gradient_vector.x1+0.5)) || (y != (long) (gradient->gradient_vector.y1+0.5))) offset=GetStopColorOffset(gradient,x,y); if (gradient->type == LinearGradient) offset/=length; if (offset < 0.0) offset=(-offset); if ((long) fmod(offset,2.0) == 0) offset=fmod(offset,1.0); else offset=1.0-fmod(offset,1.0); for (i=0; i < (long) gradient->number_stops; i++) if (offset < gradient->stops[i].offset) break; if (i == 0) composite=gradient->stops[0].color; else if (i == (long) gradient->number_stops) composite=gradient->stops[gradient->number_stops-1].color; else { j=i; i--; beta=(offset-gradient->stops[i].offset)/ (gradient->stops[j].offset-gradient->stops[i].offset); alpha=1.0-beta; composite.red=((alpha*gradient->stops[i].color.red)+ (beta*gradient->stops[j].color.red)); composite.green=((alpha*gradient->stops[i].color.green)+ (beta*gradient->stops[j].color.green)); composite.blue=((alpha*gradient->stops[i].color.blue)+ (beta*gradient->stops[j].color.blue)); composite.opacity=((alpha*gradient->stops[i].color.opacity)+ (beta*gradient->stops[j].color.opacity)); } break; } case RepeatSpread: { MagickBooleanType antialias; MagickRealType repeat; antialias=MagickFalse; repeat=0.0; if ((x != (long) (gradient->gradient_vector.x1+0.5)) || (y != (long) (gradient->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=repeat/length; } 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 < (long) gradient->number_stops; i++) if (offset < gradient->stops[i].offset) break; if (i == 0) composite=gradient->stops[0].color; else if (i == (long) gradient->number_stops) composite=gradient->stops[gradient->number_stops-1].color; else { j=i; i--; beta=(offset-gradient->stops[i].offset)/ (gradient->stops[j].offset-gradient->stops[i].offset); if (antialias != MagickFalse) { if (gradient->type == LinearGradient) beta=length-repeat; else beta=gradient->radius-repeat; i=0; j=(long) gradient->number_stops-1L; } alpha=1.0-beta; composite.red=((alpha*gradient->stops[i].color.red)+ (beta*gradient->stops[j].color.red)); composite.green=((alpha*gradient->stops[i].color.green)+ (beta*gradient->stops[j].color.green)); composite.blue=((alpha*gradient->stops[i].color.blue)+ (beta*gradient->stops[j].color.blue)); composite.opacity=((alpha*gradient->stops[i].color.opacity)+ (beta*gradient->stops[j].color.opacity)); } break; } } MagickPixelCompositeOver(&composite,composite.opacity,&pixel, pixel.opacity,&pixel); SetPixelPacket(image,&pixel,q,indexes+x); q++; } if (SyncCacheView(image_view[id]) == MagickFalse) status=MagickFalse; } image_view=DestroyCacheViewThreadSet(image_view); return(status); } ======================== (test.c) ======================== /* Usage: test (-linear|-radial) (-pad|-repeat|-reflect) gcc \ `MagickCore-config --cflags` \ -o test test.c \ `Magick-config --ldflags --libs` */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <magick/MagickCore.h> int main(int argc, char **argv) { ExceptionInfo *exception; Image *img; ImageInfo *image_info; DrawInfo draw_info; StopInfo stops[3]; long img_width, img_height; img_width = 200; img_height = 140; MagickCoreGenesis(*argv, MagickTrue); exception = AcquireExceptionInfo(); image_info = CloneImageInfo((ImageInfo *) NULL); { char str_buffer[MaxTextExtent]; int str_len; /* Give image size */ str_len = snprintf( str_buffer, MaxTextExtent, "%ldx%ld", img_width, img_height ); (void) CloneString(&image_info->size, str_buffer); /* Give image color */ str_len = snprintf( str_buffer, MaxTextExtent, "xc:%s", "#FFFFFF" ); strncpy( image_info->filename, str_buffer, str_len ); } img = ReadImage(image_info, exception); if (exception->severity != UndefinedException) CatchException(exception); if (img == (Image *) NULL) exit(1); /* Fill parameters */ stops[0].color.red = MaxMap; stops[0].color.green = 0; stops[0].color.blue = 0; stops[0].color.opacity = 0; stops[0].offset = 0.0; stops[1].color.red = 0; stops[1].color.green = MaxMap; stops[1].color.blue = 0; stops[1].color.opacity = 0; stops[1].offset = 0.5; stops[2].color.red = 0; stops[2].color.green = 0; stops[2].color.blue = MaxMap; stops[2].color.opacity = 0; stops[2].offset = 1.0; draw_info.gradient.stops = stops; draw_info.gradient.number_stops = sizeof(stops) / sizeof(StopInfo); if (argc < 3) { (void) fprintf(stderr,"Usage: %s (-linear|-radial) " "(-pad|-repeat|-reflect)\n",argv[0]); exit(0); } if (strcmp(argv[1],"-linear")==0) { draw_info.gradient.type = LinearGradient; } else if (strcmp(argv[1],"-radial")==0) { draw_info.gradient.type = RadialGradient; } else { (void) fprintf(stderr,"Usage: %s (-linear|-radial) " "(-pad|-repeat|-reflect)\n",argv[0]); exit(0); } if (strcmp(argv[2],"-pad")==0) { draw_info.gradient.spread = PadSpread; } else if (strcmp(argv[2],"-repeat")==0) { draw_info.gradient.spread = RepeatSpread; } else if (strcmp(argv[2],"-reflect")==0) { draw_info.gradient.spread = ReflectSpread; } else { (void) fprintf(stderr,"Usage: %s (-linear|-radial) " "(-pad|-repeat|-reflect)\n",argv[0]); exit(0); } if (draw_info.gradient.type == LinearGradient) { draw_info.gradient.gradient_vector.x1 = 20.0; draw_info.gradient.gradient_vector.y1 = 20.0; draw_info.gradient.gradient_vector.x2 = 80.0; draw_info.gradient.gradient_vector.y2 = 40.0; } else { draw_info.gradient.center.x = 60.0; draw_info.gradient.center.y = 60.0; draw_info.gradient.radius = 80.0; } draw_info.gradient.bounding_box.x = 0; draw_info.gradient.bounding_box.y = 0; draw_info.gradient.bounding_box.width = img_width; draw_info.gradient.bounding_box.height = img_height; DrawGradientImage(img, &draw_info); /* Give the result back */ if (argc >= 4) { (void) strcpy(img->filename, argv[3]); WriteImage(image_info, img); } else { DisplayImages(image_info, img); } /* Destroy the image and exit */ img = DestroyImage(img); image_info = DestroyImageInfo(image_info); exception = DestroyExceptionInfo(exception); MagickCoreTerminus(); return(0); } ======================== (EOF) ======================== -- With Regards Florent _______________________________________________ Magick-developers mailing list [email protected] http://studio.imagemagick.org/mailman/listinfo/magick-developers
