static void gimp_view_renderer_transform_create (GimpViewRenderer *renderer, GtkWidget *widget, GeglBuffer *src_buffer, GeglBuffer *dest_buffer) { if (GIMP_IS_COLOR_MANAGED (renderer->viewable)) { GimpColorManaged *managed = GIMP_COLOR_MANAGED (renderer->viewable); GimpPickable *pickable = GIMP_PICKABLE (renderer->viewable); GimpColorProfile *profile; profile = gimp_color_managed_get_color_profile (managed); if (profile) { GimpImage *image = gimp_pickable_get_image (pickable); GimpColorConfig *config = image->gimp->config->color_management; renderer->profile_src_format = gegl_buffer_get_format (src_buffer); renderer->profile_dest_format = gegl_buffer_get_format (dest_buffer); renderer->profile_transform = gimp_widget_get_color_transform (widget, config, profile, &renderer->profile_src_format, &renderer->profile_dest_format); } } }
static void transfer_registration_color (GeglBuffer *src, GeglBuffer **dst, gint count) { GimpRGB color, test; GeglBufferIterator *gi; const Babl *src_format, *dst_format; gint i, src_bpp, dst_bpp; gdouble white; gimp_context_get_foreground (&color); white = 1.0; src_format = gegl_buffer_get_format (src); src_bpp = babl_format_get_bytes_per_pixel (src_format); dst_format = gegl_buffer_get_format (dst[0]); dst_bpp = babl_format_get_bytes_per_pixel (dst_format); gi = gegl_buffer_iterator_new (src, NULL, 0, NULL, GEGL_BUFFER_READ, GEGL_ABYSS_NONE); for (i = 0; i < count; i++) { gegl_buffer_iterator_add (gi, dst[i], NULL, 0, NULL, GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE); } while (gegl_buffer_iterator_next (gi)) { guint j, k; gpointer src_data, dst_data[MAX_EXTRACT_IMAGES]; src_data = gi->data[0]; for (j = 0; j < count; j++) dst_data[j] = gi->data[j+1]; for (k = 0; k < gi->length; k++) { gulong pos; pos = k * src_bpp; gimp_rgba_set_pixel (&test, src_format, ((guchar *)src_data) + pos); if (gimp_rgb_distance (&test, &color) < 1e-6) { for (j = 0; j < count; j++) { gpointer data; data = dst_data[j]; babl_process (babl_fish (babl_format ("Y double"), dst_format), &white, (guchar *)data + (k * dst_bpp), 1); } } } } }
static gboolean test_opacity_common (const Babl *in_format, const Babl *out_format) { /* Validate that gegl:opacity produces out_format when given in_format */ gboolean result = TRUE; GeglNode *ptn, *src, *opacity, *sink; GeglBuffer *src_buffer; GeglBuffer *sink_buffer = NULL; src_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, 10, 10), in_format); ptn = gegl_node_new (); src = gegl_node_new_child (ptn, "operation", "gegl:buffer-source", "buffer", src_buffer, NULL); opacity = gegl_node_new_child (ptn, "operation", "gegl:opacity", "value", 0.5, NULL); sink = gegl_node_new_child (ptn, "operation", "gegl:buffer-sink", "buffer", &sink_buffer, "format", NULL, NULL); gegl_node_link_many (src, opacity, sink, NULL); gegl_node_blit_buffer (sink, NULL, NULL, 0, GEGL_ABYSS_NONE); if (out_format != gegl_buffer_get_format (sink_buffer)) { printf ("Got %s expected %s\n", babl_get_name (gegl_buffer_get_format (sink_buffer)), babl_get_name (out_format)); result = FALSE; } if (!gegl_rectangle_equal (gegl_buffer_get_extent (src_buffer), gegl_buffer_get_extent (sink_buffer))) result = FALSE; g_object_unref (ptn); g_object_unref (src_buffer); g_object_unref (sink_buffer); return result; }
static void gegl_affine_fast_reflect_x (GeglBuffer *dest, GeglBuffer *src, const GeglRectangle *dest_rect, const GeglRectangle *src_rect) { const Babl *format = gegl_buffer_get_format (src); const gint px_size = babl_format_get_bytes_per_pixel (format), rowstride = src_rect->width * px_size; gint i; guchar *buf = (guchar *) g_malloc (src_rect->height * rowstride); gegl_buffer_get (src, 1.0, src_rect, format, buf, GEGL_AUTO_ROWSTRIDE); for (i = 0; i < src_rect->height / 2; i++) { gint dest_offset = (src_rect->height - i - 1) * rowstride, src_offset = i * rowstride, j; for (j = 0; j < rowstride; j++) { const guchar tmp = buf[src_offset]; buf[src_offset] = buf[dest_offset]; buf[dest_offset] = tmp; dest_offset++; src_offset++; } } gegl_buffer_set (dest, dest_rect, format, buf, GEGL_AUTO_ROWSTRIDE); g_free (buf); }
const Babl * gimp_buffer_get_format (const GimpBuffer *buffer) { g_return_val_if_fail (GIMP_IS_BUFFER (buffer), NULL); return gegl_buffer_get_format (buffer->buffer); }
TileManager * gimp_buffer_to_tiles (GeglBuffer *buffer) { const Babl *format = gegl_buffer_get_format (buffer); TileManager *new_tiles = NULL; GeglNode *source = NULL; GeglNode *sink = NULL; g_return_val_if_fail (buffer != NULL, NULL); /* Setup and process the graph */ new_tiles = tile_manager_new (gegl_buffer_get_width (buffer), gegl_buffer_get_height (buffer), gimp_babl_format_to_legacy_bpp (format)); source = gegl_node_new_child (NULL, "operation", "gegl:buffer-source", "buffer", buffer, NULL); sink = gegl_node_new_child (NULL, "operation", "gimp:tilemanager-sink", "tile-manager", new_tiles, NULL); gegl_node_link_many (source, sink, NULL); gegl_node_process (sink); /* Clenaup */ g_object_unref (sink); g_object_unref (source); return new_tiles; }
gboolean gimp_gegl_mask_is_empty (GeglBuffer *buffer) { GeglBufferIterator *iter; const Babl *format; gint bpp; g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE); format = gegl_buffer_get_format (buffer); bpp = babl_format_get_bytes_per_pixel (format); iter = gegl_buffer_iterator_new (buffer, NULL, 0, format, GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1); while (gegl_buffer_iterator_next (iter)) { if (! gegl_memeq_zero (iter->items[0].data, bpp * iter->length)) { gegl_buffer_iterator_stop (iter); return FALSE; } } return TRUE; }
void gimp_create_image_from_buffer (Gimp *gimp, GeglBuffer *buffer) { GimpImage *image; GimpLayer *layer; const Babl *format; g_return_if_fail (GIMP_IS_GIMP (gimp)); g_return_if_fail (GEGL_IS_BUFFER (buffer)); format = gegl_buffer_get_format (buffer); image = gimp_create_image (gimp, gegl_buffer_get_width (buffer), gegl_buffer_get_height (buffer), gimp_babl_format_get_base_type (format), gimp_babl_format_get_precision (format), FALSE); layer = gimp_layer_new_from_buffer (buffer, image, format, "Debug Image", GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE); gimp_image_add_layer (image, layer, NULL, -1, FALSE); gimp_create_display (gimp, image, GIMP_UNIT_PIXEL, 1.0); }
GimpTransformResize gimp_drawable_transform_get_effective_clip (GimpDrawable *drawable, GeglBuffer *orig_buffer, GimpTransformResize clip_result) { g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), clip_result); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), clip_result); g_return_val_if_fail (orig_buffer == NULL || GEGL_IS_BUFFER (orig_buffer), clip_result); /* Always clip unfloated buffers since they must keep their size */ if (GIMP_IS_CHANNEL (drawable)) { if (orig_buffer) { if (! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer))) clip_result = GIMP_TRANSFORM_RESIZE_CLIP; } else { GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); if (gimp_channel_is_empty (gimp_image_get_mask (image))) clip_result = GIMP_TRANSFORM_RESIZE_CLIP; } } return clip_result; }
guchar * gegl_buffer_introspectable_get (GeglBuffer *buffer, const GeglRectangle *rect, gdouble scale, const gchar *format_name, GeglAbyssPolicy repeat_mode, guint *data_length) { const Babl *format; guint bpp; guchar *result; *data_length = 0; if (format_name) format = babl_format (format_name); else format = gegl_buffer_get_format (buffer); if (rect->width <= 0 || rect->height <= 0) return NULL; if (scale <= 0.0) return NULL; bpp = babl_format_get_bytes_per_pixel (format); *data_length = bpp * rect->width * rect->height; result = g_malloc (*data_length); gegl_buffer_get (buffer, rect, scale, format, result, GEGL_AUTO_ROWSTRIDE, repeat_mode); return result; }
static gboolean gimp_operation_histogram_sink_process (GeglOperation *operation, GeglOperationContext *context, const gchar *output_prop, const GeglRectangle *result, gint level) { GeglBuffer *input; GeglBuffer *aux; if (strcmp (output_prop, "output")) { g_warning ("requested processing of %s pad on a sink", output_prop); return FALSE; } input = gegl_operation_context_get_source (context, "input"); aux = gegl_operation_context_get_source (context, "aux"); if (! input) { g_warning ("received NULL input"); return FALSE; } if (aux) { /* do hist with mask */ g_printerr ("aux format: %s\n", babl_get_name (gegl_buffer_get_format (aux))); g_object_unref (aux); } else { /* without */ } g_printerr ("input format: %s\n", babl_get_name (gegl_buffer_get_format (input))); g_object_unref (input); return TRUE; }
/* Do the final setup of the iter struct */ static void prepare_iteration (GeglBufferIterator *iter) { int index; GeglBufferIteratorPriv *priv = iter->priv; gint origin_offset_x; gint origin_offset_y; /* Set up the origin tile */ /* FIXME: Pick the most compatable buffer, not just the first */ { GeglBuffer *buf = priv->sub_iter[0].buffer; priv->origin_tile.x = buf->shift_x; priv->origin_tile.y = buf->shift_y; priv->origin_tile.width = buf->tile_width; priv->origin_tile.height = buf->tile_height; origin_offset_x = buf->shift_x + priv->sub_iter[0].full_rect.x; origin_offset_y = buf->shift_y + priv->sub_iter[0].full_rect.y; } for (index = 0; index < priv->num_buffers; index++) { SubIterState *sub = &priv->sub_iter[index]; GeglBuffer *buf = sub->buffer; gint current_offset_x = buf->shift_x + priv->sub_iter[index].full_rect.x; gint current_offset_y = buf->shift_y + priv->sub_iter[index].full_rect.y; /* Format converison needed */ if (gegl_buffer_get_format (sub->buffer) != sub->format) sub->access_mode |= GEGL_ITERATOR_INCOMPATIBLE; /* Incompatable tiles */ else if ((priv->origin_tile.width != buf->tile_width) || (priv->origin_tile.height != buf->tile_height) || (abs(origin_offset_x - current_offset_x) % priv->origin_tile.width != 0) || (abs(origin_offset_y - current_offset_y) % priv->origin_tile.height != 0)) { /* Check if the buffer is a linear buffer */ if ((buf->extent.x == -buf->shift_x) && (buf->extent.y == -buf->shift_y) && (buf->extent.width == buf->tile_width) && (buf->extent.height == buf->tile_height)) { sub->linear_tile = gegl_buffer_get_tile (sub->buffer, 0, 0, 0); if (sub->access_mode & GEGL_ACCESS_WRITE) gegl_tile_lock (sub->linear_tile); } else sub->access_mode |= GEGL_ITERATOR_INCOMPATIBLE; } gegl_buffer_lock (sub->buffer); } }
int gegl_buffer_iterator_add (GeglBufferIterator *iter, GeglBuffer *buf, const GeglRectangle *roi, gint level, const Babl *format, GeglAccessMode access_mode, GeglAbyssPolicy abyss_policy) { GeglBufferIteratorPriv *priv = iter->priv; int index; SubIterState *sub; g_return_val_if_fail (priv->num_buffers < GEGL_BUFFER_MAX_ITERATORS, 0); index = priv->num_buffers++; sub = &priv->sub_iter[index]; if (!format) format = gegl_buffer_get_format (buf); if (!roi) roi = &buf->extent; sub->buffer = buf; sub->full_rect = *roi; if (level) { sub->full_rect.x >>= level; sub->full_rect.y >>= level; sub->full_rect.width >>= level; sub->full_rect.height >>= level; } sub->access_mode = access_mode; sub->abyss_policy = abyss_policy; sub->current_tile = NULL; sub->real_data = NULL; sub->linear_tile = NULL; sub->format = format; sub->format_bpp = babl_format_get_bytes_per_pixel (format); sub->level = level; if (index > 0) { priv->sub_iter[index].full_rect.width = priv->sub_iter[0].full_rect.width; priv->sub_iter[index].full_rect.height = priv->sub_iter[0].full_rect.height; } //if (level != 0) // g_warning ("iterator level != 0"); return index; }
static gboolean test_buffer_sink_001 (void) { /* Validate that gegl:buffer-sink doesn't modify the format of its input */ gboolean result = TRUE; GeglNode *ptn, *src, *sink; GeglBuffer *src_buffer; GeglBuffer *sink_buffer = NULL; src_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, 10, 10), babl_format ("RGB u8")); ptn = gegl_node_new (); src = gegl_node_new_child (ptn, "operation", "gegl:buffer-source", "buffer", src_buffer, NULL); sink = gegl_node_new_child (ptn, "operation", "gegl:buffer-sink", "buffer", &sink_buffer, "format", NULL, NULL); gegl_node_link_many (src, sink, NULL); gegl_node_blit_buffer (sink, NULL, NULL, 0, GEGL_ABYSS_NONE); if (gegl_buffer_get_format (src_buffer) != gegl_buffer_get_format (sink_buffer)) result = FALSE; if (!gegl_rectangle_equal (gegl_buffer_get_extent (src_buffer), gegl_buffer_get_extent (sink_buffer))) result = FALSE; g_object_unref (ptn); g_object_unref (src_buffer); g_object_unref (sink_buffer); return result; }
static void gegl_buffer_source_prepare (GeglOperation *operation) { const Babl *format = NULL; GeglProperties *o = GEGL_PROPERTIES (operation); if (o->buffer) format = gegl_buffer_get_format (GEGL_BUFFER (o->buffer)); gegl_operation_set_format (operation, "output", format); }
void gegl_apply_op_valist (GeglBuffer *buffer, const gchar *first_property_name, va_list var_args) { GeglBuffer *tempbuf = NULL; GeglNode *node; GeglNode *source; GeglNode *sink; g_return_if_fail (GEGL_IS_BUFFER (buffer)); g_object_ref (buffer); source = gegl_node_new_child (NULL, "operation", "gegl:buffer-source", "buffer", buffer, NULL); node = gegl_node_new_child (NULL, "operation", first_property_name, NULL); if (!GEGL_IS_OPERATION_POINT_FILTER (node->operation)) { tempbuf = gegl_buffer_new (gegl_buffer_get_extent (buffer), gegl_buffer_get_format (buffer)); sink = gegl_node_new_child (NULL, "operation", "gegl:write-buffer", "buffer", tempbuf, NULL); } else { sink = gegl_node_new_child (NULL, "operation", "gegl:write-buffer", "buffer", buffer, NULL); } gegl_node_link_many (source, node, sink, NULL); gegl_node_set_props (node, var_args); gegl_node_process (sink); g_object_unref (source); g_object_unref (node); g_object_unref (sink); if (tempbuf) { gegl_buffer_copy (tempbuf, NULL, buffer, NULL); g_object_unref (tempbuf); } g_object_unref (buffer); }
/* Do the final setup of the iter struct */ static void prepare_iteration (GeglBufferIterator *iter) { int index; GeglBufferIteratorPriv *priv = iter->priv; /* Set up the origin tile */ /* FIXME: Pick the most compatable buffer, not just the first */ { GeglBuffer *buf = priv->sub_iter[0].buffer; priv->origin_tile.x = buf->shift_x; priv->origin_tile.y = buf->shift_y; priv->origin_tile.width = buf->tile_width; priv->origin_tile.height = buf->tile_height; } for (index = 0; index < priv->num_buffers; index++) { SubIterState *sub = &priv->sub_iter[index]; GeglBuffer *buf = sub->buffer; /* Format converison needed */ if (gegl_buffer_get_format (sub->buffer) != sub->format) sub->flags |= GEGL_ITERATOR_INCOMPATIBLE; /* Incompatable tiles */ else if ((priv->origin_tile.x != buf->shift_x) || (priv->origin_tile.y != buf->shift_y) || (priv->origin_tile.width != buf->tile_width) || (priv->origin_tile.height != buf->tile_height)) { /* Check if the buffer is a linear buffer */ if ((buf->extent.x == -buf->shift_x) && (buf->extent.y == -buf->shift_y) && (buf->extent.width == buf->tile_width) && (buf->extent.height == buf->tile_height)) { sub->linear_tile = gegl_tile_source_get_tile ((GeglTileSource *)(sub->buffer), 0, 0, 0); if (sub->flags & GEGL_BUFFER_WRITE) gegl_tile_lock (sub->linear_tile); } else sub->flags |= GEGL_ITERATOR_INCOMPATIBLE; } gegl_buffer_lock (sub->buffer); } }
gboolean gegl_can_do_inplace_processing (GeglOperation *operation, GeglBuffer *input, const GeglRectangle *result) { if (!input) return FALSE; if (gegl_object_get_has_forked (G_OBJECT (input))) return FALSE; if (gegl_buffer_get_format (input) == gegl_operation_get_format (operation, "output") && gegl_rectangle_contains (gegl_buffer_get_extent (input), result)) return TRUE; return FALSE; }
static gboolean buffer_is_native(MyPaintGeglTiledSurface *self) { const int tile_size = self->parent.tile_size; int tile_height = -1; int tile_width = -1; g_object_get(self->buffer, "tile-width", &tile_width, "tile-height", &tile_height, NULL); g_assert(tile_height != -1); g_assert(tile_width != -1); const gboolean correct_format = gegl_buffer_get_format(self->buffer) == self->format; const gboolean correct_tile_size = tile_height == tile_size && tile_width == tile_size; return correct_format && correct_tile_size; }
gint64 gimp_gegl_buffer_get_memsize (GeglBuffer *buffer) { if (buffer) { const Babl *format = gegl_buffer_get_format (buffer); return (babl_format_get_bytes_per_pixel (format) * gegl_buffer_get_width (buffer) * gegl_buffer_get_height (buffer) + gimp_g_object_get_memsize (G_OBJECT (buffer))); } return 0; }
gint64 gimp_gegl_pyramid_get_memsize (GeglBuffer *buffer) { if (buffer) { const Babl *format = gegl_buffer_get_format (buffer); /* The pyramid levels constitute a geometric sum with a ratio of 1/4. */ return ((gint64) babl_format_get_bytes_per_pixel (format) * (gint64) gegl_buffer_get_width (buffer) * (gint64) gegl_buffer_get_height (buffer) * 1.33 + gimp_g_object_get_memsize (G_OBJECT (buffer))); } return 0; }
static const Babl * choose_format (GeglBuffer *buffer, GimpSelectCriterion select_criterion, gint *n_components, gboolean *has_alpha) { const Babl *format = gegl_buffer_get_format (buffer); *has_alpha = babl_format_has_alpha (format); switch (select_criterion) { case GIMP_SELECT_CRITERION_COMPOSITE: if (babl_format_is_palette (format)) format = babl_format ("R'G'B'A float"); else format = gimp_babl_format (gimp_babl_format_get_base_type (format), GIMP_PRECISION_FLOAT_GAMMA, *has_alpha); break; case GIMP_SELECT_CRITERION_R: case GIMP_SELECT_CRITERION_G: case GIMP_SELECT_CRITERION_B: case GIMP_SELECT_CRITERION_A: format = babl_format ("R'G'B'A float"); break; case GIMP_SELECT_CRITERION_H: case GIMP_SELECT_CRITERION_S: case GIMP_SELECT_CRITERION_V: format = babl_format ("HSVA float"); break; default: g_return_val_if_reached (NULL); break; } *n_components = babl_format_get_n_components (format); return format; }
TEST () { GeglBuffer *buffer, *buffer2; GeglRectangle bound = {0, 0, 20, 20}; test_start (); buffer = gegl_buffer_new (&bound, babl_format ("Y float")); vgrad (buffer); { GeglRectangle rect = *gegl_buffer_get_extent(buffer); rect.width-=10; rect.height-=10; buffer2 = gegl_buffer_new (gegl_buffer_get_extent (buffer), gegl_buffer_get_format (buffer)); gegl_buffer_copy (buffer, &rect, buffer2, &rect); } print_buffer (buffer2); g_object_unref (buffer); g_object_unref (buffer2); test_end (); }
static gboolean export_numpy (GeglOperation *operation, GeglBuffer *input, const GeglRectangle *result, GOutputStream *stream) { const Babl *input_format, *output_format; gint nb_components; input_format = gegl_buffer_get_format (input); nb_components = babl_format_get_n_components (input_format); if (nb_components >= 3) { output_format = babl_format ("RGB float"); nb_components = 3; } else { output_format = babl_format ("Y float"); nb_components = 1; } return !save_array (stream, input, result, output_format); }
gint gegl_buffer_export_png (GeglBuffer *gegl_buffer, const gchar *path, gint compression, gint bd, gint src_x, gint src_y, gint width, gint height) { FILE *fp; gint i; png_struct *png; png_info *info; guchar *pixels; png_color_16 white; int png_color_type; gchar format_string[16]; const Babl *format; gint bit_depth = 8; if (!strcmp (path, "-")) { fp = stdout; } else { fp = fopen (path, "wb"); } if (!fp) { return -1; } { const Babl *babl = gegl_buffer_get_format (gegl_buffer); if (bd == 16) bit_depth = 16; else bit_depth = 8; if (babl_format_has_alpha (babl)) if (babl_format_get_n_components (babl) != 2) { png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; strcpy (format_string, "R'G'B'A "); } else { png_color_type = PNG_COLOR_TYPE_GRAY_ALPHA; strcpy (format_string, "Y'A "); } else if (babl_format_get_n_components (babl) != 1) { png_color_type = PNG_COLOR_TYPE_RGB; strcpy (format_string, "R'G'B' "); } else { png_color_type = PNG_COLOR_TYPE_GRAY; strcpy (format_string, "Y' "); } } if (bit_depth == 16) strcat (format_string, "u16"); else strcat (format_string, "u8"); png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png == NULL) { if (stdout != fp) fclose (fp); return -1; } info = png_create_info_struct (png); if (setjmp (png_jmpbuf (png))) { if (stdout != fp) fclose (fp); return -1; } png_set_compression_level (png, compression); png_init_io (png, fp); png_set_IHDR (png, info, width, height, bit_depth, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_DEFAULT); if (png_color_type == PNG_COLOR_TYPE_RGB || png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { white.red = 0xff; white.blue = 0xff; white.green = 0xff; } else white.gray = 0xff; png_set_bKGD (png, info, &white); png_write_info (png, info); #if BYTE_ORDER == LITTLE_ENDIAN if (bit_depth > 8) png_set_swap (png); #endif format = babl_format (format_string); pixels = g_malloc0 (width * babl_format_get_bytes_per_pixel (format)); for (i=0; i< height; i++) { GeglRectangle rect; rect.x = src_x; rect.y = src_y+i; rect.width = width; rect.height = 1; gegl_buffer_get (gegl_buffer, &rect, 1.0, babl_format (format_string), pixels, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); png_write_rows (png, &pixels, 1); } png_write_end (png, info); png_destroy_write_struct (&png, &info); g_free (pixels); if (stdout != fp) fclose (fp); return 0; }
gboolean gimp_gegl_mask_bounds (GeglBuffer *buffer, gint *x1, gint *y1, gint *x2, gint *y2) { GeglBufferIterator *iter; GeglRectangle *roi; const Babl *format; gint bpp; gint tx1, tx2, ty1, ty2; g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE); g_return_val_if_fail (x1 != NULL, FALSE); g_return_val_if_fail (y1 != NULL, FALSE); g_return_val_if_fail (x2 != NULL, FALSE); g_return_val_if_fail (y2 != NULL, FALSE); /* go through and calculate the bounds */ tx1 = gegl_buffer_get_width (buffer); ty1 = gegl_buffer_get_height (buffer); tx2 = 0; ty2 = 0; format = gegl_buffer_get_format (buffer); bpp = babl_format_get_bytes_per_pixel (format); iter = gegl_buffer_iterator_new (buffer, NULL, 0, format, GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1); roi = &iter->items[0].roi; while (gegl_buffer_iterator_next (iter)) { const guint8 *data_u8 = iter->items[0].data; gint ex = roi->x + roi->width; gint ey = roi->y + roi->height; /* only check the pixels if this tile is not fully within the * currently computed bounds */ if (roi->x < tx1 || ex > tx2 || roi->y < ty1 || ey > ty2) { /* Check upper left and lower right corners to see if we can * avoid checking the rest of the pixels in this tile */ if (! gegl_memeq_zero (data_u8, bpp) && ! gegl_memeq_zero (data_u8 + (iter->length - 1) * bpp, bpp)) { /* "ex/ey - 1" because the internal variables are the * right/bottom pixel of the mask's contents, not one * right/below it like the return values. */ if (roi->x < tx1) tx1 = roi->x; if (ex > tx2) tx2 = ex - 1; if (roi->y < ty1) ty1 = roi->y; if (ey > ty2) ty2 = ey - 1; } else { #define FIND_BOUNDS(bpp, type) \ G_STMT_START \ { \ const type *data; \ gint y; \ \ if ((guintptr) data_u8 % bpp) \ goto generic; \ \ data = (const type *) data_u8; \ \ for (y = roi->y; y < ey; y++) \ { \ gint x1; \ \ for (x1 = 0; x1 < roi->width; x1++) \ { \ if (data[x1]) \ { \ gint x2; \ gint x2_end = MAX (x1, tx2 - roi->x); \ \ for (x2 = roi->width - 1; x2 > x2_end; x2--) \ { \ if (data[x2]) \ break; \ } \ \ x1 += roi->x; \ x2 += roi->x; \ \ if (x1 < tx1) tx1 = x1; \ if (x2 > tx2) tx2 = x2; \ \ if (y < ty1) ty1 = y; \ if (y > ty2) ty2 = y; \ } \ } \ \ data += roi->width; \ } \ } \ G_STMT_END switch (bpp) { case 1: FIND_BOUNDS (1, guint8); break; case 2: FIND_BOUNDS (2, guint16); break; case 4: FIND_BOUNDS (4, guint32); break; case 8: FIND_BOUNDS (8, guint64); break; default: generic: { const guint8 *data = data_u8; gint y; for (y = roi->y; y < ey; y++) { gint x1; for (x1 = 0; x1 < roi->width; x1++) { if (! gegl_memeq_zero (data + x1 * bpp, bpp)) { gint x2; gint x2_end = MAX (x1, tx2 - roi->x); for (x2 = roi->width - 1; x2 > x2_end; x2--) { if (! gegl_memeq_zero (data + x2 * bpp, bpp)) { break; } } x1 += roi->x; x2 += roi->x; if (x1 < tx1) tx1 = x1; if (x2 > tx2) tx2 = x2; if (y < ty1) ty1 = y; if (y > ty2) ty2 = y; } } data += roi->width * bpp; } } break; } #undef FIND_BOUNDS } } } tx2 = CLAMP (tx2 + 1, 0, gegl_buffer_get_width (buffer)); ty2 = CLAMP (ty2 + 1, 0, gegl_buffer_get_height (buffer)); if (tx1 == gegl_buffer_get_width (buffer) && ty1 == gegl_buffer_get_height (buffer)) { *x1 = 0; *y1 = 0; *x2 = gegl_buffer_get_width (buffer); *y2 = gegl_buffer_get_height (buffer); return FALSE; } *x1 = tx1; *y1 = ty1; *x2 = tx2; *y2 = ty2; return TRUE; }
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, gint *new_offset_x, gint *new_offset_y) { 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); 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; } 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 = gimp_gegl_buffer_new (GEGL_RECTANGLE (0, 0, orig_width, orig_height), gegl_buffer_get_format (orig_buffer)); *new_offset_x = orig_x; *new_offset_y = orig_y; /* "Outside" a channel is transparency, not the bg color */ if (GIMP_IS_CHANNEL (drawable)) gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0); else gimp_context_get_background (context, &bg); color = gimp_gegl_color_new (&bg); 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 = gimp_gegl_buffer_new (GEGL_RECTANGLE (0, 0, new_width, new_height), gegl_buffer_get_format (orig_buffer)); *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; g_assert (new_height == orig_width); 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; g_assert (new_width == orig_width); 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; g_assert (new_width == orig_height); 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; }
GimpDrawable * gimp_drawable_transform_rotate (GimpDrawable *drawable, GimpContext *context, GimpRotationType rotate_type, gdouble center_x, gdouble center_y, gboolean clip_result) { GimpImage *image; GeglBuffer *orig_buffer; gint orig_offset_x; gint orig_offset_y; gboolean new_layer; GimpDrawable *result = NULL; 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); image = gimp_item_get_image (GIMP_ITEM (drawable)); /* Start a transform undo group */ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, C_("undo-type", "Rotate")); /* Cut/Copy from the specified drawable */ orig_buffer = gimp_drawable_transform_cut (drawable, context, &orig_offset_x, &orig_offset_y, &new_layer); if (orig_buffer) { GeglBuffer *new_buffer; gint new_offset_x; gint new_offset_y; /* always clip unfloated buffers so they keep their size */ if (GIMP_IS_CHANNEL (drawable) && ! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer))) clip_result = TRUE; /* also transform the mask if we are transforming an entire layer */ if (GIMP_IS_LAYER (drawable) && gimp_layer_get_mask (GIMP_LAYER (drawable)) && gimp_channel_is_empty (gimp_image_get_mask (image))) { GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable)); gimp_item_rotate (GIMP_ITEM (mask), context, rotate_type, center_x, center_y, clip_result); } /* transform the buffer */ new_buffer = gimp_drawable_transform_buffer_rotate (drawable, context, orig_buffer, orig_offset_x, orig_offset_y, rotate_type, center_x, center_y, clip_result, &new_offset_x, &new_offset_y); /* Free the cut/copied buffer */ g_object_unref (orig_buffer); if (new_buffer) { result = gimp_drawable_transform_paste (drawable, new_buffer, new_offset_x, new_offset_y, new_layer); g_object_unref (new_buffer); } } /* push the undo group end */ gimp_image_undo_group_end (image); return result; }
GeglBuffer * gimp_drawable_transform_buffer_affine (GimpDrawable *drawable, GimpContext *context, GeglBuffer *orig_buffer, gint orig_offset_x, gint orig_offset_y, const GimpMatrix3 *matrix, GimpTransformDirection direction, GimpInterpolationType interpolation_type, gint recursion_level, GimpTransformResize clip_result, gint *new_offset_x, gint *new_offset_y, GimpProgress *progress) { GeglBuffer *new_buffer; GimpMatrix3 m; GimpMatrix3 inv; gint u1, v1, u2, v2; /* source bounding box */ gint x1, y1, x2, y2; /* target bounding box */ GeglNode *affine; GimpMatrix3 gegl_matrix; 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 (matrix != NULL, NULL); g_return_val_if_fail (new_offset_x != NULL, NULL); g_return_val_if_fail (new_offset_y != NULL, NULL); g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); m = *matrix; inv = *matrix; if (direction == GIMP_TRANSFORM_BACKWARD) { /* keep the original matrix here, so we dont need to recalculate * the inverse later */ gimp_matrix3_invert (&inv); } else { /* Find the inverse of the transformation matrix */ gimp_matrix3_invert (&m); } u1 = orig_offset_x; v1 = orig_offset_y; u2 = u1 + gegl_buffer_get_width (orig_buffer); v2 = v1 + gegl_buffer_get_height (orig_buffer); /* Always clip unfloated buffers since they must keep their size */ if (G_TYPE_FROM_INSTANCE (drawable) == GIMP_TYPE_CHANNEL && ! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer))) clip_result = GIMP_TRANSFORM_RESIZE_CLIP; /* Find the bounding coordinates of target */ gimp_transform_resize_boundary (&inv, clip_result, u1, v1, u2, v2, &x1, &y1, &x2, &y2); /* Get the new temporary buffer for the transformed result */ new_buffer = gimp_gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1), gegl_buffer_get_format (orig_buffer)); gimp_matrix3_identity (&gegl_matrix); gimp_matrix3_translate (&gegl_matrix, u1, v1); gimp_matrix3_mult (&inv, &gegl_matrix); gimp_matrix3_translate (&gegl_matrix, -x1, -y1); affine = gegl_node_new_child (NULL, "operation", "gegl:transform", "filter", gimp_interpolation_to_gegl_filter (interpolation_type), "hard-edges", TRUE, NULL); gimp_gegl_node_set_matrix (affine, &gegl_matrix); gimp_apply_operation (orig_buffer, progress, NULL, affine, new_buffer, NULL); g_object_unref (affine); *new_offset_x = x1; *new_offset_y = y1; return new_buffer; }
GimpDrawable * gimp_drawable_transform_affine (GimpDrawable *drawable, GimpContext *context, const GimpMatrix3 *matrix, GimpTransformDirection direction, GimpInterpolationType interpolation_type, gint recursion_level, GimpTransformResize clip_result, GimpProgress *progress) { GimpImage *image; GeglBuffer *orig_buffer; gint orig_offset_x; gint orig_offset_y; gboolean new_layer; GimpDrawable *result = NULL; 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 (matrix != NULL, NULL); g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); /* Start a transform undo group */ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM, C_("undo-type", "Transform")); /* Cut/Copy from the specified drawable */ orig_buffer = gimp_drawable_transform_cut (drawable, context, &orig_offset_x, &orig_offset_y, &new_layer); if (orig_buffer) { GeglBuffer *new_buffer; gint new_offset_x; gint new_offset_y; /* always clip unfloated buffers so they keep their size */ if (GIMP_IS_CHANNEL (drawable) && ! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer))) clip_result = GIMP_TRANSFORM_RESIZE_CLIP; /* also transform the mask if we are transforming an entire layer */ if (GIMP_IS_LAYER (drawable) && gimp_layer_get_mask (GIMP_LAYER (drawable)) && gimp_channel_is_empty (gimp_image_get_mask (image))) { GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (drawable)); gimp_item_transform (GIMP_ITEM (mask), context, matrix, direction, interpolation_type, recursion_level, clip_result, progress); } /* transform the buffer */ new_buffer = gimp_drawable_transform_buffer_affine (drawable, context, orig_buffer, orig_offset_x, orig_offset_y, matrix, direction, interpolation_type, recursion_level, clip_result, &new_offset_x, &new_offset_y, progress); /* Free the cut/copied buffer */ g_object_unref (orig_buffer); if (new_buffer) { result = gimp_drawable_transform_paste (drawable, new_buffer, new_offset_x, new_offset_y, new_layer); g_object_unref (new_buffer); } } /* push the undo group end */ gimp_image_undo_group_end (image); return result; }