void gimp_channel_combine_mask (GimpChannel *mask, GimpChannel *add_on, GimpChannelOps op, gint off_x, gint off_y) { PixelRegion srcPR, destPR; gint x, y, w, h; g_return_if_fail (GIMP_IS_CHANNEL (mask)); g_return_if_fail (GIMP_IS_CHANNEL (add_on)); if (! gimp_rectangle_intersect (off_x, off_y, gimp_item_get_width (GIMP_ITEM (add_on)), gimp_item_get_height (GIMP_ITEM (add_on)), 0, 0, gimp_item_get_width (GIMP_ITEM (mask)), gimp_item_get_height (GIMP_ITEM (mask)), &x, &y, &w, &h)) return; pixel_region_init (&srcPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (add_on)), x - off_x, y - off_y, w, h, FALSE); pixel_region_init (&destPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), x, y, w, h, TRUE); switch (op) { case GIMP_CHANNEL_OP_ADD: case GIMP_CHANNEL_OP_REPLACE: pixel_regions_process_parallel ((PixelProcessorFunc) gimp_channel_combine_sub_region_add, NULL, 2, &srcPR, &destPR); break; case GIMP_CHANNEL_OP_SUBTRACT: pixel_regions_process_parallel ((PixelProcessorFunc) gimp_channel_combine_sub_region_sub, NULL, 2, &srcPR, &destPR); break; case GIMP_CHANNEL_OP_INTERSECT: pixel_regions_process_parallel ((PixelProcessorFunc) gimp_channel_combine_sub_region_intersect, NULL, 2, &srcPR, &destPR); break; default: g_warning ("%s: unknown operation type", G_STRFUNC); break; } mask->bounds_known = FALSE; gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h); }
void floating_sel_restore (GimpLayer *layer, gint x, gint y, gint w, gint h) { PixelRegion srcPR, destPR; gint offx, offy; gint x1, y1, x2, y2; g_return_if_fail (GIMP_IS_LAYER (layer)); g_return_if_fail (gimp_layer_is_floating_sel (layer)); /* What this function does is "uncover" the specified area in the * drawable that this floating selection obscures. We do this so * that either the floating selection can be removed or it can be * translated */ /* Find the minimum area we need to uncover -- in image space */ gimp_item_offsets (GIMP_ITEM (layer->fs.drawable), &offx, &offy); x1 = MAX (GIMP_ITEM (layer)->offset_x, offx); y1 = MAX (GIMP_ITEM (layer)->offset_y, offy); x2 = MIN (GIMP_ITEM (layer)->offset_x + GIMP_ITEM (layer)->width, offx + gimp_item_width (GIMP_ITEM (layer->fs.drawable))); y2 = MIN (GIMP_ITEM(layer)->offset_y + GIMP_ITEM (layer)->height, offy + gimp_item_height (GIMP_ITEM (layer->fs.drawable))); x1 = CLAMP (x, x1, x2); y1 = CLAMP (y, y1, y2); x2 = CLAMP (x + w, x1, x2); y2 = CLAMP (y + h, y1, y2); if ((x2 - x1) > 0 && (y2 - y1) > 0) { /* Copy the area from the backing store to the drawable */ pixel_region_init (&srcPR, layer->fs.backing_store, (x1 - GIMP_ITEM (layer)->offset_x), (y1 - GIMP_ITEM (layer)->offset_y), (x2 - x1), (y2 - y1), FALSE); pixel_region_init (&destPR, gimp_drawable_get_tiles (layer->fs.drawable), (x1 - offx), (y1 - offy), (x2 - x1), (y2 - y1), TRUE); copy_region (&srcPR, &destPR); } }
static TempBuf * gimp_drawable_indexed_preview (GimpDrawable *drawable, const guchar *cmap, gint src_x, gint src_y, gint src_width, gint src_height, gint dest_width, gint dest_height) { TempBuf *preview_buf; PixelRegion srcPR; PixelRegion destPR; gint bytes = gimp_drawable_preview_bytes (drawable); gint subsample = 1; /* calculate 'acceptable' subsample */ while ((dest_width * (subsample + 1) * 2 < src_width) && (dest_height * (subsample + 1) * 2 < src_width)) subsample += 1; pixel_region_init (&srcPR, gimp_drawable_get_tiles (drawable), src_x, src_y, src_width, src_height, FALSE); preview_buf = temp_buf_new (dest_width, dest_height, bytes, 0, 0, NULL); pixel_region_init_temp_buf (&destPR, preview_buf, 0, 0, dest_width, dest_height); subsample_indexed_region (&srcPR, &destPR, cmap, subsample); return preview_buf; }
GimpBuffer * gimp_buffer_new_from_pixbuf (GdkPixbuf *pixbuf, const gchar *name) { TileManager *tiles; guchar *pixels; PixelRegion destPR; gint width; gint height; gint rowstride; gint channels; gint y; g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); g_return_val_if_fail (name != NULL, NULL); width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); channels = gdk_pixbuf_get_n_channels (pixbuf); tiles = tile_manager_new (width, height, channels); pixel_region_init (&destPR, tiles, 0, 0, width, height, TRUE); for (y = 0, pixels = gdk_pixbuf_get_pixels (pixbuf); y < height; y++, pixels += rowstride) { pixel_region_set_row (&destPR, 0, y, width, pixels); } return gimp_buffer_new (tiles, name, FALSE); }
static gboolean gimp_operation_tile_source_process (GeglOperation *operation, GeglBuffer *output, const GeglRectangle *result) { GimpOperationTileSource *self = GIMP_OPERATION_TILE_SOURCE (operation); const Babl *format; PixelRegion srcPR; gpointer pr; if (! self->tile_manager) return FALSE; format = gegl_operation_get_format (operation, "output"); pixel_region_init (&srcPR, self->tile_manager, result->x, result->y, result->width, result->height, FALSE); for (pr = pixel_regions_register (1, &srcPR); pr; pr = pixel_regions_process (pr)) { GeglRectangle rect = { srcPR.x, srcPR.y, srcPR.w, srcPR.h }; gegl_buffer_set (output, &rect, 1, format, srcPR.data, srcPR.rowstride); } return TRUE; }
static GimpTempBuf * tile_manager_create_preview (TileManager *tiles, const Babl *format, gint src_x, gint src_y, gint src_width, gint src_height, gint dest_width, gint dest_height) { GimpTempBuf *preview; PixelRegion srcPR; PixelRegion destPR; gint subsample = 1; preview = gimp_temp_buf_new (dest_width, dest_height, format); pixel_region_init (&srcPR, tiles, src_x, src_y, src_width, src_height, FALSE); pixel_region_init_temp_buf (&destPR, preview, 0, 0, dest_width, dest_height); /* calculate 'acceptable' subsample */ while ((dest_width * (subsample + 1) * 2 < src_width) && (dest_height * (subsample + 1) * 2 < src_height)) subsample += 1; subsample_region (&srcPR, &destPR, subsample); return preview; }
static GdkSegment * gimp_region_select_tool_calculate (GimpRegionSelectTool *region_sel, GimpDisplay *display, gint *num_segs) { GimpTool *tool = GIMP_TOOL (region_sel); GimpRegionSelectOptions *options = GIMP_REGION_SELECT_TOOL_GET_OPTIONS (tool); GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (display->shell); GimpDrawable *drawable; GdkSegment *segs; BoundSeg *bsegs; PixelRegion maskPR; drawable = gimp_image_get_active_drawable (display->image); gimp_display_shell_set_override_cursor (shell, GDK_WATCH); if (region_sel->region_mask) g_object_unref (region_sel->region_mask); region_sel->region_mask = GIMP_REGION_SELECT_TOOL_GET_CLASS (region_sel)->get_mask (region_sel, display); if (! region_sel->region_mask) { gimp_display_shell_unset_override_cursor (shell); *num_segs = 0; return NULL; } /* calculate and allocate a new segment array which represents the * boundary of the contiguous region */ pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (region_sel->region_mask)), 0, 0, gimp_item_width (GIMP_ITEM (region_sel->region_mask)), gimp_item_height (GIMP_ITEM (region_sel->region_mask)), FALSE); bsegs = boundary_find (&maskPR, BOUNDARY_WITHIN_BOUNDS, 0, 0, gimp_item_width (GIMP_ITEM (region_sel->region_mask)), gimp_item_height (GIMP_ITEM (region_sel->region_mask)), BOUNDARY_HALF_WAY, num_segs); segs = g_new (GdkSegment, *num_segs); gimp_display_shell_transform_segments (shell, bsegs, segs, *num_segs, ! options->sample_merged); g_free (bsegs); gimp_display_shell_unset_override_cursor (shell); return segs; }
/** * 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); } }
GimpBuffer * gimp_buffer_new (TileManager *tiles, const gchar *name, gboolean copy_pixels) { GimpBuffer *buffer; gint width, height; g_return_val_if_fail (tiles != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); width = tile_manager_width (tiles); height = tile_manager_height (tiles); buffer = g_object_new (GIMP_TYPE_BUFFER, "name", name, NULL); if (copy_pixels) { PixelRegion srcPR, destPR; buffer->tiles = tile_manager_new (width, height, tile_manager_bpp (tiles)); pixel_region_init (&srcPR, tiles, 0, 0, width, height, FALSE); pixel_region_init (&destPR, buffer->tiles, 0, 0, width, height, TRUE); copy_region (&srcPR, &destPR); } else { buffer->tiles = tiles; } return buffer; }
static BoundSeg * gimp_region_select_tool_calculate (GimpRegionSelectTool *region_sel, GimpDisplay *display, gint *n_segs) { GimpDisplayShell *shell = gimp_display_get_shell (display); BoundSeg *segs; PixelRegion maskPR; gimp_display_shell_set_override_cursor (shell, GDK_WATCH); if (region_sel->region_mask) g_object_unref (region_sel->region_mask); region_sel->region_mask = GIMP_REGION_SELECT_TOOL_GET_CLASS (region_sel)->get_mask (region_sel, display); if (! region_sel->region_mask) { gimp_display_shell_unset_override_cursor (shell); *n_segs = 0; return NULL; } /* calculate and allocate a new segment array which represents the * boundary of the contiguous region */ pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (region_sel->region_mask)), 0, 0, gimp_item_get_width (GIMP_ITEM (region_sel->region_mask)), gimp_item_get_height (GIMP_ITEM (region_sel->region_mask)), FALSE); segs = boundary_find (&maskPR, BOUNDARY_WITHIN_BOUNDS, 0, 0, gimp_item_get_width (GIMP_ITEM (region_sel->region_mask)), gimp_item_get_height (GIMP_ITEM (region_sel->region_mask)), BOUNDARY_HALF_WAY, n_segs); gimp_display_shell_unset_override_cursor (shell); return segs; }
static void paint_mask_to_canvas_tiles (GimpPaintCore *core, PixelRegion *paint_maskPR, gdouble paint_opacity) { PixelRegion srcPR; /* combine the paint mask and the canvas tiles */ pixel_region_init (&srcPR, core->canvas_tiles, core->canvas_buf->x, core->canvas_buf->y, core->canvas_buf->width, core->canvas_buf->height, TRUE); /* combine the mask to the canvas tiles */ combine_mask_and_region (&srcPR, paint_maskPR, paint_opacity * 255.999, GIMP_IS_AIRBRUSH (core)); }
static void gimp_pattern_clipboard_buffer_changed (Gimp *gimp, GimpPattern *pattern) { if (pattern->mask) { temp_buf_free (pattern->mask); pattern->mask = NULL; } if (gimp->global_buffer) { gint width; gint height; gint bytes; PixelRegion bufferPR; PixelRegion maskPR; width = MIN (gimp_buffer_get_width (gimp->global_buffer), 2048); height = MIN (gimp_buffer_get_height (gimp->global_buffer), 2048); bytes = gimp_buffer_get_bytes (gimp->global_buffer); pattern->mask = temp_buf_new (width, height, bytes, 0, 0, NULL); pixel_region_init (&bufferPR, gimp_buffer_get_tiles (gimp->global_buffer), 0, 0, width, height, FALSE); pixel_region_init_temp_buf (&maskPR, pattern->mask, 0, 0, width, height); copy_region (&bufferPR, &maskPR); } else { guchar color[3] = { 255, 255, 255 }; pattern->mask = temp_buf_new (16, 16, 3, 0, 0, color); } gimp_data_dirty (GIMP_DATA (pattern)); }
static void canvas_tiles_to_canvas_buf (GimpPaintCore *core) { PixelRegion srcPR; PixelRegion maskPR; /* combine the canvas tiles and the canvas buf */ pixel_region_init_temp_buf (&srcPR, core->canvas_buf, 0, 0, core->canvas_buf->width, core->canvas_buf->height); pixel_region_init (&maskPR, core->canvas_tiles, core->canvas_buf->x, core->canvas_buf->y, core->canvas_buf->width, core->canvas_buf->height, FALSE); /* apply the canvas tiles to the canvas buf */ apply_mask_to_region (&srcPR, &maskPR, OPAQUE_OPACITY); }
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; }
static GimpLayer * gimp_image_merge_layers (GimpImage *image, GSList *merge_list, GimpContext *context, GimpMergeType merge_type, const gchar *undo_desc) { GList *list; GSList *reverse_list = NULL; PixelRegion src1PR, src2PR, maskPR; PixelRegion *mask; GimpLayer *merge_layer; GimpLayer *layer; GimpLayer *bottom_layer; GimpImageType type; gint count; gint x1, y1, x2, y2; gint x3, y3, x4, y4; CombinationMode operation; gint position; gboolean active[MAX_CHANNELS] = { TRUE, TRUE, TRUE, TRUE }; gint off_x, off_y; gchar *name; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); layer = NULL; type = GIMP_RGBA_IMAGE; x1 = y1 = 0; x2 = y2 = 0; bottom_layer = NULL; /* Get the layer extents */ count = 0; while (merge_list) { layer = merge_list->data; gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); switch (merge_type) { case GIMP_EXPAND_AS_NECESSARY: case GIMP_CLIP_TO_IMAGE: if (! count) { x1 = off_x; y1 = off_y; x2 = off_x + gimp_item_width (GIMP_ITEM (layer)); y2 = off_y + gimp_item_height (GIMP_ITEM (layer)); } else { if (off_x < x1) x1 = off_x; if (off_y < y1) y1 = off_y; if ((off_x + gimp_item_width (GIMP_ITEM (layer))) > x2) x2 = (off_x + gimp_item_width (GIMP_ITEM (layer))); if ((off_y + gimp_item_height (GIMP_ITEM (layer))) > y2) y2 = (off_y + gimp_item_height (GIMP_ITEM (layer))); } if (merge_type == GIMP_CLIP_TO_IMAGE) { x1 = CLAMP (x1, 0, gimp_image_get_width (image)); y1 = CLAMP (y1, 0, gimp_image_get_height (image)); x2 = CLAMP (x2, 0, gimp_image_get_width (image)); y2 = CLAMP (y2, 0, gimp_image_get_height (image)); } break; case GIMP_CLIP_TO_BOTTOM_LAYER: if (merge_list->next == NULL) { x1 = off_x; y1 = off_y; x2 = off_x + gimp_item_width (GIMP_ITEM (layer)); y2 = off_y + gimp_item_height (GIMP_ITEM (layer)); } break; case GIMP_FLATTEN_IMAGE: if (merge_list->next == NULL) { x1 = 0; y1 = 0; x2 = gimp_image_get_width (image); y2 = gimp_image_get_height (image); } break; } count ++; reverse_list = g_slist_prepend (reverse_list, layer); merge_list = g_slist_next (merge_list); } if ((x2 - x1) == 0 || (y2 - y1) == 0) return NULL; /* Start a merge undo group. */ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, undo_desc); name = g_strdup (gimp_object_get_name (GIMP_OBJECT (layer))); if (merge_type == GIMP_FLATTEN_IMAGE || gimp_drawable_type (GIMP_DRAWABLE (layer)) == GIMP_INDEXED_IMAGE) { guchar bg[4] = { 0, 0, 0, 0 }; type = GIMP_IMAGE_TYPE_FROM_BASE_TYPE (gimp_image_base_type (image)); merge_layer = gimp_layer_new (image, (x2 - x1), (y2 - y1), type, gimp_object_get_name (GIMP_OBJECT (layer)), GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE); if (! merge_layer) { g_warning ("%s: could not allocate merge layer.", G_STRFUNC); return NULL; } GIMP_ITEM (merge_layer)->offset_x = x1; GIMP_ITEM (merge_layer)->offset_y = y1; /* get the background for compositing */ gimp_image_get_background (image, context, gimp_drawable_type (GIMP_DRAWABLE (merge_layer)), bg); /* init the pixel region */ pixel_region_init (&src1PR, gimp_drawable_get_tiles (GIMP_DRAWABLE (merge_layer)), 0, 0, (x2 - x1), (y2 - y1), TRUE); /* set the region to the background color */ color_region (&src1PR, bg); position = 0; } else { /* The final merged layer inherits the name of the bottom most layer * and the resulting layer has an alpha channel whether or not the * original did. Opacity is set to 100% and the MODE is set to normal. */ merge_layer = gimp_layer_new (image, (x2 - x1), (y2 - y1), gimp_drawable_type_with_alpha (GIMP_DRAWABLE (layer)), "merged layer", GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE); if (!merge_layer) { g_warning ("%s: could not allocate merge layer", G_STRFUNC); return NULL; } GIMP_ITEM (merge_layer)->offset_x = x1; GIMP_ITEM (merge_layer)->offset_y = y1; /* clear the layer */ pixel_region_init (&src1PR, gimp_drawable_get_tiles (GIMP_DRAWABLE (merge_layer)), 0, 0, (x2 - x1), (y2 - y1), TRUE); clear_region (&src1PR); /* Find the index in the layer list of the bottom layer--we need this * in order to add the final, merged layer to the layer list correctly */ layer = reverse_list->data; position = gimp_container_num_children (image->layers) - gimp_container_get_child_index (image->layers, GIMP_OBJECT (layer)); } bottom_layer = layer; /* Copy the tattoo and parasites of the bottom layer to the new layer */ gimp_item_set_tattoo (GIMP_ITEM (merge_layer), gimp_item_get_tattoo (GIMP_ITEM (bottom_layer))); g_object_unref (GIMP_ITEM (merge_layer)->parasites); GIMP_ITEM (merge_layer)->parasites = gimp_parasite_list_copy (GIMP_ITEM (bottom_layer)->parasites); while (reverse_list) { GimpLayerModeEffects mode; layer = reverse_list->data; /* determine what sort of operation is being attempted and * if it's actually legal... */ operation = gimp_image_merge_layers_get_operation (merge_layer, layer); if (operation == -1) { gimp_layer_add_alpha (layer); /* try again ... */ operation = gimp_image_merge_layers_get_operation (merge_layer, layer); } if (operation == -1) { g_warning ("%s: attempting to merge incompatible layers.", G_STRFUNC); return NULL; } gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); x3 = CLAMP (off_x, x1, x2); y3 = CLAMP (off_y, y1, y2); x4 = CLAMP (off_x + gimp_item_width (GIMP_ITEM (layer)), x1, x2); y4 = CLAMP (off_y + gimp_item_height (GIMP_ITEM (layer)), y1, y2); /* configure the pixel regions */ pixel_region_init (&src1PR, gimp_drawable_get_tiles (GIMP_DRAWABLE (merge_layer)), (x3 - x1), (y3 - y1), (x4 - x3), (y4 - y3), TRUE); pixel_region_init (&src2PR, gimp_drawable_get_tiles (GIMP_DRAWABLE (layer)), (x3 - off_x), (y3 - off_y), (x4 - x3), (y4 - y3), FALSE); if (gimp_layer_get_mask (layer) && gimp_layer_mask_get_apply (layer->mask)) { TileManager *tiles; tiles = gimp_drawable_get_tiles (GIMP_DRAWABLE (layer->mask)); pixel_region_init (&maskPR, tiles, (x3 - off_x), (y3 - off_y), (x4 - x3), (y4 - y3), FALSE); mask = &maskPR; } else { mask = NULL; } /* DISSOLVE_MODE is special since it is the only mode that does not * work on the projection with the lower layer, but only locally on * the layers alpha channel. */ mode = gimp_layer_get_mode (layer); if (layer == bottom_layer && mode != GIMP_DISSOLVE_MODE) mode = GIMP_NORMAL_MODE; combine_regions (&src1PR, &src2PR, &src1PR, mask, NULL, gimp_layer_get_opacity (layer) * 255.999, mode, active, operation); gimp_image_remove_layer (image, layer); reverse_list = g_slist_next (reverse_list); } g_slist_free (reverse_list); /* if the type is flatten, remove all the remaining layers */ if (merge_type == GIMP_FLATTEN_IMAGE) { list = GIMP_LIST (image->layers)->list; while (list) { layer = list->data; list = g_list_next (list); gimp_image_remove_layer (image, layer); } gimp_image_add_layer (image, merge_layer, position); } else { /* Add the layer to the image */ gimp_image_add_layer (image, merge_layer, gimp_container_num_children (image->layers) - position + 1); } /* set the name after the original layers have been removed so we * don't end up with #2 appended to the name */ gimp_object_take_name (GIMP_OBJECT (merge_layer), name); gimp_item_set_visible (GIMP_ITEM (merge_layer), TRUE, TRUE); /* End the merge undo group */ gimp_image_undo_group_end (image); gimp_drawable_update (GIMP_DRAWABLE (merge_layer), 0, 0, gimp_item_width (GIMP_ITEM (merge_layer)), gimp_item_height (GIMP_ITEM (merge_layer))); return merge_layer; }
static void gimp_smudge_motion (GimpPaintCore *paint_core, GimpDrawable *drawable, GimpPaintOptions *paint_options) { GimpSmudge *smudge = GIMP_SMUDGE (paint_core); GimpSmudgeOptions *options = GIMP_SMUDGE_OPTIONS (paint_options); GimpContext *context = GIMP_CONTEXT (paint_options); GimpPressureOptions *pressure_options = paint_options->pressure_options; GimpImage *image; TempBuf *area; PixelRegion srcPR, destPR, tempPR; gdouble rate; gdouble opacity; gint x, y, w, h; image = gimp_item_get_image (GIMP_ITEM (drawable)); if (gimp_drawable_is_indexed (drawable)) return; opacity = gimp_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); if (opacity == 0.0) return; /* Get the unclipped brush coordinates */ gimp_smudge_brush_coords (paint_core, &x, &y, &w, &h); /* Get the paint area (Smudge won't scale!) */ area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options); if (! area) return; /* srcPR will be the pixels under the current painthit from the drawable */ pixel_region_init (&srcPR, gimp_drawable_get_tiles (drawable), area->x, area->y, area->width, area->height, FALSE); /* Enable pressure sensitive rate */ if (pressure_options->rate) rate = MIN (options->rate / 100.0 * PRESSURE_SCALE * paint_core->cur_coords.pressure, 1.0); else rate = options->rate / 100.0; /* The tempPR will be the built up buffer (for smudge) */ pixel_region_init_data (&tempPR, smudge->accum_data, smudge->accumPR.bytes, smudge->accumPR.rowstride, area->x - x, area->y - y, area->width, area->height); /* The dest will be the paint area we got above (= canvas_buf) */ pixel_region_init_temp_buf (&destPR, area, 0, 0, area->width, area->height); /* Smudge uses the buffer Accum. * For each successive painthit Accum is built like this * Accum = rate*Accum + (1-rate)*I. * where I is the pixels under the current painthit. * Then the paint area (canvas_buf) is built as * (Accum,1) (if no alpha), */ blend_region (&srcPR, &tempPR, &tempPR, ROUND (rate * 255.0)); /* re-init the tempPR */ pixel_region_init_data (&tempPR, smudge->accum_data, smudge->accumPR.bytes, smudge->accumPR.rowstride, area->x - x, area->y - y, area->width, area->height); if (! gimp_drawable_has_alpha (drawable)) add_alpha_region (&tempPR, &destPR); else copy_region (&tempPR, &destPR); if (pressure_options->opacity) opacity *= PRESSURE_SCALE * paint_core->cur_coords.pressure; gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable, MIN (opacity, GIMP_OPACITY_OPAQUE), gimp_context_get_opacity (context), gimp_paint_options_get_brush_mode (paint_options), GIMP_PAINT_INCREMENTAL); }
void gimp_drawable_real_apply_region (GimpDrawable *drawable, PixelRegion *src2PR, gboolean push_undo, const gchar *undo_desc, gdouble opacity, GimpLayerModeEffects mode, TileManager *src1_tiles, PixelRegion *destPR, gint x, gint y) { GimpItem *item = GIMP_ITEM (drawable); GimpImage *image = gimp_item_get_image (item); GimpChannel *mask = gimp_image_get_mask (image); gint x1, y1, x2, y2; gint offset_x, offset_y; PixelRegion src1PR, my_destPR; CombinationMode operation; gboolean active_components[MAX_CHANNELS]; /* don't apply the mask to itself and don't apply an empty mask */ if (GIMP_DRAWABLE (mask) == drawable || gimp_channel_is_empty (mask)) mask = NULL; /* configure the active channel array */ gimp_drawable_get_active_components (drawable, active_components); /* determine what sort of operation is being attempted and * if it's actually legal... */ operation = gimp_image_get_combination_mode (gimp_drawable_type (drawable), src2PR->bytes); if (operation == -1) { g_warning ("%s: illegal parameters.", G_STRFUNC); return; } /* get the layer offsets */ gimp_item_get_offset (item, &offset_x, &offset_y); /* make sure the image application coordinates are within drawable bounds */ x1 = CLAMP (x, 0, gimp_item_get_width (item)); y1 = CLAMP (y, 0, gimp_item_get_height (item)); x2 = CLAMP (x + src2PR->w, 0, gimp_item_get_width (item)); y2 = CLAMP (y + src2PR->h, 0, gimp_item_get_height (item)); if (mask) { GimpItem *mask_item = GIMP_ITEM (mask); /* make sure coordinates are in mask bounds ... * we need to add the layer offset to transform coords * into the mask coordinate system */ x1 = CLAMP (x1, -offset_x, gimp_item_get_width (mask_item) - offset_x); y1 = CLAMP (y1, -offset_y, gimp_item_get_height (mask_item) - offset_y); x2 = CLAMP (x2, -offset_x, gimp_item_get_width (mask_item) - offset_x); y2 = CLAMP (y2, -offset_y, gimp_item_get_height (mask_item) - offset_y); } /* If the calling procedure specified an undo step... */ if (push_undo) { GimpDrawableUndo *undo; gimp_drawable_push_undo (drawable, undo_desc, x1, y1, x2 - x1, y2 - y1, NULL, FALSE); undo = GIMP_DRAWABLE_UNDO (gimp_image_undo_get_fadeable (image)); if (undo) { PixelRegion tmp_srcPR; PixelRegion tmp_destPR; undo->paint_mode = mode; undo->opacity = opacity; undo->src2_tiles = tile_manager_new (x2 - x1, y2 - y1, src2PR->bytes); tmp_srcPR = *src2PR; pixel_region_resize (&tmp_srcPR, src2PR->x + (x1 - x), src2PR->y + (y1 - y), x2 - x1, y2 - y1); pixel_region_init (&tmp_destPR, undo->src2_tiles, 0, 0, x2 - x1, y2 - y1, TRUE); copy_region (&tmp_srcPR, &tmp_destPR); } } /* configure the pixel regions */ /* check if an alternative to using the drawable's data as src1 was * provided... */ if (src1_tiles) { pixel_region_init (&src1PR, src1_tiles, x1, y1, x2 - x1, y2 - y1, FALSE); } else { pixel_region_init (&src1PR, gimp_drawable_get_tiles (drawable), x1, y1, x2 - x1, y2 - y1, FALSE); } /* check if an alternative to using the drawable's data as dest was * provided... */ if (!destPR) { pixel_region_init (&my_destPR, gimp_drawable_get_tiles (drawable), x1, y1, x2 - x1, y2 - y1, TRUE); destPR = &my_destPR; } pixel_region_resize (src2PR, src2PR->x + (x1 - x), src2PR->y + (y1 - y), x2 - x1, y2 - y1); if (mask) { PixelRegion maskPR; pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), x1 + offset_x, y1 + offset_y, x2 - x1, y2 - y1, FALSE); combine_regions (&src1PR, src2PR, destPR, &maskPR, NULL, opacity * 255.999, mode, active_components, operation); } else { combine_regions (&src1PR, src2PR, destPR, NULL, NULL, opacity * 255.999, mode, active_components, operation); } }
/* Similar to gimp_drawable_apply_region but works in "replace" mode (i.e. * transparent pixels in src2 make the result transparent rather than * opaque. * * Takes an additional mask pixel region as well. */ void gimp_drawable_real_replace_region (GimpDrawable *drawable, PixelRegion *src2PR, gboolean push_undo, const gchar *undo_desc, gdouble opacity, PixelRegion *maskPR, gint x, gint y) { GimpItem *item = GIMP_ITEM (drawable); GimpImage *image = gimp_item_get_image (item); GimpChannel *mask = gimp_image_get_mask (image); gint x1, y1, x2, y2; gint offset_x, offset_y; PixelRegion src1PR, destPR; CombinationMode operation; gboolean active_components[MAX_CHANNELS]; /* don't apply the mask to itself and don't apply an empty mask */ if (GIMP_DRAWABLE (mask) == drawable || gimp_channel_is_empty (mask)) mask = NULL; /* configure the active channel array */ gimp_drawable_get_active_components (drawable, active_components); /* determine what sort of operation is being attempted and * if it's actually legal... */ operation = gimp_image_get_combination_mode (gimp_drawable_type (drawable), src2PR->bytes); if (operation == -1) { g_warning ("%s: illegal parameters.", G_STRFUNC); return; } /* get the layer offsets */ gimp_item_get_offset (item, &offset_x, &offset_y); /* make sure the image application coordinates are within drawable bounds */ x1 = CLAMP (x, 0, gimp_item_get_width (item)); y1 = CLAMP (y, 0, gimp_item_get_height (item)); x2 = CLAMP (x + src2PR->w, 0, gimp_item_get_width (item)); y2 = CLAMP (y + src2PR->h, 0, gimp_item_get_height (item)); if (mask) { GimpItem *mask_item = GIMP_ITEM (mask); /* make sure coordinates are in mask bounds ... * we need to add the layer offset to transform coords * into the mask coordinate system */ x1 = CLAMP (x1, -offset_x, gimp_item_get_width (mask_item) - offset_x); y1 = CLAMP (y1, -offset_y, gimp_item_get_height (mask_item) - offset_y); x2 = CLAMP (x2, -offset_x, gimp_item_get_width (mask_item) - offset_x); y2 = CLAMP (y2, -offset_y, gimp_item_get_height (mask_item) - offset_y); } /* If the calling procedure specified an undo step... */ if (push_undo) gimp_drawable_push_undo (drawable, undo_desc, x1, y1, x2 - x1, y2 - y1, NULL, FALSE); /* configure the pixel regions */ pixel_region_init (&src1PR, gimp_drawable_get_tiles (drawable), x1, y1, x2 - x1, y2 - y1, FALSE); pixel_region_init (&destPR, gimp_drawable_get_tiles (drawable), x1, y1, x2 - x1, y2 - y1, TRUE); pixel_region_resize (src2PR, src2PR->x + (x1 - x), src2PR->y + (y1 - y), x2 - x1, y2 - y1); if (mask) { PixelRegion mask2PR, tempPR; guchar *temp_data; pixel_region_init (&mask2PR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), x1 + offset_x, y1 + offset_y, x2 - x1, y2 - y1, FALSE); temp_data = g_malloc ((y2 - y1) * (x2 - x1)); pixel_region_init_data (&tempPR, temp_data, 1, x2 - x1, 0, 0, x2 - x1, y2 - y1); copy_region (&mask2PR, &tempPR); pixel_region_init_data (&tempPR, temp_data, 1, x2 - x1, 0, 0, x2 - x1, y2 - y1); apply_mask_to_region (&tempPR, maskPR, OPAQUE_OPACITY); pixel_region_init_data (&tempPR, temp_data, 1, x2 - x1, 0, 0, x2 - x1, y2 - y1); combine_regions_replace (&src1PR, src2PR, &destPR, &tempPR, NULL, opacity * 255.999, active_components, operation); g_free (temp_data); } else { combine_regions_replace (&src1PR, src2PR, &destPR, maskPR, NULL, opacity * 255.999, active_components, operation); } }
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_brush_clipboard_buffer_changed (Gimp *gimp, GimpBrush *brush) { gint width; gint height; if (brush->mask) { temp_buf_free (brush->mask); brush->mask = NULL; } if (brush->pixmap) { temp_buf_free (brush->pixmap); brush->pixmap = NULL; } if (gimp->global_buffer) { TileManager *tiles = gimp_buffer_get_tiles (gimp->global_buffer); GimpImageType type = gimp_buffer_get_image_type (gimp->global_buffer); width = MIN (gimp_buffer_get_width (gimp->global_buffer), 1024); height = MIN (gimp_buffer_get_height (gimp->global_buffer), 1024); brush->mask = temp_buf_new (width, height, 1, 0, 0, NULL); brush->pixmap = temp_buf_new (width, height, 3, 0, 0, NULL); /* copy the alpha channel into the brush's mask */ if (GIMP_IMAGE_TYPE_HAS_ALPHA (type)) { PixelRegion bufferPR; PixelRegion maskPR; pixel_region_init (&bufferPR, tiles, 0, 0, width, height, FALSE); pixel_region_init_temp_buf (&maskPR, brush->mask, 0, 0, width, height); extract_alpha_region (&bufferPR, NULL, &maskPR); } else { PixelRegion maskPR; guchar opaque = OPAQUE_OPACITY; pixel_region_init_temp_buf (&maskPR, brush->mask, 0, 0, width, height); color_region (&maskPR, &opaque); } /* copy the color channels into the brush's pixmap */ if (GIMP_IMAGE_TYPE_IS_RGB (type)) { PixelRegion bufferPR; PixelRegion pixmapPR; pixel_region_init (&bufferPR, tiles, 0, 0, width, height, FALSE); pixel_region_init_temp_buf (&pixmapPR, brush->pixmap, 0, 0, width, height); if (GIMP_IMAGE_TYPE_HAS_ALPHA (type)) copy_color (&bufferPR, &pixmapPR); else copy_region (&bufferPR, &pixmapPR); } else { PixelRegion bufferPR; PixelRegion tempPR; TempBuf *temp = temp_buf_new (width, height, 1, 0, 0, NULL); pixel_region_init (&bufferPR, tiles, 0, 0, width, height, FALSE); pixel_region_init_temp_buf (&tempPR, temp, 0, 0, width, height); if (GIMP_IMAGE_TYPE_HAS_ALPHA (type)) copy_component (&bufferPR, &tempPR, 0); else copy_region (&bufferPR, &tempPR); temp_buf_copy (temp, brush->pixmap); temp_buf_free (temp); } } else { guchar color = 0; width = 17; height = 17; brush->mask = temp_buf_new (width, height, 1, 0, 0, &color); } brush->x_axis.x = width / 2; brush->x_axis.y = 0; brush->y_axis.x = 0; brush->y_axis.y = height / 2; gimp_data_dirty (GIMP_DATA (brush)); }
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); }
void gimp_drawable_bucket_fill_full (GimpDrawable *drawable, GimpBucketFillMode fill_mode, gint paint_mode, gdouble opacity, gboolean do_seed_fill, gboolean fill_transparent, GimpSelectCriterion fill_criterion, gdouble threshold, gboolean sample_merged, gdouble x, gdouble y, const GimpRGB *color, GimpPattern *pattern) { GimpImage *image; TileManager *buf_tiles; PixelRegion bufPR, maskPR; GimpChannel *mask = NULL; gint bytes; gint x1, y1, x2, y2; guchar col[MAX_CHANNELS]; TempBuf *pat_buf = NULL; gboolean new_buf = FALSE; gboolean selection; g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); g_return_if_fail (fill_mode != GIMP_PATTERN_BUCKET_FILL || GIMP_IS_PATTERN (pattern)); g_return_if_fail (fill_mode == GIMP_PATTERN_BUCKET_FILL || color != NULL); image = gimp_item_get_image (GIMP_ITEM (drawable)); bytes = gimp_drawable_bytes (drawable); selection = gimp_item_mask_bounds (GIMP_ITEM (drawable), &x1, &y1, &x2, &y2); if ((x1 == x2) || (y1 == y2)) return; if (fill_mode == GIMP_FG_BUCKET_FILL || fill_mode == GIMP_BG_BUCKET_FILL) { guchar tmp_col[MAX_CHANNELS]; gimp_rgb_get_uchar (color, &tmp_col[RED], &tmp_col[GREEN], &tmp_col[BLUE]); gimp_image_transform_color (image, gimp_drawable_type (drawable), col, GIMP_RGB, tmp_col); col[gimp_drawable_bytes_with_alpha (drawable) - 1] = OPAQUE_OPACITY; } else if (fill_mode == GIMP_PATTERN_BUCKET_FILL) { pat_buf = gimp_image_transform_temp_buf (image, gimp_drawable_type (drawable), pattern->mask, &new_buf); } else { g_warning ("%s: invalid fill_mode passed", G_STRFUNC); return; } gimp_set_busy (image->gimp); /* Do a seed bucket fill...To do this, calculate a new * contiguous region. If there is a selection, calculate the * intersection of this region with the existing selection. */ if (do_seed_fill) { mask = gimp_image_contiguous_region_by_seed (image, drawable, sample_merged, TRUE, (gint) threshold, fill_transparent, fill_criterion, (gint) x, (gint) y); if (selection) { gint off_x = 0; gint off_y = 0; if (! sample_merged) gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); gimp_channel_combine_mask (mask, gimp_image_get_mask (image), GIMP_CHANNEL_OP_INTERSECT, -off_x, -off_y); } gimp_channel_bounds (mask, &x1, &y1, &x2, &y2); /* make sure we handle the mask correctly if it was sample-merged */ if (sample_merged) { GimpItem *item; gint off_x, off_y; item = GIMP_ITEM (drawable); /* Limit the channel bounds to the drawable's extents */ gimp_item_get_offset (item, &off_x, &off_y); x1 = CLAMP (x1, off_x, (off_x + gimp_item_get_width (item))); y1 = CLAMP (y1, off_y, (off_y + gimp_item_get_height (item))); x2 = CLAMP (x2, off_x, (off_x + gimp_item_get_width (item))); y2 = CLAMP (y2, off_y, (off_y + gimp_item_get_height (item))); pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), x1, y1, (x2 - x1), (y2 - y1), TRUE); /* translate mask bounds to drawable coords */ x1 -= off_x; y1 -= off_y; x2 -= off_x; y2 -= off_y; } else { pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), x1, y1, (x2 - x1), (y2 - y1), TRUE); } /* if the image doesn't have an alpha channel, make sure that * the buf_tiles have. We need the alpha channel to fill with * the region calculated above */ if (! gimp_drawable_has_alpha (drawable)) bytes++; } else if (fill_mode == GIMP_PATTERN_BUCKET_FILL && (pat_buf->bytes == 2 || pat_buf->bytes == 4)) { /* If pattern being applied has an alpha channel, add one to the * buf_tiles. */ if (! gimp_drawable_has_alpha (drawable)) bytes++; } buf_tiles = tile_manager_new ((x2 - x1), (y2 - y1), bytes); pixel_region_init (&bufPR, buf_tiles, 0, 0, (x2 - x1), (y2 - y1), TRUE); switch (fill_mode) { case GIMP_FG_BUCKET_FILL: case GIMP_BG_BUCKET_FILL: if (mask) color_region_mask (&bufPR, &maskPR, col); else color_region (&bufPR, col); break; case GIMP_PATTERN_BUCKET_FILL: if (mask) pattern_region (&bufPR, &maskPR, pat_buf, x1, y1); else pattern_region (&bufPR, NULL, pat_buf, x1, y1); break; } /* Apply it to the image */ pixel_region_init (&bufPR, buf_tiles, 0, 0, (x2 - x1), (y2 - y1), FALSE); gimp_drawable_apply_region (drawable, &bufPR, TRUE, C_("undo-type", "Bucket Fill"), opacity, paint_mode, NULL, NULL, x1, y1); tile_manager_unref (buf_tiles); /* update the image */ gimp_drawable_update (drawable, x1, y1, x2 - x1, y2 - y1); /* free the mask */ if (mask) g_object_unref (mask); if (new_buf) temp_buf_free (pat_buf); gimp_unset_busy (image->gimp); }
static void gimp_convolve_motion (GimpPaintCore *paint_core, GimpDrawable *drawable, GimpPaintOptions *paint_options) { GimpConvolve *convolve = GIMP_CONVOLVE (paint_core); GimpBrushCore *brush_core = GIMP_BRUSH_CORE (paint_core); GimpConvolveOptions *options = GIMP_CONVOLVE_OPTIONS (paint_options); GimpContext *context = GIMP_CONTEXT (paint_options); GimpImage *image; TempBuf *area; PixelRegion srcPR; PixelRegion destPR; PixelRegion tempPR; guchar *buffer; gdouble opacity; gdouble rate; gint bytes; image = gimp_item_get_image (GIMP_ITEM (drawable)); if (gimp_drawable_is_indexed (drawable)) return; opacity = gimp_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); if (opacity == 0.0) return; area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options); if (! area) return; rate = options->rate; rate *= gimp_paint_options_get_dynamic_rate (paint_options, &paint_core->cur_coords); gimp_convolve_calculate_matrix (convolve, options->type, brush_core->brush->mask->width / 2, brush_core->brush->mask->height / 2, rate); /* configure the source pixel region */ pixel_region_init (&srcPR, gimp_drawable_get_tiles (drawable), area->x, area->y, area->width, area->height, FALSE); if (gimp_drawable_has_alpha (drawable)) { bytes = srcPR.bytes; buffer = g_malloc (area->height * bytes * area->width); pixel_region_init_data (&tempPR, buffer, bytes, bytes * area->width, 0, 0, area->width, area->height); copy_region (&srcPR, &tempPR); } else { /* note: this particular approach needlessly convolves the totally- opaque alpha channel. A faster approach would be to keep tempPR the same number of bytes as srcPR, and extend the paint_core_replace_canvas API to handle non-alpha images. */ bytes = srcPR.bytes + 1; buffer = g_malloc (area->height * bytes * area->width); pixel_region_init_data (&tempPR, buffer, bytes, bytes * area->width, 0, 0, area->width, area->height); add_alpha_region (&srcPR, &tempPR); } /* Convolve the region */ pixel_region_init_data (&tempPR, buffer, bytes, bytes * area->width, 0, 0, area->width, area->height); pixel_region_init_temp_buf (&destPR, area, 0, 0, area->width, area->height); convolve_region (&tempPR, &destPR, convolve->matrix, 3, convolve->matrix_divisor, GIMP_NORMAL_CONVOL, TRUE); g_free (buffer); gimp_brush_core_replace_canvas (brush_core, drawable, MIN (opacity, GIMP_OPACITY_OPAQUE), gimp_context_get_opacity (context), gimp_paint_options_get_brush_mode (paint_options), 1.0, GIMP_PAINT_INCREMENTAL); }
static gboolean gimp_smudge_start (GimpPaintCore *paint_core, GimpDrawable *drawable, GimpPaintOptions *paint_options) { GimpSmudge *smudge = GIMP_SMUDGE (paint_core); GimpImage *image; TempBuf *area; PixelRegion srcPR; gint bytes; gint x, y, w, h; image = gimp_item_get_image (GIMP_ITEM (drawable)); if (gimp_drawable_is_indexed (drawable)) return FALSE; area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options); if (! area) return FALSE; /* adjust the x and y coordinates to the upper left corner of the brush */ gimp_smudge_brush_coords (paint_core, &x, &y, &w, &h); /* Allocate the accumulation buffer */ bytes = gimp_drawable_bytes (drawable); smudge->accum_data = g_malloc (w * h * bytes); /* If clipped, prefill the smudge buffer with the color at the * brush position. */ if (x != area->x || y != area->y || w != area->width || h != area->height) { guchar fill[4]; gimp_pickable_get_pixel_at (GIMP_PICKABLE (drawable), CLAMP ((gint) paint_core->cur_coords.x, 0, gimp_item_width (GIMP_ITEM (drawable)) - 1), CLAMP ((gint) paint_core->cur_coords.y, 0, gimp_item_height (GIMP_ITEM (drawable)) - 1), fill); pixel_region_init_data (&srcPR, smudge->accum_data, bytes, bytes * w, 0, 0, w, h); color_region (&srcPR, fill); } pixel_region_init (&srcPR, gimp_drawable_get_tiles (drawable), area->x, area->y, area->width, area->height, FALSE); pixel_region_init_data (&smudge->accumPR, smudge->accum_data, bytes, bytes * w, area->x - x, area->y - y, area->width, area->height); /* copy the region under the original painthit. */ copy_region (&srcPR, &smudge->accumPR); pixel_region_init_data (&smudge->accumPR, smudge->accum_data, bytes, bytes * w, area->x - x, area->y - y, area->width, area->height); return TRUE; }
static void gimp_drawable_stroke_scan_convert (GimpDrawable *drawable, GimpStrokeOptions *options, GimpScanConvert *scan_convert) { GimpContext *context = GIMP_CONTEXT (options); GimpImage *image; gdouble width; TileManager *base; TileManager *mask; gint x, y, w, h; gint bytes; gint off_x, off_y; guchar bg[1] = { 0, }; PixelRegion maskPR, basePR; image = gimp_item_get_image (GIMP_ITEM (drawable)); /* must call gimp_channel_is_empty() instead of relying on * gimp_drawable_mask_intersect() because the selection pretends to * be empty while it is being stroked, to prevent masking itself. */ if (gimp_channel_is_empty (gimp_image_get_mask (image))) { x = 0; y = 0; w = gimp_item_width (GIMP_ITEM (drawable)); h = gimp_item_height (GIMP_ITEM (drawable)); } else if (! gimp_drawable_mask_intersect (drawable, &x, &y, &w, &h)) { return; } gimp_item_offsets (GIMP_ITEM (drawable), &off_x, &off_y); width = options->width; if (options->unit != GIMP_UNIT_PIXEL) { gimp_scan_convert_set_pixel_ratio (scan_convert, image->yresolution / image->xresolution); width *= (image->yresolution / _gimp_unit_get_factor (image->gimp, options->unit)); } gimp_scan_convert_stroke (scan_convert, width, options->join_style, options->cap_style, options->miter_limit, options->dash_offset, options->dash_info); /* fill a 1-bpp Tilemanager with black, this will describe the shape * of the stroke. */ mask = tile_manager_new (w, h, 1); pixel_region_init (&maskPR, mask, 0, 0, w, h, TRUE); color_region (&maskPR, bg); /* render the stroke into it */ gimp_scan_convert_render (scan_convert, mask, x + off_x, y + off_y, options->antialias); bytes = gimp_drawable_bytes_with_alpha (drawable); base = tile_manager_new (w, h, bytes); pixel_region_init (&basePR, base, 0, 0, w, h, TRUE); pixel_region_init (&maskPR, mask, 0, 0, w, h, FALSE); switch (options->style) { case GIMP_STROKE_STYLE_SOLID: { guchar tmp_col[MAX_CHANNELS] = { 0, }; guchar col[MAX_CHANNELS] = { 0, }; gimp_rgb_get_uchar (&context->foreground, &tmp_col[RED_PIX], &tmp_col[GREEN_PIX], &tmp_col[BLUE_PIX]); gimp_image_transform_color (image, gimp_drawable_type (drawable), col, GIMP_RGB, tmp_col); col[bytes - 1] = OPAQUE_OPACITY; color_region_mask (&basePR, &maskPR, col); } break; case GIMP_STROKE_STYLE_PATTERN: { GimpPattern *pattern; TempBuf *pat_buf; gboolean new_buf; pattern = gimp_context_get_pattern (context); pat_buf = gimp_image_transform_temp_buf (image, gimp_drawable_type (drawable), pattern->mask, &new_buf); pattern_region (&basePR, &maskPR, pat_buf, x, y); if (new_buf) temp_buf_free (pat_buf); } break; } /* Apply to drawable */ pixel_region_init (&basePR, base, 0, 0, w, h, FALSE); gimp_drawable_apply_region (drawable, &basePR, TRUE, _("Render Stroke"), gimp_context_get_opacity (context), gimp_context_get_paint_mode (context), NULL, x, y); tile_manager_unref (mask); tile_manager_unref (base); gimp_drawable_update (drawable, x, y, w, h); }
GimpImage * gimp_image_duplicate (GimpImage *image) { GimpImage *new_image; GimpLayer *floating_layer; GList *list; GimpLayer *active_layer = NULL; GimpChannel *active_channel = NULL; GimpVectors *active_vectors = NULL; GimpDrawable *new_floating_sel_drawable = NULL; GimpDrawable *floating_sel_drawable = NULL; gchar *filename; gint count; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); gimp_set_busy_until_idle (image->gimp); /* Create a new image */ new_image = gimp_create_image (image->gimp, image->width, image->height, image->base_type, FALSE); gimp_image_undo_disable (new_image); /* Store the folder to be used by the save dialog */ filename = gimp_image_get_filename (image); if (filename) { g_object_set_data_full (G_OBJECT (new_image), "gimp-image-dirname", g_path_get_dirname (filename), (GDestroyNotify) g_free); g_free (filename); } /* Copy the colormap if necessary */ if (new_image->base_type == GIMP_INDEXED) gimp_image_set_colormap (new_image, gimp_image_get_colormap (image), gimp_image_get_colormap_size (image), FALSE); /* Copy resolution information */ new_image->xresolution = image->xresolution; new_image->yresolution = image->yresolution; new_image->resolution_unit = image->resolution_unit; /* Copy floating layer */ floating_layer = gimp_image_floating_sel (image); if (floating_layer) { floating_sel_relax (floating_layer, FALSE); floating_sel_drawable = floating_layer->fs.drawable; floating_layer = NULL; } /* Copy the layers */ for (list = GIMP_LIST (image->layers)->list, count = 0; list; list = g_list_next (list)) { GimpLayer *layer = list->data; GimpLayer *new_layer; new_layer = GIMP_LAYER (gimp_item_convert (GIMP_ITEM (layer), new_image, G_TYPE_FROM_INSTANCE (layer), FALSE)); /* Make sure the copied layer doesn't say: "<old layer> copy" */ gimp_object_set_name (GIMP_OBJECT (new_layer), gimp_object_get_name (GIMP_OBJECT (layer))); /* Make sure that if the layer has a layer mask, * its name isn't screwed up */ if (new_layer->mask) gimp_object_set_name (GIMP_OBJECT (new_layer->mask), gimp_object_get_name (GIMP_OBJECT (layer->mask))); if (gimp_image_get_active_layer (image) == layer) active_layer = new_layer; if (image->floating_sel == layer) floating_layer = new_layer; if (floating_sel_drawable == GIMP_DRAWABLE (layer)) new_floating_sel_drawable = GIMP_DRAWABLE (new_layer); if (floating_layer != new_layer) gimp_image_add_layer (new_image, new_layer, count++); } /* Copy the channels */ for (list = GIMP_LIST (image->channels)->list, count = 0; list; list = g_list_next (list)) { GimpChannel *channel = list->data; GimpChannel *new_channel; new_channel = GIMP_CHANNEL (gimp_item_convert (GIMP_ITEM (channel), new_image, G_TYPE_FROM_INSTANCE (channel), FALSE)); /* Make sure the copied channel doesn't say: "<old channel> copy" */ gimp_object_set_name (GIMP_OBJECT (new_channel), gimp_object_get_name (GIMP_OBJECT (channel))); if (gimp_image_get_active_channel (image) == channel) active_channel = (new_channel); if (floating_sel_drawable == GIMP_DRAWABLE (channel)) new_floating_sel_drawable = GIMP_DRAWABLE (new_channel); gimp_image_add_channel (new_image, new_channel, count++); } /* Copy any vectors */ for (list = GIMP_LIST (image->vectors)->list, count = 0; list; list = g_list_next (list)) { GimpVectors *vectors = list->data; GimpVectors *new_vectors; new_vectors = GIMP_VECTORS (gimp_item_convert (GIMP_ITEM (vectors), new_image, G_TYPE_FROM_INSTANCE (vectors), FALSE)); /* Make sure the copied vectors doesn't say: "<old vectors> copy" */ gimp_object_set_name (GIMP_OBJECT (new_vectors), gimp_object_get_name (GIMP_OBJECT (vectors))); if (gimp_image_get_active_vectors (image) == vectors) active_vectors = new_vectors; gimp_image_add_vectors (new_image, new_vectors, count++); } /* Copy the selection mask */ { TileManager *src_tiles; TileManager *dest_tiles; PixelRegion srcPR, destPR; src_tiles = gimp_drawable_get_tiles (GIMP_DRAWABLE (image->selection_mask)); dest_tiles = gimp_drawable_get_tiles (GIMP_DRAWABLE (new_image->selection_mask)); pixel_region_init (&srcPR, src_tiles, 0, 0, image->width, image->height, FALSE); pixel_region_init (&destPR, dest_tiles, 0, 0, image->width, image->height, TRUE); copy_region (&srcPR, &destPR); new_image->selection_mask->bounds_known = FALSE; new_image->selection_mask->boundary_known = FALSE; } if (floating_layer) floating_sel_attach (floating_layer, new_floating_sel_drawable); /* Set active layer, active channel, active vectors */ if (active_layer) gimp_image_set_active_layer (new_image, active_layer); if (active_channel) gimp_image_set_active_channel (new_image, active_channel); if (active_vectors) gimp_image_set_active_vectors (new_image, active_vectors); /* Copy state of all color channels */ for (count = 0; count < MAX_CHANNELS; count++) { new_image->visible[count] = image->visible[count]; new_image->active[count] = image->active[count]; } /* Copy any guides */ for (list = image->guides; list; list = g_list_next (list)) { GimpGuide *guide = list->data; gint position = gimp_guide_get_position (guide); switch (gimp_guide_get_orientation (guide)) { case GIMP_ORIENTATION_HORIZONTAL: gimp_image_add_hguide (new_image, position, FALSE); break; case GIMP_ORIENTATION_VERTICAL: gimp_image_add_vguide (new_image, position, FALSE); break; default: g_error ("Unknown guide orientation.\n"); } } /* Copy any sample points */ for (list = image->sample_points; list; list = g_list_next (list)) { GimpSamplePoint *sample_point = list->data; gimp_image_add_sample_point_at_pos (new_image, sample_point->x, sample_point->y, FALSE); } /* Copy the grid */ if (image->grid) gimp_image_set_grid (new_image, image->grid, FALSE); /* Copy the quick mask info */ new_image->quick_mask_state = image->quick_mask_state; new_image->quick_mask_inverted = image->quick_mask_inverted; new_image->quick_mask_color = image->quick_mask_color; /* Copy parasites */ if (image->parasites) { g_object_unref (new_image->parasites); new_image->parasites = gimp_parasite_list_copy (image->parasites); } gimp_image_undo_enable (new_image); return new_image; }
static void gimp_ink_motion (GimpPaintCore *paint_core, GimpDrawable *drawable, GimpPaintOptions *paint_options, const GimpCoords *coords, guint32 time) { GimpInk *ink = GIMP_INK (paint_core); GimpInkOptions *options = GIMP_INK_OPTIONS (paint_options); GimpContext *context = GIMP_CONTEXT (paint_options); GimpImage *image; GimpBlob *blob_union = NULL; GimpBlob *blob_to_render; TempBuf *area; guchar col[MAX_CHANNELS]; PixelRegion blob_maskPR; image = gimp_item_get_image (GIMP_ITEM (drawable)); if (! ink->last_blob) { ink->last_blob = ink_pen_ellipse (options, coords->x, coords->y, coords->pressure, coords->xtilt, coords->ytilt, 100); if (ink->start_blob) g_free (ink->start_blob); ink->start_blob = gimp_blob_duplicate (ink->last_blob); blob_to_render = ink->last_blob; } else { GimpBlob *blob = ink_pen_ellipse (options, coords->x, coords->y, coords->pressure, coords->xtilt, coords->ytilt, coords->velocity * 100); blob_union = gimp_blob_convex_union (ink->last_blob, blob); g_free (ink->last_blob); ink->last_blob = blob; blob_to_render = blob_union; } /* Get the buffer */ ink->cur_blob = blob_to_render; area = gimp_paint_core_get_paint_area (paint_core, drawable, paint_options, coords); ink->cur_blob = NULL; if (! area) return; gimp_image_get_foreground (image, context, gimp_drawable_type (drawable), col); /* set the alpha channel */ col[paint_core->canvas_buf->bytes - 1] = OPAQUE_OPACITY; /* color the pixels */ color_pixels (temp_buf_get_data (paint_core->canvas_buf), col, area->width * area->height, area->bytes); gimp_paint_core_validate_canvas_tiles (paint_core, paint_core->canvas_buf->x, paint_core->canvas_buf->y, paint_core->canvas_buf->width, paint_core->canvas_buf->height); /* draw the blob directly to the canvas_tiles */ pixel_region_init (&blob_maskPR, paint_core->canvas_tiles, paint_core->canvas_buf->x, paint_core->canvas_buf->y, paint_core->canvas_buf->width, paint_core->canvas_buf->height, TRUE); render_blob (blob_to_render, &blob_maskPR); /* draw the canvas_buf using the just rendered canvas_tiles as mask */ pixel_region_init (&blob_maskPR, paint_core->canvas_tiles, paint_core->canvas_buf->x, paint_core->canvas_buf->y, paint_core->canvas_buf->width, paint_core->canvas_buf->height, FALSE); gimp_paint_core_paste (paint_core, &blob_maskPR, drawable, GIMP_OPACITY_OPAQUE, gimp_context_get_opacity (context), gimp_context_get_paint_mode (context), GIMP_PAINT_CONSTANT); if (blob_union) g_free (blob_union); }
static void gimp_text_layer_render_layout (GimpTextLayer *layer, GimpTextLayout *layout) { GimpDrawable *drawable = GIMP_DRAWABLE (layer); GimpItem *item = GIMP_ITEM (layer); GimpImage *image = gimp_item_get_image (item); cairo_t *cr; cairo_surface_t *surface; PixelRegion layerPR; const guchar *data; GimpImageType layer_type; gint layer_alpha_byte; gint rowstride; gint width; gint height; gpointer pr; g_return_if_fail (gimp_drawable_has_alpha (drawable)); width = gimp_item_get_width (item); height = gimp_item_get_height (item); surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create (surface); gimp_text_layout_render (layout, cr, layer->text->base_dir, FALSE); cairo_destroy (cr); pixel_region_init (&layerPR, gimp_drawable_get_tiles (drawable), 0, 0, width, height, TRUE); layer_type = gimp_drawable_type (drawable); layer_alpha_byte = layerPR.bytes - 1; cairo_surface_flush (surface); data = cairo_image_surface_get_data (surface); rowstride = cairo_image_surface_get_stride (surface); for (pr = pixel_regions_register (1, &layerPR); pr != NULL; pr = pixel_regions_process (pr)) { const guchar *src = data + layerPR.y * rowstride + layerPR.x * 4; guchar *dest = layerPR.data; gint rows = layerPR.h; while (rows--) { const guchar *s = src; guchar *d = dest; gint w = layerPR.w; while (w--) { guchar color[4]; GIMP_CAIRO_ARGB32_GET_PIXEL (s, color[0], color[1], color[2], color[3]); gimp_image_transform_color (image, layer_type, d, GIMP_RGB, color); d[layer_alpha_byte] = color[3]; s += 4; d += layerPR.bytes; } src += rowstride; dest += layerPR.rowstride; } } cairo_surface_destroy (surface); gimp_drawable_update (drawable, 0, 0, width, height); }
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; }
void gimp_channel_combine_rect (GimpChannel *mask, GimpChannelOps op, gint x, gint y, gint w, gint h) { PixelRegion maskPR; guchar color; g_return_if_fail (GIMP_IS_CHANNEL (mask)); if (! gimp_rectangle_intersect (x, y, w, h, 0, 0, gimp_item_get_width (GIMP_ITEM (mask)), gimp_item_get_height (GIMP_ITEM (mask)), &x, &y, &w, &h)) return; pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), x, y, w, h, TRUE); if (op == GIMP_CHANNEL_OP_ADD || op == GIMP_CHANNEL_OP_REPLACE) color = OPAQUE_OPACITY; else color = TRANSPARENT_OPACITY; color_region (&maskPR, &color); /* Determine new boundary */ if (mask->bounds_known && (op == GIMP_CHANNEL_OP_ADD) && ! mask->empty) { if (x < mask->x1) mask->x1 = x; if (y < mask->y1) mask->y1 = y; if ((x + w) > mask->x2) mask->x2 = (x + w); if ((y + h) > mask->y2) mask->y2 = (y + h); } else if (op == GIMP_CHANNEL_OP_REPLACE || mask->empty) { mask->empty = FALSE; mask->x1 = x; mask->y1 = y; mask->x2 = x + w; mask->y2 = y + h; } else { mask->bounds_known = FALSE; } mask->x1 = CLAMP (mask->x1, 0, gimp_item_get_width (GIMP_ITEM (mask))); mask->y1 = CLAMP (mask->y1, 0, gimp_item_get_height (GIMP_ITEM (mask))); mask->x2 = CLAMP (mask->x2, 0, gimp_item_get_width (GIMP_ITEM (mask))); mask->y2 = CLAMP (mask->y2, 0, gimp_item_get_height (GIMP_ITEM (mask))); gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h); }