static gboolean gimp_drawable_edit_can_fill_direct (GimpDrawable *drawable, GimpFillOptions *options) { GimpImage *image; GimpContext *context; gdouble opacity; GimpComponentMask affect; GimpLayerMode mode; GimpLayerCompositeMode composite_mode; GimpLayerCompositeRegion composite_region; image = gimp_item_get_image (GIMP_ITEM (drawable)); context = GIMP_CONTEXT (options); opacity = gimp_context_get_opacity (context); affect = gimp_drawable_get_active_mask (drawable); mode = gimp_context_get_paint_mode (context); composite_mode = gimp_layer_mode_get_paint_composite_mode (mode); composite_region = gimp_layer_mode_get_included_region (mode, composite_mode); if (gimp_channel_is_empty (gimp_image_get_mask (image)) && opacity == GIMP_OPACITY_OPAQUE && affect == GIMP_COMPONENT_MASK_ALL && gimp_layer_mode_is_trivial (mode) && (! gimp_layer_mode_is_subtractive (mode) ^ ! (composite_region & GIMP_LAYER_COMPOSITE_REGION_SOURCE))) { switch (gimp_fill_options_get_style (options)) { case GIMP_FILL_STYLE_SOLID: return TRUE; case GIMP_FILL_STYLE_PATTERN: { GimpPattern *pattern; GimpTempBuf *mask; const Babl *format; pattern = gimp_context_get_pattern (context); mask = gimp_pattern_get_mask (pattern); format = gimp_temp_buf_get_format (mask); return ! babl_format_has_alpha (format); } } } return FALSE; }
void gimp_paint_core_paste (GimpPaintCore *core, const GimpTempBuf *paint_mask, gint paint_mask_offset_x, gint paint_mask_offset_y, GimpDrawable *drawable, gdouble paint_opacity, gdouble image_opacity, GimpLayerModeEffects paint_mode, GimpPaintApplicationMode mode) { gint width = gegl_buffer_get_width (core->paint_buffer); gint height = gegl_buffer_get_height (core->paint_buffer); if (core->applicator) { /* If the mode is CONSTANT: * combine the canvas buf, the paint mask to the canvas buffer */ if (mode == GIMP_PAINT_CONSTANT) { /* Some tools (ink) paint the mask to paint_core->canvas_buffer * directly. Don't need to copy it in this case. */ if (paint_mask != NULL) { GeglBuffer *paint_mask_buffer = gimp_temp_buf_create_buffer ((GimpTempBuf *) paint_mask); gimp_gegl_combine_mask_weird (paint_mask_buffer, GEGL_RECTANGLE (paint_mask_offset_x, paint_mask_offset_y, width, height), core->canvas_buffer, GEGL_RECTANGLE (core->paint_buffer_x, core->paint_buffer_y, width, height), paint_opacity, GIMP_IS_AIRBRUSH (core)); g_object_unref (paint_mask_buffer); } gimp_gegl_apply_mask (core->canvas_buffer, GEGL_RECTANGLE (core->paint_buffer_x, core->paint_buffer_y, width, height), core->paint_buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0); gimp_applicator_set_src_buffer (core->applicator, core->undo_buffer); } /* Otherwise: * combine the canvas buf and the paint mask to the canvas buf */ else { GeglBuffer *paint_mask_buffer = gimp_temp_buf_create_buffer ((GimpTempBuf *) paint_mask); gimp_gegl_apply_mask (paint_mask_buffer, GEGL_RECTANGLE (paint_mask_offset_x, paint_mask_offset_y, width, height), core->paint_buffer, GEGL_RECTANGLE (0, 0, width, height), paint_opacity); g_object_unref (paint_mask_buffer); gimp_applicator_set_src_buffer (core->applicator, gimp_drawable_get_buffer (drawable)); } gimp_applicator_set_apply_buffer (core->applicator, core->paint_buffer); gimp_applicator_set_apply_offset (core->applicator, core->paint_buffer_x, core->paint_buffer_y); gimp_applicator_set_mode (core->applicator, image_opacity, paint_mode); /* apply the paint area to the image */ gimp_applicator_blit (core->applicator, GEGL_RECTANGLE (core->paint_buffer_x, core->paint_buffer_y, width, height)); } else { GimpTempBuf *paint_buf = gimp_gegl_buffer_get_temp_buf (core->paint_buffer); GeglBuffer *dest_buffer; GeglBuffer *src_buffer; if (! paint_buf) return; if (core->comp_buffer) dest_buffer = core->comp_buffer; else dest_buffer = gimp_drawable_get_buffer (drawable); if (mode == GIMP_PAINT_CONSTANT) { /* This step is skipped by the ink tool, which writes * directly to canvas_buffer */ if (paint_mask != NULL) { /* Mix paint mask and canvas_buffer */ combine_paint_mask_to_canvas_mask (paint_mask, paint_mask_offset_x, paint_mask_offset_y, core->canvas_buffer, core->paint_buffer_x, core->paint_buffer_y, paint_opacity, GIMP_IS_AIRBRUSH (core)); } /* Write canvas_buffer to paint_buf */ canvas_buffer_to_paint_buf_alpha (paint_buf, core->canvas_buffer, core->paint_buffer_x, core->paint_buffer_y); /* undo buf -> paint_buf -> dest_buffer */ src_buffer = core->undo_buffer; } else { g_return_if_fail (paint_mask); /* Write paint_mask to paint_buf, does not modify canvas_buffer */ paint_mask_to_paint_buffer (paint_mask, paint_mask_offset_x, paint_mask_offset_y, paint_buf, paint_opacity); /* dest_buffer -> paint_buf -> dest_buffer */ src_buffer = dest_buffer; } do_layer_blend (src_buffer, dest_buffer, paint_buf, core->mask_buffer, image_opacity, core->paint_buffer_x, core->paint_buffer_y, core->mask_x_offset, core->mask_y_offset, core->linear_mode, paint_mode); if (core->comp_buffer) { mask_components_onto (src_buffer, core->comp_buffer, gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE(core->paint_buffer_x, core->paint_buffer_y, width, height), gimp_drawable_get_active_mask (drawable), core->linear_mode); } } /* Update the undo extents */ core->x1 = MIN (core->x1, core->paint_buffer_x); core->y1 = MIN (core->y1, core->paint_buffer_y); core->x2 = MAX (core->x2, core->paint_buffer_x + width); core->y2 = MAX (core->y2, core->paint_buffer_y + height); /* Update the drawable */ gimp_drawable_update (drawable, core->paint_buffer_x, core->paint_buffer_y, width, height); }
gboolean gimp_paint_core_start (GimpPaintCore *core, GimpDrawable *drawable, GimpPaintOptions *paint_options, const GimpCoords *coords, GError **error) { GimpImage *image; GimpItem *item; GimpChannel *mask; g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), FALSE); g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE); g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), FALSE); g_return_val_if_fail (coords != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); item = GIMP_ITEM (drawable); image = gimp_item_get_image (item); if (core->stroke_buffer) { g_array_free (core->stroke_buffer, TRUE); core->stroke_buffer = NULL; } core->stroke_buffer = g_array_sized_new (TRUE, TRUE, sizeof (GimpCoords), STROKE_BUFFER_INIT_SIZE); /* remember the last stroke's endpoint for later undo */ core->start_coords = core->last_coords; core->cur_coords = *coords; if (! GIMP_PAINT_CORE_GET_CLASS (core)->start (core, drawable, paint_options, coords, error)) { return FALSE; } /* Allocate the undo structure */ if (core->undo_buffer) g_object_unref (core->undo_buffer); core->undo_buffer = gegl_buffer_dup (gimp_drawable_get_buffer (drawable)); /* Allocate the saved proj structure */ if (core->saved_proj_buffer) { g_object_unref (core->saved_proj_buffer); core->saved_proj_buffer = NULL; } if (core->use_saved_proj) { GeglBuffer *buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (image)); core->saved_proj_buffer = gegl_buffer_dup (buffer); } /* Allocate the canvas blocks structure */ if (core->canvas_buffer) g_object_unref (core->canvas_buffer); core->canvas_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, gimp_item_get_width (item), gimp_item_get_height (item)), babl_format ("Y float")); /* Get the initial undo extents */ core->x1 = core->x2 = core->cur_coords.x; core->y1 = core->y2 = core->cur_coords.y; core->last_paint.x = -1e6; core->last_paint.y = -1e6; mask = gimp_image_get_mask (image); /* don't apply the mask to itself and don't apply an empty mask */ if (GIMP_DRAWABLE (mask) != drawable && ! gimp_channel_is_empty (mask)) { GeglBuffer *mask_buffer; gint offset_x; gint offset_y; mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); gimp_item_get_offset (item, &offset_x, &offset_y); core->mask_buffer = g_object_ref (mask_buffer); core->mask_x_offset = -offset_x; core->mask_y_offset = -offset_y; } else { core->mask_buffer = NULL; } core->linear_mode = gimp_drawable_get_linear (drawable); if (paint_options->use_applicator) { core->applicator = gimp_applicator_new (NULL, core->linear_mode); if (core->mask_buffer) { gimp_applicator_set_mask_buffer (core->applicator, core->mask_buffer); gimp_applicator_set_mask_offset (core->applicator, core->mask_x_offset, core->mask_y_offset); } gimp_applicator_set_affect (core->applicator, gimp_drawable_get_active_mask (drawable)); gimp_applicator_set_dest_buffer (core->applicator, gimp_drawable_get_buffer (drawable)); } else { if (core->comp_buffer) { g_object_unref (core->comp_buffer); core->comp_buffer = NULL; } /* Allocate the scratch buffer if there's a component mask */ if (gimp_drawable_get_active_mask (drawable) != GIMP_COMPONENT_ALL) { const Babl *format; if (core->linear_mode) format = babl_format ("RGBA float"); else format = babl_format ("R'G'B'A float"); core->comp_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, gimp_item_get_width (item), gimp_item_get_height (item)), format); } } /* Freeze the drawable preview so that it isn't constantly updated. */ gimp_viewable_preview_freeze (GIMP_VIEWABLE (drawable)); return TRUE; }
gboolean gimp_paint_core_start (GimpPaintCore *core, GimpDrawable *drawable, GimpPaintOptions *paint_options, const GimpCoords *coords, GError **error) { GimpItem *item; g_return_val_if_fail (GIMP_IS_PAINT_CORE (core), FALSE); g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE); g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), FALSE); g_return_val_if_fail (coords != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); item = GIMP_ITEM (drawable); if (core->stroke_buffer) { g_array_free (core->stroke_buffer, TRUE); core->stroke_buffer = NULL; } core->stroke_buffer = g_array_sized_new (TRUE, TRUE, sizeof (GimpCoords), STROKE_BUFFER_INIT_SIZE); core->cur_coords = *coords; if (! GIMP_PAINT_CORE_GET_CLASS (core)->start (core, drawable, paint_options, coords, error)) { return FALSE; } /* Allocate the undo structure */ if (core->undo_buffer) g_object_unref (core->undo_buffer); core->undo_buffer = gegl_buffer_dup (gimp_drawable_get_buffer (drawable)); /* Allocate the saved proj structure */ if (core->saved_proj_buffer) { g_object_unref (core->saved_proj_buffer); core->saved_proj_buffer = NULL; } if (core->use_saved_proj) { GimpImage *image = gimp_item_get_image (item); GimpPickable *pickable = GIMP_PICKABLE (gimp_image_get_projection (image)); GeglBuffer *buffer = gimp_pickable_get_buffer (pickable); core->saved_proj_buffer = gegl_buffer_dup (buffer); } /* Allocate the canvas blocks structure */ if (core->canvas_buffer) g_object_unref (core->canvas_buffer); core->canvas_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, gimp_item_get_width (item), gimp_item_get_height (item)), babl_format ("Y u8")); /* Get the initial undo extents */ core->x1 = core->x2 = core->cur_coords.x; core->y1 = core->y2 = core->cur_coords.y; core->last_paint.x = -1e6; core->last_paint.y = -1e6; { GimpImage *image; GimpChannel *mask; GeglBuffer *mask_buffer = NULL; gint offset_x = 0; gint offset_y = 0; image = gimp_item_get_image (item); mask = gimp_image_get_mask (image); /* 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; if (mask) { mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); gimp_item_get_offset (item, &offset_x, &offset_y); } core->applicator = gimp_applicator_new (gimp_drawable_get_buffer (drawable), gimp_drawable_get_active_mask (drawable), mask_buffer, -offset_x, -offset_y); } /* Freeze the drawable preview so that it isn't constantly updated. */ gimp_viewable_preview_freeze (GIMP_VIEWABLE (drawable)); return TRUE; }
void gimp_drawable_real_apply_buffer (GimpDrawable *drawable, GeglBuffer *buffer, const GeglRectangle *buffer_region, gboolean push_undo, const gchar *undo_desc, gdouble opacity, GimpLayerModeEffects mode, GeglBuffer *base_buffer, gint base_x, gint base_y) { GimpItem *item = GIMP_ITEM (drawable); GimpImage *image = gimp_item_get_image (item); GimpChannel *mask = gimp_image_get_mask (image); GeglBuffer *mask_buffer = NULL; GeglNode *apply; GimpComponentMask affect; gint x, y, width, height; gint offset_x, offset_y; /* 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; if (! base_buffer) base_buffer = gimp_drawable_get_buffer (drawable); /* get the layer offsets */ gimp_item_get_offset (item, &offset_x, &offset_y); /* make sure the image application coordinates are within drawable bounds */ gimp_rectangle_intersect (base_x, base_y, buffer_region->width, buffer_region->height, 0, 0, gimp_item_get_width (item), gimp_item_get_height (item), &x, &y, &width, &height); 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 */ gimp_rectangle_intersect (x, y, width, height, -offset_x, -offset_y, gimp_item_get_width (mask_item), gimp_item_get_height (mask_item), &x, &y, &width, &height); } if (push_undo) { GimpDrawableUndo *undo; gimp_drawable_push_undo (drawable, undo_desc, NULL, x, y, width, height); undo = GIMP_DRAWABLE_UNDO (gimp_image_undo_get_fadeable (image)); if (undo) { undo->paint_mode = mode; undo->opacity = opacity; undo->applied_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), gegl_buffer_get_format (buffer)); gegl_buffer_copy (buffer, GEGL_RECTANGLE (buffer_region->x + (x - base_x), buffer_region->y + (y - base_y), width, height), undo->applied_buffer, GEGL_RECTANGLE (0, 0, width, height)); } } if (mask) mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); affect = gimp_drawable_get_active_mask (drawable); apply = gimp_gegl_create_apply_buffer_node (buffer, base_x - buffer_region->x, base_y - buffer_region->y, 0, 0, 0, 0, mask_buffer, -offset_x, -offset_y, opacity, mode, affect); gimp_gegl_apply_operation (base_buffer, NULL, NULL, apply, gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (x, y, width, height)); g_object_unref (apply); }
void gimp_drawable_edit_fill (GimpDrawable *drawable, GimpFillOptions *options, const gchar *undo_desc) { GimpContext *context; gint x, y, width, height; g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable))); g_return_if_fail (GIMP_IS_FILL_OPTIONS (options)); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height)) { return; /* nothing to do, but the fill succeeded */ } context = GIMP_CONTEXT (options); if (gimp_layer_mode_is_alpha_only (gimp_context_get_paint_mode (context))) { if (! gimp_drawable_has_alpha (drawable) || ! (gimp_drawable_get_active_mask (drawable) & GIMP_COMPONENT_MASK_ALPHA)) { return; /* nothing to do, but the fill succeeded */ } } if (! undo_desc) undo_desc = gimp_fill_options_get_undo_desc (options); /* check if we can fill the drawable's buffer directly */ if (gimp_drawable_edit_can_fill_direct (drawable, options)) { gimp_drawable_edit_fill_direct (drawable, options, undo_desc); gimp_drawable_update (drawable, x, y, width, height); } else { GeglNode *operation; GimpDrawableFilter *filter; gdouble opacity; GimpLayerMode mode; GimpLayerMode composite_mode; opacity = gimp_context_get_opacity (context); mode = gimp_context_get_paint_mode (context); composite_mode = gimp_layer_mode_get_paint_composite_mode (mode); operation = gegl_node_new_child (NULL, "operation", "gimp:fill-source", "options", options, "drawable", drawable, "pattern-offset-x", -x, "pattern-offset-y", -y, NULL); filter = gimp_drawable_filter_new (drawable, undo_desc, operation, NULL); gimp_drawable_filter_set_opacity (filter, opacity); gimp_drawable_filter_set_mode (filter, mode, GIMP_LAYER_COLOR_SPACE_AUTO, GIMP_LAYER_COLOR_SPACE_AUTO, composite_mode); gimp_drawable_filter_apply (filter, NULL); gimp_drawable_filter_commit (filter, NULL, FALSE); g_object_unref (filter); g_object_unref (operation); } }