1 /*
2  * frei0r_cairo.h
3  * Copyright 2012 Janne Liljeblad
4  *
5  * This file is part of Frei0r.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22 
23 #include <cairo.h>
24 #include <string.h>
25 #include "frei0r_math.h"
26 
27 /**
28 * String identifiers for gradient types available using Cairo.
29 */
30 #define GRADIENT_LINEAR "gradient_linear"
31 #define GRADIENT_RADIAL "gradient_radial"
32 
33 /**
34 * String identifiers for blend modes available using Cairo.
35 */
36 #define NORMAL        "normal"
37 #define ADD           "add"
38 #define SATURATE      "saturate"
39 #define MULTIPLY      "multiply"
40 #define SCREEN        "screen"
41 #define OVERLAY       "overlay"
42 #define DARKEN        "darken"
43 #define LIGHTEN       "lighten"
44 #define COLORDODGE    "colordodge"
45 #define COLORBURN     "colorburn"
46 #define HARDLIGHT     "hardlight"
47 #define SOFTLIGHT     "softlight"
48 #define DIFFERENCE    "difference"
49 #define EXCLUSION     "exclusion"
50 #define HSLHUE        "hslhue"
51 #define HSLSATURATION "hslsaturation"
52 #define HSLCOLOR      "hslcolor"
53 #define HSLLUMINOSITY "hslluminosity"
54 
55 
56 /**
57 * frei0r_cairo_set_operator
58 * @cr: Cairo context
59 * @op: String identifier for a blend mode
60 *
61 * Sets cairo context to use the defined blend mode for all paint operations.
62 */
frei0r_cairo_set_operator(cairo_t * cr,char * op)63 void frei0r_cairo_set_operator(cairo_t *cr, char *op)
64 {
65   if(strcmp(op, NORMAL) == 0)
66   {
67     cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
68   }
69   else if(strcmp(op, ADD) == 0)
70   {
71     cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
72   }
73   else if(strcmp(op, SATURATE) == 0)
74   {
75     cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE);
76   }
77   else if(strcmp(op, MULTIPLY) == 0)
78   {
79     cairo_set_operator (cr, CAIRO_OPERATOR_MULTIPLY);
80   }
81   else if(strcmp(op, SCREEN) == 0)
82   {
83     cairo_set_operator (cr, CAIRO_OPERATOR_SCREEN);
84   }
85   else if(strcmp(op, OVERLAY) == 0)
86   {
87     cairo_set_operator (cr, CAIRO_OPERATOR_OVERLAY);
88   }
89   else if(strcmp(op, DARKEN) == 0)
90   {
91     cairo_set_operator (cr, CAIRO_OPERATOR_DARKEN);
92   }
93   else if(strcmp(op, LIGHTEN) == 0)
94   {
95     cairo_set_operator (cr, CAIRO_OPERATOR_LIGHTEN);
96   }
97   else if(strcmp(op, COLORDODGE) == 0)
98   {
99     cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE);
100   }
101   else if(strcmp(op, COLORBURN) == 0)
102   {
103     cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_BURN);
104   }
105   else if(strcmp(op, HARDLIGHT) == 0)
106   {
107     cairo_set_operator (cr, CAIRO_OPERATOR_HARD_LIGHT);
108   }
109   else if(strcmp(op, SOFTLIGHT) == 0)
110   {
111     cairo_set_operator (cr, CAIRO_OPERATOR_SOFT_LIGHT);
112   }
113   else if(strcmp(op, DIFFERENCE) == 0)
114   {
115     cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE);
116   }
117   else if(strcmp(op, EXCLUSION) == 0)
118   {
119     cairo_set_operator (cr, CAIRO_OPERATOR_EXCLUSION);
120   }
121   else if(strcmp(op, HSLHUE) == 0)
122   {
123     cairo_set_operator (cr, CAIRO_OPERATOR_HSL_HUE);
124   }
125   else if(strcmp(op, HSLSATURATION) == 0)
126   {
127     cairo_set_operator (cr, CAIRO_OPERATOR_HSL_SATURATION);
128   }
129   else if(strcmp(op, HSLCOLOR) == 0)
130   {
131     cairo_set_operator (cr, CAIRO_OPERATOR_HSL_COLOR);
132   }
133   else if(strcmp(op, HSLLUMINOSITY ) == 0)
134   {
135     cairo_set_operator (cr, CAIRO_OPERATOR_HSL_LUMINOSITY);
136   }
137   else
138   {
139     cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
140   }
141 }
142 
143 
144 /**
145 * frei0r_cairo_set_rgba_LITTLE_ENDIAN
146 * @cr: Cairo context
147 * @red: red component, 0 - 1
148 * @green: green component, 0 - 1
149 * @blue: blue component, 0 - 1
150 * @alpha: opacity of color, 0 -1
151 *
152 * Sets cairo context to use the defined color paint operations.
153 * Switches red and blue channels to get correct color on little endian machines.
154 * This method only works correctly on little endian machines.
155 */
frei0r_cairo_set_rgba_LITTLE_ENDIAN(cairo_t * cr,double red,double green,double blue,double alpha)156 void frei0r_cairo_set_rgba_LITTLE_ENDIAN(cairo_t* cr, double red, double green, double blue, double alpha)
157 {
158   cairo_set_source_rgba (cr, blue, green, red, alpha);
159 }
160 
161 /**
162 * frei0r_cairo_set_rgb_LITTLE_ENDIAN
163 * @cr: Cairo context
164 * @red: red component, 0 - 1
165 * @green: green component, 0 - 1
166 * @blue: blue component, 0 - 1
167 *
168 * Sets cairo context to use the defined color paint operations.
169 * Switches red and blue channels to get correct color on little endian machines.
170 * This method only works correctly on little endian machines.
171 */
frei0r_cairo_set_rgb_LITTLE_ENDIAN(cairo_t * cr,double red,double green,double blue)172 void frei0r_cairo_set_rgb_LITTLE_ENDIAN(cairo_t* cr, double red, double green, double blue)
173 {
174   cairo_set_source_rgb (cr, blue, green, red);
175 }
176 
177 /**
178 * freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN(
179 * @pat: Cairo pattern
180 * @offset: offset of color position in pattern space, 0 - 1
181 * @red: red component, 0 - 1
182 * @green: green component, 0 - 1
183 * @blue: blue component, 0 - 1
184 * @alpha: opacity of color, 0 -1
185 *
186 * Sets color stop for cairo patterns.
187 * Switches red and blue channels to get correct color on little endian machines.
188 * This method only works correctly on little endian machines.
189 */
freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN(cairo_pattern_t * pat,double offset,double red,double green,double blue,double alpha)190 void freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN(cairo_pattern_t *pat, double offset,
191                                                     double red, double green, double blue, double alpha)
192 {
193   cairo_pattern_add_color_stop_rgba (pat, offset, blue, green, red, alpha);
194 }
195 
196 /**
197 * frei0r_cairo_get_pixel_position
198 * @norm_pos: position in range 0 - 1, either x or y
199 * @dim: dimension, either witdh or height
200 *
201 * Converts double range [0 -> 1] to pixel range [-2*dim -> 3*dim]. Input 0.4 gives position 0.
202 *
203 * Returns: position in pixels
204 */
frei0r_cairo_get_pixel_position(double norm_pos,int dim)205 double frei0r_cairo_get_pixel_position (double norm_pos, int dim)
206 {
207   double pos_o = -(dim * 2.0);
208   return pos_o + norm_pos * dim * 5.0;
209 }
210 
211 /**
212 * frei0r_cairo_get_scale
213 * @norm_scale: scale in range 0 - 1
214 *
215 * Converts double range [0 -> 1] to scale range [0 -> 5]. Input 0.2 gives scale 1.0.
216 *
217 * Returns: scale
218 */
frei0r_cairo_get_scale(double norm_scale)219 double frei0r_cairo_get_scale (double norm_scale)
220 {
221   return norm_scale * 5.0;
222 }
223 
224 /**
225  * Convert frei0r RGBA to pre-multiplied alpha as needed by Cairo.
226  *
227  * \param rgba the image buffer with format F0R_COLOR_MODEL_RGBA8888
228  * \param pixels the size of the image buffer in number of pixels
229  * \param alpha if >= 0, the alpha channel will be set to this value
230  * \see frei0r_cairo_unpremultiply_rgba
231  */
frei0r_cairo_premultiply_rgba(unsigned char * rgba,int pixels,int alpha)232 void frei0r_cairo_premultiply_rgba (unsigned char *rgba, int pixels, int alpha)
233 {
234   int i = pixels + 1;
235   while ( --i ) {
236     register unsigned char a = rgba[3];
237     if (a == 0) {
238       *((uint32_t *)rgba) = 0;
239     } else if (a < 0xff) {
240       rgba[0] = ( rgba[0] * a ) >> 8;
241       rgba[1] = ( rgba[1] * a ) >> 8;
242       rgba[2] = ( rgba[2] * a ) >> 8;
243     }
244     if (alpha >= 0) rgba[3] = alpha;
245     rgba += 4;
246   }
247 }
248 
249 /**
250  * Convert Cairo ARGB pre-multiplied alpha to frei0r straight RGBA.
251  *
252  * \param rgba the image buffer with format CAIRO_FORMAT_ARGB32
253  * \param pixels the size of the image buffer in number of pixels
254  * \see frei0r_cairo_premultiply_rgba
255  */
frei0r_cairo_unpremultiply_rgba(unsigned char * rgba,int pixels)256 void frei0r_cairo_unpremultiply_rgba (unsigned char *rgba, int pixels)
257 {
258   int i = pixels + 1;
259   while ( --i ) {
260     register unsigned char a = rgba[3];
261     if (a > 0 && a < 0xff) {
262       rgba[0] = MIN(( rgba[0] << 8 ) / a, 255);
263       rgba[1] = MIN(( rgba[1] << 8 ) / a, 255);
264       rgba[2] = MIN(( rgba[2] << 8 ) / a, 255);
265     }
266     rgba += 4;
267   }
268 }
269 
270 /**
271  * Convert frei0r RGBA to pre-multiplied alpha as needed by Cairo.
272  *
273  * \param rgba the image buffer with format F0R_COLOR_MODEL_RGBA8888
274  * \param pixels the size of the image buffer in number of pixels
275  * \param alpha if >= 0, the alpha channel will be set to this value
276  * \see frei0r_cairo_premultiply_rgba
277  *
278  * This is the same as frei0r_cairo_premultiply_rgba but it writes the
279  * output to a different buffer.
280  */
frei0r_cairo_premultiply_rgba2(unsigned char * in,unsigned char * out,int pixels,int alpha)281 void frei0r_cairo_premultiply_rgba2 (unsigned char *in, unsigned char *out,
282                                      int pixels, int alpha)
283 {
284   int i = pixels + 1;
285   while ( --i ) {
286     register unsigned char a = in[3];
287     if (a == 0) {
288       *((uint32_t *)out) = 0;
289     } else if (a == 0xff) {
290       memcpy(out, in, 4);
291     } else {
292       out[0] = ( in[0] * a ) >> 8;
293       out[1] = ( in[1] * a ) >> 8;
294       out[2] = ( in[2] * a ) >> 8;
295     }
296     if (alpha >= 0)
297         out[3] = alpha;
298     in += 4;
299     out += 4;
300   }
301 }
302