예제 #1
0
/**
 * Process the gegl filter
 * @param operation the given Gegl operation
 * @param input the input buffer.
 * @param output the output buffer.
 * @param result the region of interest.
 * @param level the level of detail
 * @return True, if the filter was successfull applied.
 */
static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *input,
         GeglBuffer          *output,
         const GeglRectangle *result,
         gint                 level)
{
  GeglProperties         *o = GEGL_PROPERTIES (operation);
  const Babl  *input_format = gegl_buffer_get_format (input);
  const int bytes_per_pixel = babl_format_get_bytes_per_pixel (input_format);

  /**
   * src_buf, dst_buf:
   * Input- and output-buffers, containing the whole selection,
   * the filter should be applied on.
   *
   * src_pixel, dst_pixel:
   * pointers to the current source- and destination-pixel.
   * Using these pointers, the reading and writing can be delayed till the
   * end of the loop. Hence src_pixel can also point to background_color if
   * necessary.
   */
  guint8    *src_buf, *dst_buf, *src_pixel, *dst_pixel, background_color[bytes_per_pixel];

  /**
   * (x, y): Position of the pixel we compute at the moment.
   * (width, height): Dimensions of the lens
   * pixel_offset: For calculating the pixels position in src_buf / dst_buf.
   */
  gint         x, y,
               width, height,
               pixel_offset, projected_pixel_offset;

  /**
   * Further parameters that are needed to calculate the position of a projected
   * further pixel at (x, y).
   */
  gfloat       a, b, c,
               asqr, bsqr, csqr,
               dx, dy, dysqr,
               projected_x, projected_y,
               refraction_index;

  gboolean     keep_surroundings;

  width = result->width;
  height = result->height;

  refraction_index = o->refraction_index;
  keep_surroundings = o->keep_surroundings;
  gegl_color_get_pixel (o->background_color, input_format, background_color);

  a = 0.5 * width;
  b = 0.5 * height;
  c = MIN (a, b);
  asqr = a * a;
  bsqr = b * b;
  csqr = c * c;

  /**
   * Todo: We might want to change the buffers sizes, as the memory consumption
   * could be rather large.However, due to the lens, it might happen, that one pixel from the
   * images center gets stretched to the whole image, so we will still need
   * at least a src_buf of size (width/2) * (height/2) * 4 to be able to
   * process one quarter of the image.
   */
  src_buf = gegl_malloc (width * height * bytes_per_pixel);
  dst_buf = gegl_malloc (width * height * bytes_per_pixel);

  gegl_buffer_get (input, result, 1.0, input_format, src_buf,
                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);

  for (y = 0; y < height; y++)
    {
      /* dy is the current pixels distance (in y-direction) of the lenses center */
      dy = -((gfloat)y - b + 0.5);

      /**
       * To determine, whether the pixel is within the elliptical region affected
       * by the lens, we furthermore need the squared distance. So we calculate it
       * once for each row.
       */
      dysqr = dy * dy;
      for (x = 0; x < width; x++)
        {
          /* Again we need to calculate the pixels distance from the lens dx. */
          dx = (gfloat)x - a + 0.5;

          /* Given x and y we can now determine the pixels offset in our buffer. */
          pixel_offset = (x + y * width) * bytes_per_pixel;

          /* As described above, we only read and write image data once. */
          dst_pixel = &dst_buf[pixel_offset];

          if (dysqr < (bsqr - (bsqr * dx * dx) / asqr))
            {
              /**
               * If (x, y) is inside the affected region, we can find its projected
               * position, calculate the projected_pixel_offset and set the src_pixel
               * to where we want to copy the pixel-data from.
               */
              find_projected_pos (asqr, bsqr, csqr, dx, dy, refraction_index,
                                  &projected_x, &projected_y);

              projected_pixel_offset = ( (gint)(-projected_y + b) * width +
                                         (gint)( projected_x + a) ) * bytes_per_pixel;

              src_pixel = &src_buf[projected_pixel_offset];
            }
          else
            {
              /**
               * Otherwise (that is for pixels outside the lens), we could either leave
               * the image data unchanged, or set it to a specified 'background_color',
               * depending on the user input.
               */
              if (keep_surroundings)
                src_pixel = &src_buf[pixel_offset];
              else
                src_pixel = background_color;
            }

          /**
           * At the end, we can copy the src_pixel (which was determined above), to
           * dst_pixel.
           */
          memcpy (dst_pixel, src_pixel, bytes_per_pixel);
        }
  }

  gegl_buffer_set (output, result, 0, gegl_buffer_get_format (output), dst_buf,
                   GEGL_AUTO_ROWSTRIDE);

  gegl_free (dst_buf);
  gegl_free (src_buf);

  return TRUE;
}
예제 #2
0
static void
drawlens (GimpDrawable *drawable,
          GimpPreview  *preview)
{
  GimpImageType  drawtype = gimp_drawable_type (drawable->drawable_id);
  GimpPixelRgn   srcPR, destPR;
  gint           width, height;
  gint           bytes;
  gint           row;
  gint           x1, y1, x2, y2;
  guchar        *src, *dest;
  gint           i, col;
  gfloat         regionwidth, regionheight, dx, dy, xsqr, ysqr;
  gfloat         a, b, c, asqr, bsqr, csqr, x, y;
  glong          pixelpos, pos;
  GimpRGB        background;
  guchar         bgr_red, bgr_blue, bgr_green;
  guchar         alphaval;

  gimp_context_get_background (&background);
  gimp_rgb_get_uchar (&background,
                      &bgr_red, &bgr_green, &bgr_blue);

  bytes = drawable->bpp;

  if (preview)
    {
      gimp_preview_get_position (preview, &x1, &y1);
      gimp_preview_get_size (preview, &width, &height);
      x2 = x1 + width;
      y2 = y1 + height;
      src = gimp_drawable_get_thumbnail_data (drawable->drawable_id,
                                              &width, &height, &bytes);
      regionwidth  = width;
      regionheight = height;
    }
  else
    {
      gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
      regionwidth = x2 - x1;
      regionheight = y2 - y1;
      width = drawable->width;
      height = drawable->height;
      gimp_pixel_rgn_init (&srcPR, drawable,
                           0, 0, width, height, FALSE, FALSE);
      gimp_pixel_rgn_init (&destPR, drawable,
                           0, 0, width, height, TRUE, TRUE);

      src  = g_new (guchar, regionwidth * regionheight * bytes);
      gimp_pixel_rgn_get_rect (&srcPR, src,
                               x1, y1, regionwidth, regionheight);
    }

  dest = g_new (guchar, regionwidth * regionheight * bytes);

  a = regionwidth / 2;
  b = regionheight / 2;

  c = MIN (a, b);

  asqr = a * a;
  bsqr = b * b;
  csqr = c * c;

  for (col = 0; col < regionwidth; col++)
    {
      dx = (gfloat) col - a + 0.5;
      xsqr = dx * dx;
      for (row = 0; row < regionheight; row++)
        {
          pixelpos = (col + row * regionwidth) * bytes;
          dy = -((gfloat) row - b) - 0.5;
          ysqr = dy * dy;
          if (ysqr < (bsqr - (bsqr * xsqr) / asqr))
            {
              find_projected_pos (asqr, bsqr, csqr, dx, dy, &x, &y);
              y = -y;
              pos = ((gint) (y + b) * regionwidth + (gint) (x + a)) * bytes;

              for (i = 0; i < bytes; i++)
                {
                  dest[pixelpos + i] = src[pos + i];
                }
            }
          else
            {
              if (lvals.keep_surr)
                {
                  for (i = 0; i < bytes; i++)
                    {
                      dest[pixelpos + i] = src[pixelpos + i];
                    }
                }
              else
                {
                  if (lvals.set_transparent)
                    alphaval = 0;
                  else
                    alphaval = 255;

                  switch (drawtype)
                    {
                    case GIMP_INDEXEDA_IMAGE:
                      dest[pixelpos + 1] = alphaval;
                    case GIMP_INDEXED_IMAGE:
                      dest[pixelpos + 0] = 0;
                      break;

                    case GIMP_RGBA_IMAGE:
                      dest[pixelpos + 3] = alphaval;
                    case GIMP_RGB_IMAGE:
                      dest[pixelpos + 0] = bgr_red;
                      dest[pixelpos + 1] = bgr_green;
                      dest[pixelpos + 2] = bgr_blue;
                      break;

                    case GIMP_GRAYA_IMAGE:
                      dest[pixelpos + 1] = alphaval;
                    case GIMP_GRAY_IMAGE:
                      dest[pixelpos+0] = bgr_red;
                      break;
                    }
                }
            }
        }

      if (!preview)
        {
          if (((gint) (regionwidth-col) % 5) == 0)
            gimp_progress_update ((gdouble) col / (gdouble) regionwidth);
        }
    }

  if (preview)
    {
      gimp_preview_draw_buffer (preview, dest, bytes * regionwidth);
    }
  else
    {
      gimp_progress_update (1.0);
      gimp_pixel_rgn_set_rect (&destPR, dest, x1, y1,
                               regionwidth, regionheight);

      gimp_drawable_flush (drawable);
      gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
      gimp_drawable_update (drawable->drawable_id, x1, y1, x2 - x1, y2 - y1);
    }

  g_free (src);
  g_free (dest);
}