Пример #1
0
  void update(double time,
              uint32_t* out,
              const uint32_t* in1,
              const uint32_t* in2,
              const uint32_t* in3)
  {
    uint8_t *dst = reinterpret_cast<uint8_t*>(out);
    const uint8_t *src1 = reinterpret_cast<const uint8_t*>(in1);
    const uint8_t *src2 = reinterpret_cast<const uint8_t*>(in2);
    
    for (int i=0; i<size; ++i)
    {
      uint32_t tmp;
      uint8_t alpha_src1 = src1[3];
      uint8_t alpha_src2 = src2[3];
      uint8_t alpha_dst;
      uint8_t w1 = alpha_src2;
      uint8_t w2 = 0xff ^ alpha_src1; // w2 = 255 - alpha_1

      // compute destination alpha
      alpha_dst = dst[3] = w1;

       // compute destination values
      if (alpha_dst == 0)
        for (int b=0; b<3; ++b)
          dst[b] = 0;
      else
        for (int b=0; b<3; ++b)
          dst[b] = CLAMP0255( (uint32_t)( (uint32_t) (INT_MULT(src1[b], alpha_src1, tmp) * w1 + INT_MULT(src2[b], alpha_src2, tmp) * w2) / alpha_dst) );
      
      src1 += 4;
      src2 += 4;
      dst += 4;
    }
  }
Пример #2
0
/*
 * For each i in [0, HISTSIZE), hist[i] is the number of occurrences of the
 * value i. Return a value in [0, HISTSIZE) weighted heavily toward the
 * most frequent values in the histogram.
 *
 * Assuming that hist_max is the maximum number of occurrences for any
 * one value in the histogram, the weight given to each value i is
 *
 *         weight = (hist[i] / hist_max)^exponent
 *
 * (i.e. the normalized histogram frequency raised to some power)
 */
static inline guchar
weighted_average_value (gint hist[HISTSIZE], gfloat exponent)
{
  gint   i;
  gint   hist_max = 1;
  gint   exponent_int = 0;
  gfloat sum = 0.0;
  gfloat div = 1.0e-6;
  gint   value;

  for (i = 0; i < HISTSIZE; i++)
    hist_max = MAX (hist_max, hist[i]);

  if ((exponent - floor (exponent)) < 0.001 && exponent <= 255.0)
    exponent_int = (gint) exponent;

  for (i = 0; i < HISTSIZE; i++)
    {
      gfloat ratio = (gfloat) hist[i] / (gfloat) hist_max;
      gfloat weight;

      if (exponent_int)
        weight = fast_powf (ratio, exponent_int);
      else
        weight = pow (ratio, exponent);

      sum += weight * (gfloat) i;
      div += weight;
    }

  value = (gint) (sum / div);

  return (guchar) CLAMP0255 (value);
}
Пример #3
0
static void
graya_filter (gint   width,     /* I - Width of line in pixels */
              guchar *src,      /* I - Source line */
              guchar *dst,      /* O - Destination line */
              intneg *neg0,     /* I - Top negative coefficient line */
              intneg *neg1,     /* I - Middle negative coefficient line */
              intneg *neg2)     /* I - Bottom negative coefficient line */
{
  intpos pixel;         /* New pixel value */

  *dst++ = *src++;
  *dst++ = *src++;
  width -= 2;

  while (width > 0)
    {
      pixel = (pos_lut[*src++] - neg0[-2] - neg0[0] - neg0[2] -
               neg1[-2] - neg1[2] -
               neg2[-2] - neg2[0] - neg2[2]);
      pixel = (pixel + 4) >> 3;
      *dst++ = CLAMP0255 (pixel);

      *dst++ = *src++;
      neg0 += 2;
      neg1 += 2;
      neg2 += 2;
      width --;
    }

  *dst++ = *src++;
  *dst++ = *src++;
}
Пример #4
0
static gint
edge_detect (const guchar *data)
{
  gint ret;

  switch (evals.edgemode)
    {
    case SOBEL:
      ret = sobel (data);
      break;
    case PREWITT:
      ret = prewitt (data);
      break;
    case GRADIENT:
      ret = gradient (data);
      break;
    case ROBERTS:
      ret = roberts (data);
      break;
    case DIFFERENTIAL:
      ret = differential (data);
      break;
    case LAPLACE:
      ret = laplace (data);
      break;
    default:
      ret = -1;
      break;
    }

  return CLAMP0255 (ret);
}
Пример #5
0
  void updateLookUpTables(const uint32_t* in)
  {
    unsigned int size = width*height;
    
    // First pass : build histograms.
    
    // Reset histograms.
    memset(rhist, 0, 256*sizeof(unsigned int));
    memset(ghist, 0, 256*sizeof(unsigned int));
    memset(bhist, 0, 256*sizeof(unsigned int));

    // Update histograms.
    const unsigned char *in_ptr = (const unsigned char*) in;
    for (unsigned int i=0; i<size; ++i)
    {
      // update 'em
      rhist[*in_ptr++]++;
      ghist[*in_ptr++]++;
      bhist[*in_ptr++]++;
      in_ptr++; // skip alpha
    }

    // Second pass : update look-up tables.
    in_ptr = (const unsigned char*) in;

    // Cumulative intensities of histograms.
    unsigned int
      rcum = 0,
      gcum = 0,
      bcum = 0;
      
    for (int i=0; i<256; ++i)
    {
      // update cumulatives
      rcum += rhist[i];
      gcum += ghist[i];
      bcum += bhist[i];
      
      // update 'em
      rlut[i] = CLAMP0255( (rcum << 8) / size ); // = 256 * rcum / size
      glut[i] = CLAMP0255( (gcum << 8) / size ); // = 256 * gcum / size
      blut[i] = CLAMP0255( (bcum << 8) / size ); // = 256 * bcum / size
      
      in_ptr++; // skip alpha
    }

  }
