1 /* This file is part of GEGL.
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2006-2012,2014-2017  Øyvind Kolås <pippin@gimp.org>
17  *           2013 Daniel Sabo
18  */
19 
20 #include "config.h"
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <math.h>
25 
26 #include <glib-object.h>
27 #include <glib/gprintf.h>
28 #include <gio/gio.h>
29 
30 #include <babl/babl.h>
31 #include "gegl-algorithms.h"
32 #include "gegl-buffer-types.h"
33 #include "gegl-buffer.h"
34 #include "gegl-buffer-private.h"
35 #include "gegl-tile-storage.h"
36 #include "gegl-tile-handler-empty.h"
37 #include "gegl-sampler.h"
38 #include "gegl-tile-backend.h"
39 #include "gegl-buffer-iterator.h"
40 #include "gegl-rectangle.h"
41 #include "gegl-buffer-iterator-private.h"
42 #include "gegl-buffer-formats.h"
43 
44 static void gegl_buffer_iterate_read_fringed (GeglBuffer          *buffer,
45                                               const GeglRectangle *roi,
46                                               const GeglRectangle *abyss,
47                                               guchar              *buf,
48                                               gint                 buf_stride,
49                                               const Babl          *format,
50                                               gint                 level,
51                                               GeglAbyssPolicy      repeat_mode);
52 
53 
54 static void gegl_buffer_iterate_read_dispatch (GeglBuffer          *buffer,
55                                                const GeglRectangle *roi,
56                                                guchar              *buf,
57                                                gint                 rowstride,
58                                                const Babl          *format,
59                                                gint                 level,
60                                                GeglAbyssPolicy      repeat_mode);
61 
62 static void inline
gegl_buffer_get_pixel(GeglBuffer * buffer,gint x,gint y,const Babl * format,gpointer data,GeglAbyssPolicy repeat_mode)63 gegl_buffer_get_pixel (GeglBuffer     *buffer,
64                        gint            x,
65                        gint            y,
66                        const Babl     *format,
67                        gpointer        data,
68                        GeglAbyssPolicy repeat_mode)
69 {
70   const GeglRectangle *abyss = &buffer->abyss;
71   guchar              *buf   = data;
72 
73   if (G_UNLIKELY (y <  abyss->y ||
74       x <  abyss->x ||
75       y >= abyss->y + abyss->height ||
76       x >= abyss->x + abyss->width))
77     {
78       switch (repeat_mode)
79       {
80         case GEGL_ABYSS_CLAMP:
81           x = CLAMP (x, abyss->x, abyss->x+abyss->width-1);
82           y = CLAMP (y, abyss->y, abyss->y+abyss->height-1);
83           break;
84 
85         case GEGL_ABYSS_LOOP:
86           x = abyss->x + GEGL_REMAINDER (x - abyss->x, abyss->width);
87           y = abyss->y + GEGL_REMAINDER (y - abyss->y, abyss->height);
88           break;
89 
90         case GEGL_ABYSS_BLACK:
91           {
92             gfloat color[4] = {0.0, 0.0, 0.0, 1.0};
93             babl_process (babl_fish (gegl_babl_rgba_linear_float (), format),
94                           color,
95                           buf,
96                           1);
97             return;
98           }
99 
100         case GEGL_ABYSS_WHITE:
101           {
102             gfloat color[4] = {1.0, 1.0, 1.0, 1.0};
103             babl_process (babl_fish (gegl_babl_rgba_linear_float (),
104                                      format),
105                           color,
106                           buf,
107                           1);
108             return;
109           }
110 
111         default:
112         case GEGL_ABYSS_NONE:
113           memset (buf, 0x00, babl_format_get_bytes_per_pixel (format));
114           return;
115       }
116     }
117 
118   {
119     gint tile_width  = buffer->tile_width;
120     gint tile_height = buffer->tile_height;
121     gint tiledy      = y + buffer->shift_y;
122     gint tiledx      = x + buffer->shift_x;
123     gint indice_x    = gegl_tile_indice (tiledx, tile_width);
124     gint indice_y    = gegl_tile_indice (tiledy, tile_height);
125 
126     GeglTile *tile = gegl_tile_storage_steal_hot_tile (buffer->tile_storage);
127 
128     if (!(tile &&
129           tile->x == indice_x &&
130           tile->y == indice_y))
131       {
132         g_rec_mutex_lock (&buffer->tile_storage->mutex);
133 
134         if (tile)
135           gegl_tile_unref (tile);
136 
137         tile = gegl_tile_source_get_tile ((GeglTileSource *) (buffer),
138                                           indice_x, indice_y,
139                                           0);
140 
141         g_rec_mutex_unlock (&buffer->tile_storage->mutex);
142       }
143 
144     if (tile)
145       {
146         gint tile_origin_x = indice_x * tile_width;
147         gint tile_origin_y = indice_y * tile_height;
148         gint       offsetx = tiledx - tile_origin_x;
149         gint       offsety = tiledy - tile_origin_y;
150         gint px_size = babl_format_get_bytes_per_pixel (buffer->soft_format);
151         guchar    *tp;
152 
153         gegl_tile_read_lock (tile);
154 
155         tp = gegl_tile_get_data (tile) +
156              (offsety * tile_width + offsetx) * px_size;
157 
158         if (G_UNLIKELY (format != buffer->soft_format))
159           {
160             babl_process (babl_fish (buffer->soft_format, format), tp, buf, 1);
161           }
162         else
163           {
164             memcpy (buf, tp, px_size);
165           }
166 
167         gegl_tile_read_unlock (tile);
168 
169         gegl_tile_storage_take_hot_tile (buffer->tile_storage, tile);
170       }
171   }
172 }
173 
174 static inline void
__gegl_buffer_set_pixel(GeglBuffer * buffer,gint x,gint y,const Babl * format,gconstpointer data)175 __gegl_buffer_set_pixel (GeglBuffer     *buffer,
176                        gint            x,
177                        gint            y,
178                        const Babl     *format,
179                        gconstpointer   data)
180 {
181   const GeglRectangle *abyss = &buffer->abyss;
182   const guchar        *buf   = data;
183 
184   if (G_UNLIKELY (y <  abyss->y ||
185       x <  abyss->x ||
186       y >= abyss->y + abyss->height ||
187       x >= abyss->x + abyss->width))
188     return;
189 
190 
191   {
192     gint tile_width  = buffer->tile_width;
193     gint tile_height = buffer->tile_height;
194     gint tiledy      = y + buffer->shift_y;
195     gint tiledx      = x + buffer->shift_x;
196     gint indice_x    = gegl_tile_indice (tiledx, tile_width);
197     gint indice_y    = gegl_tile_indice (tiledy, tile_height);
198 
199     GeglTile *tile = gegl_tile_storage_steal_hot_tile (buffer->tile_storage);
200     const Babl *fish = NULL;
201     gint px_size;
202 
203     if (G_UNLIKELY (format != buffer->soft_format))
204       {
205         fish    = babl_fish (format, buffer->soft_format);
206         px_size = babl_format_get_bytes_per_pixel (buffer->soft_format);
207       }
208     else
209       {
210         px_size = babl_format_get_bytes_per_pixel (buffer->soft_format);
211       }
212 
213     if (!(tile &&
214           tile->x == indice_x &&
215           tile->y == indice_y))
216       {
217         g_rec_mutex_lock (&buffer->tile_storage->mutex);
218 
219         if (tile)
220           gegl_tile_unref (tile);
221 
222         tile = gegl_tile_source_get_tile ((GeglTileSource *) (buffer),
223                                           indice_x, indice_y,
224                                           0);
225 
226         g_rec_mutex_unlock (&buffer->tile_storage->mutex);
227       }
228 
229     if (tile)
230       {
231         gint tile_origin_x = indice_x * tile_width;
232         gint tile_origin_y = indice_y * tile_height;
233         gint       offsetx = tiledx - tile_origin_x;
234         gint       offsety = tiledy - tile_origin_y;
235 
236         guchar *tp;
237         gegl_tile_lock (tile);
238         tp = gegl_tile_get_data (tile) + (offsety * tile_width + offsetx) * px_size;
239 
240         if (fish)
241           babl_process (fish, buf, tp, 1);
242         else
243           memcpy (tp, buf, px_size);
244 
245         gegl_tile_unlock (tile);
246 
247         gegl_tile_storage_take_hot_tile (buffer->tile_storage, tile);
248       }
249   }
250 
251 }
252 
253 enum _GeglBufferSetFlag {
254   GEGL_BUFFER_SET_FLAG_FAST   = 0,
255   GEGL_BUFFER_SET_FLAG_LOCK   = 1<<0,
256   GEGL_BUFFER_SET_FLAG_NOTIFY = 1<<1,
257   GEGL_BUFFER_SET_FLAG_FULL   = GEGL_BUFFER_SET_FLAG_LOCK |
258                                 GEGL_BUFFER_SET_FLAG_NOTIFY
259 };
260 
261 typedef enum _GeglBufferSetFlag GeglBufferSetFlag;
262 
263 void
264 gegl_buffer_set_with_flags (GeglBuffer          *buffer,
265                             const GeglRectangle *rect,
266                             gint                 level,
267                             const Babl          *format,
268                             const void          *src,
269                             gint                 rowstride,
270                             GeglBufferSetFlag    set_flags);
271 
272 
273 static inline void
_gegl_buffer_set_pixel(GeglBuffer * buffer,gint x,gint y,const Babl * format,gconstpointer data,GeglBufferSetFlag flags)274 _gegl_buffer_set_pixel (GeglBuffer       *buffer,
275                         gint              x,
276                         gint              y,
277                         const Babl       *format,
278                         gconstpointer     data,
279                         GeglBufferSetFlag flags)
280 {
281   GeglRectangle rect = {x,y,1,1};
282   switch (flags)
283   {
284     case GEGL_BUFFER_SET_FLAG_FAST:
285       __gegl_buffer_set_pixel (buffer, x, y, format, data);
286     break;
287     case GEGL_BUFFER_SET_FLAG_LOCK:
288       gegl_buffer_lock (buffer);
289       __gegl_buffer_set_pixel (buffer, x, y, format, data);
290       gegl_buffer_unlock (buffer);
291       break;
292     case GEGL_BUFFER_SET_FLAG_NOTIFY:
293       __gegl_buffer_set_pixel (buffer, x, y, format, data);
294       gegl_buffer_emit_changed_signal (buffer, &rect);
295       break;
296     case GEGL_BUFFER_SET_FLAG_LOCK|GEGL_BUFFER_SET_FLAG_NOTIFY:
297     default:
298       gegl_buffer_lock (buffer);
299       __gegl_buffer_set_pixel (buffer, x, y, format, data);
300       gegl_buffer_unlock (buffer);
301       gegl_buffer_emit_changed_signal (buffer, &rect);
302       break;
303   }
304 }
305 
306 /* flush any unwritten data (flushes the hot-cache of a single
307  * tile used by gegl_buffer_set for 1x1 pixel sized rectangles
308  */
309 void
gegl_buffer_flush(GeglBuffer * buffer)310 gegl_buffer_flush (GeglBuffer *buffer)
311 {
312   GeglTileBackend *backend;
313 
314   g_return_if_fail (GEGL_IS_BUFFER (buffer));
315   backend = gegl_buffer_backend (buffer);
316 
317   g_rec_mutex_lock (&buffer->tile_storage->mutex);
318 
319   _gegl_buffer_drop_hot_tile (buffer);
320 
321   if (backend)
322     gegl_tile_backend_set_extent (backend, &buffer->extent);
323 
324   gegl_tile_source_command (GEGL_TILE_SOURCE (buffer),
325                             GEGL_TILE_FLUSH, 0,0,0,NULL);
326 
327   g_rec_mutex_unlock (&buffer->tile_storage->mutex);
328 }
329 
330 void
gegl_buffer_flush_ext(GeglBuffer * buffer,const GeglRectangle * rect)331 gegl_buffer_flush_ext (GeglBuffer *buffer, const GeglRectangle *rect)
332 {
333   if (gegl_buffer_ext_flush)
334     gegl_buffer_ext_flush (buffer, rect);
335 }
336 
337 
338 
339 static inline void
gegl_buffer_iterate_write(GeglBuffer * buffer,const GeglRectangle * roi,guchar * buf,gint rowstride,const Babl * format,gint level)340 gegl_buffer_iterate_write (GeglBuffer          *buffer,
341                            const GeglRectangle *roi,
342                            guchar              *buf,
343                            gint                 rowstride,
344                            const Babl          *format,
345                            gint                 level)
346 {
347   gint tile_width  = buffer->tile_storage->tile_width;
348   gint tile_height = buffer->tile_storage->tile_height;
349   gint px_size     = babl_format_get_bytes_per_pixel (buffer->soft_format);
350   gint bpx_size    = babl_format_get_bytes_per_pixel (format);
351   gint tile_stride = px_size * tile_width;
352   gint buf_stride;
353   gint bufy        = 0;
354 
355   gint buffer_shift_x = buffer->shift_x;
356   gint buffer_shift_y = buffer->shift_y;
357 
358   gint width    = buffer->extent.width;
359   gint height   = buffer->extent.height;
360   gint buffer_x = buffer->extent.x + buffer_shift_x;
361   gint buffer_y = buffer->extent.y + buffer_shift_y;
362 
363   gint buffer_abyss_x = buffer->abyss.x + buffer_shift_x;
364   gint buffer_abyss_y = buffer->abyss.y + buffer_shift_y;
365   gint abyss_x_total  = buffer_abyss_x + buffer->abyss.width;
366   gint abyss_y_total  = buffer_abyss_y + buffer->abyss.height;
367   gint factor         = 1<<level;
368   const Babl *fish;
369   GeglRectangle scaled_rect;
370   if (G_UNLIKELY (level && roi))
371   {
372     scaled_rect = *roi;
373     scaled_rect.x <<= level;
374     scaled_rect.y <<= level;
375     scaled_rect.width <<= level;
376     scaled_rect.height <<= level;
377     roi = &scaled_rect;
378   }
379 
380   /* roi specified, override buffers extent */
381   if (roi)
382     {
383       width  = roi->width;
384       height = roi->height;
385       buffer_x = roi->x + buffer_shift_x;
386       buffer_y = roi->y + buffer_shift_y;
387     }
388 
389   buffer_shift_x /= factor;
390   buffer_shift_y /= factor;
391   buffer_abyss_x /= factor;
392   buffer_abyss_y /= factor;
393   abyss_x_total  /= factor;
394   abyss_y_total  /= factor;
395   buffer_x       /= factor;
396   buffer_y       /= factor;
397   width          /= factor;
398   height         /= factor;
399 
400 
401   buf_stride = width * bpx_size;
402   if (rowstride != GEGL_AUTO_ROWSTRIDE)
403     buf_stride = rowstride;
404 
405   if (G_LIKELY (format == buffer->soft_format))
406     {
407       fish = NULL;
408     }
409   else
410       fish = babl_fish ((gpointer) format,
411                         (gpointer) buffer->soft_format);
412 
413   while (bufy < height)
414     {
415       gint tiledy  = buffer_y + bufy;
416       gint offsety = gegl_tile_offset (tiledy, tile_height);
417       gint bufx    = 0;
418 
419       while (bufx < width)
420         {
421           gint      tiledx  = buffer_x + bufx;
422           gint      offsetx = gegl_tile_offset (tiledx, tile_width);
423           gint      y       = bufy;
424           gint      index_x;
425           gint      index_y;
426           gint      lskip, rskip, pixels, row;
427           guchar   *bp, *tile_base, *tp;
428           GeglTile *tile;
429           gboolean  whole_tile;
430 
431           bp = buf + (gsize) bufy * buf_stride + bufx * bpx_size;
432 
433           if (G_UNLIKELY (width + offsetx - bufx < tile_width))
434             pixels = (width + offsetx - bufx) - offsetx;
435           else
436             pixels = tile_width - offsetx;
437 
438           index_x = gegl_tile_indice (tiledx, tile_width);
439           index_y = gegl_tile_indice (tiledy, tile_height);
440 
441           lskip = (buffer_abyss_x) - (buffer_x + bufx);
442           /* gap between left side of tile, and abyss */
443           rskip = (buffer_x + bufx + pixels) - abyss_x_total;
444           /* gap between right side of tile, and abyss */
445 
446           if (G_UNLIKELY (lskip < 0))
447             lskip = 0;
448           if (G_UNLIKELY (lskip > pixels))
449             lskip = pixels;
450           if (G_UNLIKELY (rskip < 0))
451             rskip = 0;
452           if (G_UNLIKELY (rskip > pixels))
453             rskip = pixels;
454 
455           pixels -= lskip;
456           pixels -= rskip;
457 
458           whole_tile = pixels == tile_width && bufy >= buffer_abyss_y &&
459                        MIN (MIN (height - bufy, tile_height - offsety),
460                             abyss_y_total - bufy) == tile_height;
461 
462           g_rec_mutex_lock (&buffer->tile_storage->mutex);
463 
464           tile = gegl_tile_handler_get_tile ((GeglTileHandler *) buffer,
465                                              index_x, index_y, level,
466                                              ! whole_tile);
467 
468           g_rec_mutex_unlock (&buffer->tile_storage->mutex);
469 
470           if (!tile)
471             {
472               g_warning ("didn't get tile, trying to continue");
473               bufx += (tile_width - offsetx);
474               continue;
475             }
476 
477           gegl_tile_lock (tile);
478 
479           tile_base = gegl_tile_get_data (tile);
480           tp        = ((guchar *) tile_base) + (offsety * tile_width + offsetx) * px_size;
481 
482           if (G_UNLIKELY (fish))
483             {
484               int bskip, skip = 0;
485               int rows = MIN(height - bufy, tile_height - offsety);
486 
487               bskip = (buffer_y + bufy + rows) - abyss_y_total;
488               bskip = CLAMP (bskip, 0, rows);
489               rows -= bskip;
490 
491 /*
492 XXX XXX XXX
493 Making the abyss geometry handling work with babl_process_rows is proving a bit
494 tricky - the choice that skips the skipping at the start of a batch entering a
495 tile seem to let all expected code paths work and no reported non-working code
496 paths yet. This will also be a slight performance loss - but it might be that
497 we seldom do writes that fall in the abyss anyways.
498 
499 Not writing into the abyss will permit better control over sliced rendering
500 with multi-threading - and should be added back later.
501 
502 */
503 
504 #if 0
505               skip = buffer_abyss_y - bufy;
506               if (skip < 0)
507                 skip = 0;
508               rows-=skip;
509 #endif
510               if (rows==1)
511                 babl_process (fish,bp + lskip * bpx_size + skip * buf_stride, tp + lskip * px_size + skip * tile_stride, pixels);
512               else if (rows>0)
513                 babl_process_rows (fish,
514                                    bp + lskip * bpx_size + skip * buf_stride,
515                                    buf_stride,
516                                    tp + lskip * px_size + skip * tile_stride,
517                                    tile_stride,
518                                    pixels,
519                                    rows);
520             }
521           else
522             {
523               int lskip_offset = lskip * px_size;
524 
525               #ifdef __GNUC__
526               #define CHECK_ALIGNMENT_ALIGNOF __alignof__
527               #else
528               #define CHECK_ALIGNMENT_ALIGNOF(type) \
529                 (G_STRUCT_OFFSET (struct alignof_helper, b))
530               #endif
531               #define CHECK_ALIGNMENT(type)                                    \
532                 do                                                             \
533                   {                                                            \
534                     struct alignof_helper { char a; type b; };                 \
535                     /* verify 'alignof (type)' is a power of 2 */              \
536                     G_STATIC_ASSERT (! (CHECK_ALIGNMENT_ALIGNOF (type) &       \
537                                         (CHECK_ALIGNMENT_ALIGNOF (type) - 1)));\
538                     if ((((uintptr_t) tp + lskip_offset) |                     \
539                          ((uintptr_t) bp + lskip_offset) |                     \
540                          tile_stride                     |                     \
541                          buf_stride) &                                         \
542                         (CHECK_ALIGNMENT_ALIGNOF (type) - 1))                  \
543                       {                                                        \
544                         goto unaligned;                                        \
545                       }                                                        \
546                   }                                                            \
547                 while (FALSE)
548 
549               switch (pixels * px_size)
550                 {
551                   case 1:
552                     CHECK_ALIGNMENT (guchar);
553                     for (row = offsety;
554                          row < tile_height && y < height;
555                          row++, y++)
556                     {
557                       if (buffer_y + y >= buffer_abyss_y &&
558                           buffer_y + y < abyss_y_total)
559                         {
560                           tp[lskip_offset] = bp[lskip_offset];
561                         }
562                       tp += tile_stride;
563                       bp += buf_stride;
564                     }
565                     break;
566                   case 2:
567                     CHECK_ALIGNMENT (uint16_t);
568                     for (row = offsety;
569                          row < tile_height && y < height;
570                          row++, y++)
571                     {
572                       if (buffer_y + y >= buffer_abyss_y &&
573                           buffer_y + y < abyss_y_total)
574                         {
575                           ((uint16_t*)(&tp[lskip_offset]))[0] =
576                           ((uint16_t*)(&bp[lskip_offset]))[0];
577                         }
578                       tp += tile_stride;
579                       bp += buf_stride;
580                     }
581                     break;
582                   case 3:
583                     CHECK_ALIGNMENT (guchar);
584                     for (row = offsety;
585                          row < tile_height && y < height;
586                          row++, y++)
587                     {
588                       if (buffer_y + y >= buffer_abyss_y &&
589                           buffer_y + y < abyss_y_total)
590                         {
591                           tp[lskip_offset] = bp[lskip_offset];
592                           tp[lskip_offset+1] = bp[lskip_offset+1];
593                           tp[lskip_offset+2] = bp[lskip_offset+2];
594                         }
595                       tp += tile_stride;
596                       bp += buf_stride;
597                     }
598                     break;
599                   case 4:
600                     CHECK_ALIGNMENT (uint32_t);
601                     for (row = offsety;
602                          row < tile_height && y < height;
603                          row++, y++)
604                     {
605                       if (buffer_y + y >= buffer_abyss_y &&
606                           buffer_y + y < abyss_y_total)
607                         {
608                           ((uint32_t*)(&tp[lskip_offset]))[0] =
609                           ((uint32_t*)(&bp[lskip_offset]))[0];
610                         }
611                       tp += tile_stride;
612                       bp += buf_stride;
613                     }
614                     break;
615                   case 6:
616                     CHECK_ALIGNMENT (uint16_t);
617                     for (row = offsety;
618                          row < tile_height && y < height;
619                          row++, y++)
620                     {
621                       if (buffer_y + y >= buffer_abyss_y &&
622                           buffer_y + y < abyss_y_total)
623                         {
624                           ((uint16_t*)(&tp[lskip_offset]))[0] =
625                           ((uint16_t*)(&bp[lskip_offset]))[0];
626                           ((uint16_t*)(&tp[lskip_offset]))[1] =
627                           ((uint16_t*)(&bp[lskip_offset]))[1];
628                           ((uint16_t*)(&tp[lskip_offset]))[2] =
629                           ((uint16_t*)(&bp[lskip_offset]))[2];
630                         }
631                       tp += tile_stride;
632                       bp += buf_stride;
633                     }
634                     break;
635                   case 8:
636                     CHECK_ALIGNMENT (uint64_t);
637                     for (row = offsety;
638                          row < tile_height && y < height;
639                          row++, y++)
640                     {
641                       if (buffer_y + y >= buffer_abyss_y &&
642                           buffer_y + y < abyss_y_total)
643                         {
644                           ((uint64_t*)(&tp[lskip_offset]))[0] =
645                           ((uint64_t*)(&bp[lskip_offset]))[0];
646                         }
647                       tp += tile_stride;
648                       bp += buf_stride;
649                     }
650                     break;
651                   case 12:
652                     CHECK_ALIGNMENT (uint32_t);
653                     for (row = offsety;
654                          row < tile_height && y < height;
655                          row++, y++)
656                     {
657                       if (buffer_y + y >= buffer_abyss_y &&
658                           buffer_y + y < abyss_y_total)
659                         {
660                           ((uint32_t*)(&tp[lskip_offset]))[0] =
661                           ((uint32_t*)(&bp[lskip_offset]))[0];
662                           ((uint32_t*)(&tp[lskip_offset]))[1] =
663                           ((uint32_t*)(&bp[lskip_offset]))[1];
664                           ((uint32_t*)(&tp[lskip_offset]))[2] =
665                           ((uint32_t*)(&bp[lskip_offset]))[2];
666                         }
667                       tp += tile_stride;
668                       bp += buf_stride;
669                     }
670                     break;
671                   case 16:
672                     CHECK_ALIGNMENT (uint64_t);
673                     for (row = offsety;
674                          row < tile_height && y < height;
675                          row++, y++)
676                     {
677                       if (buffer_y + y >= buffer_abyss_y &&
678                           buffer_y + y < abyss_y_total)
679                         {
680                           ((uint64_t*)(&tp[lskip_offset]))[0] =
681                           ((uint64_t*)(&bp[lskip_offset]))[0];
682                           ((uint64_t*)(&tp[lskip_offset]))[1] =
683                           ((uint64_t*)(&bp[lskip_offset]))[1];
684                         }
685                       tp += tile_stride;
686                       bp += buf_stride;
687                     }
688                     break;
689                   case 24:
690                     CHECK_ALIGNMENT (uint64_t);
691                     for (row = offsety;
692                          row < tile_height && y < height;
693                          row++, y++)
694                     {
695                       if (buffer_y + y >= buffer_abyss_y &&
696                           buffer_y + y < abyss_y_total)
697                         {
698                           ((uint64_t*)(&tp[lskip_offset]))[0] =
699                           ((uint64_t*)(&bp[lskip_offset]))[0];
700                           ((uint64_t*)(&tp[lskip_offset]))[1] =
701                           ((uint64_t*)(&bp[lskip_offset]))[1];
702                           ((uint64_t*)(&tp[lskip_offset]))[2] =
703                           ((uint64_t*)(&bp[lskip_offset]))[2];
704                         }
705                       tp += tile_stride;
706                       bp += buf_stride;
707                     }
708                     break;
709                   case 32:
710                     CHECK_ALIGNMENT (uint64_t);
711                     for (row = offsety;
712                          row < tile_height && y < height;
713                          row++, y++)
714                     {
715                       if (buffer_y + y >= buffer_abyss_y &&
716                           buffer_y + y < abyss_y_total)
717                         {
718                           ((uint64_t*)(&tp[lskip_offset]))[0] =
719                           ((uint64_t*)(&bp[lskip_offset]))[0];
720                           ((uint64_t*)(&tp[lskip_offset]))[1] =
721                           ((uint64_t*)(&bp[lskip_offset]))[1];
722                           ((uint64_t*)(&tp[lskip_offset]))[2] =
723                           ((uint64_t*)(&bp[lskip_offset]))[2];
724                           ((uint64_t*)(&tp[lskip_offset]))[3] =
725                           ((uint64_t*)(&bp[lskip_offset]))[3];
726                         }
727                       tp += tile_stride;
728                       bp += buf_stride;
729                     }
730                     break;
731                   case 40:
732                     CHECK_ALIGNMENT (uint64_t);
733                     for (row = offsety;
734                          row < tile_height && y < height;
735                          row++, y++)
736                     {
737                       if (buffer_y + y >= buffer_abyss_y &&
738                           buffer_y + y < abyss_y_total)
739                         {
740                           ((uint64_t*)(&tp[lskip_offset]))[0] =
741                           ((uint64_t*)(&bp[lskip_offset]))[0];
742                           ((uint64_t*)(&tp[lskip_offset]))[1] =
743                           ((uint64_t*)(&bp[lskip_offset]))[1];
744                           ((uint64_t*)(&tp[lskip_offset]))[2] =
745                           ((uint64_t*)(&bp[lskip_offset]))[2];
746                           ((uint64_t*)(&tp[lskip_offset]))[3] =
747                           ((uint64_t*)(&bp[lskip_offset]))[3];
748                           ((uint64_t*)(&tp[lskip_offset]))[4] =
749                           ((uint64_t*)(&bp[lskip_offset]))[4];
750                         }
751                       tp += tile_stride;
752                       bp += buf_stride;
753                     }
754                     break;
755                   case 48:
756                     CHECK_ALIGNMENT (uint64_t);
757                     for (row = offsety;
758                          row < tile_height && y < height;
759                          row++, y++)
760                     {
761                       if (buffer_y + y >= buffer_abyss_y &&
762                           buffer_y + y < abyss_y_total)
763                         {
764                           ((uint64_t*)(&tp[lskip_offset]))[0] =
765                           ((uint64_t*)(&bp[lskip_offset]))[0];
766                           ((uint64_t*)(&tp[lskip_offset]))[1] =
767                           ((uint64_t*)(&bp[lskip_offset]))[1];
768                           ((uint64_t*)(&tp[lskip_offset]))[2] =
769                           ((uint64_t*)(&bp[lskip_offset]))[2];
770                           ((uint64_t*)(&tp[lskip_offset]))[3] =
771                           ((uint64_t*)(&bp[lskip_offset]))[3];
772                           ((uint64_t*)(&tp[lskip_offset]))[4] =
773                           ((uint64_t*)(&bp[lskip_offset]))[4];
774                           ((uint64_t*)(&tp[lskip_offset]))[5] =
775                           ((uint64_t*)(&bp[lskip_offset]))[5];
776                         }
777                       tp += tile_stride;
778                       bp += buf_stride;
779                     }
780                     break;
781                   case 56:
782                     CHECK_ALIGNMENT (uint64_t);
783                     for (row = offsety;
784                          row < tile_height && y < height;
785                          row++, y++)
786                     {
787                       if (buffer_y + y >= buffer_abyss_y &&
788                           buffer_y + y < abyss_y_total)
789                         {
790                           ((uint64_t*)(&tp[lskip_offset]))[0] =
791                           ((uint64_t*)(&bp[lskip_offset]))[0];
792                           ((uint64_t*)(&tp[lskip_offset]))[1] =
793                           ((uint64_t*)(&bp[lskip_offset]))[1];
794                           ((uint64_t*)(&tp[lskip_offset]))[2] =
795                           ((uint64_t*)(&bp[lskip_offset]))[2];
796                           ((uint64_t*)(&tp[lskip_offset]))[3] =
797                           ((uint64_t*)(&bp[lskip_offset]))[3];
798                           ((uint64_t*)(&tp[lskip_offset]))[4] =
799                           ((uint64_t*)(&bp[lskip_offset]))[4];
800                           ((uint64_t*)(&tp[lskip_offset]))[5] =
801                           ((uint64_t*)(&bp[lskip_offset]))[5];
802                           ((uint64_t*)(&tp[lskip_offset]))[6] =
803                           ((uint64_t*)(&bp[lskip_offset]))[6];
804                         }
805                       tp += tile_stride;
806                       bp += buf_stride;
807                     }
808                     break;
809                   case 64:
810                     CHECK_ALIGNMENT (uint64_t);
811                     for (row = offsety;
812                          row < tile_height && y < height;
813                          row++, y++)
814                     {
815                       if (buffer_y + y >= buffer_abyss_y &&
816                           buffer_y + y < abyss_y_total)
817                         {
818                           ((uint64_t*)(&tp[lskip_offset]))[0] =
819                           ((uint64_t*)(&bp[lskip_offset]))[0];
820                           ((uint64_t*)(&tp[lskip_offset]))[1] =
821                           ((uint64_t*)(&bp[lskip_offset]))[1];
822                           ((uint64_t*)(&tp[lskip_offset]))[2] =
823                           ((uint64_t*)(&bp[lskip_offset]))[2];
824                           ((uint64_t*)(&tp[lskip_offset]))[3] =
825                           ((uint64_t*)(&bp[lskip_offset]))[3];
826                           ((uint64_t*)(&tp[lskip_offset]))[4] =
827                           ((uint64_t*)(&bp[lskip_offset]))[4];
828                           ((uint64_t*)(&tp[lskip_offset]))[5] =
829                           ((uint64_t*)(&bp[lskip_offset]))[5];
830                           ((uint64_t*)(&tp[lskip_offset]))[6] =
831                           ((uint64_t*)(&bp[lskip_offset]))[6];
832                           ((uint64_t*)(&tp[lskip_offset]))[7] =
833                           ((uint64_t*)(&bp[lskip_offset]))[7];
834                         }
835                       tp += tile_stride;
836                       bp += buf_stride;
837                     }
838                     break;
839                   default:
840                   unaligned:
841                     for (row = offsety;
842                          row < tile_height && y < height;
843                          row++, y++)
844                     {
845                       if (buffer_y + y >= buffer_abyss_y &&
846                           buffer_y + y < abyss_y_total)
847                         {
848                           memcpy (tp + lskip_offset,
849                                   bp + lskip_offset,
850                                   pixels * px_size);
851                         }
852                       tp += tile_stride;
853                       bp += buf_stride;
854                     }
855                 }
856 
857               #undef CHECK_ALIGNMENT
858               #undef CHECK_ALIGNMENT_ALIGNOF
859             }
860 
861           gegl_tile_unlock_no_void (tile);
862           gegl_tile_unref (tile);
863           bufx += (tile_width - offsetx);
864         }
865       bufy += (tile_height - offsety);
866     }
867 
868   if (level == 0)
869     {
870       gegl_tile_handler_damage_rect (GEGL_TILE_HANDLER (buffer->tile_storage),
871                                      GEGL_RECTANGLE (buffer_x, buffer_y,
872                                                      width,    height));
873     }
874 }
875 
876 static inline void
gegl_buffer_set_internal(GeglBuffer * buffer,const GeglRectangle * rect,gint level,const Babl * format,const void * src,gint rowstride)877 gegl_buffer_set_internal (GeglBuffer          *buffer,
878                           const GeglRectangle *rect,
879                           gint                 level,
880                           const Babl          *format,
881                           const void          *src,
882                           gint                 rowstride)
883 {
884   if (gegl_buffer_ext_flush)
885     {
886       gegl_buffer_ext_flush (buffer, rect);
887     }
888 
889   gegl_buffer_iterate_write (buffer, rect, (void *) src, rowstride, format, level);
890 
891   if (G_UNLIKELY (gegl_buffer_is_shared (buffer)))
892     {
893       gegl_buffer_flush (buffer);
894     }
895 }
896 
897 static void inline
_gegl_buffer_set_with_flags(GeglBuffer * buffer,const GeglRectangle * rect,gint level,const Babl * format,const void * src,gint rowstride,GeglBufferSetFlag flags)898 _gegl_buffer_set_with_flags (GeglBuffer       *buffer,
899                             const GeglRectangle *rect,
900                             gint                 level,
901                             const Babl          *format,
902                             const void          *src,
903                             gint                 rowstride,
904                             GeglBufferSetFlag    flags)
905 {
906   switch (flags)
907   {
908     case GEGL_BUFFER_SET_FLAG_FAST:
909       gegl_buffer_set_internal (buffer, rect, level, format, src, rowstride);
910     break;
911     case GEGL_BUFFER_SET_FLAG_LOCK:
912       gegl_buffer_lock (buffer);
913       gegl_buffer_set_internal (buffer, rect, level, format, src, rowstride);
914       gegl_buffer_unlock (buffer);
915       break;
916     case GEGL_BUFFER_SET_FLAG_NOTIFY:
917       gegl_buffer_set_internal (buffer, rect, level, format, src, rowstride);
918       if (flags & GEGL_BUFFER_SET_FLAG_NOTIFY)
919         gegl_buffer_emit_changed_signal(buffer, rect);
920       break;
921     case GEGL_BUFFER_SET_FLAG_LOCK|GEGL_BUFFER_SET_FLAG_NOTIFY:
922     default:
923       gegl_buffer_lock (buffer);
924       gegl_buffer_set_internal (buffer, rect, level, format, src, rowstride);
925       gegl_buffer_unlock (buffer);
926       gegl_buffer_emit_changed_signal(buffer, rect);
927       break;
928   }
929 }
930 
931 void
gegl_buffer_set_with_flags(GeglBuffer * buffer,const GeglRectangle * rect,gint level,const Babl * format,const void * src,gint rowstride,GeglBufferSetFlag flags)932 gegl_buffer_set_with_flags (GeglBuffer       *buffer,
933                             const GeglRectangle *rect,
934                             gint                 level,
935                             const Babl          *format,
936                             const void          *src,
937                             gint                 rowstride,
938                             GeglBufferSetFlag    flags)
939 {
940   g_return_if_fail (GEGL_IS_BUFFER (buffer));
941   if (format == NULL)
942     format = buffer->soft_format;
943   _gegl_buffer_set_with_flags (buffer, rect, level, format, src, rowstride, flags);
944 }
945 
946 static void
gegl_buffer_iterate_read_simple(GeglBuffer * buffer,const GeglRectangle * roi,guchar * buf,gint buf_stride,const Babl * format,gint level)947 gegl_buffer_iterate_read_simple (GeglBuffer          *buffer,
948                                  const GeglRectangle *roi,
949                                  guchar              *buf,
950                                  gint                 buf_stride,
951                                  const Babl          *format,
952                                  gint                 level)
953 {
954   gint tile_width  = buffer->tile_storage->tile_width;
955   gint tile_height = buffer->tile_storage->tile_height;
956   gint px_size     = babl_format_get_bytes_per_pixel (buffer->soft_format);
957   gint bpx_size    = babl_format_get_bytes_per_pixel (format);
958   gint tile_stride = px_size * tile_width;
959   gint bufy        = 0;
960 
961   gint width    = roi->width;
962   gint height   = roi->height;
963   gint buffer_x = roi->x;
964   gint buffer_y = roi->y;
965 
966   const Babl *fish;
967 
968   if (G_LIKELY (format == buffer->soft_format))
969     fish = NULL;
970   else
971     fish = babl_fish ((gpointer) buffer->soft_format,
972                       (gpointer) format);
973 
974   while (bufy < height)
975     {
976       gint tiledy  = buffer_y + bufy;
977       gint offsety = gegl_tile_offset (tiledy, tile_height);
978       gint bufx    = 0;
979 
980       while (bufx < width)
981         {
982           gint      tiledx  = buffer_x + bufx;
983           gint      offsetx = gegl_tile_offset (tiledx, tile_width);
984           guchar   *bp, *tile_base, *tp;
985           gint      pixels, row, y;
986           GeglTile *tile;
987 
988           bp = buf + (gsize) bufy * buf_stride + bufx * bpx_size;
989 
990           if (G_LIKELY (width + offsetx - bufx < tile_width))
991             pixels = width - bufx;
992           else
993             pixels = tile_width - offsetx;
994 
995           g_rec_mutex_lock (&buffer->tile_storage->mutex);
996           tile = gegl_tile_source_get_tile ((GeglTileSource *) (buffer),
997                                           gegl_tile_indice (tiledx, tile_width),
998                                           gegl_tile_indice (tiledy, tile_height),
999                                           level);
1000           g_rec_mutex_unlock (&buffer->tile_storage->mutex);
1001 
1002           if (!tile)
1003             {
1004               g_warning ("didn't get tile, trying to continue");
1005               bufx += (tile_width - offsetx);
1006               continue;
1007             }
1008 
1009           gegl_tile_read_lock (tile);
1010 
1011           tile_base = gegl_tile_get_data (tile);
1012           tp        = ((guchar *) tile_base) + (offsety * tile_width + offsetx) * px_size;
1013 
1014           y = bufy;
1015 
1016           if (G_UNLIKELY (fish))
1017             {
1018               int rows = MIN(height - bufy, tile_height - offsety);
1019               if (rows == 1)
1020               babl_process (fish,
1021                             tp,
1022                             bp,
1023                             pixels);
1024               else
1025               babl_process_rows (fish,
1026                                  tp,
1027                                  tile_stride,
1028                                  bp,
1029                                  buf_stride,
1030                                  pixels,
1031                                  rows);
1032 
1033             }
1034           else
1035             {
1036               #ifdef __GNUC__
1037               #define CHECK_ALIGNMENT_ALIGNOF __alignof__
1038               #else
1039               #define CHECK_ALIGNMENT_ALIGNOF(type) \
1040                 (G_STRUCT_OFFSET (struct alignof_helper, b))
1041               #endif
1042               #define CHECK_ALIGNMENT(type)                                    \
1043                 do                                                             \
1044                   {                                                            \
1045                     struct alignof_helper { char a; type b; };                 \
1046                     /* verify 'alignof (type)' is a power of 2 */              \
1047                     G_STATIC_ASSERT (! (CHECK_ALIGNMENT_ALIGNOF (type) &       \
1048                                         (CHECK_ALIGNMENT_ALIGNOF (type) - 1)));\
1049                     if (((uintptr_t) tp |                                      \
1050                          (uintptr_t) bp |                                      \
1051                          tile_stride    |                                      \
1052                          buf_stride) &                                         \
1053                         (CHECK_ALIGNMENT_ALIGNOF (type) - 1))                  \
1054                       {                                                        \
1055                         goto unaligned;                                        \
1056                       }                                                        \
1057                   }                                                            \
1058                 while (FALSE)
1059 
1060               switch (pixels * px_size)
1061                 {
1062                   case 1:
1063                     CHECK_ALIGNMENT (guchar);
1064                     for (row = offsety; row < tile_height && y < height;
1065                          row++, y++)
1066                       {
1067                          bp[0] = tp[0];
1068                          tp += tile_stride;
1069                          bp += buf_stride;
1070                       }
1071                     break;
1072                   case 2:
1073                     CHECK_ALIGNMENT (uint16_t);
1074                     for (row = offsety; row < tile_height && y < height;
1075                          row++, y++)
1076                       {
1077                          ((uint16_t*)bp)[0] = ((uint16_t*)tp)[0];
1078                          tp += tile_stride;
1079                          bp += buf_stride;
1080                       }
1081                     break;
1082                   case 3:
1083                     CHECK_ALIGNMENT (guchar);
1084                     for (row = offsety; row < tile_height && y < height;
1085                          row++, y++)
1086                       {
1087                          bp[0] = tp[0];
1088                          bp[1] = tp[1];
1089                          bp[2] = tp[2];
1090                          tp += tile_stride;
1091                          bp += buf_stride;
1092                       }
1093                     break;
1094                   case 4:
1095                     CHECK_ALIGNMENT (uint32_t);
1096                     for (row = offsety; row < tile_height && y < height;
1097                          row++, y++)
1098                       {
1099                          ((uint32_t*)bp)[0] = ((uint32_t*)tp)[0];
1100                          tp += tile_stride;
1101                          bp += buf_stride;
1102                       }
1103                     break;
1104                   case 6:
1105                     CHECK_ALIGNMENT (uint16_t);
1106                     for (row = offsety; row < tile_height && y < height;
1107                          row++, y++)
1108                       {
1109                          ((uint16_t*)bp)[0] = ((uint16_t*)tp)[0];
1110                          ((uint16_t*)bp)[1] = ((uint16_t*)tp)[1];
1111                          ((uint16_t*)bp)[2] = ((uint16_t*)tp)[2];
1112                          tp += tile_stride;
1113                          bp += buf_stride;
1114                       }
1115                     break;
1116                   case 8:
1117                     CHECK_ALIGNMENT (uint64_t);
1118                     for (row = offsety; row < tile_height && y < height;
1119                          row++, y++)
1120                       {
1121                          ((uint64_t*)bp)[0] = ((uint64_t*)tp)[0];
1122                          tp += tile_stride;
1123                          bp += buf_stride;
1124                       }
1125                     break;
1126                   case 12:
1127                     CHECK_ALIGNMENT (uint32_t);
1128                     for (row = offsety; row < tile_height && y < height;
1129                          row++, y++)
1130                       {
1131                          ((uint32_t*)bp)[0] = ((uint32_t*)tp)[0];
1132                          ((uint32_t*)bp)[1] = ((uint32_t*)tp)[1];
1133                          ((uint32_t*)bp)[2] = ((uint32_t*)tp)[2];
1134                          tp += tile_stride;
1135                          bp += buf_stride;
1136                       }
1137                     break;
1138                   case 16:
1139                     CHECK_ALIGNMENT (uint64_t);
1140                     for (row = offsety; row < tile_height && y < height;
1141                          row++, y++)
1142                       {
1143                          ((uint64_t*)bp)[0] = ((uint64_t*)tp)[0];
1144                          ((uint64_t*)bp)[1] = ((uint64_t*)tp)[1];
1145                          tp += tile_stride;
1146                          bp += buf_stride;
1147                       }
1148                     break;
1149                   case 24:
1150                     CHECK_ALIGNMENT (uint64_t);
1151                     for (row = offsety; row < tile_height && y < height;
1152                          row++, y++)
1153                       {
1154                          ((uint64_t*)bp)[0] = ((uint64_t*)tp)[0];
1155                          ((uint64_t*)bp)[1] = ((uint64_t*)tp)[1];
1156                          ((uint64_t*)bp)[2] = ((uint64_t*)tp)[2];
1157                          tp += tile_stride;
1158                          bp += buf_stride;
1159                       }
1160                     break;
1161                   case 32:
1162                     CHECK_ALIGNMENT (uint64_t);
1163                     for (row = offsety; row < tile_height && y < height;
1164                          row++, y++)
1165                       {
1166                          ((uint64_t*)bp)[0] = ((uint64_t*)tp)[0];
1167                          ((uint64_t*)bp)[1] = ((uint64_t*)tp)[1];
1168                          ((uint64_t*)bp)[2] = ((uint64_t*)tp)[2];
1169                          ((uint64_t*)bp)[3] = ((uint64_t*)tp)[3];
1170                          tp += tile_stride;
1171                          bp += buf_stride;
1172                       }
1173                     break;
1174                   case 40:
1175                     CHECK_ALIGNMENT (uint64_t);
1176                     for (row = offsety; row < tile_height && y < height;
1177                          row++, y++)
1178                       {
1179                          ((uint64_t*)bp)[0] = ((uint64_t*)tp)[0];
1180                          ((uint64_t*)bp)[1] = ((uint64_t*)tp)[1];
1181                          ((uint64_t*)bp)[2] = ((uint64_t*)tp)[2];
1182                          ((uint64_t*)bp)[3] = ((uint64_t*)tp)[3];
1183                          ((uint64_t*)bp)[4] = ((uint64_t*)tp)[4];
1184                          tp += tile_stride;
1185                          bp += buf_stride;
1186                       }
1187                     break;
1188                   case 48:
1189                     CHECK_ALIGNMENT (uint64_t);
1190                     for (row = offsety; row < tile_height && y < height;
1191                          row++, y++)
1192                       {
1193                          ((uint64_t*)bp)[0] = ((uint64_t*)tp)[0];
1194                          ((uint64_t*)bp)[1] = ((uint64_t*)tp)[1];
1195                          ((uint64_t*)bp)[2] = ((uint64_t*)tp)[2];
1196                          ((uint64_t*)bp)[3] = ((uint64_t*)tp)[3];
1197                          ((uint64_t*)bp)[4] = ((uint64_t*)tp)[4];
1198                          ((uint64_t*)bp)[5] = ((uint64_t*)tp)[5];
1199                          tp += tile_stride;
1200                          bp += buf_stride;
1201                       }
1202                     break;
1203                   case 56:
1204                     CHECK_ALIGNMENT (uint64_t);
1205                     for (row = offsety; row < tile_height && y < height;
1206                          row++, y++)
1207                       {
1208                          ((uint64_t*)bp)[0] = ((uint64_t*)tp)[0];
1209                          ((uint64_t*)bp)[1] = ((uint64_t*)tp)[1];
1210                          ((uint64_t*)bp)[2] = ((uint64_t*)tp)[2];
1211                          ((uint64_t*)bp)[3] = ((uint64_t*)tp)[3];
1212                          ((uint64_t*)bp)[4] = ((uint64_t*)tp)[4];
1213                          ((uint64_t*)bp)[5] = ((uint64_t*)tp)[5];
1214                          ((uint64_t*)bp)[6] = ((uint64_t*)tp)[6];
1215                          tp += tile_stride;
1216                          bp += buf_stride;
1217                       }
1218                     break;
1219                   case 64:
1220                     CHECK_ALIGNMENT (uint64_t);
1221                     for (row = offsety; row < tile_height && y < height;
1222                          row++, y++)
1223                       {
1224                          ((uint64_t*)bp)[0] = ((uint64_t*)tp)[0];
1225                          ((uint64_t*)bp)[1] = ((uint64_t*)tp)[1];
1226                          ((uint64_t*)bp)[2] = ((uint64_t*)tp)[2];
1227                          ((uint64_t*)bp)[3] = ((uint64_t*)tp)[3];
1228                          ((uint64_t*)bp)[4] = ((uint64_t*)tp)[4];
1229                          ((uint64_t*)bp)[5] = ((uint64_t*)tp)[5];
1230                          ((uint64_t*)bp)[6] = ((uint64_t*)tp)[6];
1231                          ((uint64_t*)bp)[7] = ((uint64_t*)tp)[7];
1232                          tp += tile_stride;
1233                          bp += buf_stride;
1234                       }
1235                     break;
1236 
1237                   default:
1238                   unaligned:
1239                     for (row = offsety;
1240                          row < tile_height && y < height;
1241                          row++, y++)
1242                       {
1243                          memcpy (bp, tp, pixels * px_size);
1244                          tp += tile_stride;
1245                          bp += buf_stride;
1246                       }
1247                 }
1248 
1249               #undef CHECK_ALIGNMENT
1250               #undef CHECK_ALIGNMENT_ALIGNOF
1251             }
1252 
1253           gegl_tile_read_unlock (tile);
1254           gegl_tile_unref (tile);
1255           bufx += (tile_width - offsetx);
1256         }
1257       bufy += (tile_height - offsety);
1258     }
1259 
1260 }
1261 
1262 static void
fill_abyss_none(guchar * buf,gint width,gint height,gint buf_stride,gint pixel_size)1263 fill_abyss_none (guchar *buf, gint width, gint height, gint buf_stride, gint pixel_size)
1264 {
1265   const int byte_width = width * pixel_size;
1266 
1267   if (buf_stride == byte_width)
1268     {
1269       memset (buf, 0, byte_width * height);
1270     }
1271   else
1272     {
1273       while (height--)
1274         {
1275           memset (buf, 0, byte_width);
1276           buf += buf_stride;
1277         }
1278     }
1279 }
1280 
1281 static void
fill_abyss_color(guchar * buf,gint width,gint height,gint buf_stride,guchar * pixel,gint pixel_size)1282 fill_abyss_color (guchar *buf, gint width, gint height, gint buf_stride, guchar *pixel, gint pixel_size)
1283 {
1284   if (buf_stride == width * pixel_size)
1285     {
1286       gegl_memset_pattern (buf, pixel, pixel_size, width * height);
1287     }
1288   else
1289     {
1290       while (height--)
1291         {
1292           gegl_memset_pattern (buf, pixel, pixel_size, width);
1293           buf += buf_stride;
1294         }
1295     }
1296 }
1297 
1298 static void
gegl_buffer_iterate_read_abyss_color(GeglBuffer * buffer,const GeglRectangle * roi,const GeglRectangle * abyss,guchar * buf,gint buf_stride,const Babl * format,gint level,guchar * color,GeglAbyssPolicy repeat_mode)1299 gegl_buffer_iterate_read_abyss_color (GeglBuffer          *buffer,
1300                                       const GeglRectangle *roi,
1301                                       const GeglRectangle *abyss,
1302                                       guchar              *buf,
1303                                       gint                 buf_stride,
1304                                       const Babl          *format,
1305                                       gint                 level,
1306                                       guchar              *color,
1307                                       GeglAbyssPolicy      repeat_mode)
1308 {
1309   GeglRectangle current_roi = *roi;
1310   gint bpp = babl_format_get_bytes_per_pixel (format);
1311 
1312   if (current_roi.y < abyss->y)
1313     {
1314       /* Abyss above image */
1315       gint height = abyss->y - current_roi.y;
1316       if (current_roi.height < height)
1317         height = current_roi.height;
1318       if (color)
1319         fill_abyss_color (buf, current_roi.width, height, buf_stride, color, bpp);
1320       else
1321         fill_abyss_none (buf, current_roi.width, height, buf_stride, bpp);
1322       buf += buf_stride * height;
1323       current_roi.y += height;
1324       current_roi.height -= height;
1325     }
1326 
1327   if (current_roi.height && (current_roi.y < abyss->y + abyss->height))
1328     {
1329       GeglRectangle inner_roi = current_roi;
1330       guchar *inner_buf       = buf;
1331 
1332       if (inner_roi.height + inner_roi.y > abyss->height + abyss->y)
1333         {
1334           /* Clamp inner_roi to the in abyss height */
1335           inner_roi.height -= (inner_roi.height + inner_roi.y) - (abyss->height + abyss->y);
1336         }
1337 
1338       if (inner_roi.x < abyss->x)
1339         {
1340           /* Abyss left of image */
1341           gint width = abyss->x - inner_roi.x;
1342           if (width > inner_roi.width)
1343             width = inner_roi.width;
1344 
1345           if (color)
1346             fill_abyss_color (inner_buf, width, inner_roi.height, buf_stride, color, bpp);
1347           else
1348             fill_abyss_none (inner_buf, width, inner_roi.height, buf_stride, bpp);
1349           inner_buf += width * bpp;
1350           inner_roi.x += width;
1351           inner_roi.width -= width;
1352         }
1353 
1354       if (inner_roi.width && (inner_roi.x < abyss->x + abyss->width))
1355         {
1356           gint full_width = inner_roi.width;
1357 
1358           if (inner_roi.width + inner_roi.x > abyss->width + abyss->x)
1359             {
1360               /* Clamp inner_roi to the in abyss width */
1361               inner_roi.width -= (inner_roi.width + inner_roi.x) - (abyss->width + abyss->x);
1362             }
1363 
1364           if (level)
1365             gegl_buffer_iterate_read_fringed (buffer,
1366                                               &inner_roi,
1367                                               abyss,
1368                                               inner_buf,
1369                                               buf_stride,
1370                                               format,
1371                                               level,
1372                                               repeat_mode);
1373           else
1374             gegl_buffer_iterate_read_simple (buffer,
1375                                              &inner_roi,
1376                                              inner_buf,
1377                                              buf_stride,
1378                                              format,
1379                                              level);
1380 
1381           inner_buf += inner_roi.width * bpp;
1382           inner_roi.width = full_width - inner_roi.width;
1383         }
1384 
1385       if (inner_roi.width)
1386         {
1387           /* Abyss right of image */
1388           if (color)
1389             fill_abyss_color (inner_buf, inner_roi.width, inner_roi.height, buf_stride, color, bpp);
1390           else
1391             fill_abyss_none (inner_buf, inner_roi.width, inner_roi.height, buf_stride, bpp);
1392         }
1393 
1394       buf += inner_roi.height * buf_stride;
1395       /* current_roi.y += inner_roi.height; */
1396       current_roi.height -= inner_roi.height;
1397     }
1398 
1399   if (current_roi.height)
1400     {
1401       /* Abyss below image */
1402       if (color)
1403         fill_abyss_color (buf, current_roi.width, current_roi.height, buf_stride, color, bpp);
1404       else
1405         fill_abyss_none (buf, current_roi.width, current_roi.height, buf_stride, bpp);
1406     }
1407 }
1408 
1409 static void
gegl_buffer_iterate_read_abyss_clamp(GeglBuffer * buffer,const GeglRectangle * roi,const GeglRectangle * abyss,guchar * buf,gint buf_stride,const Babl * format,gint level)1410 gegl_buffer_iterate_read_abyss_clamp (GeglBuffer          *buffer,
1411                                       const GeglRectangle *roi,
1412                                       const GeglRectangle *abyss,
1413                                       guchar              *buf,
1414                                       gint                 buf_stride,
1415                                       const Babl          *format,
1416                                       gint                 level)
1417 {
1418   GeglRectangle read_output_rect;
1419   GeglRectangle read_input_rect;
1420 
1421   gint    bpp           = babl_format_get_bytes_per_pixel (format);
1422   gint    x_read_offset = 0;
1423   gint    y_read_offset = 0;
1424   gint    buf_offset_cols;
1425   gint    buf_offset_rows;
1426   gint    top_rows, left_cols, right_cols, bottom_rows;
1427   guchar *read_buf;
1428 
1429   if (roi->x >= abyss->x + abyss->width) /* Right of */
1430     x_read_offset = roi->x - (abyss->x + abyss->width) + 1;
1431   else if (roi->x + roi->width <= abyss->x) /* Left of */
1432     x_read_offset = (roi->x + roi->width) - abyss->x - 1;
1433 
1434   if (roi->y >= abyss->y + abyss->height) /* Above */
1435     y_read_offset = roi->y - (abyss->y + abyss->height) + 1;
1436   else if (roi->y + roi->height <= abyss->y) /* Below of */
1437     y_read_offset = (roi->y + roi->height) - abyss->y - 1;
1438 
1439   /* Intersect our shifted abyss with the roi */
1440   gegl_rectangle_intersect (&read_output_rect,
1441                             roi,
1442                             GEGL_RECTANGLE (abyss->x + x_read_offset,
1443                                             abyss->y + y_read_offset,
1444                                             abyss->width,
1445                                             abyss->height));
1446 
1447   /* Offset into *buf based on the intersected rect's x & y */
1448   buf_offset_cols = read_output_rect.x - roi->x;
1449   buf_offset_rows = read_output_rect.y - roi->y;
1450   read_buf = buf + (buf_offset_cols * bpp + buf_offset_rows * buf_stride);
1451 
1452   /* Convert the read output to a coresponding input */
1453   read_input_rect.x = read_output_rect.x - x_read_offset;
1454   read_input_rect.y = read_output_rect.y - y_read_offset;
1455   read_input_rect.width = read_output_rect.width;
1456   read_input_rect.height = read_output_rect.height;
1457 #if 1
1458   if (level)
1459     gegl_buffer_iterate_read_fringed (buffer,
1460                                       &read_input_rect,
1461                                       abyss,
1462                                       read_buf,
1463                                       buf_stride,
1464                                       format,
1465                                       level,
1466                                       GEGL_ABYSS_CLAMP);
1467   else
1468 #endif
1469     gegl_buffer_iterate_read_simple (buffer,
1470                                      &read_input_rect,
1471                                      read_buf,
1472                                      buf_stride,
1473                                      format,
1474                                      level);
1475 
1476   /* All calculations are done relative to read_output_rect because it is guranteed
1477    * to be inside of the roi rect and none of these calculations can return a value
1478    * less than 0.
1479    */
1480   top_rows = read_output_rect.y - roi->y;
1481   left_cols = read_output_rect.x - roi->x;
1482   right_cols = (roi->x + roi->width) - (read_output_rect.x + read_output_rect.width);
1483   bottom_rows = (roi->y + roi->height) - (read_output_rect.y + read_output_rect.height);
1484 
1485   if (top_rows)
1486     {
1487       guchar *fill_buf = buf;
1488       /* Top left pixel */
1489       if (left_cols)
1490         {
1491           guchar *src_pixel = read_buf;
1492           fill_abyss_color (fill_buf, left_cols, top_rows, buf_stride, src_pixel, bpp);
1493           fill_buf += left_cols * bpp;
1494         }
1495 
1496       /* Top rows */
1497       {
1498         guchar *src_pixel = read_buf;
1499         guchar *row_fill_buf = fill_buf;
1500         gint byte_width = read_output_rect.width * bpp;
1501         gint i;
1502         for (i = 0; i < top_rows; ++i)
1503           {
1504             memcpy (row_fill_buf, src_pixel, byte_width);
1505             row_fill_buf += buf_stride;
1506           }
1507       }
1508 
1509       fill_buf += (read_input_rect.width) * bpp;
1510       /* Top right pixel */
1511       if (right_cols)
1512         {
1513           guchar *src_pixel = read_buf + (read_input_rect.width - 1) * bpp;
1514           fill_abyss_color (fill_buf, right_cols, top_rows, buf_stride, src_pixel, bpp);
1515         }
1516     }
1517 
1518   /* Left */
1519   if (left_cols)
1520     {
1521       guchar *row_fill_buf = buf + (top_rows * buf_stride);
1522       guchar *src_pixel = read_buf;
1523       gint i;
1524 
1525       for (i = 0; i < read_output_rect.height; ++i)
1526         {
1527           gegl_memset_pattern (row_fill_buf, src_pixel, bpp, left_cols);
1528           row_fill_buf += buf_stride;
1529           src_pixel += buf_stride;
1530         }
1531     }
1532 
1533   /* Right */
1534   if (right_cols)
1535     {
1536       guchar *row_fill_buf = buf + (read_input_rect.width + left_cols) * bpp
1537                                  + top_rows * buf_stride;
1538       guchar *src_pixel = read_buf + (read_input_rect.width - 1) * bpp;
1539       gint i;
1540 
1541       for (i = 0; i < read_output_rect.height; ++i)
1542         {
1543           gegl_memset_pattern (row_fill_buf, src_pixel, bpp, right_cols);
1544           row_fill_buf += buf_stride;
1545           src_pixel += buf_stride;
1546         }
1547     }
1548 
1549   if (bottom_rows)
1550     {
1551       guchar *fill_buf = buf + (read_input_rect.height + top_rows) * buf_stride;
1552       /* Bottom left */
1553       if (left_cols)
1554         {
1555           guchar *src_pixel = read_buf + (read_input_rect.height - 1) * buf_stride;
1556           fill_abyss_color (fill_buf, left_cols, bottom_rows, buf_stride, src_pixel, bpp);
1557           fill_buf += left_cols * bpp;
1558         }
1559 
1560       /* Bottom rows */
1561       {
1562         guchar *src_pixel = read_buf + (read_input_rect.height - 1) * buf_stride;
1563         guchar *row_fill_buf = fill_buf;
1564         gint byte_width = read_output_rect.width * bpp;
1565         gint i;
1566         for (i = 0; i < bottom_rows; ++i)
1567           {
1568             memcpy (row_fill_buf, src_pixel, byte_width);
1569             row_fill_buf += buf_stride;
1570           }
1571       }
1572 
1573       fill_buf += read_input_rect.width * bpp;
1574       /* Bottom right */
1575       if (right_cols)
1576         {
1577           guchar *src_pixel = read_buf + (read_input_rect.width - 1) * bpp + (read_input_rect.height - 1) * buf_stride;
1578           fill_abyss_color (fill_buf, right_cols, bottom_rows, buf_stride, src_pixel, bpp);
1579         }
1580     }
1581 }
1582 
1583 static void
gegl_buffer_iterate_read_abyss_loop(GeglBuffer * buffer,const GeglRectangle * roi,const GeglRectangle * abyss,guchar * buf,gint buf_stride,const Babl * format,gint level)1584 gegl_buffer_iterate_read_abyss_loop (GeglBuffer          *buffer,
1585                                      const GeglRectangle *roi,
1586                                      const GeglRectangle *abyss,
1587                                      guchar              *buf,
1588                                      gint                 buf_stride,
1589                                      const Babl          *format,
1590                                      gint                 level)
1591 {
1592   GeglRectangle current_roi;
1593   gint          bpp = babl_format_get_bytes_per_pixel (format);
1594   gint          origin_x;
1595 
1596   /* Loop abyss works like iterating over a grid of tiles the size of the abyss */
1597   gint loop_chunk_ix = gegl_tile_indice (roi->x - abyss->x, abyss->width);
1598   gint loop_chunk_iy = gegl_tile_indice (roi->y - abyss->y, abyss->height);
1599 
1600   current_roi.x = loop_chunk_ix * abyss->width  + abyss->x;
1601   current_roi.y = loop_chunk_iy * abyss->height + abyss->y;
1602 
1603   current_roi.width  = abyss->width;
1604   current_roi.height = abyss->height;
1605 
1606   origin_x = current_roi.x;
1607 
1608   while (current_roi.y < roi->y + roi->height)
1609     {
1610       guchar *inner_buf  = buf;
1611       gint    row_height = 0;
1612 
1613       while (current_roi.x < roi->x + roi->width)
1614         {
1615           GeglRectangle simple_roi;
1616           gegl_rectangle_intersect (&simple_roi, &current_roi, roi);
1617 
1618           gegl_buffer_iterate_read_simple (buffer,
1619                                            GEGL_RECTANGLE (abyss->x + (simple_roi.x - current_roi.x),
1620                                                            abyss->y + (simple_roi.y - current_roi.y),
1621                                                            simple_roi.width,
1622                                                            simple_roi.height),
1623                                            inner_buf,
1624                                            buf_stride,
1625                                            format,
1626                                            level);
1627 
1628           row_height  = simple_roi.height;
1629           inner_buf  += simple_roi.width * bpp;
1630 
1631           current_roi.x += abyss->width;
1632         }
1633 
1634       buf += buf_stride * row_height;
1635 
1636       current_roi.x  = origin_x;
1637       current_roi.y += abyss->height;
1638     }
1639 }
1640 
1641 static gpointer
gegl_buffer_read_at_level(GeglBuffer * buffer,const GeglRectangle * roi,guchar * buf,gint rowstride,const Babl * format,gint level,GeglAbyssPolicy repeat_mode)1642 gegl_buffer_read_at_level (GeglBuffer          *buffer,
1643                            const GeglRectangle *roi,
1644                            guchar              *buf,
1645                            gint                 rowstride,
1646                            const Babl          *format,
1647                            gint                 level,
1648                            GeglAbyssPolicy      repeat_mode)
1649 {
1650   gint bpp = babl_format_get_bytes_per_pixel (format);
1651 
1652   if (level == 0)
1653     {
1654       if (!buf)
1655         {
1656           gpointer scratch = gegl_scratch_alloc (bpp * roi->width * roi->height);
1657 
1658           gegl_buffer_iterate_read_dispatch (buffer, roi, scratch, roi->width * bpp, format, 0, repeat_mode);
1659 
1660           return scratch;
1661         }
1662       else
1663         {
1664           gegl_buffer_iterate_read_dispatch (buffer, roi, buf, rowstride, format, 0, repeat_mode);
1665 
1666           return NULL;
1667         }
1668     }
1669   else
1670     {
1671       gpointer scratch;
1672       GeglRectangle next_roi;
1673       next_roi.x = roi->x * 2;
1674       next_roi.y = roi->y * 2;
1675       next_roi.width = roi->width * 2;
1676       next_roi.height = roi->height * 2;
1677 
1678       /* If the next block is too big split it in half */
1679       if (next_roi.width * next_roi.height > 256 * 256)
1680         {
1681           GeglRectangle next_roi_a = next_roi;
1682           GeglRectangle next_roi_b = next_roi;
1683           gint scratch_stride = next_roi.width * bpp;
1684           gpointer scratch_a;
1685           gpointer scratch_b;
1686           scratch = gegl_scratch_alloc (bpp * next_roi.width * next_roi.height);
1687 
1688           if (next_roi.width > next_roi.height)
1689             {
1690               next_roi_a.width = roi->width;
1691               next_roi_b.width = roi->width;
1692               next_roi_b.x += next_roi_a.width;
1693 
1694               scratch_a = scratch;
1695               scratch_b = (guchar *)scratch + next_roi_a.width * bpp;
1696             }
1697           else
1698             {
1699               next_roi_a.height = roi->height;
1700               next_roi_b.height = roi->height;
1701               next_roi_b.y += next_roi_a.height;
1702 
1703               scratch_a = scratch;
1704               scratch_b = (guchar *)scratch + next_roi_a.height * scratch_stride;
1705             }
1706 
1707           gegl_buffer_read_at_level (buffer, &next_roi_a, scratch_a, scratch_stride, format, level - 1, repeat_mode);
1708           gegl_buffer_read_at_level (buffer, &next_roi_b, scratch_b, scratch_stride, format, level - 1, repeat_mode);
1709 
1710         }
1711       else
1712         {
1713           scratch = gegl_buffer_read_at_level (buffer, &next_roi, NULL, 0, format, level - 1, repeat_mode);
1714         }
1715 
1716       if (buf)
1717         {
1718           gegl_downscale_2x2 (format,
1719                               next_roi.width,
1720                               next_roi.height,
1721                               scratch,
1722                               next_roi.width * bpp,
1723                               buf,
1724                               rowstride);
1725           gegl_scratch_free (scratch);
1726           return NULL;
1727         }
1728       else
1729         {
1730           gegl_downscale_2x2 (format,
1731                               next_roi.width,
1732                               next_roi.height,
1733                               scratch,
1734                               next_roi.width * bpp,
1735                               scratch,
1736                               roi->width * bpp);
1737           return scratch;
1738         }
1739     }
1740 }
1741 
1742 static void
gegl_buffer_iterate_read_fringed(GeglBuffer * buffer,const GeglRectangle * roi,const GeglRectangle * abyss,guchar * buf,gint buf_stride,const Babl * format,gint level,GeglAbyssPolicy repeat_mode)1743 gegl_buffer_iterate_read_fringed (GeglBuffer          *buffer,
1744                                   const GeglRectangle *roi,
1745                                   const GeglRectangle *abyss,
1746                                   guchar              *buf,
1747                                   gint                 buf_stride,
1748                                   const Babl          *format,
1749                                   gint                 level,
1750                                   GeglAbyssPolicy      repeat_mode)
1751 {
1752   gint x = roi->x;
1753   gint y = roi->y;
1754   gint width  = roi->width;
1755   gint height = roi->height;
1756   guchar        *inner_buf = buf;
1757 
1758   gint bpp = babl_format_get_bytes_per_pixel (format);
1759 
1760   if (x <= abyss->x)
1761     {
1762       GeglRectangle fringe_roi = {x, y, 1, height};
1763       guchar *fringe_buf = inner_buf;
1764 
1765       gegl_buffer_read_at_level (buffer, &fringe_roi, fringe_buf, buf_stride, format, level, repeat_mode);
1766       inner_buf += bpp;
1767       x     += 1;
1768       width -= 1;
1769 
1770       if (!width)
1771         return;
1772     }
1773 
1774   if (y <= abyss->y)
1775     {
1776       GeglRectangle fringe_roi = {x, y, width, 1};
1777       guchar *fringe_buf = inner_buf;
1778 
1779       gegl_buffer_read_at_level (buffer, &fringe_roi, fringe_buf, buf_stride, format, level, repeat_mode);
1780       inner_buf += buf_stride;
1781       y      += 1;
1782       height -= 1;
1783 
1784       if (!height)
1785         return;
1786     }
1787 
1788   if (y + height >= abyss->y + abyss->height)
1789     {
1790       GeglRectangle fringe_roi = {x, y + height - 1, width, 1};
1791       guchar *fringe_buf = inner_buf + (height - 1) * buf_stride;
1792 
1793       gegl_buffer_read_at_level (buffer, &fringe_roi, fringe_buf, buf_stride, format, level, repeat_mode);
1794       height -= 1;
1795 
1796       if (!height)
1797         return;
1798     }
1799 
1800   if (x + width >= abyss->x + abyss->width)
1801     {
1802       GeglRectangle fringe_roi = {x + width - 1, y, 1, height};
1803       guchar *fringe_buf = inner_buf + (width - 1) * bpp;
1804 
1805       gegl_buffer_read_at_level (buffer, &fringe_roi, fringe_buf, buf_stride, format, level, repeat_mode);
1806       width -= 1;
1807 
1808       if (!width)
1809         return;
1810     }
1811 
1812   gegl_buffer_iterate_read_simple (buffer,
1813                                    GEGL_RECTANGLE (x, y, width, height),
1814                                    inner_buf,
1815                                    buf_stride,
1816                                    format,
1817                                    level);
1818 }
1819 
1820 static void
gegl_buffer_iterate_read_dispatch(GeglBuffer * buffer,const GeglRectangle * roi,guchar * buf,gint rowstride,const Babl * format,gint level,GeglAbyssPolicy repeat_mode)1821 gegl_buffer_iterate_read_dispatch (GeglBuffer          *buffer,
1822                                    const GeglRectangle *roi,
1823                                    guchar              *buf,
1824                                    gint                 rowstride,
1825                                    const Babl          *format,
1826                                    gint                 level,
1827                                    GeglAbyssPolicy      repeat_mode)
1828 {
1829   GeglRectangle abyss          = buffer->abyss;
1830   GeglRectangle abyss_factored = abyss;
1831   GeglRectangle roi_factored   = *roi;
1832 
1833   if (level)
1834     {
1835       const gint    factor         = 1 << level;
1836       const gint    x1 = buffer->shift_x + abyss.x;
1837       const gint    y1 = buffer->shift_y + abyss.y;
1838       const gint    x2 = buffer->shift_x + abyss.x + abyss.width;
1839       const gint    y2 = buffer->shift_y + abyss.y + abyss.height;
1840 
1841       abyss_factored.x      = (x1 + (x1 < 0 ? 1 - factor : 0)) / factor;
1842       abyss_factored.y      = (y1 + (y1 < 0 ? 1 - factor : 0)) / factor;
1843       abyss_factored.width  = (x2 + (x2 < 0 ? 0 : factor - 1)) / factor - abyss_factored.x;
1844       abyss_factored.height = (y2 + (y2 < 0 ? 0 : factor - 1)) / factor - abyss_factored.y;
1845 
1846       roi_factored.x       = (buffer->shift_x + roi_factored.x) / factor;
1847       roi_factored.y       = (buffer->shift_y + roi_factored.y) / factor;
1848       roi_factored.width  /= factor;
1849       roi_factored.height /= factor;
1850     }
1851   else
1852     {
1853       roi_factored.x += buffer->shift_x;
1854       roi_factored.y += buffer->shift_y;
1855       abyss_factored.x += buffer->shift_x;
1856       abyss_factored.y += buffer->shift_y;
1857     }
1858 
1859   if (rowstride == GEGL_AUTO_ROWSTRIDE)
1860     rowstride = roi_factored.width * babl_format_get_bytes_per_pixel (format);
1861 
1862   if (gegl_rectangle_contains (&abyss, roi))
1863     {
1864       gegl_buffer_iterate_read_simple (buffer, &roi_factored, buf, rowstride, format, level);
1865     }
1866   else if (repeat_mode == GEGL_ABYSS_NONE)
1867     {
1868       gegl_buffer_iterate_read_abyss_color (buffer, &roi_factored, &abyss_factored,
1869                                             buf, rowstride, format, level, NULL,
1870                                             GEGL_ABYSS_NONE);
1871     }
1872   else if (repeat_mode == GEGL_ABYSS_WHITE)
1873     {
1874       guchar color[128];
1875       gfloat in_color[] = {1.0f, 1.0f, 1.0f, 1.0f};
1876 
1877       babl_process (babl_fish (gegl_babl_rgba_linear_float (), format),
1878                     in_color, color, 1);
1879 
1880       gegl_buffer_iterate_read_abyss_color (buffer, &roi_factored, &abyss_factored,
1881                                             buf, rowstride, format, level, color,
1882                                             GEGL_ABYSS_WHITE);
1883     }
1884   else if (repeat_mode == GEGL_ABYSS_BLACK)
1885     {
1886       guchar color[128];
1887       gfloat  in_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1888 
1889       babl_process (babl_fish (gegl_babl_rgba_linear_float (), format),
1890                     in_color, color, 1);
1891 
1892       gegl_buffer_iterate_read_abyss_color (buffer, &roi_factored, &abyss_factored,
1893                                             buf, rowstride, format, level, color,
1894                                             GEGL_ABYSS_BLACK);
1895     }
1896   else if (repeat_mode == GEGL_ABYSS_CLAMP)
1897     {
1898       if (abyss_factored.width == 0 || abyss_factored.height == 0)
1899         gegl_buffer_iterate_read_abyss_color (buffer, &roi_factored, &abyss_factored,
1900                                               buf, rowstride, format, level, NULL,
1901                                               GEGL_ABYSS_NONE);
1902       else
1903         gegl_buffer_iterate_read_abyss_clamp (buffer, &roi_factored, &abyss_factored,
1904                                               buf, rowstride, format, level);
1905     }
1906   else
1907     {
1908       if (abyss_factored.width == 0 || abyss_factored.height == 0)
1909         gegl_buffer_iterate_read_abyss_color (buffer, &roi_factored, &abyss_factored,
1910                                               buf, rowstride, format, level, NULL,
1911                                               GEGL_ABYSS_NONE);
1912       else
1913         gegl_buffer_iterate_read_abyss_loop (buffer, &roi_factored, &abyss_factored,
1914                                              buf, rowstride, format, level);
1915     }
1916 }
1917 
1918 void
gegl_buffer_set_unlocked(GeglBuffer * buffer,const GeglRectangle * rect,gint level,const Babl * format,const void * src,gint rowstride)1919 gegl_buffer_set_unlocked (GeglBuffer          *buffer,
1920                           const GeglRectangle *rect,
1921                           gint                 level,
1922                           const Babl          *format,
1923                           const void          *src,
1924                           gint                 rowstride)
1925 {
1926   _gegl_buffer_set_with_flags (buffer, rect, level, format, src, rowstride,
1927                                GEGL_BUFFER_SET_FLAG_NOTIFY);
1928 }
1929 
1930 void
gegl_buffer_set_unlocked_no_notify(GeglBuffer * buffer,const GeglRectangle * rect,gint level,const Babl * format,const void * src,gint rowstride)1931 gegl_buffer_set_unlocked_no_notify (GeglBuffer          *buffer,
1932                                     const GeglRectangle *rect,
1933                                     gint                 level,
1934                                     const Babl          *format,
1935                                     const void          *src,
1936                                     gint                 rowstride)
1937 {
1938   _gegl_buffer_set_with_flags (buffer, rect, level, format, src, rowstride,
1939                                GEGL_BUFFER_SET_FLAG_FAST);
1940 }
1941 
1942 
1943 void
gegl_buffer_set(GeglBuffer * buffer,const GeglRectangle * rect,gint level,const Babl * format,const void * src,gint rowstride)1944 gegl_buffer_set (GeglBuffer          *buffer,
1945                  const GeglRectangle *rect,
1946                  gint                 level,
1947                  const Babl          *format,
1948                  const void          *src,
1949                  gint                 rowstride)
1950 {
1951   g_return_if_fail (GEGL_IS_BUFFER (buffer));
1952 
1953   if (G_UNLIKELY (gegl_rectangle_is_empty (rect ? rect : &buffer->extent)))
1954     return;
1955 
1956   g_return_if_fail (src != NULL);
1957 
1958   if (G_LIKELY (format == NULL))
1959     format = buffer->soft_format;
1960 
1961   if (G_UNLIKELY (rect && rect->width == 1))
1962     {
1963       if (level == 0 && rect->height == 1)
1964         {
1965           _gegl_buffer_set_pixel (buffer, rect->x, rect->y,
1966                                   format, src,
1967                                   GEGL_BUFFER_SET_FLAG_LOCK|
1968                                   GEGL_BUFFER_SET_FLAG_NOTIFY);
1969           return;
1970         }
1971       else if (buffer->soft_format != format &&
1972                rowstride == babl_format_get_bytes_per_pixel (format))
1973         {
1974           int     bpp = babl_format_get_bytes_per_pixel (buffer->soft_format);
1975           uint8_t tmp[rect->height * bpp];
1976           babl_process (babl_fish (format, buffer->soft_format),
1977                         src, &tmp[0], rect->height);
1978           _gegl_buffer_set_with_flags (buffer, rect, level, buffer->soft_format, tmp, bpp,
1979                                        GEGL_BUFFER_SET_FLAG_LOCK|
1980                                        GEGL_BUFFER_SET_FLAG_NOTIFY);
1981           return;
1982         }
1983     }
1984 
1985     _gegl_buffer_set_with_flags (buffer, rect, level, format, src, rowstride,
1986                                  GEGL_BUFFER_SET_FLAG_LOCK|
1987                                  GEGL_BUFFER_SET_FLAG_NOTIFY);
1988 }
1989 
1990 /* Expand roi by scale so it uncludes all pixels needed
1991  * to satisfy a gegl_buffer_get() call at level 0.
1992  */
1993 GeglRectangle
_gegl_get_required_for_scale(const GeglRectangle * roi,gdouble scale)1994 _gegl_get_required_for_scale (const GeglRectangle *roi,
1995                               gdouble              scale)
1996 {
1997   if (GEGL_FLOAT_EQUAL (scale, 1.0))
1998     return *roi;
1999   else
2000     {
2001       gint x1 = int_floorf (roi->x / scale + GEGL_SCALE_EPSILON);
2002       gint x2 = int_ceilf ((roi->x + roi->width) / scale - GEGL_SCALE_EPSILON);
2003       gint y1 = int_floorf (roi->y / scale + GEGL_SCALE_EPSILON);
2004       gint y2 = int_ceilf ((roi->y + roi->height) / scale - GEGL_SCALE_EPSILON);
2005 
2006       gint pad = (1.0 / scale > 1.0) ? int_ceilf (1.0 / scale) : 1;
2007 
2008       if (scale < 1.0)
2009         {
2010           return *GEGL_RECTANGLE (x1 - pad,
2011                                   y1 - pad,
2012                                   x2 - x1 + 2 * pad,
2013                                   y2 - y1 + 2 * pad);
2014         }
2015       else
2016         {
2017           return *GEGL_RECTANGLE (x1,
2018                                   y1,
2019                                   x2 - x1,
2020                                   y2 - y1);
2021         }
2022       }
2023 }
2024 
2025 static inline void
_gegl_buffer_get_unlocked(GeglBuffer * buffer,gdouble scale,const GeglRectangle * rect,const Babl * format,gpointer dest_buf,gint rowstride,GeglAbyssPolicy flags)2026 _gegl_buffer_get_unlocked (GeglBuffer          *buffer,
2027                            gdouble              scale,
2028                            const GeglRectangle *rect,
2029                            const Babl          *format,
2030                            gpointer             dest_buf,
2031                            gint                 rowstride,
2032                            GeglAbyssPolicy      flags)
2033 {
2034   GeglAbyssPolicy repeat_mode = flags & 0x7; /* mask off interpolation from repeat mode part of flags */
2035 
2036   g_return_if_fail (scale > 0.0f);
2037 
2038   if (! rect && GEGL_FLOAT_EQUAL (scale, 1.0))
2039     rect = &buffer->extent;
2040 
2041   g_return_if_fail (rect != NULL);
2042 
2043   if (G_UNLIKELY (gegl_rectangle_is_empty (rect)))
2044     return;
2045 
2046   g_return_if_fail (dest_buf != NULL);
2047 
2048   if (! format)
2049     format = buffer->soft_format;
2050 
2051   if (gegl_buffer_ext_flush)
2052     gegl_buffer_ext_flush (buffer, rect);
2053 
2054   if (G_UNLIKELY (scale == 1.0 &&
2055       rect->width == 1))
2056   {
2057     if (rect->height == 1)
2058       {
2059         gegl_buffer_get_pixel (buffer, rect->x, rect->y, format, dest_buf,
2060                                repeat_mode);
2061       }
2062     else
2063       {
2064         if (buffer->soft_format == format ||
2065             rowstride != babl_format_get_bytes_per_pixel (format))
2066         {
2067           gegl_buffer_iterate_read_dispatch (buffer, rect, dest_buf,
2068                                              rowstride, format, 0, repeat_mode);
2069         }
2070         else
2071         {
2072           /* first fetch all pixels to a temporary buffer */
2073           gint    bpp = babl_format_get_bytes_per_pixel (buffer->soft_format);
2074           uint8_t tmp[rect->height * bpp];
2075           gegl_buffer_iterate_read_dispatch (buffer, rect, &tmp[0],
2076                                              bpp, buffer->soft_format, 0, repeat_mode);
2077           /* then convert in a single shot */
2078           babl_process (babl_fish (buffer->soft_format, format),
2079                         &tmp[0], dest_buf, rect->height);
2080         }
2081       }
2082     return;
2083   }
2084 
2085   if (GEGL_FLOAT_EQUAL (scale, 1.0))
2086     {
2087       gegl_buffer_iterate_read_dispatch (buffer, rect, dest_buf, rowstride,
2088                                          format, 0, repeat_mode);
2089       return;
2090     }
2091   else
2092   {
2093     gint chunk_height;
2094     GeglRectangle rect2       = *rect;
2095     gint    bpp               = babl_format_get_bytes_per_pixel (format);
2096     gint    ystart            = rect->y;
2097     float   scale_orig        = scale;
2098     gint    level             = 0;
2099     void   *sample_buf;
2100     gint    x1 = int_floorf (rect->x / scale_orig + GEGL_SCALE_EPSILON);
2101     gint    x2 = int_ceilf ((rect->x + rect->width) / scale_orig - GEGL_SCALE_EPSILON);
2102     int     max_bytes_per_row = ((rect->width+1) * bpp * 2);
2103     int     allocated         = 0;
2104     gint interpolation = (flags & GEGL_BUFFER_FILTER_ALL);
2105     gint    factor = 1;
2106 
2107     while (scale <= 0.5)
2108       {
2109         x1 = 0 < x1 ? x1 / 2 : (x1 - 1) / 2;
2110         x2 = 0 < x2 ? (x2 + 1) / 2 : x2 / 2;
2111         scale  *= 2;
2112         factor *= 2;
2113         level++;
2114       }
2115 
2116     if (GEGL_FLOAT_EQUAL (scale, 1.0))
2117       {
2118         GeglRectangle rect0;
2119 
2120         rect0.x      = int_floorf (rect->x / scale_orig + GEGL_SCALE_EPSILON);
2121         rect0.y      = int_floorf (rect->y / scale_orig + GEGL_SCALE_EPSILON);
2122         rect0.width  = int_ceilf ((rect->x + rect->width) / scale_orig -
2123                                   GEGL_SCALE_EPSILON) -
2124                        rect0.x;
2125         rect0.height = int_ceilf ((rect->y + rect->height) / scale_orig -
2126                                   GEGL_SCALE_EPSILON) -
2127                        rect0.y;
2128 
2129         gegl_buffer_iterate_read_dispatch (buffer, &rect0,
2130                                            dest_buf, rowstride,
2131                                            format, level, repeat_mode);
2132         return;
2133       }
2134 
2135     chunk_height = (1024 * 128) / max_bytes_per_row;
2136 
2137     if (chunk_height < 4)
2138       chunk_height = 4;
2139 
2140     rect2.y = ystart;
2141     rect2.height = chunk_height;
2142     if (rect2.y + rect2.height > rect->y + rect->height)
2143     {
2144       rect2.height = (rect->y + rect->height) - rect2.y;
2145       chunk_height = rect2.height;
2146     }
2147 
2148     allocated = max_bytes_per_row * ((chunk_height+1) * 2);
2149 
2150     if (interpolation == GEGL_BUFFER_FILTER_AUTO)
2151     {
2152       /* with no specified interpolation we aim for a trade-off where
2153          100-200% ends up using box-filter - which is a better transition
2154          to nearest neighbor which happens beyond 200% further below.
2155        */
2156       if (scale >= 2.0)
2157         interpolation = GEGL_BUFFER_FILTER_NEAREST;
2158       else if (scale > 1.0)
2159         {
2160           interpolation = GEGL_BUFFER_FILTER_BOX;
2161         }
2162       else
2163         interpolation = GEGL_BUFFER_FILTER_BILINEAR;
2164     }
2165 
2166     sample_buf = gegl_scratch_alloc (allocated);
2167 
2168     while (rect2.width > 0 && rect2.height > 0)
2169     {
2170       GeglRectangle sample_rect;
2171       gint    buf_width, buf_height;
2172       gint    y1 = int_floorf (rect2.y / scale_orig + GEGL_SCALE_EPSILON);
2173       gint    y2 = int_ceilf ((rect2.y + rect2.height) / scale_orig - GEGL_SCALE_EPSILON);
2174       scale = scale_orig;
2175 
2176       while (scale <= 0.5)
2177         {
2178           y1 = 0 < y1 ? y1 / 2 : (y1 - 1) / 2;
2179           y2 = 0 < y2 ? (y2 + 1) / 2 : y2 / 2;
2180           scale  *= 2;
2181         }
2182 
2183       if (rowstride == GEGL_AUTO_ROWSTRIDE)
2184         rowstride = rect2.width * bpp;
2185 
2186       /* this is the level where we split and chew through a small temp-buf worth of data
2187        * possibly managing to keep things in L2 cache
2188        */
2189 
2190       sample_rect.x      = factor * x1;
2191       sample_rect.y      = factor * y1;
2192       sample_rect.width  = factor * (x2 - x1);
2193       sample_rect.height = factor * (y2 - y1);
2194       buf_width  = x2 - x1;
2195       buf_height = y2 - y1;
2196 
2197 
2198       if (buf_height && buf_width)
2199         switch(interpolation)
2200         {
2201           case GEGL_BUFFER_FILTER_NEAREST:
2202 
2203             gegl_buffer_iterate_read_dispatch (buffer, &sample_rect,
2204                                                (guchar*)sample_buf,
2205                                                buf_width * bpp,
2206                                                format, level, repeat_mode);
2207             sample_rect.x      = x1;
2208             sample_rect.y      = y1;
2209             sample_rect.width  = x2 - x1;
2210             sample_rect.height = y2 - y1;
2211 
2212             gegl_resample_nearest (dest_buf,
2213                                    sample_buf,
2214                                    &rect2,
2215                                    &sample_rect,
2216                                    buf_width * bpp,
2217                                    scale,
2218                                    bpp,
2219                                    rowstride);
2220             break;
2221           case GEGL_BUFFER_FILTER_BILINEAR:
2222             buf_width  += 1;
2223             buf_height += 1;
2224 
2225             /* fill the regions of the buffer outside the sampled area with
2226              * zeros, since they may be involved in the arithmetic.  even
2227              * though their actual value should have no, or negligible, effect,
2228              * they must at least be finite, when dealing with float formats.
2229              */
2230             {
2231               guchar *p = sample_buf;
2232               gint    y;
2233 
2234               for (y = 0; y < buf_height - 1; y++)
2235                 {
2236                   memset (p + (buf_width - 1) * bpp, 0, bpp);
2237 
2238                   p += buf_width * bpp;
2239                 }
2240 
2241               memset (p, 0, buf_width * bpp);
2242             }
2243 
2244             gegl_buffer_iterate_read_dispatch (buffer, &sample_rect,
2245                                        (guchar*)sample_buf,
2246                                         buf_width * bpp,
2247                                         format, level, repeat_mode);
2248 
2249             sample_rect.x      = x1;
2250             sample_rect.y      = y1;
2251             sample_rect.width  = x2 - x1 + 1;
2252             sample_rect.height = y2 - y1 + 1;
2253 
2254             gegl_resample_bilinear (dest_buf,
2255                                     sample_buf,
2256                                     &rect2,
2257                                     &sample_rect,
2258                                     buf_width * bpp,
2259                                     scale,
2260                                     format,
2261                                     rowstride);
2262             break;
2263           case GEGL_BUFFER_FILTER_BOX:
2264           default:
2265             {
2266               gint offset;
2267               buf_width  += 2;
2268               buf_height += 2;
2269               offset = (buf_width + 1) * bpp;
2270 
2271               /* fill the regions of the buffer outside the sampled area with
2272                * zeros, since they may be involved in the arithmetic.  even
2273                * though their actual value should have no, or negligible,
2274                * effect, they must at least be finite, when dealing with float
2275                * formats.
2276                */
2277               {
2278                 guchar *p = sample_buf;
2279                 gint    y;
2280 
2281                 memset (p, 0, (buf_width - 1) * bpp);
2282 
2283                 for (y = 0; y < buf_height - 1; y++)
2284                   {
2285                     memset (p + (buf_width - 1) * bpp, 0, 2 * bpp);
2286 
2287                     p += buf_width * bpp;
2288                   }
2289 
2290                 memset (p + bpp, 0, (buf_width - 1) * bpp);
2291               }
2292 
2293               gegl_buffer_iterate_read_dispatch (buffer, &sample_rect,
2294                                          (guchar*)sample_buf + offset,
2295                                           buf_width * bpp,
2296                                           format, level, repeat_mode);
2297 
2298               sample_rect.x      = x1 - 1;
2299               sample_rect.y      = y1 - 1;
2300               sample_rect.width  = x2 - x1 + 2;
2301               sample_rect.height = y2 - y1 + 2;
2302 
2303               gegl_resample_boxfilter (dest_buf,
2304                                        sample_buf,
2305                                        &rect2,
2306                                        &sample_rect,
2307                                        buf_width * bpp,
2308                                        scale,
2309                                        format,
2310                                        rowstride);
2311             }
2312             break;
2313       }
2314 
2315     dest_buf = ((guchar*)dest_buf) + rowstride * rect2.height;
2316     ystart+=rect2.height;
2317     rect2.y = ystart;
2318     rect2.height = chunk_height;
2319     if (rect2.y + rect2.height > rect->y + rect->height)
2320       rect2.height = (rect->y + rect->height) - rect2.y;
2321 
2322     }
2323 
2324     gegl_scratch_free (sample_buf);
2325   }
2326 }
2327 
2328 void
gegl_buffer_get_unlocked(GeglBuffer * buffer,gdouble scale,const GeglRectangle * rect,const Babl * format,gpointer dest_buf,gint rowstride,GeglAbyssPolicy repeat_mode)2329 gegl_buffer_get_unlocked (GeglBuffer          *buffer,
2330                           gdouble              scale,
2331                           const GeglRectangle *rect,
2332                           const Babl          *format,
2333                           gpointer             dest_buf,
2334                           gint                 rowstride,
2335                           GeglAbyssPolicy      repeat_mode)
2336 {
2337   return _gegl_buffer_get_unlocked (buffer, scale, rect, format, dest_buf, rowstride, repeat_mode);
2338 }
2339 
2340 void
gegl_buffer_get(GeglBuffer * buffer,const GeglRectangle * rect,gdouble scale,const Babl * format,gpointer dest_buf,gint rowstride,GeglAbyssPolicy repeat_mode)2341 gegl_buffer_get (GeglBuffer          *buffer,
2342                  const GeglRectangle *rect,
2343                  gdouble              scale,
2344                  const Babl          *format,
2345                  gpointer             dest_buf,
2346                  gint                 rowstride,
2347                  GeglAbyssPolicy      repeat_mode)
2348 {
2349   g_return_if_fail (GEGL_IS_BUFFER (buffer));
2350   gegl_buffer_lock (buffer);
2351   _gegl_buffer_get_unlocked (buffer, scale, rect, format, dest_buf, rowstride, repeat_mode);
2352   gegl_buffer_unlock (buffer);
2353 }
2354 
2355 static void
gegl_buffer_copy2(GeglBuffer * src,const GeglRectangle * src_rect,GeglAbyssPolicy repeat_mode,GeglBuffer * dst,const GeglRectangle * dst_rect)2356 gegl_buffer_copy2 (GeglBuffer          *src,
2357                    const GeglRectangle *src_rect,
2358                    GeglAbyssPolicy      repeat_mode,
2359                    GeglBuffer          *dst,
2360                    const GeglRectangle *dst_rect)
2361 {
2362   GeglBufferIterator *i;
2363   gint offset_x = src_rect->x - dst_rect->x;
2364   gint offset_y = src_rect->y - dst_rect->y;
2365 
2366   i = gegl_buffer_iterator_new (dst, dst_rect, 0, dst->soft_format,
2367                                 GEGL_ACCESS_WRITE | GEGL_ITERATOR_NO_NOTIFY,
2368                                 repeat_mode, 1);
2369   while (gegl_buffer_iterator_next (i))
2370     {
2371       GeglRectangle src_rect = i->items[0].roi;
2372       src_rect.x += offset_x;
2373       src_rect.y += offset_y;
2374       gegl_buffer_iterate_read_dispatch (src, &src_rect, i->items[0].data, 0,
2375                                          dst->soft_format, 0, repeat_mode);
2376     }
2377 }
2378 
2379 void
gegl_buffer_copy(GeglBuffer * src,const GeglRectangle * src_rect,GeglAbyssPolicy repeat_mode,GeglBuffer * dst,const GeglRectangle * dst_rect)2380 gegl_buffer_copy (GeglBuffer          *src,
2381                   const GeglRectangle *src_rect,
2382                   GeglAbyssPolicy      repeat_mode,
2383                   GeglBuffer          *dst,
2384                   const GeglRectangle *dst_rect)
2385 {
2386   GeglRectangle real_src_rect;
2387   GeglRectangle real_dst_rect;
2388 
2389   g_return_if_fail (GEGL_IS_BUFFER (src));
2390   g_return_if_fail (GEGL_IS_BUFFER (dst));
2391 
2392   if (!src_rect)
2393     {
2394       src_rect = gegl_buffer_get_extent (src);
2395     }
2396   if (G_UNLIKELY (src_rect->width <= 0 ||
2397       src_rect->height <= 0))
2398     return;
2399 
2400   if (!dst_rect)
2401     {
2402       dst_rect = src_rect;
2403     }
2404 
2405   real_dst_rect        = *dst_rect;
2406   real_dst_rect.width  = src_rect->width;
2407   real_dst_rect.height = src_rect->height;
2408 
2409   if (G_UNLIKELY (! gegl_rectangle_intersect (&real_dst_rect, &real_dst_rect, &dst->abyss)))
2410     return;
2411 
2412   real_src_rect    = real_dst_rect;
2413   real_src_rect.x += src_rect->x - dst_rect->x;
2414   real_src_rect.y += src_rect->y - dst_rect->y;
2415 
2416   src_rect = &real_src_rect;
2417   dst_rect = &real_dst_rect;
2418 
2419   if (! gegl_rectangle_intersect (NULL, src_rect, &src->abyss))
2420     {
2421       switch (repeat_mode)
2422         {
2423         case GEGL_ABYSS_CLAMP:
2424         case GEGL_ABYSS_LOOP:
2425           if (! gegl_rectangle_is_empty (&src->abyss))
2426             break;
2427 
2428           /* fall through */
2429 
2430         case GEGL_ABYSS_NONE:
2431           {
2432             const gfloat color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
2433 
2434             gegl_buffer_set_color_from_pixel (dst, dst_rect, color,
2435                                               gegl_babl_rgba_linear_float ());
2436 
2437             return;
2438           }
2439 
2440         case GEGL_ABYSS_BLACK:
2441           {
2442             const gfloat color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
2443 
2444             gegl_buffer_set_color_from_pixel (dst, dst_rect, color,
2445                                               gegl_babl_rgba_linear_float ());
2446 
2447             return;
2448           }
2449 
2450         case GEGL_ABYSS_WHITE:
2451           {
2452             const gfloat color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
2453 
2454             gegl_buffer_set_color_from_pixel (dst, dst_rect, color,
2455                                               gegl_babl_rgba_linear_float ());
2456 
2457             return;
2458           }
2459 
2460         default:
2461           break;
2462         }
2463     }
2464 
2465   if (src->soft_format == dst->soft_format &&
2466       src_rect->width >= src->tile_width &&
2467       src_rect->height >= src->tile_height &&
2468       src->tile_width == dst->tile_width  &&
2469       src->tile_height == dst->tile_height &&
2470       !g_object_get_data (G_OBJECT (dst), "is-linear") &&
2471       gegl_buffer_scan_compatible (src, src_rect->x, src_rect->y,
2472                                    dst, dst_rect->x, dst_rect->y))
2473     {
2474       gint tile_width  = dst->tile_width;
2475       gint tile_height = dst->tile_height;
2476 
2477       GeglRectangle cow_rect;
2478       gint          rem;
2479 
2480       gegl_rectangle_intersect (&cow_rect, src_rect, &src->abyss);
2481 
2482       cow_rect.x += dst_rect->x - src_rect->x;
2483       cow_rect.y += dst_rect->y - src_rect->y;
2484 
2485       /* adjust origin to match the start of tile alignment */
2486       rem = (cow_rect.x + dst->shift_x) % tile_width;
2487       if (rem > 0)
2488         rem -= tile_width;
2489       cow_rect.x      -= rem;
2490       cow_rect.width  += rem;
2491 
2492       rem = (cow_rect.y + dst->shift_y) % tile_height;
2493       if (rem > 0)
2494         rem -= tile_height;
2495       cow_rect.y      -= rem;
2496       cow_rect.height += rem;
2497 
2498       /* adjust size of rect to match multiple of tiles */
2499       cow_rect.width  -= cow_rect.width  % tile_width;
2500       cow_rect.height -= cow_rect.height % tile_height;
2501 
2502       if (cow_rect.width > 0 && cow_rect.height > 0)
2503         {
2504           GeglRectangle top, bottom, left, right;
2505 
2506           /* iterate over rectangle that can be cow copied, duplicating
2507            * one and one tile
2508            */
2509           {
2510             /* first we do a dumb copy,. but with fetched tiles */
2511             GeglTileSource       *source = GEGL_TILE_SOURCE (src->tile_storage);
2512             GeglTileHandlerCache *cache  = dst->tile_storage->cache;
2513             gboolean fast_copy;
2514             gint dst_x, dst_y;
2515 
2516             /* we only attempt performing a fast copy, using the TILE_COPY
2517              * command, if the source buffer doesn't have any user-provided
2518              * tile handlers.  the problem with user-provided tile handlers is
2519              * that they might intenrally track the validity of tile contents,
2520              * in a way that's opaque to gegl, and the only way to know for
2521              * sure a given tile is valid, is to fetch it using TILE_GET.
2522              *
2523              * in the future, it might make sense to relax this condition, by
2524              * adding some api, or changing the requirements of tile handlers.
2525              */
2526             fast_copy = (src->tile_storage->n_user_handlers == 0);
2527 
2528             if (src->tile_storage < dst->tile_storage)
2529               {
2530                 g_rec_mutex_lock (&src->tile_storage->mutex);
2531                 g_rec_mutex_lock (&dst->tile_storage->mutex);
2532               }
2533             else
2534               {
2535                 g_rec_mutex_lock (&dst->tile_storage->mutex);
2536                 g_rec_mutex_lock (&src->tile_storage->mutex);
2537               }
2538 
2539             for (dst_y = cow_rect.y + dst->shift_y; dst_y < cow_rect.y + dst->shift_y + cow_rect.height; dst_y += tile_height)
2540             for (dst_x = cow_rect.x + dst->shift_x; dst_x < cow_rect.x + dst->shift_x + cow_rect.width; dst_x += tile_width)
2541               {
2542                 gint src_x, src_y;
2543                 gint stx, sty, dtx, dty;
2544 
2545                 src_x = dst_x + (src_rect->x - dst_rect->x) + (src->shift_x - dst->shift_x);
2546                 src_y = dst_y + (src_rect->y - dst_rect->y) + (src->shift_y - dst->shift_y);
2547 
2548                 stx = gegl_tile_indice (src_x, tile_width);
2549                 sty = gegl_tile_indice (src_y, tile_height);
2550                 dtx = gegl_tile_indice (dst_x, tile_width);
2551                 dty = gegl_tile_indice (dst_y, tile_height);
2552 
2553                 if (! fast_copy ||
2554                     ! gegl_tile_source_copy (source, stx, sty, 0,
2555                                              dst,    dtx, dty, 0))
2556                   {
2557                     GeglTile *src_tile;
2558                     GeglTile *dst_tile;
2559 
2560                     src_tile = gegl_tile_source_get_tile (source, stx, sty, 0);
2561 
2562                     dst_tile = gegl_tile_dup (src_tile);
2563                     dst_tile->tile_storage = dst->tile_storage;
2564                     dst_tile->x = dtx;
2565                     dst_tile->y = dty;
2566                     dst_tile->z = 0;
2567 
2568                     gegl_tile_handler_cache_insert (cache, dst_tile, dtx, dty, 0);
2569 
2570                     gegl_tile_unref (dst_tile);
2571                     gegl_tile_unref (src_tile);
2572                   }
2573               }
2574 
2575             g_rec_mutex_unlock (&src->tile_storage->mutex);
2576 
2577             gegl_tile_handler_damage_rect (
2578               GEGL_TILE_HANDLER (dst->tile_storage),
2579               GEGL_RECTANGLE (cow_rect.x + dst->shift_x,
2580                               cow_rect.y + dst->shift_y,
2581                               cow_rect.width,
2582                               cow_rect.height));
2583 
2584             g_rec_mutex_unlock (&dst->tile_storage->mutex);
2585           }
2586 
2587           top = *dst_rect;
2588           top.height = (cow_rect.y - dst_rect->y);
2589 
2590 
2591           left = *dst_rect;
2592           left.y = cow_rect.y;
2593           left.height = cow_rect.height;
2594           left.width = (cow_rect.x - dst_rect->x);
2595 
2596           bottom = *dst_rect;
2597           bottom.y = (cow_rect.y + cow_rect.height);
2598           bottom.height = (dst_rect->y + dst_rect->height) -
2599                           (cow_rect.y  + cow_rect.height);
2600 
2601           if (bottom.height < 0)
2602             bottom.height = 0;
2603 
2604           right  =  *dst_rect;
2605           right.x = (cow_rect.x + cow_rect.width);
2606           right.width = (dst_rect->x + dst_rect->width) -
2607                           (cow_rect.x  + cow_rect.width);
2608           right.y = cow_rect.y;
2609           right.height = cow_rect.height;
2610 
2611           if (right.width < 0)
2612             right.width = 0;
2613 
2614           if (top.height)
2615           gegl_buffer_copy2 (src,
2616                              GEGL_RECTANGLE (src_rect->x + (top.x-dst_rect->x),
2617                                              src_rect->y + (top.y-dst_rect->y),
2618                                  top.width, top.height),
2619                              repeat_mode, dst, &top);
2620           if (bottom.height)
2621           gegl_buffer_copy2 (src,
2622                              GEGL_RECTANGLE (src_rect->x + (bottom.x-dst_rect->x),
2623                                              src_rect->y + (bottom.y-dst_rect->y),
2624                                  bottom.width, bottom.height),
2625                              repeat_mode, dst, &bottom);
2626           if (left.width && left.height)
2627           gegl_buffer_copy2 (src,
2628                              GEGL_RECTANGLE (src_rect->x + (left.x-dst_rect->x),
2629                                              src_rect->y + (left.y-dst_rect->y),
2630                                  left.width, left.height),
2631                              repeat_mode, dst, &left);
2632           if (right.width && right.height)
2633           gegl_buffer_copy2 (src,
2634                              GEGL_RECTANGLE (src_rect->x + (right.x-dst_rect->x),
2635                                              src_rect->y + (right.y-dst_rect->y),
2636                                  right.width, right.height),
2637                              repeat_mode, dst, &right);
2638         }
2639       else
2640         {
2641           gegl_buffer_copy2 (src, src_rect, repeat_mode, dst, dst_rect);
2642         }
2643     }
2644   else
2645     {
2646       gegl_buffer_copy2 (src, src_rect, repeat_mode, dst, dst_rect);
2647     }
2648 
2649   gegl_buffer_emit_changed_signal (dst, dst_rect);
2650 }
2651 
2652 typedef void (* GeglBufferTileFunc) (GeglBuffer          *buffer,
2653                                      gint                 tile_x,
2654                                      gint                 tile_y,
2655                                      gpointer             data);
2656 typedef void (* GeglBufferRectFunc) (GeglBuffer          *buffer,
2657                                      const GeglRectangle *rect,
2658                                      gpointer             data);
2659 
2660 static void
gegl_buffer_foreach_tile(GeglBuffer * buffer,const GeglRectangle * rect,GeglBufferTileFunc tile_func,GeglBufferRectFunc rect_func,gpointer data)2661 gegl_buffer_foreach_tile (GeglBuffer          *buffer,
2662                           const GeglRectangle *rect,
2663                           GeglBufferTileFunc   tile_func,
2664                           GeglBufferRectFunc   rect_func,
2665                           gpointer             data)
2666 {
2667   if (! rect)
2668     rect = gegl_buffer_get_extent (buffer);
2669 
2670   if (G_UNLIKELY (rect->width <= 0 || rect->height <= 0))
2671     return;
2672 
2673 #if 1
2674   /* cow for clearing is currently broken */
2675   if (rect->width  >= buffer->tile_width  &&
2676       rect->height >= buffer->tile_height &&
2677       ! g_object_get_data (G_OBJECT (buffer), "is-linear"))
2678     {
2679       gint tile_width  = buffer->tile_width;
2680       gint tile_height = buffer->tile_height;
2681 
2682       GeglRectangle tile_rect = *rect;
2683       gint          rem;
2684 
2685       /* shift rect to tile coordinate system */
2686       tile_rect.x += buffer->shift_x;
2687       tile_rect.y += buffer->shift_y;
2688 
2689       /* adjust origin to match the start of tile alignment */
2690       rem = tile_rect.x % tile_width;
2691       if (rem > 0)
2692         rem -= tile_width;
2693       tile_rect.x      -= rem;
2694       tile_rect.width  += rem;
2695 
2696       rem = tile_rect.y % tile_height;
2697       if (rem > 0)
2698         rem -= tile_height;
2699       tile_rect.y      -= rem;
2700       tile_rect.height += rem;
2701 
2702       /* adjust size of rect to match multiple of tiles */
2703       tile_rect.width  -= tile_rect.width  % tile_width;
2704       tile_rect.height -= tile_rect.height % tile_height;
2705 
2706       if (tile_rect.width > 0 && tile_rect.height > 0)
2707         {
2708           GeglRectangle top, bottom, left, right;
2709 
2710           /* iterate over the tile rectangle */
2711           {
2712             gint x, y;
2713 
2714             g_rec_mutex_lock (&buffer->tile_storage->mutex);
2715 
2716             for (y = tile_rect.y;
2717                  y < tile_rect.y + tile_rect.height;
2718                  y += tile_height)
2719               {
2720                 for (x = tile_rect.x;
2721                      x < tile_rect.x + tile_rect.width;
2722                      x += tile_width)
2723                   {
2724                     gint tile_x, tile_y;
2725 
2726                     tile_x = gegl_tile_indice (x, tile_width);
2727                     tile_y = gegl_tile_indice (y, tile_height);
2728 
2729                     tile_func (buffer, tile_x, tile_y, data);
2730                   }
2731               }
2732 
2733             gegl_tile_handler_damage_rect (
2734               GEGL_TILE_HANDLER (buffer->tile_storage),
2735               &tile_rect);
2736 
2737             g_rec_mutex_unlock (&buffer->tile_storage->mutex);
2738           }
2739 
2740           /* shift rect to buffer coordinate system */
2741           tile_rect.x -= buffer->shift_x;
2742           tile_rect.y -= buffer->shift_y;
2743 
2744           top = *rect;
2745           top.height = (tile_rect.y - rect->y);
2746 
2747 
2748           left = *rect;
2749           left.y = tile_rect.y;
2750           left.height = tile_rect.height;
2751           left.width = (tile_rect.x - rect->x);
2752 
2753           bottom = *rect;
2754           bottom.y = (tile_rect.y + tile_rect.height);
2755           bottom.height = (rect->y + rect->height) -
2756                           (tile_rect.y  + tile_rect.height);
2757 
2758           if (bottom.height < 0)
2759             bottom.height = 0;
2760 
2761           right  =  *rect;
2762           right.x = (tile_rect.x + tile_rect.width);
2763           right.width = (rect->x + rect->width) -
2764                           (tile_rect.x  + tile_rect.width);
2765           right.y = tile_rect.y;
2766           right.height = tile_rect.height;
2767 
2768           if (right.width < 0)
2769             right.width = 0;
2770 
2771           if (top.height)                  rect_func (buffer, &top,    data);
2772           if (bottom.height)               rect_func (buffer, &bottom, data);
2773           if (left.width && left.height)   rect_func (buffer, &left,   data);
2774           if (right.width && right.height) rect_func (buffer, &right,  data);
2775         }
2776       else
2777         {
2778           rect_func (buffer, rect, data);
2779         }
2780     }
2781   else
2782 #endif
2783     {
2784       rect_func (buffer, rect, data);
2785     }
2786 
2787   gegl_buffer_emit_changed_signal (buffer, rect);
2788 }
2789 
2790 static void
gegl_buffer_clear_tile(GeglBuffer * dst,gint tile_x,gint tile_y,gpointer data)2791 gegl_buffer_clear_tile (GeglBuffer *dst,
2792                         gint        tile_x,
2793                         gint        tile_y,
2794                         gpointer    data)
2795 {
2796   if (dst->initialized)
2797     {
2798       gegl_tile_handler_cache_remove (dst->tile_storage->cache,
2799                                       tile_x, tile_y, 0);
2800 
2801       gegl_tile_handler_source_command (dst->tile_storage->cache,
2802                                         GEGL_TILE_VOID,
2803                                         tile_x, tile_y, 0, NULL);
2804     }
2805   else
2806     {
2807       GeglTile *tile;
2808 
2809       tile = gegl_tile_handler_empty_new_tile (dst->tile_storage->tile_size);
2810 
2811       gegl_tile_handler_cache_insert (dst->tile_storage->cache, tile,
2812                                       tile_x, tile_y, 0);
2813 
2814       gegl_tile_unref (tile);
2815     }
2816 }
2817 
2818 static void
gegl_buffer_clear_rect(GeglBuffer * dst,const GeglRectangle * dst_rect,gpointer data)2819 gegl_buffer_clear_rect (GeglBuffer          *dst,
2820                         const GeglRectangle *dst_rect,
2821                         gpointer             data)
2822 {
2823   GeglBufferIterator *i;
2824   gint                pxsize;
2825 
2826   pxsize = babl_format_get_bytes_per_pixel (dst->soft_format);
2827 
2828   if (gegl_buffer_ext_invalidate)
2829     {
2830       gegl_buffer_ext_invalidate (dst, dst_rect);
2831     }
2832 
2833   i = gegl_buffer_iterator_new (dst, dst_rect, 0, dst->soft_format,
2834                                 GEGL_ACCESS_WRITE | GEGL_ITERATOR_NO_NOTIFY,
2835                                 GEGL_ABYSS_NONE, 1);
2836   while (gegl_buffer_iterator_next (i))
2837     {
2838       memset (((guchar*)(i->items[0].data)), 0, i->length * pxsize);
2839     }
2840 }
2841 
2842 void
gegl_buffer_clear(GeglBuffer * dst,const GeglRectangle * dst_rect)2843 gegl_buffer_clear (GeglBuffer          *dst,
2844                    const GeglRectangle *dst_rect)
2845 {
2846   g_return_if_fail (GEGL_IS_BUFFER (dst));
2847 
2848   gegl_buffer_foreach_tile (dst, dst_rect,
2849                             gegl_buffer_clear_tile,
2850                             gegl_buffer_clear_rect,
2851                             NULL);
2852 }
2853 
2854 void
gegl_buffer_set_pattern(GeglBuffer * buffer,const GeglRectangle * rect,GeglBuffer * pattern,gint x_offset,gint y_offset)2855 gegl_buffer_set_pattern (GeglBuffer          *buffer,
2856                          const GeglRectangle *rect,
2857                          GeglBuffer          *pattern,
2858                          gint                 x_offset,
2859                          gint                 y_offset)
2860 {
2861   const GeglRectangle *pattern_extent;
2862   const Babl          *buffer_format;
2863   GeglRectangle        roi;                  /* the rect if not NULL, else the whole buffer */
2864   GeglRectangle        pattern_data_extent;  /* pattern_extent clamped to rect */
2865   GeglRectangle        extended_data_extent; /* many patterns to avoid copying too small chunks of data */
2866   gint                 bpp;
2867   gint                 x, y;
2868   gint                 rowstride;
2869   gpointer             pattern_data;
2870 
2871   g_return_if_fail (GEGL_IS_BUFFER (buffer));
2872   g_return_if_fail (GEGL_IS_BUFFER (pattern));
2873 
2874   if (rect != NULL)
2875     roi = *rect;
2876   else
2877     roi = *gegl_buffer_get_extent (buffer);
2878 
2879   pattern_extent = gegl_buffer_get_extent (pattern);
2880   buffer_format  = gegl_buffer_get_format (buffer);
2881 
2882   pattern_data_extent.x      = - x_offset + roi.x;
2883   pattern_data_extent.y      = - y_offset + roi.y;
2884   pattern_data_extent.width  = MIN (pattern_extent->width,  roi.width);
2885   pattern_data_extent.height = MIN (pattern_extent->height, roi.height);
2886 
2887   /* Sanity */
2888   if (pattern_data_extent.width < 1 || pattern_data_extent.height < 1)
2889     return;
2890 
2891   bpp = babl_format_get_bytes_per_pixel (buffer_format);
2892 
2893   extended_data_extent = pattern_data_extent;
2894 
2895   /* Avoid gegl_buffer_set on too small chunks */
2896   extended_data_extent.width  *= (buffer->tile_width  * 2 +
2897                                   (extended_data_extent.width  - 1)) /
2898                                  extended_data_extent.width;
2899   extended_data_extent.width   = MIN (extended_data_extent.width,  roi.width);
2900 
2901   extended_data_extent.height *= (buffer->tile_height * 2 +
2902                                   (extended_data_extent.height - 1)) /
2903                                  extended_data_extent.height;
2904   extended_data_extent.height  = MIN (extended_data_extent.height, roi.height);
2905 
2906   /* XXX: Bad taste, the pattern needs to be small enough.
2907    * See Bug 712814 for an alternative malloc-free implementation */
2908   pattern_data = gegl_scratch_alloc (extended_data_extent.width *
2909                                      extended_data_extent.height *
2910                                      bpp);
2911 
2912   rowstride = extended_data_extent.width * bpp;
2913 
2914   /* only do babl conversions once on the whole pattern */
2915   gegl_buffer_get (pattern, &pattern_data_extent, 1.0,
2916                    buffer_format, pattern_data,
2917                    rowstride, GEGL_ABYSS_LOOP);
2918 
2919   /* fill the remaining space by duplicating the small pattern */
2920   for (y = 0; y < pattern_data_extent.height; y++)
2921     for (x = pattern_extent->width;
2922          x < extended_data_extent.width;
2923          x *= 2)
2924       {
2925         guchar *src  = ((guchar*) pattern_data) + y * rowstride;
2926         guchar *dst  = src + x * bpp;
2927         gint    size = bpp * MIN (extended_data_extent.width - x, x);
2928         memcpy (dst, src, size);
2929       }
2930 
2931   for (y = pattern_extent->height;
2932        y < extended_data_extent.height;
2933        y *= 2)
2934     {
2935       guchar *src  = ((guchar*) pattern_data);
2936       guchar *dst  = src + y * rowstride;
2937       gint    size = rowstride * MIN (extended_data_extent.height - y, y);
2938       memcpy (dst, src, size);
2939     }
2940 
2941   /* Now fill the acutal buffer */
2942   for (y = roi.y; y < roi.y + roi.height; y += extended_data_extent.height)
2943     for (x = roi.x; x < roi.x + roi.width; x += extended_data_extent.width)
2944       {
2945         GeglRectangle dest_rect = {x, y,
2946                                    extended_data_extent.width,
2947                                    extended_data_extent.height};
2948 
2949         gegl_rectangle_intersect (&dest_rect, &dest_rect, &roi);
2950 
2951         gegl_buffer_set (buffer, &dest_rect, 0, buffer_format,
2952                          pattern_data, rowstride);
2953       }
2954 
2955   gegl_scratch_free (pattern_data);
2956 }
2957 
2958 typedef struct
2959 {
2960   gconstpointer  pixel;
2961   gint           bpp;
2962 
2963   GeglTile      *tile;
2964 } SetColorFromPixelData;
2965 
2966 static void
gegl_buffer_set_color_from_pixel_tile(GeglBuffer * dst,gint tile_x,gint tile_y,SetColorFromPixelData * data)2967 gegl_buffer_set_color_from_pixel_tile (GeglBuffer            *dst,
2968                                        gint                   tile_x,
2969                                        gint                   tile_y,
2970                                        SetColorFromPixelData *data)
2971 {
2972   GeglTile *tile;
2973 
2974   if (data->tile)
2975     {
2976       tile = gegl_tile_dup (data->tile);
2977     }
2978   else
2979     {
2980       gint tile_size = dst->tile_storage->tile_size;
2981 
2982       if (gegl_memeq_zero (data->pixel, data->bpp))
2983         {
2984           tile = gegl_tile_handler_empty_new_tile (tile_size);
2985         }
2986       else
2987         {
2988           tile = gegl_tile_new (tile_size);
2989 
2990           gegl_tile_lock (tile);
2991 
2992           gegl_memset_pattern (gegl_tile_get_data (tile),
2993                                data->pixel,
2994                                data->bpp,
2995                                tile_size / data->bpp);
2996 
2997           gegl_tile_unlock (tile);
2998         }
2999     }
3000 
3001   gegl_tile_handler_cache_insert (dst->tile_storage->cache, tile,
3002                                   tile_x, tile_y, 0);
3003 
3004   if (data->tile)
3005     gegl_tile_unref (tile);
3006   else
3007     data->tile = tile;
3008 }
3009 
3010 static void
gegl_buffer_set_color_from_pixel_rect(GeglBuffer * dst,const GeglRectangle * dst_rect,SetColorFromPixelData * data)3011 gegl_buffer_set_color_from_pixel_rect (GeglBuffer            *dst,
3012                                        const GeglRectangle   *dst_rect,
3013                                        SetColorFromPixelData *data)
3014 {
3015   GeglBufferIterator *i;
3016 
3017   i = gegl_buffer_iterator_new (dst, dst_rect, 0, dst->soft_format,
3018                                 GEGL_ACCESS_WRITE | GEGL_ITERATOR_NO_NOTIFY,
3019                                 GEGL_ABYSS_NONE, 1);
3020   while (gegl_buffer_iterator_next (i))
3021     {
3022       gegl_memset_pattern (i->items[0].data,
3023                            data->pixel,
3024                            data->bpp,
3025                            i->length);
3026     }
3027 }
3028 
3029 void
gegl_buffer_set_color_from_pixel(GeglBuffer * dst,const GeglRectangle * dst_rect,gconstpointer pixel,const Babl * pixel_format)3030 gegl_buffer_set_color_from_pixel (GeglBuffer          *dst,
3031                                   const GeglRectangle *dst_rect,
3032                                   gconstpointer        pixel,
3033                                   const Babl          *pixel_format)
3034 {
3035   SetColorFromPixelData data = {};
3036 
3037   g_return_if_fail (GEGL_IS_BUFFER (dst));
3038   g_return_if_fail (pixel);
3039   if (pixel_format == NULL)
3040     pixel_format = dst->soft_format;
3041 
3042   if (!dst_rect)
3043     {
3044       dst_rect = gegl_buffer_get_extent (dst);
3045     }
3046   if (dst_rect->width <= 0 ||
3047       dst_rect->height <= 0)
3048     return;
3049 
3050   data.bpp = babl_format_get_bytes_per_pixel (dst->soft_format);
3051 
3052   /* convert the pixel data to the buffer format */
3053   if (pixel_format == dst->soft_format)
3054     {
3055       data.pixel = pixel;
3056     }
3057   else
3058     {
3059       data.pixel = g_alloca (data.bpp);
3060 
3061       babl_process (babl_fish (pixel_format, dst->soft_format),
3062                     pixel, (gpointer) data.pixel, 1);
3063     }
3064 
3065   gegl_buffer_foreach_tile (
3066     dst, dst_rect,
3067     (GeglBufferTileFunc) gegl_buffer_set_color_from_pixel_tile,
3068     (GeglBufferRectFunc) gegl_buffer_set_color_from_pixel_rect,
3069     &data);
3070 
3071   if (data.tile)
3072     gegl_tile_unref (data.tile);
3073 }
3074 
3075 GeglBuffer *
gegl_buffer_dup(GeglBuffer * buffer)3076 gegl_buffer_dup (GeglBuffer *buffer)
3077 {
3078   GeglBuffer *new_buffer;
3079 
3080   g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
3081 
3082   new_buffer = g_object_new (GEGL_TYPE_BUFFER,
3083                              "format",       buffer->soft_format,
3084                              "x",            buffer->extent.x,
3085                              "y",            buffer->extent.y,
3086                              "width",        buffer->extent.width,
3087                              "height",       buffer->extent.height,
3088                              "abyss-x",      buffer->abyss.x,
3089                              "abyss-y",      buffer->abyss.y,
3090                              "abyss-width",  buffer->abyss.width,
3091                              "abyss-height", buffer->abyss.height,
3092                              "shift-x",      buffer->shift_x,
3093                              "shift-y",      buffer->shift_y,
3094                              "tile-width",   buffer->tile_width,
3095                              "tile-height",  buffer->tile_height,
3096                              NULL);
3097 
3098   gegl_buffer_copy (buffer, gegl_buffer_get_extent (buffer), GEGL_ABYSS_NONE,
3099                     new_buffer, gegl_buffer_get_extent (buffer));
3100 
3101   return new_buffer;
3102 }
3103 
3104 /*
3105  *  check whether iterations on two buffers starting from the given coordinates with
3106  *  the same width and height would be able to run parallell.
3107  */
gegl_buffer_scan_compatible(GeglBuffer * bufferA,gint xA,gint yA,GeglBuffer * bufferB,gint xB,gint yB)3108 gboolean gegl_buffer_scan_compatible (GeglBuffer *bufferA,
3109                                       gint        xA,
3110                                       gint        yA,
3111                                       GeglBuffer *bufferB,
3112                                       gint        xB,
3113                                       gint        yB)
3114 {
3115   if (bufferA->tile_storage->tile_width !=
3116       bufferB->tile_storage->tile_width)
3117     return FALSE;
3118   if (bufferA->tile_storage->tile_height !=
3119       bufferB->tile_storage->tile_height)
3120     return FALSE;
3121   if ( (abs((bufferA->shift_x+xA) - (bufferB->shift_x+xB))
3122         % bufferA->tile_storage->tile_width) != 0)
3123     return FALSE;
3124   if ( (abs((bufferA->shift_y+yA) - (bufferB->shift_y+yB))
3125         % bufferA->tile_storage->tile_height) != 0)
3126     return FALSE;
3127   return TRUE;
3128 }
3129 
3130