diff --git a/app/text/gimptextlayer.c b/app/text/gimptextlayer.c index 1da14ad2db..1696cba5e4 100644 --- a/app/text/gimptextlayer.c +++ b/app/text/gimptextlayer.c @@ -1114,7 +1114,7 @@ gimp_text_layer_render_layout (GimpTextLayer *layer, #else format = babl_format_with_space ("cairo-ARGB32", gimp_text_layout_get_space (layout)); #endif - buffer = gimp_cairo_surface_create_buffer (surface, format); + buffer = gimp_cairo_surface_get_buffer (surface, format, FALSE); gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, gimp_drawable_get_buffer (drawable), NULL); diff --git a/app/widgets/gimpviewrenderer.c b/app/widgets/gimpviewrenderer.c index f9ebd308e4..0c2744719d 100644 --- a/app/widgets/gimpviewrenderer.c +++ b/app/widgets/gimpviewrenderer.c @@ -1300,13 +1300,14 @@ gimp_view_render_temp_buf_to_surface (GimpViewRenderer *renderer, width, height); src_buffer = gimp_temp_buf_create_buffer (temp_buf); - dest_buffer = gimp_cairo_surface_create_buffer (tmp_surface, NULL); + dest_buffer = gimp_cairo_surface_get_buffer (tmp_surface, NULL, TRUE); transform = gimp_view_renderer_get_color_transform (renderer, widget, gegl_buffer_get_format (src_buffer), gegl_buffer_get_format (dest_buffer)); + gegl_buffer_freeze_changed (dest_buffer); if (transform) { gimp_color_transform_process_buffer (transform, @@ -1327,6 +1328,7 @@ gimp_view_render_temp_buf_to_surface (GimpViewRenderer *renderer, dest_buffer, GEGL_RECTANGLE (0, 0, 0, 0)); } + gegl_buffer_thaw_changed (dest_buffer); g_object_unref (src_buffer); g_object_unref (dest_buffer); diff --git a/libgimp/gimplayer.c b/libgimp/gimplayer.c index 2a839efaee..c1b29cb2b2 100644 --- a/libgimp/gimplayer.c +++ b/libgimp/gimplayer.c @@ -219,7 +219,7 @@ gimp_layer_new_from_surface (GimpImage *image, if (layer == NULL) return NULL; - src_buffer = gimp_cairo_surface_create_buffer (surface, NULL); + src_buffer = gimp_cairo_surface_get_buffer (surface, NULL, FALSE); dest_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE, diff --git a/libgimpcolor/gimpcairo.c b/libgimpcolor/gimpcairo.c index d674f10013..73f10e3c41 100644 --- a/libgimpcolor/gimpcairo.c +++ b/libgimpcolor/gimpcairo.c @@ -31,6 +31,7 @@ #include "gimpcolortypes.h" #include "gimpcairo.h" +#include "gimpcolor-private.h" /** @@ -43,6 +44,11 @@ **/ +static void gimp_cairo_surface_buffer_changed (GeglBuffer *buffer, + const GeglRectangle *rect, + cairo_surface_t *surface); + + /** * gimp_cairo_checkerboard_create: * @cr: Cairo context @@ -171,29 +177,138 @@ gimp_cairo_surface_get_format (cairo_surface_t *surface) * Returns: (transfer full): a #GeglBuffer * * Since: 2.10 + * + * Deprecated: 3.2: Use gimp_cairo_surface_get_buffer(). **/ GeglBuffer * gimp_cairo_surface_create_buffer (cairo_surface_t *surface, const Babl *format) { - GeglBuffer *buffer; - GeglRectangle extent = {0}; + return gimp_cairo_surface_get_buffer (surface, format, TRUE); +} + +/** + * gimp_cairo_surface_get_buffer: + * @surface: a Cairo surface + * @format: a Babl format. + * @sync_back: whether changes on the returned buffer should be synced + * back to @surface. + * + * This function returns a [class@Gegl.Buffer] containing @surface's + * pixels. It must only be called on image surfaces, calling it on other + * surface types is an error. + * + * If @format is set, the returned buffer will use it. It has to map + * with @surface Cairo format. If unset, the buffer format will be + * determined from @surface. The main difference is that automatically + * determined format has sRGB space and TRC by default. + * + * If you want the changes to the returned buffer to be synced back to + * @surface data, set @sync_back to %TRUE. If you don't need this and + * only want a copy of @surface at a given time, %FALSE will be less + * costly. + * + * When @sync_back is %TRUE, [method@Gegl.Buffer.freeze_changed] and + * [method@Gegl.Buffer.thaw_changed] may be useful to block intermediate + * syncing. + * + * Returns: (transfer full): a #GeglBuffer + * + * Since: 3.2 + **/ +GeglBuffer * +gimp_cairo_surface_get_buffer (cairo_surface_t *surface, + const Babl *format, + gboolean sync_back) +{ + const Babl *surface_format; + GeglBuffer *buffer; + GeglRectangle extent = {0}; + gint rowstride; + gint bpp; g_return_val_if_fail (surface != NULL, NULL); - g_return_val_if_fail (cairo_surface_get_type (surface) == - CAIRO_SURFACE_TYPE_IMAGE, NULL); - g_return_val_if_fail (format == NULL || - babl_format_get_bytes_per_pixel (format) == babl_format_get_bytes_per_pixel (gimp_cairo_surface_get_format (surface)), - NULL); + g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL); + + surface_format = gimp_cairo_surface_get_format (surface); + rowstride = cairo_image_surface_get_stride (surface); + bpp = babl_format_get_bytes_per_pixel (surface_format); + + g_return_val_if_fail (format == NULL || babl_format_get_bytes_per_pixel (format) == bpp, NULL); if (format == NULL) - format = gimp_cairo_surface_get_format (surface); + format = surface_format; + extent.width = cairo_image_surface_get_width (surface); extent.height = cairo_image_surface_get_height (surface); - buffer = gegl_buffer_new (&extent, format); + if ( ! sync_back || rowstride % bpp != 0 || + extent.width * extent.height > GIMP_LINEAR_BUFFER_MAX_SIZE) + buffer = gegl_buffer_new (&extent, format); + else + return gegl_buffer_linear_new_from_data (cairo_image_surface_get_data (surface), + format, &extent, rowstride, + (GDestroyNotify) cairo_surface_destroy, + cairo_surface_reference (surface)); - gegl_buffer_set (buffer, &extent, 0, format, cairo_image_surface_get_data (surface), cairo_image_surface_get_stride (surface)); + gegl_buffer_set (buffer, &extent, 0, format, + cairo_image_surface_get_data (surface), + rowstride); + + if (sync_back) + { + /* Making sure we don't work on a destroyed surface. */ + g_object_set_data_full (G_OBJECT (buffer), + "gimp-cairo-surface-get-buffer-surface", + cairo_surface_reference (surface), + (GDestroyNotify) cairo_surface_destroy); + gegl_buffer_signal_connect (buffer, "changed", + G_CALLBACK (gimp_cairo_surface_buffer_changed), + surface); + } return buffer; } + + +/* Private functions */ + +static void +gimp_cairo_surface_buffer_changed (GeglBuffer *buffer, + const GeglRectangle *rect, + cairo_surface_t *surface) +{ + unsigned char *data; + gint stride; + gint bpp; + + data = cairo_image_surface_get_data (surface); + stride = cairo_image_surface_get_stride (surface); + bpp = babl_format_get_bytes_per_pixel (gegl_buffer_get_format (buffer)); + + data += stride * rect->y + rect->x * bpp; + + if ((rect->x == 0 && rect->width == gegl_buffer_get_width (buffer)) || rect->height == 1) + { + gegl_buffer_get (buffer, rect, 1.0, NULL, + data, stride, GEGL_ABYSS_NONE); + } + else + { + GeglRectangle extent; + + extent.x = rect->x; + extent.width = rect->width; + extent.height = 1; + + for (gint y = rect->y; y < rect->y + rect->height; y++) + { + extent.y = y; + gegl_buffer_get (buffer, &extent, 1.0, NULL, + data, stride, GEGL_ABYSS_NONE); + data += stride; + } + } + + cairo_surface_mark_dirty (surface); +} diff --git a/libgimpcolor/gimpcairo.h b/libgimpcolor/gimpcairo.h index 883f41e3a2..821c17f8b1 100644 --- a/libgimpcolor/gimpcairo.h +++ b/libgimpcolor/gimpcairo.h @@ -30,8 +30,12 @@ cairo_pattern_t * gimp_cairo_checkerboard_create (cairo_t *cr, const GeglColor *dark); const Babl * gimp_cairo_surface_get_format (cairo_surface_t *surface); +GIMP_DEPRECATED_FOR(gimp_cairo_surface_get_buffer) GeglBuffer * gimp_cairo_surface_create_buffer (cairo_surface_t *surface, const Babl *format); +GeglBuffer * gimp_cairo_surface_get_buffer (cairo_surface_t *surface, + const Babl *format, + gboolean sync_back); /* some useful macros for writing directly to a Cairo surface */ diff --git a/libgimpcolor/gimpcolor-private.h b/libgimpcolor/gimpcolor-private.h index 9a9716d6ba..2950c80596 100644 --- a/libgimpcolor/gimpcolor-private.h +++ b/libgimpcolor/gimpcolor-private.h @@ -19,6 +19,8 @@ #ifndef __GIMP_COLOR_PRIVATE_H__ #define __GIMP_COLOR_PRIVATE_H__ +#define GIMP_LINEAR_BUFFER_MAX_SIZE 4096 + /* Legacy definition to calculate luminance from sRGB. * * These values and macro are specific to a "RGB float" to "Y float" diff --git a/libgimpcolor/gimpcolor.def b/libgimpcolor/gimpcolor.def index 1ca7196ea9..7b680ca3bd 100644 --- a/libgimpcolor/gimpcolor.def +++ b/libgimpcolor/gimpcolor.def @@ -8,6 +8,7 @@ EXPORTS gimp_bilinear_rgb gimp_cairo_checkerboard_create gimp_cairo_surface_create_buffer + gimp_cairo_surface_get_buffer gimp_cairo_surface_get_format gimp_color_is_out_of_gamut gimp_color_is_out_of_self_gamut diff --git a/plug-ins/common/file-pdf-export.c b/plug-ins/common/file-pdf-export.c index 081c530d2a..663a787832 100644 --- a/plug-ins/common/file-pdf-export.c +++ b/plug-ins/common/file-pdf-export.c @@ -1430,7 +1430,7 @@ get_cairo_surface (GimpDrawable *drawable, return NULL; } - dest_buffer = gimp_cairo_surface_create_buffer (surface, NULL); + dest_buffer = gimp_cairo_surface_get_buffer (surface, NULL, TRUE); if (as_mask) { /* src_buffer represents a mask in "Y u8", "Y u16", etc. formats. @@ -1442,7 +1442,9 @@ get_cairo_surface (GimpDrawable *drawable, gegl_buffer_set_format (dest_buffer, babl_format ("Y u8")); } + gegl_buffer_freeze_changed (dest_buffer); gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE, dest_buffer, NULL); + gegl_buffer_thaw_changed (dest_buffer); cairo_surface_mark_dirty (surface);