[cairo] propsal for blur-API in cairo

Mirco Müller macslow at bangang.de
Wed Jun 17 13:23:06 PDT 2009


Greetings cairo-lovers!

	I've spent some time thinking of what an API-addition for blurring in
cairo should look like. I tried to stick to the pattern used by the API
used for gradients.

	The main idea is to introduce an opaque blur-object similar to a
gradient-pattern. You can set type and quality of it. Along those lines
also setters and getters for the numerous values controlling each
blur-type.

        typedef enum _cairo_blur_type_t
        {
        	CAIRO_BLUR_TYPE_BOXED = 0, // exp, stack, gaussian etc.
        	CAIRO_BLUR_TYPE_VECTORIAL, // directional blur
        	CAIRO_BLUR_TYPE_RADIAL     // self-explanatory
        } cairo_blur_type_t;
        
        typedef enum _cairo_blur_quality_t
        {
        	CAIRO_BLUR_QUALITY_LOW = 0, // low quality, but fast
        	CAIRO_BLUR_QUALITY_MEDIUM,  // speed/quality compromise
        	CAIRO_BLUR_QUALITY_HIGH     // quality over speed
        } cairo_blur_quality;
        
        typedef struct _cairo_blur_private_t
        {
        	cairo_blur_type_t    type;         // radial, vectorial boxed
        	cairo_blur_quality_t quality;      // low, medium, high
        	double               center[2];    // for radial blur
        	double               direction[2]; // for vectorial blur
        	double               strength;     // for radial/vectorial blur
        	double               vradius;      // for boxed blur
        	double               hradius;      // for boxed blur
        } cairo_blur_private_t;
        
        typedef struct _cairo_blur_t
        {
        	cairo_blur_private_t* priv;
        } cairo_blur_t;
        
        cairo_blur_t*
        cairo_blur_create (cairo_blur_type_t type);
        
        cairo_blur_type_t
        cairo_blur_get_type (cairo_blur_t* blur);
        
        cairo_blur_quality_t
        cairo_blur_get_quality (cairo_blur_t* blur);
        
        void
        cairo_blur_set_quality (cairo_blur_t*        blur,
        			cairo_blur_quality_t quality);
        
        void
        cairo_blur_get_center (cairo_blur_t* blur,
        		       double*       x,
        		       double*       y);
        
        void
        cairo_blur_set_center (cairo_blur_t* blur,
        		       double        x,
                               double        y);
        
        void
        cairo_blur_get_direction (cairo_blur_t* blur,
        			  double*       x_offset,
        			  double*       y_offset);
        
        void
        cairo_blur_set_direction (cairo_blur_t* blur,
        			  double        x_offset,
        			  double        y_offset);
        
        double
        cairo_blur_get_strength (cairo_blur_t* blur);
        
        void
        cairo_blur_set_strength (cairo_blur_t* blur,
        			 double        strength);
        
        void
        cairo_blur_get_radius (cairo_blur_t* blur,
        		       double*       vradius,
        		       double*       hradius);
        
        void
        cairo_blur_set_radius (cairo_blur_t* blur,
        		       double        vradius,
        		       double        hradius);
        
        void
        cairo_blur_destroy (cairo_blur_t* blur);
        
        
	Now for the debatable part. How should the blur be applied? There are
two ideas for this.

	1.) Just have it applied to a surface. This is very simple and keeps
the overall API-addition lean. The drawback is that this is less
flexible.
        
        void
        cairo_surface_apply_blur (cairo_surface_t* surface,
        			  cairo_blur_t*    blur);

or

        void
        cairo_blur_apply (cairo_blur_t*    blur
        		  cairo_surface_t* surface);

	2.) Have a new cairo_foo_blurred() variation added for every current
drawing operator. That makes blurring a lot more versatile for users of
the new API. People don't have to think so much about how to e.g. just
apply a blur to the current stroke or fill they are doing and not to
anything else.
        
        void
        cairo_mask_blurred (cairo_t*         cr,
                            cairo_pattern_t* pattern,
        		    cairo_blur_t*    blur);
        
        void
        cairo_mask_surface_blurred (cairo_t*      cr,
                                    cairo_surface_t* surface,
                                    double           surface_x,
                                    double           surface_y,
        			    cairo_blur_t* blur);
        
        void
        cairo_fill_blurred (cairo_t*      cr,
        		    cairo_blur_t* blur);
        
        void
        cairo_fill_preserve_blurred (cairo_t*      cr,
        			     cairo_blur_t* blur);
                
        void
        cairo_stroke_blurred (cairo_t*      cr,
        		      cairo_blur_t* blur);
        
        void
        cairo_stroke_preserve_blurred (cairo_t*      cr,
        			       cairo_blur_t* blur);
        
        void
        cairo_paint_blurred (cairo_t*      cr,
        		     cairo_blur_t* blur);
                
        void
        cairo_paint_with_alpha_blurred (cairo_t*      cr,
                                        double        alpha,
                                        cairo_blur_t* blur);
        
        cairo_show_text_blurred (cairo_t*      cr,
                                 const char*   utf8,
                                 cairo_blur_t* blur);
        
        void
        cairo_show_glyphs_blurred (cairo_t*             cr,
                                   const cairo_glyph_t* glyphs,
                                   int                  num_glyphs,
                                   cairo_blur_t*        blur);
        
        void
        cairo_show_text_glyphs_blurred (cairo_t*      cr,
                                        ...
                                        cairo_blur_t* blur);

	Here an example of what it could look like to do a gaussian blur on
just one fill (white fill with black "drop-shadow"):

        cairo_blur_t* blur;
        
        // setup context and stuff
        ...
        blur = cairo_blur_create (CAIRO_BLUR_TYPE_BOXED);
        cairo_blur_set_quality (blur, CAIRO_BLUR_QUALITY_HIGH);
        cairo_blur_set_radius (blur, 0.1, 0.1);
        // draw some path
        ...
        cairo_set_rgba (cr, 0.0, 0.0, 0.0, 1.0);
        cairo_fill_blurred_preserve (cr, blur);
        cairo_blur_destroy (blur);
        cairo_set_rgba (cr, 1.0, 1.0, 1.0, 1.0);
        cairo_fill (cr);

This is how applying a radial blur would "feel" to a stroke:

        cairo_blur_t* blur;
        
        // setup context and stuff
        ...
        blur = cairo_blur_create (CAIRO_BLUR_TYPE_RADIAL);
        cairo_blur_set_quality (blur, CAIRO_BLUR_QUALITY_MEDIUM);
        cairo_blur_set_center (blur, 0.5, 0.5);
        cairo_blur_set_strength (blur, 0.3);
        // draw some path
        ...
        cairo_stroke_blurred (cr, blur);
        cairo_blur_destroy (blur);

Applying a vectorial blur to the "destination":

        cairo_blur_t* blur; 
        
        // setup context and stuff
        ...
        blur = cairo_blur_create (CAIRO_BLUR_TYPE_VECTORIAL);
        cairo_blur_set_quality (blur, CAIRO_BLUR_QUALITY_LOW);
        cairo_blur_set_direction (blur, 0.1, -0.2);
        cairo_blur_set_strength (blur, 0.2);
        cairo_push_group (cr);
        // draw something
        ...
        cairo_pop_group_to_source (cr);
        cairo_paint_blurred (cr, blur);
        cairo_blur_destroy (blur);

What do you think?

Best regards ...

Mirco "MacSlow" Müller



More information about the cairo mailing list