Пример #6
0
static guchar*
rgb_to_hsl (GimpDrawable     *drawable,
            LICEffectChannel  effect_channel)
{
  guchar       *themap, data[4];
  gint          x, y;
  GimpRGB       color;
  GimpHSL       color_hsl;
  gdouble       val = 0.0;
  glong         maxc, index = 0;
  GimpPixelRgn  region;
  GRand        *gr;

  gr = g_rand_new ();

  maxc = drawable->width * drawable->height;

  gimp_pixel_rgn_init (&region, drawable, border_x, border_y,
                       border_w, border_h, FALSE, FALSE);

  themap = g_new (guchar, maxc);

  for (y = 0; y < region.h; y++)
    {
      for (x = 0; x < region.w; x++)
        {
          data[3] = 255;

          gimp_pixel_rgn_get_pixel (&region, data, x, y);
          gimp_rgba_set_uchar (&color, data[0], data[1], data[2], data[3]);
          gimp_rgb_to_hsl (&color, &color_hsl);

          switch (effect_channel)
            {
            case LIC_HUE:
              val = color_hsl.h * 255;
              break;
            case LIC_SATURATION:
              val = color_hsl.s * 255;
              break;
            case LIC_BRIGHTNESS:
              val = color_hsl.l * 255;
              break;
            }

          /* add some random to avoid unstructured areas. */
          val += g_rand_double_range (gr, -1.0, 1.0);

          themap[index++] = (guchar) CLAMP0255 (RINT (val));
        }
    }

  g_rand_free (gr);

  return themap;
}
Пример #7
0
void alpha_demultiply(unsigned char *src,
                      unsigned int n)
{
  float mult;
  while (n--)
  {
    mult = (src[IDX_RGBA_A] == 0 ? 255.0f : 255.0f / (float)src[IDX_RGBA_A]);
    for (int i=0; i<SIZE_RGB; ++i)
      src[i] = CLAMP0255( (unsigned int) ((float)src[i] * mult ) );
    src += SIZE_RGBA;
  }
}
Пример #8
0
static inline guchar
cm_mix_pixel (CmChannelType *ch,
              guchar         r,
              guchar         g,
              guchar         b,
              gdouble        norm)
{
  gdouble c = ch->red_gain * r + ch->green_gain * g + ch->blue_gain * b;

  c *= norm;

  return (guchar) CLAMP0255 (c);
}
Пример #9
0
    virtual void update(double time,
	                    uint32_t* out,
		                const uint32_t* in,
		                const uint32_t* in2,
		                const uint32_t* in3)
    {
        // Rebuild the lookup table in case the prarameters have changed.
        updateLUT();

        unsigned char *pixel = (unsigned char *) in;
        unsigned char *dest = (unsigned char *) out;

        if (fabs(m_sat-1) < 0.001) {
            // Calculating the saturation is expensive. So first check whether
            // we really need to do it.
            // Keeping the if/else outside of the loop gives a little speed gain.
            // Worth the duplicate code, as only 4 lines so far :)

            for (unsigned int i = 0; i < size; i++) {
                *dest++ = m_lutR[*pixel++];
                *dest++ = m_lutG[*pixel++];
                *dest++ = m_lutB[*pixel++];
                *dest++ = m_lutA[*pixel++];
            }
        } else {
            double luma;
            for (unsigned int i = 0; i < size; i++) {
                luma =   0.2126 * m_lutR[*(pixel+0)]
                       + 0.7152 * m_lutG[*(pixel+1)]
                       + 0.0722 * m_lutB[*(pixel+2)];
                *dest++ = CLAMP0255(luma + m_sat*(m_lutR[*pixel++]-luma));
                *dest++ = CLAMP0255(luma + m_sat*(m_lutG[*pixel++]-luma));
                *dest++ = CLAMP0255(luma + m_sat*(m_lutB[*pixel++]-luma));
                *dest++ = m_lutA[*pixel++];
            }
        }
    }
