root/magick/gem.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Contrast
  2. ExpandAffine
  3. GenerateDifferentialNoise
  4. GenerateNoise
  5. GetOptimalKernelWidth1D
  6. GetOptimalKernelWidth2D
  7. GetOptimalKernelWidth
  8. HSLTransform
  9. HWBTransform
  10. Hull
  11. IdentityAffine
  12. Modulate
  13. TransformHSL
  14. TransformHWB

/*
% Copyright (C) 2003-2009 GraphicsMagick Group
% Copyright (C) 2002 ImageMagick Studio
%
% This program is covered by multiple licenses, which are described in
% Copyright.txt. You should have received a copy of Copyright.txt with this
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%                              GGGG  EEEEE  M   M                             %
%                             G      E      MM MM                             %
%                             G GG   EEE    M M M                             %
%                             G   G  E      M   M                             %
%                              GGGG  EEEEE  M   M                             %
%                                                                             %
%                                                                             %
%                    Graphic Gems - Graphic Support Methods                   %
%                                                                             %
%                                                                             %
%                               Software Design                               %
%                                 John Cristy                                 %
%                                 August 1996                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
%
*/

/*
  Include declarations.
*/
#include "magick/studio.h"
#include "magick/gem.h"
#include "magick/utility.h"

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   C o n s t r a s t                                                         %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method Contrast enhances the intensity differences between the lighter
%  and darker elements of the image.
%
%  The format of the Contrast method is:
%
%      void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue)
%
%  A description of each parameter follows:
%
%    o sign: A positive value enhances the contrast otherwise it is reduced.
%
%    o red, green, blue: A pointer to a pixel component of type Quantum.
%
%
*/
MagickExport void Contrast(const int sign,Quantum *red,Quantum *green,
  Quantum *blue)
{
  static const double
    alpha=0.5+MagickEpsilon;

  double
    brightness,
    hue,
    saturation;

  /*
    Enhance contrast: dark color become darker, light color become lighter.
  */
  assert(red != (Quantum *) NULL);
  assert(green != (Quantum *) NULL);
  assert(blue != (Quantum *) NULL);
  TransformHSL(*red,*green,*blue,&hue,&saturation,&brightness);
  brightness+=
    alpha*sign*(alpha*(sin(MagickPI*(brightness-alpha))+1.0)-brightness);
  if (brightness > 1.0)
    brightness=1.0;
  if (brightness < 0.0)
    brightness=0.0;
  HSLTransform(hue,saturation,brightness,red,green,blue);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   E x p a n d A f f i n e                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method ExpandAffine computes the affine's expansion factor, i.e. the
%  square root of the factor by which the affine transform affects area. In an
%  affine transform composed of scaling, rotation, shearing, and translation,
%  returns the amount of scaling.
%
%  The format of the ExpandAffine method is:
%
%      double ExpandAffine(const AffineMatrix *affine)
%
%  A description of each parameter follows:
%
%    o expansion: Method ExpandAffine returns the affine's expansion factor.
%
%    o affine: A pointer the the affine transform of type AffineMatrix.
%
%
*/
MagickExport double ExpandAffine(const AffineMatrix *affine)
{
  double
    expand;

  assert(affine != (const AffineMatrix *) NULL);
  expand=fabs(affine->sx*affine->sy)-fabs(affine->rx*affine->ry);
  if (fabs(expand) < MagickEpsilon)
    return(1.0);
  return(sqrt(fabs(expand)));
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   G e n e r a t e D i f f e r e n t i a l N o i s e                         %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method GenerateDifferentialNoise generates a differential floating-point
%  noise value which will produce in the final result when added to the
%  original pixel.  The floating point differential value is useful since
%  it allows scaling without loss of precision and avoids clipping.
%
%  The format of the GenerateDifferentialNoise method is:
%
%      double GenerateDifferentialNoise(const Quantum pixel,
%                                       const NoiseType noise_type,
%                                       MagickRandomKernel *kernel)
%
%  A description of each parameter follows:
%
%    o pixel: A structure of type Quantum.
%
%    o noise_type:  The type of noise: Uniform, gaussian,
%      multiplicative Gaussian, impulse, laplacian, or Poisson.
%
%    o kernel: Kernel for random number generator.
%
*/
#define NoiseEpsilon   1.0e-5
#define SigmaUniform   4.0
#define SigmaGaussian  4.0
#define SigmaImpulse   0.10
#define SigmaLaplacian 10.0
#define SigmaMultiplicativeGaussian  0.5
#define SigmaPoisson   0.05
#define TauGaussian    20.0

MagickExport double GenerateDifferentialNoise(const Quantum quantum_pixel,
                                              const NoiseType noise_type,
                                              MagickRandomKernel *kernel)
{
  double
    alpha,
    beta,
    pixel,
    sigma,
    value;

  pixel=(double) quantum_pixel;

#if QuantumDepth > 8
  pixel /= MaxRGBDouble/255.0;
#endif

  alpha=MagickRandomRealInlined(kernel);
  if (alpha == 0.0)
    alpha=1.0;
  switch (noise_type)
  {
    case UniformNoise:
    default:
    {
      value=SigmaUniform*(alpha-0.5);
      break;
    }
    case GaussianNoise:
    {
      double
        tau;

      beta=MagickRandomRealInlined(kernel);
      sigma=sqrt(-2.0*log(alpha))*cos(2.0*MagickPI*beta);
      tau=sqrt(-2.0*log(alpha))*sin(2.0*MagickPI*beta);
      value=sqrt((double) pixel)*SigmaGaussian*sigma+TauGaussian*tau;
      break;
    }
    case MultiplicativeGaussianNoise:
    {
      if (alpha <= NoiseEpsilon)
        sigma=255.0;
      else
        sigma=sqrt(-2.0*log(alpha));
      beta=MagickRandomRealInlined(kernel);
      value=pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*MagickPI*beta);
      break;
    }
    case ImpulseNoise:
    {
      if (alpha < (SigmaImpulse/2.0))
        value=-pixel;
       else
         if (alpha >= (1.0-(SigmaImpulse/2.0)))
           value=255.0-pixel;
         else
           value=0.0;
      break;
    }
    case LaplacianNoise:
    {
      if (alpha <= 0.5)
        {
          if (alpha <= NoiseEpsilon)
            value=-255.0;
          else
            value=SigmaLaplacian*log(2.0*alpha);
          break;
        }
      beta=1.0-alpha;
      if (beta <= (0.5*NoiseEpsilon))
        value=255.0;
      else
        value=-(SigmaLaplacian*log(2.0*beta));
      break;
    }
    case PoissonNoise:
    {
      double
        limit;

      register long
        i;

      limit=exp(-SigmaPoisson*(double) pixel);
      for (i=0; alpha > limit; i++)
      {
        beta=MagickRandomRealInlined(kernel);
        alpha=alpha*beta;
      }
      value=pixel-((double) i/SigmaPoisson);
      break;
    }
  }

#if QuantumDepth > 8
  value *= (MaxRGBFloat/255.0);
#endif

  return value;
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   G e n e r a t e N o i s e                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method GenerateNoise adds noise to a pixel.
%
%  The format of the GenerateNoise method is:
%
%      Quantum GenerateNoise(const Quantum pixel,const NoiseType noise_type)
%
%  A description of each parameter follows:
%
%    o pixel: A structure of type Quantum.
%
%    o noise_type:  The type of noise: Uniform, gaussian,
%      multiplicative Gaussian, impulse, laplacian, or Poisson.
%
%
*/
MagickExport Quantum GenerateNoise(const Quantum pixel,
                                   const NoiseType noise_type)
{
  double
    value;

  value=(double) pixel+GenerateDifferentialNoise(pixel,noise_type,
                                                 AcquireMagickRandomKernel());
  return (RoundDoubleToQuantum(value));
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   G e t O p t i m a l K e r n e l W i d t h                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method GetOptimalKernelWidth computes the optimal kernel radius for a
%  convolution filter.  Start with the minimum value of 3 pixels and walk out
%  until we drop below the threshold of one pixel numerical accuracy,
%
%  The format of the GetOptimalKernelWidth method is:
%
%      int GetOptimalKernelWidth(const double radius,const double sigma)
%
%  A description of each parameter follows:
%
%    o width: Method GetOptimalKernelWidth returns the optimal width of
%      a convolution kernel.
%
%    o radius: The radius of the Gaussian, in pixels, not counting the center
%      pixel.
%
%    o sigma: The standard deviation of the Gaussian, in pixels.
%
%
*/

MagickExport int GetOptimalKernelWidth1D(const double radius,const double sigma)
{
  double
    epsilon,
    normalize,
    value;

  long
    width;

  register long
    u;

  if (radius > 0.0)
    return((int) (2.0*ceil(radius)+1.0));
  epsilon=1.0/MaxRGBDouble;
  if (epsilon < MagickEpsilon)
    epsilon=MagickEpsilon;
  for (width=5; ;)
  {
    normalize=0.0;
    for (u=(-width/2); u <= (width/2); u++)
      normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma);
    u=width/2;
    value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize;
    if (value < epsilon)
      break;
    width+=2;
  }
  return(width-2);
}

MagickExport int GetOptimalKernelWidth2D(const double radius,const double sigma)
{
  double
    alpha,
    epsilon,
    normalize,
    value;

  long
    width;

  register long
    u,
    v;

  if (radius > 0.0)
    return((int) (2.0*ceil(radius)+1.0));
  epsilon=1.0/MaxRGBDouble;
  if (epsilon < MagickEpsilon)
    epsilon=MagickEpsilon;
  for (width=5; ;)
  {
    normalize=0.0;
    for (v=(-width/2); v <= (width/2); v++)
    {
      for (u=(-width/2); u <= (width/2); u++)
      {
        alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
        normalize+=alpha/(2.0*MagickPI*sigma*sigma);
      }
    }
    v=width/2;
    value=exp(-((double) v*v)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize;
    if (value < epsilon)
      break;
    width+=2;
  }
  return(width-2);
}

MagickExport int GetOptimalKernelWidth(const double radius,const double sigma)
{
  return(GetOptimalKernelWidth1D(radius,sigma));
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   H S L T r a n s f o r m                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method HSLTransform converts a floating point (hue, saturation,
%  luminosity) with range 0.0 to 1.0 to a (red, green, blue) triple
%  with range 0 to MaxRGB.
%
%  The format of the HSLTransformImage method is:
%
%      void HSLTransform(const double hue,const double saturation,
%        const double luminosity,Quantum *red,Quantum *green,Quantum *blue)
%
%  A description of each parameter follows:
%
%    o hue, saturation, luminosity: A double value representing a
%      component of the HSL color space.
%
%    o red, green, blue: A pointer to a pixel component of type Quantum.
%
%
*/
MagickExport void HSLTransform(const double hue,const double saturation,
  const double luminosity,Quantum *red,Quantum *green,Quantum *blue)
{
  /*
    Convert HSL to RGB colorspace.
  */
  assert(red != (Quantum *) NULL);
  assert(green != (Quantum *) NULL);
  assert(blue != (Quantum *) NULL);
  if (saturation == 0.0)
    {
      double l = MaxRGBDouble*luminosity;
      *red=*green=*blue= RoundDoubleToQuantum(l);
    }
  else
    {
      double
        b,
        g,
        r,
        v,
        x,
        y,
        z,
        hue_times_six,
        hue_fract,
        vsf;

      int
        sextant;

      v=(luminosity <= 0.5) ? (luminosity*(1.0+saturation)) :
        (luminosity+saturation-luminosity*saturation);

      hue_times_six=6.0*hue;
      sextant=(int) hue_times_six;
      hue_fract=hue_times_six-(double) sextant;

      y=luminosity+luminosity-v;
      vsf=(v-y)*hue_fract;
      x=y+vsf;
      z=v-vsf;
      
      switch (sextant)
        {
        case 0: r=v; g=x; b=y; break;
        case 1: r=z; g=v; b=y; break;
        case 2: r=y; g=v; b=x; break;
        case 3: r=y; g=z; b=v; break;
        case 4: r=x; g=y; b=v; break;
        case 5: r=v; g=y; b=z; break;
        default: r=v; g=x; b=y; break;
        }
      r *= MaxRGBDouble;
      *red=RoundDoubleToQuantum(r);
      g *= MaxRGBDouble;
      *green=RoundDoubleToQuantum(g);
      b *= MaxRGBDouble;
      *blue=RoundDoubleToQuantum(b);
    }
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   H W B T r a n s f o r m                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method HWBTransform converts a (hue, whiteness, blackness) to a
%  (red, green, blue) triple.
%
%  Algorithm derived from "HWB: A more intuitive hue-based color model."
%  Alvy Ray Smith and Eric Ray Lyons, Journal of Graphics Tools, Volume 1
%  Number 1, 1996.  http://www.acm.org/jgt/papers/SmithLyons96/
%
%  The format of the HWBTransformImage method is:
%
%      void HWBTransform(const double hue,const double whiteness,
%        const double blackness,Quantum *red,Quantum *green,Quantum *blue)
%
%  A description of each parameter follows:
%
%    o hue, whiteness, blackness: A double value representing a
%      component of the HWB color space.
%
%    o red, green, blue: A pointer to a pixel component of type Quantum.
%
%
*/
MagickExport void HWBTransform(const double hue,const double whiteness,
  const double blackness,Quantum *red,Quantum *green,Quantum *blue)
{
  double
    b,
    f,
    g,
    n,
    r,
    v;

  register unsigned int
    i;

  /*
    Convert HWB to RGB colorspace.
  */
  assert(red != (Quantum *) NULL);
  assert(green != (Quantum *) NULL);
  assert(blue != (Quantum *) NULL);
  v=1.0-blackness;
  if (hue == 0.0)
    {
      v *= MaxRGBDouble;
      *red=*green=*blue=RoundDoubleToQuantum(v);
      return;
    }
  i=(unsigned int) (6.0*hue);
  f=6.0*hue-i;
  if (i & 0x01)
    f=1.0-f;
  n=whiteness+f*(v-whiteness);  /* linear interpolation */
  switch (i)
  {
    default:
    case 6:
    case 0: r=v; g=n; b=whiteness; break;
    case 1: r=n; g=v; b=whiteness; break;
    case 2: r=whiteness; g=v; b=n; break;
    case 3: r=whiteness; g=n; b=v; break;
    case 4: r=n; g=whiteness; b=v; break;
    case 5: r=v; g=whiteness; b=n; break;
  }
  r *= MaxRGBDouble;
  g *= MaxRGBDouble;
  b *= MaxRGBDouble;
  *red=RoundDoubleToQuantum(r);
  *green=RoundDoubleToQuantum(g);
  *blue=RoundDoubleToQuantum(b);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   H u l l                                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method Hull implements the eight hull algorithm described in Applied
%  Optics, Vol. 24, No. 10, 15 May 1985, "Geometric filter for Speckle
%  Reduction", by Thomas R Crimmins.  Each pixel in the image is replaced by
%  one of its eight of its surrounding pixels using a polarity and negative
%  hull function.
%
%  The format of the Hull method is:
%
%      void Hull(const long x_offset,const long y_offset,
%        const unsigned long columns,const unsigned long rows,Quantum *f,
%        Quantum *g,const int polarity)
%
%  A description of each parameter follows:
%
%    o x_offset, y_offset: An integer value representing the offset of the
%      current pixel within the image.
%
%    o columns, rows: Specifies the number of rows and columns in the image.
%
%    o polarity: An integer value declaring the polarity (+,-).
%
%    o f, g: A pointer to an image pixel and one of it's neighbor.
%
%
*/
MagickExport void Hull(const long x_offset,const long y_offset,
  const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
  const int polarity)
{
  double
    v,
    y;

  register long
    x;

  register Quantum
    *p,
    *q,
    *r,
    *s;

  assert(f != (Quantum *) NULL);
  assert(g != (Quantum *) NULL);
  p=f+(columns+2);
  q=g+(columns+2);
  r=p+(y_offset*((long) columns+2)+x_offset);
  for (y=0; y < (long) rows; y++)
  {
    p++;
    q++;
    r++;
    if (polarity > 0)
      for (x=(long) columns; x > 0; x--)
      {
        v=(*p);
        if (*r >= (v+ScaleCharToQuantum(2)))
          v+=ScaleCharToQuantum(1);
        *q=(Quantum) v;
        p++;
        q++;
        r++;
      }
    else
      for (x=(long) columns; x > 0; x--)
      {
        v=(*p);
        if (*r <= (v-(long) ScaleCharToQuantum(2)))
          v-=(long) ScaleCharToQuantum(1);
        *q=(Quantum) v;
        p++;
        q++;
        r++;
      }
    p++;
    q++;
    r++;
  }
  p=f+(columns+2);
  q=g+(columns+2);
  r=q+(y_offset*((long) columns+2)+x_offset);
  s=q-(y_offset*((long) columns+2)+x_offset);
  for (y=0; y < (long) rows; y++)
  {
    p++;
    q++;
    r++;
    s++;
    if (polarity > 0)
      for (x=(long) columns; x > 0; x--)
      {
        v=(*q);
        if ((*s >= (v+ScaleCharToQuantum(2))) && (*r > v))
          v+=ScaleCharToQuantum(1);
        *p=(Quantum) v;
        p++;
        q++;
        r++;
        s++;
      }
    else
      for (x=(long) columns; x > 0; x--)
      {
        v=(*q);
        if ((*s <= (v-(long) ScaleCharToQuantum(2))) && (*r < v))
          v-=(long) ScaleCharToQuantum(1);
        *p=(Quantum) v;
        p++;
        q++;
        r++;
        s++;
      }
    p++;
    q++;
    r++;
    s++;
  }
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   I d e n t i t y A f f i n e                                               %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method IdentityAffine initializes the affine transform to the identity
%  matrix.
%
%  The format of the IdentityAffine method is:
%
%      IdentityAffine(AffineMatrix *affine)
%
%  A description of each parameter follows:
%
%    o affine: A pointer the the affine transform of type AffineMatrix.
%
%
*/
MagickExport void IdentityAffine(AffineMatrix *affine)
{
  assert(affine != (AffineMatrix *) NULL);
  (void) memset(affine,0,sizeof(AffineMatrix));
  affine->sx=1.0;
  affine->sy=1.0;
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   M o d u l a t e                                                           %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method Modulate modulates the hue, saturation, and brightness of an
%  image. Brightness and saturation are expressed as a ratio of the
%  existing value. Hue is expressed as a ratio of rotation from the current
%  position in that 1.0 results in the existing position, 0.5 results in a
%  counter-clockwise rotation of 90 degrees, 1.5 results in a clockwise
%  rotation of 90 degrees, and 0 and 2.0 obtain a 180 degree rotation.
%
%  The format of the Modulate method is:
%
%      void Modulate(const double percent_hue,const double percent_saturation,
%        const double percent_brightness,Quantum *red,Quantum *green,
%        Quantum *blue)
%
%  A description of each parameter follows:
%
%    o percent_hue, percent_saturation, percent_brightness: A double value
%      representing the percent change in a component of the HSL color space.
%
%    o red, green, blue: A pointer to a pixel component of type Quantum.
%
%
*/
MagickExport void Modulate(const double percent_hue,
  const double percent_saturation,const double percent_brightness,
  Quantum *red,Quantum *green,Quantum *blue)
{
  double
    brightness,
    hue,
    saturation;

  /*
    Increase or decrease color brightness, saturation, or hue.
  */
  assert(red != (Quantum *) NULL);
  assert(green != (Quantum *) NULL);
  assert(blue != (Quantum *) NULL);
  TransformHSL(*red,*green,*blue,&hue,&saturation,&brightness);
  brightness*=(0.01+MagickEpsilon)*percent_brightness;
  if (brightness > 1.0)
    brightness=1.0;
  saturation*=(0.01+MagickEpsilon)*percent_saturation;
  if (saturation > 1.0)
    saturation=1.0;

  hue += (percent_hue/200.0 - 0.5);
  while (hue < 0.0)
    hue += 1.0;
  while (hue > 1.0)
    hue -= 1.0;
  HSLTransform(hue,saturation,brightness,red,green,blue);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   T r a n s f o r m H S L                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method TransformHSL converts a (red, green, blue) to a (hue, saturation,
%  luminosity) triple with values in range 0.0 to 1.0.
%
%  The format of the TransformHSL method is:
%
%      void TransformHSL(const Quantum red,const Quantum green,
%        const Quantum blue,double *hue,double *saturation,double *luminosity)
%
%  A description of each parameter follows:
%
%    o red, green, blue: A Quantum value representing the red, green, and
%      blue component of a pixel..
%
%    o hue, saturation, luminosity: A pointer to a double value representing a
%      component of the HSL color space.
%
%
*/
MagickExport void TransformHSL(const Quantum red,const Quantum green,
  const Quantum blue,double *hue_result,double *saturation_result,double *luminosity_result)
{
  double
    hue,
    saturation,
    luminosity,
    b,
    delta,
    g,
    max,
    min,
    r;

  /*
    Convert RGB to HSL colorspace.
  */
  assert(hue_result != (double *) NULL);
  assert(saturation_result != (double *) NULL);
  assert(luminosity_result != (double *) NULL);

  r=(double) red/MaxRGBDouble;
  g=(double) green/MaxRGBDouble;
  b=(double) blue/MaxRGBDouble;
  max=Max(r,Max(g,b));
  min=Min(r,Min(g,b));
  hue=0.0;
  saturation=0.0;
  luminosity=(min+max)/2.0;
  delta=max-min;
  if (delta != 0.0)
    {
      saturation=delta/((luminosity <= 0.5) ? (min+max) : (2.0-max-min));
      if (r == max)
        hue=(g == min ? 5.0+(max-b)/delta : 1.0-(max-g)/delta);
      else
        if (g == max)
          hue=(b == min ? 1.0+(max-r)/delta : 3.0-(max-b)/delta);
        else
          hue=(r == min ? 3.0+(max-g)/delta : 5.0-(max-r)/delta);
      hue/=6.0;
    }

  *hue_result=ConstrainToRange(0.0,1.0,hue);
  *saturation_result=ConstrainToRange(0.0,1.0,saturation);
  *luminosity_result=ConstrainToRange(0.0,1.0,luminosity);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   T r a n s f o r m H W B                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method TransformHWB converts a (red, green, blue) to a
%  (hue, whiteness, blackness) triple.
%
%  Algorithm derived from "HWB: A more intuitive hue-based color model."
%  Alvy Ray Smith and Eric Ray Lyons, Journal of Graphics Tools, Volume 1
%  Number 1, 1996.  http://www.acm.org/jgt/papers/SmithLyons96/
%
%  The format of the TransformHWB method is:
%
%      void TransformHWB(const Quantum red,const Quantum green,
%        const Quantum blue,double *hue,double *whiteness,double *blackness)
%
%  A description of each parameter follows:
%
%    o red, green, blue: A Quantum value representing the red, green, and
%      blue component of a pixel.
%
%    o hue, whiteness, blackness: A pointer to a double value representing a
%      component of the HWB color space.
%
%
*/
MagickExport void TransformHWB(const Quantum red,const Quantum green, const Quantum blue,
                               double *hue,double *whiteness,double *blackness)
{
  double
    f,
    v,
    w;

  register long
    i;

  /*
    Convert RGB to HWB colorspace.
  */
  assert(hue != (double *) NULL);
  assert(whiteness != (double *) NULL);
  assert(blackness != (double *) NULL);
  w=(double) Min(red,Min(green,blue));
  v=(double) Max(red,Max(green,blue));
  *blackness=((double) MaxRGBDouble-v)/MaxRGBDouble;
  if (v == w)
    {
      *hue=0.0;
      *whiteness=1.0-(*blackness);
    }
  else
    {
      f=(red == w) ? (double) green-blue :
        ((green == w) ? (double) blue-red :
         (double) red-green);
      i=(red == w) ? 3 : ((green == w) ? 5 : 1);
      *hue=((double) i-f/(v-w))/6.0;
      *whiteness=((double) w/MaxRGBDouble);
    }
}

/* [<][>][^][v][top][bottom][index][help] */