[gegl] operations: rewrite the CPU version of pixelize.c



commit 3fa0f601dab771f8fbe5bff5813b67864ed108e9
Author: Téo Mazars <teo mazars ensimag fr>
Date:   Sun Oct 13 15:27:48 2013 +0200

    operations: rewrite the CPU version of pixelize.c
    
    - Clearer and faster in most cases
    - Don't try to allocate too much memory
    - Use different paths depending on the size of pixels
    - Handle properly the edges by not reading outside the extent
    - Fix the test so it does not look outside of the extent either

 operations/common/pixelize.c              |  278 +++++++++++++++++++----------
 tests/compositions/pixelize.xml           |    6 +
 tests/compositions/reference/pixelize.png |  Bin 5392 -> 4657 bytes
 3 files changed, 193 insertions(+), 91 deletions(-)
---
diff --git a/operations/common/pixelize.c b/operations/common/pixelize.c
index eb73269..4a5039c 100644
--- a/operations/common/pixelize.c
+++ b/operations/common/pixelize.c
@@ -38,8 +38,11 @@ gegl_chant_int_ui (size_y, _("Block Height"),
 #include "gegl-chant.h"
 
 #define CELL_X(px, cell_width)  ((px) / (cell_width))
-#define CELL_Y(py, cell_height)  ((py) / (cell_height))
+#define CELL_Y(py, cell_height) ((py) / (cell_height))
 
+#define CHUNK_SIZE              (1024)
+#define ALLOC_THRESHOLD_SIZE    (128)
+#define SQR(x)                  ((x)*(x))
 
 static void
 prepare (GeglOperation *operation)
@@ -47,8 +50,6 @@ prepare (GeglOperation *operation)
   GeglChantO              *o;
   GeglOperationAreaFilter *op_area;
 
-  const Babl *source_format = gegl_operation_get_source_format (operation, "input");
-
   op_area = GEGL_OPERATION_AREA_FILTER (operation);
   o       = GEGL_CHANT_PROPERTIES (operation);
 
@@ -57,88 +58,153 @@ prepare (GeglOperation *operation)
   op_area->top    =
   op_area->bottom = o->size_y;
 
-  gegl_operation_set_format (operation, "input",
-                             babl_format ("RaGaBaA float"));
-
-  if (source_format && !babl_format_has_alpha (source_format))
-    gegl_operation_set_format (operation, "output", babl_format ("RGB float"));
-  else
-    gegl_operation_set_format (operation, "output", babl_format ("RaGaBaA float"));
+  gegl_operation_set_format (operation, "input",  babl_format ("RaGaBaA float"));
+  gegl_operation_set_format (operation, "output", babl_format ("RaGaBaA float"));
 }
 
+
 static void
-calc_block_colors (gfloat* block_colors,
-                   const gfloat* input,
-                   const GeglRectangle* roi,
-                   gint size_x,
-                   gint size_y)
+mean_rectangle_noalloc (GeglBuffer    *input,
+                        GeglRectangle *rect,
+                        GeglColor     *color)
 {
-  gint cx0 = CELL_X(roi->x, size_x);
-  gint cy0 = CELL_Y(roi->y, size_y);
-  gint cx1 = CELL_X(roi->x + roi->width - 1, size_x);
-  gint cy1 = CELL_Y(roi->y + roi->height - 1, size_y);
-
-  gint cx;
-  gint cy;
-  gfloat weight = 1.0f / (size_x * size_y);
-  gint line_width = roi->width + 2*size_x;
-  /* loop over the blocks within the region of interest */
-  for (cy=cy0; cy<=cy1; ++cy)
+  GeglBufferIterator *gi;
+  gfloat              col[] = {0.0, 0.0, 0.0, 0.0};
+  gint                c;
+
+  gi = gegl_buffer_iterator_new (input, rect, 0, babl_format ("RaGaBaA float"),
+                                 GEGL_BUFFER_READ, GEGL_ABYSS_CLAMP);
+
+  while (gegl_buffer_iterator_next (gi))
     {
-      for (cx=cx0; cx<=cx1; ++cx)
+      gint  k;
+      gfloat *data = (gfloat*) gi->data[0];
+
+      for (k = 0; k < gi->length; k++)
         {
-          gint px = (cx * size_x) - roi->x + size_x;
-          gint py = (cy * size_y) - roi->y + size_y;
-
-          /* calculate the average color for this block */
-          gint j,i,c;
-          gfloat col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-          for (j=py; j<py+size_y; ++j)
-            {
-              for (i=px; i<px+size_x; ++i)
-                {
-                  for (c=0; c<4; ++c)
-                    col[c] += input[(j*line_width + i)*4 + c];
-                }
-            }
-          for (c=0; c<4; ++c)
-            block_colors[c] = weight * col[c];
-          block_colors += 4;
+          for (c = 0; c < 4; c++)
+            col[c] += data[c];
+
+          data += 4;
         }
     }
+
+  for (c = 0; c < 4; c++)
+    col[c] /= rect->width * rect->height;
+
+  gegl_color_set_pixel (color, babl_format ("RaGaBaA float"), col);
 }
 
 static void
