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