[magick-developers] radial gradient
Florent Monnier
monnier.florent at gmail.com
Wed Oct 1 05:32:54 PDT 2008
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
More information about the Magick-developers
mailing list