-pixelize (gfloat* buf,
-          const GeglRectangle* roi,
-          gint size_x,
-          gint size_y)
+mean_rectangle (gfloat        *input,
+                GeglRectangle *rect,
+                gint           rowstride,
+                gfloat        *color)
 {
-  gint cx0 = CELL_X(roi->x, size_x);
-  gint cy0 = CELL_Y(roi->y, size_y);
-  gint block_count_x = CELL_X(roi->x + roi->width - 1, size_x) - cx0 + 1;
-  gint block_count_y = CELL_Y(roi->y + roi->height - 1, size_y) - cy0 + 1;
-  gfloat* block_colors = g_new0 (gfloat, block_count_x * block_count_y * 4);
-  gint x;
-  gint y;
-  gint c;
-
-  /* calculate the average color of all the blocks */
-  calc_block_colors(block_colors, buf, roi, size_x, size_y);
-
-  /* set each pixel to the average color of the block it belongs to */
-  for (y=0; y<roi->height; ++y)
-    {
-      gint cy = CELL_Y(y + roi->y, size_y) - cy0;
-      for (x=0; x<roi->width; ++x)
-        {
-          gint cx = CELL_X(x + roi->x, size_x) - cx0;
-          for (c=0; c<4; ++c)
-            *buf++ = block_colors[(cy*block_count_x + cx)*4 + c];
-        }
-    }
+  gint c, x, y;
+
+  for (c = 0; c < 4; c++)
+    color[c] = 0;
+
+  for (y = rect->y; y < rect->y + rect->height; y++)
+    for (x = rect->x; x < rect->x + rect->width; x++)
+      for (c = 0; c < 4; c++)
+        color[c] += input [4 * (y * rowstride + x) + c];
+
+  for (c = 0; c < 4; c++)
+    color[c] /= rect->width * rect->height;
+}
+
+static void
+set_rectangle (gfloat        *output,
+               GeglRectangle *rect,
+               gint           rowstride,
+               gfloat        *color)
+{
+  gint c, x, y;
+
+  for (y = rect->y; y < rect->y + rect->height; y++)
+    for (x = rect->x; x < rect->x + rect->width; x++)
+      for (c = 0; c < 4; c++)
+        output [4 * (y * rowstride + x) + c] = color[c];
+}
+
+
+static void
+pixelize_noalloc (GeglBuffer          *input,
+                  GeglBuffer          *output,
+                  const GeglRectangle *roi,
+                  const GeglRectangle *whole_region,
+                  gint                 size_x,
+                  gint                 size_y)
+{
+  gint start_x = (roi->x / size_x) * size_x;
+  gint start_y = (roi->y / size_y) * size_y;
+  gint x, y;
+
+  GeglColor *color = gegl_color_new ("white");
+
+  for (y = start_y; y < roi->y + roi->height; y += size_y)
+    for (x = start_x; x < roi->x + roi->width; x += size_x)
+      {
+        GeglRectangle rect = {x, y, size_x, size_y};
 
-  g_free (block_colors);
+        gegl_rectangle_intersect (&rect, whole_region, &rect);
+
+        if (rect.width < 1 || rect.height < 1)
+          continue;
+
+        mean_rectangle_noalloc (input, &rect, color);
+
+        gegl_rectangle_intersect (&rect, roi, &rect);
+
+        gegl_buffer_set_color (output, &rect, color);
+      }
+
+  g_object_unref (color);
+}
+
+static void
+pixelize (gfloat              *input,
+          gfloat              *output,
+          const GeglRectangle *roi,
+          const GeglRectangle *extended_roi,
+          const GeglRectangle *whole_region,
+          gint                 size_x,
+          gint                 size_y)
+{
+  gint start_x = (roi->x / size_x) * size_x;
+  gint start_y = (roi->y / size_y) * size_y;
+  gint x, y;
+  gfloat color[4];
+
+  for (y = start_y; y < roi->y + roi->height; y += size_y)
+    for (x = start_x; x < roi->x + roi->width; x += size_x)
+      {
+        GeglRectangle rect = {x, y, size_x, size_y};
+        GeglRectangle rect2;
+
+        gegl_rectangle_intersect (&rect, whole_region, &rect);
+
+        if (rect.width < 1 || rect.height < 1)
+          continue;
+
+        rect2.x = rect.x - extended_roi->x;
+        rect2.y = rect.y - extended_roi->y;
+        rect2.width  = rect.width;
+        rect2.height = rect.height;
+
+        mean_rectangle (input, &rect2, extended_roi->width, color);
+
+        gegl_rectangle_intersect (&rect, roi, &rect);
+
+        rect2.x = rect.x - roi->x;
+        rect2.y = rect.y - roi->y;
+        rect2.width  = rect.width;
+        rect2.height = rect.height;
+
+        set_rectangle (output, &rect2, roi->width, color);
+      }
 }
 
 #include "opencl/gegl-cl.h"
