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 (®ion, 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