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