static void ico_dialog_update_icon_preview (GtkWidget *dialog, gint32 layer, gint bpp) { GtkWidget *preview = ico_dialog_get_layer_preview (dialog, layer); GdkPixbuf *pixbuf; const Babl *format; gint w = gimp_drawable_width (layer); gint h = gimp_drawable_height (layer); if (! preview) return; switch (gimp_drawable_type (layer)) { case GIMP_RGB_IMAGE: format = babl_format ("R'G'B' u8"); break; case GIMP_RGBA_IMAGE: format = babl_format ("R'G'B'A u8"); break; case GIMP_GRAY_IMAGE: format = babl_format ("Y' u8"); break; case GIMP_GRAYA_IMAGE: format = babl_format ("Y'A u8"); break; case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE: format = gimp_drawable_get_format (layer); default: g_return_if_reached (); } if (bpp <= 8) { GeglBuffer *buffer; GeglBuffer *tmp; gint32 image; gint32 tmp_image; gint32 tmp_layer; guchar *buf; guchar *cmap; gint num_colors; image = gimp_item_get_image (layer); tmp_image = gimp_image_new (w, h, gimp_image_base_type (image)); gimp_image_undo_disable (tmp_image); if (gimp_drawable_is_indexed (layer)) { cmap = gimp_image_get_colormap (image, &num_colors); gimp_image_set_colormap (tmp_image, cmap, num_colors); g_free (cmap); } tmp_layer = gimp_layer_new (tmp_image, "temporary", w, h, gimp_drawable_type (layer), 100, GIMP_NORMAL_MODE); gimp_image_insert_layer (tmp_image, tmp_layer, -1, 0); buffer = gimp_drawable_get_buffer (layer); tmp = gimp_drawable_get_buffer (tmp_layer); buf = g_malloc (w * h * 4); gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); gegl_buffer_copy (buffer, NULL, tmp, NULL); g_object_unref (tmp); g_object_unref (buffer); if (gimp_drawable_is_indexed (layer)) gimp_image_convert_rgb (tmp_image); gimp_image_convert_indexed (tmp_image, GIMP_FS_DITHER, GIMP_MAKE_PALETTE, 1 << bpp, TRUE, FALSE, "dummy"); cmap = gimp_image_get_colormap (tmp_image, &num_colors); if (num_colors == (1 << bpp) && ! ico_cmap_contains_black (cmap, num_colors)) { /* Windows icons with color maps need the color black. * We need to eliminate one more color to make room for black. */ if (gimp_drawable_is_indexed (layer)) { g_free (cmap); cmap = gimp_image_get_colormap (image, &num_colors); gimp_image_set_colormap (tmp_image, cmap, num_colors); } else if (gimp_drawable_is_gray (layer)) { gimp_image_convert_grayscale (tmp_image); } else { gimp_image_convert_rgb (tmp_image); } tmp = gimp_drawable_get_buffer (tmp_layer); gegl_buffer_set (tmp, GEGL_RECTANGLE (0, 0, w, h), 0, format, buf, GEGL_AUTO_ROWSTRIDE); g_object_unref (tmp); if (!gimp_drawable_is_rgb (layer)) gimp_image_convert_rgb (tmp_image); gimp_image_convert_indexed (tmp_image, GIMP_FS_DITHER, GIMP_MAKE_PALETTE, (1 << bpp) - 1, TRUE, FALSE, "dummy"); } g_free (cmap); g_free (buf); pixbuf = gimp_drawable_get_thumbnail (tmp_layer, MIN (w, 128), MIN (h, 128), GIMP_PIXBUF_SMALL_CHECKS); gimp_image_delete (tmp_image); } else if (bpp == 24) { GeglBuffer *buffer; GeglBuffer *tmp; gint32 image; gint32 tmp_image; gint32 tmp_layer; GimpParam *return_vals; gint n_return_vals; image = gimp_item_get_image (layer); tmp_image = gimp_image_new (w, h, gimp_image_base_type (image)); gimp_image_undo_disable (tmp_image); if (gimp_drawable_is_indexed (layer)) { guchar *cmap; gint num_colors; cmap = gimp_image_get_colormap (image, &num_colors); gimp_image_set_colormap (tmp_image, cmap, num_colors); g_free (cmap); } tmp_layer = gimp_layer_new (tmp_image, "temporary", w, h, gimp_drawable_type (layer), 100, GIMP_NORMAL_MODE); gimp_image_insert_layer (tmp_image, tmp_layer, -1, 0); buffer = gimp_drawable_get_buffer (layer); tmp = gimp_drawable_get_buffer (tmp_layer); gegl_buffer_copy (buffer, NULL, tmp, NULL); g_object_unref (tmp); g_object_unref (buffer); if (gimp_drawable_is_indexed (layer)) gimp_image_convert_rgb (tmp_image); return_vals = gimp_run_procedure ("plug-in-threshold-alpha", &n_return_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, tmp_image, GIMP_PDB_DRAWABLE, tmp_layer, GIMP_PDB_INT32, ICO_ALPHA_THRESHOLD, GIMP_PDB_END); gimp_destroy_params (return_vals, n_return_vals); pixbuf = gimp_drawable_get_thumbnail (tmp_layer, MIN (w, 128), MIN (h, 128), GIMP_PIXBUF_SMALL_CHECKS); gimp_image_delete (tmp_image); } else { pixbuf = gimp_drawable_get_thumbnail (layer, MIN (w, 128), MIN (h, 128), GIMP_PIXBUF_SMALL_CHECKS); } gtk_image_set_from_pixbuf (GTK_IMAGE (preview), pixbuf); g_object_unref (pixbuf); }
static GimpLayer * gimp_image_merge_layers (GimpImage *image, GimpContainer *container, GSList *merge_list, GimpContext *context, GimpMergeType merge_type) { GList *list; GSList *reverse_list = NULL; GSList *layers; GimpLayer *merge_layer; GimpLayer *layer; GimpLayer *bottom_layer; GimpParasiteList *parasites; gint count; gint x1, y1, x2, y2; gint off_x, off_y; gint position; gchar *name; GimpLayer *parent; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); layer = NULL; x1 = y1 = 0; x2 = y2 = 0; bottom_layer = NULL; parent = gimp_layer_get_parent (merge_list->data); /* Get the layer extents */ count = 0; while (merge_list) { layer = merge_list->data; gimp_item_get_offset (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_get_width (GIMP_ITEM (layer)); y2 = off_y + gimp_item_get_height (GIMP_ITEM (layer)); } else { if (off_x < x1) x1 = off_x; if (off_y < y1) y1 = off_y; if ((off_x + gimp_item_get_width (GIMP_ITEM (layer))) > x2) x2 = (off_x + gimp_item_get_width (GIMP_ITEM (layer))); if ((off_y + gimp_item_get_height (GIMP_ITEM (layer))) > y2) y2 = (off_y + gimp_item_get_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_get_width (GIMP_ITEM (layer)); y2 = off_y + gimp_item_get_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. */ name = g_strdup (gimp_object_get_name (layer)); if (merge_type == GIMP_FLATTEN_IMAGE || (gimp_drawable_is_indexed (GIMP_DRAWABLE (layer)) && ! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))) { GeglColor *color; GimpRGB bg; merge_layer = gimp_layer_new (image, (x2 - x1), (y2 - y1), gimp_image_get_layer_format (image, FALSE), gimp_object_get_name (layer), GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE); if (! merge_layer) { g_warning ("%s: could not allocate merge layer.", G_STRFUNC); return NULL; } gimp_item_set_offset (GIMP_ITEM (merge_layer), x1, y1); /* get the background for compositing */ gimp_context_get_background (context, &bg); color = gimp_gegl_color_new (&bg); gegl_buffer_set_color (gimp_drawable_get_buffer (GIMP_DRAWABLE (merge_layer)), GEGL_RECTANGLE(0,0,x2-x1,y2-y1), color); g_object_unref (color); 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_get_format_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_set_offset (GIMP_ITEM (merge_layer), x1, y1); /* clear the layer */ gegl_buffer_clear (gimp_drawable_get_buffer (GIMP_DRAWABLE (merge_layer)), NULL); /* 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_get_n_children (container) - gimp_container_get_child_index (container, 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))); parasites = gimp_item_get_parasites (GIMP_ITEM (bottom_layer)); parasites = gimp_parasite_list_copy (parasites); gimp_item_set_parasites (GIMP_ITEM (merge_layer), parasites); g_object_unref (parasites); for (layers = reverse_list; layers; layers = g_slist_next (layers)) { GeglBuffer *merge_buffer; GeglBuffer *layer_buffer; GimpApplicator *applicator; GimpLayerModeEffects mode; layer = layers->data; gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y); /* 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; merge_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (merge_layer)); layer_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); applicator = gimp_applicator_new (NULL, gimp_drawable_get_linear (GIMP_DRAWABLE (layer)), FALSE); if (gimp_layer_get_mask (layer) && gimp_layer_get_apply_mask (layer)) { GeglBuffer *mask_buffer; mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer->mask)); gimp_applicator_set_mask_buffer (applicator, mask_buffer); gimp_applicator_set_mask_offset (applicator, - (x1 - off_x), - (y1 - off_y)); } gimp_applicator_set_src_buffer (applicator, merge_buffer); gimp_applicator_set_dest_buffer (applicator, merge_buffer); gimp_applicator_set_apply_buffer (applicator, layer_buffer); gimp_applicator_set_apply_offset (applicator, - (x1 - off_x), - (y1 - off_y)); gimp_applicator_set_mode (applicator, gimp_layer_get_opacity (layer), mode); gimp_applicator_blit (applicator, GEGL_RECTANGLE (0, 0, gegl_buffer_get_width (merge_buffer), gegl_buffer_get_height (merge_buffer))); g_object_unref (applicator); gimp_image_remove_layer (image, layer, TRUE, NULL); } g_slist_free (reverse_list); gimp_object_take_name (GIMP_OBJECT (merge_layer), name); gimp_item_set_visible (GIMP_ITEM (merge_layer), TRUE, FALSE); /* if the type is flatten, remove all the remaining layers */ if (merge_type == GIMP_FLATTEN_IMAGE) { list = gimp_image_get_layer_iter (image); while (list) { layer = list->data; list = g_list_next (list); gimp_image_remove_layer (image, layer, TRUE, NULL); } gimp_image_add_layer (image, merge_layer, parent, position, TRUE); } else { /* Add the layer to the image */ gimp_image_add_layer (image, merge_layer, parent, gimp_container_get_n_children (container) - position + 1, TRUE); } gimp_drawable_update (GIMP_DRAWABLE (merge_layer), 0, 0, gimp_item_get_width (GIMP_ITEM (merge_layer)), gimp_item_get_height (GIMP_ITEM (merge_layer))); return merge_layer; }
void gimp_display_shell_render (GimpDisplayShell *shell, cairo_t *cr, gint x, gint y, gint w, gint h) { GimpImage *image; GimpProjection *projection; GeglBuffer *buffer; gdouble scale_x = 1.0; gdouble scale_y = 1.0; gdouble buffer_scale = 1.0; gint viewport_offset_x; gint viewport_offset_y; gint viewport_width; gint viewport_height; gint scaled_x; gint scaled_y; gint scaled_width; gint scaled_height; cairo_surface_t *xfer; gint xfer_src_x; gint xfer_src_y; gint mask_src_x = 0; gint mask_src_y = 0; gint stride; guchar *data; g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell)); g_return_if_fail (cr != NULL); g_return_if_fail (w > 0 && h > 0); image = gimp_display_get_image (shell->display); projection = gimp_image_get_projection (image); buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (projection)); #ifdef GIMP_DISPLAY_RENDER_ENABLE_SCALING /* if we had this future API, things would look pretty on hires (retina) */ scale_x = gdk_window_get_scale_factor (gtk_widget_get_window (gtk_widget_get_toplevel (GTK_WIDGET (shell)))); #endif scale_x = MIN (scale_x, GIMP_DISPLAY_RENDER_MAX_SCALE); scale_y = scale_x; if (shell->scale_x > shell->scale_y) { scale_y *= (shell->scale_x / shell->scale_y); buffer_scale = shell->scale_y * scale_y; } else if (shell->scale_y > shell->scale_x) { scale_x *= (shell->scale_y / shell->scale_x); buffer_scale = shell->scale_x * scale_x; } else { buffer_scale = shell->scale_x * scale_x; } gimp_display_shell_scroll_get_scaled_viewport (shell, &viewport_offset_x, &viewport_offset_y, &viewport_width, &viewport_height); scaled_x = floor ((x + viewport_offset_x) * scale_x); scaled_y = floor ((y + viewport_offset_y) * scale_y); scaled_width = ceil (w * scale_x); scaled_height = ceil (h * scale_y); if (shell->rotate_transform) { xfer = cairo_surface_create_similar_image (cairo_get_target (cr), CAIRO_FORMAT_ARGB32, scaled_width, scaled_height); cairo_surface_mark_dirty (xfer); xfer_src_x = 0; xfer_src_y = 0; } else { xfer = gimp_display_xfer_get_surface (shell->xfer, scaled_width, scaled_height, &xfer_src_x, &xfer_src_y); } stride = cairo_image_surface_get_stride (xfer); data = cairo_image_surface_get_data (xfer); data += xfer_src_y * stride + xfer_src_x * 4; /* apply filters to the rendered projection */ if (shell->filter_stack) { const Babl *filter_format = babl_format ("R'G'B'A float"); if (! shell->filter_buffer) { gint w = GIMP_DISPLAY_RENDER_BUF_WIDTH * GIMP_DISPLAY_RENDER_MAX_SCALE; gint h = GIMP_DISPLAY_RENDER_BUF_HEIGHT * GIMP_DISPLAY_RENDER_MAX_SCALE; shell->filter_data = gegl_malloc (w * h * babl_format_get_bytes_per_pixel (filter_format)); shell->filter_stride = w * babl_format_get_bytes_per_pixel (filter_format); shell->filter_buffer = gegl_buffer_linear_new_from_data (shell->filter_data, filter_format, GEGL_RECTANGLE (0, 0, w, h), GEGL_AUTO_ROWSTRIDE, (GDestroyNotify) gegl_free, shell->filter_data); } gegl_buffer_get (buffer, GEGL_RECTANGLE (scaled_x, scaled_y, scaled_width, scaled_height), buffer_scale, filter_format, shell->filter_data, shell->filter_stride, GEGL_ABYSS_CLAMP); gimp_color_display_stack_convert_buffer (shell->filter_stack, shell->filter_buffer, GEGL_RECTANGLE (0, 0, scaled_width, scaled_height)); gegl_buffer_get (shell->filter_buffer, GEGL_RECTANGLE (0, 0, scaled_width, scaled_height), 1.0, babl_format ("cairo-ARGB32"), data, stride, GEGL_ABYSS_CLAMP); } else { gegl_buffer_get (buffer, GEGL_RECTANGLE (scaled_x, scaled_y, scaled_width, scaled_height), buffer_scale, babl_format ("cairo-ARGB32"), data, stride, GEGL_ABYSS_CLAMP); } if (shell->mask) { gint mask_height; if (! shell->mask_surface) { shell->mask_surface = cairo_image_surface_create (CAIRO_FORMAT_A8, GIMP_DISPLAY_RENDER_BUF_WIDTH * GIMP_DISPLAY_RENDER_MAX_SCALE, GIMP_DISPLAY_RENDER_BUF_HEIGHT * GIMP_DISPLAY_RENDER_MAX_SCALE); } cairo_surface_mark_dirty (shell->mask_surface); stride = cairo_image_surface_get_stride (shell->mask_surface); data = cairo_image_surface_get_data (shell->mask_surface); data += mask_src_y * stride + mask_src_x * 4; gegl_buffer_get (shell->mask, GEGL_RECTANGLE (scaled_x, scaled_y, scaled_width, scaled_height), buffer_scale, babl_format ("Y u8"), data, stride, GEGL_ABYSS_CLAMP); /* invert the mask so what is *not* the foreground object is masked */ mask_height = scaled_height; while (mask_height--) { gint mask_width = scaled_width; guchar *d = data; while (mask_width--) { guchar inv = 255 - *d; *d++ = inv; } data += stride; } } /* put it to the screen */ cairo_save (cr); cairo_rectangle (cr, x, y, w, h); cairo_scale (cr, 1.0 / scale_x, 1.0 / scale_y); cairo_set_source_surface (cr, xfer, x * scale_x - xfer_src_x, y * scale_y - xfer_src_y); if (shell->rotate_transform) { cairo_pattern_t *pattern; pattern = cairo_get_source (cr); cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); cairo_set_line_width (cr, 1.0); cairo_stroke_preserve (cr); cairo_surface_destroy (xfer); } cairo_clip (cr); cairo_paint (cr); if (shell->mask) { gimp_cairo_set_source_rgba (cr, &shell->mask_color); cairo_mask_surface (cr, shell->mask_surface, (x - mask_src_x) * scale_x, (y - mask_src_y) * scale_y); } cairo_restore (cr); }
GeglBuffer * gimp_drawable_transform_buffer_flip (GimpDrawable *drawable, GimpContext *context, GeglBuffer *orig_buffer, gint orig_offset_x, gint orig_offset_y, GimpOrientationType flip_type, gdouble axis, gboolean clip_result, gint *new_offset_x, gint *new_offset_y) { GeglBuffer *new_buffer; GeglRectangle src_rect; GeglRectangle dest_rect; gint orig_x, orig_y; gint orig_width, orig_height; gint new_x, new_y; gint new_width, new_height; gint i; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL); orig_x = orig_offset_x; orig_y = orig_offset_y; orig_width = gegl_buffer_get_width (orig_buffer); orig_height = gegl_buffer_get_height (orig_buffer); new_x = orig_x; new_y = orig_y; new_width = orig_width; new_height = orig_height; switch (flip_type) { case GIMP_ORIENTATION_HORIZONTAL: new_x = RINT (-((gdouble) orig_x + (gdouble) orig_width - axis) + axis); break; case GIMP_ORIENTATION_VERTICAL: new_y = RINT (-((gdouble) orig_y + (gdouble) orig_height - axis) + axis); break; case GIMP_ORIENTATION_UNKNOWN: g_return_val_if_reached (NULL); break; } new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, new_width, new_height), gegl_buffer_get_format (orig_buffer)); if (clip_result && (new_x != orig_x || new_y != orig_y)) { GimpRGB bg; GeglColor *color; gint clip_x, clip_y; gint clip_width, clip_height; *new_offset_x = orig_x; *new_offset_y = orig_y; /* "Outside" a channel is transparency, not the bg color */ if (GIMP_IS_CHANNEL (drawable)) gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0); else gimp_context_get_background (context, &bg); color = gimp_gegl_color_new (&bg); gegl_buffer_set_color (new_buffer, NULL, color); g_object_unref (color); if (gimp_rectangle_intersect (orig_x, orig_y, orig_width, orig_height, new_x, new_y, new_width, new_height, &clip_x, &clip_y, &clip_width, &clip_height)) { orig_x = new_x = clip_x - orig_x; orig_y = new_y = clip_y - orig_y; } orig_width = new_width = clip_width; orig_height = new_height = clip_height; } else { *new_offset_x = new_x; *new_offset_y = new_y; orig_x = 0; orig_y = 0; new_x = 0; new_y = 0; } if (new_width == 0 && new_height == 0) return new_buffer; switch (flip_type) { case GIMP_ORIENTATION_HORIZONTAL: src_rect.x = orig_x; src_rect.y = orig_y; src_rect.width = 1; src_rect.height = orig_height; dest_rect.x = new_x + new_width - 1; dest_rect.y = new_y; dest_rect.width = 1; dest_rect.height = new_height; for (i = 0; i < orig_width; i++) { src_rect.x = i + orig_x; dest_rect.x = new_x + new_width - i - 1; gegl_buffer_copy (orig_buffer, &src_rect, new_buffer, &dest_rect); } break; case GIMP_ORIENTATION_VERTICAL: src_rect.x = orig_x; src_rect.y = orig_y; src_rect.width = orig_width; src_rect.height = 1; dest_rect.x = new_x; dest_rect.y = new_y + new_height - 1; dest_rect.width = new_width; dest_rect.height = 1; for (i = 0; i < orig_height; i++) { src_rect.y = i + orig_y; dest_rect.y = new_y + new_height - i - 1; gegl_buffer_copy (orig_buffer, &src_rect, new_buffer, &dest_rect); } break; case GIMP_ORIENTATION_UNKNOWN: break; } return new_buffer; }
GeglBuffer * gimp_drawable_transform_buffer_affine (GimpDrawable *drawable, GimpContext *context, GeglBuffer *orig_buffer, gint orig_offset_x, gint orig_offset_y, const GimpMatrix3 *matrix, GimpTransformDirection direction, GimpInterpolationType interpolation_type, gint recursion_level, GimpTransformResize clip_result, gint *new_offset_x, gint *new_offset_y, GimpProgress *progress) { GeglBuffer *new_buffer; GimpMatrix3 m; GimpMatrix3 inv; gint u1, v1, u2, v2; /* source bounding box */ gint x1, y1, x2, y2; /* target bounding box */ GeglNode *affine; GimpMatrix3 gegl_matrix; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL); g_return_val_if_fail (matrix != NULL, NULL); g_return_val_if_fail (new_offset_x != NULL, NULL); g_return_val_if_fail (new_offset_y != NULL, NULL); g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); m = *matrix; inv = *matrix; if (direction == GIMP_TRANSFORM_BACKWARD) { /* keep the original matrix here, so we dont need to recalculate * the inverse later */ gimp_matrix3_invert (&inv); } else { /* Find the inverse of the transformation matrix */ gimp_matrix3_invert (&m); } u1 = orig_offset_x; v1 = orig_offset_y; u2 = u1 + gegl_buffer_get_width (orig_buffer); v2 = v1 + gegl_buffer_get_height (orig_buffer); /* Always clip unfloated buffers since they must keep their size */ if (G_TYPE_FROM_INSTANCE (drawable) == GIMP_TYPE_CHANNEL && ! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer))) clip_result = GIMP_TRANSFORM_RESIZE_CLIP; /* Find the bounding coordinates of target */ gimp_transform_resize_boundary (&inv, clip_result, u1, v1, u2, v2, &x1, &y1, &x2, &y2); /* Get the new temporary buffer for the transformed result */ new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1), gegl_buffer_get_format (orig_buffer)); gimp_matrix3_identity (&gegl_matrix); gimp_matrix3_translate (&gegl_matrix, u1, v1); gimp_matrix3_mult (&inv, &gegl_matrix); gimp_matrix3_translate (&gegl_matrix, -x1, -y1); affine = gegl_node_new_child (NULL, "operation", "gegl:transform", "filter", gimp_interpolation_to_gegl_filter (interpolation_type), "hard-edges", TRUE, NULL); gimp_gegl_node_set_matrix (affine, &gegl_matrix); gimp_apply_operation (orig_buffer, progress, NULL, affine, new_buffer, NULL); g_object_unref (affine); *new_offset_x = x1; *new_offset_y = y1; return new_buffer; }
static gint ico_get_layer_num_colors (gint32 layer, gboolean *uses_alpha_levels) { gint w, h; gint bpp; gint num_colors = 0; guint num_pixels; guchar *buf; guchar *src; guint32 *colors; guint32 *c; GHashTable *hash; GeglBuffer *buffer = gimp_drawable_get_buffer (layer); const Babl *format; w = gegl_buffer_get_width (buffer); h = gegl_buffer_get_height (buffer); num_pixels = w * h; switch (gimp_drawable_type (layer)) { case GIMP_RGB_IMAGE: format = babl_format ("R'G'B' u8"); break; case GIMP_RGBA_IMAGE: format = babl_format ("R'G'B'A u8"); break; case GIMP_GRAY_IMAGE: format = babl_format ("Y' u8"); break; case GIMP_GRAYA_IMAGE: format = babl_format ("Y'A u8"); break; case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE: format = gegl_buffer_get_format (buffer); default: g_return_val_if_reached (0); } bpp = babl_format_get_bytes_per_pixel (format); buf = src = g_new (guchar, num_pixels * bpp); gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); g_object_unref (buffer); hash = g_hash_table_new (g_int_hash, g_int_equal); *uses_alpha_levels = FALSE; colors = c = g_new (guint32, num_pixels); switch (bpp) { case 1: while (num_pixels--) { *c = *src; g_hash_table_insert (hash, c, c); src++; c++; } break; case 2: while (num_pixels--) { *c = (src[1] << 8) | src[0]; if (src[1] != 0 && src[1] != 255) *uses_alpha_levels = TRUE; g_hash_table_insert (hash, c, c); src += 2; c++; } break; case 3: while (num_pixels--) { *c = (src[2] << 16) | (src[1] << 8) | src[0]; g_hash_table_insert (hash, c, c); src += 3; c++; } break; case 4: while (num_pixels--) { *c = (src[3] << 24) | (src[2] << 16) | (src[1] << 8) | src[0]; if (src[3] != 0 && src[3] != 255) *uses_alpha_levels = TRUE; g_hash_table_insert (hash, c, c); src += 4; c++; } break; } num_colors = g_hash_table_size (hash); g_hash_table_destroy (hash); g_free (colors); g_free (buf); return num_colors; }
static gint32 ReadImage (FILE *fd, const gchar *filename, gint len, gint height, CMap cmap, gint ncols, gint format, gint interlace, gint number, guint leftpos, guint toppos, guint screenwidth, guint screenheight) { static gint32 image_ID = -1; static gint frame_number = 1; gint32 layer_ID; GeglBuffer *buffer; guchar *dest, *temp; guchar c; gint xpos = 0, ypos = 0, pass = 0; gint cur_progress, max_progress; gint v; gint i, j; gchar *framename; gchar *framename_ptr; gboolean alpha_frame = FALSE; static gint previous_disposal; /* Guard against bogus frame size */ if (len < 1 || height < 1) { g_message ("Bogus frame dimensions"); return -1; } /* ** Initialize the Compression routines */ if (! ReadOK (fd, &c, 1)) { g_message ("EOF / read error on image data"); return -1; } if (LZWReadByte (fd, TRUE, c) < 0) { g_message ("Error while reading"); return -1; } if (frame_number == 1) { /* Guard against bogus logical screen size values */ if (screenwidth == 0) screenwidth = len; if (screenheight == 0) screenheight = height; image_ID = gimp_image_new (screenwidth, screenheight, GIMP_INDEXED); gimp_image_set_filename (image_ID, filename); for (i = 0, j = 0; i < ncols; i++) { used_cmap[0][i] = gimp_cmap[j++] = cmap[0][i]; used_cmap[1][i] = gimp_cmap[j++] = cmap[1][i]; used_cmap[2][i] = gimp_cmap[j++] = cmap[2][i]; } gimp_image_set_colormap (image_ID, gimp_cmap, ncols); if (Gif89.delayTime < 0) framename = g_strdup (_("Background")); else framename = g_strdup_printf (_("Background (%d%s)"), 10 * Gif89.delayTime, "ms"); previous_disposal = Gif89.disposal; if (Gif89.transparent == -1) { layer_ID = gimp_layer_new (image_ID, framename, len, height, GIMP_INDEXED_IMAGE, 100, GIMP_NORMAL_MODE); } else { layer_ID = gimp_layer_new (image_ID, framename, len, height, GIMP_INDEXEDA_IMAGE, 100, GIMP_NORMAL_MODE); alpha_frame=TRUE; } g_free (framename); } else /* NOT FIRST FRAME */ { gimp_progress_set_text_printf (_("Opening '%s' (frame %d)"), gimp_filename_to_utf8 (filename), frame_number); gimp_progress_pulse (); /* If the colourmap is now different, we have to promote to RGB! */ if (! promote_to_rgb) { for (i = 0; i < ncols; i++) { if ((used_cmap[0][i] != cmap[0][i]) || (used_cmap[1][i] != cmap[1][i]) || (used_cmap[2][i] != cmap[2][i])) { /* Everything is RGB(A) from now on... sigh. */ promote_to_rgb = TRUE; /* Promote everything we have so far into RGB(A) */ #ifdef GIFDEBUG g_print ("GIF: Promoting image to RGB...\n"); #endif gimp_image_convert_rgb (image_ID); break; } } } if (Gif89.delayTime < 0) framename = g_strdup_printf (_("Frame %d"), frame_number); else framename = g_strdup_printf (_("Frame %d (%d%s)"), frame_number, 10 * Gif89.delayTime, "ms"); switch (previous_disposal) { case 0x00: break; /* 'don't care' */ case 0x01: framename_ptr = framename; framename = g_strconcat (framename, " (combine)", NULL); g_free (framename_ptr); break; case 0x02: framename_ptr = framename; framename = g_strconcat (framename, " (replace)", NULL); g_free (framename_ptr); break; case 0x03: /* Rarely-used, and unhandled by many loaders/players (including GIMP: we treat as 'combine' mode). */ framename_ptr = framename; framename = g_strconcat (framename, " (combine) (!)", NULL); g_free (framename_ptr); break; case 0x04: /* I've seen a composite of this type. stvo_online_banner2.gif */ case 0x05: case 0x06: /* I've seen a composite of this type. bn31.Gif */ case 0x07: framename_ptr = framename; framename = g_strconcat (framename, " (unknown disposal)", NULL); g_free (framename_ptr); g_message (_("GIF: Undocumented GIF composite type %d is " "not handled. Animation might not play or " "re-save perfectly."), previous_disposal); break; default: g_message ("Disposal word got corrupted. Bug."); break; } previous_disposal = Gif89.disposal; layer_ID = gimp_layer_new (image_ID, framename, len, height, promote_to_rgb ? GIMP_RGBA_IMAGE : GIMP_INDEXEDA_IMAGE, 100, GIMP_NORMAL_MODE); alpha_frame = TRUE; g_free (framename); } frame_number++; gimp_image_insert_layer (image_ID, layer_ID, -1, 0); gimp_layer_translate (layer_ID, (gint) leftpos, (gint) toppos); cur_progress = 0; max_progress = height; if (len > (G_MAXSIZE / height / (alpha_frame ? (promote_to_rgb ? 4 : 2) : 1))) { g_message ("'%s' has a larger image size than GIMP can handle.", gimp_filename_to_utf8 (filename)); return -1; } if (alpha_frame) dest = (guchar *) g_malloc ((gsize)len * (gsize)height * (promote_to_rgb ? 4 : 2)); else dest = (guchar *) g_malloc ((gsize)len * (gsize)height); #ifdef GIFDEBUG g_print ("GIF: reading %d by %d%s GIF image, ncols=%d\n", len, height, interlace ? " interlaced" : "", ncols); #endif if (! alpha_frame && promote_to_rgb) { /* I don't see how one would easily construct a GIF in which this could happen, but it's a mad mad world. */ g_message ("Ouch! Can't handle non-alpha RGB frames.\n" "Please file a bug report in GIMP's bugzilla."); gimp_quit (); } while ((v = LZWReadByte (fd, FALSE, c)) >= 0) { if (alpha_frame) { if (((guchar) v > highest_used_index) && !(v == Gif89.transparent)) highest_used_index = (guchar) v; if (promote_to_rgb) { temp = dest + ( (ypos * len) + xpos ) * 4; *(temp ) = (guchar) cmap[0][v]; *(temp+1) = (guchar) cmap[1][v]; *(temp+2) = (guchar) cmap[2][v]; *(temp+3) = (guchar) ((v == Gif89.transparent) ? 0 : 255); } else { temp = dest + ( (ypos * len) + xpos ) * 2; *temp = (guchar) v; *(temp+1) = (guchar) ((v == Gif89.transparent) ? 0 : 255); } } else { if ((guchar) v > highest_used_index) highest_used_index = (guchar) v; temp = dest + (ypos * len) + xpos; *temp = (guchar) v; } xpos++; if (xpos == len) { xpos = 0; if (interlace) { switch (pass) { case 0: case 1: ypos += 8; break; case 2: ypos += 4; break; case 3: ypos += 2; break; } if (ypos >= height) { pass++; switch (pass) { case 1: ypos = 4; break; case 2: ypos = 2; break; case 3: ypos = 1; break; default: goto fini; } } } else { ypos++; } if (frame_number == 1) { cur_progress++; if ((cur_progress % 16) == 0) gimp_progress_update ((gdouble) cur_progress / (gdouble) max_progress); } } if (ypos >= height) break; } fini: if (LZWReadByte (fd, FALSE, c) >= 0) g_print ("GIF: too much input data, ignoring extra...\n"); buffer = gimp_drawable_get_buffer (layer_ID); gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, len, height), 0, NULL, dest, GEGL_AUTO_ROWSTRIDE); g_free (dest); g_object_unref (buffer); gimp_progress_update (1.0); return image_ID; }
static GeglBuffer * gradient_precalc_shapeburst (GimpImage *image, GimpDrawable *drawable, const GeglRectangle *region, gdouble dist, GimpProgress *progress) { GimpChannel *mask; GeglBuffer *dist_buffer; GeglBuffer *temp_buffer; GeglNode *shapeburst; gimp_progress_set_text (progress, _("Calculating distance map")); /* allocate the distance map */ dist_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, region->width, region->height), babl_format ("Y float")); /* allocate the selection mask copy */ temp_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, region->width, region->height), babl_format ("Y float")); mask = gimp_image_get_mask (image); /* If the image mask is not empty, use it as the shape burst source */ if (! gimp_channel_is_empty (mask)) { gint x, y, width, height; gint off_x, off_y; gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height); gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); /* copy the mask to the temp mask */ gegl_buffer_copy (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)), GEGL_RECTANGLE (x + off_x, y + off_y, width, height), temp_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); } else { /* If the intended drawable has an alpha channel, use that */ if (gimp_drawable_has_alpha (drawable)) { const Babl *component_format; component_format = babl_format ("A float"); /* extract the aplha into the temp mask */ gegl_buffer_set_format (temp_buffer, component_format); gegl_buffer_copy (gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (region->x, region->y, region->width, region->height), temp_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); gegl_buffer_set_format (temp_buffer, NULL); } else { GeglColor *white = gegl_color_new ("white"); /* Otherwise, just fill the shapeburst to white */ gegl_buffer_set_color (temp_buffer, NULL, white); g_object_unref (white); } } shapeburst = gegl_node_new_child (NULL, "operation", "gimp:shapeburst", "normalize", TRUE, NULL); gimp_gegl_progress_connect (shapeburst, progress, NULL); gimp_gegl_apply_operation (temp_buffer, NULL, NULL, shapeburst, dist_buffer, NULL); g_object_unref (shapeburst); g_object_unref (temp_buffer); return dist_buffer; }
static void do_zcrop (gint32 drawable_id, gint32 image_id) { GeglBuffer *drawable_buffer; GeglBuffer *shadow_buffer; gfloat *linear_buf; const Babl *format; gint x, y, width, height; gint components; gint8 *killrows; gint8 *killcols; gint32 livingrows, livingcols, destrow, destcol; gint32 selection_copy_id; gboolean has_alpha; drawable_buffer = gimp_drawable_get_buffer (drawable_id); shadow_buffer = gimp_drawable_get_shadow_buffer (drawable_id); width = gegl_buffer_get_width (drawable_buffer); height = gegl_buffer_get_height (drawable_buffer); has_alpha = gimp_drawable_has_alpha (drawable_id); if (has_alpha) format = babl_format ("R'G'B'A float"); else format = babl_format ("R'G'B' float"); components = babl_format_get_n_components (format); killrows = g_new (gint8, height); killcols = g_new (gint8, width); linear_buf = g_new (gfloat, (width > height ? width : height) * components); /* search which rows to remove */ livingrows = 0; for (y = 0; y < height; y++) { gegl_buffer_get (drawable_buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0, format, linear_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); killrows[y] = TRUE; for (x = components; x < width * components; x += components) { if (! colors_equal (linear_buf, &linear_buf[x], components, has_alpha)) { livingrows++; killrows[y] = FALSE; break; } } } gimp_progress_update (0.25); /* search which columns to remove */ livingcols = 0; for (x = 0; x < width; x++) { gegl_buffer_get (drawable_buffer, GEGL_RECTANGLE (x, 0, 1, height), 1.0, format, linear_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); killcols[x] = TRUE; for (y = components; y < height * components; y += components) { if (! colors_equal (linear_buf, &linear_buf[y], components, has_alpha)) { livingcols++; killcols[x] = FALSE; break; } } } gimp_progress_update (0.5); if ((livingcols == 0 || livingrows == 0) || (livingcols == width && livingrows == height)) { g_message (_("Nothing to crop.")); g_free (linear_buf); g_free (killrows); g_free (killcols); return; } /* restitute living rows */ destrow = 0; for (y = 0; y < height; y++) { if (!killrows[y]) { gegl_buffer_copy (drawable_buffer, GEGL_RECTANGLE (0, y, width, 1), GEGL_ABYSS_NONE, shadow_buffer, GEGL_RECTANGLE (0, destrow, width, 1)); destrow++; } } gimp_progress_update (0.75); /* restitute living columns */ destcol = 0; for (x = 0; x < width; x++) { if (!killcols[x]) { gegl_buffer_copy (shadow_buffer, GEGL_RECTANGLE (x, 0, 1, height), GEGL_ABYSS_NONE, shadow_buffer, GEGL_RECTANGLE (destcol, 0, 1, height)); destcol++; } } gimp_progress_update (1.00); gimp_image_undo_group_start (image_id); selection_copy_id = gimp_selection_save (image_id); gimp_selection_none (image_id); gegl_buffer_flush (shadow_buffer); gimp_drawable_merge_shadow (drawable_id, TRUE); gegl_buffer_flush (drawable_buffer); gimp_image_select_item (image_id, GIMP_CHANNEL_OP_REPLACE, selection_copy_id); gimp_image_remove_channel (image_id, selection_copy_id); gimp_image_crop (image_id, livingcols, livingrows, 0, 0); gimp_image_undo_group_end (image_id); g_object_unref (shadow_buffer); g_object_unref (drawable_buffer); g_free (linear_buf); g_free (killrows); g_free (killcols); }
static void gimp_dodge_burn_motion (GimpPaintCore *paint_core, GimpDrawable *drawable, GimpPaintOptions *paint_options, const GimpCoords *coords) { GimpDodgeBurnOptions *options = GIMP_DODGE_BURN_OPTIONS (paint_options); GimpContext *context = GIMP_CONTEXT (paint_options); GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); GeglBuffer *paint_buffer; gint paint_buffer_x; gint paint_buffer_y; gdouble fade_point; gdouble opacity; gdouble force; fade_point = gimp_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); opacity = gimp_dynamics_get_linear_value (dynamics, GIMP_DYNAMICS_OUTPUT_OPACITY, coords, paint_options, fade_point); if (opacity == 0.0) return; paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable, paint_options, coords, &paint_buffer_x, &paint_buffer_y); if (! paint_buffer) return; /* DodgeBurn the region */ gimp_gegl_dodgeburn (gimp_paint_core_get_orig_image (paint_core), GEGL_RECTANGLE (paint_buffer_x, paint_buffer_y, gegl_buffer_get_width (paint_buffer), gegl_buffer_get_height (paint_buffer)), paint_buffer, GEGL_RECTANGLE (0, 0, 0, 0), options->exposure / 100.0, options->type, options->mode); if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE)) force = gimp_dynamics_get_linear_value (dynamics, GIMP_DYNAMICS_OUTPUT_FORCE, coords, paint_options, fade_point); else force = paint_options->brush_force; /* Replace the newly dodgedburned area (paint_area) to the image */ gimp_brush_core_replace_canvas (GIMP_BRUSH_CORE (paint_core), drawable, coords, MIN (opacity, GIMP_OPACITY_OPAQUE), gimp_context_get_opacity (context), gimp_paint_options_get_brush_mode (paint_options), force, GIMP_PAINT_CONSTANT); }
void gimp_drawable_blend (GimpDrawable *drawable, GimpContext *context, GimpGradient *gradient, GimpLayerModeEffects paint_mode, GimpGradientType gradient_type, gdouble opacity, gdouble offset, GimpRepeatMode repeat, gboolean reverse, gboolean supersample, gint max_depth, gdouble threshold, gboolean dither, gdouble startx, gdouble starty, gdouble endx, gdouble endy, GimpProgress *progress) { GimpImage *image; GeglBuffer *buffer; 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_CONTEXT (context)); g_return_if_fail (GIMP_IS_GRADIENT (gradient)); g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); image = gimp_item_get_image (GIMP_ITEM (drawable)); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height)) return; gimp_set_busy (image->gimp); /* Always create an alpha temp buf (for generality) */ buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), gimp_drawable_get_format_with_alpha (drawable)); gradient_fill_region (image, drawable, context, buffer, GEGL_RECTANGLE (0, 0, width, height), gradient, gradient_type, offset, repeat, reverse, supersample, max_depth, threshold, dither, (startx - x), (starty - y), (endx - x), (endy - y), progress); gimp_drawable_apply_buffer (drawable, buffer, GEGL_RECTANGLE (0, 0, width, height), TRUE, C_("undo-type", "Blend"), opacity, paint_mode, NULL, x, y); /* update the image */ gimp_drawable_update (drawable, x, y, width, height); /* free the temporary buffer */ g_object_unref (buffer); gimp_unset_busy (image->gimp); }
GeglBuffer * gegl_operation_context_get_target (GeglOperationContext *context, const gchar *padname) { GeglBuffer *output; const GeglRectangle *result; const Babl *format; GeglNode *node; GeglOperation *operation; static gint linear_buffers = -1; #if 0 g_return_val_if_fail (GEGL_IS_OPERATION_CONTEXT (context), NULL); #endif if (linear_buffers == -1) linear_buffers = g_getenv ("GEGL_LINEAR_BUFFERS")?1:0; operation = context->operation; node = operation->node; /* <ick */ format = gegl_operation_get_format (operation, padname); if (format == NULL) { g_warning ("no format for %s presuming RGBA float\n", gegl_node_get_debug_name (node)); format = gegl_babl_rgba_linear_float (); } g_assert (format != NULL); g_assert (!strcmp (padname, "output")); result = &context->result_rect; if (result->width == 0 || result->height == 0) { if (linear_buffers) output = gegl_buffer_linear_new (GEGL_RECTANGLE(0, 0, 0, 0), format); else output = gegl_buffer_new (GEGL_RECTANGLE (0, 0, 0, 0), format); } else if (node->dont_cache == FALSE && ! GEGL_OPERATION_CLASS (G_OBJECT_GET_CLASS (operation))->no_cache) { GeglBuffer *cache; cache = GEGL_BUFFER (gegl_node_get_cache (node)); /* Only use the cache if the result is within the cache * extent. This is certainly not optimal. My gut feeling is that * the current caching mechanism needs to be redesigned */ if (gegl_rectangle_contains (gegl_buffer_get_extent (cache), result)) { output = g_object_ref (cache); } else { if (linear_buffers) output = gegl_buffer_linear_new (result, format); else output = gegl_buffer_new (result, format); } } else { if (linear_buffers) output = gegl_buffer_linear_new (result, format); else output = gegl_buffer_new (result, format); } gegl_operation_context_take_object (context, padname, G_OBJECT (output)); return output; }
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); GimpApplicator *applicator; 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)); } } applicator = gimp_applicator_new (NULL, gimp_drawable_get_linear (drawable), FALSE); if (mask) { GeglBuffer *mask_buffer; mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); gimp_applicator_set_mask_buffer (applicator, mask_buffer); gimp_applicator_set_mask_offset (applicator, -offset_x, -offset_y); } gimp_applicator_set_src_buffer (applicator, base_buffer); gimp_applicator_set_dest_buffer (applicator, gimp_drawable_get_buffer (drawable)); gimp_applicator_set_apply_buffer (applicator, buffer); gimp_applicator_set_apply_offset (applicator, base_x - buffer_region->x, base_y - buffer_region->y); gimp_applicator_set_mode (applicator, opacity, mode); gimp_applicator_set_affect (applicator, gimp_drawable_get_active_mask (drawable)); gimp_applicator_blit (applicator, GEGL_RECTANGLE (x, y, width, height)); g_object_unref (applicator); }
/* 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_buffer (GimpDrawable *drawable, GeglBuffer *buffer, const GeglRectangle *buffer_region, gboolean push_undo, const gchar *undo_desc, gdouble opacity, GeglBuffer *mask_buffer, const GeglRectangle *mask_buffer_region, gint dest_x, gint dest_y) { GimpItem *item = GIMP_ITEM (drawable); GimpImage *image = gimp_item_get_image (item); GimpChannel *mask = gimp_image_get_mask (image); GeglBuffer *drawable_buffer; gint x, y, width, height; gint offset_x, offset_y; 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); /* 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 (dest_x, dest_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 the calling procedure specified an undo step... */ if (push_undo) gimp_drawable_push_undo (drawable, undo_desc, NULL, x, y, width, height); drawable_buffer = gimp_drawable_get_buffer (drawable); if (mask) { GeglBuffer *src_buffer; GeglBuffer *dest_buffer; src_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)); dest_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), gegl_buffer_get_format (src_buffer)); gegl_buffer_copy (src_buffer, GEGL_RECTANGLE (x + offset_x, y + offset_y, width, height), dest_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); gimp_gegl_combine_mask (mask_buffer, mask_buffer_region, dest_buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0); gimp_gegl_replace (buffer, buffer_region, drawable_buffer, GEGL_RECTANGLE (x, y, width, height), dest_buffer, GEGL_RECTANGLE (0, 0, width, height), drawable_buffer, GEGL_RECTANGLE (x, y, width, height), opacity, active_components); g_object_unref (dest_buffer); } else { gimp_gegl_replace (buffer, buffer_region, drawable_buffer, GEGL_RECTANGLE (x, y, width, height), mask_buffer, mask_buffer_region, drawable_buffer, GEGL_RECTANGLE (x, y, width, height), opacity, active_components); } }
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); GimpBlob *blob_union = NULL; GimpBlob *blob_to_render; GeglBuffer *paint_buffer; gint paint_buffer_x; gint paint_buffer_y; GimpRGB foreground; GeglColor *color; 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; paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable, paint_options, coords, &paint_buffer_x, &paint_buffer_y); ink->cur_blob = NULL; if (! paint_buffer) return; gimp_context_get_foreground (context, &foreground); color = gimp_gegl_color_new (&foreground); gegl_buffer_set_color (paint_buffer, NULL, color); g_object_unref (color); /* draw the blob directly to the canvas_buffer */ render_blob (paint_core->canvas_buffer, GEGL_RECTANGLE (paint_core->paint_buffer_x, paint_core->paint_buffer_y, gegl_buffer_get_width (paint_core->paint_buffer), gegl_buffer_get_height (paint_core->paint_buffer)), blob_to_render); /* draw the paint_area using the just rendered canvas_buffer as mask */ gimp_paint_core_paste (paint_core, NULL, paint_core->paint_buffer_x, paint_core->paint_buffer_y, 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_convolve_motion (GimpPaintCore *paint_core, GimpDrawable *drawable, GimpPaintOptions *paint_options, const GimpCoords *coords) { 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); GimpDynamics *dynamics = GIMP_BRUSH_CORE (paint_core)->dynamics; GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); GeglBuffer *paint_buffer; gint paint_buffer_x; gint paint_buffer_y; GimpTempBuf *temp_buf; GeglBuffer *convolve_buffer; gdouble fade_point; gdouble opacity; gdouble rate; fade_point = gimp_paint_options_get_fade (paint_options, image, paint_core->pixel_dist); opacity = gimp_dynamics_get_linear_value (dynamics, GIMP_DYNAMICS_OUTPUT_OPACITY, coords, paint_options, fade_point); if (opacity == 0.0) return; paint_buffer = gimp_paint_core_get_paint_buffer (paint_core, drawable, paint_options, coords, &paint_buffer_x, &paint_buffer_y); if (! paint_buffer) return; rate = (options->rate * gimp_dynamics_get_linear_value (dynamics, GIMP_DYNAMICS_OUTPUT_RATE, coords, paint_options, fade_point)); gimp_convolve_calculate_matrix (convolve, options->type, gimp_brush_get_width (brush_core->brush) / 2, gimp_brush_get_height (brush_core->brush) / 2, rate); /* need a linear buffer for gimp_gegl_convolve() */ temp_buf = gimp_temp_buf_new (gegl_buffer_get_width (paint_buffer), gegl_buffer_get_height (paint_buffer), gegl_buffer_get_format (paint_buffer)); convolve_buffer = gimp_temp_buf_create_buffer (temp_buf); gimp_temp_buf_unref (temp_buf); gegl_buffer_copy (gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (paint_buffer_x, paint_buffer_y, gegl_buffer_get_width (paint_buffer), gegl_buffer_get_height (paint_buffer)), convolve_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); gimp_gegl_convolve (convolve_buffer, GEGL_RECTANGLE (0, 0, gegl_buffer_get_width (convolve_buffer), gegl_buffer_get_height (convolve_buffer)), paint_buffer, GEGL_RECTANGLE (0, 0, gegl_buffer_get_width (paint_buffer), gegl_buffer_get_height (paint_buffer)), convolve->matrix, 3, convolve->matrix_divisor, GIMP_NORMAL_CONVOL, TRUE); g_object_unref (convolve_buffer); gimp_brush_core_replace_canvas (brush_core, drawable, coords, 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 process (GeglOperation *operation, GeglBuffer *input, GeglBuffer *output, const GeglRectangle *roi, gint level) { GeglRectangle src_rect; GeglRectangle *whole_region; GeglProperties *o = GEGL_PROPERTIES (operation); GeglOperationAreaFilter *op_area; op_area = GEGL_OPERATION_AREA_FILTER (operation); whole_region = gegl_operation_source_get_bounding_box (operation, "input"); if (gegl_operation_use_opencl (operation)) if (cl_process (operation, input, output, roi)) return TRUE; if (o->size_x * o->size_y < SQR (ALLOC_THRESHOLD_SIZE)) { gfloat background_color[4]; gfloat *input_buf = g_new (gfloat, (CHUNK_SIZE + o->size_x * 2) * (CHUNK_SIZE + o->size_y * 2) * 4); gfloat *output_buf = g_new (gfloat, SQR (CHUNK_SIZE) * 4); gint i, j; gegl_color_get_pixel (o->background, babl_format("RaGaBaA float"), background_color); for (j = 0; (j-1) * CHUNK_SIZE < roi->height; j++) for (i = 0; (i-1) * CHUNK_SIZE < roi->width; i++) { GeglRectangle chunked_result; GeglRectangle chunked_sizes; chunked_result = *GEGL_RECTANGLE (roi->x + i * CHUNK_SIZE, roi->y + j * CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE); gegl_rectangle_intersect (&chunked_result, &chunked_result, roi); if (chunked_result.width < 1 || chunked_result.height < 1) continue; src_rect = chunked_result; src_rect.x -= op_area->left; src_rect.y -= op_area->top; src_rect.width += op_area->left + op_area->right; src_rect.height += op_area->top + op_area->bottom; gegl_buffer_get (input, &src_rect, 1.0, babl_format ("RaGaBaA float"), input_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); gegl_rectangle_copy (&chunked_sizes, &chunked_result); chunked_sizes.x = 0; chunked_sizes.y = 0; set_rectangle (output_buf, &chunked_sizes, &chunked_sizes, chunked_result.width, background_color, GEGL_PIXELIZE_NORM_INFINITY); pixelize (input_buf, output_buf, &chunked_result, &src_rect, whole_region, o); gegl_buffer_set (output, &chunked_result, 0, babl_format ("RaGaBaA float"), output_buf, GEGL_AUTO_ROWSTRIDE); } g_free (input_buf); g_free (output_buf); } else { gegl_buffer_set_color (output, roi, o->background); pixelize_noalloc (input, output, roi, whole_region, o); } return TRUE; }
void gimp_gegl_apply_operation (GeglBuffer *src_buffer, GimpProgress *progress, const gchar *undo_desc, GeglNode *operation, GeglBuffer *dest_buffer, const GeglRectangle *dest_rect) { GeglNode *gegl; GeglNode *dest_node; GeglRectangle rect = { 0, }; gdouble value; gboolean progress_active = FALSE; g_return_if_fail (src_buffer == NULL || GEGL_IS_BUFFER (src_buffer)); g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); g_return_if_fail (GEGL_IS_NODE (operation)); g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); if (dest_rect) { rect = *dest_rect; } else { rect = *GEGL_RECTANGLE (0, 0, gegl_buffer_get_width (dest_buffer), gegl_buffer_get_height (dest_buffer)); } gegl = gegl_node_new (); if (! gegl_node_get_parent (operation)) gegl_node_add_child (gegl, operation); if (src_buffer) { GeglNode *src_node; /* dup() because reading and writing the same buffer doesn't * work with area ops when using a processor. See bug #701875. */ if (progress && (src_buffer == dest_buffer)) src_buffer = gegl_buffer_dup (src_buffer); else g_object_ref (src_buffer); src_node = gegl_node_new_child (gegl, "operation", "gegl:buffer-source", "buffer", src_buffer, NULL); g_object_unref (src_buffer); gegl_node_connect_to (src_node, "output", operation, "input"); } dest_node = gegl_node_new_child (gegl, "operation", "gegl:write-buffer", "buffer", dest_buffer, NULL); gegl_node_connect_to (operation, "output", dest_node, "input"); if (progress) { GeglProcessor *processor; processor = gegl_node_new_processor (dest_node, &rect); progress_active = gimp_progress_is_active (progress); if (progress_active) { if (undo_desc) gimp_progress_set_text (progress, undo_desc); } else { gimp_progress_start (progress, undo_desc, FALSE); } while (gegl_processor_work (processor, &value)) gimp_progress_set_value (progress, value); g_object_unref (processor); } else { gegl_node_blit (dest_node, 1.0, &rect, NULL, NULL, 0, GEGL_BLIT_DEFAULT); } g_object_unref (gegl); if (progress && ! progress_active) gimp_progress_end (progress); }
static void ico_image_get_reduced_buf (guint32 layer, gint bpp, gint *num_colors, guchar **cmap_out, guchar **buf_out) { gint32 tmp_image; gint32 tmp_layer; gint w, h; guchar *buf; guchar *cmap = NULL; GeglBuffer *buffer = gimp_drawable_get_buffer (layer); const Babl *format; w = gegl_buffer_get_width (buffer); h = gegl_buffer_get_height (buffer); switch (gimp_drawable_type (layer)) { case GIMP_RGB_IMAGE: format = babl_format ("R'G'B' u8"); break; case GIMP_RGBA_IMAGE: format = babl_format ("R'G'B'A u8"); break; case GIMP_GRAY_IMAGE: format = babl_format ("Y' u8"); break; case GIMP_GRAYA_IMAGE: format = babl_format ("Y'A u8"); break; case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE: format = gegl_buffer_get_format (buffer); default: g_return_if_reached (); } *num_colors = 0; buf = g_new (guchar, w * h * 4); if (bpp <= 8 || bpp == 24 || babl_format_get_bytes_per_pixel (format) != 4) { gint32 image = gimp_item_get_image (layer); GeglBuffer *tmp; tmp_image = gimp_image_new (w, h, gimp_image_base_type (image)); gimp_image_undo_disable (tmp_image); if (gimp_drawable_is_indexed (layer)) { guchar *cmap; gint num_colors; cmap = gimp_image_get_colormap (image, &num_colors); gimp_image_set_colormap (tmp_image, cmap, num_colors); g_free (cmap); } tmp_layer = gimp_layer_new (tmp_image, "tmp", w, h, gimp_drawable_type (layer), 100, GIMP_NORMAL_MODE); gimp_image_insert_layer (tmp_image, tmp_layer, -1, 0); tmp = gimp_drawable_get_buffer (tmp_layer); gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, tmp, NULL); g_object_unref (tmp); if (! gimp_drawable_is_rgb (tmp_layer)) gimp_image_convert_rgb (tmp_image); if (bpp <= 8) { gimp_image_convert_indexed (tmp_image, GIMP_FS_DITHER, GIMP_MAKE_PALETTE, 1 << bpp, TRUE, FALSE, "dummy"); cmap = gimp_image_get_colormap (tmp_image, num_colors); if (*num_colors == (1 << bpp) && ! ico_cmap_contains_black (cmap, *num_colors)) { /* Windows icons with color maps need the color black. * We need to eliminate one more color to make room for black. */ if (gimp_drawable_is_indexed (layer)) { g_free (cmap); cmap = gimp_image_get_colormap (image, num_colors); gimp_image_set_colormap (tmp_image, cmap, *num_colors); } else if (gimp_drawable_is_gray (layer)) { gimp_image_convert_grayscale (tmp_image); } else { gimp_image_convert_rgb (tmp_image); } tmp = gimp_drawable_get_buffer (tmp_layer); gegl_buffer_set (tmp, GEGL_RECTANGLE (0, 0, w, h), 0, format, buf, GEGL_AUTO_ROWSTRIDE); g_object_unref (tmp); if (! gimp_drawable_is_rgb (layer)) gimp_image_convert_rgb (tmp_image); gimp_image_convert_indexed (tmp_image, GIMP_FS_DITHER, GIMP_MAKE_PALETTE, (1<<bpp) - 1, TRUE, FALSE, "dummy"); g_free (cmap); cmap = gimp_image_get_colormap (tmp_image, num_colors); } gimp_image_convert_rgb (tmp_image); } else if (bpp == 24) { GimpParam *return_vals; gint n_return_vals; return_vals = gimp_run_procedure ("plug-in-threshold-alpha", &n_return_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, tmp_image, GIMP_PDB_DRAWABLE, tmp_layer, GIMP_PDB_INT32, ICO_ALPHA_THRESHOLD, GIMP_PDB_END); gimp_destroy_params (return_vals, n_return_vals); } gimp_layer_add_alpha (tmp_layer); tmp = gimp_drawable_get_buffer (tmp_layer); gegl_buffer_get (tmp, GEGL_RECTANGLE (0, 0, w, h), 1.0, NULL, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); g_object_unref (tmp); gimp_image_delete (tmp_image); } else { gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); } g_object_unref (buffer); *cmap_out = cmap; *buf_out = buf; }
TEST () { GeglBuffer *out; gint i; GeglBuffer *linear_a; GeglBuffer *linear_b; GeglBuffer *linear_c; GeglBuffer *linear_d; gfloat *linear_data; GeglBufferIterator *iter; GeglRectangle out_extent = {-1, -1, 5, 5}; test_start(); linear_data = malloc (sizeof(float) * 3 * 3); for (i = 0; i < 3 * 3; ++i) linear_data[i] = 0.25; linear_a = gegl_buffer_linear_new_from_data (linear_data, babl_format ("Y float"), GEGL_RECTANGLE(-1, -1, 3, 3), GEGL_AUTO_ROWSTRIDE, NULL, NULL); linear_b = gegl_buffer_linear_new_from_data (linear_data, babl_format ("Y float"), GEGL_RECTANGLE(1, -1, 3, 3), GEGL_AUTO_ROWSTRIDE, NULL, NULL); linear_c = gegl_buffer_linear_new_from_data (linear_data, babl_format ("Y float"), GEGL_RECTANGLE(1, 1, 3, 3), GEGL_AUTO_ROWSTRIDE, NULL, NULL); linear_d = gegl_buffer_linear_new_from_data (linear_data, babl_format ("Y float"), GEGL_RECTANGLE(-1, 1, 3, 3), GEGL_AUTO_ROWSTRIDE, NULL, NULL); out = gegl_buffer_new (&out_extent, babl_format ("Y float")); iter = gegl_buffer_iterator_new (out, &out_extent, 0, NULL, GEGL_BUFFER_WRITE, GEGL_ABYSS_NONE); gegl_buffer_iterator_add (iter, linear_a, &out_extent, 0, NULL, GEGL_BUFFER_READ, GEGL_ABYSS_BLACK); gegl_buffer_iterator_add (iter, linear_b, &out_extent, 0, NULL, GEGL_BUFFER_READ, GEGL_ABYSS_BLACK); gegl_buffer_iterator_add (iter, linear_c, &out_extent, 0, NULL, GEGL_BUFFER_READ, GEGL_ABYSS_BLACK); gegl_buffer_iterator_add (iter, linear_d, &out_extent, 0, NULL, GEGL_BUFFER_READ, GEGL_ABYSS_BLACK); while (gegl_buffer_iterator_next (iter)) { gint ix, iy, pos; pos = 0; for (iy = iter->roi[0].y; iy < iter->roi[0].y + iter->roi[0].height; ++iy) for (ix = iter->roi[0].x; ix < iter->roi[0].x + iter->roi[0].width; ++ix) { gfloat **fdata = (gfloat **)iter->data; fdata[0][pos] = fdata[1][pos] + fdata[2][pos] + fdata[3][pos] + fdata[4][pos]; if (fdata[0][pos] > 1.0f) fdata[0][pos] = 1.0f; ++pos; } } print_buffer (out); g_object_unref (out); g_object_unref (linear_a); g_object_unref (linear_b); g_object_unref (linear_c); g_object_unref (linear_d); free (linear_data); test_end (); }
static void gimp_mask_undo_pop (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { GimpMaskUndo *mask_undo = GIMP_MASK_UNDO (undo); GimpChannel *channel = GIMP_CHANNEL (GIMP_ITEM_UNDO (undo)->item); GimpDrawable *drawable = GIMP_DRAWABLE (channel); GeglBuffer *new_buffer; const Babl *format; gint x1, y1, x2, y2; gint width = 0; gint height = 0; GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum); if (gimp_channel_bounds (channel, &x1, &y1, &x2, &y2)) { new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, x2 - x1, y2 - y1), gimp_drawable_get_format (drawable)); gegl_buffer_copy (gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1), new_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); gegl_buffer_clear (gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1)); } else { new_buffer = NULL; } format = gimp_drawable_get_format (drawable); if (mask_undo->convert_format) { GeglBuffer *buffer; gint width = gimp_item_get_width (GIMP_ITEM (channel)); gint height = gimp_item_get_height (GIMP_ITEM (channel)); buffer = gimp_gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), mask_undo->format); gegl_buffer_clear (buffer, NULL); gimp_drawable_set_buffer (drawable, FALSE, NULL, buffer); g_object_unref (buffer); } if (mask_undo->buffer) { width = gegl_buffer_get_width (mask_undo->buffer); height = gegl_buffer_get_height (mask_undo->buffer); gegl_buffer_copy (mask_undo->buffer, NULL, gimp_drawable_get_buffer (drawable), GEGL_RECTANGLE (mask_undo->x, mask_undo->y, 0, 0)); g_object_unref (mask_undo->buffer); } /* invalidate the current bounds and boundary of the mask */ gimp_drawable_invalidate_boundary (drawable); if (mask_undo->buffer) { channel->empty = FALSE; channel->x1 = mask_undo->x; channel->y1 = mask_undo->y; channel->x2 = mask_undo->x + width; channel->y2 = mask_undo->y + height; } else { channel->empty = TRUE; channel->x1 = 0; channel->y1 = 0; channel->x2 = gimp_item_get_width (GIMP_ITEM (channel)); channel->y2 = gimp_item_get_height (GIMP_ITEM (channel)); } /* we know the bounds */ channel->bounds_known = TRUE; /* set the new mask undo parameters */ mask_undo->buffer = new_buffer; mask_undo->x = x1; mask_undo->y = y1; mask_undo->format = format; gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, gimp_item_get_width (GIMP_ITEM (channel)), gimp_item_get_height (GIMP_ITEM (channel))); }
static GeglTile * gimp_tile_handler_projection_validate (GeglTileSource *source, GeglTile *tile, gint x, gint y) { GimpTileHandlerProjection *projection; cairo_region_t *tile_region; cairo_rectangle_int_t tile_rect; projection = GIMP_TILE_HANDLER_PROJECTION (source); if (cairo_region_is_empty (projection->dirty_region)) return tile; tile_region = cairo_region_copy (projection->dirty_region); tile_rect.x = x * projection->tile_width; tile_rect.y = y * projection->tile_height; tile_rect.width = projection->tile_width; tile_rect.height = projection->tile_height; cairo_region_intersect_rectangle (tile_region, &tile_rect); if (! cairo_region_is_empty (tile_region)) { gint tile_bpp; gint tile_stride; gint n_rects; gint i; if (! tile) tile = gegl_tile_handler_create_tile (GEGL_TILE_HANDLER (source), x, y, 0); cairo_region_subtract_rectangle (projection->dirty_region, &tile_rect); tile_bpp = babl_format_get_bytes_per_pixel (projection->format); tile_stride = tile_bpp * projection->tile_width; gegl_tile_lock (tile); n_rects = cairo_region_num_rectangles (tile_region); #if 0 g_printerr ("%d ", n_rects); #endif for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t blit_rect; cairo_region_get_rectangle (tile_region, i, &blit_rect); #if 0 g_printerr ("constructing projection at %d %d %d %d\n", blit_rect.x, blit_rect.y, blit_rect.width, blit_rect.height); #endif gegl_node_blit (projection->graph, 1.0, GEGL_RECTANGLE (blit_rect.x, blit_rect.y, blit_rect.width, blit_rect.height), projection->format, gegl_tile_get_data (tile) + (blit_rect.y % projection->tile_height) * tile_stride + (blit_rect.x % projection->tile_width) * tile_bpp, tile_stride, GEGL_BLIT_DEFAULT); } gegl_tile_unlock (tile); } cairo_region_destroy (tile_region); return tile; }
GeglBuffer * gimp_drawable_transform_buffer_rotate (GimpDrawable *drawable, GimpContext *context, GeglBuffer *orig_buffer, gint orig_offset_x, gint orig_offset_y, GimpRotationType rotate_type, gdouble center_x, gdouble center_y, gboolean clip_result, gint *new_offset_x, gint *new_offset_y) { GeglBuffer *new_buffer; GeglRectangle src_rect; GeglRectangle dest_rect; gint orig_x, orig_y; gint orig_width, orig_height; gint orig_bpp; gint new_x, new_y; gint new_width, new_height; g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (GEGL_IS_BUFFER (orig_buffer), NULL); orig_x = orig_offset_x; orig_y = orig_offset_y; orig_width = gegl_buffer_get_width (orig_buffer); orig_height = gegl_buffer_get_height (orig_buffer); orig_bpp = babl_format_get_bytes_per_pixel (gegl_buffer_get_format (orig_buffer)); switch (rotate_type) { case GIMP_ROTATE_90: gimp_drawable_transform_rotate_point (orig_x, orig_y + orig_height, rotate_type, center_x, center_y, &new_x, &new_y); new_width = orig_height; new_height = orig_width; break; case GIMP_ROTATE_180: gimp_drawable_transform_rotate_point (orig_x + orig_width, orig_y + orig_height, rotate_type, center_x, center_y, &new_x, &new_y); new_width = orig_width; new_height = orig_height; break; case GIMP_ROTATE_270: gimp_drawable_transform_rotate_point (orig_x + orig_width, orig_y, rotate_type, center_x, center_y, &new_x, &new_y); new_width = orig_height; new_height = orig_width; break; default: g_return_val_if_reached (NULL); break; } if (clip_result && (new_x != orig_x || new_y != orig_y || new_width != orig_width || new_height != orig_height)) { GimpRGB bg; GeglColor *color; gint clip_x, clip_y; gint clip_width, clip_height; new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, orig_width, orig_height), gegl_buffer_get_format (orig_buffer)); *new_offset_x = orig_x; *new_offset_y = orig_y; /* "Outside" a channel is transparency, not the bg color */ if (GIMP_IS_CHANNEL (drawable)) gimp_rgba_set (&bg, 0.0, 0.0, 0.0, 0.0); else gimp_context_get_background (context, &bg); color = gimp_gegl_color_new (&bg); gegl_buffer_set_color (new_buffer, NULL, color); g_object_unref (color); if (gimp_rectangle_intersect (orig_x, orig_y, orig_width, orig_height, new_x, new_y, new_width, new_height, &clip_x, &clip_y, &clip_width, &clip_height)) { gint saved_orig_x = orig_x; gint saved_orig_y = orig_y; new_x = clip_x - orig_x; new_y = clip_y - orig_y; switch (rotate_type) { case GIMP_ROTATE_90: gimp_drawable_transform_rotate_point (clip_x + clip_width, clip_y, GIMP_ROTATE_270, center_x, center_y, &orig_x, &orig_y); orig_x -= saved_orig_x; orig_y -= saved_orig_y; orig_width = clip_height; orig_height = clip_width; break; case GIMP_ROTATE_180: orig_x = clip_x - orig_x; orig_y = clip_y - orig_y; orig_width = clip_width; orig_height = clip_height; break; case GIMP_ROTATE_270: gimp_drawable_transform_rotate_point (clip_x, clip_y + clip_height, GIMP_ROTATE_90, center_x, center_y, &orig_x, &orig_y); orig_x -= saved_orig_x; orig_y -= saved_orig_y; orig_width = clip_height; orig_height = clip_width; break; } new_width = clip_width; new_height = clip_height; } else { new_width = 0; new_height = 0; } } else { new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, new_width, new_height), gegl_buffer_get_format (orig_buffer)); *new_offset_x = new_x; *new_offset_y = new_y; orig_x = 0; orig_y = 0; new_x = 0; new_y = 0; } if (new_width < 1 || new_height < 1) return new_buffer; src_rect.x = orig_x; src_rect.y = orig_y; src_rect.width = orig_width; src_rect.height = orig_height; dest_rect.x = new_x; dest_rect.y = new_y; dest_rect.width = new_width; dest_rect.height = new_height; switch (rotate_type) { case GIMP_ROTATE_90: { guchar *buf = g_new (guchar, new_height * orig_bpp); gint i; g_assert (new_height == orig_width); src_rect.y = orig_y + orig_height - 1; src_rect.height = 1; dest_rect.x = new_x; dest_rect.width = 1; for (i = 0; i < orig_height; i++) { src_rect.y = orig_y + orig_height - 1 - i; dest_rect.x = new_x + i; gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf, GEGL_AUTO_ROWSTRIDE); } g_free (buf); } break; case GIMP_ROTATE_180: { guchar *buf = g_new (guchar, new_width * orig_bpp); gint i, j, k; g_assert (new_width == orig_width); src_rect.y = orig_y + orig_height - 1; src_rect.height = 1; dest_rect.y = new_y; dest_rect.height = 1; for (i = 0; i < orig_height; i++) { src_rect.y = orig_y + orig_height - 1 - i; dest_rect.y = new_y + i; gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); for (j = 0; j < orig_width / 2; j++) { guchar *left = buf + j * orig_bpp; guchar *right = buf + (orig_width - 1 - j) * orig_bpp; for (k = 0; k < orig_bpp; k++) { guchar tmp = left[k]; left[k] = right[k]; right[k] = tmp; } } gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf, GEGL_AUTO_ROWSTRIDE); } g_free (buf); } break; case GIMP_ROTATE_270: { guchar *buf = g_new (guchar, new_width * orig_bpp); gint i; g_assert (new_width == orig_height); src_rect.x = orig_x + orig_width - 1; src_rect.width = 1; dest_rect.y = new_y; dest_rect.height = 1; for (i = 0; i < orig_width; i++) { src_rect.x = orig_x + orig_width - 1 - i; dest_rect.y = new_y + i; gegl_buffer_get (orig_buffer, &src_rect, 1.0, NULL, buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); gegl_buffer_set (new_buffer, &dest_rect, 0, NULL, buf, GEGL_AUTO_ROWSTRIDE); } g_free (buf); } break; } return new_buffer; }
gint32 load_image (const gchar *filename, GimpRunMode runmode, gboolean preview, gboolean *resolution_loaded, GError **error) { gint32 volatile image_ID; gint32 layer_ID; struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; jpeg_saved_marker_ptr marker; FILE *infile; guchar *buf; guchar **rowbuf; GimpImageBaseType image_type; GimpImageType layer_type; GeglBuffer *buffer = NULL; const Babl *format; gint tile_height; gint scanlines; gint i, start, end; cmsHTRANSFORM cmyk_transform = NULL; /* We set up the normal JPEG error routines. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; if (!preview) { jerr.pub.output_message = my_output_message; gimp_progress_init_printf (_("Opening '%s'"), gimp_filename_to_utf8 (filename)); } if ((infile = g_fopen (filename, "rb")) == NULL) { g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), _("Could not open '%s' for reading: %s"), gimp_filename_to_utf8 (filename), g_strerror (errno)); return -1; } image_ID = -1; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object, close the input file, and return. */ jpeg_destroy_decompress (&cinfo); if (infile) fclose (infile); if (image_ID != -1 && !preview) gimp_image_delete (image_ID); if (preview) destroy_preview (); if (buffer) g_object_unref (buffer); return -1; } /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress (&cinfo); /* Step 2: specify data source (eg, a file) */ jpeg_stdio_src (&cinfo, infile); if (! preview) { /* - step 2.1: tell the lib to save the comments */ jpeg_save_markers (&cinfo, JPEG_COM, 0xffff); /* - step 2.2: tell the lib to save APP1 data (Exif or XMP) */ jpeg_save_markers (&cinfo, JPEG_APP0 + 1, 0xffff); /* - step 2.3: tell the lib to save APP2 data (ICC profiles) */ jpeg_save_markers (&cinfo, JPEG_APP0 + 2, 0xffff); } /* Step 3: read file parameters with jpeg_read_header() */ jpeg_read_header (&cinfo, TRUE); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here, except set the DCT * method. */ cinfo.dct_method = JDCT_FLOAT; /* Step 5: Start decompressor */ jpeg_start_decompress (&cinfo); /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. */ /* temporary buffer */ tile_height = gimp_tile_height (); buf = g_new (guchar, tile_height * cinfo.output_width * cinfo.output_components); rowbuf = g_new (guchar *, tile_height); for (i = 0; i < tile_height; i++) rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i; switch (cinfo.output_components) { case 1: image_type = GIMP_GRAY; layer_type = GIMP_GRAY_IMAGE; break; case 3: image_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; break; case 4: if (cinfo.out_color_space == JCS_CMYK) { image_type = GIMP_RGB; layer_type = GIMP_RGB_IMAGE; break; } /*fallthrough*/ default: g_message ("Don't know how to load JPEG images " "with %d color channels, using colorspace %d (%d).", cinfo.output_components, cinfo.out_color_space, cinfo.jpeg_color_space); return -1; break; } if (preview) { image_ID = preview_image_ID; } else { image_ID = gimp_image_new_with_precision (cinfo.output_width, cinfo.output_height, image_type, GIMP_PRECISION_U8_GAMMA); gimp_image_undo_disable (image_ID); gimp_image_set_filename (image_ID, filename); } if (preview) { preview_layer_ID = gimp_layer_new (preview_image_ID, _("JPEG preview"), cinfo.output_width, cinfo.output_height, layer_type, 100, GIMP_NORMAL_MODE); layer_ID = preview_layer_ID; } else { layer_ID = gimp_layer_new (image_ID, _("Background"), cinfo.output_width, cinfo.output_height, layer_type, 100, GIMP_NORMAL_MODE); } if (! preview) { GString *comment_buffer = NULL; guint8 *profile = NULL; guint profile_size = 0; /* Step 5.0: save the original JPEG settings in a parasite */ jpeg_detect_original_settings (&cinfo, image_ID); /* Step 5.1: check for comments, or Exif metadata in APP1 markers */ for (marker = cinfo.marker_list; marker; marker = marker->next) { const gchar *data = (const gchar *) marker->data; gsize len = marker->data_length; if (marker->marker == JPEG_COM) { #ifdef GIMP_UNSTABLE g_print ("jpeg-load: found image comment (%d bytes)\n", marker->data_length); #endif if (! comment_buffer) { comment_buffer = g_string_new_len (data, len); } else { /* concatenate multiple comments, separate them with LF */ g_string_append_c (comment_buffer, '\n'); g_string_append_len (comment_buffer, data, len); } } else if ((marker->marker == JPEG_APP0 + 1) && (len > sizeof (JPEG_APP_HEADER_EXIF) + 8) && ! strcmp (JPEG_APP_HEADER_EXIF, data)) { #ifdef GIMP_UNSTABLE g_print ("jpeg-load: found Exif block (%d bytes)\n", (gint) (len - sizeof (JPEG_APP_HEADER_EXIF))); #endif } } if (jpeg_load_resolution (image_ID, &cinfo)) { if (resolution_loaded) *resolution_loaded = TRUE; } /* if we found any comments, then make a parasite for them */ if (comment_buffer && comment_buffer->len) { GimpParasite *parasite; jpeg_load_sanitize_comment (comment_buffer->str); parasite = gimp_parasite_new ("gimp-comment", GIMP_PARASITE_PERSISTENT, strlen (comment_buffer->str) + 1, comment_buffer->str); gimp_image_attach_parasite (image_ID, parasite); gimp_parasite_free (parasite); g_string_free (comment_buffer, TRUE); } /* Step 5.3: check for an embedded ICC profile in APP2 markers */ jpeg_icc_read_profile (&cinfo, &profile, &profile_size); if (cinfo.out_color_space == JCS_CMYK) { cmyk_transform = jpeg_load_cmyk_transform (profile, profile_size); } else if (profile) /* don't attach the profile if we are transforming */ { GimpParasite *parasite; parasite = gimp_parasite_new ("icc-profile", GIMP_PARASITE_PERSISTENT | GIMP_PARASITE_UNDOABLE, profile_size, profile); gimp_image_attach_parasite (image_ID, parasite); gimp_parasite_free (parasite); } g_free (profile); /* Do not attach the "jpeg-save-options" parasite to the image * because this conflicts with the global defaults (bug #75398). */ } /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ buffer = gimp_drawable_get_buffer (layer_ID); format = babl_format (image_type == GIMP_RGB ? "R'G'B' u8" : "Y' u8"); while (cinfo.output_scanline < cinfo.output_height) { start = cinfo.output_scanline; end = cinfo.output_scanline + tile_height; end = MIN (end, cinfo.output_height); scanlines = end - start; for (i = 0; i < scanlines; i++) jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1); if (cinfo.out_color_space == JCS_CMYK) jpeg_load_cmyk_to_rgb (buf, cinfo.output_width * scanlines, cmyk_transform); gegl_buffer_set (buffer, GEGL_RECTANGLE (0, start, cinfo.output_width, scanlines), 0, format, buf, GEGL_AUTO_ROWSTRIDE); if (! preview && (cinfo.output_scanline % 32) == 0) gimp_progress_update ((gdouble) cinfo.output_scanline / (gdouble) cinfo.output_height); } /* Step 7: Finish decompression */ jpeg_finish_decompress (&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ if (cmyk_transform) cmsDeleteTransform (cmyk_transform); /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress (&cinfo); g_object_unref (buffer); /* free up the temporary buffers */ g_free (rowbuf); g_free (buf); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose (infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.num_warnings is nonzero). */ /* Detach from the drawable and add it to the image. */ if (! preview) { gimp_progress_update (1.0); } gimp_image_insert_layer (image_ID, layer_ID, -1, 0); return image_ID; }
static gboolean find_contiguous_segment (const gfloat *col, GeglBuffer *src_buffer, GeglBuffer *mask_buffer, const Babl *format, gint n_components, gboolean has_alpha, gint width, gboolean select_transparent, GimpSelectCriterion select_criterion, gboolean antialias, gfloat threshold, gint initial_x, gint initial_y, gint *start, gint *end) { gfloat s[MAX_CHANNELS]; gfloat mask_row[width]; gfloat diff; gegl_buffer_sample (src_buffer, initial_x, initial_y, NULL, s, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); diff = pixel_difference (col, s, antialias, threshold, n_components, has_alpha, select_transparent, select_criterion); /* check the starting pixel */ if (! diff) return FALSE; mask_row[initial_x] = diff; *start = initial_x - 1; while (*start >= 0 && diff) { gegl_buffer_sample (src_buffer, *start, initial_y, NULL, s, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); diff = pixel_difference (col, s, antialias, threshold, n_components, has_alpha, select_transparent, select_criterion); mask_row[*start] = diff; if (diff) (*start)--; } diff = 1; *end = initial_x + 1; while (*end < width && diff) { gegl_buffer_sample (src_buffer, *end, initial_y, NULL, s, format, GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE); diff = pixel_difference (col, s, antialias, threshold, n_components, has_alpha, select_transparent, select_criterion); mask_row[*end] = diff; if (diff) (*end)++; } gegl_buffer_set (mask_buffer, GEGL_RECTANGLE (*start, initial_y, *end - *start, 1), 0, babl_format ("Y float"), &mask_row[*start], GEGL_AUTO_ROWSTRIDE); return TRUE; }
void gimp_drawable_offset (GimpDrawable *drawable, GimpContext *context, gboolean wrap_around, GimpOffsetType fill_type, gint offset_x, gint offset_y) { GimpItem *item; GeglBuffer *src_buffer; GeglBuffer *new_buffer; GeglRectangle src_rect; GeglRectangle dest_rect; gint width, height; gint src_x, src_y; gint dest_x, dest_y; g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); g_return_if_fail (GIMP_IS_CONTEXT (context)); item = GIMP_ITEM (drawable); width = gimp_item_get_width (item); height = gimp_item_get_height (item); if (wrap_around) { /* avoid modulo operation on negative values */ while (offset_x < 0) offset_x += width; while (offset_y < 0) offset_y += height; offset_x %= width; offset_y %= height; } else { offset_x = CLAMP (offset_x, -width, width); offset_y = CLAMP (offset_y, -height, height); } if (offset_x == 0 && offset_y == 0) return; src_buffer = gimp_drawable_get_buffer (drawable); new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), gimp_drawable_get_format (drawable)); if (! wrap_around) { if (fill_type == GIMP_OFFSET_BACKGROUND) { GimpRGB bg; GeglColor *color; gimp_context_get_background (context, &bg); gimp_pickable_srgb_to_image_color (GIMP_PICKABLE (drawable), &bg, &bg); color = gimp_gegl_color_new (&bg); gegl_buffer_set_color (new_buffer, NULL, color); g_object_unref (color); } } if (offset_x >= 0) { src_x = 0; dest_x = offset_x; width = CLAMP ((width - offset_x), 0, width); } else { src_x = -offset_x; dest_x = 0; width = CLAMP ((width + offset_x), 0, width); } if (offset_y >= 0) { src_y = 0; dest_y = offset_y; height = CLAMP ((height - offset_y), 0, height); } else { src_y = -offset_y; dest_y = 0; height = CLAMP ((height + offset_y), 0, height); } /* Copy the center region */ if (width && height) { gegl_buffer_copy (src_buffer, GEGL_RECTANGLE (src_x, src_y, width, height), GEGL_ABYSS_NONE, new_buffer, GEGL_RECTANGLE (dest_x,dest_y, width, height)); } if (wrap_around) { /* Copy appropriately for wrap around */ if (offset_x >= 0 && offset_y >= 0) { src_x = gimp_item_get_width (item) - offset_x; src_y = gimp_item_get_height (item) - offset_y; } else if (offset_x >= 0 && offset_y < 0) { src_x = gimp_item_get_width (item) - offset_x; src_y = 0; } else if (offset_x < 0 && offset_y >= 0) { src_x = 0; src_y = gimp_item_get_height (item) - offset_y; } else if (offset_x < 0 && offset_y < 0) { src_x = 0; src_y = 0; } dest_x = (src_x + offset_x) % gimp_item_get_width (item); if (dest_x < 0) dest_x = gimp_item_get_width (item) + dest_x; dest_y = (src_y + offset_y) % gimp_item_get_height (item); if (dest_y < 0) dest_y = gimp_item_get_height (item) + dest_y; /* intersecting region */ if (offset_x != 0 && offset_y != 0) { gegl_buffer_copy (src_buffer, GEGL_RECTANGLE (src_x, src_y, ABS (offset_x), ABS (offset_y)), GEGL_ABYSS_NONE, new_buffer, GEGL_RECTANGLE (dest_x, dest_y, 0, 0)); } /* X offset */ if (offset_x != 0) { if (offset_y >= 0) { src_rect.x = src_x; src_rect.y = 0; src_rect.width = ABS (offset_x); src_rect.height = gimp_item_get_height (item) - ABS (offset_y); dest_rect.x = dest_x; dest_rect.y = dest_y + offset_y; } else if (offset_y < 0) { src_rect.x = src_x; src_rect.y = src_y - offset_y; src_rect.width = ABS (offset_x); src_rect.height = gimp_item_get_height (item) - ABS (offset_y); dest_rect.x = dest_x; dest_rect.y = 0; } gegl_buffer_copy (src_buffer, &src_rect, GEGL_ABYSS_NONE, new_buffer, &dest_rect); } /* X offset */ if (offset_y != 0) { if (offset_x >= 0) { src_rect.x = 0; src_rect.y = src_y; src_rect.width = gimp_item_get_width (item) - ABS (offset_x); src_rect.height = ABS (offset_y); dest_rect.x = dest_x + offset_x; dest_rect.y = dest_y; } else if (offset_x < 0) { src_rect.x = src_x - offset_x; src_rect.y = src_y; src_rect.width = gimp_item_get_width (item) - ABS (offset_x); src_rect.height = ABS (offset_y); dest_rect.x = 0; dest_rect.y = dest_y; } gegl_buffer_copy (src_buffer, &src_rect, GEGL_ABYSS_NONE, new_buffer, &dest_rect); } } gimp_drawable_set_buffer (drawable, gimp_item_is_attached (item), C_("undo-type", "Offset Drawable"), new_buffer); g_object_unref (new_buffer); }
void gimp_image_convert_precision (GimpImage *image, GimpPrecision precision, gint layer_dither_type, gint text_layer_dither_type, gint mask_dither_type, GimpProgress *progress) { GList *all_drawables; GList *list; const gchar *undo_desc = NULL; gint nth_drawable, n_drawables; g_return_if_fail (GIMP_IS_IMAGE (image)); g_return_if_fail (precision != gimp_image_get_precision (image)); g_return_if_fail (precision == GIMP_PRECISION_U8_GAMMA || gimp_image_get_base_type (image) != GIMP_INDEXED); g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); all_drawables = g_list_concat (gimp_image_get_layer_list (image), gimp_image_get_channel_list (image)); n_drawables = g_list_length (all_drawables) + 1 /* + selection */; switch (precision) { case GIMP_PRECISION_U8_LINEAR: undo_desc = C_("undo-type", "Convert Image to 8 bit linear integer"); break; case GIMP_PRECISION_U8_GAMMA: undo_desc = C_("undo-type", "Convert Image to 8 bit gamma integer"); break; case GIMP_PRECISION_U16_LINEAR: undo_desc = C_("undo-type", "Convert Image to 16 bit linear integer"); break; case GIMP_PRECISION_U16_GAMMA: undo_desc = C_("undo-type", "Convert Image to 16 bit gamma integer"); break; case GIMP_PRECISION_U32_LINEAR: undo_desc = C_("undo-type", "Convert Image to 32 bit linear integer"); break; case GIMP_PRECISION_U32_GAMMA: undo_desc = C_("undo-type", "Convert Image to 32 bit gamma integer"); break; case GIMP_PRECISION_HALF_LINEAR: undo_desc = C_("undo-type", "Convert Image to 16 bit linear floating point"); break; case GIMP_PRECISION_HALF_GAMMA: undo_desc = C_("undo-type", "Convert Image to 16 bit gamma floating point"); break; case GIMP_PRECISION_FLOAT_LINEAR: undo_desc = C_("undo-type", "Convert Image to 32 bit linear floating point"); break; case GIMP_PRECISION_FLOAT_GAMMA: undo_desc = C_("undo-type", "Convert Image to 32 bit gamma floating point"); break; case GIMP_PRECISION_DOUBLE_LINEAR: undo_desc = C_("undo-type", "Convert Image to 64 bit linear floating point"); break; case GIMP_PRECISION_DOUBLE_GAMMA: undo_desc = C_("undo-type", "Convert Image to 64 bit gamma floating point"); break; } if (progress) gimp_progress_start (progress, undo_desc, FALSE); g_object_freeze_notify (G_OBJECT (image)); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CONVERT, undo_desc); /* Push the image precision to the stack */ gimp_image_undo_push_image_precision (image, NULL); /* Set the new precision */ g_object_set (image, "precision", precision, NULL); for (list = all_drawables, nth_drawable = 0; list; list = g_list_next (list), nth_drawable++) { GimpDrawable *drawable = list->data; gint dither_type; if (gimp_item_is_text_layer (GIMP_ITEM (drawable))) dither_type = text_layer_dither_type; else dither_type = layer_dither_type; gimp_drawable_convert_type (drawable, image, gimp_drawable_get_base_type (drawable), precision, dither_type, mask_dither_type, TRUE); if (progress) gimp_progress_set_value (progress, (gdouble) nth_drawable / (gdouble) n_drawables); } g_list_free (all_drawables); /* convert the selection mask */ { GimpChannel *mask = gimp_image_get_mask (image); GeglBuffer *buffer; gimp_image_undo_push_mask_precision (image, NULL, mask); buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, gimp_image_get_width (image), gimp_image_get_height (image)), gimp_image_get_mask_format (image)); gegl_buffer_copy (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)), NULL, buffer, NULL); gimp_drawable_set_buffer (GIMP_DRAWABLE (mask), FALSE, NULL, buffer); g_object_unref (buffer); nth_drawable++; if (progress) gimp_progress_set_value (progress, (gdouble) nth_drawable / (gdouble) n_drawables); } gimp_image_undo_group_end (image); gimp_image_precision_changed (image); g_object_thaw_notify (G_OBJECT (image)); if (progress) gimp_progress_end (progress); }
GdkPixbuf * gimp_drawable_get_sub_pixbuf (GimpDrawable *drawable, gint src_x, gint src_y, gint src_width, gint src_height, gint dest_width, gint dest_height) { GimpItem *item; GimpImage *image; GeglBuffer *buffer; GdkPixbuf *pixbuf; gdouble scale; gint scaled_x; gint scaled_y; GimpColorTransform *transform; 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_get_width (item), NULL); g_return_val_if_fail ((src_y + src_height) <= gimp_item_get_height (item), NULL); image = gimp_item_get_image (item); if (! image->gimp->config->layer_previews) return NULL; buffer = gimp_drawable_get_buffer (drawable); pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, dest_width, dest_height); scale = MIN ((gdouble) dest_width / (gdouble) src_width, (gdouble) dest_height / (gdouble) src_height); scaled_x = RINT ((gdouble) src_x * scale); scaled_y = RINT ((gdouble) src_y * scale); transform = gimp_image_get_color_transform_to_srgb_u8 (image); if (transform) { GimpTempBuf *temp_buf; GeglBuffer *src_buf; GeglBuffer *dest_buf; temp_buf = gimp_temp_buf_new (dest_width, dest_height, gimp_drawable_get_format (drawable)); gegl_buffer_get (buffer, GEGL_RECTANGLE (scaled_x, scaled_y, dest_width, dest_height), scale, gimp_temp_buf_get_format (temp_buf), gimp_temp_buf_get_data (temp_buf), GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP); src_buf = gimp_temp_buf_create_buffer (temp_buf); dest_buf = gimp_pixbuf_create_buffer (pixbuf); gimp_temp_buf_unref (temp_buf); gimp_color_transform_process_buffer (transform, src_buf, GEGL_RECTANGLE (0, 0, dest_width, dest_height), dest_buf, GEGL_RECTANGLE (0, 0, 0, 0)); g_object_unref (src_buf); g_object_unref (dest_buf); } else { gegl_buffer_get (buffer, GEGL_RECTANGLE (scaled_x, scaled_y, dest_width, dest_height), scale, gimp_pixbuf_get_format (pixbuf), gdk_pixbuf_get_pixels (pixbuf), gdk_pixbuf_get_rowstride (pixbuf), GEGL_ABYSS_CLAMP); } return pixbuf; }
void gimp_drawable_bucket_fill (GimpDrawable *drawable, GimpFillOptions *options, gboolean fill_transparent, GimpSelectCriterion fill_criterion, gdouble threshold, gboolean sample_merged, gboolean diagonal_neighbors, gdouble seed_x, gdouble seed_y) { GimpImage *image; GimpPickable *pickable; GeglBuffer *buffer; GeglBuffer *mask_buffer; gboolean antialias; gint x, y, width, height; gint mask_offset_x = 0; gint mask_offset_y = 0; gint sel_x, sel_y, sel_width, sel_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)); image = gimp_item_get_image (GIMP_ITEM (drawable)); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &sel_x, &sel_y, &sel_width, &sel_height)) return; gimp_set_busy (image->gimp); if (sample_merged) pickable = GIMP_PICKABLE (image); else pickable = GIMP_PICKABLE (drawable); antialias = gimp_fill_options_get_antialias (options); /* Do a seed bucket fill...To do this, calculate a new * contiguous region. */ mask_buffer = gimp_pickable_contiguous_region_by_seed (pickable, antialias, threshold, fill_transparent, fill_criterion, diagonal_neighbors, (gint) seed_x, (gint) seed_y); gimp_gegl_mask_bounds (mask_buffer, &x, &y, &width, &height); width -= x; height -= y; /* If there is a selection, inersect the region bounds * with the selection bounds, to avoid processing areas * that are going to be masked out anyway. The actual * intersection of the fill region with the mask data * happens when combining the fill buffer, in * gimp_drawable_apply_buffer(). */ if (! gimp_channel_is_empty (gimp_image_get_mask (image))) { gint off_x = 0; gint off_y = 0; if (sample_merged) gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); if (! gimp_rectangle_intersect (x, y, width, height, sel_x + off_x, sel_y + off_y, sel_width, sel_height, &x, &y, &width, &height)) { /* The fill region and the selection are disjoint; bail. */ g_object_unref (mask_buffer); gimp_unset_busy (image->gimp); return; } } /* make sure we handle the mask correctly if it was sample-merged */ if (sample_merged) { GimpItem *item = GIMP_ITEM (drawable); gint off_x, off_y; /* Limit the channel bounds to the drawable's extents */ gimp_item_get_offset (item, &off_x, &off_y); gimp_rectangle_intersect (x, y, width, height, off_x, off_y, gimp_item_get_width (item), gimp_item_get_height (item), &x, &y, &width, &height); mask_offset_x = x; mask_offset_y = y; /* translate mask bounds to drawable coords */ x -= off_x; y -= off_y; } else { mask_offset_x = x; mask_offset_y = y; } buffer = gimp_fill_options_create_buffer (options, drawable, GEGL_RECTANGLE (0, 0, width, height)); gimp_gegl_apply_opacity (buffer, NULL, NULL, buffer, mask_buffer, -mask_offset_x, -mask_offset_y, 1.0); g_object_unref (mask_buffer); /* Apply it to the image */ gimp_drawable_apply_buffer (drawable, buffer, GEGL_RECTANGLE (0, 0, width, height), TRUE, C_("undo-type", "Bucket Fill"), gimp_context_get_opacity (GIMP_CONTEXT (options)), gimp_context_get_paint_mode (GIMP_CONTEXT (options)), NULL, x, y); g_object_unref (buffer); gimp_drawable_update (drawable, x, y, width, height); gimp_unset_busy (image->gimp); }
gboolean gimp_edit_fill_full (GimpImage *image, GimpDrawable *drawable, const GimpRGB *color, GimpPattern *pattern, gdouble opacity, GimpLayerModeEffects paint_mode, const gchar *undo_desc) { GeglBuffer *dest_buffer; const Babl *format; gint x, y, width, height; g_return_val_if_fail (GIMP_IS_IMAGE (image), 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 (color != NULL || pattern != NULL, FALSE); if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height)) return TRUE; /* nothing to do, but the fill succeeded */ if (pattern && babl_format_has_alpha (gimp_temp_buf_get_format (pattern->mask)) && ! gimp_drawable_has_alpha (drawable)) { format = gimp_drawable_get_format_with_alpha (drawable); } else { format = gimp_drawable_get_format (drawable); } dest_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height), format); if (pattern) { GeglBuffer *src_buffer = gimp_pattern_create_buffer (pattern); gegl_buffer_set_pattern (dest_buffer, NULL, src_buffer, 0, 0); g_object_unref (src_buffer); } else { GeglColor *gegl_color = gimp_gegl_color_new (color); gegl_buffer_set_color (dest_buffer, NULL, gegl_color); g_object_unref (gegl_color); } gimp_drawable_apply_buffer (drawable, dest_buffer, GEGL_RECTANGLE (0, 0, width, height), TRUE, undo_desc, opacity, paint_mode, NULL, x, y); g_object_unref (dest_buffer); gimp_drawable_update (drawable, x, y, width, height); return TRUE; }