Re: Unable to create a blur image

Uli Schlachter <[email protected]> Fri, 11 Oct 2019 09:20:21 +0200
Newsgroups gmane.comp.lib.cairo
Message-ID <[email protected]>
This is a multi-part message in MIME format.
--------------970D76B87374CCC5309623CC
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

Hi,

On 10.10.19 13:15, Murali Krishna wrote:
> I have been trying to create a blur image in C++ using cairo library

You are not actually using cairo to blur the image. You are bluring it
yourself. Butokay, whatever.

> the code I used doesn't actually work.

"Doesn't actually work" is not the best error description I ever heard.

I took your code and removed the osg references. The attached program
loads "img.png" and writes "out.png". For me, this results in a blur, so
I guess your blurring code works.

However, your createBlur() function for some reason fills the whole
surface with blue and blurring a solid color is not really interesting.

(It is blue, because you use cairo_set_source_rgba(cr, b, g, a), so your
variable names do not match the order of function arguments.)

(It is the whole surface, because you use cairo_paint(). This fills the
whole surface. To just fill the current path, you would use
cairo_fill(). Which you also call...?)

Cheers,
Uli
-- 
“Some people are worth melting for.” - Olaf

--------------970D76B87374CCC5309623CC
Content-Type: text/x-c++src;
 name="BlurExample.cpp"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="BlurExample.cpp"

#include<chrono>
#include <cairo/cairo.h>
#include <vector>
#include <cmath>
#include <iostream>
using namespace std;
using namespace std::chrono;


/* Performs a simple 2D Gaussian blur of radius @radius on surface @surface. */
void
blur_image_surface(cairo_surface_t *surface, int radius)
{
   cairo_surface_t *tmp;
   int width, height;
   int src_stride, dst_stride;
   int x, y, z, w;
   uint8_t *src, *dst;
   uint32_t *s, *d, a, p;
   int i, j, k;
   int size = radius * 2 + 1;
   std::vector<uint8_t> kernel;
   kernel.resize(size);
   const int half = size / 2;

   if (cairo_surface_status(surface))
      return;

   std::cout << "foo1" << std::endl;
   width = cairo_image_surface_get_width(surface);
   height = cairo_image_surface_get_height(surface);

   switch (cairo_image_surface_get_format(surface)) {
   case CAIRO_FORMAT_A1:
   default:
      /* Don't even think about it! */
      return;

   case CAIRO_FORMAT_A8:
      /* Handle a8 surfaces by effectively unrolling the loops by a
      * factor of 4 - this is safe since we know that stride has to be a
      * multiple of uint32_t. */
      width /= 4;
      break;

   case CAIRO_FORMAT_RGB24:
   case CAIRO_FORMAT_ARGB32:
      break;
   }

   std::cout << "foo2" << std::endl;
   tmp = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
   std::cout << "foo3" << std::endl;
   if (cairo_surface_status(tmp))
      return;

   src = cairo_image_surface_get_data(surface);
   src_stride = cairo_image_surface_get_stride(surface);

   dst = cairo_image_surface_get_data(tmp);
   dst_stride = cairo_image_surface_get_stride(tmp);

   a = 0;
   for (i = 0; i < size; i++) {
      double f = i - half;
      a += kernel[i] = exp(-f * f / 30.0) * 80;
   }

   /* Horizontally blur from surface -> tmp */
   std::cout << "foo4 " << height << " " << width << " " << size << std::endl;
   for (i = 0; i < height; i++) {
	   std::cout << "4a " << i << " out of " << height << "\r" << std::flush;
      s = (uint32_t *)(src + i * src_stride);
      d = (uint32_t *)(dst + i * dst_stride);
      for (j = 0; j < width; j++) {
         if (radius < j && j < width - radius) {
            d[j] = s[j];
            continue;
         }

         x = y = z = w = 0;
         for (k = 0; k < size; k++) {
            if (j - half + k < 0 || j - half + k >= width)
               continue;

            p = s[j - half + k];

            x += ((p >> 24) & 0xff) * kernel[k];
            y += ((p >> 16) & 0xff) * kernel[k];
            z += ((p >> 8) & 0xff) * kernel[k];
            w += ((p >> 0) & 0xff) * kernel[k];
         }
         d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
      }
   }
   std::cout << "foo5 " << height << " " << width << " " << size << std::endl;

   /* Then vertically blur from tmp -> surface */
   for (i = 0; i < height; i++) {
	   std::cout << "5a " << i << " out of " << height << "\r" << std::flush;
      s = (uint32_t *)(dst + i * dst_stride);
      d = (uint32_t *)(src + i * src_stride);
      for (j = 0; j < width; j++) {
         if (radius <= i && i < height - radius) {
            d[j] = s[j];
            continue;
         }

         x = y = z = w = 0;
         for (k = 0; k < size; k++) {
            if (i - half + k < 0 || i - half + k >= height)
               continue;

            s = (uint32_t *)(dst + (i - half + k) * dst_stride);
            p = s[j];

            x += ((p >> 24) & 0xff) * kernel[k];
            y += ((p >> 16) & 0xff) * kernel[k];
            z += ((p >> 8) & 0xff) * kernel[k];
            w += ((p >> 0) & 0xff) * kernel[k];
         }
         d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
      }
   }
	   std::cout << "\ndone" << std::endl;

   cairo_surface_destroy(tmp);
   cairo_surface_mark_dirty(surface);
}

void createBlur(cairo_surface_t *cairoRawSurface)
{
   if (cairoRawSurface)
   {
      cairo_t* cr = cairo_create(cairoRawSurface);
      unsigned int _width = cairo_image_surface_get_width(cairoRawSurface);
      unsigned int _height = cairo_image_surface_get_height(cairoRawSurface);
      if (cr)
      {
#if 0
I am not sure what all of this is supposed so I removed it (after making it work without osg)
         cairo_line_to(cr, 10, 20);
         cairo_line_to(cr, 30, 10);
         cairo_line_to(cr, 30, 10);
         cairo_line_to(cr, 30, 30);
         cairo_line_to(cr, 10, 30);
         cairo_close_path(cr);
         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
         double r = 255;
         double g = 0;
         double b = 0;
         double a = 0.5;
         cairo_set_source_rgba(cr, b / a, g / a, r / a, a); // Uhm, the arguments should be in range [0,1]. b/a is 510.
         cairo_paint(cr); // This fills the whole surface with your blue color?
         cairo_set_source_rgba(cr, b / a, g / a, r / a, a);
         cairo_fill_preserve(cr);
         cairo_set_source_rgba(cr, b / a, g / a, r / a, a);
#endif

         const double blurPixels = (20 / 100.0) * static_cast<double>(_width);
         cairo_set_line_width(cr, blurPixels / 2.0);
         cairo_stroke(cr);

         // apply blur
         blur_image_surface(cairoRawSurface, blurPixels);
         cairo_destroy(cr);
      }
   }
}
int main()
{
   cairo_surface_t *areaImage = cairo_image_surface_create_from_png("img.png");

   createBlur(areaImage);

   cairo_surface_write_to_png(areaImage, "out.png");
}

--------------970D76B87374CCC5309623CC
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: inline

LS0gCmNhaXJvIG1haWxpbmcgbGlzdApjYWlyb0BjYWlyb2dyYXBoaWNzLm9yZwpodHRwczovL2xp
c3RzLmNhaXJvZ3JhcGhpY3Mub3JnL21haWxtYW4vbGlzdGluZm8vY2Fpcm8=

--------------970D76B87374CCC5309623CC--