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 gimp_paint_core_validate_undo_tiles (GimpPaintCore *core, GimpDrawable *drawable, 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_DRAWABLE (drawable)); g_return_if_fail (core->undo_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->undo_tiles, j, i, FALSE, FALSE); if (! tile_is_valid (dest_tile)) { Tile *src_tile = tile_manager_get_tile (gimp_drawable_get_tiles (drawable), j, i, TRUE, FALSE); tile_manager_map_tile (core->undo_tiles, j, i, src_tile); tile_release (src_tile, FALSE); } } } }
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; }
static void gimp_foreground_select_tool_stroke (GimpChannel *mask, FgSelectStroke *stroke) { GimpScanConvert *scan_convert = gimp_scan_convert_new (); if (stroke->num_points == 1) { GimpVector2 points[2]; points[0] = points[1] = stroke->points[0]; points[1].x += 0.01; points[1].y += 0.01; gimp_scan_convert_add_polyline (scan_convert, 2, points, FALSE); } else { gimp_scan_convert_add_polyline (scan_convert, stroke->num_points, stroke->points, FALSE); } gimp_scan_convert_stroke (scan_convert, stroke->width, GIMP_JOIN_ROUND, GIMP_CAP_ROUND, 10.0, 0.0, NULL); gimp_scan_convert_compose_value (scan_convert, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), 0, 0, stroke->background ? 0 : 255); gimp_scan_convert_free (scan_convert); }
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; }
static void gimp_drawable_apply_operation_private (GimpDrawable *drawable, GimpProgress *progress, const gchar *undo_desc, GeglNode *operation, gboolean linear, TileManager *dest_tiles, const GeglRectangle *rect) { GeglNode *gegl; GeglNode *input; GeglNode *output; GeglProcessor *processor; gdouble value; gegl = gegl_node_new (); /* Disable caching on all children of the node unless explicitly re-enabled. */ g_object_set (gegl, "dont-cache", TRUE, NULL); input = gegl_node_new_child (gegl, "operation", "gimp:tilemanager-source", "tile-manager", gimp_drawable_get_tiles (drawable), "linear", linear, NULL); output = gegl_node_new_child (gegl, "operation", "gimp:tilemanager-sink", "tile-manager", dest_tiles, "linear", linear, NULL); gegl_node_add_child (gegl, operation); gegl_node_link_many (input, operation, output, NULL); processor = gegl_node_new_processor (output, rect); if (progress) gimp_progress_start (progress, undo_desc, FALSE); while (gegl_processor_work (processor, &value)) if (progress) gimp_progress_set_value (progress, value); g_object_unref (processor); g_object_unref (gegl); }
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 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; }
void gimp_channel_select_scan_convert (GimpChannel *channel, const gchar *undo_desc, GimpScanConvert *scan_convert, gint offset_x, gint offset_y, GimpChannelOps op, gboolean antialias, gboolean feather, gdouble feather_radius_x, gdouble feather_radius_y, gboolean push_undo) { GimpItem *item; GimpChannel *add_on; g_return_if_fail (GIMP_IS_CHANNEL (channel)); g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (channel))); g_return_if_fail (undo_desc != NULL); g_return_if_fail (scan_convert != NULL); if (push_undo) gimp_channel_push_undo (channel, undo_desc); /* if applicable, replace the current selection */ if (op == GIMP_CHANNEL_OP_REPLACE) gimp_channel_clear (channel, NULL, FALSE); item = GIMP_ITEM (channel); add_on = gimp_channel_new_mask (gimp_item_get_image (item), gimp_item_width (item), gimp_item_height (item)); gimp_scan_convert_render (scan_convert, gimp_drawable_get_tiles (GIMP_DRAWABLE (add_on)), offset_x, offset_y, antialias); if (feather) gimp_channel_feather (add_on, feather_radius_x, feather_radius_y, FALSE /* no undo */); gimp_channel_combine_mask (channel, add_on, op, 0, 0); g_object_unref (add_on); }
void gimp_paint_core_cancel (GimpPaintCore *core, GimpDrawable *drawable) { gint x, y; gint width, height; g_return_if_fail (GIMP_IS_PAINT_CORE (core)); g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); /* Determine if any part of the image has been altered-- * if nothing has, then just return... */ if ((core->x2 == core->x1) || (core->y2 == core->y1)) return; if (gimp_rectangle_intersect (core->x1, core->y1, core->x2 - core->x1, core->y2 - core->y1, 0, 0, gimp_item_get_width (GIMP_ITEM (drawable)), gimp_item_get_height (GIMP_ITEM (drawable)), &x, &y, &width, &height)) { gimp_paint_core_copy_valid_tiles (core->undo_tiles, gimp_drawable_get_tiles (drawable), x, y, width, height); } tile_manager_unref (core->undo_tiles); core->undo_tiles = NULL; if (core->saved_proj_tiles) { tile_manager_unref (core->saved_proj_tiles); core->saved_proj_tiles = NULL; } gimp_drawable_update (drawable, x, y, width, height); gimp_viewable_preview_thaw (GIMP_VIEWABLE (drawable)); }
TempBuf * gimp_drawable_get_sub_preview (GimpDrawable *drawable, gint src_x, gint src_y, gint src_width, gint src_height, gint dest_width, gint dest_height) { GimpItem *item; GimpImage *image; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (src_x >= 0, NULL); g_return_val_if_fail (src_y >= 0, NULL); g_return_val_if_fail (src_width > 0, NULL); g_return_val_if_fail (src_height > 0, NULL); g_return_val_if_fail (dest_width > 0, NULL); g_return_val_if_fail (dest_height > 0, NULL); item = GIMP_ITEM (drawable); g_return_val_if_fail ((src_x + src_width) <= gimp_item_width (item), NULL); g_return_val_if_fail ((src_y + src_height) <= gimp_item_height (item), NULL); image = gimp_item_get_image (item); if (! image->gimp->config->layer_previews) return NULL; if (GIMP_IMAGE_TYPE_BASE_TYPE (gimp_drawable_type (drawable)) == GIMP_INDEXED) return gimp_drawable_indexed_preview (drawable, gimp_image_get_colormap (image), src_x, src_y, src_width, src_height, dest_width, dest_height); return tile_manager_get_sub_preview (gimp_drawable_get_tiles (drawable), src_x, src_y, src_width, src_height, dest_width, dest_height); }
/* 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); } }
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); }
const BoundSeg * floating_sel_boundary (GimpLayer *layer, gint *n_segs) { PixelRegion bPR; gint i; g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL); g_return_val_if_fail (gimp_layer_is_floating_sel (layer), NULL); g_return_val_if_fail (n_segs != NULL, NULL); if (layer->fs.boundary_known == FALSE) { gint width, height; gint off_x, off_y; width = gimp_item_width (GIMP_ITEM (layer)); height = gimp_item_height (GIMP_ITEM (layer)); gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); if (layer->fs.segs) g_free (layer->fs.segs); if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) { /* find the segments */ pixel_region_init (&bPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (layer)), 0, 0, width, height, FALSE); layer->fs.segs = boundary_find (&bPR, BOUNDARY_WITHIN_BOUNDS, 0, 0, width, height, BOUNDARY_HALF_WAY, &layer->fs.num_segs); /* offset the segments */ for (i = 0; i < layer->fs.num_segs; i++) { layer->fs.segs[i].x1 += off_x; layer->fs.segs[i].y1 += off_y; layer->fs.segs[i].x2 += off_x; layer->fs.segs[i].y2 += off_y; } } else { layer->fs.num_segs = 4; layer->fs.segs = g_new0 (BoundSeg, 4); /* top */ layer->fs.segs[0].x1 = off_x; layer->fs.segs[0].y1 = off_y; layer->fs.segs[0].x2 = off_x + width; layer->fs.segs[0].y2 = off_y; /* left */ layer->fs.segs[1].x1 = off_x; layer->fs.segs[1].y1 = off_y; layer->fs.segs[1].x2 = off_x; layer->fs.segs[1].y2 = off_y + height; /* right */ layer->fs.segs[2].x1 = off_x + width; layer->fs.segs[2].y1 = off_y; layer->fs.segs[2].x2 = off_x + width; layer->fs.segs[2].y2 = off_y + height; /* bottom */ layer->fs.segs[3].x1 = off_x; layer->fs.segs[3].y1 = off_y + height; layer->fs.segs[3].x2 = off_x + width; layer->fs.segs[3].y2 = off_y + height; } layer->fs.boundary_known = TRUE; } *n_segs = layer->fs.num_segs; return layer->fs.segs; }
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_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); }
void floating_sel_store (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)); /* Check the backing store & make sure it has the correct dimensions */ if ((tile_manager_width (layer->fs.backing_store) != gimp_item_width (GIMP_ITEM(layer))) || (tile_manager_height (layer->fs.backing_store) != gimp_item_height (GIMP_ITEM(layer))) || (tile_manager_bpp (layer->fs.backing_store) != gimp_drawable_bytes (layer->fs.drawable))) { /* free the backing store and allocate anew */ tile_manager_unref (layer->fs.backing_store); layer->fs.backing_store = tile_manager_new (GIMP_ITEM (layer)->width, GIMP_ITEM (layer)->height, gimp_drawable_bytes (layer->fs.drawable)); } /* What this function does is save the specified area of the * drawable that this floating selection obscures. We do this so * that it will be possible to subsequently restore the drawable's area */ gimp_item_offsets (GIMP_ITEM (layer->fs.drawable), &offx, &offy); /* Find the minimum area we need to uncover -- in image space */ 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 drawable to the backing store */ pixel_region_init (&srcPR, gimp_drawable_get_tiles (layer->fs.drawable), (x1 - offx), (y1 - offy), (x2 - x1), (y2 - y1), FALSE); pixel_region_init (&destPR, layer->fs.backing_store, (x1 - GIMP_ITEM (layer)->offset_x), (y1 - GIMP_ITEM (layer)->offset_y), (x2 - x1), (y2 - y1), TRUE); copy_region (&srcPR, &destPR); } }
static void gimp_foreground_select_tool_select (GimpFreeSelectTool *free_sel, GimpDisplay *display) { GimpForegroundSelectTool *fg_select; GimpForegroundSelectOptions *options; GimpImage *image = gimp_display_get_image (display); GimpDrawable *drawable; GimpScanConvert *scan_convert; GimpChannel *mask; const GimpVector2 *points; gint n_points; drawable = gimp_image_get_active_drawable (image); fg_select = GIMP_FOREGROUND_SELECT_TOOL (free_sel); options = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (free_sel); if (fg_select->idle_id) { g_source_remove (fg_select->idle_id); fg_select->idle_id = 0; } if (! drawable) return; scan_convert = gimp_scan_convert_new (); gimp_free_select_tool_get_points (free_sel, &points, &n_points); gimp_scan_convert_add_polyline (scan_convert, n_points, points, TRUE); mask = gimp_channel_new (image, gimp_image_get_width (image), gimp_image_get_height (image), "foreground-extraction", NULL); gimp_scan_convert_render_value (scan_convert, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), 0, 0, 128); gimp_scan_convert_free (scan_convert); if (fg_select->strokes) { GList *list; gimp_set_busy (image->gimp); /* apply foreground and background markers */ for (list = fg_select->strokes; list; list = list->next) gimp_foreground_select_tool_stroke (mask, list->data); if (fg_select->state) gimp_drawable_foreground_extract_siox (GIMP_DRAWABLE (mask), fg_select->state, fg_select->refinement, options->smoothness, options->sensitivity, ! options->contiguous, GIMP_PROGRESS (fg_select)); fg_select->refinement = SIOX_REFINEMENT_NO_CHANGE; gimp_unset_busy (image->gimp); } else { gint x1, y1; gint x2, y2; g_object_set (options, "background", FALSE, NULL); gimp_foreground_select_tool_get_area (mask, &x1, &y1, &x2, &y2); if (fg_select->state) g_warning ("state should be NULL here"); fg_select->state = gimp_drawable_foreground_extract_siox_init (drawable, x1, y1, x2 - x1, y2 - y1); } gimp_foreground_select_tool_set_mask (fg_select, display, mask); g_object_unref (mask); }
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); } }
TempBuf * gimp_paint_core_get_orig_image (GimpPaintCore *core, GimpDrawable *drawable, gint x, gint y, gint width, gint height) { PixelRegion srcPR; PixelRegion destPR; Tile *undo_tile; gboolean release_tile; gint h; gint pixelwidth; gint drawable_width; gint drawable_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_DRAWABLE (drawable), NULL); g_return_val_if_fail (core->undo_tiles != NULL, NULL); core->orig_buf = temp_buf_resize (core->orig_buf, gimp_drawable_bytes (drawable), x, y, width, height); drawable_width = gimp_item_get_width (GIMP_ITEM (drawable)); drawable_height = gimp_item_get_height (GIMP_ITEM (drawable)); gimp_rectangle_intersect (x, y, width, height, 0, 0, drawable_width, drawable_height, &x, &y, &width, &height); /* configure the pixel regions */ pixel_region_init (&srcPR, gimp_drawable_get_tiles (drawable), x, y, width, height, FALSE); pixel_region_init_temp_buf (&destPR, core->orig_buf, x - core->orig_buf->x, y - core->orig_buf->y, width, height); for (pr = pixel_regions_register (2, &srcPR, &destPR); pr != NULL; pr = pixel_regions_process (pr)) { /* If the undo tile corresponding to this location is valid, use it */ undo_tile = tile_manager_get_tile (core->undo_tiles, srcPR.x, srcPR.y, FALSE, FALSE); if (tile_is_valid (undo_tile)) { release_tile = TRUE; undo_tile = tile_manager_get_tile (core->undo_tiles, srcPR.x, srcPR.y, TRUE, FALSE); s = tile_data_pointer (undo_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 (undo_tile, FALSE); } return core->orig_buf; }
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); }
/** * gimp_channel_combine_ellipse_rect: * @mask: the channel with which to combine the elliptic rect * @op: whether to replace, add to, or subtract from the current * contents * @x: x coordinate of upper left corner of bounding rect * @y: y coordinate of upper left corner of bounding rect * @w: width of bounding rect * @h: height of bounding rect * @a: elliptic a-constant applied to corners * @b: elliptic b-constant applied to corners * @antialias: if %TRUE, antialias the elliptic corners * * Used for rounded cornered rectangles and ellipses. If @op is * %GIMP_CHANNEL_OP_REPLACE or %GIMP_CHANNEL_OP_ADD, sets pixels * within the ellipse to 255. If @op is %GIMP_CHANNEL_OP_SUBTRACT, * sets pixels within to zero. If @antialias is %TRUE, pixels that * impinge on the edge of the ellipse are set to intermediate values, * depending on how much they overlap. **/ void gimp_channel_combine_ellipse_rect (GimpChannel *mask, GimpChannelOps op, gint x, gint y, gint w, gint h, gdouble a, gdouble b, gboolean antialias) { PixelRegion maskPR; gdouble a_sqr; gdouble b_sqr; gdouble ellipse_center_x; gint x0, y0; gint width, height; gpointer pr; g_return_if_fail (GIMP_IS_CHANNEL (mask)); g_return_if_fail (a >= 0.0 && b >= 0.0); g_return_if_fail (op != GIMP_CHANNEL_OP_INTERSECT); /* Make sure the elliptic corners fit into the rect */ a = MIN (a, w / 2.0); b = MIN (b, h / 2.0); a_sqr = SQR (a); b_sqr = SQR (b); if (! gimp_rectangle_intersect (x, y, w, h, 0, 0, gimp_item_get_width (GIMP_ITEM (mask)), gimp_item_get_height (GIMP_ITEM (mask)), &x0, &y0, &width, &height)) return; ellipse_center_x = x + a; pixel_region_init (&maskPR, gimp_drawable_get_tiles (GIMP_DRAWABLE (mask)), x0, y0, width, height, TRUE); for (pr = pixel_regions_register (1, &maskPR); pr != NULL; pr = pixel_regions_process (pr)) { guchar *data = maskPR.data; gint py; for (py = maskPR.y; py < maskPR.y + maskPR.h; py++, data += maskPR.rowstride) { const gint px = maskPR.x; gdouble ellipse_center_y; if (py >= y + b && py < y + h - b) { /* we are on a row without rounded corners */ gimp_channel_combine_span (data, op, 0, maskPR.w, 255); continue; } /* Match the ellipse center y with our current y */ if (py < y + b) { ellipse_center_y = y + b; } else { ellipse_center_y = y + h - b; } /* For a non-antialiased ellipse, use the normal equation * for an ellipse with an arbitrary center * (ellipse_center_x, ellipse_center_y). */ if (! antialias) { gdouble half_ellipse_width_at_y; gint x_start; gint x_end; half_ellipse_width_at_y = sqrt (a_sqr - a_sqr * SQR (py + 0.5f - ellipse_center_y) / b_sqr); x_start = ROUND (ellipse_center_x - half_ellipse_width_at_y); x_end = ROUND (ellipse_center_x + w - 2 * a + half_ellipse_width_at_y); gimp_channel_combine_span (data, op, MAX (x_start - px, 0), MIN (x_end - px, maskPR.w), 255); } else /* use antialiasing */ { /* algorithm changed 7-18-04, because the previous one * did not work well for eccentric ellipses. The new * algorithm measures the distance to the ellipse in the * X and Y directions, and uses trigonometry to * approximate the distance to the ellipse as the * distance to the hypotenuse of a right triangle whose * legs are the X and Y distances. (WES) */ const gfloat yi = ABS (py + 0.5 - ellipse_center_y); gint last_val = -1; gint x_start = px; gint cur_x; for (cur_x = px; cur_x < (px + maskPR.w); cur_x++) { gfloat xj; gfloat xdist; gfloat ydist; gfloat r; gfloat dist; gint val; if (cur_x < x + w / 2) { ellipse_center_x = x + a; } else { ellipse_center_x = x + w - a; } xj = ABS (cur_x + 0.5 - ellipse_center_x); if (yi < b) xdist = xj - a * sqrt (1 - SQR (yi) / b_sqr); else xdist = 1000.0; /* anything large will work */ if (xj < a) ydist = yi - b * sqrt (1 - SQR (xj) / a_sqr); else ydist = 1000.0; /* anything large will work */ r = hypot (xdist, ydist); if (r < 0.001) dist = 0.; else dist = xdist * ydist / r; /* trig formula for distance to * hypotenuse */ if (xdist < 0.0) dist *= -1; if (dist < -0.5) val = 255; else if (dist < 0.5) val = (gint) (255 * (1 - (dist + 0.5))); else val = 0; if (last_val != val) { if (last_val != -1) gimp_channel_combine_span (data, op, MAX (x_start - px, 0), MIN (cur_x - px, maskPR.w), last_val); x_start = cur_x; last_val = val; } /* skip ahead if we are on the straight segment * between rounded corners */ if (cur_x >= x + a && cur_x < x + w - a) { gimp_channel_combine_span (data, op, MAX (x_start - px, 0), MIN (cur_x - px, maskPR.w), last_val); x_start = cur_x; cur_x = x + w - a; last_val = val = 255; } /* Time to change center? */ if (cur_x >= x + w / 2) { ellipse_center_x = x + w - a; } } gimp_channel_combine_span (data, op, MAX (x_start - px, 0), MIN (cur_x - px, maskPR.w), last_val); } } } /* use the intersected values for the boundary calculation */ x = x0; y = y0; w = width; h = height; /* 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; } gimp_drawable_update (GIMP_DRAWABLE (mask), x, y, w, h); }
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 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_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_display_shell_render (GimpDisplayShell *shell, gint x, gint y, gint w, gint h, GdkRectangle *highlight) { GimpProjection *projection; GimpImage *image; RenderInfo info; GimpImageType type; g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); g_return_if_fail (w > 0 && h > 0); image = shell->display->image; projection = image->projection; /* Initialize RenderInfo with values that don't change during the * call of this function. */ info.shell = shell; info.x = x + shell->offset_x; info.y = y + shell->offset_y; info.w = w; info.h = h; info.dest_bpp = 3; info.dest_bpl = info.dest_bpp * GIMP_RENDER_BUF_WIDTH; info.dest_width = info.dest_bpp * info.w; switch (GIMP_DISPLAY_CONFIG (image->gimp->config)->zoom_quality) { case GIMP_ZOOM_QUALITY_LOW: info.zoom_quality = GIMP_DISPLAY_ZOOM_FAST; break; case GIMP_ZOOM_QUALITY_HIGH: info.zoom_quality = GIMP_DISPLAY_ZOOM_PIXEL_AA; break; } if (GIMP_IMAGE_TYPE_HAS_ALPHA (gimp_projection_get_image_type (projection))) { gdouble opacity = gimp_projection_get_opacity (projection); info.alpha = render_image_init_alpha (opacity * 255.999); } /* Setup RenderInfo for rendering a GimpProjection level. */ { TileManager *src_tiles; gint level; level = gimp_projection_get_level (projection, shell->scale_x, shell->scale_y); src_tiles = gimp_projection_get_tiles_at_level (projection, level); gimp_display_shell_render_info_scale (&info, shell, src_tiles, level); } /* Currently, only RGBA and GRAYA projection types are used. */ type = gimp_projection_get_image_type (projection); switch (type) { case GIMP_RGBA_IMAGE: render_image_rgb_a (&info); break; case GIMP_GRAYA_IMAGE: render_image_gray_a (&info); break; default: g_warning ("%s: unsupported projection type (%d)", G_STRFUNC, type); g_assert_not_reached (); } /* apply filters to the rendered projection */ if (shell->filter_stack) gimp_color_display_stack_convert (shell->filter_stack, shell->render_buf, w, h, 3, 3 * GIMP_RENDER_BUF_WIDTH); /* dim pixels outside the highlighted rectangle */ if (highlight) { gimp_display_shell_render_highlight (shell, x, y, w, h, highlight); } else if (shell->mask) { TileManager *src_tiles = gimp_drawable_get_tiles (shell->mask); /* The mask does not (yet) have an image pyramid, use 0 as level, */ gimp_display_shell_render_info_scale (&info, shell, src_tiles, 0); gimp_display_shell_render_mask (shell, &info); } /* put it to the screen */ gimp_canvas_draw_rgb (GIMP_CANVAS (shell->canvas), GIMP_CANVAS_STYLE_RENDER, x + shell->disp_xoffset, y + shell->disp_yoffset, w, h, shell->render_buf, 3 * GIMP_RENDER_BUF_WIDTH, shell->offset_x, shell->offset_y); }