Exemplo n.º 1
0
static void bilateral_prepare (GeglOperation *operation)
{
  gegl_operation_set_format (operation, "input",  babl_format ("RGBA float"));
  gegl_operation_set_format (operation, "output", babl_format ("RGBA float"));
}
Exemplo n.º 2
0
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
         const GeglRectangle *result,
         gint                 level)
{
  GeglOperationAreaFilter *area = GEGL_OPERATION_AREA_FILTER (operation);
  GeglProperties          *o    = GEGL_PROPERTIES (operation);

  GeglBuffer *dest, *dest_tmp;

  GeglRectangle  working_region;
  GeglRectangle *whole_region;

  GeglBufferIterator *iter;

  whole_region = gegl_operation_source_get_bounding_box (operation, "input");

  working_region.x      = result->x - area->left;
  working_region.width  = result->width + area->left + area->right;
  working_region.y      = result->y - area->top;
  working_region.height = result->height + area->top + area->bottom;

  gegl_rectangle_intersect (&working_region, &working_region, whole_region);

  dest_tmp = gegl_buffer_new (&working_region, babl_format ("Y' float"));

  iter = gegl_buffer_iterator_new (dest_tmp, &working_region, 0, babl_format ("Y' float"),
                                   GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);

  gegl_buffer_iterator_add (iter, input, &working_region, 0, babl_format ("Y' float"),
                            GEGL_ACCESS_READ, GEGL_ABYSS_NONE);

  while (gegl_buffer_iterator_next (iter))
    {
      gint    i;
      gfloat *data_out = iter->data[0];
      gfloat *data_in  = iter->data[1];

      for (i = 0; i < iter->length; i++)
        {
          /* compute sigmoidal transfer */
          gfloat val = *data_in;
          val = 1.0 / (1.0 + exp (-(SIGMOIDAL_BASE + (o->sharpness * SIGMOIDAL_RANGE)) * (val - 0.5)));
          val = val * o->brightness;
          *data_out = CLAMP (val, 0.0, 1.0);

          data_out +=1;
          data_in  +=1;
        }
    }

  dest = grey_blur_buffer (dest_tmp, o->glow_radius, result);

  iter = gegl_buffer_iterator_new (output, result, 0, babl_format ("RGBA float"),
                                   GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);

  gegl_buffer_iterator_add (iter, input, result, 0, babl_format ("RGBA float"),
                            GEGL_ACCESS_READ, GEGL_ABYSS_NONE);

  gegl_buffer_iterator_add (iter, dest, result, 0, babl_format ("Y' float"),
                            GEGL_ACCESS_READ, GEGL_ABYSS_NONE);

  while (gegl_buffer_iterator_next (iter))
    {
      gint    i;
      gfloat *data_out  = iter->data[0];
      gfloat *data_in   = iter->data[1];
      gfloat *data_blur = iter->data[2];

      for (i = 0; i < iter->length; i++)
        {
          gint c;
          for (c = 0; c < 3; c++)
            {
              gfloat tmp = (1.0 - data_in[c]) * (1.0 - *data_blur);
              data_out[c] = CLAMP (1.0 - tmp, 0.0, 1.0);
            }

          data_out[3] = data_in[3];

          data_out += 4;
          data_in  += 4;
          data_blur+= 1;
        }
    }

  g_object_unref (dest);
  g_object_unref (dest_tmp);

  return TRUE;
}
Exemplo n.º 3
0
static void
gimp_mypaint_surface_get_color (MyPaintSurface *base_surface,
                                float           x,
                                float           y,
                                float           radius,
                                float          *color_r,
                                float          *color_g,
                                float          *color_b,
                                float          *color_a)
{
  GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
  GeglRectangle dabRect;

  if (radius < 1.0f)
    radius = 1.0f;

  dabRect = calculate_dab_roi (x, y, radius);

  *color_r = 0.0f;
  *color_g = 0.0f;
  *color_b = 0.0f;
  *color_a = 0.0f;

  if (dabRect.width > 0 || dabRect.height > 0)
  {
    const float one_over_radius2 = 1.0f / (radius * radius);
    float sum_weight = 0.0f;
    float sum_r = 0.0f;
    float sum_g = 0.0f;
    float sum_b = 0.0f;
    float sum_a = 0.0f;

     /* Read in clamp mode to avoid transparency bleeding in at the edges */
    GeglBufferIterator *iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0,
                                                         babl_format ("R'aG'aB'aA float"),
                                                         GEGL_BUFFER_READ,
                                                         GEGL_ABYSS_CLAMP);
    if (surface->paint_mask)
      {
        GeglRectangle mask_roi = dabRect;
        mask_roi.x -= surface->paint_mask_x;
        mask_roi.y -= surface->paint_mask_y;
        gegl_buffer_iterator_add (iter, surface->paint_mask, &mask_roi, 0,
                                  babl_format ("Y float"),
                                  GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
      }

    while (gegl_buffer_iterator_next (iter))
      {
        float *pixel = (float *)iter->data[0];
        float *mask;
        int iy, ix;

        if (surface->paint_mask)
          mask = iter->data[1];
        else
          mask = NULL;

        for (iy = iter->roi[0].y; iy < iter->roi[0].y + iter->roi[0].height; iy++)
          {
            float yy = (iy + 0.5f - y);
            for (ix = iter->roi[0].x; ix < iter->roi[0].x +  iter->roi[0].width; ix++)
              {
                /* pixel_weight == a standard dab with hardness = 0.5, aspect_ratio = 1.0, and angle = 0.0 */
                float xx = (ix + 0.5f - x);
                float rr = (yy * yy + xx * xx) * one_over_radius2;
                float pixel_weight = 0.0f;
                if (rr <= 1.0f)
                  pixel_weight = 1.0f - rr;
                if (mask)
                  pixel_weight *= *mask;

                sum_r += pixel_weight * pixel[RED];
                sum_g += pixel_weight * pixel[GREEN];
                sum_b += pixel_weight * pixel[BLUE];
                sum_a += pixel_weight * pixel[ALPHA];
                sum_weight += pixel_weight;

                pixel += 4;
                if (mask)
                  mask += 1;
              }
          }
      }

    if (sum_a > 0.0f && sum_weight > 0.0f)
      {
        sum_r /= sum_weight;
        sum_g /= sum_weight;
        sum_b /= sum_weight;
        sum_a /= sum_weight;

        sum_r /= sum_a;
        sum_g /= sum_a;
        sum_b /= sum_a;

        /* FIXME: Clamping is wrong because GEGL allows alpha > 1, this should probably re-multipy things */
        *color_r = CLAMP(sum_r, 0.0f, 1.0f);
        *color_g = CLAMP(sum_g, 0.0f, 1.0f);
        *color_b = CLAMP(sum_b, 0.0f, 1.0f);
        *color_a = CLAMP(sum_a, 0.0f, 1.0f);
      }
  }

}
Exemplo n.º 4
0
static void
prepare (GeglOperation *operation)
{
  gegl_operation_set_format (operation, "output", babl_format ("R'G'B'A float"));
}
Exemplo n.º 5
0
static void
prepare (GeglOperation *operation)
{
  gegl_operation_set_format (operation, "input", babl_format ("RGBA u16"));
  gegl_operation_set_format (operation, "output", babl_format ("RGBA u16"));
}
Exemplo n.º 6
0
Arquivo: waves.c Projeto: jonnor/gegl
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
         const GeglRectangle *result,
         gint                 level)
{
  GeglProperties     *o       = GEGL_PROPERTIES (operation);
  GeglSampler        *sampler = gegl_buffer_sampler_new_at_level (input,
                                                         babl_format ("RGBA float"),
                                                         o->sampler_type,
                                                         level);
  GeglRectangle      *in_extent = gegl_operation_source_get_bounding_box (operation, "input");
  GeglBufferIterator *iter;

  GeglAbyssPolicy abyss = o->clamp ? GEGL_ABYSS_CLAMP : GEGL_ABYSS_NONE;

  gdouble px_x = gegl_coordinate_relative_to_pixel (o->x, in_extent->width);
  gdouble px_y = gegl_coordinate_relative_to_pixel (o->y, in_extent->height);

  gdouble scalex;
  gdouble scaley;

  if (o->aspect > 1.0)
    {
      scalex = 1.0;
      scaley = o->aspect;
    }
  else if (o->aspect < 1.0)
    {
      scalex = 1.0 / o->aspect;
      scaley = 1.0;
    }
  else
    {
      scalex = 1.0;
      scaley = 1.0;
    }

  iter = gegl_buffer_iterator_new (output, result, 0, babl_format ("RGBA float"),
                                   GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);

  while (gegl_buffer_iterator_next (iter))
    {
      gint x = result->x;
      gint y = result->y;
      gfloat *out_pixel = iter->data[0];

      for (y = iter->roi[0].y; y < iter->roi[0].y + iter->roi[0].height; ++y)
        for (x = iter->roi[0].x; x < iter->roi[0].x + iter->roi[0].width; ++x)
          {
            gdouble radius;
            gdouble shift;
            gdouble dx;
            gdouble dy;
            gdouble ux;
            gdouble uy;

            dx = (x - px_x) * scalex;
            dy = (y - px_y) * scaley;

            if (!dx && !dy)
              radius = 0.000001;
            else
              radius = sqrt (dx * dx + dy * dy);

            shift = o->amplitude * sin (2.0 * G_PI * radius / o->period +
                                        2.0 * G_PI * o->phi);

            ux = dx / radius;
            uy = dy / radius;

            gegl_sampler_get (sampler,
                              x + (shift + ux) / scalex,
                              y + (shift + uy) / scaley,
                              NULL,
                              out_pixel,
                              abyss);

            out_pixel += 4;
          }
    }

  g_object_unref (sampler);

  return TRUE;
}
Exemplo n.º 7
0
static gboolean
cl_process (GeglOperation       *operation,
            GeglBuffer          *input,
            GeglBuffer          *output,
            const GeglRectangle *roi)
{
  const Babl *in_format  = babl_format ("RaGaBaA float");
  const Babl *out_format = babl_format ("RaGaBaA float");
  gint   err;
  gfloat bg_color[4];
  gint   norm;

  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
  GeglProperties *o = GEGL_PROPERTIES (operation);
  GeglRectangle *image_extent;

  GeglBufferClIterator *i = gegl_buffer_cl_iterator_new   (output,
                                                           roi,
                                                           out_format,
                                                           GEGL_CL_BUFFER_WRITE);

  gint read = gegl_buffer_cl_iterator_add_2 (i,
                                             input,
                                             roi,
                                             in_format,
                                             GEGL_CL_BUFFER_READ,
                                             op_area->left,
                                             op_area->right,
                                             op_area->top,
                                             op_area->bottom,
                                             GEGL_ABYSS_CLAMP);

  gint aux = gegl_buffer_cl_iterator_add_aux (i,
                                              roi,
                                              in_format,
                                              op_area->left,
                                              op_area->right,
                                              op_area->top,
                                              op_area->bottom);


  gegl_color_get_pixel (o->background, babl_format ("RaGaBaA float"), bg_color);

  norm = 0;
  norm = o->norm == GEGL_PIXELIZE_NORM_EUCLIDEAN ? 1 : norm;
  norm = o->norm == GEGL_PIXELIZE_NORM_INFINITY  ? 2 : norm;

  image_extent = gegl_operation_source_get_bounding_box (operation, "input");

  while (gegl_buffer_cl_iterator_next (i, &err) && !err)
    {
      err = cl_pixelize(i->tex[read],
                        i->tex[aux],
                        i->tex[0],
                        &i->roi[read],
                        &i->roi[0],
                        o->size_x,
                        o->size_y,
                        o->ratio_x,
                        o->ratio_y,
                        bg_color,
                        norm,
                        image_extent);

      if (err)
        {
          gegl_buffer_cl_iterator_stop (i);
          break;
        }
    }

  return !err;
}
Exemplo n.º 8
0
static void
lens_distort_func (gfloat              *src_buf,
                   gfloat              *dst_buf,
                   const GeglRectangle *extended,
                   const GeglRectangle *result,
                   const GeglRectangle *boundary,
                   LensDistortion       old,
                   gint                 xx,
                   gint                 yy,
                   GeglBuffer          *input)
{
  gdouble sx, sy, mag;
  gdouble brighten;
  gfloat  pixel_buffer [16 * 4], temp[4];
  gdouble dx, dy;
  gint    x_int, y_int, x = 0, y = 0, offset = 0;

  temp[0] = temp[1] = temp[2] = temp[3] = 0.0;

  lens_get_source_coord ((gdouble)xx, (gdouble)yy, &sx, &sy, &mag, &old);

  brighten = 1.0 + mag * old.brighten;

  x_int = floor (sx);
  dx = sx - x_int;

  y_int = floor (sy);
  dy = sy - y_int;

  for (y = y_int - 1; y <= y_int + 2; y++)
    {
      for (x = x_int - 1; x <= x_int + 2; x++)
        {
          gint b;

          if (x >= extended->x && x<(extended->x + extended->width)
              && y >= extended->y && y < (extended->y + extended->height))
            {
              gint src_off;
              src_off = (y - extended->y) * extended->width * 4 +
                (x - extended->x) * 4;
              for (b=0; b<4; b++)
                temp[b] = src_buf[src_off++];

            }
          else if (x >= boundary->x && x < boundary->x + boundary->width &&
                   y >= boundary->y && y < boundary->y + boundary->height)
            {
              gegl_buffer_sample (input, x, y, NULL, temp,
                                  babl_format ("RGBA float"),
                                  GEGL_SAMPLER_CUBIC,
                                  GEGL_ABYSS_NONE);
            }
          else
            {
              for (b=0; b<4; b++)
                temp[b] = 0.0;
            }

          for (b=0; b<4; b++)
            pixel_buffer[offset++] = temp[b];
        }
    }

  lens_cubic_interpolate (pixel_buffer, temp, dx, dy, brighten);

  offset = (yy - result->y) * result->width * 4 + (xx - result->x) * 4;
  for (x=0; x<4; x++)
    dst_buf[offset++] = temp[x];
}
Exemplo n.º 9
0
Arquivo: lcms.c Projeto: K-Sonoda/gimp
static void
lcms_layers_transform_rgb (gint                     *layers,
                           gint                      num_layers,
                           cmsHPROFILE               src_profile,
                           cmsHPROFILE               dest_profile,
                           GimpColorRenderingIntent  intent,
                           gboolean                  bpc)
{
  gint i;

  for (i = 0; i < num_layers; i++)
    {
      gint32           layer_id = layers[i];
      const Babl      *layer_format;
      gboolean         has_alpha;
      const Babl      *type;
      const Babl      *iter_format = NULL;
      cmsUInt32Number  lcms_format = 0;
      cmsHTRANSFORM    transform   = NULL;
      gint            *children;
      gint             num_children;

      children = gimp_item_get_children (layer_id, &num_children);

      if (children)
        {
          lcms_layers_transform_rgb (children, num_children,
                                     src_profile, dest_profile,
                                     intent, bpc);

          g_free (children);

          continue;
        }

      layer_format = gimp_drawable_get_format (layer_id);
      has_alpha    = babl_format_has_alpha (layer_format);
      type         = babl_format_get_type (layer_format, 0);

      if (type == babl_type ("u8"))
        {
          if (has_alpha)
            {
              lcms_format = TYPE_RGBA_8;
              iter_format = babl_format ("R'G'B'A u8");
            }
          else
            {
              lcms_format = TYPE_RGB_8;
              iter_format = babl_format ("R'G'B' u8");
            }
        }
      else if (type == babl_type ("u16"))
        {
          if (has_alpha)
            {
              lcms_format = TYPE_RGBA_16;
              iter_format = babl_format ("R'G'B'A u16");
            }
          else
            {
              lcms_format = TYPE_RGB_16;
              iter_format = babl_format ("R'G'B' u16");
            }
        }
      else if (type == babl_type ("half")) /* 16-bit floating point (half) */
        {
#ifdef TYPE_RGB_HALF_FLT
          /* half float types are only in lcms 2.4 and newer */
          if (has_alpha)
            {
              lcms_format = TYPE_RGBA_HALF_FLT;
              iter_format = babl_format ("R'G'B'A half");
            }
          else
            {
              lcms_format = TYPE_RGB_HALF_FLT;
              iter_format = babl_format ("R'G'B' float");
            }
#endif /* TYPE_RGB_HALF_FLT */
        }
      else if (type == babl_type ("float"))
        {
          if (has_alpha)
            {
              lcms_format = TYPE_RGBA_FLT;
              iter_format = babl_format ("R'G'B'A float");
            }
          else
            {
              lcms_format = TYPE_RGB_FLT;
              iter_format = babl_format ("R'G'B' float");
            }
        }
      else if (type == babl_type ("double"))
        {
          if (has_alpha)
            {
#ifdef TYPE_RGBA_DBL
              /* RGBA double not implemented in lcms */
              lcms_format = TYPE_RGBA_DBL;
              iter_format = babl_format ("R'G'B'A double");
#endif /* TYPE_RGBA_DBL */
            }
          else
            {
              lcms_format = TYPE_RGB_DBL;
              iter_format = babl_format ("R'G'B' double");
            }
        }

      if (lcms_format == 0)
        {
          g_printerr ("lcms: layer format %s not supported, "
                      "falling back to float\n",
                      babl_get_name (layer_format));

          if (has_alpha)
            {
              lcms_format = TYPE_RGBA_FLT;
              iter_format = babl_format ("R'G'B'A float");
            }
          else
            {
              lcms_format = TYPE_RGB_FLT;
              iter_format = babl_format ("R'G'B' float");
            }
        }

      transform = cmsCreateTransform (src_profile,  lcms_format,
                                      dest_profile, lcms_format,
                                      intent,
                                      cmsFLAGS_NOOPTIMIZE |
                                      (bpc ? cmsFLAGS_BLACKPOINTCOMPENSATION : 0));

      if (transform)
        {
          GeglBuffer         *src_buffer;
          GeglBuffer         *dest_buffer;
          GeglBufferIterator *iter;
          gint                layer_width;
          gint                layer_height;
          gint                layer_bpp;
          gboolean            layer_alpha;
          gdouble             progress_start = (gdouble) i / num_layers;
          gdouble             progress_end   = (gdouble) (i + 1) / num_layers;
          gdouble             range          = progress_end - progress_start;
          gint                count          = 0;
          gint                done           = 0;

          src_buffer   = gimp_drawable_get_buffer (layer_id);
          dest_buffer  = gimp_drawable_get_shadow_buffer (layer_id);
          layer_width  = gegl_buffer_get_width (src_buffer);
          layer_height = gegl_buffer_get_height (src_buffer);
          layer_bpp    = babl_format_get_bytes_per_pixel (iter_format);
          layer_alpha  = babl_format_has_alpha (iter_format);

          iter = gegl_buffer_iterator_new (src_buffer, NULL, 0,
                                           iter_format,
                                           GEGL_ACCESS_READ, GEGL_ABYSS_NONE);

          gegl_buffer_iterator_add (iter, dest_buffer, NULL, 0,
                                    iter_format,
                                    GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);

          while (gegl_buffer_iterator_next (iter))
            {
              /*  lcms doesn't touch the alpha channel, simply
               *  copy everything to dest before the transform
               */
              if (layer_alpha)
                memcpy (iter->data[1], iter->data[0],
                        iter->length * layer_bpp);

              cmsDoTransform (transform,
                              iter->data[0], iter->data[1], iter->length);
            }

          g_object_unref (src_buffer);
          g_object_unref (dest_buffer);

          gimp_drawable_merge_shadow (layer_id, TRUE);
          gimp_drawable_update (layer_id, 0, 0, layer_width, layer_height);

          if (count++ % 32 == 0)
            {
              gimp_progress_update (progress_start +
                                    (gdouble) done /
                                    (layer_width * layer_height) * range);
            }

          cmsDeleteTransform (transform);
        }
    }
}
Exemplo n.º 10
0
gint32
load_image (const gchar  *filename,
            GimpRunMode   runmode,
            gboolean      preview,
            gboolean     *resolution_loaded,
            GError      **error)
{
  gint32 volatile  image_ID;
  gint32           layer_ID;
  struct jpeg_decompress_struct cinfo;
  struct my_error_mgr           jerr;
  jpeg_saved_marker_ptr         marker;
  FILE            *infile;
  guchar          *buf;
  guchar         **rowbuf;
  GimpImageBaseType image_type;
  GimpImageType    layer_type;
  GeglBuffer      *buffer = NULL;
  const Babl      *format;
  gint             tile_height;
  gint             scanlines;
  gint             i, start, end;
  cmsHTRANSFORM    cmyk_transform = NULL;

  /* We set up the normal JPEG error routines. */
  cinfo.err = jpeg_std_error (&jerr.pub);
  jerr.pub.error_exit = my_error_exit;

  if (!preview)
    {
      jerr.pub.output_message = my_output_message;

      gimp_progress_init_printf (_("Opening '%s'"),
                                 gimp_filename_to_utf8 (filename));
    }

  if ((infile = g_fopen (filename, "rb")) == NULL)
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for reading: %s"),
                   gimp_filename_to_utf8 (filename), g_strerror (errno));
      return -1;
    }

  image_ID = -1;

  /* Establish the setjmp return context for my_error_exit to use. */
  if (setjmp (jerr.setjmp_buffer))
    {
      /* If we get here, the JPEG code has signaled an error.
       * We need to clean up the JPEG object, close the input file, and return.
       */
      jpeg_destroy_decompress (&cinfo);
      if (infile)
        fclose (infile);

      if (image_ID != -1 && !preview)
        gimp_image_delete (image_ID);

      if (preview)
        destroy_preview ();

      if (buffer)
        g_object_unref (buffer);

      return -1;
    }

  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress (&cinfo);

  /* Step 2: specify data source (eg, a file) */

  jpeg_stdio_src (&cinfo, infile);

  if (! preview)
    {
      /* - step 2.1: tell the lib to save the comments */
      jpeg_save_markers (&cinfo, JPEG_COM, 0xffff);

      /* - step 2.2: tell the lib to save APP1 data (Exif or XMP) */
      jpeg_save_markers (&cinfo, JPEG_APP0 + 1, 0xffff);

      /* - step 2.3: tell the lib to save APP2 data (ICC profiles) */
      jpeg_save_markers (&cinfo, JPEG_APP0 + 2, 0xffff);
    }

  /* Step 3: read file parameters with jpeg_read_header() */

  jpeg_read_header (&cinfo, TRUE);

  /* We can ignore the return value from jpeg_read_header since
   *   (a) suspension is not possible with the stdio data source, and
   *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.doc for more info.
   */

  /* Step 4: set parameters for decompression */

  /* In this example, we don't need to change any of the defaults set by
   * jpeg_read_header(), so we do nothing here.
   */

  /* Step 5: Start decompressor */

  jpeg_start_decompress (&cinfo);

  /* We may need to do some setup of our own at this point before reading
   * the data.  After jpeg_start_decompress() we have the correct scaled
   * output image dimensions available, as well as the output colormap
   * if we asked for color quantization.
   */

  /* temporary buffer */
  tile_height = gimp_tile_height ();
  buf = g_new (guchar,
               tile_height * cinfo.output_width * cinfo.output_components);

  rowbuf = g_new (guchar *, tile_height);

  for (i = 0; i < tile_height; i++)
    rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i;

  switch (cinfo.output_components)
    {
    case 1:
      image_type = GIMP_GRAY;
      layer_type = GIMP_GRAY_IMAGE;
      break;

    case 3:
      image_type = GIMP_RGB;
      layer_type = GIMP_RGB_IMAGE;
      break;

    case 4:
      if (cinfo.out_color_space == JCS_CMYK)
        {
          image_type = GIMP_RGB;
          layer_type = GIMP_RGB_IMAGE;
          break;
        }
      /*fallthrough*/

    default:
      g_message ("Don't know how to load JPEG images "
                 "with %d color channels, using colorspace %d (%d).",
                 cinfo.output_components, cinfo.out_color_space,
                 cinfo.jpeg_color_space);
      return -1;
      break;
    }

  if (preview)
    {
      image_ID = preview_image_ID;
    }
  else
    {
      image_ID = gimp_image_new_with_precision (cinfo.output_width,
                                                cinfo.output_height,
                                                image_type,
                                                GIMP_PRECISION_U8_GAMMA);

      gimp_image_undo_disable (image_ID);
      gimp_image_set_filename (image_ID, filename);
    }

  if (preview)
    {
      preview_layer_ID = gimp_layer_new (preview_image_ID, _("JPEG preview"),
                                         cinfo.output_width,
                                         cinfo.output_height,
                                         layer_type, 100, GIMP_NORMAL_MODE);
      layer_ID = preview_layer_ID;
    }
  else
    {
      layer_ID = gimp_layer_new (image_ID, _("Background"),
                                 cinfo.output_width,
                                 cinfo.output_height,
                                 layer_type, 100, GIMP_NORMAL_MODE);
    }

  if (! preview)
    {
      GString  *comment_buffer = NULL;
      guint8   *profile        = NULL;
      guint     profile_size   = 0;

      /* Step 5.0: save the original JPEG settings in a parasite */
      jpeg_detect_original_settings (&cinfo, image_ID);

      /* Step 5.1: check for comments, or Exif metadata in APP1 markers */
      for (marker = cinfo.marker_list; marker; marker = marker->next)
        {
          const gchar *data = (const gchar *) marker->data;
          gsize        len  = marker->data_length;

          if (marker->marker == JPEG_COM)
            {
#ifdef GIMP_UNSTABLE
              g_print ("jpeg-load: found image comment (%d bytes)\n",
                       marker->data_length);
#endif

              if (! comment_buffer)
                {
                  comment_buffer = g_string_new_len (data, len);
                }
              else
                {
                  /* concatenate multiple comments, separate them with LF */
                  g_string_append_c (comment_buffer, '\n');
                  g_string_append_len (comment_buffer, data, len);
                }
            }
          else if ((marker->marker == JPEG_APP0 + 1)
                   && (len > sizeof (JPEG_APP_HEADER_EXIF) + 8)
                   && ! strcmp (JPEG_APP_HEADER_EXIF, data))
            {
#ifdef GIMP_UNSTABLE
              g_print ("jpeg-load: found Exif block (%d bytes)\n",
                       (gint) (len - sizeof (JPEG_APP_HEADER_EXIF)));
#endif
            }
        }

      if (jpeg_load_resolution (image_ID, &cinfo))
        {
          if (resolution_loaded)
            *resolution_loaded = TRUE;
        }

      /* if we found any comments, then make a parasite for them */
      if (comment_buffer && comment_buffer->len)
        {
          GimpParasite *parasite;

          jpeg_load_sanitize_comment (comment_buffer->str);
          parasite = gimp_parasite_new ("gimp-comment",
                                        GIMP_PARASITE_PERSISTENT,
                                        strlen (comment_buffer->str) + 1,
                                        comment_buffer->str);
          gimp_image_attach_parasite (image_ID, parasite);
          gimp_parasite_free (parasite);

          g_string_free (comment_buffer, TRUE);
        }

      /* Step 5.3: check for an embedded ICC profile in APP2 markers */
      jpeg_icc_read_profile (&cinfo, &profile, &profile_size);

      if (cinfo.out_color_space == JCS_CMYK)
        {
          cmyk_transform = jpeg_load_cmyk_transform (profile, profile_size);
        }
      else if (profile) /* don't attach the profile if we are transforming */
        {
          GimpParasite *parasite;

          parasite = gimp_parasite_new ("icc-profile",
                                        GIMP_PARASITE_PERSISTENT |
                                        GIMP_PARASITE_UNDOABLE,
                                        profile_size, profile);
          gimp_image_attach_parasite (image_ID, parasite);
          gimp_parasite_free (parasite);
        }

      g_free (profile);

      /* Do not attach the "jpeg-save-options" parasite to the image
       * because this conflicts with the global defaults (bug #75398).
       */
    }

  /* Step 6: while (scan lines remain to be read) */
  /*           jpeg_read_scanlines(...); */

  /* Here we use the library's state variable cinfo.output_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   */

  buffer = gimp_drawable_get_buffer (layer_ID);
  format = babl_format (image_type == GIMP_RGB ? "R'G'B' u8" : "Y' u8");

  while (cinfo.output_scanline < cinfo.output_height)
    {
      start = cinfo.output_scanline;
      end   = cinfo.output_scanline + tile_height;
      end   = MIN (end, cinfo.output_height);

      scanlines = end - start;

      for (i = 0; i < scanlines; i++)
        jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1);

      if (cinfo.out_color_space == JCS_CMYK)
        jpeg_load_cmyk_to_rgb (buf, cinfo.output_width * scanlines,
                               cmyk_transform);

      gegl_buffer_set (buffer,
                       GEGL_RECTANGLE (0, start, cinfo.output_width, scanlines),
                       0,
                       format,
                       buf,
                       GEGL_AUTO_ROWSTRIDE);

      if (! preview && (cinfo.output_scanline % 32) == 0)
        gimp_progress_update ((gdouble) cinfo.output_scanline /
                              (gdouble) cinfo.output_height);
    }

  /* Step 7: Finish decompression */

  jpeg_finish_decompress (&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  if (cmyk_transform)
    cmsDeleteTransform (cmyk_transform);

  /* Step 8: Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress (&cinfo);

  g_object_unref (buffer);

  /* free up the temporary buffers */
  g_free (rowbuf);
  g_free (buf);

  /* After finish_decompress, we can close the input file.
   * Here we postpone it until after no more JPEG errors are possible,
   * so as to simplify the setjmp error logic above.  (Actually, I don't
   * think that jpeg_destroy can do an error exit, but why assume anything...)
   */
  fclose (infile);

  /* At this point you may want to check to see whether any corrupt-data
   * warnings occurred (test whether jerr.num_warnings is nonzero).
   */

  /* Detach from the drawable and add it to the image.
   */
  if (! preview)
    {
      gimp_progress_update (1.0);
    }

  gimp_image_insert_layer (image_ID, layer_ID, -1, 0);

  return image_ID;
}
Exemplo n.º 11
0
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
         const GeglRectangle *result,
         gint                 level)
{
  GeglProperties         *o       = GEGL_PROPERTIES (operation);
  GeglSampler        *sampler = gegl_buffer_sampler_new_at_level (input,
                                                         babl_format ("RGBA float"),
                                                         o->sampler_type,
                                                         level);
  GeglBufferIterator *iter;

  GeglAbyssPolicy abyss = o->tileable ? GEGL_ABYSS_LOOP : GEGL_ABYSS_NONE;

  iter = gegl_buffer_iterator_new (output, result, 0, babl_format ("RGBA float"),
                                   GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);

  while (gegl_buffer_iterator_next (iter))
    {
      gint x = result->x;
      gint y = result->y;
      gfloat *out_pixel = iter->data[0];

      for (y = iter->roi[0].y; y < iter->roi[0].y + iter->roi[0].height; ++y)
        for (x = iter->roi[0].x; x < iter->roi[0].x + iter->roi[0].width; ++x)
          {
            gdouble shift;
            gdouble coordsx;
            gdouble coordsy;
            gdouble lambda;

            gdouble angle_rad = o->angle / 180.0 * G_PI;
            gdouble nx = x * cos (angle_rad) + y * sin (angle_rad);

            switch (o->wave_type)
              {
                case GEGL_RIPPLE_WAVE_TYPE_SAWTOOTH:
                  lambda = div (nx,o->period).rem - o->phi * o->period;
                  if (lambda < 0)
                    lambda += o->period;
                  shift = o->amplitude * (fabs (((lambda / o->period) * 4) - 2) - 1);
                  break;
                case GEGL_RIPPLE_WAVE_TYPE_SINE:
                default:
                  shift = o->amplitude * sin (2.0 * G_PI * nx / o->period + 2.0 * G_PI * o->phi);
                  break;
              }

            coordsx = x + shift * sin (angle_rad);
            coordsy = y + shift * cos (angle_rad);

            gegl_sampler_get (sampler,
                              coordsx,
                              coordsy,
                              NULL,
                              out_pixel,
                              abyss);

            out_pixel += 4;
          }
    }

  g_object_unref (sampler);

  return  TRUE;
}
Exemplo n.º 12
0
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
         const GeglRectangle *result,
         gint                 level)
{
  GeglChantO              *o            = GEGL_CHANT_PROPERTIES (operation);
  GeglOperationAreaFilter *op_area      = GEGL_OPERATION_AREA_FILTER (operation);
  GeglRectangle            boundary     = get_effective_area (operation);
  GeglRectangle            extended;
  const Babl              *format       = babl_format ("RGBA float");

  GRand  *gr = g_rand_new_with_seed (o->seed);
  gfloat  color[4];
  gint    cols, rows, num_tiles, count;
  gint   *random_indices;
  gfloat *dst_buf;

  Polygon poly;
  gint    i;

  extended.x      = CLAMP (result->x - op_area->left, boundary.x, boundary.x +
                           boundary.width);
  extended.width  = CLAMP (result->width + op_area->left + op_area->right, 0,
                           boundary.width);
  extended.y      = CLAMP (result->y - op_area->top, boundary.y, boundary.y +
                           boundary.width);
  extended.height = CLAMP (result->height + op_area->top + op_area->bottom, 0,
                           boundary.height);

  dst_buf = g_new0 (gfloat, extended.width * extended.height * 4);

  cols = (result->width + o->tile_size - 1) / o->tile_size;
  rows = (result->height + o->tile_size - 1) / o->tile_size;

  num_tiles = (rows + 1) * (cols + 1);

  random_indices = g_new0 (gint, num_tiles);

  for (i = 0; i < num_tiles; i++)
    random_indices[i] = i;

  randomize_indices (num_tiles, random_indices, gr);

  for (count = 0; count < num_tiles; count++)
    {
      gint i, j, ix, iy;
      gdouble x, y, width, height, theta;

      i = random_indices[count] / (cols + 1);
      j = random_indices[count] % (cols + 1);

      x = j * o->tile_size + (o->tile_size / 4.0)
        - g_rand_double_range (gr, 0, (o->tile_size /2.0)) + result->x;

      y = i * o->tile_size + (o->tile_size / 4.0)
        - g_rand_double_range (gr, 0, (o->tile_size /2.0)) + result->y;

      width  = (o->tile_size +
                g_rand_double_range (gr, -o->tile_size / 8.0, o->tile_size / 8.0))
        * o->tile_saturation;

      height = (o->tile_size +
                g_rand_double_range (gr, -o->tile_size / 8.0, o->tile_size / 8.0))
        * o->tile_saturation;

      theta = g_rand_double_range (gr, 0, 2 * G_PI);

      polygon_reset (&poly);
      polygon_add_point (&poly, -width / 2.0, -height / 2.0);
      polygon_add_point (&poly,  width / 2.0,  -height / 2.0);
      polygon_add_point (&poly,  width / 2.0,   height / 2.0);
      polygon_add_point (&poly, -width / 2.0,  height / 2.0);
      polygon_rotate (&poly, theta);
      polygon_translate (&poly, x, y);

      ix = CLAMP (x, boundary.x, boundary.x + boundary.width - 1);
      iy = CLAMP (y, boundary.y, boundary.y + boundary.height - 1);

      gegl_buffer_sample (input, ix, iy, NULL, color, format,
                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);

      fill_poly_color (&poly, &extended, &boundary, dst_buf, color);
    }

  gegl_buffer_set (output, &extended, 0, format, dst_buf, GEGL_AUTO_ROWSTRIDE);

  g_free (dst_buf);
  g_free (random_indices);
  g_free (gr);

  return TRUE;
}
Exemplo n.º 13
0
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
         const GeglRectangle *result)
{
  GeglRectangle rect;
  GeglBuffer *temp;
  GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
  GeglChantO              *o       = GEGL_CHANT_PROPERTIES (operation);

  GeglRectangle temp_extend;
  gdouble       B, b[4];
  gdouble      *cmatrix;
  gint          cmatrix_len;
  gboolean      force_iir;
  gboolean      force_fir;

  rect.x      = result->x - op_area->left;
  rect.width  = result->width + op_area->left + op_area->right;
  rect.y      = result->y - op_area->top;
  rect.height = result->height + op_area->top + op_area->bottom;

  temp_extend = rect;
  temp_extend.x      = result->x;
  temp_extend.width  = result->width;
  temp = gegl_buffer_new (&temp_extend, babl_format ("RaGaBaA float"));

  force_iir = o->filter && !strcmp (o->filter, "iir");
  force_fir = o->filter && !strcmp (o->filter, "fir");

  if ((force_iir || o->std_dev_x > 1.0) && !force_fir)
    {
      iir_young_find_constants (o->std_dev_x, &B, b);
      iir_young_hor_blur (input, &rect, temp, &temp_extend,  B, b);
    }
  else
    {
      cmatrix_len = fir_gen_convolve_matrix (o->std_dev_x, &cmatrix);
      fir_hor_blur (input, &rect, temp, &temp_extend,
                    cmatrix, cmatrix_len, op_area->left);
      g_free (cmatrix);
    }


  if ((force_iir || o->std_dev_y > 1.0) && !force_fir)
    {
      iir_young_find_constants (o->std_dev_y, &B, b);
      iir_young_ver_blur (temp, &temp_extend, output, result, B, b);
    }
  else
    { 
      cmatrix_len = fir_gen_convolve_matrix (o->std_dev_y, &cmatrix);
      fir_ver_blur (temp, &temp_extend, output, result, cmatrix, cmatrix_len,
                    op_area->top);
      g_free (cmatrix);
    }

  g_object_unref (temp);
  return  TRUE;
}
Exemplo n.º 14
0
static gboolean
save_image (const gchar  *filename,
            GeglBuffer   *buffer,
            GError      **error)
{
  const Babl *format = babl_format ("R'G'B'A u8");
  gint        row, col, cols, rows, x, y;
  gint        colcount, colspan, rowspan;
  gint       *palloc;
  guchar     *buf, *buf2;
  gchar      *width, *height;
  FILE       *fp;

  cols = gegl_buffer_get_width  (buffer);
  rows = gegl_buffer_get_height (buffer);

  fp = g_fopen (filename, "w");

  if (! fp)
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for writing: %s"),
                   gimp_filename_to_utf8 (filename), g_strerror (errno));
      return FALSE;
    }

  palloc = g_new (int, rows * cols);

  if (gtmvals.fulldoc)
    {
      fprintf (fp, "<HTML>\n<HEAD><TITLE>%s</TITLE></HEAD>\n<BODY>\n",
               filename);
      fprintf (fp, "<H1>%s</H1>\n",
               filename);
    }

  fprintf (fp, "<TABLE BORDER=%d CELLPADDING=%d CELLSPACING=%d>\n",
           gtmvals.border, gtmvals.cellpadding, gtmvals.cellspacing);

  if (gtmvals.caption)
    fprintf (fp, "<CAPTION>%s</CAPTION>\n",
             gtmvals.captiontxt);

  gimp_progress_init_printf (_("Saving '%s'"),
                             gimp_filename_to_utf8 (filename));

  buf  = g_new (guchar, babl_format_get_bytes_per_pixel (format));
  buf2 = g_new (guchar, babl_format_get_bytes_per_pixel (format));

  width = height = NULL;

  if (strcmp (gtmvals.clwidth, "") != 0)
    {
      width = g_strdup_printf (" WIDTH=\"%s\"", gtmvals.clwidth);
    }

  if (strcmp (gtmvals.clheight, "") != 0)
    {
      height = g_strdup_printf (" HEIGHT=\"%s\" ", gtmvals.clheight);
    }

  if (! width)
    width = g_strdup (" ");

  if (! height)
    height = g_strdup (" ");

  /* Initialize array to hold ROWSPAN and COLSPAN cell allocation table */

  for (row = 0; row < rows; row++)
    for (col = 0; col < cols; col++)
      palloc[cols * row + col] = 1;

  colspan = 0;
  rowspan = 0;

  for (y = 0; y < rows; y++)
    {
      fprintf (fp,"   <TR>\n");

      for (x = 0; x < cols; x++)
        {
          gegl_buffer_sample (buffer, x, y, NULL, buf, format,
                              GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);

          /* Determine ROWSPAN and COLSPAN */

          if (gtmvals.spantags)
            {
              col      = x;
              row      = y;
              colcount = 0;
              colspan  = 0;
              rowspan  = 0;

              gegl_buffer_sample (buffer, col, row, NULL, buf2, format,
                                  GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);

              while (color_comp (buf, buf2) &&
                     palloc[cols * row + col] == 1 &&
                     row < rows)
                {
                  while (color_comp (buf, buf2) &&
                         palloc[cols * row + col] == 1 &&
                         col < cols)
                    {
                      colcount++;
                      col++;

                      gegl_buffer_sample (buffer, col, row, NULL, buf2, format,
                                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
                    }

                  if (colcount != 0)
                    {
                      row++;
                      rowspan++;
                    }

                  if (colcount < colspan || colspan == 0)
                    colspan = colcount;

                  col = x;
                  colcount = 0;

                  gegl_buffer_sample (buffer, col, row, NULL, buf2, format,
                                      GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
                }

              if (colspan > 1 || rowspan > 1)
                {
                  for (row = 0; row < rowspan; row++)
                    for (col = 0; col < colspan; col++)
                      palloc[cols * (row + y) + (col + x)] = 0;

                  palloc[cols * y + x] = 2;
                }
            }

          if (palloc[cols * y + x] == 1)
            fprintf (fp, "      <TD%s%sBGCOLOR=#%02x%02x%02x>",
                     width, height, buf[0], buf[1], buf[2]);

          if (palloc[cols * y + x] == 2)
            fprintf (fp,"      <TD ROWSPAN=\"%d\" COLSPAN=\"%d\"%s%sBGCOLOR=#%02x%02x%02x>",
                     rowspan, colspan, width, height,
                     buf[0], buf[1], buf[2]);

          if (palloc[cols * y + x] != 0)
            {
              if (gtmvals.tdcomp)
                fprintf (fp, "%s</TD>\n", gtmvals.cellcontent);
              else
                fprintf (fp, "\n      %s\n      </TD>\n", gtmvals.cellcontent);
            }
        }

      fprintf (fp,"   </TR>\n");

      gimp_progress_update ((double) y / (double) rows);
    }

  gimp_progress_update (1.0);

  if (gtmvals.fulldoc)
    fprintf (fp, "</TABLE></BODY></HTML>\n");
  else
    fprintf (fp, "</TABLE>\n");

  fclose (fp);
  g_free (width);
  g_free (height);
  g_free (palloc);

  return TRUE;
}
Exemplo n.º 15
0
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *output,
         const GeglRectangle *result,
         gint                 level)
{
  GeglChantO   *o = GEGL_CHANT_PROPERTIES (operation);
  FILE         *fp;
  pnm_struct    img;
  GeglRectangle rect = {0,0,0,0};
  gboolean      ret = FALSE;

  fp = (!strcmp (o->path, "-") ? stdin : fopen (o->path,"rb"));

  if (!fp)
    return FALSE;

  if (!ppm_load_read_header (fp, &img))
    goto out;

  /* Allocating Array Size */

  /* Should use g_try_malloc(), but this causes crashes elsewhere because the
   * error signalled by returning FALSE isn't properly acted upon. Therefore
   * g_malloc() is used here which aborts if the requested memory size can't be
   * allocated causing a controlled crash. */
  img.data = (guchar*) g_malloc (img.numsamples * img.bpc);

  /* No-op without g_try_malloc(), see above. */
  if (! img.data)
    {
      g_warning ("Couldn't allocate %" G_GSIZE_FORMAT " bytes, giving up.", ((gsize)img.numsamples * img.bpc));
      goto out;
    }

  rect.height = img.height;
  rect.width = img.width;

  if (img.bpc == 1)
    {
      if (img.channels == 3)
        gegl_buffer_get (output, &rect, 1.0, babl_format ("R'G'B' u8"), img.data,
                         GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
      else
        gegl_buffer_get (output, &rect, 1.0, babl_format ("Y' u8"), img.data,
                         GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
    }
  else if (img.bpc == 2)
    {
      if (img.channels == 3)
        gegl_buffer_get (output, &rect, 1.0, babl_format ("R'G'B' u16"), img.data,
                         GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
      else
        gegl_buffer_get (output, &rect, 1.0, babl_format ("Y' u16"), img.data,
                         GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
    }
  else
    g_warning ("%s: Programmer stupidity error", G_STRLOC);

  ppm_load_read_image (fp, &img);

  if (img.bpc == 1)
    {
      if (img.channels == 3)
        gegl_buffer_set (output, &rect, 0, babl_format ("R'G'B' u8"), img.data,
                         GEGL_AUTO_ROWSTRIDE);
      else
        gegl_buffer_set (output, &rect, 0, babl_format ("Y' u8"), img.data,
                         GEGL_AUTO_ROWSTRIDE);
    }
  else if (img.bpc == 2)
    {
      if (img.channels == 3)
        gegl_buffer_set (output, &rect, 0, babl_format ("R'G'B' u16"), img.data,
                         GEGL_AUTO_ROWSTRIDE);
      else
        gegl_buffer_set (output, &rect, 0, babl_format ("Y' u16"), img.data,
                         GEGL_AUTO_ROWSTRIDE);
    }
  else
    g_warning ("%s: Programmer stupidity error", G_STRLOC);

  g_free (img.data);

  ret = TRUE;

 out:
  if (stdin != fp)
    fclose (fp);

  return ret;
}
Exemplo n.º 16
0
static void
lens_distort_func (gfloat              *src_buf,
                   gfloat              *dst_buf,
                   const GeglRectangle *extended,
                   const GeglRectangle *result,
                   const GeglRectangle *boundary,
                   LensValues          *lens,
                   gint                 xx,
                   gint                 yy,
                   GeglBuffer          *input,
                   gfloat              *background)
{
  gdouble sx, sy, mag;
  gdouble brighten;
  gfloat  pixel_buffer [16 * 4], temp[4];
  gdouble dx, dy;
  gint    x_int, y_int, x = 0, y = 0, offset = 0;

  temp[0] = temp[1] = temp[2] = temp[3] = 0.0;

  lens_get_source_coord ((gdouble) xx, (gdouble) yy, &sx, &sy, &mag, lens);

  /* pseudo gamma transformation, since the input is scRGB */
  brighten = pow (MAX (1.0 + mag * lens->brighten, 0.0), 2.4);

  x_int = floor (sx);
  dx = sx - x_int;

  y_int = floor (sy);
  dy = sy - y_int;

  for (y = y_int - 1; y <= y_int + 2; y++)
    {
      for (x = x_int - 1; x <= x_int + 2; x++)
        {
          gint b;

          if (x < boundary->x || x >= (boundary->x + boundary->width) ||
              y < boundary->y || y >= (boundary->y + boundary->height))
            {
              for (b = 0; b < 4; b++)
                pixel_buffer[offset++] = background[b];
            }
          else
            {

              if (x >= extended->x && x < (extended->x + extended->width) &&
                  y >= extended->y && y < (extended->y + extended->height))
                {
                  gint src_off;
                  src_off = (y - extended->y) * extended->width * 4 +
                    (x - extended->x) * 4;

                  for (b = 0; b < 4; b++)
                    temp[b] = src_buf[src_off++];
                }
              else
                {
                  gegl_buffer_sample (input, x, y, NULL, temp,
                                      babl_format ("RGBA float"),
                                      GEGL_SAMPLER_LINEAR,
                                      GEGL_ABYSS_CLAMP);
                }

              for (b = 0; b < 4; b++)
                pixel_buffer[offset++] = temp[b];
            }
        }
    }

  lens_cubic_interpolate (pixel_buffer, temp, dx, dy, brighten);

  offset = (yy - result->y) * result->width * 4 + (xx - result->x) * 4;

  for (x = 0; x < 4; x++)
    dst_buf[offset++] = temp[x];
}
Exemplo n.º 17
0
static void
gimp_color_frame_init (GimpColorFrame *frame)
{
  GtkWidget *vbox;
  GtkWidget *vbox2;
  gint       i;

  frame->sample_valid  = FALSE;
  frame->sample_format = babl_format ("R'G'B' u8");

  gimp_rgba_set (&frame->color, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);

  frame->menu = gimp_enum_combo_box_new (GIMP_TYPE_COLOR_FRAME_MODE);
  gtk_frame_set_label_widget (GTK_FRAME (frame), frame->menu);
  gtk_widget_show (frame->menu);

  g_signal_connect (frame->menu, "changed",
                    G_CALLBACK (gimp_color_frame_menu_callback),
                    frame);

  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
  gtk_container_add (GTK_CONTAINER (frame), vbox);
  gtk_widget_show (vbox);

  frame->color_area =
    g_object_new (GIMP_TYPE_COLOR_AREA,
                  "color",          &frame->color,
                  "type",           GIMP_COLOR_AREA_SMALL_CHECKS,
                  "drag-mask",      GDK_BUTTON1_MASK,
                  "draw-border",    TRUE,
                  "height-request", 20,
                  NULL);
  gtk_box_pack_start (GTK_BOX (vbox), frame->color_area, FALSE, FALSE, 0);

  vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
  gtk_box_set_homogeneous (GTK_BOX (vbox2), TRUE);
  gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
  gtk_widget_show (vbox2);

  for (i = 0; i < GIMP_COLOR_FRAME_ROWS; i++)
    {
      GtkWidget *hbox;

      hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
      gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
      gtk_widget_show (hbox);

      frame->name_labels[i] = gtk_label_new (" ");
      gtk_label_set_xalign (GTK_LABEL (frame->name_labels[i]), 0.0);
      gtk_box_pack_start (GTK_BOX (hbox), frame->name_labels[i],
                          FALSE, FALSE, 0);
      gtk_widget_show (frame->name_labels[i]);

      frame->value_labels[i] = gtk_label_new (" ");
      gtk_label_set_selectable (GTK_LABEL (frame->value_labels[i]), TRUE);
      gtk_label_set_xalign (GTK_LABEL (frame->value_labels[i]), 1.0);
      gtk_box_pack_end (GTK_BOX (hbox), frame->value_labels[i],
                        FALSE, FALSE, 0);
      gtk_widget_show (frame->value_labels[i]);
    }
}
Exemplo n.º 18
0
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
         const GeglRectangle *result,
         gint                 level)
{
  GeglChantO    *o = GEGL_CHANT_PROPERTIES (operation);
  LensValues     lens;
  GeglRectangle  boundary;
  gint           i, j;
  gfloat        *src_buf, *dst_buf;
  gfloat         background[4];

  boundary = *gegl_operation_source_get_bounding_box (operation, "input");
  lens     =  lens_setup_calc (o, boundary);

  src_buf = g_new0 (gfloat, SQR (MAX_WH) * 4);
  dst_buf = g_new0 (gfloat, SQR (CHUNK_SIZE) * 4);

  gegl_color_get_pixel (o->background, babl_format ("RGBA float"), background);

  for (j = 0; (j-1) * CHUNK_SIZE < result->height; j++)
    for (i = 0; (i-1) * CHUNK_SIZE < result->width; i++)
      {
        GeglRectangle chunked_result;
        GeglRectangle area;
        gint          x, y;

        chunked_result = *GEGL_RECTANGLE (result->x + i * CHUNK_SIZE,
                                          result->y + j * CHUNK_SIZE,
                                          CHUNK_SIZE, CHUNK_SIZE);

        gegl_rectangle_intersect (&chunked_result, &chunked_result, result);

        if (chunked_result.width < 1  || chunked_result.height < 1)
          continue;

        area = get_required (&boundary, &chunked_result, operation);

        clamp_area (&area, lens.centre_x, lens.centre_y);

        gegl_buffer_get (input, &area, 1.0, babl_format ("RGBA float"), src_buf,
                         GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);

        for (y = chunked_result.y; y < chunked_result.y + chunked_result.height; y++)
          for (x = chunked_result.x; x < chunked_result.x + chunked_result.width; x++)
            {
              lens_distort_func (src_buf, dst_buf, &area, &chunked_result, &boundary,
                                 &lens, x, y, input, background);
            }

        gegl_buffer_set (output, &chunked_result, 0, babl_format ("RGBA float"),
                         dst_buf, GEGL_AUTO_ROWSTRIDE);
      }

  g_free (dst_buf);
  g_free (src_buf);

  return TRUE;
}
Exemplo n.º 19
0
static void
set_rectangle_noalloc (GeglBuffer      *output,
                       GeglRectangle   *rect,
                       GeglRectangle   *rect_shape,
                       GeglColor       *color,
                       GeglPixelizeNorm norm)
{
  if (norm == GEGL_PIXELIZE_NORM_INFINITY)
    {
      GeglRectangle rect2;
      gegl_rectangle_intersect (&rect2, rect, rect_shape);
      gegl_buffer_set_color (output, &rect2, color);
    }
  else
    {
      GeglBufferIterator *gi;
      gint                c, x, y;
      gfloat              col[4];
      gfloat              center_x, center_y;
      gfloat              shape_area = rect_shape->width * rect_shape->height;

      center_x = rect_shape->x + rect_shape->width / 2.0f;
      center_y = rect_shape->y + rect_shape->height / 2.0f;

      gegl_color_get_pixel (color, babl_format ("RaGaBaA float"), col);

      gi = gegl_buffer_iterator_new (output, rect, 0, babl_format ("RaGaBaA float"),
                                     GEGL_ACCESS_WRITE, GEGL_ABYSS_CLAMP);

      while (gegl_buffer_iterator_next (gi))
        {
          gfloat       *data = (gfloat*) gi->data[0];
          GeglRectangle roi = gi->roi[0];

          switch (norm)
            {
            case (GEGL_PIXELIZE_NORM_EUCLIDEAN):

              for (y = 0; y < roi.height; y++)
                for (x = 0; x < roi.width; x++)
                  if (SQR ((x + roi.x - center_x) / (gfloat) rect_shape->width) +
                      SQR ((y + roi.y - center_y) / (gfloat) rect_shape->height) <= 1.0f)
                    for (c = 0; c < 4; c++)
                      data [4 * (y * roi.width + x) + c] = col[c];
              break;

            case (GEGL_PIXELIZE_NORM_MANHATTAN):

              for (y = 0; y < roi.height; y++)
                for (x = 0; x < roi.width; x++)
                  if (fabsf (x + roi.x - center_x) * rect_shape->height +
                      fabsf (y + roi.y - center_y) * rect_shape->width
                      < shape_area)
                    for (c = 0; c < 4; c++)
                      data [4 * (y * roi.width + x) + c] = col[c];
              break;

            case (GEGL_PIXELIZE_NORM_INFINITY):
              break;
            }
        }
    }
}
Exemplo n.º 20
0
static void
prepare (GeglOperation *operation)
{
  GeglProperties *o = GEGL_PROPERTIES (operation);
  const Babl *output_format = (o->linear) ? babl_format ("Y float") : babl_format ("Y' float");

  switch (o->component)
    {
      case GEGL_COMPONENT_EXTRACT_ALPHA:
        {
          gegl_operation_set_format (operation, "input",
                                     babl_format ("YA float"));
        }
      break;

      case GEGL_COMPONENT_EXTRACT_RGB_RED:
      case GEGL_COMPONENT_EXTRACT_RGB_GREEN:
      case GEGL_COMPONENT_EXTRACT_RGB_BLUE:
        {
          gegl_operation_set_format (operation, "input",
                                     babl_format ("R'G'B' float"));
        }
      break;

      case GEGL_COMPONENT_EXTRACT_HUE:
      case GEGL_COMPONENT_EXTRACT_HSV_SATURATION:
      case GEGL_COMPONENT_EXTRACT_HSV_VALUE:
        {
          gegl_operation_set_format (operation, "input",
                                     babl_format ("HSV float"));
        }
      break;

      case GEGL_COMPONENT_EXTRACT_HSL_LIGHTNESS:
      case GEGL_COMPONENT_EXTRACT_HSL_SATURATION:
        {
          gegl_operation_set_format (operation, "input",
                                     babl_format ("HSL float"));
        }
      break;

      case GEGL_COMPONENT_EXTRACT_CMYK_CYAN:
      case GEGL_COMPONENT_EXTRACT_CMYK_MAGENTA:
      case GEGL_COMPONENT_EXTRACT_CMYK_YELLOW:
      case GEGL_COMPONENT_EXTRACT_CMYK_KEY:
        {
          gegl_operation_set_format (operation, "input",
                                     babl_format ("CMYK float"));
        }
      break;

      case GEGL_COMPONENT_EXTRACT_YCBCR_Y:
      case GEGL_COMPONENT_EXTRACT_YCBCR_CB:
      case GEGL_COMPONENT_EXTRACT_YCBCR_CR:
        {
          gegl_operation_set_format (operation, "input",
                                     babl_format ("Y'CbCr float"));
        }
      break;

      case GEGL_COMPONENT_EXTRACT_LAB_L:
      case GEGL_COMPONENT_EXTRACT_LAB_A:
      case GEGL_COMPONENT_EXTRACT_LAB_B:
        {
          gegl_operation_set_format (operation, "input",
                                     babl_format ("CIE Lab float"));
        }
      break;
    }

    gegl_operation_set_format (operation, "output", output_format);
}
Exemplo n.º 21
0
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
         const GeglRectangle *roi,
         gint                 level)
{
  GeglRectangle            src_rect;
  GeglRectangle           *whole_region;
  GeglProperties          *o = GEGL_PROPERTIES (operation);
  GeglOperationAreaFilter *op_area;

  op_area = GEGL_OPERATION_AREA_FILTER (operation);

  whole_region = gegl_operation_source_get_bounding_box (operation, "input");

  if (gegl_operation_use_opencl (operation))
    if (cl_process (operation, input, output, roi))
      return TRUE;

  if (o->size_x * o->size_y < SQR (ALLOC_THRESHOLD_SIZE))
    {
      gfloat  background_color[4];
      gfloat *input_buf  = g_new (gfloat,
                                  (CHUNK_SIZE + o->size_x * 2) *
                                  (CHUNK_SIZE + o->size_y * 2) * 4);
      gfloat *output_buf = g_new (gfloat, SQR (CHUNK_SIZE) * 4);
      gint    i, j;

      gegl_color_get_pixel (o->background, babl_format("RaGaBaA float"),
                            background_color);

      for (j = 0; (j-1) * CHUNK_SIZE < roi->height; j++)
        for (i = 0; (i-1) * CHUNK_SIZE < roi->width; i++)
          {
            GeglRectangle chunked_result;
            GeglRectangle chunked_sizes;

            chunked_result = *GEGL_RECTANGLE (roi->x + i * CHUNK_SIZE,
                                              roi->y + j * CHUNK_SIZE,
                                              CHUNK_SIZE, CHUNK_SIZE);

            gegl_rectangle_intersect (&chunked_result, &chunked_result, roi);

            if (chunked_result.width < 1  || chunked_result.height < 1)
              continue;

            src_rect = chunked_result;
            src_rect.x -= op_area->left;
            src_rect.y -= op_area->top;
            src_rect.width += op_area->left + op_area->right;
            src_rect.height += op_area->top + op_area->bottom;

            gegl_buffer_get (input, &src_rect, 1.0, babl_format ("RaGaBaA float"),
                             input_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);

            gegl_rectangle_copy (&chunked_sizes, &chunked_result);
            chunked_sizes.x = 0;
            chunked_sizes.y = 0;

            set_rectangle (output_buf, &chunked_sizes, &chunked_sizes,
                           chunked_result.width, background_color,
                           GEGL_PIXELIZE_NORM_INFINITY);

            pixelize (input_buf, output_buf, &chunked_result, &src_rect,
                      whole_region, o);

            gegl_buffer_set (output, &chunked_result, 0,
                             babl_format ("RaGaBaA float"),
                             output_buf, GEGL_AUTO_ROWSTRIDE);
          }

      g_free (input_buf);
      g_free (output_buf);
    }
  else
    {
      gegl_buffer_set_color (output, roi, o->background);
      pixelize_noalloc (input, output, roi, whole_region, o);
    }

  return  TRUE;
}
Exemplo n.º 22
0
/**
 * gimp_drawable_get_bucket_fill_buffer:
 * @drawable: the #GimpDrawable to edit.
 * @options:
 * @fill_transparent:
 * @fill_criterion:
 * @threshold:
 * @sample_merged:
 * @diagonal_neighbors:
 * @seed_x: X coordinate to start the fill.
 * @seed_y: Y coordinate to start the fill.
 * @mask_buffer: mask of the fill in-progress when in an interactive
 *               filling process. Set to NULL if you need a one-time
 *               fill.
 * @mask_x: returned x bound of @mask_buffer.
 * @mask_y: returned x bound of @mask_buffer.
 * @mask_width: returned width bound of @mask_buffer.
 * @mask_height: returned height bound of @mask_buffer.
 *
 * Creates the fill buffer for a bucket fill operation on @drawable,
 * without actually applying it (if you want to apply it directly as a
 * one-time operation, use gimp_drawable_bucket_fill() instead). If
 * @mask_buffer is not NULL, the intermediate fill mask will also be
 * returned. This fill mask can later be reused in successive calls to
 * gimp_drawable_get_bucket_fill_buffer() for interactive filling.
 *
 * Returns: a fill buffer which can be directly applied to @drawable, or
 *          used in a drawable filter as preview.
 */
GeglBuffer *
gimp_drawable_get_bucket_fill_buffer (GimpDrawable         *drawable,
                                      GimpFillOptions      *options,
                                      gboolean              fill_transparent,
                                      GimpSelectCriterion   fill_criterion,
                                      gdouble               threshold,
                                      gboolean              sample_merged,
                                      gboolean              diagonal_neighbors,
                                      gdouble               seed_x,
                                      gdouble               seed_y,
                                      GeglBuffer          **mask_buffer,
                                      gdouble              *mask_x,
                                      gdouble              *mask_y,
                                      gint                 *mask_width,
                                      gint                 *mask_height)
{
  GimpImage    *image;
  GimpPickable *pickable;
  GeglBuffer   *buffer;
  GeglBuffer   *new_mask;
  gboolean      antialias;
  gint          x, y, width, height;
  gint          mask_offset_x = 0;
  gint          mask_offset_y = 0;
  gint          sel_x, sel_y, sel_width, sel_height;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
  g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL);

  image = gimp_item_get_image (GIMP_ITEM (drawable));

  if (! gimp_item_mask_intersect (GIMP_ITEM (drawable),
                                  &sel_x, &sel_y, &sel_width, &sel_height))
    return NULL;

  if (mask_buffer && *mask_buffer && threshold == 0.0)
    {
      gfloat pixel;

      gegl_buffer_sample (*mask_buffer, seed_x, seed_y, NULL, &pixel,
                          babl_format ("Y float"),
                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);

      if (pixel != 0.0)
        /* Already selected. This seed won't change the selection. */
        return NULL;
    }

  gimp_set_busy (image->gimp);
  if (sample_merged)
    pickable = GIMP_PICKABLE (image);
  else
    pickable = GIMP_PICKABLE (drawable);

  antialias = gimp_fill_options_get_antialias (options);

  /*  Do a seed bucket fill...To do this, calculate a new
   *  contiguous region.
   */
  new_mask = gimp_pickable_contiguous_region_by_seed (pickable,
                                                      antialias,
                                                      threshold,
                                                      fill_transparent,
                                                      fill_criterion,
                                                      diagonal_neighbors,
                                                      (gint) seed_x,
                                                      (gint) seed_y);
  if (mask_buffer && *mask_buffer)
    {
      gimp_gegl_mask_combine_buffer (new_mask, *mask_buffer,
                                     GIMP_CHANNEL_OP_ADD, 0, 0);
      g_object_unref (*mask_buffer);
    }
  if (mask_buffer)
    *mask_buffer = new_mask;

  gimp_gegl_mask_bounds (new_mask, &x, &y, &width, &height);
  width  -= x;
  height -= y;

  /*  If there is a selection, intersect the region bounds
   *  with the selection bounds, to avoid processing areas
   *  that are going to be masked out anyway.  The actual
   *  intersection of the fill region with the mask data
   *  happens when combining the fill buffer, in
   *  gimp_drawable_apply_buffer().
   */
  if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
    {
      gint off_x = 0;
      gint off_y = 0;

      if (sample_merged)
        gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);

      if (! gimp_rectangle_intersect (x, y, width, height,

                                      sel_x + off_x, sel_y + off_y,
                                      sel_width,     sel_height,

                                      &x, &y, &width, &height))
        {
          if (! mask_buffer)
            g_object_unref (new_mask);
          /*  The fill region and the selection are disjoint; bail.  */
          gimp_unset_busy (image->gimp);

          return NULL;
        }
    }

  /*  make sure we handle the mask correctly if it was sample-merged  */
  if (sample_merged)
    {
      GimpItem *item = GIMP_ITEM (drawable);
      gint      off_x, off_y;

      /*  Limit the channel bounds to the drawable's extents  */
      gimp_item_get_offset (item, &off_x, &off_y);

      gimp_rectangle_intersect (x, y, width, height,

                                off_x, off_y,
                                gimp_item_get_width (item),
                                gimp_item_get_height (item),

                                &x, &y, &width, &height);

      mask_offset_x = x;
      mask_offset_y = y;

     /*  translate mask bounds to drawable coords  */
      x -= off_x;
      y -= off_y;
    }
  else
    {
      mask_offset_x = x;
      mask_offset_y = y;
    }

  buffer = gimp_fill_options_create_buffer (options, drawable,
                                            GEGL_RECTANGLE (0, 0,
                                                            width, height),
                                            -x, -y);

  gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask,
                           -mask_offset_x, -mask_offset_y, 1.0);

  if (mask_x)
    *mask_x = x;
  if (mask_y)
    *mask_y = y;
  if (mask_width)
    *mask_width = width;
  if (mask_height)
    *mask_height = height;

  if (! mask_buffer)
    g_object_unref (new_mask);

  gimp_unset_busy (image->gimp);

  return buffer;
}
Exemplo n.º 23
0
Arquivo: gluas.c Projeto: jonnor/gegl
static void
drawable_lua_process (GeglOperation       *op,
                      GeglBuffer          *drawable,
                      GeglBuffer          *aux,
                      GeglBuffer          *result,
                      const GeglRectangle *roi,
                      const gchar         *file,
                      const gchar         *buffer,
                      gdouble              user_value)
{
    /*GimpRGB    background;*/

    GeglRectangle *in_rect = gegl_operation_source_get_bounding_box (GEGL_OPERATION (op),
                                                                       "input");

    lua_State *L;
    Priv p;

    L = luaL_newstate ();
    luaL_openlibs (L);

    register_functions (L, gluas_functions);

    p.rgba_float = babl_format ("RGBA float");
    p.L = L;
    p.width = in_rect->width;
    p.height = in_rect->height;

    p.bx1 = roi->x;
    p.by1 = roi->y;
    p.bx2 = roi->x + roi->width;
    p.by2 = roi->y + roi->height;

    lua_pushnumber (L, (double) user_value);
    lua_setglobal (L, "user_value");
    lua_pushnumber (L, (double) p.width);
    lua_setglobal (L, "width");
    lua_pushnumber (L, (double) p.height);
    lua_setglobal (L, "height");

    lua_pushstring (L, "priv");
    lua_pushlightuserdata (L, &p);
    lua_settable (L, LUA_REGISTRYINDEX);

    p.in_drawable  = drawable;
    p.aux_drawable = aux;
    p.out_drawable = result;

    lua_pushnumber (L, (double) p.bx1);
    lua_setglobal (L, "bound_x0");
    lua_pushnumber (L, (double) p.bx2);
    lua_setglobal (L, "bound_x1");
    lua_pushnumber (L, (double) p.by1);
    lua_setglobal (L, "bound_y0");
    lua_pushnumber (L, (double) p.by2);
    lua_setglobal (L, "bound_y1");

        {
      gint status = 0;

      luaL_loadstring (L, "os.setlocale ('C', 'numeric')");


      /* insert default loop start/end filling the selection */
      if (file && file[0]!='\0')
        status = luaL_loadfile (L, file);
      else if (buffer)
      {
        GString *str = g_string_new (buffer);

        if (!strstr (buffer, "for x"))
        {
          g_string_prepend (str, "for y=bound_y0, bound_y1 do\n for x=bound_x0, bound_x1 do\n");
          g_string_append (str, " end \n progress (y/height)\n end\n");
        }
        status = luaL_loadbuffer (L, str->str, str->len, "buffer");

        g_string_free (str, TRUE);
      }

      if (status == 0)
        status = lua_pcall (L, 0, LUA_MULTRET, 0);

      if (status != 0)
        gegl_node_set (op->node, "error", lua_tostring (L, -1), NULL);
    }
}
Exemplo n.º 24
0
/**
 * gimp_drawable_get_line_art_fill_buffer:
 * @drawable: the #GimpDrawable to edit.
 * @line_art: the #GimpLineArt computed as fill source.
 * @options: the #GimpFillOptions.
 * @sample_merged:
 * @seed_x: X coordinate to start the fill.
 * @seed_y: Y coordinate to start the fill.
 * @mask_buffer: mask of the fill in-progress when in an interactive
 *               filling process. Set to NULL if you need a one-time
 *               fill.
 * @mask_x: returned x bound of @mask_buffer.
 * @mask_y: returned x bound of @mask_buffer.
 * @mask_width: returned width bound of @mask_buffer.
 * @mask_height: returned height bound of @mask_buffer.
 *
 * Creates the fill buffer for a bucket fill operation on @drawable
 * based on @line_art and @options, without actually applying it.
 * If @mask_buffer is not NULL, the intermediate fill mask will also be
 * returned. This fill mask can later be reused in successive calls to
 * gimp_drawable_get_bucket_fill_buffer() for interactive filling.
 *
 * Returns: a fill buffer which can be directly applied to @drawable, or
 *          used in a drawable filter as preview.
 */