Пример #10
0
void HSLFilter::setSaturation(double val)
{
    val = CLAMP(val, -100.0, 100.0);
    int value;

    for (int i = 0; i < 65536; ++i)
    {
        value = lround( (i * (100.0 + val)) / 100.0 );
        d->stransfer16[i] = CLAMP065535(value);
    }

    for (int i = 0; i < 256; ++i)
    {
        value = lround( (i * (100.0 + val)) / 100.0 );
        d->stransfer[i]  = CLAMP0255(value);
    }
}
Пример #11
0
/*
 * For each i in [0, HISTSIZE), hist[i] is the number of occurrences of
 * pixels with intensity i. hist_rgb[][i] is the average color of those
 * pixels with intensity i, but with each channel multiplied by hist[i].
 * Write to dest a pixel whose color is a weighted average of all the
 * colors in hist_rgb[][], biased heavily toward those with the most
 * frequently-occurring intensities (as noted in hist[]).
 *
 * The weight formula is the same as in weighted_average_value().
 */
static inline void
weighted_average_color (gint    hist[HISTSIZE],
                        gint    hist_rgb[4][HISTSIZE],
                        gfloat  exponent,
                        guchar *dest,
                        gint    bpp)
{
  gint   i, b;
  gint   hist_max = 1;
  gint   exponent_int = 0;
  gfloat div = 1.0e-6;
  gfloat color[4] = { 0.0, 0.0, 0.0, 0.0 };

  for (i = 0; i < HISTSIZE; i++)
    hist_max = MAX (hist_max, hist[i]);

  if ((exponent - floor (exponent)) < 0.001 && exponent <= 255.0)
    exponent_int = (gint) exponent;

  for (i = 0; i < HISTSIZE; i++)
    {
      gfloat ratio = (gfloat) hist[i] / (gfloat) hist_max;
      gfloat weight;

      if (exponent_int)
        weight = fast_powf (ratio, exponent_int);
      else
        weight = pow (ratio, exponent);

      if (hist[i] > 0)
        for (b = 0; b < bpp; b++)
          color[b] += weight * (gfloat) hist_rgb[b][i] / (gfloat) hist[i];

      div += weight;
    }

  for (b = 0; b < bpp; b++)
    {
      gint c = (gint) (color[b] / div);

      dest[b] = (guchar) CLAMP0255 (c);
    }
}
Пример #12
0
static void
transfer_pixels (gdouble *src1,
                 gdouble *src2,
                 guchar  *dest,
                 gint     jump,
                 gint     width)
{
  gint    i;
  gdouble sum;

  for (i = 0; i < width; i++)
    {
      sum = src1[i] + src2[i];

      sum = CLAMP0255 (sum);

      *dest = (guchar) sum;
      dest += jump;
    }
}
Пример #13
0
/* This function chooses a gray value for the text color, based on
 * the average luminance of the text area of the splash image.
 */
