[gegl] buffers: improve gegl_buffer_set_pattern



commit 4cf196003ddbfd1a7bf314a1eab7acc9589b95b6
Author: Téo Mazars <teomazars gmail com>
Date:   Wed Nov 20 19:54:23 2013 +0100

    buffers: improve gegl_buffer_set_pattern
    
    - don't ignore the input rectangle
    - offsets are integer
    - limit the number of babl conversions
    - that implementation linearizes the whole pattern in a single memory chunk,
      which is still bad
    - See Bug 712814 for more details

 gegl/buffer/gegl-buffer-access.c |  113 +++++++++++++++++++++++++++++--------
 gegl/buffer/gegl-buffer.h        |   10 ++-
 2 files changed, 94 insertions(+), 29 deletions(-)
---
diff --git a/gegl/buffer/gegl-buffer-access.c b/gegl/buffer/gegl-buffer-access.c
index 1b8d665..cb90999 100644
--- a/gegl/buffer/gegl-buffer-access.c
+++ b/gegl/buffer/gegl-buffer-access.c
@@ -1695,41 +1695,104 @@ gegl_buffer_clear (GeglBuffer          *dst,
 
 void
 gegl_buffer_set_pattern (GeglBuffer          *buffer,
-                         const GeglRectangle *rect, /* XXX:should be respected*/
+                         const GeglRectangle *rect,
                          GeglBuffer          *pattern,
-                         gdouble              x_offset,
-                         gdouble              y_offset)
+                         gint                 x_offset,
+                         gint                 y_offset)
 {
-  GeglRectangle src_rect = {0,}, dst_rect;
-  int pat_width, pat_height;
-  int cols, rows;
-  int col, row;
-  int width, height;
+  const GeglRectangle *pattern_extent;
+  const Babl          *buffer_format;
+  GeglRectangle        roi;                  /* the rect if not NULL, else the whole buffer */
+  GeglRectangle        pattern_data_extent;  /* pattern_extent clamped to rect */
+  GeglRectangle        extended_data_extent; /* many patterns to avoid copying too small chunks of data */
+  gint                 bpp;
+  gint                 x, y;
+  gint                 rowstride;
+  gpointer             pattern_data;
 
-  pat_width  = gegl_buffer_get_width (pattern);
-  pat_height = gegl_buffer_get_height (pattern);
-  width      = gegl_buffer_get_width (buffer);
-  height     = gegl_buffer_get_height (buffer);
+  g_return_if_fail (GEGL_IS_BUFFER (buffer));
+  g_return_if_fail (GEGL_IS_BUFFER (pattern));
+
+  if (rect != NULL)
+    roi = *rect;
+  else
+    roi = *gegl_buffer_get_extent (buffer);
+
+  pattern_extent = gegl_buffer_get_extent (pattern);
+  buffer_format  = gegl_buffer_get_format (buffer);
+
+  pattern_data_extent.x      = - x_offset + roi.x;
+  pattern_data_extent.y      = - y_offset + roi.y;
+  pattern_data_extent.width  = MIN (pattern_extent->width,  roi.width);
+  pattern_data_extent.height = MIN (pattern_extent->height, roi.height);
+
+  /* Sanity */
+  if (pattern_data_extent.width < 1 || pattern_data_extent.height < 1)
+    return;
+
+  bpp = babl_format_get_bytes_per_pixel (buffer_format);
+
+  extended_data_extent = pattern_data_extent;
+
+  /* Avoid gegl_buffer_set on too small chunks */
+  while (extended_data_extent.width < buffer->tile_width * 2 &&
+         extended_data_extent.width < roi.width)
+    extended_data_extent.width += pattern_extent->width;
 
-  while (y_offset < 0) y_offset += pat_height;
-  while (x_offset < 0) x_offset += pat_width;
+  while (extended_data_extent.height < buffer->tile_height * 2 &&
+         extended_data_extent.height < roi.height)
+    extended_data_extent.height += pattern_extent->height;
 
-  x_offset = fmod (x_offset, pat_width);
-  y_offset = fmod (y_offset, pat_height);
+  /* XXX: Bad taste, the pattern needs to be small enough.
+   * See Bug 712814 for an alternative malloc-free implementation */
+  pattern_data = gegl_malloc (extended_data_extent.width *
+                              extended_data_extent.height *
+                              bpp);
 
-  src_rect.width  = dst_rect.width  = pat_width;
-  src_rect.height = dst_rect.height = pat_height;
+  rowstride = extended_data_extent.width * bpp;
 
-  cols = width  / pat_width  + 1;
-  rows = height / pat_height + 1;
+  /* only do babl conversions once on the whole pattern */
+  gegl_buffer_get (pattern, &pattern_data_extent, 1.0,
+                   buffer_format, pattern_data,
+                   rowstride, GEGL_ABYSS_LOOP);
 
-  for (row = 0; row <= rows + 1; row++)
-    for (col = 0; col <= cols + 1; col++)
+  /* fill the remaining space by duplicating the small pattern */
+  for (y = 0; y < pattern_data_extent.height; y++)
+    for (x = pattern_extent->width;
+         x < extended_data_extent.width;
+         x *= 2)
       {
-        dst_rect.x = x_offset + (col-1) * pat_width;
-        dst_rect.y = y_offset + (row-1) * pat_height;
-        gegl_buffer_copy (pattern, &src_rect, buffer, &dst_rect);
+        guchar *src  = ((guchar*) pattern_data) + y * rowstride;
+        guchar *dst  = src + x * bpp;
+        gint    size = bpp * MIN (extended_data_extent.width - x, x);
+        memcpy (dst, src, size);
       }
+
+  for (y = pattern_extent->height;
+       y < extended_data_extent.height;
+       y *= 2)
+    {
+      guchar *src  = ((guchar*) pattern_data);
+      guchar *dst  = src + y * rowstride;
+      gint    size = rowstride * MIN (extended_data_extent.height - y, y);
+      memcpy (dst, src, size);
+    }
+
+  /* Now fill the acutal buffer */
+  for (y = roi.y; y < roi.y + roi.height; y += extended_data_extent.height)
+    for (x = roi.x; x < roi.x + roi.width; x += extended_data_extent.width)
+      {
+        GeglRectangle dest_rect = {x, y,
+                                   extended_data_extent.width,
+                                   extended_data_extent.height};
+
+        gegl_rectangle_intersect (&dest_rect, &dest_rect, &roi);
+
+        gegl_buffer_set (buffer, &dest_rect, 1, buffer_format,
+                         pattern_data, rowstride);
+      }
+
+  gegl_free (pattern_data);
 }
 
 void
