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

Reply via email to