@@ -232,8 +298,6 @@ cl_process (GeglOperation       *operation,
 {
   const Babl *in_format  = babl_format ("RaGaBaA float");
   const Babl *out_format = babl_format ("RaGaBaA float");
-  gboolean    has_alpha  = babl_format_has_alpha (gegl_operation_get_format (operation, "output"));
-  GeglAbyssPolicy read_abyss = has_alpha ? GEGL_ABYSS_NONE : GEGL_ABYSS_BLACK;
   gint err;
 
   GeglOperationAreaFilter *op_area = GEGL_OPERATION_AREA_FILTER (operation);
@@ -253,7 +317,7 @@ cl_process (GeglOperation       *operation,
                                              op_area->right,
                                              op_area->top,
                                              op_area->bottom,
-                                             read_abyss);
+                                             GEGL_ABYSS_CLAMP);
 
   gint aux  = gegl_buffer_cl_iterator_add_2 (i,
                                              NULL,
@@ -264,7 +328,7 @@ cl_process (GeglOperation       *operation,
                                              op_area->right,
                                              op_area->top,
                                              op_area->bottom,
-                                             GEGL_ABYSS_NONE);
+                                             GEGL_ABYSS_CLAMP);
 
   while (gegl_buffer_cl_iterator_next (i, &err))
     {
@@ -291,33 +355,65 @@ process (GeglOperation       *operation,
          const GeglRectangle *roi,
          gint                 level)
 {
-  GeglRectangle src_rect;
-  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  GeglRectangle            src_rect;
+  GeglRectangle           *whole_region;
+  GeglChantO              *o = GEGL_CHANT_PROPERTIES (operation);
   GeglOperationAreaFilter *op_area;
-  gfloat* buf;
-  gboolean        has_alpha  = babl_format_has_alpha (gegl_operation_get_format (operation, "output"));
-  GeglAbyssPolicy read_abyss = has_alpha ? GEGL_ABYSS_NONE : GEGL_ABYSS_BLACK;
 
   op_area = GEGL_OPERATION_AREA_FILTER (operation);
-  src_rect = *roi;
-  src_rect.x -= op_area->left;
-  src_rect.y -= op_area->top;
-  src_rect.width += op_area->left + op_area->right;
-  src_rect.height += op_area->top + op_area->bottom;
+
+  whole_region = gegl_operation_source_get_bounding_box (operation, "input");
 
   if (gegl_cl_is_accelerated ())
     if (cl_process (operation, input, output, roi))
       return TRUE;
 
-  buf = g_new0 (gfloat, src_rect.width * src_rect.height * 4);
+  if (o->size_x * o->size_y < SQR (ALLOC_THRESHOLD_SIZE))
+    {
+      gfloat *input_buf  = g_new (gfloat,
+                                  (CHUNK_SIZE + o->size_x * 2) *
+                                  (CHUNK_SIZE + o->size_y * 2) * 4);
+      gfloat *output_buf = g_new (gfloat, SQR (CHUNK_SIZE) * 4);
+      gint    i, j;
+
+      for (j = 0; (j-1) * CHUNK_SIZE < roi->height; j++)
+        for (i = 0; (i-1) * CHUNK_SIZE < roi->width; i++)
+          {
+            GeglRectangle chunked_result;
+
+            chunked_result = *GEGL_RECTANGLE (roi->x + i * CHUNK_SIZE,
+                                              roi->y + j * CHUNK_SIZE,
+                                              CHUNK_SIZE, CHUNK_SIZE);
 
-  gegl_buffer_get (input, &src_rect, 1.0, babl_format ("RaGaBaA float"), buf, GEGL_AUTO_ROWSTRIDE, 
read_abyss);
+            gegl_rectangle_intersect (&chunked_result, &chunked_result, roi);
 
-  pixelize(buf, roi, o->size_x, o->size_y);
+            if (chunked_result.width < 1  || chunked_result.height < 1)
+              continue;
 
-  gegl_buffer_set (output, roi, 0, babl_format ("RaGaBaA float"), buf, GEGL_AUTO_ROWSTRIDE);
+            src_rect = chunked_result;
+            src_rect.x -= op_area->left;
+            src_rect.y -= op_area->top;
+            src_rect.width += op_area->left + op_area->right;
+            src_rect.height += op_area->top + op_area->bottom;
 
-  g_free (buf);
+            gegl_buffer_get (input, &src_rect, 1.0, babl_format ("RaGaBaA float"),
+                             input_buf, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
+
+            pixelize (input_buf, output_buf, &chunked_result, &src_rect, whole_region,
+                      o->size_x, o->size_y);
+
+            gegl_buffer_set (output, &chunked_result, 1.0, babl_format ("RaGaBaA float"),
+                             output_buf, GEGL_AUTO_ROWSTRIDE);
+          }
+
+      g_free (input_buf);
+      g_free (output_buf);
+    }
+  else
+    {
+      pixelize_noalloc (input, output, roi, whole_region,
+                        o->size_x, o->size_y);
+    }
 
   return  TRUE;
 }
diff --git a/tests/compositions/pixelize.xml b/tests/compositions/pixelize.xml
index 88cfd3b..ed6e2a4 100644
--- a/tests/compositions/pixelize.xml
+++ b/tests/compositions/pixelize.xml
@@ -1,5 +1,11 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <gegl>
+  <node operation='gegl:crop'>
+    <params>
+      <param name='width'>144</param>
+      <param name='height'>220</param>
+    </params>
+  </node>
   <node operation='gegl:pixelize'>
     <params>
       <param name='size-x'>9</param>
diff --git a/tests/compositions/reference/pixelize.png b/tests/compositions/reference/pixelize.png
index 09ee594..a3e47cb 100644
Binary files a/tests/compositions/reference/pixelize.png and b/tests/compositions/reference/pixelize.png 
differ


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