void gimp_paint_core_validate_saved_proj_tiles (GimpPaintCore *core, GimpPickable *pickable, gint x, gint y, gint w, gint h) { gint i, j; g_return_if_fail (GIMP_IS_PAINT_CORE (core)); g_return_if_fail (GIMP_IS_PICKABLE (pickable)); g_return_if_fail (core->saved_proj_tiles != NULL); for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT))) { for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH))) { Tile *dest_tile = tile_manager_get_tile (core->saved_proj_tiles, j, i, FALSE, FALSE); if (! tile_is_valid (dest_tile)) { Tile *src_tile = tile_manager_get_tile (gimp_pickable_get_tiles (pickable), j, i, TRUE, FALSE); tile_manager_map_tile (core->saved_proj_tiles, j, i, src_tile); tile_release (src_tile, FALSE); } } } }
/** * gimp_projection_initialize: * @proj: A #GimpProjection. * @x: * @y: * @w: * @h: * * This function determines whether a visible layer with combine mode * Normal provides complete coverage over the specified area. If not, * the projection is initialized to transparent black. */ static void gimp_projection_initialize (GimpProjection *proj, gint x, gint y, gint w, gint h) { GList *list; gint proj_off_x; gint proj_off_y; gboolean coverage = FALSE; gimp_projectable_get_offset (proj->projectable, &proj_off_x, &proj_off_y); for (list = gimp_projectable_get_layers (proj->projectable); list; list = g_list_next (list)) { GimpLayer *layer = list->data; GimpDrawable *drawable = GIMP_DRAWABLE (layer); GimpItem *item = GIMP_ITEM (layer); gint off_x, off_y; gimp_item_get_offset (item, &off_x, &off_y); /* subtract the projectable's offsets because the list of * update areas is in tile-pyramid coordinates, but our * external API is always in terms of image coordinates. */ off_x -= proj_off_x; off_y -= proj_off_y; if (gimp_item_get_visible (item) && ! gimp_drawable_has_alpha (drawable) && ! gimp_layer_get_mask (layer) && gimp_layer_get_mode (layer) == GIMP_NORMAL_MODE && gimp_layer_get_opacity (layer) == GIMP_OPACITY_OPAQUE && off_x <= x && off_y <= y && (off_x + gimp_item_get_width (item)) >= (x + w) && (off_y + gimp_item_get_height (item)) >= (y + h)) { coverage = TRUE; break; } } if (! coverage) { PixelRegion region; pixel_region_init (®ion, gimp_pickable_get_tiles (GIMP_PICKABLE (proj)), x, y, w, h, TRUE); clear_region (®ion); } }
void gimp_projection_construct (GimpProjection *proj, gint x, gint y, gint w, gint h) { g_return_if_fail (GIMP_IS_PROJECTION (proj)); #if 0 GList *layers = gimp_projectable_get_layers (proj->projectable); if (layers && ! layers->next) /* a single layer */ { GimpLayer *layer = layers->data; GimpDrawable *drawable = GIMP_DRAWABLE (layer); GimpItem *item = GIMP_ITEM (layer); gint width, height; gint off_x, off_y; gimp_projectable_get_offset (proj->projectable, &proj_off_x, &proj_off_y); gimp_projectable_get_size (proj->projectable, &width, &height); gimp_item_get_offset (item, &off_x, &off_y); if (gimp_drawable_has_alpha (drawable) && gimp_item_get_visible (item) && gimp_item_get_width (item) == width && gimp_item_get_height (item) == height && ! gimp_drawable_is_indexed (layer) && gimp_layer_get_opacity (layer) == GIMP_OPACITY_OPAQUE && off_x == 0 && off_y == 0 && proj_offset_x == 0 && proj_offset_y == 0) { PixelRegion srcPR, destPR; g_printerr ("cow-projection!"); pixel_region_init (&srcPR, gimp_drawable_get_tiles (layer), x, y, w,h, FALSE); pixel_region_init (&destPR, gimp_pickable_get_tiles (GIMP_PICKABLE (proj)), x, y, w,h, TRUE); copy_region (&srcPR, &destPR); proj->construct_flag = TRUE; gimp_projection_construct_legacy (proj, FALSE, x, y, w, h); return; } } #endif /* First, determine if the projection image needs to be * initialized--this is the case when there are no visible * layers that cover the entire canvas--either because layers * are offset or only a floating selection is visible */ gimp_projection_initialize (proj, x, y, w, h); /* call functions which process the list of layers and * the list of channels */ if (proj->use_gegl) { gimp_projection_construct_gegl (proj, x, y, w, h); } else { proj->construct_flag = FALSE; gimp_projection_construct_legacy (proj, TRUE, x, y, w, h); } }
static void gimp_projection_construct_legacy (GimpProjection *proj, gboolean with_layers, gint x, gint y, gint w, gint h) { GList *list; GList *reverse_list = NULL; gint proj_off_x; gint proj_off_y; for (list = gimp_projectable_get_channels (proj->projectable); list; list = g_list_next (list)) { if (gimp_item_get_visible (GIMP_ITEM (list->data))) { reverse_list = g_list_prepend (reverse_list, list->data); } } if (with_layers) { for (list = gimp_projectable_get_layers (proj->projectable); list; list = g_list_next (list)) { GimpLayer *layer = list->data; if (! gimp_layer_is_floating_sel (layer) && gimp_item_get_visible (GIMP_ITEM (layer))) { /* only add layers that are visible and not floating selections * to the list */ reverse_list = g_list_prepend (reverse_list, layer); } } } gimp_projectable_get_offset (proj->projectable, &proj_off_x, &proj_off_y); for (list = reverse_list; list; list = g_list_next (list)) { GimpItem *item = list->data; PixelRegion projPR; gint x1, y1; gint x2, y2; gint off_x; gint off_y; gimp_item_get_offset (item, &off_x, &off_y); /* subtract the projectable's offsets because the list of * update areas is in tile-pyramid coordinates, but our * external API is always in terms of image coordinates. */ off_x -= proj_off_x; off_y -= proj_off_y; x1 = CLAMP (off_x, x, x + w); y1 = CLAMP (off_y, y, y + h); x2 = CLAMP (off_x + gimp_item_get_width (item), x, x + w); y2 = CLAMP (off_y + gimp_item_get_height (item), y, y + h); pixel_region_init (&projPR, gimp_pickable_get_tiles (GIMP_PICKABLE (proj)), x1, y1, x2 - x1, y2 - y1, TRUE); gimp_drawable_project_region (GIMP_DRAWABLE (item), x1 - off_x, y1 - off_y, x2 - x1, y2 - y1, &projPR, proj->construct_flag); proj->construct_flag = TRUE; /* something was projected */ } g_list_free (reverse_list); }
static gboolean gimp_source_core_real_get_source (GimpSourceCore *source_core, GimpDrawable *drawable, GimpPaintOptions *paint_options, GimpPickable *src_pickable, gint src_offset_x, gint src_offset_y, TempBuf *paint_area, gint *paint_area_offset_x, gint *paint_area_offset_y, gint *paint_area_width, gint *paint_area_height, PixelRegion *srcPR) { GimpSourceOptions *options = GIMP_SOURCE_OPTIONS (paint_options); GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); GimpImage *src_image = gimp_pickable_get_image (src_pickable); TileManager *src_tiles = gimp_pickable_get_tiles (src_pickable); gint x1, y1; gint x2, y2; x1 = CLAMP (paint_area->x + src_offset_x, 0, tile_manager_width (src_tiles)); y1 = CLAMP (paint_area->y + src_offset_y, 0, tile_manager_height (src_tiles)); x2 = CLAMP (paint_area->x + src_offset_x + paint_area->width, 0, tile_manager_width (src_tiles)); y2 = CLAMP (paint_area->y + src_offset_y + paint_area->height, 0, tile_manager_height (src_tiles)); if (!(x2 - x1) || !(y2 - y1)) return FALSE; /* If the source image is different from the destination, * then we should copy straight from the source image * to the canvas. * Otherwise, we need a call to get_orig_image to make sure * we get a copy of the unblemished (offset) image */ if (( options->sample_merged && (src_image != image)) || (! options->sample_merged && (source_core->src_drawable != drawable))) { pixel_region_init (srcPR, src_tiles, x1, y1, x2 - x1, y2 - y1, FALSE); } else { TempBuf *orig; /* get the original image */ if (options->sample_merged) orig = gimp_paint_core_get_orig_proj (GIMP_PAINT_CORE (source_core), src_pickable, x1, y1, x2, y2); else orig = gimp_paint_core_get_orig_image (GIMP_PAINT_CORE (source_core), GIMP_DRAWABLE (src_pickable), x1, y1, x2, y2); pixel_region_init_temp_buf (srcPR, orig, 0, 0, x2 - x1, y2 - y1); } *paint_area_offset_x = x1 - (paint_area->x + src_offset_x); *paint_area_offset_y = y1 - (paint_area->y + src_offset_y); *paint_area_width = x2 - x1; *paint_area_height = y2 - y1; return TRUE; }
gboolean gimp_image_crop_auto_shrink (GimpImage *image, gint x1, gint y1, gint x2, gint y2, gboolean active_drawable_only, gint *shrunk_x1, gint *shrunk_y1, gint *shrunk_x2, gint *shrunk_y2) { GimpDrawable *active_drawable = NULL; GimpPickable *pickable; ColorsEqualFunc colors_equal_func; guchar bgcolor[MAX_CHANNELS] = { 0, 0, 0, 0 }; gboolean has_alpha; PixelRegion PR; guchar *buffer = NULL; gint width, height; GimpImageType type; gint bytes; gint x, y, abort; gboolean retval = FALSE; g_return_val_if_fail (image != NULL, FALSE); g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE); g_return_val_if_fail (shrunk_x1 != NULL, FALSE); g_return_val_if_fail (shrunk_y1 != NULL, FALSE); g_return_val_if_fail (shrunk_x2 != NULL, FALSE); g_return_val_if_fail (shrunk_y2 != NULL, FALSE); gimp_set_busy (image->gimp); /* You should always keep in mind that crop->tx2 and crop->ty2 are the NOT the coordinates of the bottomright corner of the area to be cropped. They point at the pixel located one to the right and one to the bottom. */ if (active_drawable_only) { active_drawable = gimp_image_get_active_drawable (image); if (! active_drawable) goto FINISH; pickable = GIMP_PICKABLE (active_drawable); } else { pickable = GIMP_PICKABLE (image->projection); } gimp_pickable_flush (pickable); type = gimp_pickable_get_image_type (pickable); bytes = GIMP_IMAGE_TYPE_BYTES (type); has_alpha = GIMP_IMAGE_TYPE_HAS_ALPHA (type); switch (gimp_image_crop_guess_bgcolor (pickable, bytes, has_alpha, bgcolor, x1, x2-1, y1, y2-1)) { case AUTO_CROP_ALPHA: colors_equal_func = (ColorsEqualFunc) gimp_image_crop_colors_alpha; break; case AUTO_CROP_COLOR: colors_equal_func = (ColorsEqualFunc) gimp_image_crop_colors_equal; break; default: goto FINISH; break; } width = x2 - x1; height = y2 - y1; pixel_region_init (&PR, gimp_pickable_get_tiles (pickable), x1, y1, width, height, FALSE); /* The following could be optimized further by processing * the smaller side first instead of defaulting to width --Sven */ buffer = g_malloc ((width > height ? width : height) * bytes); /* Check how many of the top lines are uniform/transparent. */ abort = FALSE; for (y = y1; y < y2 && !abort; y++) { pixel_region_get_row (&PR, x1, y, width, buffer, 1); for (x = 0; x < width && !abort; x++) abort = !(colors_equal_func) (bgcolor, buffer + x * bytes, bytes); } if (y == y2 && !abort) goto FINISH; y1 = y - 1; /* Check how many of the bottom lines are uniform/transparent. */ abort = FALSE; for (y = y2; y > y1 && !abort; y--) { pixel_region_get_row (&PR, x1, y-1 , width, buffer, 1); for (x = 0; x < width && !abort; x++) abort = !(colors_equal_func) (bgcolor, buffer + x * bytes, bytes); } y2 = y + 1; /* compute a new height for the next operations */ height = y2 - y1; /* Check how many of the left lines are uniform/transparent. */ abort = FALSE; for (x = x1; x < x2 && !abort; x++) { pixel_region_get_col (&PR, x, y1, height, buffer, 1); for (y = 0; y < height && !abort; y++) abort = !(colors_equal_func) (bgcolor, buffer + y * bytes, bytes); } x1 = x - 1; /* Check how many of the right lines are uniform/transparent. */ abort = FALSE; for (x = x2; x > x1 && !abort; x--) { pixel_region_get_col (&PR, x-1, y1, height, buffer, 1); for (y = 0; y < height && !abort; y++) abort = !(colors_equal_func) (bgcolor, buffer + y * bytes, bytes); } x2 = x + 1; *shrunk_x1 = x1; *shrunk_y1 = y1; *shrunk_x2 = x2; *shrunk_y2 = y2; retval = TRUE; FINISH: g_free (buffer); gimp_unset_busy (image->gimp); return retval; }
TempBuf * gimp_paint_core_get_orig_proj (GimpPaintCore *core, GimpPickable *pickable, gint x, gint y, gint width, gint height) { TileManager *src_tiles; PixelRegion srcPR; PixelRegion destPR; Tile *saved_tile; gboolean release_tile; gint h; gint pixelwidth; gint pickable_width; gint pickable_height; const guchar *s; guchar *d; gpointer pr; g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), NULL); g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL); g_return_val_if_fail (core->saved_proj_tiles != NULL, NULL); core->orig_proj_buf = temp_buf_resize (core->orig_proj_buf, gimp_pickable_get_bytes (pickable), x, y, width, height); src_tiles = gimp_pickable_get_tiles (pickable); pickable_width = tile_manager_width (src_tiles); pickable_height = tile_manager_height (src_tiles); gimp_rectangle_intersect (x, y, width, height, 0, 0, pickable_width, pickable_height, &x, &y, &width, &height); /* configure the pixel regions */ pixel_region_init (&srcPR, src_tiles, x, y, width, height, FALSE); pixel_region_init_temp_buf (&destPR, core->orig_proj_buf, x - core->orig_proj_buf->x, y - core->orig_proj_buf->y, width, height); for (pr = pixel_regions_register (2, &srcPR, &destPR); pr != NULL; pr = pixel_regions_process (pr)) { /* If the saved tile corresponding to this location is valid, use it */ saved_tile = tile_manager_get_tile (core->saved_proj_tiles, srcPR.x, srcPR.y, FALSE, FALSE); if (tile_is_valid (saved_tile)) { release_tile = TRUE; saved_tile = tile_manager_get_tile (core->saved_proj_tiles, srcPR.x, srcPR.y, TRUE, FALSE); s = tile_data_pointer (saved_tile, srcPR.x, srcPR.y); } else { release_tile = FALSE; s = srcPR.data; } d = destPR.data; pixelwidth = srcPR.w * srcPR.bytes; h = srcPR.h; while (h --) { memcpy (d, s, pixelwidth); s += srcPR.rowstride; d += destPR.rowstride; } if (release_tile) tile_release (saved_tile, FALSE); } return core->orig_proj_buf; }
gboolean gimp_paint_core_start (GimpPaintCore *core, GimpDrawable *drawable, GimpPaintOptions *paint_options, const GimpCoords *coords, GError **error) { GimpItem *item; g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), FALSE); g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE); g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), FALSE); g_return_val_if_fail (coords != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); item = GIMP_ITEM (drawable); if (core->stroke_buffer) { g_array_free (core->stroke_buffer, TRUE); core->stroke_buffer = NULL; } core->stroke_buffer = g_array_sized_new (TRUE, TRUE, sizeof (GimpCoords), STROKE_BUFFER_INIT_SIZE); core->cur_coords = *coords; if (! GIMP_PAINT_CORE_GET_CLASS (core)->start (core, drawable, paint_options, coords, error)) { return FALSE; } /* Allocate the undo structure */ if (core->undo_tiles) tile_manager_unref (core->undo_tiles); core->undo_tiles = tile_manager_new (gimp_item_get_width (item), gimp_item_get_height (item), gimp_drawable_bytes (drawable)); /* Allocate the saved proj structure */ if (core->saved_proj_tiles) tile_manager_unref (core->saved_proj_tiles); core->saved_proj_tiles = NULL; if (core->use_saved_proj) { GimpImage *image = gimp_item_get_image (item); GimpPickable *pickable = GIMP_PICKABLE (gimp_image_get_projection (image)); TileManager *tiles = gimp_pickable_get_tiles (pickable); core->saved_proj_tiles = tile_manager_new (tile_manager_width (tiles), tile_manager_height (tiles), tile_manager_bpp (tiles)); } /* Allocate the canvas blocks structure */ if (core->canvas_tiles) tile_manager_unref (core->canvas_tiles); core->canvas_tiles = tile_manager_new (gimp_item_get_width (item), gimp_item_get_height (item), 1); /* Get the initial undo extents */ core->x1 = core->x2 = core->cur_coords.x; core->y1 = core->y2 = core->cur_coords.y; core->last_paint.x = -1e6; core->last_paint.y = -1e6; /* Freeze the drawable preview so that it isn't constantly updated. */ gimp_viewable_preview_freeze (GIMP_VIEWABLE (drawable)); return TRUE; }
static gboolean gimp_perspective_clone_get_source (GimpSourceCore *source_core, GimpDrawable *drawable, GimpPaintOptions *paint_options, GimpPickable *src_pickable, gint src_offset_x, gint src_offset_y, TempBuf *paint_area, gint *paint_area_offset_x, gint *paint_area_offset_y, gint *paint_area_width, gint *paint_area_height, PixelRegion *srcPR) { GimpPerspectiveClone *clone = GIMP_PERSPECTIVE_CLONE (source_core); GimpPaintCore *paint_core = GIMP_PAINT_CORE (source_core); GimpSourceOptions *options = GIMP_SOURCE_OPTIONS (paint_options); GimpImage *src_image; GimpImage *image; GimpImageType src_type; gint x1d, y1d, x2d, y2d; gdouble x1s, y1s, x2s, y2s, x3s, y3s, x4s, y4s; gint xmin, ymin, xmax, ymax; TileManager *src_tiles; TileManager *orig_tiles; PixelRegion origPR; PixelRegion destPR; GimpMatrix3 matrix; gint bytes; src_image = gimp_pickable_get_image (src_pickable); image = gimp_item_get_image (GIMP_ITEM (drawable)); src_type = gimp_pickable_get_image_type (src_pickable); src_tiles = gimp_pickable_get_tiles (src_pickable); /* Destination coordinates that will be painted */ x1d = paint_area->x; y1d = paint_area->y; x2d = paint_area->x + paint_area->width; y2d = paint_area->y + paint_area->height; /* Boundary box for source pixels to copy: Convert all the vertex of * the box to paint in destination area to its correspondent in * source area bearing in mind perspective */ gimp_perspective_clone_get_source_point (clone, x1d, y1d, &x1s, &y1s); gimp_perspective_clone_get_source_point (clone, x1d, y2d, &x2s, &y2s); gimp_perspective_clone_get_source_point (clone, x2d, y1d, &x3s, &y3s); gimp_perspective_clone_get_source_point (clone, x2d, y2d, &x4s, &y4s); xmin = floor (MIN4 (x1s, x2s, x3s, x4s)); ymin = floor (MIN4 (y1s, y2s, y3s, y4s)); xmax = ceil (MAX4 (x1s, x2s, x3s, x4s)); ymax = ceil (MAX4 (y1s, y2s, y3s, y4s)); xmin = CLAMP (xmin - 1, 0, tile_manager_width (src_tiles)); ymin = CLAMP (ymin - 1, 0, tile_manager_height (src_tiles)); xmax = CLAMP (xmax + 1, 0, tile_manager_width (src_tiles)); ymax = CLAMP (ymax + 1, 0, tile_manager_height (src_tiles)); /* if the source area is completely out of the image */ if (!(xmax - xmin) || !(ymax - ymin)) return FALSE; /* If the source image is different from the destination, * then we should copy straight from the source image * to the canvas. * Otherwise, we need a call to get_orig_image to make sure * we get a copy of the unblemished (offset) image */ if (( options->sample_merged && (src_image != image)) || (! options->sample_merged && (source_core->src_drawable != drawable))) { pixel_region_init (&origPR, src_tiles, xmin, ymin, xmax - xmin, ymax - ymin, FALSE); } else { TempBuf *orig; /* get the original image */ if (options->sample_merged) orig = gimp_paint_core_get_orig_proj (paint_core, src_pickable, xmin, ymin, xmax, ymax); else orig = gimp_paint_core_get_orig_image (paint_core, GIMP_DRAWABLE (src_pickable), xmin, ymin, xmax, ymax); pixel_region_init_temp_buf (&origPR, orig, 0, 0, xmax - xmin, ymax - ymin); } /* copy the original image to a tile manager, adding alpha if needed */ bytes = GIMP_IMAGE_TYPE_BYTES (GIMP_IMAGE_TYPE_WITH_ALPHA (src_type)); orig_tiles = tile_manager_new (xmax - xmin, ymax - ymin, bytes); tile_manager_set_offsets (orig_tiles, xmin, ymin); pixel_region_init (&destPR, orig_tiles, 0, 0, xmax - xmin, ymax - ymin, TRUE); if (bytes > origPR.bytes) add_alpha_region (&origPR, &destPR); else copy_region (&origPR, &destPR); clone->src_area = temp_buf_resize (clone->src_area, tile_manager_bpp (orig_tiles), 0, 0, x2d - x1d, y2d - y1d); pixel_region_init_temp_buf (&destPR, clone->src_area, 0, 0, x2d - x1d, y2d - y1d); gimp_perspective_clone_get_matrix (clone, &matrix); gimp_transform_region (src_pickable, GIMP_CONTEXT (paint_options), orig_tiles, &destPR, x1d, y1d, x2d, y2d, &matrix, GIMP_INTERPOLATION_LINEAR, 0, NULL); tile_manager_unref (orig_tiles); pixel_region_init_temp_buf (srcPR, clone->src_area, 0, 0, x2d - x1d, y2d - y1d); return TRUE; }