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 "libgimpbase/gimpbase.h"
24 #include "libgimpcolor/gimpcolor.h"
25 #include "libgimpmath/gimpmath.h"
26 #include "libgimpwidgets/gimpwidgets.h"
27 
28 #include "display-types.h"
29 
30 #include "core/gimp-cairo.h"
31 #include "core/gimp-utils.h"
32 #include "core/gimpimage.h"
33 
34 #include "gimpcanvas.h"
35 #include "gimpcanvas-style.h"
36 #include "gimpcanvaspath.h"
37 #include "gimpdisplay.h"
38 #include "gimpdisplayshell.h"
39 #include "gimpdisplayshell-draw.h"
40 #include "gimpdisplayshell-render.h"
41 #include "gimpdisplayshell-scale.h"
42 #include "gimpdisplayshell-transform.h"
43 #include "gimpdisplayxfer.h"
44 
45 #ifdef GDK_WINDOWING_QUARTZ
46 #import <AppKit/AppKit.h>
47 #endif
48 
49 /* #define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1 */
50 
51 
52 /*  public functions  */
53 
54 void
gimp_display_shell_draw_selection_out(GimpDisplayShell * shell,cairo_t * cr,GimpSegment * segs,gint n_segs)55 gimp_display_shell_draw_selection_out (GimpDisplayShell *shell,
56                                        cairo_t          *cr,
57                                        GimpSegment      *segs,
58                                        gint              n_segs)
59 {
60   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
61   g_return_if_fail (cr != NULL);
62   g_return_if_fail (segs != NULL && n_segs > 0);
63 
64   gimp_canvas_set_selection_out_style (shell->canvas, cr,
65                                        shell->offset_x, shell->offset_y);
66 
67   gimp_cairo_segments (cr, segs, n_segs);
68   cairo_stroke (cr);
69 }
70 
71 void
gimp_display_shell_draw_selection_in(GimpDisplayShell * shell,cairo_t * cr,cairo_pattern_t * mask,gint index)72 gimp_display_shell_draw_selection_in (GimpDisplayShell   *shell,
73                                       cairo_t            *cr,
74                                       cairo_pattern_t    *mask,
75                                       gint                index)
76 {
77   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
78   g_return_if_fail (cr != NULL);
79   g_return_if_fail (mask != NULL);
80 
81   gimp_canvas_set_selection_in_style (shell->canvas, cr, index,
82                                       shell->offset_x, shell->offset_y);
83 
84   cairo_mask (cr, mask);
85 }
86 
87 void
gimp_display_shell_draw_checkerboard(GimpDisplayShell * shell,cairo_t * cr)88 gimp_display_shell_draw_checkerboard (GimpDisplayShell *shell,
89                                       cairo_t          *cr)
90 {
91   GimpImage *image;
92 
93   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
94   g_return_if_fail (cr != NULL);
95 
96   image = gimp_display_get_image (shell->display);
97 
98   if (G_UNLIKELY (! shell->checkerboard))
99     {
100       GimpCheckSize  check_size;
101       GimpCheckType  check_type;
102       guchar         check_light;
103       guchar         check_dark;
104       GimpRGB        light;
105       GimpRGB        dark;
106 
107       g_object_get (shell->display->config,
108                     "transparency-size", &check_size,
109                     "transparency-type", &check_type,
110                     NULL);
111 
112       gimp_checks_get_shades (check_type, &check_light, &check_dark);
113       gimp_rgb_set_uchar (&light, check_light, check_light, check_light);
114       gimp_rgb_set_uchar (&dark,  check_dark,  check_dark,  check_dark);
115 
116       shell->checkerboard =
117         gimp_cairo_checkerboard_create (cr,
118                                         1 << (check_size + 2), &light, &dark);
119     }
120 
121   cairo_translate (cr, - shell->offset_x, - shell->offset_y);
122 
123   if (gimp_image_get_component_visible (image, GIMP_CHANNEL_ALPHA))
124     cairo_set_source (cr, shell->checkerboard);
125   else
126     cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
127 
128   cairo_paint (cr);
129 }
130 
131 void
gimp_display_shell_draw_image(GimpDisplayShell * shell,cairo_t * cr,gint x,gint y,gint w,gint h)132 gimp_display_shell_draw_image (GimpDisplayShell *shell,
133                                cairo_t          *cr,
134                                gint              x,
135                                gint              y,
136                                gint              w,
137                                gint              h)
138 {
139   gdouble chunk_width;
140   gdouble chunk_height;
141   gdouble scale = 1.0;
142   gint    n_rows;
143   gint    n_cols;
144   gint    r, c;
145 
146   g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
147   g_return_if_fail (gimp_display_get_image (shell->display));
148   g_return_if_fail (cr != NULL);
149 
150   /*  display the image in RENDER_BUF_WIDTH x RENDER_BUF_HEIGHT
151    *  maximally-sized image-space chunks.  adjust the screen-space
152    *  chunk size as necessary, to accommodate for the display
153    *  transform and window scale factor.
154    */
155   chunk_width  = GIMP_DISPLAY_RENDER_BUF_WIDTH;
156   chunk_height = GIMP_DISPLAY_RENDER_BUF_HEIGHT;
157 
158 #ifdef GIMP_DISPLAY_RENDER_ENABLE_SCALING
159   /* if we had this future API, things would look pretty on hires (retina) */
160   scale *=
161     gdk_window_get_scale_factor (
162       gtk_widget_get_window (gtk_widget_get_toplevel (GTK_WIDGET (shell))));
163 #elif defined(GDK_WINDOWING_QUARTZ)
164   /* gtk2/osx retina support */
165   if ([
166       [NSScreen mainScreen]
167       respondsToSelector: @selector(backingScaleFactor)
168     ]) {
169     for (NSScreen * screen in [NSScreen screens]) {
170       float s = [screen backingScaleFactor];
171       if (s > scale) scale = s;
172     }
173   }
174 #endif
175 
176   scale  = MIN (scale, GIMP_DISPLAY_RENDER_MAX_SCALE);
177   scale *= MAX (shell->scale_x, shell->scale_y);
178 
179   if (scale != shell->scale_x)
180     chunk_width  = (chunk_width  - 1.0) * (shell->scale_x / scale);
181   if (scale != shell->scale_y)
182     chunk_height = (chunk_height - 1.0) * (shell->scale_y / scale);
183 
184   if (shell->rotate_untransform)
185     {
186       gdouble a = shell->rotate_angle * G_PI / 180.0;
187 
188       chunk_width = chunk_height = (MIN (chunk_width, chunk_height) - 1.0) /
189                                    (fabs (sin (a)) + fabs (cos (a)));
190     }
191 
192   /* divide the painted area to evenly-sized chunks */
193   n_rows = ceil (h / floor (chunk_height));
194   n_cols = ceil (w / floor (chunk_width));
195 
196   for (r = 0; r < n_rows; r++)
197     {
198       gint y1 = y + (2 *  r      * h + n_rows) / (2 * n_rows);
199       gint y2 = y + (2 * (r + 1) * h + n_rows) / (2 * n_rows);
200 
201       for (c = 0; c < n_cols; c++)
202         {
203           gint    x1 = x + (2 *  c      * w + n_cols) / (2 * n_cols);
204           gint    x2 = x + (2 * (c + 1) * w + n_cols) / (2 * n_cols);
205           gdouble ix1, iy1;
206           gdouble ix2, iy2;
207           gint    ix, iy;
208           gint    iw, ih;
209 
210           /* map chunk from screen space to scaled image space */
211           gimp_display_shell_untransform_bounds_with_scale (
212             shell, scale,
213             x1,   y1,   x2,   y2,
214             &ix1, &iy1, &ix2, &iy2);
215 
216           ix = floor (ix1);
217           iy = floor (iy1);
218           iw = ceil  (ix2) - ix;
219           ih = ceil  (iy2) - iy;
220 
221           cairo_save (cr);
222 
223           /* clip to chunk bounds, in screen space */
224           cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
225           cairo_clip (cr);
226 
227           /* transform to scaled image space, and apply uneven scaling */
228           if (shell->rotate_transform)
229             cairo_transform (cr, shell->rotate_transform);
230           cairo_translate (cr, -shell->offset_x, -shell->offset_y);
231           cairo_scale (cr, shell->scale_x / scale, shell->scale_y / scale);
232 
233           /* render image */
234           gimp_display_shell_render (shell, cr, ix, iy, iw, ih, scale);
235 
236           cairo_restore (cr);
237 
238           /* if the GIMP_BRICK_WALL environment variable is defined,
239            * show chunk bounds
240            */
241           {
242             static gint brick_wall = -1;
243 
244             if (brick_wall < 0)
245               brick_wall = (g_getenv ("GIMP_BRICK_WALL") != NULL);
246 
247             if (brick_wall)
248               {
249                 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
250                 cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
251                 cairo_stroke (cr);
252               }
253           }
254         }
255     }
256 }
257