GeglBuffer * gimp_drawable_transform_buffer_flip (GimpDrawable *drawable, GimpContext *context, GeglBuffer *orig_buffer, gint orig_offset_x, gint orig_offset_y, GimpOrientationType flip_type, gdouble axis, gboolean clip_result, GimpColorProfile **buffer_profile, gint *new_offset_x, gint *new_offset_y) { const Babl *format; GeglBuffer *new_buffer; GeglBufferIterator *iter; GeglRectangle src_rect; GeglRectangle dest_rect; gint bpp; gint orig_x, orig_y; gint orig_width, orig_height; gint new_x, new_y; gint new_width, new_height; gint x, y; 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_CONTEXT (context), NULL); g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL); g_return_val_if_fail (buffer_profile != NULL, NULL); g_return_val_if_fail (new_offset_x != NULL, NULL); g_return_val_if_fail (new_offset_y != NULL, NULL); *buffer_profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable)); orig_x = orig_offset_x; orig_y = orig_offset_y; orig_width = gegl_buffer_get_width (orig_buffer); orig_height = gegl_buffer_get_height (orig_buffer); new_x = orig_x; new_y = orig_y; new_width = orig_width; new_height = orig_height; switch (flip_type) { case GIMP_ORIENTATION_HORIZONTAL: new_x = RINT (-((gdouble) orig_x + (gdouble) orig_width - axis) + axis); break; case GIMP_ORIENTATION_VERTICAL: new_y = RINT (-((gdouble) orig_y + (gdouble) orig_height - axis) + axis); break; case GIMP_ORIENTATION_UNKNOWN: g_return_val_if_reached (NULL); break; } format = gegl_buffer_get_format (orig_buffer); bpp = babl_format_get_bytes_per_pixel (format); new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, new_width, new_height), format); if (clip_result && (new_x != orig_x || new_y != orig_y)) { GimpRGB bg; GeglColor *color; gint clip_x, clip_y; gint clip_width, clip_height; *new_offset_x = orig_x; *new_offset_y = orig_y; /* Use transparency, rather than the bg color, as the "outside" color of * channels, and drawables with an alpha channel. */ if (GIMP_IS_CHANNEL (drawable) || babl_format_has_alpha (format)) { gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0); } else { gimp_context_get_background (context, &bg); gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), &bg, &bg); } color = gimp_gegl_color_new (&bg, gimp_drawable_get_space (drawable)); gegl_buffer_set_color (new_buffer, NULL, color); g_object_unref (color); if (gimp_rectangle_intersect (orig_x, orig_y, orig_width, orig_height, new_x, new_y, new_width, new_height, &clip_x, &clip_y, &clip_width, &clip_height)) { orig_x = new_x = clip_x - orig_x; orig_y = new_y = clip_y - orig_y; } orig_width = new_width = clip_width; orig_height = new_height = clip_height; } else { *new_offset_x = new_x; *new_offset_y = new_y; orig_x = 0; orig_y = 0; new_x = 0; new_y = 0; } if (new_width == 0 && new_height == 0) return new_buffer; dest_rect.x = new_x; dest_rect.y = new_y; dest_rect.width = new_width; dest_rect.height = new_height; iter = gegl_buffer_iterator_new (new_buffer, &dest_rect, 0, NULL, GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE, 1); switch (flip_type) { case GIMP_ORIENTATION_HORIZONTAL: while (gegl_buffer_iterator_next (iter)) { gint stride = iter->items[0].roi.width * bpp; src_rect = iter->items[0].roi; src_rect.x = (orig_x + orig_width) - (iter->items[0].roi.x - dest_rect.x) - iter->items[0].roi.width; gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, iter->items[0].data, stride, GEGL_ABYSS_NONE); for (y = 0; y < iter->items[0].roi.height; y++) { guint8 *left = iter->items[0].data; guint8 *right = iter->items[0].data; left += y * stride; right += y * stride + (iter->items[0].roi.width - 1) * bpp; for (x = 0; x < iter->items[0].roi.width / 2; x++) { guint8 temp[bpp]; memcpy (temp, left, bpp); memcpy (left, right, bpp); memcpy (right, temp, bpp); left += bpp; right -= bpp; } } } break; case GIMP_ORIENTATION_VERTICAL: while (gegl_buffer_iterator_next (iter)) { gint stride = iter->items[0].roi.width * bpp; src_rect = iter->items[0].roi; src_rect.y = (orig_y + orig_height) - (iter->items[0].roi.y - dest_rect.y) - iter->items[0].roi.height; gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, iter->items[0].data, stride, GEGL_ABYSS_NONE); for (x = 0; x < iter->items[0].roi.width; x++) { guint8 *top = iter->items[0].data; guint8 *bottom = iter->items[0].data; top += x * bpp; bottom += x * bpp + (iter->items[0].roi.height - 1) * stride; for (y = 0; y < iter->items[0].roi.height / 2; y++) { guint8 temp[bpp]; memcpy (temp, top, bpp); memcpy (top, bottom, bpp); memcpy (bottom, temp, bpp); top += stride; bottom -= stride; } } } break; case GIMP_ORIENTATION_UNKNOWN: gegl_buffer_iterator_stop (iter); break; } return new_buffer; }
GeglBuffer * gimp_drawable_transform_buffer_rotate (GimpDrawable *drawable, GimpContext *context, GeglBuffer *orig_buffer, gint orig_offset_x, gint orig_offset_y, GimpRotationType rotate_type, gdouble center_x, gdouble center_y, gboolean clip_result, GimpColorProfile **buffer_profile, gint *new_offset_x, gint *new_offset_y) { const Babl *format; GeglBuffer *new_buffer; GeglRectangle src_rect; GeglRectangle dest_rect; gint orig_x, orig_y; gint orig_width, orig_height; gint orig_bpp; gint new_x, new_y; gint new_width, new_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_CONTEXT (context), NULL); g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL); g_return_val_if_fail (buffer_profile != NULL, NULL); g_return_val_if_fail (new_offset_x != NULL, NULL); g_return_val_if_fail (new_offset_y != NULL, NULL); *buffer_profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (drawable)); orig_x = orig_offset_x; orig_y = orig_offset_y; orig_width = gegl_buffer_get_width (orig_buffer); orig_height = gegl_buffer_get_height (orig_buffer); orig_bpp = babl_format_get_bytes_per_pixel (gegl_buffer_get_format (orig_buffer)); switch (rotate_type) { case GIMP_ROTATE_90: gimp_drawable_transform_rotate_point (orig_x, orig_y + orig_height, rotate_type, center_x, center_y, &new_x, &new_y); new_width = orig_height; new_height = orig_width; break; case GIMP_ROTATE_180: gimp_drawable_transform_rotate_point (orig_x + orig_width, orig_y + orig_height, rotate_type, center_x, center_y, &new_x, &new_y); new_width = orig_width; new_height = orig_height; break; case GIMP_ROTATE_270: gimp_drawable_transform_rotate_point (orig_x + orig_width, orig_y, rotate_type, center_x, center_y, &new_x, &new_y); new_width = orig_height; new_height = orig_width; break; default: g_return_val_if_reached (NULL); break; } format = gegl_buffer_get_format (orig_buffer); if (clip_result && (new_x != orig_x || new_y != orig_y || new_width != orig_width || new_height != orig_height)) { GimpRGB bg; GeglColor *color; gint clip_x, clip_y; gint clip_width, clip_height; new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, orig_width, orig_height), format); *new_offset_x = orig_x; *new_offset_y = orig_y; /* Use transparency, rather than the bg color, as the "outside" color of * channels, and drawables with an alpha channel. */ if (GIMP_IS_CHANNEL (drawable) || babl_format_has_alpha (format)) { gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0); } else { gimp_context_get_background (context, &bg); gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), &bg, &bg); } color = gimp_gegl_color_new (&bg, gimp_drawable_get_space (drawable)); gegl_buffer_set_color (new_buffer, NULL, color); g_object_unref (color); if (gimp_rectangle_intersect (orig_x, orig_y, orig_width, orig_height, new_x, new_y, new_width, new_height, &clip_x, &clip_y, &clip_width, &clip_height)) { gint saved_orig_x = orig_x; gint saved_orig_y = orig_y; new_x = clip_x - orig_x; new_y = clip_y - orig_y; switch (rotate_type) { case GIMP_ROTATE_90: gimp_drawable_transform_rotate_point (clip_x + clip_width, clip_y, GIMP_ROTATE_270, center_x, center_y, &orig_x, &orig_y); orig_x -= saved_orig_x; orig_y -= saved_orig_y; orig_width = clip_height; orig_height = clip_width; break; case GIMP_ROTATE_180: orig_x = clip_x - orig_x; orig_y = clip_y - orig_y; orig_width = clip_width; orig_height = clip_height; break; case GIMP_ROTATE_270: gimp_drawable_transform_rotate_point (clip_x, clip_y + clip_height, GIMP_ROTATE_90, center_x, center_y, &orig_x, &orig_y); orig_x -= saved_orig_x; orig_y -= saved_orig_y; orig_width = clip_height; orig_height = clip_width; break; } new_width = clip_width; new_height = clip_height; } else { new_width = 0; new_height = 0; } } else { new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, new_width, new_height), format); *new_offset_x = new_x; *new_offset_y = new_y; orig_x = 0; orig_y = 0; new_x = 0; new_y = 0; } if (new_width < 1 || new_height < 1) return new_buffer; src_rect.x = orig_x; src_rect.y = orig_y; src_rect.width = orig_width; src_rect.height = orig_height; dest_rect.x = new_x; dest_rect.y = new_y; dest_rect.width = new_width; dest_rect.height = new_height; switch (rotate_type) { case GIMP_ROTATE_90: { guchar *buf = g_new (guchar, new_height * orig_bpp); gint i; /* Not cool, we leak memory if we return, but anyway that is * never supposed to happen. If we see this warning, a bug has * to be fixed! */ g_return_val_if_fail (new_height == orig_width, NULL); src_rect.y = orig_y + orig_height - 1; src_rect.height = 1; dest_rect.x = new_x; dest_rect.width = 1; for (i = 0; i < orig_height; i++) { src_rect.y = orig_y + orig_height - 1 - i; dest_rect.x = new_x + i; gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf, GEGL_AUTO_ROWSTRIDE); } g_free (buf); } break; case GIMP_ROTATE_180: { guchar *buf = g_new (guchar, new_width * orig_bpp); gint i, j, k; /* Not cool, we leak memory if we return, but anyway that is * never supposed to happen. If we see this warning, a bug has * to be fixed! */ g_return_val_if_fail (new_width == orig_width, NULL); src_rect.y = orig_y + orig_height - 1; src_rect.height = 1; dest_rect.y = new_y; dest_rect.height = 1; for (i = 0; i < orig_height; i++) { src_rect.y = orig_y + orig_height - 1 - i; dest_rect.y = new_y + i; gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); for (j = 0; j < orig_width / 2; j++) { guchar *left = buf + j * orig_bpp; guchar *right = buf + (orig_width - 1 - j) * orig_bpp; for (k = 0; k < orig_bpp; k++) { guchar tmp = left[k]; left[k] = right[k]; right[k] = tmp; } } gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf, GEGL_AUTO_ROWSTRIDE); } g_free (buf); } break; case GIMP_ROTATE_270: { guchar *buf = g_new (guchar, new_width * orig_bpp); gint i; /* Not cool, we leak memory if we return, but anyway that is * never supposed to happen. If we see this warning, a bug has * to be fixed! */ g_return_val_if_fail (new_width == orig_height, NULL); src_rect.x = orig_x + orig_width - 1; src_rect.width = 1; dest_rect.y = new_y; dest_rect.height = 1; for (i = 0; i < orig_width; i++) { src_rect.x = orig_x + orig_width - 1 - i; dest_rect.y = new_y + i; gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf, GEGL_AUTO_ROWSTRIDE); } g_free (buf); } break; } return new_buffer; }
void gimp_drawable_offset (GimpDrawable *drawable, GimpContext *context, gboolean wrap_around, GimpOffsetType fill_type, gint offset_x, gint offset_y) { GimpItem *item; GeglBuffer *src_buffer; GeglBuffer *new_buffer; GeglRectangle src_rect; GeglRectangle dest_rect; gint width, height; gint src_x, src_y; gint dest_x, dest_y; g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (GIMP_IS_CONTEXT (context)); item = GIMP_ITEM (drawable); width = gimp_item_get_width (item); height = gimp_item_get_height (item); if (wrap_around) { /* avoid modulo operation on negative values */ while (offset_x < 0) offset_x += width; while (offset_y < 0) offset_y += height; offset_x %= width; offset_y %= height; } else { offset_x = CLAMP (offset_x, -width, width); offset_y = CLAMP (offset_y, -height, height); } if (offset_x == 0 && offset_y == 0) return; src_buffer = gimp_drawable_get_buffer (drawable); new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), gimp_drawable_get_format (drawable)); if (! wrap_around) { if (fill_type == GIMP_OFFSET_BACKGROUND) { GimpRGB bg; GeglColor *color; gimp_context_get_background (context, &bg); gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), &bg, &bg); color = gimp_gegl_color_new (&bg); gegl_buffer_set_color (new_buffer, NULL, color); g_object_unref (color); } } if (offset_x >= 0) { src_x = 0; dest_x = offset_x; width = CLAMP ((width - offset_x), 0, width); } else { src_x = -offset_x; dest_x = 0; width = CLAMP ((width + offset_x), 0, width); } if (offset_y >= 0) { src_y = 0; dest_y = offset_y; height = CLAMP ((height - offset_y), 0, height); } else { src_y = -offset_y; dest_y = 0; height = CLAMP ((height + offset_y), 0, height); } /* Copy the center region */ if (width && height) { gegl_buffer_copy (src_buffer, GEGL_RECTANGLE (src_x, src_y, width, height), GEGL_ABYSS_NONE, new_buffer, GEGL_RECTANGLE (dest_x,dest_y, width, height)); } if (wrap_around) { /* Copy appropriately for wrap around */ if (offset_x >= 0 && offset_y >= 0) { src_x = gimp_item_get_width (item) - offset_x; src_y = gimp_item_get_height (item) - offset_y; } else if (offset_x >= 0 && offset_y < 0) { src_x = gimp_item_get_width (item) - offset_x; src_y = 0; } else if (offset_x < 0 && offset_y >= 0) { src_x = 0; src_y = gimp_item_get_height (item) - offset_y; } else if (offset_x < 0 && offset_y < 0) { src_x = 0; src_y = 0; } dest_x = (src_x + offset_x) % gimp_item_get_width (item); if (dest_x < 0) dest_x = gimp_item_get_width (item) + dest_x; dest_y = (src_y + offset_y) % gimp_item_get_height (item); if (dest_y < 0) dest_y = gimp_item_get_height (item) + dest_y; /* intersecting region */ if (offset_x != 0 && offset_y != 0) { gegl_buffer_copy (src_buffer, GEGL_RECTANGLE (src_x, src_y, ABS (offset_x), ABS (offset_y)), GEGL_ABYSS_NONE, new_buffer, GEGL_RECTANGLE (dest_x, dest_y, 0, 0)); } /* X offset */ if (offset_x != 0) { if (offset_y >= 0) { src_rect.x = src_x; src_rect.y = 0; src_rect.width = ABS (offset_x); src_rect.height = gimp_item_get_height (item) - ABS (offset_y); dest_rect.x = dest_x; dest_rect.y = dest_y + offset_y; } else if (offset_y < 0) { src_rect.x = src_x; src_rect.y = src_y - offset_y; src_rect.width = ABS (offset_x); src_rect.height = gimp_item_get_height (item) - ABS (offset_y); dest_rect.x = dest_x; dest_rect.y = 0; } gegl_buffer_copy (src_buffer, &src_rect, GEGL_ABYSS_NONE, new_buffer, &dest_rect); } /* X offset */ if (offset_y != 0) { if (offset_x >= 0) { src_rect.x = 0; src_rect.y = src_y; src_rect.width = gimp_item_get_width (item) - ABS (offset_x); src_rect.height = ABS (offset_y); dest_rect.x = dest_x + offset_x; dest_rect.y = dest_y; } else if (offset_x < 0) { src_rect.x = src_x - offset_x; src_rect.y = src_y; src_rect.width = gimp_item_get_width (item) - ABS (offset_x); src_rect.height = ABS (offset_y); dest_rect.x = 0; dest_rect.y = dest_y; } gegl_buffer_copy (src_buffer, &src_rect, GEGL_ABYSS_NONE, new_buffer, &dest_rect); } } gimp_drawable_set_buffer (drawable, gimp_item_is_attached (item), C_("undo-type", "Offset Drawable"), new_buffer); g_object_unref (new_buffer); }