1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <gegl.h>
21 #include <gtk/gtk.h>
22
23 #include "libgimpmath/gimpmath.h"
24 #include "libgimpcolor/gimpcolor.h"
25 #include "libgimpwidgets/gimpwidgets.h"
26
27 #include "display-types.h"
28
29 #include "config/gimpdisplayconfig.h"
30
31 #include "gegl/gimp-gegl-utils.h"
32
33 #include "core/gimpdrawable.h"
34 #include "core/gimpimage.h"
35 #include "core/gimppickable.h"
36 #include "core/gimpprojectable.h"
37
38 #include "gimpdisplay.h"
39 #include "gimpdisplayshell.h"
40 #include "gimpdisplayshell-transform.h"
41 #include "gimpdisplayshell-filter.h"
42 #include "gimpdisplayshell-profile.h"
43 #include "gimpdisplayshell-render.h"
44 #include "gimpdisplayshell-scroll.h"
45 #include "gimpdisplayxfer.h"
46
47
48 void
gimp_display_shell_render(GimpDisplayShell * shell,cairo_t * cr,gint x,gint y,gint w,gint h,gdouble scale)49 gimp_display_shell_render (GimpDisplayShell *shell,
50 cairo_t *cr,
51 gint x,
52 gint y,
53 gint w,
54 gint h,
55 gdouble scale)
56 {
57 GimpImage *image;
58 GeglBuffer *buffer;
59 #ifdef USE_NODE_BLIT
60 GeglNode *node;
61 #endif
62 GeglAbyssPolicy abyss_policy;
63 cairo_surface_t *xfer;
64 gint xfer_src_x;
65 gint xfer_src_y;
66 gint mask_src_x = 0;
67 gint mask_src_y = 0;
68 gint cairo_stride;
69 guchar *cairo_data;
70
71 g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
72 g_return_if_fail (cr != NULL);
73 g_return_if_fail (w > 0 && w <= GIMP_DISPLAY_RENDER_BUF_WIDTH);
74 g_return_if_fail (h > 0 && h <= GIMP_DISPLAY_RENDER_BUF_HEIGHT);
75 g_return_if_fail (scale > 0.0);
76
77 image = gimp_display_get_image (shell->display);
78 buffer = gimp_pickable_get_buffer (
79 gimp_display_shell_get_pickable (shell));
80 #ifdef USE_NODE_BLIT
81 node = gimp_projectable_get_graph (GIMP_PROJECTABLE (image));
82
83 gimp_projectable_begin_render (GIMP_PROJECTABLE (image));
84 #endif
85
86 if (shell->show_all)
87 abyss_policy = GEGL_ABYSS_NONE;
88 else
89 abyss_policy = GEGL_ABYSS_CLAMP;
90
91 xfer = gimp_display_xfer_get_surface (shell->xfer, w, h,
92 &xfer_src_x, &xfer_src_y);
93
94 cairo_stride = cairo_image_surface_get_stride (xfer);
95 cairo_data = cairo_image_surface_get_data (xfer) +
96 xfer_src_y * cairo_stride + xfer_src_x * 4;
97
98 if (shell->profile_transform ||
99 gimp_display_shell_has_filter (shell))
100 {
101 gboolean can_convert_to_u8;
102
103 /* if there is a profile transform or a display filter, we need
104 * to use temp buffers
105 */
106
107 can_convert_to_u8 = gimp_display_shell_profile_can_convert_to_u8 (shell);
108
109 /* create the filter buffer if we have filters, or can't convert
110 * to u8 directly
111 */
112 if ((gimp_display_shell_has_filter (shell) || ! can_convert_to_u8) &&
113 ! shell->filter_buffer)
114 {
115 gint fw = GIMP_DISPLAY_RENDER_BUF_WIDTH;
116 gint fh = GIMP_DISPLAY_RENDER_BUF_HEIGHT;
117
118 shell->filter_data =
119 gegl_malloc (fw * fh *
120 babl_format_get_bytes_per_pixel (shell->filter_format));
121
122 shell->filter_stride =
123 fw * babl_format_get_bytes_per_pixel (shell->filter_format);
124
125 shell->filter_buffer =
126 gegl_buffer_linear_new_from_data (shell->filter_data,
127 shell->filter_format,
128 GEGL_RECTANGLE (0, 0, fw, fh),
129 GEGL_AUTO_ROWSTRIDE,
130 (GDestroyNotify) gegl_free,
131 shell->filter_data);
132 }
133
134 if (! gimp_display_shell_has_filter (shell) || shell->filter_transform)
135 {
136 /* if there are no filters, or there is a filter transform,
137 * load the projection pixels into the profile_buffer
138 */
139 #ifndef USE_NODE_BLIT
140 gegl_buffer_get (buffer,
141 GEGL_RECTANGLE (x, y, w, h), scale,
142 gimp_projectable_get_format (GIMP_PROJECTABLE (image)),
143 shell->profile_data, shell->profile_stride,
144 abyss_policy);
145 #else
146 gegl_node_blit (node,
147 scale, GEGL_RECTANGLE (x, y, w, h),
148 gimp_projectable_get_format (GIMP_PROJECTABLE (image)),
149 shell->profile_data, shell->profile_stride,
150 GEGL_BLIT_CACHE);
151 #endif
152 }
153 else
154 {
155 /* otherwise, load the pixels directly into the filter_buffer
156 */
157 #ifndef USE_NODE_BLIT
158 gegl_buffer_get (buffer,
159 GEGL_RECTANGLE (x, y, w, h), scale,
160 shell->filter_format,
161 shell->filter_data, shell->filter_stride,
162 abyss_policy);
163 #else
164 gegl_node_blit (node,
165 scale, GEGL_RECTANGLE (x, y, w, h),
166 shell->filter_format,
167 shell->filter_data, shell->filter_stride,
168 GEGL_BLIT_CACHE);
169 #endif
170 }
171
172 /* if there is a filter transform, convert the pixels from
173 * the profile_buffer to the filter_buffer
174 */
175 if (shell->filter_transform)
176 {
177 gimp_color_transform_process_buffer (shell->filter_transform,
178 shell->profile_buffer,
179 GEGL_RECTANGLE (0, 0, w, h),
180 shell->filter_buffer,
181 GEGL_RECTANGLE (0, 0, w, h));
182 }
183
184 /* if there are filters, apply them
185 */
186 if (gimp_display_shell_has_filter (shell))
187 {
188 GeglBuffer *filter_buffer;
189
190 /* shift the filter_buffer so that the area passed to
191 * the filters is the real render area, allowing for
192 * position-dependent filters
193 */
194 filter_buffer = g_object_new (GEGL_TYPE_BUFFER,
195 "source", shell->filter_buffer,
196 "shift-x", -x,
197 "shift-y", -y,
198 NULL);
199
200 /* convert the filter_buffer in place
201 */
202 gimp_color_display_stack_convert_buffer (shell->filter_stack,
203 filter_buffer,
204 GEGL_RECTANGLE (x, y, w, h));
205
206 g_object_unref (filter_buffer);
207 }
208
209 /* if there is a profile transform...
210 */
211 if (shell->profile_transform)
212 {
213 if (gimp_display_shell_has_filter (shell))
214 {
215 /* if we have filters, convert the pixels in the filter_buffer
216 * in-place
217 */
218 gimp_color_transform_process_buffer (shell->profile_transform,
219 shell->filter_buffer,
220 GEGL_RECTANGLE (0, 0, w, h),
221 shell->filter_buffer,
222 GEGL_RECTANGLE (0, 0, w, h));
223 }
224 else if (! can_convert_to_u8)
225 {
226 /* otherwise, if we can't convert to u8 directly, convert
227 * the pixels from the profile_buffer to the filter_buffer
228 */
229 gimp_color_transform_process_buffer (shell->profile_transform,
230 shell->profile_buffer,
231 GEGL_RECTANGLE (0, 0, w, h),
232 shell->filter_buffer,
233 GEGL_RECTANGLE (0, 0, w, h));
234 }
235 else
236 {
237 GeglBuffer *buffer =
238 gegl_buffer_linear_new_from_data (cairo_data,
239 babl_format ("cairo-ARGB32"),
240 GEGL_RECTANGLE (0, 0, w, h),
241 cairo_stride,
242 NULL, NULL);
243
244 /* otherwise, convert the profile_buffer directly into
245 * the cairo_buffer
246 */
247 gimp_color_transform_process_buffer (shell->profile_transform,
248 shell->profile_buffer,
249 GEGL_RECTANGLE (0, 0, w, h),
250 buffer,
251 GEGL_RECTANGLE (0, 0, w, h));
252 g_object_unref (buffer);
253 }
254 }
255
256 /* finally, copy the filter buffer to the cairo-ARGB32 buffer,
257 * if necessary
258 */
259 if (gimp_display_shell_has_filter (shell) || ! can_convert_to_u8)
260 {
261 gegl_buffer_get (shell->filter_buffer,
262 GEGL_RECTANGLE (0, 0, w, h), 1.0,
263 babl_format ("cairo-ARGB32"),
264 cairo_data, cairo_stride,
265 GEGL_ABYSS_NONE);
266 }
267 }
268 else
269 {
270 /* otherwise we can copy the projection pixels straight to the
271 * cairo-ARGB32 buffer
272 */
273 #ifndef USE_NODE_BLIT
274 gegl_buffer_get (buffer,
275 GEGL_RECTANGLE (x, y, w, h), scale,
276 babl_format ("cairo-ARGB32"),
277 cairo_data, cairo_stride,
278 abyss_policy);
279 #else
280 gegl_node_blit (node,
281 scale, GEGL_RECTANGLE (x, y, w, h),
282 babl_format ("cairo-ARGB32"),
283 cairo_data, cairo_stride,
284 GEGL_BLIT_CACHE);
285 #endif
286 }
287
288 #ifdef USE_NODE_BLIT
289 gimp_projectable_end_render (GIMP_PROJECTABLE (image));
290 #endif
291
292 if (shell->mask)
293 {
294 if (! shell->mask_surface)
295 {
296 shell->mask_surface =
297 cairo_image_surface_create (CAIRO_FORMAT_A8,
298 GIMP_DISPLAY_RENDER_BUF_WIDTH,
299 GIMP_DISPLAY_RENDER_BUF_HEIGHT);
300 }
301
302 cairo_surface_mark_dirty (shell->mask_surface);
303
304 cairo_stride = cairo_image_surface_get_stride (shell->mask_surface);
305 cairo_data = cairo_image_surface_get_data (shell->mask_surface) +
306 mask_src_y * cairo_stride + mask_src_x;
307
308 gegl_buffer_get (shell->mask,
309 GEGL_RECTANGLE (x - floor (shell->mask_offset_x * scale),
310 y - floor (shell->mask_offset_y * scale),
311 w, h),
312 scale,
313 babl_format ("Y u8"),
314 cairo_data, cairo_stride,
315 GEGL_ABYSS_NONE);
316
317 if (shell->mask_inverted)
318 {
319 gint mask_height = h;
320
321 while (mask_height--)
322 {
323 gint mask_width = w;
324 guchar *d = cairo_data;
325
326 while (mask_width--)
327 {
328 guchar inv = 255 - *d;
329
330 *d++ = inv;
331 }
332
333 cairo_data += cairo_stride;
334 }
335 }
336 }
337
338 /* put it to the screen */
339 cairo_set_source_surface (cr, xfer,
340 x - xfer_src_x,
341 y - xfer_src_y);
342 cairo_paint (cr);
343
344 if (shell->mask)
345 {
346 gimp_cairo_set_source_rgba (cr, &shell->mask_color);
347 cairo_mask_surface (cr, shell->mask_surface,
348 x - mask_src_x,
349 y - mask_src_y);
350 }
351 }
352