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