1 /*
2  * Copyright (C) 2011 Paul Davis <paul@linuxaudiosystems.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #ifndef __gtkmm2ext_rgb_macros_h__
20 #define __gtkmm2ext_rgb_macros_h__
21 
22 /*
23   Some convenient macros for drawing into an RGB buffer.
24   Beware of side effects, code-bloat, and all of the other classic
25   cpp-perils...
26 */
27 
28 #define RGB_TO_UINT(r,g,b) ((((guint)(r))<<16)|(((guint)(g))<<8)|((guint)(b)))
29 #define RGB_TO_RGBA(x,a) (((x) << 8) | ((((guint)a) & 0xff)))
30 #define RGBA_TO_UINT(r,g,b,a) RGB_TO_RGBA(RGB_TO_UINT(r,g,b), a)
31 #define RGB_WHITE  RGB_TO_UINT(0xff, 0xff, 0xff)
32 #define RGB_BLACK  RGB_TO_UINT(0x00, 0x00, 0x00)
33 #define RGB_RED    RGB_TO_UINT(0xff, 0x00, 0x00)
34 #define RGB_GREEN  RGB_TO_UINT(0x00, 0xff, 0x00)
35 #define RGB_BLUE   RGB_TO_UINT(0x00, 0x00, 0xff)
36 #define RGB_YELLOW RGB_TO_UINT(0xff, 0xff, 0x00)
37 #define RGB_VIOLET RGB_TO_UINT(0xff, 0x00, 0xff)
38 #define RGB_CYAN   RGB_TO_UINT(0x00, 0xff, 0xff)
39 #define RGBA_WHITE  RGB_TO_RGBA(RGB_WHITE, 0xff)
40 #define RGBA_BLACK  RGB_TO_RGBA(RGB_BLACK, 0xff)
41 #define RGBA_RED    RGB_TO_RGBA(RGB_RED, 0xff)
42 #define RGBA_GREEN  RGB_TO_RGBA(RGB_GREEN, 0xff)
43 #define RGBA_BLUE   RGB_TO_RGBA(RGB_BLUE, 0xff)
44 #define RGBA_YELLOW RGB_TO_RGBA(RGB_YELLOW, 0xff)
45 #define RGBA_VIOLET RGB_TO_RGBA(RGB_VIOLET, 0xff)
46 #define RGBA_CYAN   RGB_TO_RGBA(RGB_CYAN, 0xff)
47 #define RGB_GREY(x) RGB_TO_UINT(x,x,x)
48 #define RGBA_GREY(x) RGB_TO_RGBA(RGB_GREY(x), 0xff)
49 #define UINT_RGBA_R(x) (((guint)(x))>>24)
50 #define UINT_RGBA_G(x) ((((guint)(x))>>16)&0xff)
51 #define UINT_RGBA_B(x) ((((guint)(x))>>8)&0xff)
52 #define UINT_RGBA_A(x) (((guint)(x))&0xff)
53 #define UINT_RGBA_R_FLT(x) ((((guint)(x))>>24)/255.0)
54 #define UINT_RGBA_G_FLT(x) (((((guint)(x))>>16)&0xff)/255.0)
55 #define UINT_RGBA_B_FLT(x) (((((guint)(x))>>8)&0xff)/255.0)
56 #define UINT_RGBA_A_FLT(x) ((((guint)(x))&0xff)/255.0)
57 #define UINT_RGBA_CHANGE_R(x, r) (((x)&(~(0xff<<24)))|(((r)&0xff)<<24))
58 #define UINT_RGBA_CHANGE_G(x, g) (((x)&(~(0xff<<16)))|(((g)&0xff)<<16))
59 #define UINT_RGBA_CHANGE_B(x, b) (((x)&(~(0xff<<8)))|(((b)&0xff)<<8))
60 #define UINT_RGBA_CHANGE_A(x, a) (((x)&(~0xff))|((a)&0xff))
61 #define UINT_TO_RGB(u,r,g,b) \
62 { (*(r)) = ((u)>>16)&0xff; (*(g)) = ((u)>>8)&0xff; (*(b)) = (u)&0xff; }
63 #define UINT_TO_RGBA(u,r,g,b,a) \
64 { UINT_TO_RGB(((u)>>8),r,g,b); (*(a)) = (u)&0xff; }
65 #define MONO_INTERPOLATE(v1, v2, t) ((gint)rint((v2)*(t)+(v1)*(1-(t))))
66 #define UINT_INTERPOLATE(c1, c2, t) \
67   RGBA_TO_UINT( MONO_INTERPOLATE(UINT_RGBA_R(c1), UINT_RGBA_R(c2), t), \
68 		MONO_INTERPOLATE(UINT_RGBA_G(c1), UINT_RGBA_G(c2), t), \
69 		MONO_INTERPOLATE(UINT_RGBA_B(c1), UINT_RGBA_B(c2), t), \
70 		MONO_INTERPOLATE(UINT_RGBA_A(c1), UINT_RGBA_A(c2), t) )
71 #define PIXEL_RGB(p, r, g, b) \
72 {((guchar*)(p))[0]=(r); ((guchar*)(p))[1]=(g); ((guchar*)(p))[2]=(b);}
73 #define PIXEL_RGBA(p, r, g, b, a) \
74 { if ((a)>=0xff) { PIXEL_RGB(p,r,g,b) } \
75   else if ((a)>0) { \
76     guint pixel_tmp; \
77     pixel_tmp = ((guchar*)(p))[0]; \
78     ((guchar*)(p))[0] = pixel_tmp + ((((r)-pixel_tmp)*(a)+0x80) >> 8); \
79     pixel_tmp = ((guchar*)(p))[1]; \
80     ((guchar*)(p))[1] = pixel_tmp + ((((g)-pixel_tmp)*(a)+0x80) >> 8); \
81     pixel_tmp = ((guchar*)(p))[2]; \
82     ((guchar*)(p))[2] = pixel_tmp + ((((b)-pixel_tmp)*(a)+0x80) >> 8); }}
83 #define PIXEL_RGB_UINT(p, i) \
84 UINT_TO_RGB((i), ((guchar*)p), ((guchar*)p)+1, ((guchar*)p)+2)
85 #define PIXEL_RGBA_UINT(p, i) \
86   PIXEL_RGBA((p), ((i)>>24)&0xff, ((i)>>16)&0xff, ((i)>>8)&0xff, (i)&0xff)
87 #define PIXEL_BLACK(p) PIXEL_RGB(p,0,0,0)
88 #define PIXEL_WHITE(p) PIXEL_RGB(p,0xff,0xff,0xff)
89 #define PIXEL_GREY(p,g) PIXEL_RGB(p,g,g,g)
90 #define PIXEL_GREYA(p,g,a) PIXEL_RGBA(p,g,g,g,a)
91 #define BUF_PTR(inbuf, ptx, pty) \
92  ((inbuf)->buf + 3*((ptx)-(inbuf)->rect.x0) + (inbuf)->buf_rowstride*((pty)-(inbuf)->rect.y0))
93 #define BUF_INBOUNDS_X(inbuf, ptx) \
94 ((inbuf)->rect.x0 <= (ptx) && (ptx) < (inbuf)->rect.x1)
95 #define BUF_INBOUNDS_Y(inbuf, pty) \
96 ((inbuf)->rect.y0 <= (pty) && (pty) < (inbuf)->rect.y1)
97 #define PAINT_DOT(inbuf, colr, colg, colb,ptx, pty) \
98 { \
99   guchar* pd_p; \
100   if (BUF_INBOUNDS_X(inbuf, ptx) && BUF_INBOUNDS_Y(inbuf, pty)) { \
101     pd_p = BUF_PTR(inbuf, ptx, pty); \
102     PIXEL_RGB(pd_p, (colr), (colg), (colb)); \
103   } \
104 }
105 #define FAST_PAINT_DOT(inbuf, colr, colg, colb,ptx, pty) \
106 { \
107   guchar* pd_p; \
108   pd_p = BUF_PTR(inbuf, ptx, pty); \
109   PIXEL_RGB(pd_p, (colr), (colg), (colb)); \
110 }
111 #define PAINT_DOTA(inbuf, colr, colg, colb, cola, ptx, pty) \
112 { \
113   guchar* pd_p; \
114   if (BUF_INBOUNDS_X(inbuf, ptx) && BUF_INBOUNDS_Y(inbuf, pty)) { \
115     pd_p = BUF_PTR(inbuf, ptx, pty); \
116     PIXEL_RGBA(pd_p, (colr), (colg), (colb), (cola)); \
117   } \
118 }
119 #define FAST_PAINT_DOTA(inbuf, colr, colg, colb, cola, ptx, pty) \
120 { \
121   guchar* pd_p; \
122   pd_p = BUF_PTR(inbuf, ptx, pty); \
123   PIXEL_RGBA(pd_p, (colr), (colg), (colb), (cola)); \
124 }
125 #define PAINT_HORIZ(inbuf, colr, colg, colb, ptx0, ptx1, pty) \
126 { \
127   GnomeCanvasBuf* ph_buf = (inbuf); \
128   guchar* ph_p; \
129   gint ph_a0, ph_a1; \
130   gint ph_colr=(colr), ph_colg=(colg), ph_colb=(colb); \
131 \
132   ph_a0 = MAX(ph_buf->rect.x0, (gint)(ptx0)); \
133   ph_a1 = MIN(ph_buf->rect.x1, (gint)(ptx1)); \
134 \
135   if (ph_a0 < ph_a1 && BUF_INBOUNDS_Y(ph_buf, (gint)(pty))) { \
136     ph_p = BUF_PTR(ph_buf, ph_a0, pty); \
137     while (ph_a0 < ph_a1) { \
138       PIXEL_RGB(ph_p, ph_colr, ph_colg, ph_colb); \
139       ++ph_a0; \
140       ph_p += 3; \
141     } \
142   } \
143 }
144 #define FAST_PAINT_HORIZ(inbuf, colr, colg, colb, ptx0, ptx1, pty) \
145 { \
146   GnomeCanvasBuf* ph_buf = (inbuf); \
147   guchar* ph_p; \
148   gint ph_a0, ph_a1; \
149   gint ph_colr=(colr), ph_colg=(colg), ph_colb=(colb); \
150 \
151   ph_a0 = MAX(ph_buf->rect.x0, (gint)(ptx0)); \
152   ph_a1 = MIN(ph_buf->rect.x1, (gint)(ptx1)); \
153 \
154   if (ph_a0 < ph_a1 && BUF_INBOUNDS_Y(ph_buf, (gint)(pty))) { \
155     ph_p = BUF_PTR(ph_buf, ph_a0, pty); \
156     while (ph_a0 < ph_a1) { \
157       PIXEL_RGB(ph_p, ph_colr, ph_colg, ph_colb); \
158       ++ph_a0; \
159       ph_p += 3; \
160     } \
161   } \
162 }
163 #define PAINT_HORIZA(inbuf, colr, colg, colb, cola, ptx0, ptx1, pty) \
164 { \
165   GnomeCanvasBuf* ph_buf = (inbuf); \
166   guchar* ph_p; \
167   gint ph_a0, ph_a1; \
168   gint ph_colr=(colr), ph_colg=(colg), ph_colb=(colb), ph_cola=(cola); \
169 \
170   ph_a0 = MAX(ph_buf->rect.x0, (gint)(ptx0)); \
171   ph_a1 = MIN(ph_buf->rect.x1, (gint)(ptx1)); \
172 \
173   if (ph_a0 < ph_a1 && BUF_INBOUNDS_Y(ph_buf, (gint)(pty))) { \
174     ph_p = BUF_PTR(ph_buf, ph_a0, pty); \
175     while (ph_a0 < ph_a1) { \
176       PIXEL_RGBA(ph_p, ph_colr, ph_colg, ph_colb, ph_cola); \
177       ++ph_a0; \
178       ph_p += 3; \
179     } \
180   } \
181 }
182 #define PAINT_VERT(inbuf, colr, colg, colb, ptx, pty0, pty1) \
183 { \
184   GnomeCanvasBuf* pv_buf = (inbuf); \
185   guchar* pv_p; \
186   gint pv_b0, pv_b1; \
187   gint pv_colr=(colr), pv_colg=(colg), pv_colb=(colb);\
188 \
189   pv_b0 = MAX(pv_buf->rect.y0, (gint)(pty0)); \
190   pv_b1 = MIN(pv_buf->rect.y1, (gint)(pty1)); \
191 \
192  if (pv_b0 < pv_b1 && BUF_INBOUNDS_X(pv_buf, (gint)(ptx))) { \
193     pv_p = BUF_PTR(pv_buf, ptx, pv_b0); \
194     while (pv_b0 < pv_b1) { \
195       PIXEL_RGB(pv_p, pv_colr, pv_colg, pv_colb); \
196       ++pv_b0; \
197       pv_p += pv_buf->buf_rowstride; \
198     } \
199   } \
200 }
201 #define FAST_PAINT_VERT(inbuf, colr, colg, colb, ptx, pty0, pty1) \
202 { \
203   GnomeCanvasBuf* fpv_buf = (inbuf); \
204   guchar* fpv_p; \
205   gint fpv_b0, fpv_b1; \
206 \
207   fpv_b0 = MAX(fpv_buf->rect.y0, (gint)(pty0)); \
208   fpv_b1 = MIN(fpv_buf->rect.y1, (gint)(pty1)); \
209 \
210   fpv_p = BUF_PTR(fpv_buf, ptx, fpv_b0); \
211 \
212   while (fpv_b0 < fpv_b1) { \
213       PIXEL_RGB(fpv_p, colr, colg, colb); \
214       ++fpv_b0; \
215       fpv_p += fpv_buf->buf_rowstride; \
216   } \
217 }
218 #define PAINT_VERTA(inbuf, colr, colg, colb, cola, ptx, pty0, pty1) \
219 { \
220   GnomeCanvasBuf* pv_buf = (inbuf); \
221   guchar* pv_p; \
222   gint pv_b0, pv_b1; \
223   gint pv_colr=(colr), pv_colg=(colg), pv_colb=(colb), pv_cola=(cola);\
224 \
225   pv_b0 = MAX(pv_buf->rect.y0, (pty0)); \
226   pv_b1 = MIN(pv_buf->rect.y1, (pty1)); \
227 \
228  if (pv_b0 < pv_b1 && BUF_INBOUNDS_X(pv_buf, ptx)) { \
229     pv_p = BUF_PTR(pv_buf, ptx, pv_b0); \
230     while (pv_b0 < pv_b1) { \
231       PIXEL_RGBA(pv_p, pv_colr, pv_colg, pv_colb, pv_cola); \
232       ++pv_b0; \
233       pv_p += pv_buf->buf_rowstride; \
234     } \
235   } \
236 }
237 
238 #define PAINT_VERTA_GR(inbuf, colr, colg, colb, cola, ptx, pty0, pty1, origin_y, obj_top) \
239 { \
240   GnomeCanvasBuf* pv_buf = (inbuf); \
241   guchar* pv_p; \
242   gint pv_b0, pv_b1; \
243   gint pv_colr=(colr), pv_colg=(colg), pv_colb=(colb), pv_cola=(cola); \
244   gint y_fract; \
245   gint y_span = (origin_y - obj_top); \
246   gint sat; \
247 \
248   pv_b0 = MAX(pv_buf->rect.y0, (pty0)); \
249   pv_b1 = MIN(pv_buf->rect.y1, (pty1)); \
250 \
251  if (pv_b0 < pv_b1 && BUF_INBOUNDS_X(pv_buf, ptx)) { \
252     pv_p = BUF_PTR(pv_buf, ptx, pv_b0); \
253     while (pv_b0 < pv_b1) { \
254       y_fract = (abs(origin_y - pv_b0)) * 0xFF; \
255 	  y_fract = y_fract / y_span; \
256 	  sat = 0xFF - (y_fract); \
257 	  PIXEL_RGBA(pv_p, (((pv_colr << 8) * sat) >> 16), (((pv_colg << 8) * sat) >> 16), (((pv_colb << 8) * sat) >> 16), pv_cola); \
258       ++pv_b0; \
259       pv_p += pv_buf->buf_rowstride; \
260     } \
261   } \
262 }
263 
264 /* Paint a solid-colored box into a GnomeCanvasBuf (clipping as necessary).
265    The box contains (ptx0,pty0), but not (ptx1, pty1).
266    Each macro arg should appear exactly once in the body of the code. */
267 #define PAINT_BOX(inbuf, colr, colg, colb, cola, ptx0, pty0, ptx1, pty1) \
268 { \
269   GnomeCanvasBuf* pb_buf = (inbuf); \
270   guchar* pb_p; \
271   guchar* pb_pp; \
272   gint pb_a0, pb_a1, pb_b0, pb_b1, pb_i, pb_j; \
273   gint pb_colr=(colr), pb_colg=(colg), pb_colb=(colb), pb_cola=(cola); \
274 \
275   pb_a0 = MAX(pb_buf->rect.x0, (ptx0)); \
276   pb_a1 = MIN(pb_buf->rect.x1, (ptx1)); \
277   pb_b0 = MAX(pb_buf->rect.y0, (pty0)); \
278   pb_b1 = MIN(pb_buf->rect.y1, (pty1)); \
279 \
280   if (pb_a0 < pb_a1 && pb_b0 < pb_b1) { \
281     pb_p = BUF_PTR(pb_buf, pb_a0, pb_b0); \
282     for (pb_j=pb_b0; pb_j<pb_b1; ++pb_j) { \
283       pb_pp = pb_p; \
284       for (pb_i=pb_a0; pb_i<pb_a1; ++pb_i) { \
285         PIXEL_RGBA(pb_pp, pb_colr, pb_colg, pb_colb, pb_cola); \
286         pb_pp += 3; \
287       } \
288       pb_p += pb_buf->buf_rowstride; \
289     } \
290   } \
291 }
292 
293 /* Paint a gradient-colored box into a GnomeCanvasBuf (clipping as necessary).
294    The box contains (ptx0,pty0), but not (ptx1, pty1).
295    Each macro arg should appear exactly once in the body of the code. */
296 #define PAINT_BOX_GR(inbuf, colr, colg, colb, cola, ptx0, pty0, ptx1, pty1, v_span) \
297 { \
298   GnomeCanvasBuf* pb_buf = (inbuf); \
299   guchar* pb_p; \
300   guchar* pb_pp; \
301   gint pb_a0, pb_a1, pb_b0, pb_b1, pb_i, pb_j; \
302   gint pb_colr=(colr), pb_colg=(colg), pb_colb=(colb), pb_cola=(cola); \
303   gint sat; \
304   gint y_fract; \
305   gint y_span = MAX(abs(v_span), 1); \
306 \
307   pb_a0 = MAX(pb_buf->rect.x0, (ptx0)); \
308   pb_a1 = MIN(pb_buf->rect.x1, (ptx1)); \
309   pb_b0 = MAX(pb_buf->rect.y0, (pty0)); \
310   pb_b1 = MIN(pb_buf->rect.y1, (pty1)); \
311 \
312   if (pb_a0 < pb_a1 && pb_b0 < pb_b1) { \
313     pb_p = BUF_PTR(pb_buf, pb_a0, pb_b0); \
314     for (pb_j=pb_b0; pb_j<pb_b1; ++pb_j) { \
315 	  y_fract = 0xFF * (abs(pb_j - pty0));  \
316 	  y_fract = y_fract / y_span; \
317 	  sat = 0xFF - (y_fract >> 1); \
318       pb_pp = pb_p; \
319       for (pb_i=pb_a0; pb_i<pb_a1; ++pb_i) { \
320         PIXEL_RGBA(pb_pp, (((pb_colr << 8) * sat) >> 16), (((pb_colg << 8) * sat) >> 16), (((pb_colb << 8) * sat) >> 16), pb_cola); \
321         pb_pp += 3; \
322       } \
323       pb_p += pb_buf->buf_rowstride; \
324     } \
325   } \
326 }
327 
328 
329 /* No bounds checking in this version */
330 
331 #define FAST_PAINT_BOX(inbuf, colr, colg, colb, cola, ptx0, pty0, ptx1, pty1) \
332 { \
333   GnomeCanvasBuf* pb_buf = (inbuf); \
334   guchar* pb_p; \
335   guchar* pb_pp; \
336   gint pb_i, pb_j; \
337 \
338   pb_p = BUF_PTR(pb_buf, ptx0, pty0); \
339   for (pb_j=pty0; pb_j<pty1; ++pb_j) { \
340       pb_pp = pb_p; \
341       for (pb_i=ptx0; pb_i<ptx1; ++pb_i) { \
342         PIXEL_RGBA(pb_pp, colr, colg, colb, cola); \
343         pb_pp += 3; \
344       } \
345       pb_p += pb_buf->buf_rowstride; \
346     } \
347 }
348 
349 #endif /* __gtkmm2ext_rgb_macros_h__ */
350