diff --git a/gegl/buffer/gegl-buffer.h b/gegl/buffer/gegl-buffer.h
index 6cde687..98029b5 100644
--- a/gegl/buffer/gegl-buffer.h
+++ b/gegl/buffer/gegl-buffer.h
@@ -310,18 +310,20 @@ void            gegl_buffer_set_color         (GeglBuffer          *buffer,
 /**
  * gegl_buffer_set_pattern:
  * @buffer: a #GeglBuffer
- * @rect: a rectangular region
+ * @rect: the region of @buffer to fill
  * @pattern: a #GeglBuffer to be repeated as a pattern
  * @x_offset: where the pattern starts horizontally
  * @y_offset: where the pattern starts vertical
  *
- * Fill a region with a repeating pattern.
+ * Fill a region with a repeating pattern. Offsets parameters are
+ * relative to the origin (0, 0) and not to the rectangle. So be carefull
+ * about the origin of @pattern and @buffer extents.
  */
 void            gegl_buffer_set_pattern       (GeglBuffer          *buffer,
                                                const GeglRectangle *rect,
                                                GeglBuffer          *pattern,
-                                               gdouble              x_offset,
-                                               gdouble              y_offset);
+                                               gint                 x_offset,
+                                               gint                 y_offset);
 
 /**
  * gegl_buffer_get_format: (skip)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]