/* 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 . */ #include "config.h" #include #include #include "gimp-gegl-types.h" #include "gimptilehandlerprojection.h" enum { PROP_0, PROP_FORMAT, PROP_TILE_WIDTH, PROP_TILE_HEIGHT }; static void gimp_tile_handler_projection_finalize (GObject *object); static void gimp_tile_handler_projection_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gimp_tile_handler_projection_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gpointer gimp_tile_handler_projection_command (GeglTileSource *source, GeglTileCommand command, gint x, gint y, gint z, gpointer data); G_DEFINE_TYPE (GimpTileHandlerProjection, gimp_tile_handler_projection, GEGL_TYPE_TILE_HANDLER) #define parent_class gimp_tile_handler_projection_parent_class static void gimp_tile_handler_projection_class_init (GimpTileHandlerProjectionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gimp_tile_handler_projection_finalize; object_class->set_property = gimp_tile_handler_projection_set_property; object_class->get_property = gimp_tile_handler_projection_get_property; 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)); } static void gimp_tile_handler_projection_init (GimpTileHandlerProjection *projection) { GeglTileSource *source = GEGL_TILE_SOURCE (projection); source->command = gimp_tile_handler_projection_command; projection->dirty_region = cairo_region_create (); } static void gimp_tile_handler_projection_finalize (GObject *object) { GimpTileHandlerProjection *projection = GIMP_TILE_HANDLER_PROJECTION (object); if (projection->graph) { g_object_unref (projection->graph); projection->graph = NULL; } cairo_region_destroy (projection->dirty_region); projection->dirty_region = NULL; G_OBJECT_CLASS (parent_class)->finalize (object); } static void gimp_tile_handler_projection_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GimpTileHandlerProjection *projection = GIMP_TILE_HANDLER_PROJECTION (object); switch (property_id) { case PROP_FORMAT: projection->format = g_value_get_pointer (value); break; case PROP_TILE_WIDTH: projection->tile_width = g_value_get_int (value); break; case PROP_TILE_HEIGHT: projection->tile_height = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gimp_tile_handler_projection_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GimpTileHandlerProjection *projection = GIMP_TILE_HANDLER_PROJECTION (object); switch (property_id) { case PROP_FORMAT: g_value_set_pointer (value, (gpointer) projection->format); break; case PROP_TILE_WIDTH: g_value_set_int (value, projection->tile_width); break; case PROP_TILE_HEIGHT: g_value_set_int (value, projection->tile_height); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static GeglTile * gimp_tile_handler_projection_validate (GeglTileSource *source, GeglTile *tile, gint x, gint y) { GimpTileHandlerProjection *projection; cairo_region_t *tile_region; cairo_rectangle_int_t tile_rect; projection = GIMP_TILE_HANDLER_PROJECTION (source); if (cairo_region_is_empty (projection->dirty_region)) return tile; tile_region = cairo_region_copy (projection->dirty_region); tile_rect.x = x * projection->tile_width; tile_rect.y = y * projection->tile_height; tile_rect.width = projection->tile_width; tile_rect.height = projection->tile_height; cairo_region_intersect_rectangle (tile_region, &tile_rect); if (! cairo_region_is_empty (tile_region)) { gint tile_bpp; gint tile_stride; gint n_rects; gint i; if (! tile) tile = gegl_tile_handler_create_tile (GEGL_TILE_HANDLER (source), x, y, 0); cairo_region_subtract_rectangle (projection->dirty_region, &tile_rect); tile_bpp = babl_format_get_bytes_per_pixel (projection->format); tile_stride = tile_bpp * projection->tile_width; gegl_tile_lock (tile); n_rects = cairo_region_num_rectangles (tile_region); #if 0 g_printerr ("%d ", n_rects); #endif for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t blit_rect; cairo_region_get_rectangle (tile_region, i, &blit_rect); #if 0 g_printerr ("constructing projection at %d %d %d %d\n", blit_rect.x, blit_rect.y, blit_rect.width, blit_rect.height); #endif gegl_node_blit (projection->graph, 1.0, GEGL_RECTANGLE (blit_rect.x, blit_rect.y, blit_rect.width, blit_rect.height), projection->format, gegl_tile_get_data (tile) + (blit_rect.y % projection->tile_height) * tile_stride + (blit_rect.x % projection->tile_width) * tile_bpp, tile_stride, GEGL_BLIT_DEFAULT); } gegl_tile_unlock (tile); } cairo_region_destroy (tile_region); return tile; } static gpointer gimp_tile_handler_projection_command (GeglTileSource *source, GeglTileCommand command, gint x, gint y, gint z, gpointer data) { gpointer retval; retval = gegl_tile_handler_source_command (source, command, x, y, z, data); if (command == GEGL_TILE_GET && z == 0) retval = gimp_tile_handler_projection_validate (source, retval, x, y); return retval; } GeglTileHandler * gimp_tile_handler_projection_new (GeglNode *graph) { GimpTileHandlerProjection *projection; g_return_val_if_fail (GEGL_IS_NODE (graph), NULL); projection = g_object_new (GIMP_TYPE_TILE_HANDLER_PROJECTION, NULL); projection->graph = g_object_ref (graph); return GEGL_TILE_HANDLER (projection); } static void gimp_tile_handler_projection_void_pyramid (GeglTileSource *source, gint x, gint y, gint z) { gegl_tile_source_void (source, x, y, z); if (x / 2 != x || y / 2 != y) gimp_tile_handler_projection_void_pyramid (source, x / 2, y / 2, z + 1); } void gimp_tile_handler_projection_invalidate (GimpTileHandlerProjection *projection, gint x, gint y, gint width, gint height) { cairo_rectangle_int_t rect = { x, y, width, height }; gint tile_x1; gint tile_y1; gint tile_x2; gint tile_y2; gint tile_x; gint tile_y; g_return_if_fail (GIMP_IS_TILE_HANDLER_PROJECTION (projection)); cairo_region_union_rectangle (projection->dirty_region, &rect); tile_x1 = x / projection->tile_width; tile_y1 = y / projection->tile_height; tile_x2 = (x + width - 1) / projection->tile_width; tile_y2 = (y + height - 1) / projection->tile_height; for (tile_y = tile_y1; tile_y <= tile_y2; tile_y++) { for (tile_x = tile_x1; tile_x <= tile_x2; tile_x++) { gimp_tile_handler_projection_void_pyramid (GEGL_TILE_SOURCE (projection), tile_x / 2, tile_y / 2, 1); } } }