Evas filters: Fix blur corner cases with small images
authorJean-Philippe Andre <jp.andre@samsung.com>
Thu, 20 Mar 2014 09:26:04 +0000 (18:26 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Fri, 21 Mar 2014 06:23:02 +0000 (15:23 +0900)
If the buffer size is smaller than the blurring kernel, then
special precautions must be taken to properly read the source
pixels. Also, fix the corner cases near the left & right edges
(or top & bottom).

src/lib/evas/filters/blur/blur_box_alpha_.c
src/lib/evas/filters/blur/blur_box_rgba_.c
src/lib/evas/filters/evas_filter_blur.c

index 71ac943..4257a5e 100644 (file)
@@ -32,6 +32,8 @@ _box_blur_alpha_horiz_step(const DATA8* restrict const srcdata,
 
    span1 = alloca(len);
    span2 = alloca(len);
+   memset(span1, 0, len);
+   memset(span2, 0, len);
 
    // For each line, apply as many blurs as requested
    for (int l = 0; l < loops; l++)
@@ -50,8 +52,6 @@ _box_blur_alpha_horiz_step(const DATA8* restrict const srcdata,
           {
              const int radius = radii[run];
              const int left = MIN(radius, len);
-             const int right = MIN(radius, (len - radius));
-             int acc = 0;
 
 #if DIV_USING_BITSHIFT
              const int pow2 = pow2_shifts[run];
@@ -60,44 +60,46 @@ _box_blur_alpha_horiz_step(const DATA8* restrict const srcdata,
              const int divider = 2 * radius + 1;
 #endif
 
-             const DATA8* restrict sr = src;
              const DATA8* restrict sl = src;
+             const DATA8* restrict sr = src;
+             const DATA8* restrict sre = src + len;
+             const DATA8* restrict sle = src + len - radius;
              DATA8* restrict d = dst;
+             int acc = 0, count = 0;
 
              // Read-ahead & accumulate
-             for (int k = left; k; k--)
+             for (int x = left; x > 0; x--)
                {
-                  acc += *sr;
-                  sr += 1;
+                  acc += *sr++;
+                  count++;
                }
 
              // Left edge
-             for (int k = 0; k < left; k++)
+             for (int x = left; x > 0; x--)
                {
-                  acc += *sr;
-                  *d = acc / (k + left + 1);
-                  sr += 1;
-                  d += 1;
+                  if (sr < sre)
+                    {
+                       acc += *sr++;
+                       count++;
+                    }
+
+                  *d++ = acc / count;
                }
 
              // Middle part, normal blur
-             for (int k = len - (2 * radius); k; k--)
+             while (sr < sre)
                {
-                  acc += *sr;
-                  *d = DIVIDE(acc);
-                  acc -= *sl;
-                  sl += 1;
-                  sr += 1;
-                  d += 1;
+                  acc += *sr++;
+                  *d++ = DIVIDE(acc);
+                  acc -= *sl++;
                }
 
              // Right edge
-             for (int k = right; k; k--)
+             count = 2 * radius + 1;
+             while (sl < sle)
                {
-                  *d = acc / (k + right);
-                  acc -= *sl;
-                  d += 1;
-                  sl += 1;
+                  *d++ = acc / (--count);
+                  acc -= *sl++;
                }
 
              // More runs to go: swap spans
@@ -177,8 +179,6 @@ _box_blur_alpha_vert_step(const DATA8* restrict const srcdata,
           {
              const int radius = radii[run];
              const int left = MIN(radius, len);
-             const int right = MIN(radius, (len - radius));
-             int acc = 0;
 
 #if DIV_USING_BITSHIFT
              const int pow2 = pow2_shifts[run];
@@ -187,44 +187,46 @@ _box_blur_alpha_vert_step(const DATA8* restrict const srcdata,
              const int divider = 2 * radius + 1;
 #endif
 
-             const DATA8* restrict sr = src;
              const DATA8* restrict sl = src;
+             const DATA8* restrict sr = src;
+             const DATA8* restrict sre = src + len;
+             const DATA8* restrict sle = src + len - radius;
              DATA8* restrict d = dst;
+             int acc = 0, count = 0;
 
              // Read-ahead & accumulate
-             for (int k = left; k; k--)
+             for (int x = left; x > 0; x--)
                {
-                  acc += *sr;
-                  sr += 1;
+                  acc += *sr++;
+                  count++;
                }
 
              // Left edge
-             for (int k = 0; k < left; k++)
+             for (int x = left; x > 0; x--)
                {
-                  acc += *sr;
-                  *d = acc / (k + left + 1);
-                  sr += 1;
-                  d += 1;
+                  if (sr < sre)
+                    {
+                       acc += *sr++;
+                       count++;
+                    }
+
+                  *d++ = acc / count;
                }
 
              // Middle part, normal blur
-             for (int k = len - (2 * radius); k; k--)
+             while (sr < sre)
                {
-                  acc += *sr;
-                  *d = DIVIDE(acc);
-                  acc -= *sl;
-                  sl += 1;
-                  sr += 1;
-                  d += 1;
+                  acc += *sr++;
+                  *d++ = DIVIDE(acc);
+                  acc -= *sl++;
                }
 
              // Right edge
-             for (int k = right; k; k--)
+             count = 2 * radius + 1;
+             while (sl < sle)
                {
-                  *d = acc / (k + right);
-                  acc -= *sl;
-                  d += 1;
-                  sl += 1;
+                  *d++ = acc / (--count);
+                  acc -= *sl++;
                }
 
              // More runs to go: swap spans
index 95d381a..7c22c65 100644 (file)
@@ -32,6 +32,8 @@ _box_blur_horiz_rgba_step(const DATA32* restrict const srcdata,
 
    span1 = alloca(len * sizeof(DATA32));
    span2 = alloca(len * sizeof(DATA32));
+   memset(span1, 0, len * sizeof(DATA32));
+   memset(span2, 0, len * sizeof(DATA32));
 
    // For each line, apply as many blurs as requested
    for (int l = 0; l < loops; l++)
@@ -50,7 +52,6 @@ _box_blur_horiz_rgba_step(const DATA32* restrict const srcdata,
           {
              const int radius = radii[run];
              const int left = MIN(radius, len);
-             const int right = MIN(radius, (len - radius));
 
 #if DIV_USING_BITSHIFT
              const int pow2 = pow2_shifts[run];
@@ -61,39 +62,44 @@ _box_blur_horiz_rgba_step(const DATA32* restrict const srcdata,
 
              const DATA8* restrict sl = (DATA8 *) src;
              const DATA8* restrict sr = (DATA8 *) src;
+             const DATA8* restrict sre = (DATA8 *) (src + len);
+             const DATA8* restrict sle = (DATA8 *) (src + len - radius);
              DATA8* restrict d = (DATA8 *) dst;
              int acc[4] = {0};
-             int x, k;
+             int count = 0;
 
              // Read-ahead
-             for (x = left; x; x--)
+             for (int x = left; x > 0; x--)
                {
-                  for (k = 0; k < 4; k++)
+                  for (int k = 0; k < 4; k++)
                     acc[k] += sr[k];
                   sr += sizeof(DATA32);
+                  count++;
                }
 
              // Left
-             for (x = 0; x < left; x++)
+             for (int x = left; x > 0; x--)
                {
-                  for (k = 0; k < 4; k++)
-                    acc[k] += sr[k];
-                  sr += sizeof(DATA32);
+                  if (sr < sre)
+                    {
+                       for (int k = 0; k < 4; k++)
+                         acc[k] += sr[k];
+                       sr += sizeof(DATA32);
+                       count++;
+                    }
 
-                  const int divider = x + left + 1;
-                  d[ALPHA] = acc[ALPHA] / divider;
-                  d[RED]   = acc[RED]   / divider;
-                  d[GREEN] = acc[GREEN] / divider;
-                  d[BLUE]  = acc[BLUE]  / divider;
+                  d[ALPHA] = acc[ALPHA] / count;
+                  d[RED]   = acc[RED]   / count;
+                  d[GREEN] = acc[GREEN] / count;
+                  d[BLUE]  = acc[BLUE]  / count;
                   d += sizeof(DATA32);
                }
 
              // Main part
-             for (x = len - (2 * radius); x > 0; x--)
+             for (; sr < sre; sr += sizeof(DATA32), sl += sizeof(DATA32))
                {
-                  for (k = 0; k < 4; k++)
+                  for (int k = 0; k < 4; k++)
                     acc[k] += sr[k];
-                  sr += sizeof(DATA32);
 
                   d[ALPHA] = DIVIDE(acc[ALPHA]);
                   d[RED]   = DIVIDE(acc[RED]);
@@ -101,24 +107,23 @@ _box_blur_horiz_rgba_step(const DATA32* restrict const srcdata,
                   d[BLUE]  = DIVIDE(acc[BLUE]);
                   d += sizeof(DATA32);
 
-                  for (k = 0; k < 4; k++)
+                  for (int k = 0; k < 4; k++)
                     acc[k] -= sl[k];
-                  sl += sizeof(DATA32);
                }
 
              // Right part
-             for (x = right; x; x--)
+             count = 2 * radius + 1;
+             for (; sl < sle; sl += sizeof(DATA32))
                {
-                  const int divider = x + right;
+                  const int divider = --count;
                   d[ALPHA] = acc[ALPHA] / divider;
                   d[RED]   = acc[RED]   / divider;
                   d[GREEN] = acc[GREEN] / divider;
                   d[BLUE]  = acc[BLUE]  / divider;
                   d += sizeof(DATA32);
 
-                  for (k = 0; k < 4; k++)
+                  for (int k = 0; k < 4; k++)
                     acc[k] -= sl[k];
-                  sl += sizeof(DATA32);
                }
 
              // More runs to go: swap spans
@@ -173,6 +178,8 @@ _box_blur_vert_rgba_step(const DATA32* restrict const srcdata,
 
    span1 = alloca(len * sizeof(DATA32));
    span2 = alloca(len * sizeof(DATA32));
+   memset(span1, 0, len * sizeof(DATA32));
+   memset(span2, 0, len * sizeof(DATA32));
 
    // For each line, apply as many blurs as requested
    for (int l = 0; l < loops; l++)
@@ -196,7 +203,6 @@ _box_blur_vert_rgba_step(const DATA32* restrict const srcdata,
           {
              const int radius = radii[run];
              const int left = MIN(radius, len);
-             const int right = MIN(radius, (len - radius));
 
 #if DIV_USING_BITSHIFT
              const int pow2 = pow2_shifts[run];
@@ -207,39 +213,44 @@ _box_blur_vert_rgba_step(const DATA32* restrict const srcdata,
 
              const DATA8* restrict sl = (DATA8 *) src;
              const DATA8* restrict sr = (DATA8 *) src;
+             const DATA8* restrict sre = (DATA8 *) (src + len);
+             const DATA8* restrict sle = (DATA8 *) (src + len - radius);
              DATA8* restrict d = (DATA8 *) dst;
              int acc[4] = {0};
-             int x, k;
+             int count = 0;
 
              // Read-ahead
-             for (x = left; x; x--)
+             for (int x = left; x > 0; x--)
                {
-                  for (k = 0; k < 4; k++)
+                  for (int k = 0; k < 4; k++)
                     acc[k] += sr[k];
                   sr += sizeof(DATA32);
+                  count++;
                }
 
              // Left
-             for (x = 0; x < left; x++)
+             for (int x = left; x > 0; x--)
                {
-                  for (k = 0; k < 4; k++)
-                    acc[k] += sr[k];
-                  sr += sizeof(DATA32);
+                  if (sr < sre)
+                    {
+                       for (int k = 0; k < 4; k++)
+                         acc[k] += sr[k];
+                       sr += sizeof(DATA32);
+                       count++;
+                    }
 
-                  const int divider = x + left + 1;
-                  d[ALPHA] = acc[ALPHA] / divider;
-                  d[RED]   = acc[RED]   / divider;
-                  d[GREEN] = acc[GREEN] / divider;
-                  d[BLUE]  = acc[BLUE]  / divider;
+                  d[ALPHA] = acc[ALPHA] / count;
+                  d[RED]   = acc[RED]   / count;
+                  d[GREEN] = acc[GREEN] / count;
+                  d[BLUE]  = acc[BLUE]  / count;
                   d += sizeof(DATA32);
                }
 
              // Main part
-             for (x = len - (2 * radius); x > 0; x--)
+             for (; sr < sre; sr += sizeof(DATA32), sl += sizeof(DATA32))
                {
-                  for (k = 0; k < 4; k++)
+                  for (int k = 0; k < 4; k++)
                     acc[k] += sr[k];
-                  sr += sizeof(DATA32);
 
                   d[ALPHA] = DIVIDE(acc[ALPHA]);
                   d[RED]   = DIVIDE(acc[RED]);
@@ -247,24 +258,23 @@ _box_blur_vert_rgba_step(const DATA32* restrict const srcdata,
                   d[BLUE]  = DIVIDE(acc[BLUE]);
                   d += sizeof(DATA32);
 
-                  for (k = 0; k < 4; k++)
+                  for (int k = 0; k < 4; k++)
                     acc[k] -= sl[k];
-                  sl += sizeof(DATA32);
                }
 
              // Right part
-             for (x = right; x; x--)
+             count = 2 * radius + 1;
+             for (; sl < sle; sl += sizeof(DATA32))
                {
-                  const int divider = x + right;
+                  const int divider = --count;
                   d[ALPHA] = acc[ALPHA] / divider;
                   d[RED]   = acc[RED]   / divider;
                   d[GREEN] = acc[GREEN] / divider;
                   d[BLUE]  = acc[BLUE]  / divider;
                   d += sizeof(DATA32);
 
-                  for (k = 0; k < 4; k++)
+                  for (int k = 0; k < 4; k++)
                     acc[k] -= sl[k];
-                  sl += sizeof(DATA32);
                }
 
              // More runs to go: swap spans
index 36a06e1..955fc64 100644 (file)
@@ -73,7 +73,6 @@ _box_blur_horiz_apply_rgba(Evas_Filter_Command *cmd)
 
    EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.w >= (2*r + 1), EINA_FALSE);
 
    _box_blur_horiz_rgba(in->image.data, out->image.data, radii,
                         in->cache_entry.w, in->cache_entry.h);
@@ -103,7 +102,6 @@ _box_blur_vert_apply_rgba(Evas_Filter_Command *cmd)
 
    EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.h >= (2*r + 1), EINA_FALSE);
 
    _box_blur_vert_rgba(in->image.data, out->image.data, radii,
                        in->cache_entry.w, in->cache_entry.h);
@@ -151,7 +149,6 @@ _box_blur_horiz_apply_alpha(Evas_Filter_Command *cmd)
 
    EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data8, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data8, EINA_FALSE);
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.w >= (2*r + 1), EINA_FALSE);
 
    _box_blur_horiz_alpha(in->image.data8, out->image.data8, radii,
                          in->cache_entry.w, in->cache_entry.h);
@@ -181,7 +178,6 @@ _box_blur_vert_apply_alpha(Evas_Filter_Command *cmd)
 
    EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data8, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data8, EINA_FALSE);
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.h >= (2*r + 1), EINA_FALSE);
 
    _box_blur_vert_alpha(in->image.data8, out->image.data8, radii,
                         in->cache_entry.w, in->cache_entry.h);
@@ -317,7 +313,6 @@ _gaussian_blur_horiz_apply_alpha(Evas_Filter_Command *cmd)
 
    EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data8, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data8, EINA_FALSE);
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.w >= (2*r + 1), EINA_FALSE);
 
    _gaussian_blur_horiz_alpha(in->image.data8, out->image.data8, r,
                               in->cache_entry.w, in->cache_entry.h);
@@ -341,7 +336,6 @@ _gaussian_blur_vert_apply_alpha(Evas_Filter_Command *cmd)
 
    EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data8, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data8, EINA_FALSE);
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.h >= (2*r + 1), EINA_FALSE);
 
    _gaussian_blur_vert_alpha(in->image.data8, out->image.data8, r,
                              in->cache_entry.w, in->cache_entry.h);
@@ -365,7 +359,6 @@ _gaussian_blur_horiz_apply_rgba(Evas_Filter_Command *cmd)
 
    EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.w >= (2*r + 1), EINA_FALSE);
 
    _gaussian_blur_horiz_rgba(in->image.data, out->image.data, r,
                              in->cache_entry.w, in->cache_entry.h);
@@ -389,7 +382,6 @@ _gaussian_blur_vert_apply_rgba(Evas_Filter_Command *cmd)
 
    EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(out->cache_entry.h >= (2*r + 1), EINA_FALSE);
 
    _gaussian_blur_vert_rgba(in->image.data, out->image.data, r,
                             in->cache_entry.w, in->cache_entry.h);