Merge branch 'backport' into work
[dana/openbox.git] / render / gradient.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    gradient.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2008   Dana Jansens
6    Copyright (c) 2003        Derek Foreman
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    See the COPYING file for a copy of the GNU General Public License.
19 */
20
21 #include "render.h"
22 #include "gradient.h"
23 #include "color.h"
24 #include <glib.h>
25 #include <string.h>
26
27 static void highlight(RrSurface *s, RrPixel32 *x, RrPixel32 *y,
28                       gboolean raised);
29 static void gradient_parentrelative(RrAppearance *a, gint w, gint h);
30 static void gradient_solid(RrAppearance *l, gint w, gint h);
31 static void gradient_splitvertical(RrAppearance *a, gint w, gint h);
32 static void gradient_vertical(RrSurface *sf, gint w, gint h);
33 static void gradient_horizontal(RrSurface *sf, gint w, gint h);
34 static void gradient_mirrorhorizontal(RrSurface *sf, gint w, gint h);
35 static void gradient_diagonal(RrSurface *sf, gint w, gint h);
36 static void gradient_crossdiagonal(RrSurface *sf, gint w, gint h);
37 static void gradient_pyramid(RrSurface *sf, gint inw, gint inh);
38
39 void RrRender(RrAppearance *a, gint w, gint h)
40 {
41     RrPixel32 *data = a->surface.pixel_data;
42     RrPixel32 current;
43     guint r,g,b;
44     gint off, x;
45
46     switch (a->surface.grad) {
47     case RR_SURFACE_PARENTREL:
48         gradient_parentrelative(a, w, h);
49         break;
50     case RR_SURFACE_SOLID:
51         gradient_solid(a, w, h);
52         break;
53     case RR_SURFACE_SPLIT_VERTICAL:
54         gradient_splitvertical(a, w, h);
55         break;
56     case RR_SURFACE_VERTICAL:
57         gradient_vertical(&a->surface, w, h);
58         break;
59     case RR_SURFACE_HORIZONTAL:
60         gradient_horizontal(&a->surface, w, h);
61         break;
62     case RR_SURFACE_MIRROR_HORIZONTAL:
63         gradient_mirrorhorizontal(&a->surface, w, h);
64         break;
65     case RR_SURFACE_DIAGONAL:
66         gradient_diagonal(&a->surface, w, h);
67         break;
68     case RR_SURFACE_CROSS_DIAGONAL:
69         gradient_crossdiagonal(&a->surface, w, h);
70         break;
71     case RR_SURFACE_PYRAMID:
72         gradient_pyramid(&a->surface, w, h);
73         break;
74     default:
75         g_assert_not_reached(); /* unhandled gradient */
76         return;
77     }
78
79     if (a->surface.interlaced) {
80         gint i;
81         RrPixel32 *p;
82
83         r = a->surface.interlace_color->r;
84         g = a->surface.interlace_color->g;
85         b = a->surface.interlace_color->b;
86         current = (r << RrDefaultRedOffset)
87             + (g << RrDefaultGreenOffset)
88             + (b << RrDefaultBlueOffset);
89         p = data;
90         for (i = 0; i < h; i += 2, p += w)
91             for (x = 0; x < w; ++x, ++p)
92                 *p = current;
93     }
94
95     if (a->surface.relief == RR_RELIEF_FLAT && a->surface.border) {
96         r = a->surface.border_color->r;
97         g = a->surface.border_color->g;
98         b = a->surface.border_color->b;
99         current = (r << RrDefaultRedOffset)
100             + (g << RrDefaultGreenOffset)
101             + (b << RrDefaultBlueOffset);
102         for (off = 0, x = 0; x < w; ++x, off++) {
103             *(data + off) = current;
104             *(data + off + ((h-1) * w)) = current;
105         }
106         for (off = 0, x = 0; x < h; ++x, off++) {
107             *(data + (off * w)) = current;
108             *(data + (off * w) + w - 1) = current;
109         }
110     }
111
112     if (a->surface.relief != RR_RELIEF_FLAT) {
113         if (a->surface.bevel == RR_BEVEL_1) {
114             for (off = 1, x = 1; x < w - 1; ++x, off++)
115                 highlight(&a->surface, data + off,
116                           data + off + (h-1) * w,
117                           a->surface.relief==RR_RELIEF_RAISED);
118             for (off = 0, x = 0; x < h; ++x, off++)
119                 highlight(&a->surface, data + off * w,
120                           data + off * w + w - 1,
121                           a->surface.relief==RR_RELIEF_RAISED);
122         }
123
124         if (a->surface.bevel == RR_BEVEL_2) {
125             for (off = 2, x = 2; x < w - 2; ++x, off++)
126                 highlight(&a->surface, data + off + w,
127                           data + off + (h-2) * w,
128                           a->surface.relief==RR_RELIEF_RAISED);
129             for (off = 1, x = 1; x < h-1; ++x, off++)
130                 highlight(&a->surface, data + off * w + 1,
131                           data + off * w + w - 2,
132                           a->surface.relief==RR_RELIEF_RAISED);
133         }
134     }
135 }
136
137 static void highlight(RrSurface *s, RrPixel32 *x, RrPixel32 *y, gboolean raised)
138 {
139     gint r, g, b;
140
141     RrPixel32 *up, *down;
142     if (raised) {
143         up = x;
144         down = y;
145     } else {
146         up = y;
147         down = x;
148     }
149
150     r = (*up >> RrDefaultRedOffset) & 0xFF;
151     r += (r * s->bevel_light_adjust) >> 8;
152     g = (*up >> RrDefaultGreenOffset) & 0xFF;
153     g += (g * s->bevel_light_adjust) >> 8;
154     b = (*up >> RrDefaultBlueOffset) & 0xFF;
155     b += (b * s->bevel_light_adjust) >> 8;
156     if (r > 0xFF) r = 0xFF;
157     if (g > 0xFF) g = 0xFF;
158     if (b > 0xFF) b = 0xFF;
159     *up = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset)
160         + (b << RrDefaultBlueOffset);
161
162     r = (*down >> RrDefaultRedOffset) & 0xFF;
163     r -= (r * s->bevel_dark_adjust) >> 8;
164     g = (*down >> RrDefaultGreenOffset) & 0xFF;
165     g -= (g * s->bevel_dark_adjust) >> 8;
166     b = (*down >> RrDefaultBlueOffset) & 0xFF;
167     b -= (b * s->bevel_dark_adjust) >> 8;
168     *down = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset)
169         + (b << RrDefaultBlueOffset);
170 }
171
172 static void create_bevel_colors(RrAppearance *l)
173 {
174     gint r, g, b;
175
176     /* light color */
177     r = l->surface.primary->r;
178     r += (r * l->surface.bevel_light_adjust) >> 8;
179     g = l->surface.primary->g;
180     g += (g * l->surface.bevel_light_adjust) >> 8;
181     b = l->surface.primary->b;
182     b += (b * l->surface.bevel_light_adjust) >> 8;
183     if (r > 0xFF) r = 0xFF;
184     if (g > 0xFF) g = 0xFF;
185     if (b > 0xFF) b = 0xFF;
186     g_assert(!l->surface.bevel_light);
187     l->surface.bevel_light = RrColorNew(l->inst, r, g, b);
188
189     /* dark color */
190     r = l->surface.primary->r;
191     r -= (r * l->surface.bevel_dark_adjust) >> 8;
192     g = l->surface.primary->g;
193     g -= (g * l->surface.bevel_dark_adjust) >> 8;
194     b = l->surface.primary->b;
195     b -= (b * l->surface.bevel_dark_adjust) >> 8;
196     g_assert(!l->surface.bevel_dark);
197     l->surface.bevel_dark = RrColorNew(l->inst, r, g, b);
198 }
199
200 /*! Repeat the first pixel over the entire block of memory
201   @param start The block of memory. start[0] will be copied
202          to the rest of the block.
203   @param w The width of the block of memory (including the already-set first
204            element
205 */
206 static inline void repeat_pixel(RrPixel32 *start, gint w)
207 {
208     gint x;
209     RrPixel32 *dest;
210
211     dest = start + 1;
212
213     /* for really small things, just copy ourselves */
214     if (w < 8) {
215         for (x = w-1; x > 0; --x)
216             *(dest++) = *start;
217     }
218
219     /* for >= 8, then use O(log n) memcpy's... */
220     else {
221         gchar *cdest;
222         gint lenbytes;
223
224         /* copy the first 3 * 32 bits (3 words) ourselves - then we have
225            3 + the original 1 = 4 words to make copies of at a time
226
227            this is faster than doing memcpy for 1 or 2 words at a time
228         */
229         for (x = 3; x > 0; --x)
230             *(dest++) = *start;
231
232         /* cdest is a pointer to the pixel data that is typed char* so that
233            adding 1 to its position moves it only one byte
234
235            lenbytes is the amount of bytes that we will be copying each
236            iteration.  this doubles each time through the loop.
237
238            x is the number of bytes left to copy into.  lenbytes will alwaysa
239            be bounded by x
240
241            this loop will run O(log n) times (n is the number of bytes we
242            need to copy into), since the size of the copy is doubled each
243            iteration.  it seems that gcc does some nice optimizations to make
244            this memcpy very fast on hardware with support for vector operations
245            such as mmx or see.  here is an idea of the kind of speed up we are
246            getting by doing this (splitvertical3 switches from doing
247            "*(data++) = color" n times to doing this memcpy thing log n times:
248
249            %   cumulative   self              self     total           
250            time   seconds   seconds    calls  ms/call  ms/call  name    
251            49.44      0.88     0.88     1063     0.83     0.83  splitvertical1
252            47.19      1.72     0.84     1063     0.79     0.79  splitvertical2
253             2.81      1.77     0.05     1063     0.05     0.05  splitvertical3
254         */
255         cdest = (gchar*)dest;
256         lenbytes = 4 * sizeof(RrPixel32);
257         for (x = (w - 4) * sizeof(RrPixel32); x > 0;) {
258             memcpy(cdest, start, lenbytes);
259             x -= lenbytes;
260             cdest += lenbytes;
261             lenbytes <<= 1;
262             if (lenbytes > x)
263                 lenbytes = x;
264         }
265     }
266 }
267
268 static void gradient_parentrelative(RrAppearance *a, gint w, gint h)
269 {
270     RrPixel32 *source, *dest;
271     gint sw, sh, partial_w, partial_h, i;
272
273     g_assert (a->surface.parent);
274     g_assert (a->surface.parent->w);
275
276     sw = a->surface.parent->w;
277     sh = a->surface.parent->h;
278
279     /* This is a little hack. When a texture is parentrelative, and the same
280        area as the parent, and has a bevel, it will draw its bevel on top
281        of the parent's, amplifying it. So instead, rerender the child with
282        the parent's settings, but the child's bevel and interlace */
283     if (a->surface.relief != RR_RELIEF_FLAT &&
284         (a->surface.parent->surface.relief != RR_RELIEF_FLAT ||
285          a->surface.parent->surface.border) &&
286         !a->surface.parentx && !a->surface.parenty &&
287         sw == w && sh == h)
288     {
289         RrSurface old = a->surface;
290         a->surface = a->surface.parent->surface;
291
292         /* turn these off for the parent */
293         a->surface.relief = RR_RELIEF_FLAT;
294         a->surface.border = FALSE;
295
296         a->surface.pixel_data = old.pixel_data;
297
298         RrRender(a, w, h);
299         a->surface = old;
300     } else {
301         source = (a->surface.parent->surface.pixel_data +
302                   a->surface.parentx + sw * a->surface.parenty);
303         dest = a->surface.pixel_data;
304
305         if (a->surface.parentx + w > sw) {
306             partial_w = sw - a->surface.parentx;
307         } else partial_w = w;
308
309         if (a->surface.parenty + h > sh) {
310             partial_h = sh - a->surface.parenty;
311         } else partial_h = h;
312
313         for (i = 0; i < partial_h; i++, source += sw, dest += w) {
314             memcpy(dest, source, partial_w * sizeof(RrPixel32));
315         }
316     }
317 }
318
319 static void gradient_solid(RrAppearance *l, gint w, gint h)
320 {
321     gint i;
322     RrPixel32 pix;
323     RrPixel32 *data = l->surface.pixel_data;
324     RrSurface *sp = &l->surface;
325     gint left = 0, top = 0, right = w - 1, bottom = h - 1;
326
327     pix = (sp->primary->r << RrDefaultRedOffset)
328         + (sp->primary->g << RrDefaultGreenOffset)
329         + (sp->primary->b << RrDefaultBlueOffset);
330
331     for (i = 0; i < w * h; i++)
332         *data++ = pix;
333
334     if (sp->interlaced)
335         return;
336
337     XFillRectangle(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->primary),
338                    0, 0, w, h);
339
340     switch (sp->relief) {
341     case RR_RELIEF_RAISED:
342         if (!sp->bevel_dark)
343             create_bevel_colors(l);
344
345         switch (sp->bevel) {
346         case RR_BEVEL_1:
347             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
348                       left, bottom, right, bottom);
349             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
350                       right, bottom, right, top);
351
352             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
353                       left, top, right, top);
354             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
355                       left, bottom, left, top);
356             break;
357         case RR_BEVEL_2:
358             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
359                       left + 2, bottom - 1, right - 2, bottom - 1);
360             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
361                       right - 1, bottom - 1, right - 1, top + 1);
362
363             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
364                       left + 2, top + 1, right - 2, top + 1);
365             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
366                       left + 1, bottom - 1, left + 1, top + 1);
367             break;
368         default:
369             g_assert_not_reached(); /* unhandled BevelType */
370         }
371         break;
372     case RR_RELIEF_SUNKEN:
373         if (!sp->bevel_dark)
374             create_bevel_colors(l);
375
376         switch (sp->bevel) {
377         case RR_BEVEL_1:
378             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
379                       left, bottom, right, bottom);
380             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
381                       right, bottom, right, top);
382
383             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
384                       left, top, right, top);
385             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
386                       left, bottom, left, top);
387             break;
388         case RR_BEVEL_2:
389             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
390                       left + 2, bottom - 1, right - 2, bottom - 1);
391             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
392                       right - 1, bottom - 1, right - 1, top + 1);
393
394             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
395                       left + 2, top + 1, right - 2, top + 1);
396             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
397                       left + 1, bottom - 1, left + 1, top + 1);
398             break;
399         default:
400             g_assert_not_reached(); /* unhandled BevelType */
401         }
402         break;
403     case RR_RELIEF_FLAT:
404         if (sp->border) {
405             XDrawRectangle(RrDisplay(l->inst), l->pixmap,
406                            RrColorGC(sp->border_color),
407                            left, top, right, bottom);
408         }
409         break;
410     default:
411         g_assert_not_reached(); /* unhandled ReliefType */
412     }
413 }
414
415 /* * * * * * * * * * * * * * GRADIENT MAGIC WOOT * * * * * * * * * * * * * * */
416
417 #define VARS(x)                                                     \
418     guint color##x[3];                                       \
419     gint len##x, cdelta##x[3], error##x[3] = { 0, 0, 0 }, inc##x[3]; \
420     gboolean bigslope##x[3] /* color slope > 1 */
421
422 #define SETUP(x, from, to, w)         \
423     len##x = w;                       \
424                                       \
425     color##x[0] = from->r;            \
426     color##x[1] = from->g;            \
427     color##x[2] = from->b;            \
428                                       \
429     cdelta##x[0] = to->r - from->r;   \
430     cdelta##x[1] = to->g - from->g;   \
431     cdelta##x[2] = to->b - from->b;   \
432                                       \
433     if (cdelta##x[0] < 0) {           \
434         cdelta##x[0] = -cdelta##x[0]; \
435         inc##x[0] = -1;               \
436     } else                            \
437         inc##x[0] = 1;                \
438     if (cdelta##x[1] < 0) {           \
439         cdelta##x[1] = -cdelta##x[1]; \
440         inc##x[1] = -1;               \
441     } else                            \
442         inc##x[1] = 1;                \
443     if (cdelta##x[2] < 0) {           \
444         cdelta##x[2] = -cdelta##x[2]; \
445         inc##x[2] = -1;               \
446     } else                            \
447         inc##x[2] = 1;                \
448     bigslope##x[0] = cdelta##x[0] > w;\
449     bigslope##x[1] = cdelta##x[1] > w;\
450     bigslope##x[2] = cdelta##x[2] > w
451
452 #define COLOR_RR(x, c)                       \
453     c->r = color##x[0];                      \
454     c->g = color##x[1];                      \
455     c->b = color##x[2]
456
457 #define COLOR(x)                             \
458     ((color##x[0] << RrDefaultRedOffset) +   \
459      (color##x[1] << RrDefaultGreenOffset) + \
460      (color##x[2] << RrDefaultBlueOffset))
461
462 #define INCREMENT(x, i) \
463     (inc##x[i])
464
465 #define NEXT(x)                                           \
466 {                                                         \
467     gint i;                                                \
468     for (i = 2; i >= 0; --i) {                            \
469         if (!cdelta##x[i]) continue;                      \
470                                                           \
471         if (!bigslope##x[i]) {                            \
472             /* Y (color) is dependant on X */             \
473             error##x[i] += cdelta##x[i];                  \
474             if ((error##x[i] << 1) >= len##x) {           \
475                 color##x[i] += INCREMENT(x, i);           \
476                 error##x[i] -= len##x;                    \
477             }                                             \
478         } else {                                          \
479             /* X is dependant on Y (color) */             \
480             while (1) {                                   \
481                 color##x[i] += INCREMENT(x, i);           \
482                 error##x[i] += len##x;                    \
483                 if ((error##x[i] << 1) >= cdelta##x[i]) { \
484                     error##x[i] -= cdelta##x[i];          \
485                     break;                                \
486                 }                                         \
487             }                                             \
488         }                                                 \
489     }                                                     \
490 }
491
492 static void gradient_splitvertical(RrAppearance *a, gint w, gint h)
493 {
494     gint y1, y2, y3;
495     RrSurface *sf = &a->surface;
496     RrPixel32 *data;
497     gint y1sz, y2sz, y3sz;
498
499     VARS(y1);
500     VARS(y2);
501     VARS(y3);
502
503     /* if h <= 5, then a 0 or 1px middle gradient.
504        if h > 5, then always a 1px middle gradient.
505     */
506     if (h <= 5) {
507         y1sz = MAX(h/2, 0);
508         y2sz = (h < 3 ? 0 : h % 2);
509         y3sz = MAX(h/2, 1);
510     }
511     else {
512         y1sz = h/2 - (1 - (h % 2));
513         y2sz = 1;
514         y3sz = h/2;
515     }
516
517     SETUP(y1, sf->split_primary, sf->primary, y1sz);
518     if (y2sz) {
519         /* setup to get the colors _in between_ these other 2 */
520         SETUP(y2, sf->primary, sf->secondary, y2sz + 2);
521         NEXT(y2); /* skip the first one, its the same as the last of y1 */
522     }
523     SETUP(y3, sf->secondary, sf->split_secondary,  y3sz);
524
525     /* find the color for the first pixel of each row first */
526     data = sf->pixel_data;
527
528     for (y1 = y1sz-1; y1 > 0; --y1) {
529         *data = COLOR(y1);
530         data += w;
531         NEXT(y1);
532     }
533     *data = COLOR(y1);
534     data += w;
535     for (y2 = y2sz-1; y2 > 0; --y2) {
536         *data = COLOR(y2);
537         data += w;
538         NEXT(y2);
539     }
540     *data = COLOR(y2);
541     data += w;
542     for (y3 = y3sz-1; y3 > 0; --y3) {
543         *data = COLOR(y3);
544         data += w;
545         NEXT(y3);
546     }
547     *data = COLOR(y3);
548
549     /* copy the first pixels into the whole rows */
550     data = sf->pixel_data;
551     for (y1 = h; y1 > 0; --y1) {
552         repeat_pixel(data, w);
553         data += w;
554     }
555 }
556
557 static void gradient_horizontal(RrSurface *sf, gint w, gint h)
558 {
559     gint x, y, cpbytes;
560     RrPixel32 *data = sf->pixel_data, *datav;
561     gchar *datac;
562
563     VARS(x);
564     SETUP(x, sf->primary, sf->secondary, w);
565
566     /* set the color values for the first row */
567     datav = data;
568     for (x = w - 1; x > 0; --x) {  /* 0 -> w - 1 */
569         *datav = COLOR(x);
570         ++datav;
571         NEXT(x);
572     }
573     *datav = COLOR(x);
574     ++datav;
575
576     /* copy the first row to the rest in O(logn) copies */
577     datac = (gchar*)datav;
578     cpbytes = 1 * w * sizeof(RrPixel32);
579     for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) {
580         memcpy(datac, data, cpbytes);
581         y -= cpbytes;
582         datac += cpbytes;
583         cpbytes <<= 1;
584         if (cpbytes > y)
585             cpbytes = y;
586     }
587 }
588
589 static void gradient_mirrorhorizontal(RrSurface *sf, gint w, gint h)
590 {
591     gint x, y, half1, half2, cpbytes;
592     RrPixel32 *data = sf->pixel_data, *datav;
593     gchar *datac;
594
595     VARS(x);
596
597     half1 = (w + 1) / 2;
598     half2 = w / 2;
599
600     /* set the color values for the first row */
601
602     SETUP(x, sf->primary, sf->secondary, half1);
603     datav = data;
604     for (x = half1 - 1; x > 0; --x) {  /* 0 -> half1 - 1 */
605         *datav = COLOR(x);
606         ++datav;
607         NEXT(x);
608     }
609     *datav = COLOR(x);
610     ++datav;
611
612     if (half2 > 0) {
613         SETUP(x, sf->secondary, sf->primary, half2);
614         for (x = half2 - 1; x > 0; --x) {  /* 0 -> half2 - 1 */
615             *datav = COLOR(x);
616             ++datav;
617             NEXT(x);
618         }
619         *datav = COLOR(x);
620         ++datav;
621     }
622
623     /* copy the first row to the rest in O(logn) copies */
624     datac = (gchar*)datav;
625     cpbytes = 1 * w * sizeof(RrPixel32);
626     for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) {
627         memcpy(datac, data, cpbytes);
628         y -= cpbytes;
629         datac += cpbytes;
630         cpbytes <<= 1;
631         if (cpbytes > y)
632             cpbytes = y;
633     }
634 }
635
636 static void gradient_vertical(RrSurface *sf, gint w, gint h)
637 {
638     gint y;
639     RrPixel32 *data;
640
641     VARS(y);
642     SETUP(y, sf->primary, sf->secondary, h);
643
644     /* find the color for the first pixel of each row first */
645     data = sf->pixel_data;
646
647     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
648         *data = COLOR(y);
649         data += w;
650         NEXT(y);
651     }
652     *data = COLOR(y);
653
654     /* copy the first pixels into the whole rows */
655     data = sf->pixel_data;
656     for (y = h; y > 0; --y) {
657         repeat_pixel(data, w);
658         data += w;
659     }
660 }
661
662
663 static void gradient_diagonal(RrSurface *sf, gint w, gint h)
664 {
665     gint x, y;
666     RrPixel32 *data = sf->pixel_data;
667     RrColor left, right;
668     RrColor extracorner;
669
670     VARS(lefty);
671     VARS(righty);
672     VARS(x);
673
674     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
675     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
676     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
677
678     SETUP(lefty, sf->primary, (&extracorner), h);
679     SETUP(righty, (&extracorner), sf->secondary, h);
680
681     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
682         COLOR_RR(lefty, (&left));
683         COLOR_RR(righty, (&right));
684
685         SETUP(x, (&left), (&right), w);
686
687         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
688             *(data++) = COLOR(x);
689
690             NEXT(x);
691         }
692         *(data++) = COLOR(x);
693
694         NEXT(lefty);
695         NEXT(righty);
696     }
697     COLOR_RR(lefty, (&left));
698     COLOR_RR(righty, (&right));
699
700     SETUP(x, (&left), (&right), w);
701
702     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
703         *(data++) = COLOR(x);
704
705         NEXT(x);
706     }
707     *data = COLOR(x);
708 }
709
710 static void gradient_crossdiagonal(RrSurface *sf, gint w, gint h)
711 {
712     gint x, y;
713     RrPixel32 *data = sf->pixel_data;
714     RrColor left, right;
715     RrColor extracorner;
716
717     VARS(lefty);
718     VARS(righty);
719     VARS(x);
720
721     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
722     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
723     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
724
725     SETUP(lefty, (&extracorner), sf->secondary, h);
726     SETUP(righty, sf->primary, (&extracorner), h);
727
728     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
729         COLOR_RR(lefty, (&left));
730         COLOR_RR(righty, (&right));
731
732         SETUP(x, (&left), (&right), w);
733
734         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
735             *(data++) = COLOR(x);
736
737             NEXT(x);
738         }
739         *(data++) = COLOR(x);
740
741         NEXT(lefty);
742         NEXT(righty);
743     }
744     COLOR_RR(lefty, (&left));
745     COLOR_RR(righty, (&right));
746
747     SETUP(x, (&left), (&right), w);
748
749     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
750         *(data++) = COLOR(x);
751
752         NEXT(x);
753     }
754     *data = COLOR(x);
755 }
756
757 static void gradient_pyramid(RrSurface *sf, gint w, gint h)
758 {
759     RrPixel32 *ldata, *rdata;
760     RrPixel32 *cp;
761     RrColor left, right;
762     RrColor extracorner;
763     gint x, y, halfw, halfh, midx, midy;
764
765     VARS(lefty);
766     VARS(righty);
767     VARS(x);
768
769     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
770     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
771     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
772
773     halfw = w >> 1;
774     halfh = h >> 1;
775     midx = w - halfw - halfw; /* 0 or 1, depending if w is even or odd */
776     midy = h - halfh - halfh;   /* 0 or 1, depending if h is even or odd */
777
778     SETUP(lefty, sf->primary, (&extracorner), halfh + midy);
779     SETUP(righty, (&extracorner), sf->secondary, halfh + midy);
780
781     /* draw the top half
782
783        it is faster to draw both top quarters together than to draw one and
784        then copy it over to the other side.
785     */
786
787     ldata = sf->pixel_data;
788     rdata = ldata + w - 1;
789     for (y = halfh + midy; y > 0; --y) {  /* 0 -> (h+1)/2 */
790         RrPixel32 c;
791
792         COLOR_RR(lefty, (&left));
793         COLOR_RR(righty, (&right));
794
795         SETUP(x, (&left), (&right), halfw + midx);
796
797         for (x = halfw + midx - 1; x > 0; --x) {  /* 0 -> (w+1)/2 */
798             c = COLOR(x);
799             *(ldata++) = *(rdata--) = c;
800
801             NEXT(x);
802         }
803         c = COLOR(x);
804         *ldata = *rdata = c;
805         ldata += halfw + 1;
806         rdata += halfw - 1 + midx + w;
807
808         NEXT(lefty);
809         NEXT(righty);
810     }
811
812     /* copy the top half into the bottom half, mirroring it, so we can only
813        copy one row at a time
814
815        it is faster, to move the writing pointer forward, and the reading
816        pointer backward
817
818        this is the current code, moving the write pointer forward and read
819        pointer backward
820        41.78      4.26     1.78      504     3.53     3.53  gradient_pyramid2
821        this is the opposite, moving the read pointer forward and the write
822        pointer backward
823        42.27      4.40     1.86      504     3.69     3.69  gradient_pyramid2
824        
825     */
826     ldata = sf->pixel_data + (halfh - 1) * w;
827     cp = ldata + (midy + 1) * w;
828     for (y = halfh; y > 0; --y) {
829         memcpy(cp, ldata, w * sizeof(RrPixel32));
830         ldata -= w;
831         cp += w;
832     }
833 }