gimpdisplayshell-render.c 14.8 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Elliot Lee's avatar
Elliot Lee committed
2 3
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4
 * This program is free software: you can redistribute it and/or modify
Elliot Lee's avatar
Elliot Lee committed
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
Elliot Lee's avatar
Elliot Lee committed
7 8 9 10 11 12 13 14
 * (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
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16
 */
Sven Neumann's avatar
Sven Neumann committed
17 18 19

#include "config.h"

20
#include <gegl.h>
Sven Neumann's avatar
Sven Neumann committed
21 22
#include <gtk/gtk.h>

23
#include "libgimpmath/gimpmath.h"
24
#include "libgimpcolor/gimpcolor.h"
25 26
#include "libgimpwidgets/gimpwidgets.h"

27
#include "display-types.h"
Sven Neumann's avatar
Sven Neumann committed
28

29 30
#include "config/gimpdisplayconfig.h"

31 32
#include "gegl/gimp-gegl-utils.h"

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"
Michael Natterer's avatar
Michael Natterer committed
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 49 50
/* #define GIMP_DISPLAY_RENDER_ENABLE_SCALING 1 */


Elliot Lee's avatar
Elliot Lee committed
51
void
52 53 54 55 56 57
gimp_display_shell_render (GimpDisplayShell *shell,
                           cairo_t          *cr,
                           gint              x,
                           gint              y,
                           gint              w,
                           gint              h)
Elliot Lee's avatar
Elliot Lee committed
58
{
59 60
  GimpImage       *image;
  GeglBuffer      *buffer;
61 62 63
#ifdef USE_NODE_BLIT
  GeglNode        *node;
#endif
64 65 66
  gdouble          scale_x       = 1.0;
  gdouble          scale_y       = 1.0;
  gdouble          buffer_scale  = 1.0;
67 68 69 70
  gint             viewport_offset_x;
  gint             viewport_offset_y;
  gint             viewport_width;
  gint             viewport_height;
71 72 73 74
  gint             scaled_x;
  gint             scaled_y;
  gint             scaled_width;
  gint             scaled_height;
75
  cairo_surface_t *xfer;
76 77
  gint             xfer_src_x;
  gint             xfer_src_y;
78 79
  gint             mask_src_x = 0;
  gint             mask_src_y = 0;
80 81 82
  gint             cairo_stride;
  guchar          *cairo_data;
  GeglBuffer      *cairo_buffer;
83

84
  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
85
  g_return_if_fail (cr != NULL);
Sven Neumann's avatar
Sven Neumann committed
86 87
  g_return_if_fail (w > 0 && h > 0);

88 89
  image  = gimp_display_get_image (shell->display);
  buffer = gimp_pickable_get_buffer (GIMP_PICKABLE (image));
90 91
#ifdef USE_NODE_BLIT
  node   = gimp_projectable_get_graph (GIMP_PROJECTABLE (image));
92 93

  gimp_projectable_begin_render (GIMP_PROJECTABLE (image));
94
#endif
95

96 97
#ifdef GIMP_DISPLAY_RENDER_ENABLE_SCALING
  /* if we had this future API, things would look pretty on hires (retina) */
98
  scale_x = gdk_window_get_scale_factor (gtk_widget_get_window (gtk_widget_get_toplevel (GTK_WIDGET (shell))));
99 100
#endif

101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
  scale_x = MIN (scale_x, GIMP_DISPLAY_RENDER_MAX_SCALE);
  scale_y = scale_x;

  if (shell->scale_x > shell->scale_y)
    {
      scale_y *= (shell->scale_x / shell->scale_y);

      buffer_scale = shell->scale_y * scale_y;
    }
  else if (shell->scale_y > shell->scale_x)
    {
      scale_x *= (shell->scale_y / shell->scale_x);

      buffer_scale = shell->scale_x * scale_x;
    }
  else
    {
      buffer_scale = shell->scale_x * scale_x;
    }
120

121 122 123 124 125
  gimp_display_shell_scroll_get_scaled_viewport (shell,
                                                 &viewport_offset_x,
                                                 &viewport_offset_y,
                                                 &viewport_width,
                                                 &viewport_height);
126 127 128 129 130 131

  scaled_x      = floor ((x + viewport_offset_x) * scale_x);
  scaled_y      = floor ((y + viewport_offset_y) * scale_y);
  scaled_width  = ceil (w * scale_x);
  scaled_height = ceil (h * scale_y);

132 133 134 135
  if (shell->rotate_transform)
    {
      xfer = cairo_surface_create_similar_image (cairo_get_target (cr),
                                                 CAIRO_FORMAT_ARGB32,
136 137
                                                 scaled_width,
                                                 scaled_height);
138
      cairo_surface_mark_dirty (xfer);
139 140
      xfer_src_x = 0;
      xfer_src_y = 0;
141 142 143 144
    }
  else
    {
      xfer = gimp_display_xfer_get_surface (shell->xfer,
145 146
                                            scaled_width,
                                            scaled_height,
147 148
                                            &xfer_src_x,
                                            &xfer_src_y);
149
    }
150

151 152 153
  cairo_stride = cairo_image_surface_get_stride (xfer);
  cairo_data   = cairo_image_surface_get_data (xfer) +
                 xfer_src_y * cairo_stride + xfer_src_x * 4;
154

155 156 157 158 159 160 161
  cairo_buffer = gegl_buffer_linear_new_from_data (cairo_data,
                                                   babl_format ("cairo-ARGB32"),
                                                   GEGL_RECTANGLE (0, 0,
                                                                   scaled_width,
                                                                   scaled_height),
                                                   cairo_stride,
                                                   NULL, NULL);
162

163 164 165
  if (shell->profile_transform ||
      gimp_display_shell_has_filter (shell))
    {
166 167
      gboolean can_convert_to_u8;

168 169 170 171
      /*  if there is a profile transform or a display filter, we need
       *  to use temp buffers
       */

172 173
      can_convert_to_u8 = gimp_display_shell_profile_can_convert_to_u8 (shell);

174 175
      /*  create the filter buffer if we have filters
       */
176
      if ((gimp_display_shell_has_filter (shell) || ! can_convert_to_u8) &&
177
          ! shell->filter_buffer)
178 179 180 181 182
        {
          gint w = GIMP_DISPLAY_RENDER_BUF_WIDTH  * GIMP_DISPLAY_RENDER_MAX_SCALE;
          gint h = GIMP_DISPLAY_RENDER_BUF_HEIGHT * GIMP_DISPLAY_RENDER_MAX_SCALE;

          shell->filter_data =
183
            gegl_malloc (w * h * babl_format_get_bytes_per_pixel (shell->filter_format));
184

185
          shell->filter_stride =
186
            w * babl_format_get_bytes_per_pixel (shell->filter_format);
187 188 189

          shell->filter_buffer =
            gegl_buffer_linear_new_from_data (shell->filter_data,
190
                                              shell->filter_format,
191 192 193 194 195
                                              GEGL_RECTANGLE (0, 0, w, h),
                                              GEGL_AUTO_ROWSTRIDE,
                                              (GDestroyNotify) gegl_free,
                                              shell->filter_data);
        }
196

197 198 199 200 201
      if (shell->profile_transform)
        {
          /*  if there is a profile transform, load the projection
           *  pixels into the profile_buffer
           */
202
#ifndef USE_NODE_BLIT
203 204 205 206
          gegl_buffer_get (buffer,
                           GEGL_RECTANGLE (scaled_x, scaled_y,
                                           scaled_width, scaled_height),
                           buffer_scale,
207
                           gimp_projectable_get_format (GIMP_PROJECTABLE (image)),
208 209
                           shell->profile_data, shell->profile_stride,
                           GEGL_ABYSS_CLAMP);
210
#else
211 212 213 214
          gegl_node_blit (node,
                          buffer_scale,
                          GEGL_RECTANGLE (scaled_x, scaled_y,
                                          scaled_width, scaled_height),
215
                          gimp_projectable_get_format (GIMP_PROJECTABLE (image)),
216 217
                          shell->profile_data, shell->profile_stride,
                          GEGL_BLIT_CACHE);
218
#endif
219

220
          if (gimp_display_shell_has_filter (shell) || ! can_convert_to_u8)
221 222 223 224
            {
              /*  if there are filters, convert the pixels from the
               *  profile_buffer to the filter_buffer
               */
225 226 227 228 229 230 231 232 233
              gimp_color_transform_process_buffer (shell->profile_transform,
                                                   shell->profile_buffer,
                                                   GEGL_RECTANGLE (0, 0,
                                                                   scaled_width,
                                                                   scaled_height),
                                                   shell->filter_buffer,
                                                   GEGL_RECTANGLE (0, 0,
                                                                   scaled_width,
                                                                   scaled_height));
234 235 236 237 238 239
            }
          else
            {
              /*  otherwise, convert the profile_buffer directly into
               *  the cairo_buffer
               */
240 241 242 243 244 245 246 247 248
              gimp_color_transform_process_buffer (shell->profile_transform,
                                                   shell->profile_buffer,
                                                   GEGL_RECTANGLE (0, 0,
                                                                   scaled_width,
                                                                   scaled_height),
                                                   cairo_buffer,
                                                   GEGL_RECTANGLE (0, 0,
                                                                   scaled_width,
                                                                   scaled_height));
249 250 251 252 253 254 255 256 257 258 259 260
            }
        }
      else
        {
          /*  otherwise, load the projection pixels directly into the
           *  filter_buffer
           */
#ifndef USE_NODE_BLIT
          gegl_buffer_get (buffer,
                           GEGL_RECTANGLE (scaled_x, scaled_y,
                                           scaled_width, scaled_height),
                           buffer_scale,
261
                           shell->filter_format,
262 263 264 265 266 267 268
                           shell->filter_data, shell->filter_stride,
                           GEGL_ABYSS_CLAMP);
#else
          gegl_node_blit (node,
                          buffer_scale,
                          GEGL_RECTANGLE (scaled_x, scaled_y,
                                          scaled_width, scaled_height),
269
                          shell->filter_format,
270 271 272 273 274 275 276 277 278 279 280 281 282 283
                          shell->filter_data, shell->filter_stride,
                          GEGL_BLIT_CACHE);
#endif
        }

      if (gimp_display_shell_has_filter (shell))
        {
          /*  convert the filter_buffer in place
           */
          gimp_color_display_stack_convert_buffer (shell->filter_stack,
                                                   shell->filter_buffer,
                                                   GEGL_RECTANGLE (0, 0,
                                                                   scaled_width,
                                                                   scaled_height));
284
        }
285

286 287
      if (gimp_display_shell_has_filter (shell) || ! can_convert_to_u8)
        {
288 289 290 291 292 293 294 295 296 297 298
          /*  finally, copy the filter buffer to the cairo-ARGB32 buffer
           */
          gegl_buffer_get (shell->filter_buffer,
                           GEGL_RECTANGLE (0, 0,
                                           scaled_width,
                                           scaled_height),
                           1.0,
                           babl_format ("cairo-ARGB32"),
                           cairo_data, cairo_stride,
                           GEGL_ABYSS_CLAMP);
        }
299 300 301
    }
  else
    {
302 303 304
      /*  otherwise we can copy the projection pixels straight to the
       *  cairo-ARGB32 buffer
       */
305
#ifndef USE_NODE_BLIT
306
      gegl_buffer_get (buffer,
307 308 309
                       GEGL_RECTANGLE (scaled_x, scaled_y,
                                       scaled_width, scaled_height),
                       buffer_scale,
310
                       babl_format ("cairo-ARGB32"),
311
                       cairo_data, cairo_stride,
312
                       GEGL_ABYSS_CLAMP);
313 314 315 316 317 318
#else
      gegl_node_blit (node,
                      buffer_scale,
                      GEGL_RECTANGLE (scaled_x, scaled_y,
                                      scaled_width, scaled_height),
                      babl_format ("cairo-ARGB32"),
319
                      cairo_data, cairo_stride,
320 321
                      GEGL_BLIT_CACHE);
#endif
322 323
    }

324 325
  g_object_unref (cairo_buffer);

326 327 328 329
#ifdef USE_NODE_BLIT
  gimp_projectable_end_render (GIMP_PROJECTABLE (image));
#endif

330
  if (shell->mask)
331
    {
332 333 334
      if (! shell->mask_surface)
        {
          shell->mask_surface =
335 336 337 338 339
            cairo_image_surface_create (CAIRO_FORMAT_A8,
                                        GIMP_DISPLAY_RENDER_BUF_WIDTH  *
                                        GIMP_DISPLAY_RENDER_MAX_SCALE,
                                        GIMP_DISPLAY_RENDER_BUF_HEIGHT *
                                        GIMP_DISPLAY_RENDER_MAX_SCALE);
340 341
        }

342 343
      cairo_surface_mark_dirty (shell->mask_surface);

344 345 346
      cairo_stride = cairo_image_surface_get_stride (shell->mask_surface);
      cairo_data   = cairo_image_surface_get_data (shell->mask_surface) +
                     mask_src_y * cairo_stride + mask_src_x * 4;
347

348
      gegl_buffer_get (shell->mask,
349 350
                       GEGL_RECTANGLE (scaled_x - shell->mask_offset_x,
                                       scaled_y - shell->mask_offset_y,
351 352
                                       scaled_width, scaled_height),
                       buffer_scale,
353
                       babl_format ("Y u8"),
354
                       cairo_data, cairo_stride,
355
                       GEGL_ABYSS_NONE);
356

357
      if (shell->mask_inverted)
358
        {
359
          gint mask_height = scaled_height;
360

361
          while (mask_height--)
362
            {
363
              gint    mask_width = scaled_width;
364
              guchar *d          = cairo_data;
365

366 367 368
              while (mask_width--)
                {
                  guchar inv = 255 - *d;
369

370 371 372
                  *d++ = inv;
                }

373
              cairo_data += cairo_stride;
374
            }
375
        }
376
    }
377

378
  /*  put it to the screen  */
379
  cairo_save (cr);
380

381
  cairo_rectangle (cr, x, y, w, h);
382

383
  cairo_scale (cr, 1.0 / scale_x, 1.0 / scale_y);
384

385
  cairo_set_source_surface (cr, xfer,
386 387
                            x * scale_x - xfer_src_x,
                            y * scale_y - xfer_src_y);
388

389 390 391 392 393 394 395 396 397 398 399 400 401 402
  if (shell->rotate_transform)
    {
      cairo_pattern_t *pattern;

      pattern = cairo_get_source (cr);
      cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);

      cairo_set_line_width (cr, 1.0);
      cairo_stroke_preserve (cr);

      cairo_surface_destroy (xfer);
    }

  cairo_clip (cr);
403
  cairo_paint (cr);
404

405 406 407 408
  if (shell->mask)
    {
      gimp_cairo_set_source_rgba (cr, &shell->mask_color);
      cairo_mask_surface (cr, shell->mask_surface,
409 410
                          (x - mask_src_x) * scale_x,
                          (y - mask_src_y) * scale_y);
411
    }
412

413
  cairo_restore (cr);
Elliot Lee's avatar
Elliot Lee committed
414
}