/** * 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; }
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); }