use RrColorGC
[mikachu/openbox.git] / render / gradient.c
1 #include "render.h"
2 #include "gradient.h"
3 #include "color.h"
4 #include <glib.h>
5
6 static void highlight(RrPixel32 *x, RrPixel32 *y, gboolean raised);
7 static void gradient_solid(RrAppearance *l, int w, int h);
8 static void gradient_vertical(RrSurface *sf, int w, int h);
9 static void gradient_horizontal(RrSurface *sf, int w, int h);
10 static void gradient_diagonal(RrSurface *sf, int w, int h);
11 static void gradient_crossdiagonal(RrSurface *sf, int w, int h);
12 static void gradient_pyramid(RrSurface *sf, int inw, int inh);
13
14 void RrRender(RrAppearance *a, int w, int h)
15 {
16     RrPixel32 *data = a->surface.pixel_data;
17     RrPixel32 current;
18     unsigned int r,g,b;
19     int off, x;
20
21     switch (a->surface.grad) {
22     case RR_SURFACE_SOLID:
23         gradient_solid(a, w, h);
24         break;
25     case RR_SURFACE_VERTICAL:
26         gradient_vertical(&a->surface, w, h);
27         break;
28     case RR_SURFACE_HORIZONTAL:
29         gradient_horizontal(&a->surface, w, h);
30         break;
31     case RR_SURFACE_DIAGONAL:
32         gradient_diagonal(&a->surface, w, h);
33         break;
34     case RR_SURFACE_CROSS_DIAGONAL:
35         gradient_crossdiagonal(&a->surface, w, h);
36         break;
37     case RR_SURFACE_PYRAMID:
38         gradient_pyramid(&a->surface, w, h);
39         break;
40     default:
41         g_assert_not_reached(); /* unhandled gradient */
42         return;
43     }
44   
45     if (a->surface.relief == RR_RELIEF_FLAT && a->surface.border) {
46         r = a->surface.border_color->r;
47         g = a->surface.border_color->g;
48         b = a->surface.border_color->b;
49         current = (r << RrDefaultRedOffset)
50             + (g << RrDefaultGreenOffset)
51             + (b << RrDefaultBlueOffset);
52         for (off = 0, x = 0; x < w; ++x, off++) {
53             *(data + off) = current;
54             *(data + off + ((h-1) * w)) = current;
55         }
56         for (off = 0, x = 0; x < h; ++x, off++) {
57             *(data + (off * w)) = current;
58             *(data + (off * w) + w - 1) = current;
59         }
60     }
61
62     if (a->surface.relief != RR_RELIEF_FLAT) {
63         if (a->surface.bevel == RR_BEVEL_1) {
64             for (off = 1, x = 1; x < w - 1; ++x, off++)
65                 highlight(data + off,
66                           data + off + (h-1) * w,
67                           a->surface.relief==RR_RELIEF_RAISED);
68             for (off = 0, x = 0; x < h; ++x, off++)
69                 highlight(data + off * w,
70                           data + off * w + w - 1,
71                           a->surface.relief==RR_RELIEF_RAISED);
72         }
73
74         if (a->surface.bevel == RR_BEVEL_2) {
75             for (off = 2, x = 2; x < w - 2; ++x, off++)
76                 highlight(data + off + w,
77                           data + off + (h-2) * w,
78                           a->surface.relief==RR_RELIEF_RAISED);
79             for (off = 1, x = 1; x < h-1; ++x, off++)
80                 highlight(data + off * w + 1,
81                           data + off * w + w - 2,
82                           a->surface.relief==RR_RELIEF_RAISED);
83         }
84     }
85 }
86
87 static void highlight(RrPixel32 *x, RrPixel32 *y, gboolean raised)
88 {
89     int r, g, b;
90
91     RrPixel32 *up, *down;
92     if (raised) {
93         up = x;
94         down = y;
95     } else {
96         up = y;
97         down = x;
98     }
99     r = (*up >> RrDefaultRedOffset) & 0xFF;
100     r += r >> 1;
101     g = (*up >> RrDefaultGreenOffset) & 0xFF;
102     g += g >> 1;
103     b = (*up >> RrDefaultBlueOffset) & 0xFF;
104     b += b >> 1;
105     if (r > 0xFF) r = 0xFF;
106     if (g > 0xFF) g = 0xFF;
107     if (b > 0xFF) b = 0xFF;
108     *up = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset)
109         + (b << RrDefaultBlueOffset);
110   
111     r = (*down >> RrDefaultRedOffset) & 0xFF;
112     r = (r >> 1) + (r >> 2);
113     g = (*down >> RrDefaultGreenOffset) & 0xFF;
114     g = (g >> 1) + (g >> 2);
115     b = (*down >> RrDefaultBlueOffset) & 0xFF;
116     b = (b >> 1) + (b >> 2);
117     *down = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset)
118         + (b << RrDefaultBlueOffset);
119 }
120
121 static void create_bevel_colors(RrAppearance *l)
122 {
123     int r, g, b;
124
125     /* light color */
126     r = l->surface.primary->r;
127     r += r >> 1;
128     g = l->surface.primary->g;
129     g += g >> 1;
130     b = l->surface.primary->b;
131     b += b >> 1;
132     if (r > 0xFF) r = 0xFF;
133     if (g > 0xFF) g = 0xFF;
134     if (b > 0xFF) b = 0xFF;
135     g_assert(!l->surface.bevel_light);
136     l->surface.bevel_light = RrColorNew(l->inst, r, g, b);
137
138     /* dark color */
139     r = l->surface.primary->r;
140     r = (r >> 1) + (r >> 2);
141     g = l->surface.primary->g;
142     g = (g >> 1) + (g >> 2);
143     b = l->surface.primary->b;
144     b = (b >> 1) + (b >> 2);
145     g_assert(!l->surface.bevel_dark);
146     l->surface.bevel_dark = RrColorNew(l->inst, r, g, b);
147 }
148
149 static void gradient_solid(RrAppearance *l, int w, int h) 
150 {
151     RrPixel32 pix;
152     int i, a, b;
153     RrSurface *sp = &l->surface;
154     int left = 0, top = 0, right = w - 1, bottom = h - 1;
155
156     pix = (sp->primary->r << RrDefaultRedOffset)
157         + (sp->primary->g << RrDefaultGreenOffset)
158         + (sp->primary->b << RrDefaultBlueOffset);
159
160     for (a = 0; a < w; a++)
161         for (b = 0; b < h; b++)
162             sp->pixel_data[a + b * w] = pix;
163
164     XFillRectangle(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->primary),
165                    0, 0, w, h);
166
167     if (sp->interlaced) {
168         for (i = 0; i < h; i += 2)
169             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->secondary),
170                       0, i, w, i);
171     }
172
173     switch (sp->relief) {
174     case RR_RELIEF_RAISED:
175         if (!sp->bevel_dark)
176             create_bevel_colors(l);
177
178         switch (sp->bevel) {
179         case RR_BEVEL_1:
180             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
181                       left, bottom, right, bottom);
182             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
183                       right, bottom, right, top);
184                 
185             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
186                       left, top, right, top);
187             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
188                       left, bottom, left, top);
189             break;
190         case RR_BEVEL_2:
191             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
192                       left + 1, bottom - 2, right - 2, bottom - 2);
193             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
194                       right - 2, bottom - 2, right - 2, top + 1);
195
196             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
197                       left + 1, top + 1, right - 2, top + 1);
198             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
199                       left + 1, bottom - 2, left + 1, top + 1);
200             break;
201         default:
202             g_assert_not_reached(); /* unhandled BevelType */
203         }
204         break;
205     case RR_RELIEF_SUNKEN:
206         if (!sp->bevel_dark)
207             create_bevel_colors(l);
208
209         switch (sp->bevel) {
210         case RR_BEVEL_1:
211             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
212                       left, bottom, right, bottom);
213             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
214                       right, bottom, right, top);
215       
216             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
217                       left, top, right, top);
218             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
219                       left, bottom, left, top);
220             break;
221         case RR_BEVEL_2:
222             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
223                       left + 1, bottom - 2, right - 2, bottom - 2);
224             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
225                       right - 2, bottom - 2, right - 2, top + 1);
226       
227             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
228                       left + 1, top + 1, right - 2, top + 1);
229             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
230                       left + 1, bottom - 2, left + 1, top + 1);
231
232             break;
233         default:
234             g_assert_not_reached(); /* unhandled BevelType */
235         }
236         break;
237     case RR_RELIEF_FLAT:
238         if (sp->border) {
239             XDrawRectangle(RrDisplay(l->inst), l->pixmap,
240                            RrColorGC(sp->border_color),
241                            left, top, right, bottom);
242         }
243         break;
244     default:  
245         g_assert_not_reached(); /* unhandled ReliefType */
246     }
247 }
248
249 /* * * * * * * * * * * * * * GRADIENT MAGIC WOOT * * * * * * * * * * * * * * */
250
251 #define VARS(x)                                                     \
252     unsigned int color##x[3];                                       \
253     int len##x, cdelta##x[3], error##x[3] = { 0, 0, 0 }, inc##x[3]; \
254     gboolean bigslope##x[3] /* color slope > 1 */
255
256 #define SETUP(x, from, to, w)         \
257     len##x = w;                       \
258                                       \
259     color##x[0] = from->r;            \
260     color##x[1] = from->g;            \
261     color##x[2] = from->b;            \
262                                       \
263     cdelta##x[0] = to->r - from->r;   \
264     cdelta##x[1] = to->g - from->g;   \
265     cdelta##x[2] = to->b - from->b;   \
266                                       \
267     if (cdelta##x[0] < 0) {           \
268         cdelta##x[0] = -cdelta##x[0]; \
269         inc##x[0] = -1;               \
270     } else                            \
271         inc##x[0] = 1;                \
272     if (cdelta##x[1] < 0) {           \
273         cdelta##x[1] = -cdelta##x[1]; \
274         inc##x[1] = -1;               \
275     } else                            \
276         inc##x[1] = 1;                \
277     if (cdelta##x[2] < 0) {           \
278         cdelta##x[2] = -cdelta##x[2]; \
279         inc##x[2] = -1;               \
280     } else                            \
281         inc##x[2] = 1;                \
282     bigslope##x[0] = cdelta##x[0] > w;\
283     bigslope##x[1] = cdelta##x[1] > w;\
284     bigslope##x[2] = cdelta##x[2] > w
285
286 #define COLOR_RR(x, c)                       \
287     c->r = color##x[0];                      \
288     c->g = color##x[1];                      \
289     c->b = color##x[2]
290
291 #define COLOR(x)                             \
292     ((color##x[0] << RrDefaultRedOffset) +   \
293      (color##x[1] << RrDefaultGreenOffset) + \
294      (color##x[2] << RrDefaultBlueOffset))
295
296 #define INCREMENT(x, i) \
297     (inc##x[i])
298
299 #define NEXT(x)                                           \
300 {                                                         \
301     int i;                                                \
302     for (i = 2; i >= 0; --i) {                            \
303         if (!cdelta##x[i]) continue;                      \
304                                                           \
305         if (!bigslope##x[i]) {                            \
306             /* Y (color) is dependant on X */             \
307             error##x[i] += cdelta##x[i];                  \
308             if ((error##x[i] << 1) >= len##x) {           \
309                 color##x[i] += INCREMENT(x, i);           \
310                 error##x[i] -= len##x;                    \
311             }                                             \
312         } else {                                          \
313             /* X is dependant on Y (color) */             \
314             while (1) {                                   \
315                 color##x[i] += INCREMENT(x, i);           \
316                 error##x[i] += len##x;                    \
317                 if ((error##x[i] << 1) >= cdelta##x[i]) { \
318                     error##x[i] -= cdelta##x[i];          \
319                     break;                                \
320                 }                                         \
321             }                                             \
322         }                                                 \
323     }                                                     \
324 }
325
326 static void gradient_horizontal(RrSurface *sf, int w, int h)
327 {
328     int x, y;
329     RrPixel32 *data = sf->pixel_data, *datav;
330     RrPixel32 current;
331
332     VARS(x);
333     SETUP(x, sf->primary, sf->secondary, w);
334
335     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
336         current = COLOR(x);
337         datav = data;
338         for (y = h - 1; y >= 0; --y) {  /* 0 -> h */
339             *datav = current;
340             datav += w;
341         }
342         ++data;
343
344         NEXT(x);
345     }
346     current = COLOR(x);
347     for (y = h - 1; y >= 0; --y)  /* 0 -> h */
348         *(data + y * w) = current;
349 }
350
351 static void gradient_vertical(RrSurface *sf, int w, int h)
352 {
353     int x, y;
354     RrPixel32 *data = sf->pixel_data;
355     RrPixel32 current;
356
357     VARS(y);
358     SETUP(y, sf->primary, sf->secondary, h);
359
360     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
361         current = COLOR(y);
362         for (x = w - 1; x >= 0; --x)  /* 0 -> w */
363             *(data++) = current;
364
365         NEXT(y);
366     }
367     current = COLOR(y);
368     for (x = w - 1; x >= 0; --x)  /* 0 -> w */
369         *(data++) = current;
370 }
371
372
373 static void gradient_diagonal(RrSurface *sf, int w, int h)
374 {
375     int x, y;
376     RrPixel32 *data = sf->pixel_data;
377     RrColor left, right;
378     RrColor extracorner;
379
380     VARS(lefty);
381     VARS(righty);
382     VARS(x);
383
384     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
385     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
386     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
387
388     SETUP(lefty, sf->primary, (&extracorner), h);
389     SETUP(righty, (&extracorner), sf->secondary, h);
390
391     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
392         COLOR_RR(lefty, (&left));
393         COLOR_RR(righty, (&right));
394
395         SETUP(x, (&left), (&right), w);
396
397         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
398             *(data++) = COLOR(x);
399
400             NEXT(x);
401         }
402         *(data++) = COLOR(x);
403
404         NEXT(lefty);
405         NEXT(righty);
406     }
407     COLOR_RR(lefty, (&left));
408     COLOR_RR(righty, (&right));
409
410     SETUP(x, (&left), (&right), w);
411
412     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
413         *(data++) = COLOR(x);
414         
415         NEXT(x);
416     }
417     *data = COLOR(x);
418 }
419
420 static void gradient_crossdiagonal(RrSurface *sf, int w, int h)
421 {
422     int x, y;
423     RrPixel32 *data = sf->pixel_data;
424     RrColor left, right;
425     RrColor extracorner;
426
427     VARS(lefty);
428     VARS(righty);
429     VARS(x);
430
431     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
432     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
433     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
434
435     SETUP(lefty, (&extracorner), sf->secondary, h);
436     SETUP(righty, sf->primary, (&extracorner), h);
437
438     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
439         COLOR_RR(lefty, (&left));
440         COLOR_RR(righty, (&right));
441
442         SETUP(x, (&left), (&right), w);
443
444         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
445             *(data++) = COLOR(x);
446
447             NEXT(x);
448         }
449         *(data++) = COLOR(x);
450
451         NEXT(lefty);
452         NEXT(righty);
453     }
454     COLOR_RR(lefty, (&left));
455     COLOR_RR(righty, (&right));
456
457     SETUP(x, (&left), (&right), w);
458
459     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
460         *(data++) = COLOR(x);
461         
462         NEXT(x);
463     }
464     *data = COLOR(x);
465 }
466
467 static void gradient_pyramid(RrSurface *sf, int inw, int inh)
468 {
469     int x, y, w = (inw >> 1) + 1, h = (inh >> 1) + 1;
470     RrPixel32 *data = sf->pixel_data;
471     RrPixel32 *end = data + inw*inh - 1;
472     RrPixel32 current;
473     RrColor left, right;
474     RrColor extracorner;
475
476     VARS(lefty);
477     VARS(righty);
478     VARS(x);
479
480     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
481     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
482     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
483
484     SETUP(lefty, (&extracorner), sf->secondary, h);
485     SETUP(righty, sf->primary, (&extracorner), h);
486
487     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
488         COLOR_RR(lefty, (&left));
489         COLOR_RR(righty, (&right));
490
491         SETUP(x, (&left), (&right), w);
492
493         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
494             current = COLOR(x);
495             *(data+x) = current;
496             *(data+inw-x) = current;
497             *(end-x) = current;
498             *(end-(inw-x)) = current;
499
500             NEXT(x);
501         }
502         current = COLOR(x);
503         *(data+x) = current;
504         *(data+inw-x) = current;
505         *(end-x) = current;
506         *(end-(inw-x)) = current;
507
508         data+=inw;
509         end-=inw;
510
511         NEXT(lefty);
512         NEXT(righty);
513     }
514     COLOR_RR(lefty, (&left));
515     COLOR_RR(righty, (&right));
516
517     SETUP(x, (&left), (&right), w);
518
519     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
520         current = COLOR(x);
521         *(data+x) = current;
522         *(data+inw-x) = current;
523         *(end-x) = current;
524         *(end-(inw-x)) = current;
525         
526         NEXT(x);
527     }
528     current = COLOR(x);
529     *(data+x) = current;
530     *(data+inw-x) = current;
531     *(end-x) = current;
532     *(end-(inw-x)) = current;
533 }
534