static gboolean
splash_average_text_area (GimpSplash *splash,
                          GdkPixbuf  *pixbuf,
                          GdkColor   *color)
{
  const guchar *pixels;
  gint          rowstride;
  gint          channels;
  gint          luminance = 0;
  guint         sum[3]    = { 0, 0, 0 };
  GdkRectangle  image     = { 0, 0, 0, 0 };
  GdkRectangle  area      = { 0, 0, 0, 0 };

  g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
  g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8, FALSE);

  image.width  = gdk_pixbuf_get_width (pixbuf);
  image.height = gdk_pixbuf_get_height (pixbuf);
  rowstride    = gdk_pixbuf_get_rowstride (pixbuf);
  channels     = gdk_pixbuf_get_n_channels (pixbuf);
  pixels       = gdk_pixbuf_get_pixels (pixbuf);

  splash_position_layouts (splash, "Short text", "Somewhat longer text", &area);
  splash_position_layouts (splash, "", "", NULL);

  if (gdk_rectangle_intersect (&image, &area, &area))
    {
      const gint count = area.width * area.height;
      gint       x, y;

      pixels += area.x * channels;
      pixels += area.y * rowstride;

      for (y = 0; y < area.height; y++)
        {
          const guchar *src = pixels;

          for (x = 0; x < area.width; x++)
            {
              sum[0] += src[0];
              sum[1] += src[1];
              sum[2] += src[2];

              src += channels;
            }

          pixels += rowstride;
        }

      luminance = GIMP_RGB_LUMINANCE (sum[0] / count,
                                      sum[1] / count,
                                      sum[2] / count);

      luminance = CLAMP0255 (luminance > 127 ?
                             luminance - 223 : luminance + 223);

    }

  color->red = color->green = color->blue = (luminance << 8 | luminance);

  return gdk_colormap_alloc_color (gtk_widget_get_colormap (splash->area),
                                   color, FALSE, TRUE);
}
Пример #14
0
void BCGFilter::applyBCG(uchar* const bits, uint width, uint height, bool sixteenBits)
{
    if (!bits)
    {
        return;
    }

    uint size = width * height;
    int  progress;

    if (!sixteenBits)                    // 8 bits image.
    {
        uchar* data = bits;

        for (uint i = 0; runningFlag() && (i < size); ++i)
        {
            switch (d->settings.channel)
            {
                case BlueChannel:
                    data[0] = CLAMP0255(d->map[data[0]]);
                    break;

                case GreenChannel:
                    data[1] = CLAMP0255(d->map[data[1]]);
                    break;

                case RedChannel:
                    data[2] = CLAMP0255(d->map[data[2]]);
                    break;

                default:      // all channels
                    data[0] = CLAMP0255(d->map[data[0]]);
                    data[1] = CLAMP0255(d->map[data[1]]);
                    data[2] = CLAMP0255(d->map[data[2]]);
                    break;
            }

            data += 4;

            progress = (int)(((double)i * 100.0) / size);

            if (progress % 5 == 0)
            {
                postProgress(progress);
            }
        }
    }
    else                                        // 16 bits image.
    {
        ushort* data = reinterpret_cast<ushort*>(bits);

        for (uint i = 0; runningFlag() && (i < size); ++i)
        {
            switch (d->settings.channel)
            {
                case BlueChannel:
                    data[0] = CLAMP065535(d->map16[data[0]]);
                    break;

                case GreenChannel:
                    data[1] = CLAMP065535(d->map16[data[1]]);
                    break;

                case RedChannel:
                    data[2] = CLAMP065535(d->map16[data[2]]);
                    break;

                default:      // all channels
                    data[0] = CLAMP065535(d->map16[data[0]]);
                    data[1] = CLAMP065535(d->map16[data[1]]);
                    data[2] = CLAMP065535(d->map16[data[2]]);
                    break;
            }

            data += 4;

            progress = (int)(((double)i * 100.0) / size);

            if (progress % 5 == 0)
            {
                postProgress(progress);
            }
        }
    }

}
Пример #15
0
static void
compute_difference (GimpDrawable *drawable,
                    GimpDrawable *drawable1,
                    GimpDrawable *drawable2,
                    guchar       *maxval)
{
  GimpPixelRgn src1_rgn, src2_rgn, dest_rgn;
  gint         width, height;
  gint         bpp;
  gpointer     pr;
  gint         x, y, k;
  gint         x1, y1, x2, y2;
  gboolean     has_alpha;

  *maxval = 0;

  gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);

  width  = (x2 - x1);
  height = (y2 - y1);

  if (width < 1 || height < 1)
    return;

  bpp = drawable->bpp;
  has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);

  gimp_pixel_rgn_init (&src1_rgn,
                       drawable1, 0, 0, drawable1->width, drawable1->height,
                       FALSE, FALSE);
  gimp_pixel_rgn_init (&src2_rgn,
                       drawable2, 0, 0, drawable1->width, drawable1->height,
                       FALSE, FALSE);
  gimp_pixel_rgn_init (&dest_rgn,
                       drawable, 0, 0, drawable->width, drawable->height,
                       TRUE, TRUE);

  for (pr = gimp_pixel_rgns_register (3, &src1_rgn, &src2_rgn, &dest_rgn);
       pr != NULL;
       pr = gimp_pixel_rgns_process (pr))
    {
      guchar *src1 = src1_rgn.data;
      guchar *src2 = src2_rgn.data;
      guchar *dest = dest_rgn.data;
      gint    row  = src1_rgn.y - y1;

      for (y = 0; y < src1_rgn.h; y++, row++)
        {
          guchar *s1  = src1;
          guchar *s2  = src2;
          guchar *d   = dest;
          gint    col = src1_rgn.x - x1;

          for (x = 0; x < src1_rgn.w; x++, col++)
            {

              if (has_alpha)
                {
                  for (k = 0; k < bpp-1; k++)
                    {
                      d[k] = CLAMP0255 (s1[k] - s2[k]);
                      *maxval = MAX (d[k], *maxval);
                    }
                }
              else
                {
                  for (k = 0; k < bpp; k++)
                    {
                      d[k] = CLAMP0255 (s1[k] - s2[k]);
                      *maxval = MAX (d[k], *maxval);
                    }
                }

              s1 += bpp;
              s2 += bpp;
              d += bpp;
            }

          src1 += src1_rgn.rowstride;
          src2 += src2_rgn.rowstride;
          dest += dest_rgn.rowstride;
        }
    }
}
Пример #16
0
static void
curves_plot_curve (Curves *curves,
                   gint    channel,
                   gint    p1,
                   gint    p2,
                   gint    p3,
                   gint    p4)
{
  gint    i;
  gdouble x0, x3;
  gdouble y0, y1, y2, y3;
  gdouble dx, dy;
  gdouble y, t;
  gdouble slope;

  /* the outer control points for the bezier curve. */
  x0 = curves->points[channel][p2][0];
  y0 = curves->points[channel][p2][1];
  x3 = curves->points[channel][p3][0];
  y3 = curves->points[channel][p3][1];

  /*
   * the x values of the inner control points are fixed at
   * x1 = 1/3*x0 + 2/3*x3   and  x2 = 2/3*x0 + 1/3*x3
   * this ensures that the x values increase linearily with the
   * parameter t and enables us to skip the calculation of the x
   * values altogehter - just calculate y(t) evenly spaced.
   */

  dx = x3 - x0;
  dy = y3 - y0;

  g_return_if_fail (dx > 0);

  if (p1 == p2 && p3 == p4)
    {
      /* No information about the neighbors,
       * calculate y1 and y2 to get a straight line
       */
      y1 = y0 + dy / 3.0;
      y2 = y0 + dy * 2.0 / 3.0;
    }
  else if (p1 == p2 && p3 != p4)
    {
      /* only the right neighbor is available. Make the tangent at the
       * right endpoint parallel to the line between the left endpoint
       * and the right neighbor. Then point the tangent at the left towards
       * the control handle of the right tangent, to ensure that the curve
       * does not have an inflection point.
       */
      slope = (curves->points[channel][p4][1] - y0) /
              (curves->points[channel][p4][0] - x0);

      y2 = y3 - slope * dx / 3.0;
      y1 = y0 + (y2 - y0) / 2.0;
    }
  else if (p1 != p2 && p3 == p4)
    {
      /* see previous case */
      slope = (y3 - curves->points[channel][p1][1]) /
              (x3 - curves->points[channel][p1][0]);

      y1 = y0 + slope * dx / 3.0;
      y2 = y3 + (y1 - y3) / 2.0;
    }
  else /* (p1 != p2 && p3 != p4) */
    {
      /* Both neighbors are available. Make the tangents at the endpoints
       * parallel to the line between the opposite endpoint and the adjacent
       * neighbor.
       */
      slope = (y3 - curves->points[channel][p1][1]) /
              (x3 - curves->points[channel][p1][0]);

      y1 = y0 + slope * dx / 3.0;

      slope = (curves->points[channel][p4][1] - y0) /
              (curves->points[channel][p4][0] - x0);

      y2 = y3 - slope * dx / 3.0;
    }

  /*
   * finally calculate the y(t) values for the given bezier values. We can
   * use homogenously distributed values for t, since x(t) increases linearily.
   */
  for (i = 0; i <= dx; i++)
    {
      t = i / dx;
      y =     y0 * (1-t) * (1-t) * (1-t) +
          3 * y1 * (1-t) * (1-t) * t     +
          3 * y2 * (1-t) * t     * t     +
              y3 * t     * t     * t;

      curves->curve[channel][ROUND(x0) + i] = CLAMP0255 (ROUND (y));
    }
}
Пример #17
0
/**
 * jpeg_detect_original_settings:
 * @cinfo: a pointer to a JPEG decompressor info.
 * @image_ID: the image to which the parasite should be attached.
 *
 * Analyze the image being decompressed (@cinfo) and extract the
 * sampling factors, quantization tables and overall image quality.
 * Store this information in a parasite and attach it to @image_ID.
 *
 * This function must be called after jpeg_read_header() so that
 * @cinfo contains the quantization tables and the sampling factors
 * for each component.
 *
 * Return Value: TRUE if a parasite has been attached to @image_ID.
 */
