2006-12-10 05:33:38 +08:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
1997-11-25 06:05:25 +08:00
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
2009-01-18 06:28:01 +08:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
1997-11-25 06:05:25 +08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
2009-01-18 06:28:01 +08:00
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
1997-11-25 06:05:25 +08:00
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2018-07-12 05:27:07 +08:00
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
2000-12-17 05:37:03 +08:00
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2008-10-10 04:24:04 +08:00
|
|
|
#include <gegl.h>
|
2000-12-17 05:37:03 +08:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
2014-04-21 00:06:52 +08:00
|
|
|
#include "libgimpmath/gimpmath.h"
|
2011-04-28 21:50:39 +08:00
|
|
|
#include "libgimpcolor/gimpcolor.h"
|
2010-09-29 06:10:37 +08:00
|
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
|
2001-09-26 07:23:09 +08:00
|
|
|
#include "display-types.h"
|
2000-12-17 05:37:03 +08:00
|
|
|
|
2007-10-08 21:30:49 +08:00
|
|
|
#include "config/gimpdisplayconfig.h"
|
|
|
|
|
2001-05-09 10:32:03 +08:00
|
|
|
#include "core/gimpimage.h"
|
2008-11-04 20:07:17 +08:00
|
|
|
#include "core/gimppickable.h"
|
2012-06-21 03:44:09 +08:00
|
|
|
#include "core/gimpprojectable.h"
|
2001-05-09 10:32:03 +08:00
|
|
|
|
2001-09-26 07:23:09 +08:00
|
|
|
#include "gimpdisplay.h"
|
2001-11-01 05:20:09 +08:00
|
|
|
#include "gimpdisplayshell.h"
|
2012-06-21 03:44:09 +08:00
|
|
|
#include "gimpdisplayshell-transform.h"
|
2002-03-15 06:42:50 +08:00
|
|
|
#include "gimpdisplayshell-filter.h"
|
2015-06-02 05:30:03 +08:00
|
|
|
#include "gimpdisplayshell-profile.h"
|
2001-11-02 17:31:21 +08:00
|
|
|
#include "gimpdisplayshell-render.h"
|
2001-09-26 07:23:09 +08:00
|
|
|
|
2013-02-02 21:10:23 +08:00
|
|
|
|
2019-09-12 02:00:32 +08:00
|
|
|
#define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1
|
|
|
|
#define GIMP_DISPLAY_RENDER_MAX_SCALE 4
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_display_shell_render_set_scale (GimpDisplayShell *shell,
|
|
|
|
gint scale)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
|
|
|
|
|
|
|
|
#if GIMP_DISPLAY_RENDER_ENABLE_SCALING
|
|
|
|
scale = CLAMP (scale, 1, GIMP_DISPLAY_RENDER_MAX_SCALE);
|
|
|
|
|
|
|
|
if (scale != shell->render_scale)
|
|
|
|
{
|
|
|
|
shell->render_scale = scale;
|
|
|
|
|
|
|
|
gimp_display_shell_render_invalidate_full (shell);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-16 05:24:35 +08:00
|
|
|
void
|
|
|
|
gimp_display_shell_render_invalidate_full (GimpDisplayShell *shell)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
|
|
|
|
|
|
|
|
g_clear_pointer (&shell->render_cache_valid, cairo_region_destroy);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_display_shell_render_invalidate_area (GimpDisplayShell *shell,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
|
|
|
|
|
|
|
|
if (shell->render_cache_valid)
|
|
|
|
{
|
|
|
|
cairo_rectangle_int_t rect;
|
|
|
|
|
|
|
|
rect.x = x;
|
|
|
|
rect.y = y;
|
|
|
|
rect.width = width;
|
|
|
|
rect.height = height;
|
|
|
|
|
|
|
|
cairo_region_subtract_rectangle (shell->render_cache_valid, &rect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gimp_display_shell_render_validate_area (GimpDisplayShell *shell,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
|
|
|
|
|
|
|
|
if (shell->render_cache_valid)
|
|
|
|
{
|
|
|
|
cairo_rectangle_int_t rect;
|
|
|
|
|
|
|
|
rect.x = x;
|
|
|
|
rect.y = y;
|
|
|
|
rect.width = width;
|
|
|
|
rect.height = height;
|
|
|
|
|
|
|
|
cairo_region_union_rectangle (shell->render_cache_valid, &rect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
gimp_display_shell_render_is_valid (GimpDisplayShell *shell,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
if (shell->render_cache_valid)
|
|
|
|
{
|
|
|
|
cairo_rectangle_int_t rect;
|
|
|
|
cairo_region_overlap_t overlap;
|
|
|
|
|
|
|
|
rect.x = x;
|
|
|
|
rect.y = y;
|
|
|
|
rect.width = width;
|
|
|
|
rect.height = height;
|
|
|
|
|
|
|
|
overlap = cairo_region_contains_rectangle (shell->render_cache_valid,
|
|
|
|
&rect);
|
|
|
|
|
|
|
|
return (overlap == CAIRO_REGION_OVERLAP_IN);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
void
|
2010-09-29 04:26:05 +08:00
|
|
|
gimp_display_shell_render (GimpDisplayShell *shell,
|
|
|
|
cairo_t *cr,
|
2019-07-16 05:24:35 +08:00
|
|
|
gint tx,
|
|
|
|
gint ty,
|
|
|
|
gint twidth,
|
|
|
|
gint theight,
|
2017-12-09 17:25:35 +08:00
|
|
|
gdouble scale)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2019-07-17 00:55:40 +08:00
|
|
|
GimpDisplayConfig *display_config;
|
|
|
|
GimpImage *image;
|
|
|
|
GeglBuffer *buffer;
|
2014-07-04 02:52:02 +08:00
|
|
|
#ifdef USE_NODE_BLIT
|
2019-07-17 00:55:40 +08:00
|
|
|
GeglNode *node;
|
2014-07-04 02:52:02 +08:00
|
|
|
#endif
|
2019-07-17 00:55:40 +08:00
|
|
|
|
|
|
|
cairo_t *my_cr;
|
|
|
|
gint cairo_stride;
|
|
|
|
guchar *cairo_data;
|
|
|
|
gdouble x1, y1;
|
|
|
|
gdouble x2, y2;
|
|
|
|
gint x;
|
|
|
|
gint y;
|
|
|
|
gint width;
|
|
|
|
gint height;
|
2019-09-04 20:50:29 +08:00
|
|
|
GeglAbyssPolicy abyss_policy;
|
2019-07-17 00:55:40 +08:00
|
|
|
gint filter = GEGL_BUFFER_FILTER_AUTO;
|
2001-12-01 02:23:49 +08:00
|
|
|
|
2007-10-08 21:30:49 +08:00
|
|
|
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
|
2010-08-28 01:32:16 +08:00
|
|
|
g_return_if_fail (cr != NULL);
|
2017-12-09 17:25:35 +08:00
|
|
|
g_return_if_fail (scale > 0.0);
|
2005-04-28 07:27:10 +08:00
|
|
|
|
2019-07-16 05:24:35 +08:00
|
|
|
/* map chunk from screen space to scaled image space */
|
|
|
|
gimp_display_shell_untransform_bounds_with_scale (shell, scale,
|
|
|
|
tx, ty,
|
|
|
|
tx + twidth, ty + theight,
|
|
|
|
&x1, &y1,
|
|
|
|
&x2, &y2);
|
|
|
|
|
|
|
|
x = floor (x1);
|
|
|
|
y = floor (y1);
|
|
|
|
width = ceil (x2) - x;
|
|
|
|
height = ceil (y2) - y;
|
|
|
|
|
|
|
|
g_return_if_fail (width > 0 && width <= shell->render_buf_width);
|
|
|
|
g_return_if_fail (height > 0 && height <= shell->render_buf_height);
|
|
|
|
|
2019-09-12 02:00:32 +08:00
|
|
|
tx *= shell->render_scale;
|
|
|
|
ty *= shell->render_scale;
|
|
|
|
twidth *= shell->render_scale;
|
|
|
|
theight *= shell->render_scale;
|
|
|
|
|
2019-07-17 00:55:40 +08:00
|
|
|
display_config = shell->display->config;
|
|
|
|
|
2019-09-04 20:50:29 +08:00
|
|
|
if (shell->show_all)
|
|
|
|
abyss_policy = GEGL_ABYSS_NONE;
|
|
|
|
else
|
|
|
|
abyss_policy = GEGL_ABYSS_CLAMP;
|
|
|
|
|
2019-07-17 00:55:40 +08:00
|
|
|
if (display_config->zoom_quality != GIMP_ZOOM_QUALITY_HIGH)
|
|
|
|
{
|
|
|
|
filter = GEGL_BUFFER_FILTER_NEAREST;
|
|
|
|
}
|
|
|
|
|
2014-06-30 05:11:53 +08:00
|
|
|
image = gimp_display_get_image (shell->display);
|
app: fix a crash when converting to higher precision.
gimp_display_shell_render() writes to a GeglBuffer backed by allocated memory
(shell->profile_data). Unfortunately while converting prevision in
gimp_image_convert_precision(), we change the "precision" property (hence the
source format) first, hence end up trying to write data in a too small buffer.
This crash was hard to find as it was not showing up on my machine (though it
did produce rendering artifacts!), unless I built both GIMP and babl with
`b_sanitize=address`.
Note that an alternate fix was to make sure that the profile_data buffer is big
enough (by calling gimp_display_shell_profile_update() before rendering), but
anyway the image is in an inconsistent state while conversion is in progress:
whereas the `src_format` is the new one, the `src_profile` is still the old one
(and cannot be changed before we finish converting).
Moreover the render happen regularly on progress signals, once after each
converted drawable. So each of these rendering step happens in an inconsistent
state, with the wrong profile set, some of the drawables converted and others
not yet.
We could still render properly if each drawable's buffer used space-aware format
(thus allowing different drawables to use different profiles/spaces), but it
feels over-engineering the problem. It might be much better to ignore rendering
steps while converting the image precision. Moreover it would obviously make a
faster conversion.
See discussions in #9136 for this crash, which didn't have dedicated report
AFAIK.
(cherry picked from commit de25be92104e6883cb35ea7bf0ea12408c8ca722)
Note: on the `master` branch, even with sanitized code, I don't get the crash.
Yet this change seems relevant enough that I'm adding it.
2023-02-19 20:54:03 +08:00
|
|
|
|
|
|
|
/* While converting, the render can be wrong; but worse, we rely on allocated
|
|
|
|
* data which might be the wrong size and this was a crash we had which was
|
|
|
|
* hard to diagnose as it doesn't always crash immediately (see discussions in
|
|
|
|
* #9136). This is why this assert is important. We want to make sure we never
|
|
|
|
* call this when the shell's image is in the inconsistent "converting" state.
|
|
|
|
*/
|
|
|
|
g_return_if_fail (! gimp_image_get_converting (image));
|
|
|
|
|
2019-09-04 20:50:29 +08:00
|
|
|
buffer = gimp_pickable_get_buffer (
|
|
|
|
gimp_display_shell_get_pickable (shell));
|
2014-07-04 02:52:02 +08:00
|
|
|
#ifdef USE_NODE_BLIT
|
|
|
|
node = gimp_projectable_get_graph (GIMP_PROJECTABLE (image));
|
2017-04-23 02:22:06 +08:00
|
|
|
|
|
|
|
gimp_projectable_begin_render (GIMP_PROJECTABLE (image));
|
2014-07-04 02:52:02 +08:00
|
|
|
#endif
|
2012-07-06 03:42:26 +08:00
|
|
|
|
2019-07-16 05:24:35 +08:00
|
|
|
if (! shell->render_surface)
|
|
|
|
{
|
|
|
|
shell->render_surface =
|
|
|
|
cairo_surface_create_similar_image (cairo_get_target (cr),
|
|
|
|
CAIRO_FORMAT_ARGB32,
|
|
|
|
shell->render_buf_width,
|
|
|
|
shell->render_buf_height);
|
|
|
|
}
|
2013-01-30 17:50:54 +08:00
|
|
|
|
2019-07-16 05:24:35 +08:00
|
|
|
cairo_surface_flush (shell->render_surface);
|
|
|
|
|
|
|
|
if (! shell->render_cache)
|
|
|
|
{
|
2019-09-12 02:00:32 +08:00
|
|
|
shell->render_cache = cairo_surface_create_similar_image (
|
|
|
|
cairo_get_target (cr),
|
|
|
|
CAIRO_FORMAT_ARGB32,
|
|
|
|
shell->disp_width * shell->render_scale,
|
|
|
|
shell->disp_height * shell->render_scale);
|
2019-07-16 05:24:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (! shell->render_cache_valid)
|
|
|
|
{
|
|
|
|
shell->render_cache_valid = cairo_region_create ();
|
|
|
|
}
|
|
|
|
|
|
|
|
my_cr = cairo_create (shell->render_cache);
|
|
|
|
|
|
|
|
/* clip to chunk bounds, in screen space */
|
|
|
|
cairo_rectangle (my_cr, tx, ty, twidth, theight);
|
|
|
|
cairo_clip (my_cr);
|
|
|
|
|
|
|
|
/* transform to scaled image space, and apply uneven scaling */
|
2019-09-12 02:00:32 +08:00
|
|
|
cairo_scale (my_cr, shell->render_scale, shell->render_scale);
|
2019-07-16 05:24:35 +08:00
|
|
|
if (shell->rotate_transform)
|
|
|
|
cairo_transform (my_cr, shell->rotate_transform);
|
|
|
|
cairo_translate (my_cr, -shell->offset_x, -shell->offset_y);
|
|
|
|
cairo_scale (my_cr, shell->scale_x / scale, shell->scale_y / scale);
|
|
|
|
|
|
|
|
cairo_stride = cairo_image_surface_get_stride (shell->render_surface);
|
|
|
|
cairo_data = cairo_image_surface_get_data (shell->render_surface);
|
2012-06-21 03:44:09 +08:00
|
|
|
|
2015-06-02 05:30:03 +08:00
|
|
|
if (shell->profile_transform ||
|
|
|
|
gimp_display_shell_has_filter (shell))
|
|
|
|
{
|
2015-06-13 06:27:21 +08:00
|
|
|
gboolean can_convert_to_u8;
|
|
|
|
|
2015-06-02 05:30:03 +08:00
|
|
|
/* if there is a profile transform or a display filter, we need
|
|
|
|
* to use temp buffers
|
|
|
|
*/
|
|
|
|
|
2015-06-13 06:27:21 +08:00
|
|
|
can_convert_to_u8 = gimp_display_shell_profile_can_convert_to_u8 (shell);
|
|
|
|
|
2017-11-03 15:59:06 +08:00
|
|
|
/* create the filter buffer if we have filters, or can't convert
|
|
|
|
* to u8 directly
|
2015-06-02 05:30:03 +08:00
|
|
|
*/
|
2015-06-13 06:27:21 +08:00
|
|
|
if ((gimp_display_shell_has_filter (shell) || ! can_convert_to_u8) &&
|
2015-06-02 05:30:03 +08:00
|
|
|
! shell->filter_buffer)
|
2013-11-03 03:56:25 +08:00
|
|
|
{
|
2019-07-16 05:24:35 +08:00
|
|
|
gint fw = shell->render_buf_width;
|
|
|
|
gint fh = shell->render_buf_height;
|
2013-11-03 03:56:25 +08:00
|
|
|
|
|
|
|
shell->filter_data =
|
2017-12-09 17:25:35 +08:00
|
|
|
gegl_malloc (fw * fh *
|
|
|
|
babl_format_get_bytes_per_pixel (shell->filter_format));
|
2013-11-03 03:56:25 +08:00
|
|
|
|
2015-06-02 05:30:03 +08:00
|
|
|
shell->filter_stride =
|
2017-12-09 17:25:35 +08:00
|
|
|
fw * babl_format_get_bytes_per_pixel (shell->filter_format);
|
2013-11-03 03:56:25 +08:00
|
|
|
|
|
|
|
shell->filter_buffer =
|
|
|
|
gegl_buffer_linear_new_from_data (shell->filter_data,
|
2015-06-02 06:01:28 +08:00
|
|
|
shell->filter_format,
|
2017-12-09 17:25:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0, fw, fh),
|
2013-11-03 03:56:25 +08:00
|
|
|
GEGL_AUTO_ROWSTRIDE,
|
|
|
|
(GDestroyNotify) gegl_free,
|
|
|
|
shell->filter_data);
|
|
|
|
}
|
2013-11-02 07:48:02 +08:00
|
|
|
|
2017-11-03 15:59:06 +08:00
|
|
|
if (! gimp_display_shell_has_filter (shell) || shell->filter_transform)
|
2015-06-02 05:30:03 +08:00
|
|
|
{
|
2017-11-03 15:59:06 +08:00
|
|
|
/* if there are no filters, or there is a filter transform,
|
|
|
|
* load the projection pixels into the profile_buffer
|
2015-06-02 05:30:03 +08:00
|
|
|
*/
|
2014-07-04 02:52:02 +08:00
|
|
|
#ifndef USE_NODE_BLIT
|
2015-06-02 05:30:03 +08:00
|
|
|
gegl_buffer_get (buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (x, y, width, height), scale,
|
2016-05-26 03:35:54 +08:00
|
|
|
gimp_projectable_get_format (GIMP_PROJECTABLE (image)),
|
2015-06-02 05:30:03 +08:00
|
|
|
shell->profile_data, shell->profile_stride,
|
2019-09-04 20:50:29 +08:00
|
|
|
abyss_policy | filter);
|
2014-07-04 02:52:02 +08:00
|
|
|
#else
|
2015-06-02 05:30:03 +08:00
|
|
|
gegl_node_blit (node,
|
2019-07-16 05:24:35 +08:00
|
|
|
scale, GEGL_RECTANGLE (x, y, width, height),
|
2016-05-26 03:35:54 +08:00
|
|
|
gimp_projectable_get_format (GIMP_PROJECTABLE (image)),
|
2015-06-02 05:30:03 +08:00
|
|
|
shell->profile_data, shell->profile_stride,
|
2019-07-17 00:55:40 +08:00
|
|
|
GEGL_BLIT_CACHE | filter);
|
2014-07-04 02:52:02 +08:00
|
|
|
#endif
|
2015-06-02 05:30:03 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-03 15:59:06 +08:00
|
|
|
/* otherwise, load the pixels directly into the filter_buffer
|
2015-06-02 05:30:03 +08:00
|
|
|
*/
|
|
|
|
#ifndef USE_NODE_BLIT
|
|
|
|
gegl_buffer_get (buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (x, y, width, height), scale,
|
2015-06-02 06:01:28 +08:00
|
|
|
shell->filter_format,
|
2015-06-02 05:30:03 +08:00
|
|
|
shell->filter_data, shell->filter_stride,
|
2019-09-04 20:50:29 +08:00
|
|
|
abyss_policy | filter);
|
2015-06-02 05:30:03 +08:00
|
|
|
#else
|
|
|
|
gegl_node_blit (node,
|
2019-07-16 05:24:35 +08:00
|
|
|
scale, GEGL_RECTANGLE (x, y, width, height),
|
2015-06-02 06:01:28 +08:00
|
|
|
shell->filter_format,
|
2015-06-02 05:30:03 +08:00
|
|
|
shell->filter_data, shell->filter_stride,
|
2019-07-17 00:55:40 +08:00
|
|
|
GEGL_BLIT_CACHE | filter);
|
2015-06-02 05:30:03 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-11-03 15:59:06 +08:00
|
|
|
/* if there is a filter transform, convert the pixels from
|
|
|
|
* the profile_buffer to the filter_buffer
|
|
|
|
*/
|
|
|
|
if (shell->filter_transform)
|
|
|
|
{
|
|
|
|
gimp_color_transform_process_buffer (shell->filter_transform,
|
|
|
|
shell->profile_buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0,
|
|
|
|
width, height),
|
2017-11-03 15:59:06 +08:00
|
|
|
shell->filter_buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0,
|
|
|
|
width, height));
|
2017-11-03 15:59:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* if there are filters, apply them
|
|
|
|
*/
|
2015-06-02 05:30:03 +08:00
|
|
|
if (gimp_display_shell_has_filter (shell))
|
|
|
|
{
|
2017-11-03 01:33:52 +08:00
|
|
|
GeglBuffer *filter_buffer;
|
|
|
|
|
2017-11-03 15:59:06 +08:00
|
|
|
/* shift the filter_buffer so that the area passed to
|
|
|
|
* the filters is the real render area, allowing for
|
|
|
|
* position-dependent filters
|
|
|
|
*/
|
2017-11-03 01:33:52 +08:00
|
|
|
filter_buffer = g_object_new (GEGL_TYPE_BUFFER,
|
|
|
|
"source", shell->filter_buffer,
|
2017-12-09 17:25:35 +08:00
|
|
|
"shift-x", -x,
|
|
|
|
"shift-y", -y,
|
2017-11-03 01:33:52 +08:00
|
|
|
NULL);
|
|
|
|
|
2015-06-02 05:30:03 +08:00
|
|
|
/* convert the filter_buffer in place
|
|
|
|
*/
|
|
|
|
gimp_color_display_stack_convert_buffer (shell->filter_stack,
|
2017-11-03 01:33:52 +08:00
|
|
|
filter_buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (x, y,
|
|
|
|
width, height));
|
2017-11-03 01:33:52 +08:00
|
|
|
|
|
|
|
g_object_unref (filter_buffer);
|
2015-06-13 06:27:21 +08:00
|
|
|
}
|
2015-06-02 05:30:03 +08:00
|
|
|
|
2017-11-03 15:59:06 +08:00
|
|
|
/* if there is a profile transform...
|
|
|
|
*/
|
|
|
|
if (shell->profile_transform)
|
|
|
|
{
|
|
|
|
if (gimp_display_shell_has_filter (shell))
|
|
|
|
{
|
|
|
|
/* if we have filters, convert the pixels in the filter_buffer
|
|
|
|
* in-place
|
|
|
|
*/
|
|
|
|
gimp_color_transform_process_buffer (shell->profile_transform,
|
|
|
|
shell->filter_buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0,
|
|
|
|
width, height),
|
2017-11-03 15:59:06 +08:00
|
|
|
shell->filter_buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0,
|
|
|
|
width, height));
|
2017-11-03 15:59:06 +08:00
|
|
|
}
|
|
|
|
else if (! can_convert_to_u8)
|
|
|
|
{
|
|
|
|
/* otherwise, if we can't convert to u8 directly, convert
|
|
|
|
* the pixels from the profile_buffer to the filter_buffer
|
|
|
|
*/
|
|
|
|
gimp_color_transform_process_buffer (shell->profile_transform,
|
|
|
|
shell->profile_buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0,
|
|
|
|
width, height),
|
2017-11-03 15:59:06 +08:00
|
|
|
shell->filter_buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0,
|
|
|
|
width, height));
|
2017-11-03 15:59:06 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-03-28 06:44:33 +08:00
|
|
|
GeglBuffer *buffer =
|
|
|
|
gegl_buffer_linear_new_from_data (cairo_data,
|
|
|
|
babl_format ("cairo-ARGB32"),
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0,
|
|
|
|
width, height),
|
2018-03-28 06:44:33 +08:00
|
|
|
cairo_stride,
|
|
|
|
NULL, NULL);
|
2018-03-28 06:35:08 +08:00
|
|
|
|
2017-11-03 15:59:06 +08:00
|
|
|
/* otherwise, convert the profile_buffer directly into
|
|
|
|
* the cairo_buffer
|
|
|
|
*/
|
|
|
|
gimp_color_transform_process_buffer (shell->profile_transform,
|
|
|
|
shell->profile_buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0,
|
|
|
|
width, height),
|
2018-03-28 06:35:08 +08:00
|
|
|
buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0,
|
|
|
|
width, height));
|
2018-03-28 06:35:08 +08:00
|
|
|
g_object_unref (buffer);
|
2017-11-03 15:59:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* finally, copy the filter buffer to the cairo-ARGB32 buffer,
|
|
|
|
* if necessary
|
|
|
|
*/
|
2015-06-13 06:27:21 +08:00
|
|
|
if (gimp_display_shell_has_filter (shell) || ! can_convert_to_u8)
|
|
|
|
{
|
2015-06-02 05:30:03 +08:00
|
|
|
gegl_buffer_get (shell->filter_buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (0, 0, width, height), 1.0,
|
2015-06-02 05:30:03 +08:00
|
|
|
babl_format ("cairo-ARGB32"),
|
|
|
|
cairo_data, cairo_stride,
|
2019-09-04 20:50:29 +08:00
|
|
|
GEGL_ABYSS_NONE);
|
2015-06-02 05:30:03 +08:00
|
|
|
}
|
2013-11-02 07:48:02 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-06-02 05:30:03 +08:00
|
|
|
/* otherwise we can copy the projection pixels straight to the
|
|
|
|
* cairo-ARGB32 buffer
|
|
|
|
*/
|
2014-07-04 02:52:02 +08:00
|
|
|
#ifndef USE_NODE_BLIT
|
2013-11-02 07:48:02 +08:00
|
|
|
gegl_buffer_get (buffer,
|
2019-07-16 05:24:35 +08:00
|
|
|
GEGL_RECTANGLE (x, y, width, height), scale,
|
2013-11-02 07:48:02 +08:00
|
|
|
babl_format ("cairo-ARGB32"),
|
2015-06-02 05:30:03 +08:00
|
|
|
cairo_data, cairo_stride,
|
2019-09-04 20:50:29 +08:00
|
|
|
abyss_policy | filter);
|
2014-07-04 02:52:02 +08:00
|
|
|
#else
|
|
|
|
gegl_node_blit (node,
|
2019-07-16 05:24:35 +08:00
|
|
|
scale, GEGL_RECTANGLE (x, y, width, height),
|
2014-07-04 02:52:02 +08:00
|
|
|
babl_format ("cairo-ARGB32"),
|
2015-06-02 05:30:03 +08:00
|
|
|
cairo_data, cairo_stride,
|
2019-07-17 00:55:40 +08:00
|
|
|
GEGL_BLIT_CACHE | filter);
|
2014-07-04 02:52:02 +08:00
|
|
|
#endif
|
2012-06-09 21:36:34 +08:00
|
|
|
}
|
|
|
|
|
2017-04-23 02:22:06 +08:00
|
|
|
#ifdef USE_NODE_BLIT
|
|
|
|
gimp_projectable_end_render (GIMP_PROJECTABLE (image));
|
|
|
|
#endif
|
|
|
|
|
2019-07-16 05:24:35 +08:00
|
|
|
cairo_surface_mark_dirty (shell->render_surface);
|
|
|
|
|
2019-07-17 05:16:27 +08:00
|
|
|
/* SOURCE so the destination's alpha is replaced */
|
|
|
|
cairo_set_operator (my_cr, CAIRO_OPERATOR_SOURCE);
|
|
|
|
|
2019-07-16 05:24:35 +08:00
|
|
|
cairo_set_source_surface (my_cr, shell->render_surface, x, y);
|
|
|
|
cairo_paint (my_cr);
|
|
|
|
|
2019-07-17 05:16:27 +08:00
|
|
|
cairo_set_operator (my_cr, CAIRO_OPERATOR_OVER);
|
|
|
|
|
2010-09-29 04:04:07 +08:00
|
|
|
if (shell->mask)
|
2005-07-31 02:19:54 +08:00
|
|
|
{
|
2010-09-29 05:49:22 +08:00
|
|
|
if (! shell->mask_surface)
|
|
|
|
{
|
|
|
|
shell->mask_surface =
|
2013-04-30 05:15:37 +08:00
|
|
|
cairo_image_surface_create (CAIRO_FORMAT_A8,
|
2019-07-16 05:24:35 +08:00
|
|
|
shell->render_buf_width,
|
|
|
|
shell->render_buf_height);
|
2010-09-29 05:49:22 +08:00
|
|
|
}
|
|
|
|
|
2019-07-16 05:24:35 +08:00
|
|
|
cairo_surface_flush (shell->mask_surface);
|
2013-04-30 05:15:37 +08:00
|
|
|
|
2015-06-02 05:30:03 +08:00
|
|
|
cairo_stride = cairo_image_surface_get_stride (shell->mask_surface);
|
2019-07-16 05:24:35 +08:00
|
|
|
cairo_data = cairo_image_surface_get_data (shell->mask_surface);
|
2005-07-31 06:29:02 +08:00
|
|
|
|
2013-06-23 04:26:46 +08:00
|
|
|
gegl_buffer_get (shell->mask,
|
2018-01-01 00:34:58 +08:00
|
|
|
GEGL_RECTANGLE (x - floor (shell->mask_offset_x * scale),
|
|
|
|
y - floor (shell->mask_offset_y * scale),
|
2019-07-16 05:24:35 +08:00
|
|
|
width, height),
|
2017-12-09 17:25:35 +08:00
|
|
|
scale,
|
2013-04-26 16:33:10 +08:00
|
|
|
babl_format ("Y u8"),
|
2015-06-02 05:30:03 +08:00
|
|
|
cairo_data, cairo_stride,
|
2019-07-17 00:55:40 +08:00
|
|
|
GEGL_ABYSS_NONE | filter);
|
2013-07-15 07:57:00 +08:00
|
|
|
|
2014-06-12 03:33:57 +08:00
|
|
|
if (shell->mask_inverted)
|
2013-07-15 07:57:00 +08:00
|
|
|
{
|
2019-07-16 05:24:35 +08:00
|
|
|
gint mask_height = height;
|
2013-07-15 07:57:00 +08:00
|
|
|
|
2014-06-12 03:33:57 +08:00
|
|
|
while (mask_height--)
|
2013-07-15 07:57:00 +08:00
|
|
|
{
|
2019-07-16 05:24:35 +08:00
|
|
|
gint mask_width = width;
|
2015-06-02 05:30:03 +08:00
|
|
|
guchar *d = cairo_data;
|
2013-07-15 07:57:00 +08:00
|
|
|
|
2014-06-12 03:33:57 +08:00
|
|
|
while (mask_width--)
|
|
|
|
{
|
|
|
|
guchar inv = 255 - *d;
|
2013-07-15 07:57:00 +08:00
|
|
|
|
2014-06-12 03:33:57 +08:00
|
|
|
*d++ = inv;
|
|
|
|
}
|
|
|
|
|
2015-06-02 05:30:03 +08:00
|
|
|
cairo_data += cairo_stride;
|
2014-06-12 03:33:57 +08:00
|
|
|
}
|
2013-07-15 07:57:00 +08:00
|
|
|
}
|
2010-08-28 01:32:16 +08:00
|
|
|
|
2019-07-16 05:24:35 +08:00
|
|
|
cairo_surface_mark_dirty (shell->mask_surface);
|
2010-09-29 05:49:22 +08:00
|
|
|
|
2023-12-21 00:39:33 +08:00
|
|
|
gimp_cairo_set_source_color (my_cr, shell->mask_color,
|
|
|
|
GIMP_CORE_CONFIG (display_config)->color_management,
|
|
|
|
FALSE, GTK_WIDGET (shell));
|
2019-07-16 05:24:35 +08:00
|
|
|
cairo_mask_surface (my_cr, shell->mask_surface, x, y);
|
2012-07-06 03:42:26 +08:00
|
|
|
}
|
2019-07-16 05:24:35 +08:00
|
|
|
|
|
|
|
cairo_destroy (my_cr);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|