GeglBuffer *
gimp_drawable_get_line_art_fill_buffer (GimpDrawable     *drawable,
                                        GimpLineArt      *line_art,
                                        GimpFillOptions  *options,
                                        gboolean          sample_merged,
                                        gdouble           seed_x,
                                        gdouble           seed_y,
                                        GeglBuffer      **mask_buffer,
                                        gdouble          *mask_x,
                                        gdouble          *mask_y,
                                        gint             *mask_width,
                                        gint             *mask_height)
{
  GimpImage  *image;
  GeglBuffer *buffer;
  GeglBuffer *new_mask;
  gint        x, y, width, height;
  gint        mask_offset_x = 0;
  gint        mask_offset_y = 0;
  gint        sel_x, sel_y, sel_width, sel_height;

  g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
  g_return_val_if_fail (GIMP_IS_FILL_OPTIONS (options), NULL);

  image = gimp_item_get_image (GIMP_ITEM (drawable));

  if (! gimp_item_mask_intersect (GIMP_ITEM (drawable),
                                  &sel_x, &sel_y, &sel_width, &sel_height))
    return NULL;

  if (mask_buffer && *mask_buffer)
    {
      gfloat pixel;

      gegl_buffer_sample (*mask_buffer, seed_x, seed_y, NULL, &pixel,
                          babl_format ("Y float"),
                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);

      if (pixel != 0.0)
        /* Already selected. This seed won't change the selection. */
        return NULL;
    }

  gimp_set_busy (image->gimp);

  /*  Do a seed bucket fill...To do this, calculate a new
   *  contiguous region.
   */
  new_mask = gimp_pickable_contiguous_region_by_line_art (NULL, line_art,
                                                          (gint) seed_x,
                                                          (gint) seed_y);
  if (mask_buffer && *mask_buffer)
    {
      gimp_gegl_mask_combine_buffer (new_mask, *mask_buffer,
                                     GIMP_CHANNEL_OP_ADD, 0, 0);
      g_object_unref (*mask_buffer);
    }
  if (mask_buffer)
    *mask_buffer = new_mask;

  gimp_gegl_mask_bounds (new_mask, &x, &y, &width, &height);
  width  -= x;
  height -= y;

  /*  If there is a selection, intersect the region bounds
   *  with the selection bounds, to avoid processing areas
   *  that are going to be masked out anyway.  The actual
   *  intersection of the fill region with the mask data
   *  happens when combining the fill buffer, in
   *  gimp_drawable_apply_buffer().
   */
  if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
    {
      gint off_x = 0;
      gint off_y = 0;

      if (sample_merged)
        gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);

      if (! gimp_rectangle_intersect (x, y, width, height,

                                      sel_x + off_x, sel_y + off_y,
                                      sel_width,     sel_height,

                                      &x, &y, &width, &height))
        {
          if (! mask_buffer)
            g_object_unref (new_mask);
          /*  The fill region and the selection are disjoint; bail.  */
          gimp_unset_busy (image->gimp);

          return NULL;
        }
    }

  /*  make sure we handle the mask correctly if it was sample-merged  */
  if (sample_merged)
    {
      GimpItem *item = GIMP_ITEM (drawable);
      gint      off_x, off_y;

      /*  Limit the channel bounds to the drawable's extents  */
      gimp_item_get_offset (item, &off_x, &off_y);

      gimp_rectangle_intersect (x, y, width, height,

                                off_x, off_y,
                                gimp_item_get_width (item),
                                gimp_item_get_height (item),

                                &x, &y, &width, &height);

      mask_offset_x = x;
      mask_offset_y = y;

     /*  translate mask bounds to drawable coords  */
      x -= off_x;
      y -= off_y;
    }
  else
    {
      mask_offset_x = x;
      mask_offset_y = y;
    }

  buffer = gimp_fill_options_create_buffer (options, drawable,
                                            GEGL_RECTANGLE (0, 0,
                                                            width, height),
                                            -x, -y);

  gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, new_mask,
                           -mask_offset_x, -mask_offset_y, 1.0);

  if (gimp_fill_options_get_antialias (options))
    {
      /* Antialias for the line art algorithm is not applied during mask
       * creation because it is not based on individual pixel colors.
       * Instead we just want to apply it on the borders of the mask at
       * the end (since the mask can evolve, we don't want to actually
       * touch it, but only the intermediate results).
       */
      GeglNode   *graph;
      GeglNode   *input;
      GeglNode   *op;

      graph = gegl_node_new ();
      input = gegl_node_new_child (graph,
                                   "operation", "gegl:buffer-source",
                                   "buffer", buffer,
                                   NULL);
      op  = gegl_node_new_child (graph,
                                 "operation", "gegl:gaussian-blur",
                                 "std-dev-x", 0.5,
                                 "std-dev-y", 0.5,
                                 NULL);
      gegl_node_connect_to (input, "output", op, "input");
      gegl_node_blit_buffer (op, buffer, NULL, 0,
                             GEGL_ABYSS_NONE);
      g_object_unref (graph);
    }

  if (mask_x)
    *mask_x = x;
  if (mask_y)
    *mask_y = y;
  if (mask_width)
    *mask_width = width;
  if (mask_height)
    *mask_height = height;

  if (! mask_buffer)
    g_object_unref (new_mask);

  gimp_unset_busy (image->gimp);

  return buffer;
}
Exemplo n.º 25
0
static void
process_floyd_steinberg (GeglBuffer *input,
                         GeglBuffer *output,
                         const GeglRectangle *result,
                         guint *channel_bits)
{
  GeglRectangle        line_rect;
  guint16             *line_buf;
  gdouble             *error_buf [2];
  guint                channel_mask [4];
  gint                 y;

  line_rect.x      = result->x;
  line_rect.y      = result->y;
  line_rect.width  = result->width;
  line_rect.height = 1;

  line_buf      = g_new  (guint16, line_rect.width * 4);
  error_buf [0] = g_new0 (gdouble, line_rect.width * 4);
  error_buf [1] = g_new0 (gdouble, line_rect.width * 4);

  generate_channel_masks (channel_bits, channel_mask);

  for (y = 0; y < result->height; y++)
  {
    gdouble  *error_buf_swap;
    gint      step;
    gint      start_x;
    gint      end_x;
    gint      x;

    /* Serpentine scanning; reverse direction every row */

    if (y & 1)
    {
      start_x = result->width - 1;
      end_x   = -1;
      step    = -1;
    }
    else
    {
      start_x = 0;
      end_x   = result->width;
      step    = 1;
    }

    /* Pull input row */

    gegl_buffer_get (input, 1.0, &line_rect, babl_format ("RGBA u16"), line_buf, GEGL_AUTO_ROWSTRIDE);

    /* Process the row */

    for (x = start_x; x != end_x; x += step)
    {
      guint16  *pixel = &line_buf [x * 4];
      guint     ch;

      for (ch = 0; ch < 4; ch++)
      {
        gdouble value;
        gdouble value_clamped;
        gdouble quantized;
        gdouble qerror;

        value         = pixel [ch] + error_buf [0] [x * 4 + ch];
        value_clamped = CLAMP (value, 0.0, 65535.0);
        quantized     = quantize_value ((guint) (value_clamped + 0.5), channel_bits [ch], channel_mask [ch]);
        qerror        = value - quantized;

        pixel [ch] = (guint16) quantized;

        /* Distribute the error */

        error_buf [1] [x * 4 + ch] += qerror * 5.0 / 16.0;  /* Down */

        if (x + step >= 0 && x + step < result->width)
        {
          error_buf [0] [(x + step) * 4 + ch] += qerror * 6.0 / 16.0;  /* Ahead */
          error_buf [1] [(x + step) * 4 + ch] += qerror * 1.0 / 16.0;  /* Down, ahead */
        }

        if (x - step >= 0 && x - step < result->width)
        {
          error_buf [1] [(x - step) * 4 + ch] += qerror * 3.0 / 16.0;  /* Down, behind */
        }
      }
    }

    /* Swap error accumulation rows */

    error_buf_swap = error_buf [0];
    error_buf [0]  = error_buf [1];
    error_buf [1]  = error_buf_swap;

    /* Clear error buffer for next-plus-one line */

    memset (error_buf [1], 0, line_rect.width * 4 * sizeof (gdouble));

    /* Push output row */

    gegl_buffer_set (output, &line_rect, babl_format ("RGBA u16"), line_buf, GEGL_AUTO_ROWSTRIDE);
    line_rect.y++;
  }

  g_free (line_buf);
  g_free (error_buf [0]);
  g_free (error_buf [1]);
}
Exemplo n.º 26
0
static void
gimp_brush_clipboard_buffer_changed (Gimp      *gimp,
                                     GimpBrush *brush)
{
  gint width;
  gint height;

  if (brush->mask)
    {
      gimp_temp_buf_unref (brush->mask);
      brush->mask = NULL;
    }

  if (brush->pixmap)
    {
      gimp_temp_buf_unref (brush->pixmap);
      brush->pixmap = NULL;
    }

  if (gimp->global_buffer)
    {
      GeglBuffer *buffer = gimp_buffer_get_buffer (gimp->global_buffer);
      const Babl *format = gegl_buffer_get_format (buffer);
      GeglBuffer *dest_buffer;

      width  = MIN (gimp_buffer_get_width  (gimp->global_buffer), 512);
      height = MIN (gimp_buffer_get_height (gimp->global_buffer), 512);

      brush->mask   = gimp_temp_buf_new (width, height,
                                         babl_format ("Y u8"));
      brush->pixmap = gimp_temp_buf_new (width, height,
                                         babl_format ("R'G'B' u8"));

      /*  copy the alpha channel into the brush's mask  */
      if (babl_format_has_alpha (format))
        {
          dest_buffer = gimp_temp_buf_create_buffer (brush->mask);

          gegl_buffer_set_format (dest_buffer, babl_format ("A u8"));
          gegl_buffer_copy (buffer, NULL, dest_buffer, NULL);

          g_object_unref (dest_buffer);
        }
      else
        {
          memset (gimp_temp_buf_get_data (brush->mask), 255,
                  width * height);
        }

      /*  copy the color channels into the brush's pixmap  */
      dest_buffer = gimp_temp_buf_create_buffer (brush->pixmap);

      gegl_buffer_copy (buffer, NULL, dest_buffer, NULL);

      g_object_unref (dest_buffer);
    }
  else
    {
      width  = 17;
      height = 17;

      brush->mask = gimp_temp_buf_new (width, height, babl_format ("Y u8"));
      gimp_temp_buf_data_clear (brush->mask);
    }

  brush->x_axis.x = width / 2;
  brush->x_axis.y = 0;
  brush->y_axis.x = 0;
  brush->y_axis.y = height / 2;

  gimp_data_dirty (GIMP_DATA (brush));
}
Exemplo n.º 27
0
Arquivo: ui.c Projeto: imgflo/imgflo
static void
process_image_callback (SoupServer *server, SoupMessage *msg,
		 const char *path, GHashTable *query,
		 SoupClientContext *context, gpointer user_data) {

    UiConnection *self = (UiConnection *)user_data;

    // Lookup network
    Network *network = NULL;
    {
        const gchar *graph_id = g_hash_table_lookup(query, "graph");
        network = (graph_id) ? g_hash_table_lookup(self->network_map, graph_id) : NULL;
    }
    if (!network) {
        soup_message_set_status_full(msg, SOUP_STATUS_BAD_REQUEST, "'graph' not specified or wrong");
        return;
    }

    // Lookup node
    GeglNode * node = NULL;
    Processor *processor = NULL;
    {
        const gchar *node_id = g_hash_table_lookup(query, "node");
        processor = (node_id) ? network_processor(network, node_id) : NULL;
        if (processor) {
            node = processor->node;
        } else {
            node = graph_get_gegl_node(network->graph, node_id);
        }

    }
    if (!processor && !node) {
        soup_message_set_status_full(msg, SOUP_STATUS_BAD_REQUEST, "'node' not specified or wrong");
        return;
    }

    // Render output
    // FIXME: allow region-of-interest and scale as query params

    const Babl *format = babl_format("R'G'B'A u8");
    GeglRectangle roi = { 0, 0, 300, 300 };
    gchar *rgba = (processor) ?
                processor_blit(processor, format, &roi) :
                blit_node_preview(node, format, &roi);
    if (!rgba) {
        soup_message_set_status(msg, SOUP_STATUS_BAD_REQUEST);
        return;
    }
    if (!(roi.width > 0 && roi.height > 0)) {
        soup_message_set_status(msg, SOUP_STATUS_BAD_REQUEST);
        g_free(rgba);
        return;
    }

    // Compress to PNG
    {
        PngEncoder *encoder = png_encoder_new();
        png_encoder_encode_rgba(encoder, roi.width, roi.height, rgba);
        char *png = encoder->buffer;
        const size_t len = encoder->size;
        soup_message_set_status(msg, SOUP_STATUS_OK);
        soup_message_set_response(msg, "image/png", SOUP_MEMORY_COPY, png, len);
        png_encoder_free(encoder);
    }
}
Exemplo n.º 28
0
const GimpBoundSeg *
floating_sel_boundary (GimpLayer *layer,
                       gint      *n_segs)
{
  g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL);
  g_return_val_if_fail (gimp_layer_is_floating_sel (layer), NULL);
  g_return_val_if_fail (n_segs != NULL, NULL);

  if (layer->fs.boundary_known == FALSE)
    {
      gint width, height;
      gint off_x, off_y;

      width  = gimp_item_get_width  (GIMP_ITEM (layer));
      height = gimp_item_get_height (GIMP_ITEM (layer));
      gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);

      if (layer->fs.segs)
        g_free (layer->fs.segs);

      if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))
        {
          GeglBuffer *buffer;
          gint        i;

          /*  find the segments  */
          buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));

          layer->fs.segs = gimp_boundary_find (buffer, NULL,
                                               babl_format ("A float"),
                                               GIMP_BOUNDARY_WITHIN_BOUNDS,
                                               0, 0, width, height,
                                               GIMP_BOUNDARY_HALF_WAY,
                                               &layer->fs.num_segs);

          /*  offset the segments  */
          for (i = 0; i < layer->fs.num_segs; i++)
            {
              layer->fs.segs[i].x1 += off_x;
              layer->fs.segs[i].y1 += off_y;
              layer->fs.segs[i].x2 += off_x;
              layer->fs.segs[i].y2 += off_y;
            }
        }
      else
        {
          layer->fs.num_segs = 4;
          layer->fs.segs     = g_new0 (GimpBoundSeg, 4);

          /* top */
          layer->fs.segs[0].x1 = off_x;
          layer->fs.segs[0].y1 = off_y;
          layer->fs.segs[0].x2 = off_x + width;
          layer->fs.segs[0].y2 = off_y;

          /* left */
          layer->fs.segs[1].x1 = off_x;
          layer->fs.segs[1].y1 = off_y;
          layer->fs.segs[1].x2 = off_x;
          layer->fs.segs[1].y2 = off_y + height;

          /* right */
          layer->fs.segs[2].x1 = off_x + width;
          layer->fs.segs[2].y1 = off_y;
          layer->fs.segs[2].x2 = off_x + width;
          layer->fs.segs[2].y2 = off_y + height;

          /* bottom */
          layer->fs.segs[3].x1 = off_x;
          layer->fs.segs[3].y1 = off_y + height;
          layer->fs.segs[3].x2 = off_x + width;
          layer->fs.segs[3].y2 = off_y + height;
        }

      layer->fs.boundary_known = TRUE;
    }

  *n_segs = layer->fs.num_segs;

  return layer->fs.segs;
}
Exemplo n.º 29
0
static int
gimp_mypaint_surface_draw_dab (MyPaintSurface *base_surface,
                               float           x,
                               float           y,
                               float           radius,
                               float           color_r,
                               float           color_g,
                               float           color_b,
                               float           opaque,
                               float           hardness,
                               float           color_a,
                               float           aspect_ratio,
                               float           angle,
                               float           lock_alpha,
                               float           colorize)
{
  GimpMybrushSurface *surface = (GimpMybrushSurface *)base_surface;
  GeglBufferIterator *iter;
  GeglRectangle       dabRect;
  GimpComponentMask   component_mask = surface->component_mask;

  const float one_over_radius2 = 1.0f / (radius * radius);
  const double angle_rad = angle / 360 * 2 * M_PI;
  const float cs = cos(angle_rad);
  const float sn = sin(angle_rad);
  float normal_mode;
  float segment1_slope;
  float segment2_slope;
  float r_aa_start;

  hardness = CLAMP (hardness, 0.0f, 1.0f);
  segment1_slope = -(1.0f / hardness - 1.0f);
  segment2_slope = -hardness / (1.0f - hardness);
  aspect_ratio = MAX (1.0f, aspect_ratio);

  r_aa_start = radius - 1.0f;
  r_aa_start = MAX (r_aa_start, 0);
  r_aa_start = (r_aa_start * r_aa_start) / aspect_ratio;

  normal_mode = opaque * (1.0f - colorize);
  colorize = opaque * colorize;

  /* FIXME: This should use the real matrix values to trim aspect_ratio dabs */
  dabRect = calculate_dab_roi (x, y, radius);
  gegl_rectangle_intersect (&dabRect, &dabRect, gegl_buffer_get_extent (surface->buffer));

  if (dabRect.width <= 0 || dabRect.height <= 0)
    return 0;

  gegl_rectangle_bounding_box (&surface->dirty, &surface->dirty, &dabRect);

  iter = gegl_buffer_iterator_new (surface->buffer, &dabRect, 0,
                                   babl_format ("R'G'B'A float"),
                                   GEGL_BUFFER_READWRITE,
                                   GEGL_ABYSS_NONE);
  if (surface->paint_mask)
    {
      GeglRectangle mask_roi = dabRect;
      mask_roi.x -= surface->paint_mask_x;
      mask_roi.y -= surface->paint_mask_y;
      gegl_buffer_iterator_add (iter, surface->paint_mask, &mask_roi, 0,
                                babl_format ("Y float"),
                                GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
    }

  while (gegl_buffer_iterator_next (iter))
    {
      float *pixel = (float *)iter->data[0];
      float *mask;
      int iy, ix;

      if (surface->paint_mask)
        mask = iter->data[1];
      else
        mask = NULL;

      for (iy = iter->roi[0].y; iy < iter->roi[0].y + iter->roi[0].height; iy++)
        {
          for (ix = iter->roi[0].x; ix < iter->roi[0].x +  iter->roi[0].width; ix++)
            {
              float rr, base_alpha, alpha, dst_alpha, r, g, b, a;
              if (radius < 3.0f)
                rr = calculate_rr_antialiased (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2, r_aa_start);
              else
                rr = calculate_rr (ix, iy, x, y, aspect_ratio, sn, cs, one_over_radius2);
              base_alpha = calculate_alpha_for_rr (rr, hardness, segment1_slope, segment2_slope);
              alpha = base_alpha * normal_mode;
              if (mask)
                alpha *= *mask;
              dst_alpha = pixel[ALPHA];
              /* a = alpha * color_a + dst_alpha * (1.0f - alpha);
               * which converts to: */
              a = alpha * (color_a - dst_alpha) + dst_alpha;
              r = pixel[RED];
              g = pixel[GREEN];
              b = pixel[BLUE];

              if (a > 0.0f)
                {
                  /* By definition the ratio between each color[] and pixel[] component in a non-pre-multipled blend always sums to 1.0f.
                   * Originaly this would have been "(color[n] * alpha * color_a + pixel[n] * dst_alpha * (1.0f - alpha)) / a",
                   * instead we only calculate the cheaper term. */
                  float src_term = (alpha * color_a) / a;
                  float dst_term = 1.0f - src_term;
                  r = color_r * src_term + r * dst_term;
                  g = color_g * src_term + g * dst_term;
                  b = color_b * src_term + b * dst_term;
                }

              if (colorize > 0.0f && base_alpha > 0.0f)
                {
                  alpha = base_alpha * colorize;
                  a = alpha + dst_alpha - alpha * dst_alpha;
                  if (a > 0.0f)
                    {
                      GimpHSL pixel_hsl, out_hsl;
                      GimpRGB pixel_rgb = {color_r, color_g, color_b};
                      GimpRGB out_rgb   = {r, g, b};
                      float src_term = alpha / a;
                      float dst_term = 1.0f - src_term;

                      gimp_rgb_to_hsl (&pixel_rgb, &pixel_hsl);
                      gimp_rgb_to_hsl (&out_rgb, &out_hsl);

                      out_hsl.h = pixel_hsl.h;
                      out_hsl.s = pixel_hsl.s;
                      gimp_hsl_to_rgb (&out_hsl, &out_rgb);

                      r = (float)out_rgb.r * src_term + r * dst_term;
                      g = (float)out_rgb.g * src_term + g * dst_term;
                      b = (float)out_rgb.b * src_term + b * dst_term;
                    }
                }

              if (component_mask != GIMP_COMPONENT_MASK_ALL)
                {
                  if (component_mask & GIMP_COMPONENT_MASK_RED)
                    pixel[RED]   = r;
                  if (component_mask & GIMP_COMPONENT_MASK_GREEN)
                    pixel[GREEN] = g;
                  if (component_mask & GIMP_COMPONENT_MASK_BLUE)
                    pixel[BLUE]  = b;
                  if (component_mask & GIMP_COMPONENT_MASK_ALPHA)
                    pixel[ALPHA] = a;
                }
              else
                {
                  pixel[RED]   = r;
                  pixel[GREEN] = g;
                  pixel[BLUE]  = b;
                  pixel[ALPHA] = a;
                }

              pixel += 4;
              if (mask)
                mask += 1;
            }
        }
    }

  return 1;
}
Exemplo n.º 30
0
static void
bilateral_filter (GeglBuffer          *src,
                  const GeglRectangle *src_rect,
                  GeglBuffer          *dst,
                  const GeglRectangle *dst_rect,
                  gint                 s_sigma,
                  gfloat               r_sigma)
{
  gint c, x, y, z, k, i;

  const gint padding_xy = 2;
  const gint padding_z  = 2;

  const gint width    = src_rect->width;
  const gint height   = src_rect->height;
  const gint channels = 4;

  const gint sw = (width -1) / s_sigma + 1 + (2 * padding_xy);
  const gint sh = (height-1) / s_sigma + 1 + (2 * padding_xy);
  const gint depth = (int)(1.0f / r_sigma) + 1 + (2 * padding_z);

  /* down-sampling */

  gfloat *grid     = g_new0 (gfloat, sw * sh * depth * channels * 2);
  gfloat *blurx    = g_new0 (gfloat, sw * sh * depth * channels * 2);
  gfloat *blury    = g_new0 (gfloat, sw * sh * depth * channels * 2);
  gfloat *blurz    = g_new0 (gfloat, sw * sh * depth * channels * 2);

  gfloat *input    = g_new0 (gfloat, width * height * channels);
  gfloat *smoothed = g_new0 (gfloat, width * height * channels);

  #define INPUT(x,y,c) input[c+channels*(x + width * y)]

  #define  GRID(x,y,z,c,i) grid [i+2*(c+channels*(x+sw*(y+z*sh)))]
  #define BLURX(x,y,z,c,i) blurx[i+2*(c+channels*(x+sw*(y+z*sh)))]
  #define BLURY(x,y,z,c,i) blury[i+2*(c+channels*(x+sw*(y+z*sh)))]
  #define BLURZ(x,y,z,c,i) blurz[i+2*(c+channels*(x+sw*(y+z*sh)))]

  gegl_buffer_get (src, src_rect, 1.0, babl_format ("RGBA float"), input, GEGL_AUTO_ROWSTRIDE,
                   GEGL_ABYSS_NONE);

  for (k=0; k < (sw * sh * depth * channels * 2); k++)
    {
      grid [k] = 0.0f;
      blurx[k] = 0.0f;
      blury[k] = 0.0f;
      blurz[k] = 0.0f;
    }

#if 0
  /* in case we want to normalize the color space */

  gfloat input_min[4] = { FLT_MAX,  FLT_MAX,  FLT_MAX};
  gfloat input_max[4] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};

  for(y = 0; y < height; y++)
    for(x = 0; x < width; x++)
      for(c = 0; c < channels; c++)
        {
          input_min[c] = MIN(input_min[c], INPUT(x,y,c));
          input_max[c] = MAX(input_max[c], INPUT(x,y,c));
        }
#endif

  /* downsampling */

  for(y = 0; y < height; y++)
    for(x = 0; x < width; x++)
      for(c = 0; c < channels; c++)
        {
          const float z = INPUT(x,y,c); // - input_min[c];

          const int small_x = (int)((float)(x) / s_sigma + 0.5f) + padding_xy;
          const int small_y = (int)((float)(y) / s_sigma + 0.5f) + padding_xy;
          const int small_z = (int)((float)(z) / r_sigma + 0.5f) + padding_z;

          g_assert(small_x >= 0 && small_x < sw);
          g_assert(small_y >= 0 && small_y < sh);
          g_assert(small_z >= 0 && small_z < depth);

          GRID(small_x, small_y, small_z, c, 0) += INPUT(x, y, c);
          GRID(small_x, small_y, small_z, c, 1) += 1.0f;
        }

  /* blur in x, y and z */
  /* XXX: we could use less memory, but at expense of code readability */

  for (z = 1; z < depth-1; z++)
    for (y = 1; y < sh-1; y++)
      for (x = 1; x < sw-1; x++)
        for(c = 0; c < channels; c++)
          for (i=0; i<2; i++)
            BLURX(x, y, z, c, i) = (GRID (x-1, y, z, c, i) + 2.0f * GRID (x, y, z, c, i) + GRID (x+1, y, z, c, i)) / 4.0f;

  for (z = 1; z < depth-1; z++)
    for (y = 1; y < sh-1; y++)
      for (x = 1; x < sw-1; x++)
        for(c = 0; c < channels; c++)
          for (i=0; i<2; i++)
            BLURY(x, y, z, c, i) = (BLURX (x, y-1, z, c, i) + 2.0f * BLURX (x, y, z, c, i) + BLURX (x, y+1, z, c, i)) / 4.0f;

  for (z = 1; z < depth-1; z++)
    for (y = 1; y < sh-1; y++)
      for (x = 1; x < sw-1; x++)
        for(c = 0; c < channels; c++)
          for (i=0; i<2; i++)
            BLURZ(x, y, z, c, i) = (BLURY (x, y-1, z, c, i) + 2.0f * BLURY (x, y, z, c, i) + BLURY (x, y+1, z, c, i)) / 4.0f;

  /* trilinear filtering */

  for (y=0; y < height; y++)
    for (x=0; x < width; x++)
      for(c = 0; c < channels; c++)
        {
          float xf = (float)(x) / s_sigma + padding_xy;
          float yf = (float)(y) / s_sigma + padding_xy;
          float zf = INPUT(x,y,c) / r_sigma + padding_z;

          int x1 = CLAMP((int)xf, 0, sw-1);
          int y1 = CLAMP((int)yf, 0, sh-1);
          int z1 = CLAMP((int)zf, 0, depth-1);

          int x2 = CLAMP(x1+1, 0, sw-1);
          int y2 = CLAMP(y1+1, 0, sh-1);
          int z2 = CLAMP(z1+1, 0, depth-1);

          float x_alpha = xf - x1;
          float y_alpha = yf - y1;
          float z_alpha = zf - z1;

          float interpolated[2];

          g_assert(xf >= 0 && xf < sw);
          g_assert(yf >= 0 && yf < sh);
          g_assert(zf >= 0 && zf < depth);

          for (i=0; i<2; i++)
              interpolated[i] =
              lerp(lerp(lerp(BLURZ(x1, y1, z1, c, i), BLURZ(x2, y1, z1, c, i), x_alpha),
                        lerp(BLURZ(x1, y2, z1, c, i), BLURZ(x2, y2, z1, c, i), x_alpha), y_alpha),
                   lerp(lerp(BLURZ(x1, y1, z2, c, i), BLURZ(x2, y1, z2, c, i), x_alpha),
                        lerp(BLURZ(x1, y2, z2, c, i), BLURZ(x2, y2, z2, c, i), x_alpha), y_alpha), z_alpha);

          smoothed[channels*(y*width+x)+c] = interpolated[0] / interpolated[1];
        }

  gegl_buffer_set (dst, dst_rect, 0, babl_format ("RGBA float"), smoothed,
                   GEGL_AUTO_ROWSTRIDE);

  g_free (grid);
  g_free (blurx);
  g_free (blury);
  g_free (blurz);
  g_free (input);
  g_free (smoothed);
}