gboolean
jpeg_detect_original_settings (struct jpeg_decompress_struct *cinfo,
                               gint32                         image_ID)
{
  guint         parasite_size;
  guchar       *parasite_data;
  GimpParasite *parasite;
  guchar       *dest;
  gint          quality;
  gint          num_quant_tables = 0;
  gint          t;
  gint          i;

  g_return_val_if_fail (cinfo != NULL, FALSE);
  if (cinfo->jpeg_color_space == JCS_UNKNOWN
      || cinfo->out_color_space == JCS_UNKNOWN)
    return FALSE;

  quality = jpeg_detect_quality (cinfo);
  /* no need to attach quantization tables if they are the ones from IJG */
  if (quality <= 0)
    {
      for (t = 0; t < 4; t++)
        if (cinfo->quant_tbl_ptrs[t])
          num_quant_tables++;
    }

  parasite_size = 4 + cinfo->num_components * 2 + num_quant_tables * 128;
  parasite_data = g_new (guchar, parasite_size);
  dest = parasite_data;

  *dest++ = CLAMP0255 (cinfo->jpeg_color_space);
  *dest++ = ABS (quality);
  *dest++ = CLAMP0255 (cinfo->num_components);
  *dest++ = num_quant_tables;

  for (i = 0; i < cinfo->num_components; i++)
    {
      *dest++ = CLAMP0255 (cinfo->comp_info[i].h_samp_factor);
      *dest++ = CLAMP0255 (cinfo->comp_info[i].v_samp_factor);
    }

  if (quality <= 0)
    {
      for (t = 0; t < 4; t++)
        if (cinfo->quant_tbl_ptrs[t])
          for (i = 0; i < DCTSIZE2; i++)
            {
              guint16 c = cinfo->quant_tbl_ptrs[t]->quantval[i];
              *dest++ = c / 256;
              *dest++ = c & 255;
            }
    }

  parasite = gimp_parasite_new ("jpeg-settings",
                                GIMP_PARASITE_PERSISTENT,
                                parasite_size,
                                parasite_data);
  g_free (parasite_data);
  gimp_image_parasite_attach (image_ID, parasite);
  gimp_parasite_free (parasite);
  return TRUE;
}
Пример #18
0
void
color_balance_create_lookup_tables (ColorBalance *cb)
{
  gdouble *cyan_red_transfer[3];
  gdouble *magenta_green_transfer[3];
  gdouble *yellow_blue_transfer[3];
  gint     i;
  gint32   r_n, g_n, b_n;

  g_return_if_fail (cb != NULL);

  if (! transfer_initialized)
    {
      color_balance_transfer_init ();
      transfer_initialized = TRUE;
    }

  /*  Set the transfer arrays  (for speed)  */
  cyan_red_transfer[GIMP_SHADOWS] =
    (cb->cyan_red[GIMP_SHADOWS] > 0) ? shadows_add : shadows_sub;
  cyan_red_transfer[GIMP_MIDTONES] =
    (cb->cyan_red[GIMP_MIDTONES] > 0) ? midtones_add : midtones_sub;
  cyan_red_transfer[GIMP_HIGHLIGHTS] =
    (cb->cyan_red[GIMP_HIGHLIGHTS] > 0) ? highlights_add : highlights_sub;

  magenta_green_transfer[GIMP_SHADOWS] =
    (cb->magenta_green[GIMP_SHADOWS] > 0) ? shadows_add : shadows_sub;
  magenta_green_transfer[GIMP_MIDTONES] =
    (cb->magenta_green[GIMP_MIDTONES] > 0) ? midtones_add : midtones_sub;
  magenta_green_transfer[GIMP_HIGHLIGHTS] =
    (cb->magenta_green[GIMP_HIGHLIGHTS] > 0) ? highlights_add : highlights_sub;
  yellow_blue_transfer[GIMP_SHADOWS] =
    (cb->yellow_blue[GIMP_SHADOWS] > 0) ? shadows_add : shadows_sub;
  yellow_blue_transfer[GIMP_MIDTONES] =
    (cb->yellow_blue[GIMP_MIDTONES] > 0) ? midtones_add : midtones_sub;
  yellow_blue_transfer[GIMP_HIGHLIGHTS] =
    (cb->yellow_blue[GIMP_HIGHLIGHTS] > 0) ? highlights_add : highlights_sub;

  for (i = 0; i < 256; i++)
    {
      r_n = i;
      g_n = i;
      b_n = i;

      r_n += cb->cyan_red[GIMP_SHADOWS] * cyan_red_transfer[GIMP_SHADOWS][r_n];
      r_n = CLAMP0255 (r_n);
      r_n += cb->cyan_red[GIMP_MIDTONES] * cyan_red_transfer[GIMP_MIDTONES][r_n];
      r_n = CLAMP0255 (r_n);
      r_n += cb->cyan_red[GIMP_HIGHLIGHTS] * cyan_red_transfer[GIMP_HIGHLIGHTS][r_n];
      r_n = CLAMP0255 (r_n);

      g_n += cb->magenta_green[GIMP_SHADOWS] * magenta_green_transfer[GIMP_SHADOWS][g_n];
      g_n = CLAMP0255 (g_n);
      g_n += cb->magenta_green[GIMP_MIDTONES] * magenta_green_transfer[GIMP_MIDTONES][g_n];
      g_n = CLAMP0255 (g_n);
      g_n += cb->magenta_green[GIMP_HIGHLIGHTS] * magenta_green_transfer[GIMP_HIGHLIGHTS][g_n];
      g_n = CLAMP0255 (g_n);

      b_n += cb->yellow_blue[GIMP_SHADOWS] * yellow_blue_transfer[GIMP_SHADOWS][b_n];
      b_n = CLAMP0255 (b_n);
      b_n += cb->yellow_blue[GIMP_MIDTONES] * yellow_blue_transfer[GIMP_MIDTONES][b_n];
      b_n = CLAMP0255 (b_n);
      b_n += cb->yellow_blue[GIMP_HIGHLIGHTS] * yellow_blue_transfer[GIMP_HIGHLIGHTS][b_n];
      b_n = CLAMP0255 (b_n);

      cb->r_lookup[i] = r_n;
      cb->g_lookup[i] = g_n;
      cb->b_lookup[i] = b_n;
    }
}