diff options
Diffstat (limited to '')
-rw-r--r-- | app/gegl/gimptilehandlervalidate.c | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/app/gegl/gimptilehandlervalidate.c b/app/gegl/gimptilehandlervalidate.c new file mode 100644 index 0000000..01915d5 --- /dev/null +++ b/app/gegl/gimptilehandlervalidate.c @@ -0,0 +1,766 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <cairo.h> +#include <gegl.h> + +#include "gimp-gegl-types.h" + +#include "core/gimpchunkiterator.h" +#include "core/gimpmarshal.h" + +#include "gimp-gegl-loops.h" +#include "gimp-gegl-utils.h" +#include "gimptilehandlervalidate.h" + + +enum +{ + INVALIDATED, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_FORMAT, + PROP_TILE_WIDTH, + PROP_TILE_HEIGHT, + PROP_WHOLE_TILE +}; + + +static void gimp_tile_handler_validate_finalize (GObject *object); +static void gimp_tile_handler_validate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tile_handler_validate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tile_handler_validate_real_begin_validate (GimpTileHandlerValidate *validate); +static void gimp_tile_handler_validate_real_end_validate (GimpTileHandlerValidate *validate); +static void gimp_tile_handler_validate_real_validate (GimpTileHandlerValidate *validate, + const GeglRectangle *rect, + const Babl *format, + gpointer dest_buf, + gint dest_stride); +static void gimp_tile_handler_validate_real_validate_buffer (GimpTileHandlerValidate *validate, + const GeglRectangle *rect, + GeglBuffer *buffer); + +static gpointer gimp_tile_handler_validate_command (GeglTileSource *source, + GeglTileCommand command, + gint x, + gint y, + gint z, + gpointer data); + + +G_DEFINE_TYPE (GimpTileHandlerValidate, gimp_tile_handler_validate, + GEGL_TYPE_TILE_HANDLER) + +#define parent_class gimp_tile_handler_validate_parent_class + +static guint gimp_tile_handler_validate_signals[LAST_SIGNAL]; + + +static void +gimp_tile_handler_validate_class_init (GimpTileHandlerValidateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + gimp_tile_handler_validate_signals[INVALIDATED] = + g_signal_new ("invalidated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpTileHandlerValidateClass, invalidated), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + GEGL_TYPE_RECTANGLE); + + object_class->finalize = gimp_tile_handler_validate_finalize; + object_class->set_property = gimp_tile_handler_validate_set_property; + object_class->get_property = gimp_tile_handler_validate_get_property; + + klass->begin_validate = gimp_tile_handler_validate_real_begin_validate; + klass->end_validate = gimp_tile_handler_validate_real_end_validate; + klass->validate = gimp_tile_handler_validate_real_validate; + klass->validate_buffer = gimp_tile_handler_validate_real_validate_buffer; + + g_object_class_install_property (object_class, PROP_FORMAT, + g_param_spec_pointer ("format", NULL, NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_TILE_WIDTH, + g_param_spec_int ("tile-width", NULL, NULL, + 1, G_MAXINT, 1, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TILE_HEIGHT, + g_param_spec_int ("tile-height", NULL, NULL, + 1, G_MAXINT, 1, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_WHOLE_TILE, + g_param_spec_boolean ("whole-tile", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); +} + +static void +gimp_tile_handler_validate_init (GimpTileHandlerValidate *validate) +{ + GeglTileSource *source = GEGL_TILE_SOURCE (validate); + + source->command = gimp_tile_handler_validate_command; + + validate->dirty_region = cairo_region_create (); +} + +static void +gimp_tile_handler_validate_finalize (GObject *object) +{ + GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (object); + + g_clear_object (&validate->graph); + g_clear_pointer (&validate->dirty_region, cairo_region_destroy); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_tile_handler_validate_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (object); + + switch (property_id) + { + case PROP_FORMAT: + validate->format = g_value_get_pointer (value); + break; + case PROP_TILE_WIDTH: + validate->tile_width = g_value_get_int (value); + break; + case PROP_TILE_HEIGHT: + validate->tile_height = g_value_get_int (value); + break; + case PROP_WHOLE_TILE: + validate->whole_tile = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tile_handler_validate_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (object); + + switch (property_id) + { + case PROP_FORMAT: + g_value_set_pointer (value, (gpointer) validate->format); + break; + case PROP_TILE_WIDTH: + g_value_set_int (value, validate->tile_width); + break; + case PROP_TILE_HEIGHT: + g_value_set_int (value, validate->tile_height); + break; + case PROP_WHOLE_TILE: + g_value_set_boolean (value, validate->whole_tile); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tile_handler_validate_real_begin_validate (GimpTileHandlerValidate *validate) +{ + validate->suspend_validate++; +} + +static void +gimp_tile_handler_validate_real_end_validate (GimpTileHandlerValidate *validate) +{ + validate->suspend_validate--; +} + +static void +gimp_tile_handler_validate_real_validate (GimpTileHandlerValidate *validate, + const GeglRectangle *rect, + const Babl *format, + gpointer dest_buf, + gint dest_stride) +{ +#if 0 + g_printerr ("validating at %d %d %d %d\n", + rect.x, + rect.y, + rect.width, + rect.height); +#endif + + gegl_node_blit (validate->graph, 1.0, rect, format, + dest_buf, dest_stride, + GEGL_BLIT_DEFAULT); +} + +static void +gimp_tile_handler_validate_real_validate_buffer (GimpTileHandlerValidate *validate, + const GeglRectangle *rect, + GeglBuffer *buffer) +{ + GimpTileHandlerValidateClass *klass; + + klass = GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate); + + if (klass->validate == gimp_tile_handler_validate_real_validate) + { + gegl_node_blit_buffer (validate->graph, buffer, rect, 0, + GEGL_ABYSS_NONE); + } + else + { + const Babl *format = gegl_buffer_get_format (buffer); + gpointer data; + gint stride; + + data = gegl_buffer_linear_open (buffer, rect, &stride, format); + + klass->validate (validate, rect, format, data, stride); + + gegl_buffer_linear_close (buffer, data); + } +} + +static GeglTile * +gimp_tile_handler_validate_validate_tile (GeglTileSource *source, + gint x, + gint y) +{ + GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (source); + GeglTile *tile; + cairo_rectangle_int_t tile_rect; + cairo_region_overlap_t overlap; + + if (validate->suspend_validate || + cairo_region_is_empty (validate->dirty_region)) + { + return gegl_tile_handler_source_command (source, + GEGL_TILE_GET, x, y, 0, NULL); + } + + tile_rect.x = x * validate->tile_width; + tile_rect.y = y * validate->tile_height; + tile_rect.width = validate->tile_width; + tile_rect.height = validate->tile_height; + + overlap = cairo_region_contains_rectangle (validate->dirty_region, + &tile_rect); + + if (overlap == CAIRO_REGION_OVERLAP_OUT) + { + return gegl_tile_handler_source_command (source, + GEGL_TILE_GET, x, y, 0, NULL); + } + + if (overlap == CAIRO_REGION_OVERLAP_IN || validate->whole_tile) + { + gint tile_bpp; + gint tile_stride; + + cairo_region_subtract_rectangle (validate->dirty_region, &tile_rect); + + tile_bpp = babl_format_get_bytes_per_pixel (validate->format); + tile_stride = tile_bpp * validate->tile_width; + + tile = gegl_tile_handler_get_source_tile (GEGL_TILE_HANDLER (source), + x, y, 0, FALSE); + + gimp_tile_handler_validate_begin_validate (validate); + + gegl_tile_lock (tile); + + GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->validate + (validate, + GEGL_RECTANGLE (tile_rect.x, + tile_rect.y, + tile_rect.width, + tile_rect.height), + validate->format, + gegl_tile_get_data (tile), + tile_stride); + + gegl_tile_unlock (tile); + + gimp_tile_handler_validate_end_validate (validate); + } + else + { + cairo_region_t *tile_region; + gint tile_bpp; + gint tile_stride; + gint n_rects; + gint i; + + tile_region = cairo_region_copy (validate->dirty_region); + cairo_region_intersect_rectangle (tile_region, &tile_rect); + + cairo_region_subtract_rectangle (validate->dirty_region, &tile_rect); + + tile_bpp = babl_format_get_bytes_per_pixel (validate->format); + tile_stride = tile_bpp * validate->tile_width; + + tile = gegl_tile_handler_source_command (source, + GEGL_TILE_GET, x, y, 0, NULL); + + if (! tile) + { + tile = gegl_tile_handler_create_tile (GEGL_TILE_HANDLER (source), + x, y, 0); + + memset (gegl_tile_get_data (tile), + 0, tile_stride * validate->tile_height); + } + + gimp_tile_handler_validate_begin_validate (validate); + + gegl_tile_lock (tile); + + n_rects = cairo_region_num_rectangles (tile_region); + +#if 0 + g_printerr ("%d chunks\n", n_rects); +#endif + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t blit_rect; + gint tile_x; + gint tile_y; + + cairo_region_get_rectangle (tile_region, i, &blit_rect); + + tile_x = blit_rect.x % validate->tile_width; + if (tile_x < 0) tile_x += validate->tile_width; + + tile_y = blit_rect.y % validate->tile_height; + if (tile_y < 0) tile_y += validate->tile_height; + + GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->validate + (validate, + GEGL_RECTANGLE (blit_rect.x, + blit_rect.y, + blit_rect.width, + blit_rect.height), + validate->format, + gegl_tile_get_data (tile) + + tile_y * tile_stride + + tile_x * tile_bpp, + tile_stride); + } + + gegl_tile_unlock (tile); + + gimp_tile_handler_validate_end_validate (validate); + + cairo_region_destroy (tile_region); + } + + return tile; +} + +static gpointer +gimp_tile_handler_validate_command (GeglTileSource *source, + GeglTileCommand command, + gint x, + gint y, + gint z, + gpointer data) +{ + if (command == GEGL_TILE_GET && z == 0) + return gimp_tile_handler_validate_validate_tile (source, x, y); + + return gegl_tile_handler_source_command (source, command, x, y, z, data); +} + + +/* public functions */ + +GeglTileHandler * +gimp_tile_handler_validate_new (GeglNode *graph) +{ + GimpTileHandlerValidate *validate; + + g_return_val_if_fail (GEGL_IS_NODE (graph), NULL); + + validate = g_object_new (GIMP_TYPE_TILE_HANDLER_VALIDATE, NULL); + + validate->graph = g_object_ref (graph); + + return GEGL_TILE_HANDLER (validate); +} + +void +gimp_tile_handler_validate_assign (GimpTileHandlerValidate *validate, + GeglBuffer *buffer) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + g_return_if_fail (gimp_tile_handler_validate_get_assigned (buffer) == NULL); + + gegl_buffer_add_handler (buffer, validate); + + g_object_get (buffer, + "format", &validate->format, + "tile-width", &validate->tile_width, + "tile-height", &validate->tile_height, + NULL); + + g_object_set_data (G_OBJECT (buffer), + "gimp-tile-handler-validate", validate); +} + +void +gimp_tile_handler_validate_unassign (GimpTileHandlerValidate *validate, + GeglBuffer *buffer) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (GEGL_IS_BUFFER (buffer)); + g_return_if_fail (gimp_tile_handler_validate_get_assigned (buffer) == validate); + + g_object_set_data (G_OBJECT (buffer), + "gimp-tile-handler-validate", NULL); + + gegl_buffer_remove_handler (buffer, validate); +} + +GimpTileHandlerValidate * +gimp_tile_handler_validate_get_assigned (GeglBuffer *buffer) +{ + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL); + + return g_object_get_data (G_OBJECT (buffer), + "gimp-tile-handler-validate"); +} + +void +gimp_tile_handler_validate_invalidate (GimpTileHandlerValidate *validate, + const GeglRectangle *rect) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (rect != NULL); + + cairo_region_union_rectangle (validate->dirty_region, + (cairo_rectangle_int_t *) rect); + + gegl_tile_handler_damage_rect (GEGL_TILE_HANDLER (validate), rect); + + g_signal_emit (validate, gimp_tile_handler_validate_signals[INVALIDATED], + 0, rect, NULL); +} + +void +gimp_tile_handler_validate_undo_invalidate (GimpTileHandlerValidate *validate, + const GeglRectangle *rect) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (rect != NULL); + + cairo_region_subtract_rectangle (validate->dirty_region, + (cairo_rectangle_int_t *) rect); +} + +void +gimp_tile_handler_validate_begin_validate (GimpTileHandlerValidate *validate) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + + if (validate->validating++ == 0) + GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->begin_validate (validate); +} + +void +gimp_tile_handler_validate_end_validate (GimpTileHandlerValidate *validate) +{ + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (validate->validating > 0); + + if (--validate->validating == 0) + GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->end_validate (validate); +} + +void +gimp_tile_handler_validate_validate (GimpTileHandlerValidate *validate, + GeglBuffer *buffer, + const GeglRectangle *rect, + gboolean intersect, + gboolean chunked) +{ + GimpTileHandlerValidateClass *klass; + cairo_region_t *region = NULL; + + g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate)); + g_return_if_fail (gimp_tile_handler_validate_get_assigned (buffer) == + validate); + + klass = GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate); + + if (! rect) + rect = gegl_buffer_get_extent (buffer); + + if (intersect) + { + region = cairo_region_copy (validate->dirty_region); + + cairo_region_intersect_rectangle (region, + (const cairo_rectangle_int_t *) rect); + } + else if (chunked) + { + region = cairo_region_create_rectangle ( + (const cairo_rectangle_int_t *) rect); + } + + if (region) + { + if (! cairo_region_is_empty (region)) + { + gimp_tile_handler_validate_begin_validate (validate); + + if (chunked) + { + GimpChunkIterator *iter; + + iter = gimp_chunk_iterator_new (region); + region = NULL; + + while (gimp_chunk_iterator_next (iter)) + { + GeglRectangle blit_rect; + + while (gimp_chunk_iterator_get_rect (iter, &blit_rect)) + klass->validate_buffer (validate, &blit_rect, buffer); + } + } + else + { + gint n_rects; + gint i; + + n_rects = cairo_region_num_rectangles (region); + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t blit_rect; + + cairo_region_get_rectangle (region, i, &blit_rect); + + klass->validate_buffer (validate, + (const GeglRectangle *) &blit_rect, + buffer); + } + } + + gimp_tile_handler_validate_end_validate (validate); + + cairo_region_subtract_rectangle ( + validate->dirty_region, + (const cairo_rectangle_int_t *) rect); + } + + g_clear_pointer (®ion, cairo_region_destroy); + } + else + { + gimp_tile_handler_validate_begin_validate (validate); + + klass->validate_buffer (validate, rect, buffer); + + gimp_tile_handler_validate_end_validate (validate); + + cairo_region_subtract_rectangle ( + validate->dirty_region, + (const cairo_rectangle_int_t *) rect); + } +} + +gboolean +gimp_tile_handler_validate_buffer_set_extent (GeglBuffer *buffer, + const GeglRectangle *extent) +{ + GimpTileHandlerValidate *validate; + + g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE); + g_return_val_if_fail (extent != NULL, FALSE); + + validate = gimp_tile_handler_validate_get_assigned (buffer); + + g_return_val_if_fail (validate != NULL, FALSE); + + validate->suspend_validate++; + + if (gimp_gegl_buffer_set_extent (buffer, extent)) + { + validate->suspend_validate--; + + cairo_region_intersect_rectangle (validate->dirty_region, + (const cairo_rectangle_int_t *) extent); + + return TRUE; + } + + validate->suspend_validate--; + + return FALSE; +} + +void +gimp_tile_handler_validate_buffer_copy (GeglBuffer *src_buffer, + const GeglRectangle *src_rect, + GeglBuffer *dst_buffer, + const GeglRectangle *dst_rect) +{ + GimpTileHandlerValidate *src_validate; + GimpTileHandlerValidate *dst_validate; + GeglRectangle real_src_rect; + GeglRectangle real_dst_rect; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (GEGL_IS_BUFFER (dst_buffer)); + g_return_if_fail (src_rect != dst_rect); + + src_validate = gimp_tile_handler_validate_get_assigned (src_buffer); + dst_validate = gimp_tile_handler_validate_get_assigned (dst_buffer); + + g_return_if_fail (dst_validate != NULL); + + if (! src_rect) + src_rect = gegl_buffer_get_extent (src_buffer); + + if (! dst_rect) + dst_rect = src_rect; + + real_src_rect = *src_rect; + + gegl_rectangle_intersect (&real_dst_rect, + dst_rect, gegl_buffer_get_extent (dst_buffer)); + + real_src_rect.x += real_dst_rect.x - dst_rect->x; + real_src_rect.y += real_dst_rect.y - dst_rect->y; + real_src_rect.width -= real_dst_rect.x - dst_rect->x; + real_src_rect.height -= real_dst_rect.y - dst_rect->y; + + real_src_rect.width = CLAMP (real_src_rect.width, 0, real_dst_rect.width); + real_src_rect.height = CLAMP (real_src_rect.height, 0, real_dst_rect.height); + + /* temporarily remove the source buffer's validate handler, so that + * gegl_buffer_copy() can use fast tile copying, using the TILE_COPY command. + * currently, gegl only uses TILE_COPY when the source buffer has no user- + * provided tile handlers. + */ + if (src_validate) + { + g_object_ref (src_validate); + + gimp_tile_handler_validate_unassign (src_validate, src_buffer); + } + + dst_validate->suspend_validate++; + + gimp_gegl_buffer_copy (src_buffer, &real_src_rect, GEGL_ABYSS_NONE, + dst_buffer, &real_dst_rect); + + dst_validate->suspend_validate--; + + if (src_validate) + { + gimp_tile_handler_validate_assign (src_validate, src_buffer); + + g_object_unref (src_validate); + } + + cairo_region_subtract_rectangle (dst_validate->dirty_region, + (cairo_rectangle_int_t *) &real_dst_rect); + + if (src_validate) + { + if (real_src_rect.x == real_dst_rect.x && + real_src_rect.y == real_dst_rect.y && + gegl_rectangle_equal (&real_src_rect, + gegl_buffer_get_extent (src_buffer))) + { + cairo_region_union (dst_validate->dirty_region, + src_validate->dirty_region); + } + else if (cairo_region_contains_rectangle ( + src_validate->dirty_region, + (cairo_rectangle_int_t *) &real_src_rect) != + CAIRO_REGION_OVERLAP_OUT) + { + cairo_region_t *region; + + region = cairo_region_copy (src_validate->dirty_region); + + if (! gegl_rectangle_equal (&real_src_rect, + gegl_buffer_get_extent (src_buffer))) + { + cairo_region_intersect_rectangle ( + region, (cairo_rectangle_int_t *) &real_src_rect); + } + + cairo_region_translate (region, + real_dst_rect.x - real_src_rect.x, + real_dst_rect.y - real_src_rect.y); + + if (cairo_region_is_empty (dst_validate->dirty_region)) + { + cairo_region_destroy (dst_validate->dirty_region); + + dst_validate->dirty_region = region; + } + else + { + cairo_region_union (dst_validate->dirty_region, region); + + cairo_region_destroy (region); + } + } + } +} |