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 <cairo.h>
21 #include <gegl.h>
22 
23 #include "gimp-gegl-types.h"
24 
25 #include "core/gimpchunkiterator.h"
26 #include "core/gimpmarshal.h"
27 
28 #include "gimp-gegl-loops.h"
29 #include "gimp-gegl-utils.h"
30 #include "gimptilehandlervalidate.h"
31 
32 
33 enum
34 {
35   INVALIDATED,
36   LAST_SIGNAL
37 };
38 
39 enum
40 {
41   PROP_0,
42   PROP_FORMAT,
43   PROP_TILE_WIDTH,
44   PROP_TILE_HEIGHT,
45   PROP_WHOLE_TILE
46 };
47 
48 
49 static void     gimp_tile_handler_validate_finalize             (GObject         *object);
50 static void     gimp_tile_handler_validate_set_property         (GObject         *object,
51                                                                  guint            property_id,
52                                                                  const GValue    *value,
53                                                                  GParamSpec      *pspec);
54 static void     gimp_tile_handler_validate_get_property         (GObject         *object,
55                                                                  guint            property_id,
56                                                                  GValue          *value,
57                                                                  GParamSpec      *pspec);
58 
59 static void     gimp_tile_handler_validate_real_begin_validate  (GimpTileHandlerValidate *validate);
60 static void     gimp_tile_handler_validate_real_end_validate    (GimpTileHandlerValidate *validate);
61 static void     gimp_tile_handler_validate_real_validate        (GimpTileHandlerValidate *validate,
62                                                                  const GeglRectangle     *rect,
63                                                                  const Babl              *format,
64                                                                  gpointer                 dest_buf,
65                                                                  gint                     dest_stride);
66 static void     gimp_tile_handler_validate_real_validate_buffer (GimpTileHandlerValidate *validate,
67                                                                  const GeglRectangle     *rect,
68                                                                  GeglBuffer              *buffer);
69 
70 static gpointer gimp_tile_handler_validate_command              (GeglTileSource  *source,
71                                                                  GeglTileCommand  command,
72                                                                  gint             x,
73                                                                  gint             y,
74                                                                  gint             z,
75                                                                  gpointer         data);
76 
77 
G_DEFINE_TYPE(GimpTileHandlerValidate,gimp_tile_handler_validate,GEGL_TYPE_TILE_HANDLER)78 G_DEFINE_TYPE (GimpTileHandlerValidate, gimp_tile_handler_validate,
79                GEGL_TYPE_TILE_HANDLER)
80 
81 #define parent_class gimp_tile_handler_validate_parent_class
82 
83 static guint gimp_tile_handler_validate_signals[LAST_SIGNAL];
84 
85 
86 static void
87 gimp_tile_handler_validate_class_init (GimpTileHandlerValidateClass *klass)
88 {
89   GObjectClass *object_class = G_OBJECT_CLASS (klass);
90 
91   gimp_tile_handler_validate_signals[INVALIDATED] =
92     g_signal_new ("invalidated",
93                   G_TYPE_FROM_CLASS (klass),
94                   G_SIGNAL_RUN_FIRST,
95                   G_STRUCT_OFFSET (GimpTileHandlerValidateClass, invalidated),
96                   NULL, NULL,
97                   g_cclosure_marshal_VOID__BOXED,
98                   G_TYPE_NONE, 1,
99                   GEGL_TYPE_RECTANGLE);
100 
101   object_class->finalize     = gimp_tile_handler_validate_finalize;
102   object_class->set_property = gimp_tile_handler_validate_set_property;
103   object_class->get_property = gimp_tile_handler_validate_get_property;
104 
105   klass->begin_validate      = gimp_tile_handler_validate_real_begin_validate;
106   klass->end_validate        = gimp_tile_handler_validate_real_end_validate;
107   klass->validate            = gimp_tile_handler_validate_real_validate;
108   klass->validate_buffer     = gimp_tile_handler_validate_real_validate_buffer;
109 
110   g_object_class_install_property (object_class, PROP_FORMAT,
111                                    g_param_spec_pointer ("format", NULL, NULL,
112                                                          GIMP_PARAM_READWRITE));
113 
114   g_object_class_install_property (object_class, PROP_TILE_WIDTH,
115                                    g_param_spec_int ("tile-width", NULL, NULL,
116                                                      1, G_MAXINT, 1,
117                                                      GIMP_PARAM_READWRITE |
118                                                      G_PARAM_CONSTRUCT));
119 
120   g_object_class_install_property (object_class, PROP_TILE_HEIGHT,
121                                    g_param_spec_int ("tile-height", NULL, NULL,
122                                                      1, G_MAXINT, 1,
123                                                      GIMP_PARAM_READWRITE |
124                                                      G_PARAM_CONSTRUCT));
125 
126   g_object_class_install_property (object_class, PROP_WHOLE_TILE,
127                                    g_param_spec_boolean ("whole-tile", NULL, NULL,
128                                                          FALSE,
129                                                          GIMP_PARAM_READWRITE |
130                                                          G_PARAM_CONSTRUCT));
131 }
132 
133 static void
gimp_tile_handler_validate_init(GimpTileHandlerValidate * validate)134 gimp_tile_handler_validate_init (GimpTileHandlerValidate *validate)
135 {
136   GeglTileSource *source = GEGL_TILE_SOURCE (validate);
137 
138   source->command = gimp_tile_handler_validate_command;
139 
140   validate->dirty_region = cairo_region_create ();
141 }
142 
143 static void
gimp_tile_handler_validate_finalize(GObject * object)144 gimp_tile_handler_validate_finalize (GObject *object)
145 {
146   GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (object);
147 
148   g_clear_object (&validate->graph);
149   g_clear_pointer (&validate->dirty_region, cairo_region_destroy);
150 
151   G_OBJECT_CLASS (parent_class)->finalize (object);
152 }
153 
154 static void
gimp_tile_handler_validate_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)155 gimp_tile_handler_validate_set_property (GObject      *object,
156                                          guint         property_id,
157                                          const GValue *value,
158                                          GParamSpec   *pspec)
159 {
160   GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (object);
161 
162   switch (property_id)
163     {
164     case PROP_FORMAT:
165       validate->format = g_value_get_pointer (value);
166       break;
167     case PROP_TILE_WIDTH:
168       validate->tile_width = g_value_get_int (value);
169       break;
170     case PROP_TILE_HEIGHT:
171       validate->tile_height = g_value_get_int (value);
172       break;
173     case PROP_WHOLE_TILE:
174       validate->whole_tile = g_value_get_boolean (value);
175       break;
176 
177     default:
178       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
179       break;
180     }
181 }
182 
183 static void
gimp_tile_handler_validate_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)184 gimp_tile_handler_validate_get_property (GObject    *object,
185                                          guint       property_id,
186                                          GValue     *value,
187                                          GParamSpec *pspec)
188 {
189   GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (object);
190 
191   switch (property_id)
192     {
193     case PROP_FORMAT:
194       g_value_set_pointer (value, (gpointer) validate->format);
195       break;
196     case PROP_TILE_WIDTH:
197       g_value_set_int (value, validate->tile_width);
198       break;
199     case PROP_TILE_HEIGHT:
200       g_value_set_int (value, validate->tile_height);
201       break;
202     case PROP_WHOLE_TILE:
203       g_value_set_boolean (value, validate->whole_tile);
204       break;
205 
206     default:
207       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
208       break;
209     }
210 }
211 
212 static void
gimp_tile_handler_validate_real_begin_validate(GimpTileHandlerValidate * validate)213 gimp_tile_handler_validate_real_begin_validate (GimpTileHandlerValidate *validate)
214 {
215   validate->suspend_validate++;
216 }
217 
218 static void
gimp_tile_handler_validate_real_end_validate(GimpTileHandlerValidate * validate)219 gimp_tile_handler_validate_real_end_validate (GimpTileHandlerValidate *validate)
220 {
221   validate->suspend_validate--;
222 }
223 
224 static void
gimp_tile_handler_validate_real_validate(GimpTileHandlerValidate * validate,const GeglRectangle * rect,const Babl * format,gpointer dest_buf,gint dest_stride)225 gimp_tile_handler_validate_real_validate (GimpTileHandlerValidate *validate,
226                                           const GeglRectangle     *rect,
227                                           const Babl              *format,
228                                           gpointer                 dest_buf,
229                                           gint                     dest_stride)
230 {
231 #if 0
232   g_printerr ("validating at %d %d %d %d\n",
233               rect.x,
234               rect.y,
235               rect.width,
236               rect.height);
237 #endif
238 
239   gegl_node_blit (validate->graph, 1.0, rect, format,
240                   dest_buf, dest_stride,
241                   GEGL_BLIT_DEFAULT);
242 }
243 
244 static void
gimp_tile_handler_validate_real_validate_buffer(GimpTileHandlerValidate * validate,const GeglRectangle * rect,GeglBuffer * buffer)245 gimp_tile_handler_validate_real_validate_buffer (GimpTileHandlerValidate *validate,
246                                                  const GeglRectangle     *rect,
247                                                  GeglBuffer              *buffer)
248 {
249   GimpTileHandlerValidateClass *klass;
250 
251   klass = GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate);
252 
253   if (klass->validate == gimp_tile_handler_validate_real_validate)
254     {
255       gegl_node_blit_buffer (validate->graph, buffer, rect, 0,
256                              GEGL_ABYSS_NONE);
257     }
258   else
259     {
260       const Babl *format = gegl_buffer_get_format (buffer);
261       gpointer    data;
262       gint        stride;
263 
264       data = gegl_buffer_linear_open (buffer, rect, &stride, format);
265 
266       klass->validate (validate, rect, format, data, stride);
267 
268       gegl_buffer_linear_close (buffer, data);
269     }
270 }
271 
272 static GeglTile *
gimp_tile_handler_validate_validate_tile(GeglTileSource * source,gint x,gint y)273 gimp_tile_handler_validate_validate_tile (GeglTileSource *source,
274                                           gint            x,
275                                           gint            y)
276 {
277   GimpTileHandlerValidate *validate = GIMP_TILE_HANDLER_VALIDATE (source);
278   GeglTile                *tile;
279   cairo_rectangle_int_t    tile_rect;
280   cairo_region_overlap_t   overlap;
281 
282   if (validate->suspend_validate ||
283       cairo_region_is_empty (validate->dirty_region))
284     {
285       return gegl_tile_handler_source_command (source,
286                                                GEGL_TILE_GET, x, y, 0, NULL);
287     }
288 
289   tile_rect.x      = x * validate->tile_width;
290   tile_rect.y      = y * validate->tile_height;
291   tile_rect.width  = validate->tile_width;
292   tile_rect.height = validate->tile_height;
293 
294   overlap = cairo_region_contains_rectangle (validate->dirty_region,
295                                              &tile_rect);
296 
297   if (overlap == CAIRO_REGION_OVERLAP_OUT)
298     {
299       return gegl_tile_handler_source_command (source,
300                                                GEGL_TILE_GET, x, y, 0, NULL);
301     }
302 
303   if (overlap == CAIRO_REGION_OVERLAP_IN || validate->whole_tile)
304     {
305       gint tile_bpp;
306       gint tile_stride;
307 
308       cairo_region_subtract_rectangle (validate->dirty_region, &tile_rect);
309 
310       tile_bpp    = babl_format_get_bytes_per_pixel (validate->format);
311       tile_stride = tile_bpp * validate->tile_width;
312 
313       tile = gegl_tile_handler_get_source_tile (GEGL_TILE_HANDLER (source),
314                                                 x, y, 0, FALSE);
315 
316       gimp_tile_handler_validate_begin_validate (validate);
317 
318       gegl_tile_lock (tile);
319 
320       GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->validate
321         (validate,
322          GEGL_RECTANGLE (tile_rect.x,
323                          tile_rect.y,
324                          tile_rect.width,
325                          tile_rect.height),
326          validate->format,
327          gegl_tile_get_data (tile),
328          tile_stride);
329 
330       gegl_tile_unlock (tile);
331 
332       gimp_tile_handler_validate_end_validate (validate);
333     }
334   else
335     {
336       cairo_region_t *tile_region;
337       gint            tile_bpp;
338       gint            tile_stride;
339       gint            n_rects;
340       gint            i;
341 
342       tile_region = cairo_region_copy (validate->dirty_region);
343       cairo_region_intersect_rectangle (tile_region, &tile_rect);
344 
345       cairo_region_subtract_rectangle (validate->dirty_region, &tile_rect);
346 
347       tile_bpp    = babl_format_get_bytes_per_pixel (validate->format);
348       tile_stride = tile_bpp * validate->tile_width;
349 
350       tile = gegl_tile_handler_source_command (source,
351                                                GEGL_TILE_GET, x, y, 0, NULL);
352 
353       if (! tile)
354         {
355           tile = gegl_tile_handler_create_tile (GEGL_TILE_HANDLER (source),
356                                                 x, y, 0);
357 
358           memset (gegl_tile_get_data (tile),
359                   0, tile_stride * validate->tile_height);
360         }
361 
362       gimp_tile_handler_validate_begin_validate (validate);
363 
364       gegl_tile_lock (tile);
365 
366       n_rects = cairo_region_num_rectangles (tile_region);
367 
368 #if 0
369       g_printerr ("%d chunks\n", n_rects);
370 #endif
371 
372       for (i = 0; i < n_rects; i++)
373         {
374           cairo_rectangle_int_t blit_rect;
375           gint                  tile_x;
376           gint                  tile_y;
377 
378           cairo_region_get_rectangle (tile_region, i, &blit_rect);
379 
380           tile_x = blit_rect.x % validate->tile_width;
381           if (tile_x < 0) tile_x += validate->tile_width;
382 
383           tile_y = blit_rect.y % validate->tile_height;
384           if (tile_y < 0) tile_y += validate->tile_height;
385 
386           GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->validate
387             (validate,
388              GEGL_RECTANGLE (blit_rect.x,
389                              blit_rect.y,
390                              blit_rect.width,
391                              blit_rect.height),
392              validate->format,
393              gegl_tile_get_data (tile) +
394              tile_y * tile_stride      +
395              tile_x * tile_bpp,
396              tile_stride);
397         }
398 
399       gegl_tile_unlock (tile);
400 
401       gimp_tile_handler_validate_end_validate (validate);
402 
403       cairo_region_destroy (tile_region);
404     }
405 
406   return tile;
407 }
408 
409 static gpointer
gimp_tile_handler_validate_command(GeglTileSource * source,GeglTileCommand command,gint x,gint y,gint z,gpointer data)410 gimp_tile_handler_validate_command (GeglTileSource  *source,
411                                     GeglTileCommand  command,
412                                     gint             x,
413                                     gint             y,
414                                     gint             z,
415                                     gpointer         data)
416 {
417   if (command == GEGL_TILE_GET && z == 0)
418     return gimp_tile_handler_validate_validate_tile (source, x, y);
419 
420   return gegl_tile_handler_source_command (source, command, x, y, z, data);
421 }
422 
423 
424 /*  public functions  */
425 
426 GeglTileHandler *
gimp_tile_handler_validate_new(GeglNode * graph)427 gimp_tile_handler_validate_new (GeglNode *graph)
428 {
429   GimpTileHandlerValidate *validate;
430 
431   g_return_val_if_fail (GEGL_IS_NODE (graph), NULL);
432 
433   validate = g_object_new (GIMP_TYPE_TILE_HANDLER_VALIDATE, NULL);
434 
435   validate->graph = g_object_ref (graph);
436 
437   return GEGL_TILE_HANDLER (validate);
438 }
439 
440 void
gimp_tile_handler_validate_assign(GimpTileHandlerValidate * validate,GeglBuffer * buffer)441 gimp_tile_handler_validate_assign (GimpTileHandlerValidate *validate,
442                                    GeglBuffer              *buffer)
443 {
444   g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate));
445   g_return_if_fail (GEGL_IS_BUFFER (buffer));
446   g_return_if_fail (gimp_tile_handler_validate_get_assigned (buffer) == NULL);
447 
448   gegl_buffer_add_handler (buffer, validate);
449 
450   g_object_get (buffer,
451                 "format",      &validate->format,
452                 "tile-width",  &validate->tile_width,
453                 "tile-height", &validate->tile_height,
454                 NULL);
455 
456   g_object_set_data (G_OBJECT (buffer),
457                      "gimp-tile-handler-validate", validate);
458 }
459 
460 void
gimp_tile_handler_validate_unassign(GimpTileHandlerValidate * validate,GeglBuffer * buffer)461 gimp_tile_handler_validate_unassign (GimpTileHandlerValidate *validate,
462                                      GeglBuffer              *buffer)
463 {
464   g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate));
465   g_return_if_fail (GEGL_IS_BUFFER (buffer));
466   g_return_if_fail (gimp_tile_handler_validate_get_assigned (buffer) == validate);
467 
468   g_object_set_data (G_OBJECT (buffer),
469                      "gimp-tile-handler-validate", NULL);
470 
471   gegl_buffer_remove_handler (buffer, validate);
472 }
473 
474 GimpTileHandlerValidate *
gimp_tile_handler_validate_get_assigned(GeglBuffer * buffer)475 gimp_tile_handler_validate_get_assigned (GeglBuffer *buffer)
476 {
477   g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
478 
479   return g_object_get_data (G_OBJECT (buffer),
480                             "gimp-tile-handler-validate");
481 }
482 
483 void
gimp_tile_handler_validate_invalidate(GimpTileHandlerValidate * validate,const GeglRectangle * rect)484 gimp_tile_handler_validate_invalidate (GimpTileHandlerValidate *validate,
485                                        const GeglRectangle     *rect)
486 {
487   g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate));
488   g_return_if_fail (rect != NULL);
489 
490   cairo_region_union_rectangle (validate->dirty_region,
491                                 (cairo_rectangle_int_t *) rect);
492 
493   gegl_tile_handler_damage_rect (GEGL_TILE_HANDLER (validate), rect);
494 
495   g_signal_emit (validate, gimp_tile_handler_validate_signals[INVALIDATED],
496                  0, rect, NULL);
497 }
498 
499 void
gimp_tile_handler_validate_undo_invalidate(GimpTileHandlerValidate * validate,const GeglRectangle * rect)500 gimp_tile_handler_validate_undo_invalidate (GimpTileHandlerValidate *validate,
501                                             const GeglRectangle     *rect)
502 {
503   g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate));
504   g_return_if_fail (rect != NULL);
505 
506   cairo_region_subtract_rectangle (validate->dirty_region,
507                                    (cairo_rectangle_int_t *) rect);
508 }
509 
510 void
gimp_tile_handler_validate_begin_validate(GimpTileHandlerValidate * validate)511 gimp_tile_handler_validate_begin_validate (GimpTileHandlerValidate *validate)
512 {
513   g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate));
514 
515   if (validate->validating++ == 0)
516     GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->begin_validate (validate);
517 }
518 
519 void
gimp_tile_handler_validate_end_validate(GimpTileHandlerValidate * validate)520 gimp_tile_handler_validate_end_validate (GimpTileHandlerValidate *validate)
521 {
522   g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate));
523   g_return_if_fail (validate->validating > 0);
524 
525   if (--validate->validating == 0)
526     GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate)->end_validate (validate);
527 }
528 
529 void
gimp_tile_handler_validate_validate(GimpTileHandlerValidate * validate,GeglBuffer * buffer,const GeglRectangle * rect,gboolean intersect,gboolean chunked)530 gimp_tile_handler_validate_validate (GimpTileHandlerValidate *validate,
531                                      GeglBuffer              *buffer,
532                                      const GeglRectangle     *rect,
533                                      gboolean                 intersect,
534                                      gboolean                 chunked)
535 {
536   GimpTileHandlerValidateClass *klass;
537   cairo_region_t               *region = NULL;
538 
539   g_return_if_fail (GIMP_IS_TILE_HANDLER_VALIDATE (validate));
540   g_return_if_fail (gimp_tile_handler_validate_get_assigned (buffer) ==
541                     validate);
542 
543   klass = GIMP_TILE_HANDLER_VALIDATE_GET_CLASS (validate);
544 
545   if (! rect)
546     rect = gegl_buffer_get_extent (buffer);
547 
548   if (intersect)
549     {
550       region = cairo_region_copy (validate->dirty_region);
551 
552       cairo_region_intersect_rectangle (region,
553                                         (const cairo_rectangle_int_t *) rect);
554     }
555   else if (chunked)
556     {
557       region = cairo_region_create_rectangle (
558         (const cairo_rectangle_int_t *) rect);
559     }
560 
561   if (region)
562     {
563       if (! cairo_region_is_empty (region))
564         {
565           gimp_tile_handler_validate_begin_validate (validate);
566 
567           if (chunked)
568             {
569               GimpChunkIterator *iter;
570 
571               iter   = gimp_chunk_iterator_new (region);
572               region = NULL;
573 
574               while (gimp_chunk_iterator_next (iter))
575                 {
576                   GeglRectangle blit_rect;
577 
578                   while (gimp_chunk_iterator_get_rect (iter, &blit_rect))
579                     klass->validate_buffer (validate, &blit_rect, buffer);
580                 }
581             }
582           else
583             {
584               gint n_rects;
585               gint i;
586 
587               n_rects = cairo_region_num_rectangles (region);
588 
589               for (i = 0; i < n_rects; i++)
590                 {
591                   cairo_rectangle_int_t blit_rect;
592 
593                   cairo_region_get_rectangle (region, i, &blit_rect);
594 
595                   klass->validate_buffer (validate,
596                                           (const GeglRectangle *) &blit_rect,
597                                           buffer);
598                 }
599               }
600 
601           gimp_tile_handler_validate_end_validate (validate);
602 
603           cairo_region_subtract_rectangle (
604             validate->dirty_region,
605             (const cairo_rectangle_int_t *) rect);
606         }
607 
608       g_clear_pointer (&region, cairo_region_destroy);
609     }
610   else
611     {
612       gimp_tile_handler_validate_begin_validate (validate);
613 
614       klass->validate_buffer (validate, rect, buffer);
615 
616       gimp_tile_handler_validate_end_validate (validate);
617 
618       cairo_region_subtract_rectangle (
619             validate->dirty_region,
620             (const cairo_rectangle_int_t *) rect);
621     }
622 }
623 
624 gboolean
gimp_tile_handler_validate_buffer_set_extent(GeglBuffer * buffer,const GeglRectangle * extent)625 gimp_tile_handler_validate_buffer_set_extent (GeglBuffer          *buffer,
626                                               const GeglRectangle *extent)
627 {
628   GimpTileHandlerValidate *validate;
629 
630   g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE);
631   g_return_val_if_fail (extent != NULL, FALSE);
632 
633   validate = gimp_tile_handler_validate_get_assigned (buffer);
634 
635   g_return_val_if_fail (validate != NULL, FALSE);
636 
637   validate->suspend_validate++;
638 
639   if (gimp_gegl_buffer_set_extent (buffer, extent))
640     {
641       validate->suspend_validate--;
642 
643       cairo_region_intersect_rectangle (validate->dirty_region,
644                                         (const cairo_rectangle_int_t *) extent);
645 
646       return TRUE;
647     }
648 
649   validate->suspend_validate--;
650 
651   return FALSE;
652 }
653 
654 void
gimp_tile_handler_validate_buffer_copy(GeglBuffer * src_buffer,const GeglRectangle * src_rect,GeglBuffer * dst_buffer,const GeglRectangle * dst_rect)655 gimp_tile_handler_validate_buffer_copy (GeglBuffer          *src_buffer,
656                                         const GeglRectangle *src_rect,
657                                         GeglBuffer          *dst_buffer,
658                                         const GeglRectangle *dst_rect)
659 {
660   GimpTileHandlerValidate *src_validate;
661   GimpTileHandlerValidate *dst_validate;
662   GeglRectangle            real_src_rect;
663   GeglRectangle            real_dst_rect;
664 
665   g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
666   g_return_if_fail (GEGL_IS_BUFFER (dst_buffer));
667   g_return_if_fail (src_rect != dst_rect);
668 
669   src_validate = gimp_tile_handler_validate_get_assigned (src_buffer);
670   dst_validate = gimp_tile_handler_validate_get_assigned (dst_buffer);
671 
672   g_return_if_fail (dst_validate != NULL);
673 
674   if (! src_rect)
675     src_rect = gegl_buffer_get_extent (src_buffer);
676 
677   if (! dst_rect)
678     dst_rect = src_rect;
679 
680   real_src_rect = *src_rect;
681 
682   gegl_rectangle_intersect (&real_dst_rect,
683                             dst_rect, gegl_buffer_get_extent (dst_buffer));
684 
685   real_src_rect.x      += real_dst_rect.x - dst_rect->x;
686   real_src_rect.y      += real_dst_rect.y - dst_rect->y;
687   real_src_rect.width  -= real_dst_rect.x - dst_rect->x;
688   real_src_rect.height -= real_dst_rect.y - dst_rect->y;
689 
690   real_src_rect.width  = CLAMP (real_src_rect.width,  0, real_dst_rect.width);
691   real_src_rect.height = CLAMP (real_src_rect.height, 0, real_dst_rect.height);
692 
693   /* temporarily remove the source buffer's validate handler, so that
694    * gegl_buffer_copy() can use fast tile copying, using the TILE_COPY command.
695    * currently, gegl only uses TILE_COPY when the source buffer has no user-
696    * provided tile handlers.
697    */
698   if (src_validate)
699     {
700       g_object_ref (src_validate);
701 
702       gimp_tile_handler_validate_unassign (src_validate, src_buffer);
703     }
704 
705   dst_validate->suspend_validate++;
706 
707   gimp_gegl_buffer_copy (src_buffer, &real_src_rect, GEGL_ABYSS_NONE,
708                          dst_buffer, &real_dst_rect);
709 
710   dst_validate->suspend_validate--;
711 
712   if (src_validate)
713     {
714       gimp_tile_handler_validate_assign (src_validate, src_buffer);
715 
716       g_object_unref (src_validate);
717     }
718 
719   cairo_region_subtract_rectangle (dst_validate->dirty_region,
720                                    (cairo_rectangle_int_t *) &real_dst_rect);
721 
722   if (src_validate)
723     {
724       if (real_src_rect.x == real_dst_rect.x &&
725           real_src_rect.y == real_dst_rect.y &&
726           gegl_rectangle_equal (&real_src_rect,
727                                 gegl_buffer_get_extent (src_buffer)))
728         {
729           cairo_region_union (dst_validate->dirty_region,
730                               src_validate->dirty_region);
731         }
732       else if (cairo_region_contains_rectangle (
733                  src_validate->dirty_region,
734                  (cairo_rectangle_int_t *) &real_src_rect) !=
735                CAIRO_REGION_OVERLAP_OUT)
736         {
737           cairo_region_t *region;
738 
739           region = cairo_region_copy (src_validate->dirty_region);
740 
741           if (! gegl_rectangle_equal (&real_src_rect,
742                                       gegl_buffer_get_extent (src_buffer)))
743             {
744               cairo_region_intersect_rectangle (
745                 region, (cairo_rectangle_int_t *) &real_src_rect);
746             }
747 
748           cairo_region_translate (region,
749                                   real_dst_rect.x - real_src_rect.x,
750                                   real_dst_rect.y - real_src_rect.y);
751 
752           if (cairo_region_is_empty (dst_validate->dirty_region))
753             {
754               cairo_region_destroy (dst_validate->dirty_region);
755 
756               dst_validate->dirty_region = region;
757             }
758           else
759             {
760               cairo_region_union (dst_validate->dirty_region, region);
761 
762               cairo_region_destroy (region);
763             }
764         }
765     }
766 }
767