]> icculus.org git repositories - mikachu/openbox.git/blob - render/gradient.c
integer math for gradients. horz vert and diags and pipecross work so far.
[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 static void gradient_rectangle(RrSurface *sf, int inw, int inh);
14 static void gradient_pipecross(RrSurface *sf, int inw, int inh);
15
16 void RrRender(RrAppearance *a, int w, int h)
17 {
18     RrPixel32 *data = a->surface.RrPixel_data;
19     RrPixel32 current;
20     unsigned int r,g,b;
21     int off, x;
22
23     switch (a->surface.grad) {
24     case RR_SURFACE_SOLID:
25         gradient_solid(a, w, h);
26         return;
27     case RR_SURFACE_VERTICAL:
28         gradient_vertical(&a->surface, w, h);
29         break;
30     case RR_SURFACE_HORIZONTAL:
31         gradient_horizontal(&a->surface, w, h);
32         break;
33     case RR_SURFACE_DIAGONAL:
34         gradient_diagonal(&a->surface, w, h);
35         break;
36     case RR_SURFACE_CROSS_DIAGONAL:
37         gradient_crossdiagonal(&a->surface, w, h);
38         break;
39     case RR_SURFACE_PYRAMID:
40         gradient_pyramid(&a->surface, w, h);
41         break;
42     case RR_SURFACE_PIPECROSS:
43         gradient_pipecross(&a->surface, w, h);
44         break;
45     case RR_SURFACE_RECTANGLE:
46         gradient_rectangle(&a->surface, w, h);
47         break;
48     default:
49         g_message("unhandled gradient");
50         return;
51     }
52   
53     if (a->surface.relief == RR_RELIEF_FLAT && a->surface.border) {
54         r = a->surface.border_color->r;
55         g = a->surface.border_color->g;
56         b = a->surface.border_color->b;
57         current = (r << RrDefaultRedOffset)
58             + (g << RrDefaultGreenOffset)
59             + (b << RrDefaultBlueOffset);
60         for (off = 0, x = 0; x < w; ++x, off++) {
61             *(data + off) = current;
62             *(data + off + ((h-1) * w)) = current;
63         }
64         for (off = 0, x = 0; x < h; ++x, off++) {
65             *(data + (off * w)) = current;
66             *(data + (off * w) + w - 1) = current;
67         }
68     }
69
70     if (a->surface.relief != RR_RELIEF_FLAT) {
71         if (a->surface.bevel == RR_BEVEL_1) {
72             for (off = 1, x = 1; x < w - 1; ++x, off++)
73                 highlight(data + off,
74                           data + off + (h-1) * w,
75                           a->surface.relief==RR_RELIEF_RAISED);
76             for (off = 0, x = 0; x < h; ++x, off++)
77                 highlight(data + off * w,
78                           data + off * w + w - 1,
79                           a->surface.relief==RR_RELIEF_RAISED);
80         }
81
82         if (a->surface.bevel == RR_BEVEL_2) {
83             for (off = 2, x = 2; x < w - 2; ++x, off++)
84                 highlight(data + off + w,
85                           data + off + (h-2) * w,
86                           a->surface.relief==RR_RELIEF_RAISED);
87             for (off = 1, x = 1; x < h-1; ++x, off++)
88                 highlight(data + off * w + 1,
89                           data + off * w + w - 2,
90                           a->surface.relief==RR_RELIEF_RAISED);
91         }
92     }
93 }
94
95 static void highlight(RrPixel32 *x, RrPixel32 *y, gboolean raised)
96 {
97     int r, g, b;
98
99     RrPixel32 *up, *down;
100     if (raised) {
101         up = x;
102         down = y;
103     } else {
104         up = y;
105         down = x;
106     }
107     r = (*up >> RrDefaultRedOffset) & 0xFF;
108     r += r >> 1;
109     g = (*up >> RrDefaultGreenOffset) & 0xFF;
110     g += g >> 1;
111     b = (*up >> RrDefaultBlueOffset) & 0xFF;
112     b += b >> 1;
113     if (r > 0xFF) r = 0xFF;
114     if (g > 0xFF) g = 0xFF;
115     if (b > 0xFF) b = 0xFF;
116     *up = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset)
117         + (b << RrDefaultBlueOffset);
118   
119     r = (*down >> RrDefaultRedOffset) & 0xFF;
120     r = (r >> 1) + (r >> 2);
121     g = (*down >> RrDefaultGreenOffset) & 0xFF;
122     g = (g >> 1) + (g >> 2);
123     b = (*down >> RrDefaultBlueOffset) & 0xFF;
124     b = (b >> 1) + (b >> 2);
125     *down = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset)
126         + (b << RrDefaultBlueOffset);
127 }
128
129 static void create_bevel_colors(RrAppearance *l)
130 {
131     int r, g, b;
132
133     /* light color */
134     r = l->surface.primary->r;
135     r += r >> 1;
136     g = l->surface.primary->g;
137     g += g >> 1;
138     b = l->surface.primary->b;
139     b += b >> 1;
140     if (r > 0xFF) r = 0xFF;
141     if (g > 0xFF) g = 0xFF;
142     if (b > 0xFF) b = 0xFF;
143     g_assert(!l->surface.bevel_light);
144     l->surface.bevel_light = RrColorNew(l->inst, r, g, b);
145     RrColorAllocateGC(l->surface.bevel_light);
146
147     /* dark color */
148     r = l->surface.primary->r;
149     r = (r >> 1) + (r >> 2);
150     g = l->surface.primary->g;
151     g = (g >> 1) + (g >> 2);
152     b = l->surface.primary->b;
153     b = (b >> 1) + (b >> 2);
154     g_assert(!l->surface.bevel_dark);
155     l->surface.bevel_dark = RrColorNew(l->inst, r, g, b);
156     RrColorAllocateGC(l->surface.bevel_dark);
157 }
158
159 static void gradient_solid(RrAppearance *l, int w, int h) 
160 {
161     RrPixel32 pix;
162     int i, a, b;
163     RrSurface *sp = &l->surface;
164     int left = 0, top = 0, right = w - 1, bottom = h - 1;
165
166     if (sp->primary->gc == None)
167         RrColorAllocateGC(sp->primary);
168     pix = (sp->primary->r << RrDefaultRedOffset)
169         + (sp->primary->g << RrDefaultGreenOffset)
170         + (sp->primary->b << RrDefaultBlueOffset);
171
172     for (a = 0; a < w; a++)
173         for (b = 0; b < h; b++)
174             sp->RrPixel_data[a + b * w] = pix;
175
176     XFillRectangle(RrDisplay(l->inst), l->pixmap, sp->primary->gc,
177                    0, 0, w, h);
178
179     if (sp->interlaced) {
180         if (sp->secondary->gc == None)
181             RrColorAllocateGC(sp->secondary);
182         for (i = 0; i < h; i += 2)
183             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->secondary->gc,
184                       0, i, w, i);
185     }
186
187     switch (sp->relief) {
188     case RR_RELIEF_RAISED:
189         if (!sp->bevel_dark)
190             create_bevel_colors(l);
191
192         switch (sp->bevel) {
193         case RR_BEVEL_1:
194             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_dark->gc,
195                       left, bottom, right, bottom);
196             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_dark->gc,
197                       right, bottom, right, top);
198                 
199             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_light->gc,
200                       left, top, right, top);
201             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_light->gc,
202                       left, bottom, left, top);
203             break;
204         case RR_BEVEL_2:
205             XDrawLine(RrDisplay(l->inst), l->pixmap,
206                       sp->bevel_dark->gc,
207                       left + 1, bottom - 2, right - 2, bottom - 2);
208             XDrawLine(RrDisplay(l->inst), l->pixmap,
209                       sp->bevel_dark->gc,
210                       right - 2, bottom - 2, right - 2, top + 1);
211
212             XDrawLine(RrDisplay(l->inst), l->pixmap,
213                       sp->bevel_light->gc,
214                       left + 1, top + 1, right - 2, top + 1);
215             XDrawLine(RrDisplay(l->inst), l->pixmap,
216                       sp->bevel_light->gc,
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, sp->bevel_light->gc,
230                       left, bottom, right, bottom);
231             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_light->gc,
232                       right, bottom, right, top);
233       
234             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_dark->gc,
235                       left, top, right, top);
236             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_dark->gc,
237                       left, bottom, left, top);
238             break;
239         case RR_BEVEL_2:
240             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_light->gc,
241                       left + 1, bottom - 2, right - 2, bottom - 2);
242             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_light->gc,
243                       right - 2, bottom - 2, right - 2, top + 1);
244       
245             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_dark->gc,
246                       left + 1, top + 1, right - 2, top + 1);
247             XDrawLine(RrDisplay(l->inst), l->pixmap, sp->bevel_dark->gc,
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             if (sp->border_color->gc == None)
258                 RrColorAllocateGC(sp->border_color);
259             XDrawRectangle(RrDisplay(l->inst), l->pixmap, sp->border_color->gc,
260                            left, top, right, bottom);
261         }
262         break;
263     default:  
264         g_assert_not_reached(); /* unhandled ReliefType */
265     }
266 }
267
268 /* * * * * * * * * * * * * * GRADIENT MAGIC WOOT * * * * * * * * * * * * * * */
269
270 #define VARS(x)                                                     \
271     unsigned int color##x[3];                                       \
272     int len##x, cdelta##x[3], error##x[3] = { 0, 0, 0 }, inc##x[3]; \
273     gboolean bigslope##x[3] /* color slope > 1 */
274
275 #define SETUP(x, from, to, w)         \
276     len##x = w;                       \
277                                       \
278     color##x[0] = from->r;            \
279     color##x[1] = from->g;            \
280     color##x[2] = from->b;            \
281                                       \
282     cdelta##x[0] = to->r - from->r;   \
283     cdelta##x[1] = to->g - from->g;   \
284     cdelta##x[2] = to->b - from->b;   \
285                                       \
286     if (cdelta##x[0] < 0) {           \
287         cdelta##x[0] = -cdelta##x[0]; \
288         inc##x[0] = -1;               \
289     } else                            \
290         inc##x[0] = 1;                \
291     if (cdelta##x[1] < 0) {           \
292         cdelta##x[1] = -cdelta##x[1]; \
293         inc##x[1] = -1;               \
294     } else                            \
295         inc##x[1] = 1;                \
296     if (cdelta##x[2] < 0) {           \
297         cdelta##x[2] = -cdelta##x[2]; \
298         inc##x[2] = -1;               \
299     } else                            \
300         inc##x[2] = 1;                \
301     bigslope##x[0] = cdelta##x[0] > w;\
302     bigslope##x[1] = cdelta##x[1] > w;\
303     bigslope##x[2] = cdelta##x[2] > w
304
305 #define COLOR_RR(x, c)                       \
306     c->r = color##x[0];                      \
307     c->g = color##x[1];                      \
308     c->b = color##x[2]
309
310 #define COLOR(x)                             \
311     ((color##x[0] << RrDefaultRedOffset) +   \
312      (color##x[1] << RrDefaultGreenOffset) + \
313      (color##x[2] << RrDefaultBlueOffset))
314
315 #define NEXT(x)                                           \
316 {                                                         \
317     int i;                                                \
318     for (i = 2; i >= 0; --i) {                            \
319         if (!cdelta##x[i]) continue;                      \
320                                                           \
321         if (!bigslope##x[i]) {                            \
322             /* Y (color) is dependant on X */             \
323             error##x[i] += cdelta##x[i];                  \
324             if ((error##x[i] << 1) >= len##x) {           \
325                 color##x[i] += inc##x[i];                 \
326                 error##x[i] -= len##x;                    \
327             }                                             \
328         } else {                                          \
329             /* X is dependant on Y (color) */             \
330             while (1) {                                   \
331                 color##x[i] += inc##x[i];                 \
332                 error##x[i] += len##x;                    \
333                 if ((error##x[i] << 1) >= cdelta##x[i]) { \
334                     error##x[i] -= cdelta##x[i];          \
335                     break;                                \
336                 }                                         \
337             }                                             \
338         }                                                 \
339     }                                                     \
340 }
341
342 static void gradient_horizontal(RrSurface *sf, int w, int h)
343 {
344     int x, y;
345     RrPixel32 *data = sf->RrPixel_data;
346     RrPixel32 current;
347
348     VARS(x);
349     SETUP(x, sf->primary, sf->secondary, w);
350
351     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
352         current = COLOR(x);
353         for (y = h - 1; y >= 0; --y)  /* 0 -> h */
354             *(data + y * w) = current;
355         ++data;
356
357         NEXT(x);
358     }
359     current = COLOR(x);
360     for (y = h - 1; y >= 0; --y)  /* 0 -> h */
361         *(data + y * w) = current;
362 }
363
364 static void gradient_vertical(RrSurface *sf, int w, int h)
365 {
366     int x, y;
367     RrPixel32 *data = sf->RrPixel_data;
368     RrPixel32 current;
369
370     VARS(y);
371     SETUP(y, sf->primary, sf->secondary, h);
372
373     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
374         current = COLOR(y);
375         for (x = w - 1; x >= 0; --x)  /* 0 -> w */
376             *(data++) = current;
377
378         NEXT(y);
379     }
380     current = COLOR(y);
381     for (x = w - 1; x >= 0; --x)  /* 0 -> w */
382         *(data++) = current;
383 }
384
385
386 static void gradient_diagonal(RrSurface *sf, int w, int h)
387 {
388     int x, y;
389     RrPixel32 *data = sf->RrPixel_data;
390     RrColor left, right;
391     RrColor extracorner;
392
393     VARS(lefty);
394     VARS(righty);
395     VARS(x);
396
397     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
398     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
399     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
400
401     SETUP(lefty, sf->primary, (&extracorner), h);
402     SETUP(righty, (&extracorner), sf->secondary, h);
403
404     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
405         COLOR_RR(lefty, (&left));
406         COLOR_RR(righty, (&right));
407
408         SETUP(x, (&left), (&right), w);
409
410         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
411             *(data++) = COLOR(x);
412
413             NEXT(x);
414         }
415         *(data++) = COLOR(x);
416
417         NEXT(lefty);
418         NEXT(righty);
419     }
420     COLOR_RR(lefty, (&left));
421     COLOR_RR(righty, (&right));
422
423     SETUP(x, (&left), (&right), w);
424
425     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
426         *(data++) = COLOR(x);
427         
428         NEXT(x);
429     }
430     *data = COLOR(x);
431 }
432
433 static void gradient_crossdiagonal(RrSurface *sf, int w, int h)
434 {
435     int x, y;
436     RrPixel32 *data = sf->RrPixel_data;
437     RrColor left, right;
438     RrColor extracorner;
439
440     VARS(lefty);
441     VARS(righty);
442     VARS(x);
443
444     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
445     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
446     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
447
448     SETUP(lefty, (&extracorner), sf->secondary, h);
449     SETUP(righty, sf->primary, (&extracorner), h);
450
451     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
452         COLOR_RR(lefty, (&left));
453         COLOR_RR(righty, (&right));
454
455         SETUP(x, (&left), (&right), w);
456
457         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
458             *(data++) = COLOR(x);
459
460             NEXT(x);
461         }
462         *(data++) = COLOR(x);
463
464         NEXT(lefty);
465         NEXT(righty);
466     }
467     COLOR_RR(lefty, (&left));
468     COLOR_RR(righty, (&right));
469
470     SETUP(x, (&left), (&right), w);
471
472     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
473         *(data++) = COLOR(x);
474         
475         NEXT(x);
476     }
477     *data = COLOR(x);
478 }
479
480 static void gradient_pyramid(RrSurface *sf, int inw, int inh)
481 {
482     int x, y, w = (inw >> 1) + 1, h = (inh >> 1) + 1;
483     RrPixel32 *data = sf->RrPixel_data;
484     RrPixel32 *end = data + inw*inh - 1;
485     RrPixel32 current;
486     RrColor left, right;
487     RrColor extracorner;
488
489     VARS(lefty);
490     VARS(righty);
491     VARS(x);
492
493     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
494     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
495     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
496
497     SETUP(lefty, (&extracorner), sf->secondary, h);
498     SETUP(righty, sf->primary, (&extracorner), h);
499
500     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
501         COLOR_RR(lefty, (&left));
502         COLOR_RR(righty, (&right));
503
504         SETUP(x, (&left), (&right), w);
505
506         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
507             current = COLOR(x);
508             *(data+x) = current;
509             *(data+inw-x) = current;
510             *(end-x) = current;
511             *(end-(inw-x)) = current;
512
513             NEXT(x);
514         }
515         current = COLOR(x);
516         *(data+x) = current;
517         *(data+inw-x) = current;
518         *(end-x) = current;
519         *(end-(inw-x)) = current;
520
521         data+=inw;
522         end-=inw;
523
524         NEXT(lefty);
525         NEXT(righty);
526     }
527     COLOR_RR(lefty, (&left));
528     COLOR_RR(righty, (&right));
529
530     SETUP(x, (&left), (&right), w);
531
532     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
533         current = COLOR(x);
534         *(data+x) = current;
535         *(data+inw-x) = current;
536         *(end-x) = current;
537         *(end-(inw-x)) = current;
538         
539         NEXT(x);
540     }
541     *(data+x) = current;
542     *(data+inw-x) = current;
543     *(end-x) = current;
544     *(end-(inw-x)) = current;
545 }
546
547 static void gradient_rectangle(RrSurface *sf, int inw, int inh)
548 {
549     int x, y, w = (inw >> 1) + 1, h = (inh >> 1) + 1;
550     RrPixel32 *data = sf->RrPixel_data;
551     RrPixel32 *end = data + inw*inh - 1;
552     RrPixel32 current;
553     RrColor left, right;
554     RrColor extracorner;
555
556     VARS(lefty);
557     VARS(righty);
558     VARS(x);
559
560     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
561     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
562     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
563
564     SETUP(lefty, (&extracorner), sf->secondary, h);
565     SETUP(righty, sf->primary, (&extracorner), h);
566
567     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
568         COLOR_RR(lefty, (&left));
569         COLOR_RR(righty, (&right));
570
571         SETUP(x, (&left), (&right), w);
572
573         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
574             current = COLOR(x);
575             *(data+x) = current;
576             *(data+inw-x) = current;
577             *(end-x) = current;
578             *(end-(inw-x)) = current;
579
580             NEXT(x);
581         }
582         current = COLOR(x);
583         *(data+x) = current;
584         *(data+inw-x) = current;
585         *(end-x) = current;
586         *(end-(inw-x)) = current;
587
588         data+=inw;
589         end-=inw;
590
591         NEXT(lefty);
592         NEXT(righty);
593     }
594     COLOR_RR(lefty, (&left));
595     COLOR_RR(righty, (&right));
596
597     SETUP(x, (&left), (&right), w);
598
599     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
600         current = COLOR(x);
601         *(data+x) = current;
602         *(data+inw-x) = current;
603         *(end-x) = current;
604         *(end-(inw-x)) = current;
605         
606         NEXT(x);
607     }
608     *(data+x) = current;
609     *(data+inw-x) = current;
610     *(end-x) = current;
611     *(end-(inw-x)) = current;
612 }
613
614 static void gradient_pipecross(RrSurface *sf, int inw, int inh)
615 {
616     RrPixel32 *data = sf->RrPixel_data;
617     RrPixel32 *end = data + inw*inh - 1;
618     RrPixel32 current;
619     float drx, dgx, dbx, dry, dgy, dby;
620     unsigned int r,g,b;
621     int x, y, h=(inh/2) + 1, w=(inw/2) + 1;
622
623     drx = (float)(sf->secondary->r -
624                   sf->primary->r);
625     dry = drx/(float)h;
626     drx/= (float)w;
627
628     dgx = (float)(sf->secondary->g -
629                   sf->primary->g);
630     dgy = dgx/(float)h;
631     dgx/= (float)w;
632
633     dbx = (float)(sf->secondary->b -
634                   sf->primary->b);
635     dby = dbx/(float)h;
636     dbx/= (float)w;
637
638     for (y = 0; y < h; ++y) {
639         for (x = 0; x < w; ++x, data) {
640             if ((float)x/(float)w > (float)y/(float)h) {
641                 r = sf->primary->r + (drx * x);
642                 g = sf->primary->g + (dgx * x);
643                 b = sf->primary->b + (dbx * x);
644             } else {
645                 r = sf->primary->r + (dry * x);
646                 g = sf->primary->g + (dgy * x);
647                 b = sf->primary->b + (dby * x);
648             }
649             current = (r << RrDefaultRedOffset)
650                 + (g << RrDefaultGreenOffset)
651                 + (b << RrDefaultBlueOffset);
652             *(data+x) = current;
653             *(data+inw-x) = current;
654             *(end-x) = current;
655             *(end-(inw-x)) = current;
656         }
657         data+=inw;
658         end-=inw;
659     }
660 }