8b6850f0e5a41adbea92d01f8f34007946069eed
[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     register 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     register 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     register 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     register 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;
272     register gint i;
273
274     g_assert (a->surface.parent);
275     g_assert (a->surface.parent->w);
276
277     sw = a->surface.parent->w;
278     sh = a->surface.parent->h;
279
280     /* This is a little hack. When a texture is parentrelative, and the same
281        area as the parent, and has a bevel, it will draw its bevel on top
282        of the parent's, amplifying it. So instead, rerender the child with
283        the parent's settings, but the child's bevel and interlace */
284     if (a->surface.relief != RR_RELIEF_FLAT &&
285         (a->surface.parent->surface.relief != RR_RELIEF_FLAT ||
286          a->surface.parent->surface.border) &&
287         !a->surface.parentx && !a->surface.parenty &&
288         sw == w && sh == h)
289     {
290         RrSurface old = a->surface;
291         a->surface = a->surface.parent->surface;
292
293         /* turn these off for the parent */
294         a->surface.relief = RR_RELIEF_FLAT;
295         a->surface.border = FALSE;
296
297         a->surface.pixel_data = old.pixel_data;
298
299         RrRender(a, w, h);
300         a->surface = old;
301     } else {
302         source = (a->surface.parent->surface.pixel_data +
303                   a->surface.parentx + sw * a->surface.parenty);
304         dest = a->surface.pixel_data;
305
306         if (a->surface.parentx + w > sw) {
307             partial_w = sw - a->surface.parentx;
308         } else partial_w = w;
309
310         if (a->surface.parenty + h > sh) {
311             partial_h = sh - a->surface.parenty;
312         } else partial_h = h;
313
314         for (i = 0; i < partial_h; i++, source += sw, dest += w) {
315             memcpy(dest, source, partial_w * sizeof(RrPixel32));
316         }
317     }
318 }
319
320 static void gradient_solid(RrAppearance *l, gint w, gint h)
321 {
322     register gint i;
323     RrPixel32 pix;
324     RrPixel32 *data = l->surface.pixel_data;
325     RrSurface *sp = &l->surface;
326     gint left = 0, top = 0, right = w - 1, bottom = h - 1;
327
328     pix = (sp->primary->r << RrDefaultRedOffset)
329         + (sp->primary->g << RrDefaultGreenOffset)
330         + (sp->primary->b << RrDefaultBlueOffset);
331
332     for (i = 0; i < w * h; i++)
333         *data++ = pix;
334
335     if (sp->interlaced)
336         return;
337
338     XFillRectangle(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->primary),
339                    0, 0, w, h);
340
341     switch (sp->relief) {
342     case RR_RELIEF_RAISED:
343         if (!sp->bevel_dark)
344             create_bevel_colors(l);
345
346         switch (sp->bevel) {
347         case RR_BEVEL_1:
348             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
349                       left, bottom, right, bottom);
350             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
351                       right, bottom, right, top);
352
353             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
354                       left, top, right, top);
355             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
356                       left, bottom, left, top);
357             break;
358         case RR_BEVEL_2:
359             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
360                       left + 2, bottom - 1, right - 2, bottom - 1);
361             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
362                       right - 1, bottom - 1, right - 1, top + 1);
363
364             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
365                       left + 2, top + 1, right - 2, top + 1);
366             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
367                       left + 1, bottom - 1, left + 1, top + 1);
368             break;
369         default:
370             g_assert_not_reached(); /* unhandled BevelType */
371         }
372         break;
373     case RR_RELIEF_SUNKEN:
374         if (!sp->bevel_dark)
375             create_bevel_colors(l);
376
377         switch (sp->bevel) {
378         case RR_BEVEL_1:
379             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
380                       left, bottom, right, bottom);
381             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
382                       right, bottom, right, top);
383
384             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
385                       left, top, right, top);
386             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
387                       left, bottom, left, top);
388             break;
389         case RR_BEVEL_2:
390             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
391                       left + 2, bottom - 1, right - 2, bottom - 1);
392             XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light),
393                       right - 1, bottom - 1, right - 1, top + 1);
394
395             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
396                       left + 2, top + 1, right - 2, top + 1);
397             XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark),
398                       left + 1, bottom - 1, left + 1, top + 1);
399             break;
400         default:
401             g_assert_not_reached(); /* unhandled BevelType */
402         }
403         break;
404     case RR_RELIEF_FLAT:
405         if (sp->border) {
406             XDrawRectangle(RrDisplay(l->inst), l->pixmap,
407                            RrColorGC(sp->border_color),
408                            left, top, right, bottom);
409         }
410         break;
411     default:
412         g_assert_not_reached(); /* unhandled ReliefType */
413     }
414 }
415
416 /* * * * * * * * * * * * * * GRADIENT MAGIC WOOT * * * * * * * * * * * * * * */
417
418 #define VARS(x)                                                \
419     register gint len##x;                                      \
420     guint color##x[3];                                         \
421     gint cdelta##x[3], error##x[3] = { 0, 0, 0 }, inc##x[3];   \
422     gboolean bigslope##x[3] /* color slope > 1 */
423
424 #define SETUP(x, from, to, w)         \
425     len##x = w;                       \
426                                       \
427     color##x[0] = from->r;            \
428     color##x[1] = from->g;            \
429     color##x[2] = from->b;            \
430                                       \
431     cdelta##x[0] = to->r - from->r;   \
432     cdelta##x[1] = to->g - from->g;   \
433     cdelta##x[2] = to->b - from->b;   \
434                                       \
435     if (cdelta##x[0] < 0) {           \
436         cdelta##x[0] = -cdelta##x[0]; \
437         inc##x[0] = -1;               \
438     } else                            \
439         inc##x[0] = 1;                \
440     if (cdelta##x[1] < 0) {           \
441         cdelta##x[1] = -cdelta##x[1]; \
442         inc##x[1] = -1;               \
443     } else                            \
444         inc##x[1] = 1;                \
445     if (cdelta##x[2] < 0) {           \
446         cdelta##x[2] = -cdelta##x[2]; \
447         inc##x[2] = -1;               \
448     } else                            \
449         inc##x[2] = 1;                \
450     bigslope##x[0] = cdelta##x[0] > w;\
451     bigslope##x[1] = cdelta##x[1] > w;\
452     bigslope##x[2] = cdelta##x[2] > w
453
454 #define COLOR_RR(x, c)                       \
455     c->r = color##x[0];                      \
456     c->g = color##x[1];                      \
457     c->b = color##x[2]
458
459 #define COLOR(x)                             \
460     ((color##x[0] << RrDefaultRedOffset) +   \
461      (color##x[1] << RrDefaultGreenOffset) + \
462      (color##x[2] << RrDefaultBlueOffset))
463
464 #define INCREMENT(x, i) \
465     (inc##x[i])
466
467 #define NEXT(x)                                           \
468 {                                                         \
469     register gint i;                                      \
470     for (i = 2; i >= 0; --i) {                            \
471         if (!cdelta##x[i]) continue;                      \
472                                                           \
473         if (!bigslope##x[i]) {                            \
474             /* Y (color) is dependant on X */             \
475             error##x[i] += cdelta##x[i];                  \
476             if ((error##x[i] << 1) >= len##x) {           \
477                 color##x[i] += INCREMENT(x, i);           \
478                 error##x[i] -= len##x;                    \
479             }                                             \
480         } else {                                          \
481             /* X is dependant on Y (color) */             \
482             while (1) {                                   \
483                 color##x[i] += INCREMENT(x, i);           \
484                 error##x[i] += len##x;                    \
485                 if ((error##x[i] << 1) >= cdelta##x[i]) { \
486                     error##x[i] -= cdelta##x[i];          \
487                     break;                                \
488                 }                                         \
489             }                                             \
490         }                                                 \
491     }                                                     \
492 }
493
494 static void gradient_splitvertical(RrAppearance *a, gint w, gint h)
495 {
496     register gint y1, y2, y3;
497     RrSurface *sf = &a->surface;
498     RrPixel32 *data;
499     register gint y1sz, y2sz, y3sz;
500
501     VARS(y1);
502     VARS(y2);
503     VARS(y3);
504
505     /* if h <= 5, then a 0 or 1px middle gradient.
506        if h > 5, then always a 1px middle gradient.
507     */
508     if (h <= 5) {
509         y1sz = MAX(h/2, 0);
510         y2sz = (h < 3 ? 0 : h % 2);
511         y3sz = MAX(h/2, 1);
512     }
513     else {
514         y1sz = h/2 - (1 - (h % 2));
515         y2sz = 1;
516         y3sz = h/2;
517     }
518
519     SETUP(y1, sf->split_primary, sf->primary, y1sz);
520     if (y2sz) {
521         /* setup to get the colors _in between_ these other 2 */
522         SETUP(y2, sf->primary, sf->secondary, y2sz + 2);
523         NEXT(y2); /* skip the first one, its the same as the last of y1 */
524     }
525     SETUP(y3, sf->secondary, sf->split_secondary,  y3sz);
526
527     /* find the color for the first pixel of each row first */
528     data = sf->pixel_data;
529
530     for (y1 = y1sz-1; y1 > 0; --y1) {
531         *data = COLOR(y1);
532         data += w;
533         NEXT(y1);
534     }
535     *data = COLOR(y1);
536     data += w;
537     for (y2 = y2sz-1; y2 > 0; --y2) {
538         *data = COLOR(y2);
539         data += w;
540         NEXT(y2);
541     }
542     *data = COLOR(y2);
543     data += w;
544     for (y3 = y3sz-1; y3 > 0; --y3) {
545         *data = COLOR(y3);
546         data += w;
547         NEXT(y3);
548     }
549     *data = COLOR(y3);
550
551     /* copy the first pixels into the whole rows */
552     data = sf->pixel_data;
553     for (y1 = h; y1 > 0; --y1) {
554         repeat_pixel(data, w);
555         data += w;
556     }
557 }
558
559 static void gradient_horizontal(RrSurface *sf, gint w, gint h)
560 {
561     register gint x, y, cpbytes;
562     RrPixel32 *data = sf->pixel_data, *datav;
563     gchar *datac;
564
565     VARS(x);
566     SETUP(x, sf->primary, sf->secondary, w);
567
568     /* set the color values for the first row */
569     datav = data;
570     for (x = w - 1; x > 0; --x) {  /* 0 -> w - 1 */
571         *datav = COLOR(x);
572         ++datav;
573         NEXT(x);
574     }
575     *datav = COLOR(x);
576     ++datav;
577
578     /* copy the first row to the rest in O(logn) copies */
579     datac = (gchar*)datav;
580     cpbytes = 1 * w * sizeof(RrPixel32);
581     for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) {
582         memcpy(datac, data, cpbytes);
583         y -= cpbytes;
584         datac += cpbytes;
585         cpbytes <<= 1;
586         if (cpbytes > y)
587             cpbytes = y;
588     }
589 }
590
591 static void gradient_mirrorhorizontal(RrSurface *sf, gint w, gint h)
592 {
593     register gint x, y, half1, half2, cpbytes;
594     RrPixel32 *data = sf->pixel_data, *datav;
595     gchar *datac;
596
597     VARS(x);
598
599     half1 = (w + 1) / 2;
600     half2 = w / 2;
601
602     /* set the color values for the first row */
603
604     SETUP(x, sf->primary, sf->secondary, half1);
605     datav = data;
606     for (x = half1 - 1; x > 0; --x) {  /* 0 -> half1 - 1 */
607         *datav = COLOR(x);
608         ++datav;
609         NEXT(x);
610     }
611     *datav = COLOR(x);
612     ++datav;
613
614     if (half2 > 0) {
615         SETUP(x, sf->secondary, sf->primary, half2);
616         for (x = half2 - 1; x > 0; --x) {  /* 0 -> half2 - 1 */
617             *datav = COLOR(x);
618             ++datav;
619             NEXT(x);
620         }
621         *datav = COLOR(x);
622         ++datav;
623     }
624
625     /* copy the first row to the rest in O(logn) copies */
626     datac = (gchar*)datav;
627     cpbytes = 1 * w * sizeof(RrPixel32);
628     for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) {
629         memcpy(datac, data, cpbytes);
630         y -= cpbytes;
631         datac += cpbytes;
632         cpbytes <<= 1;
633         if (cpbytes > y)
634             cpbytes = y;
635     }
636 }
637
638 static void gradient_vertical(RrSurface *sf, gint w, gint h)
639 {
640     register gint y;
641     RrPixel32 *data;
642
643     VARS(y);
644     SETUP(y, sf->primary, sf->secondary, h);
645
646     /* find the color for the first pixel of each row first */
647     data = sf->pixel_data;
648
649     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
650         *data = COLOR(y);
651         data += w;
652         NEXT(y);
653     }
654     *data = COLOR(y);
655
656     /* copy the first pixels into the whole rows */
657     data = sf->pixel_data;
658     for (y = h; y > 0; --y) {
659         repeat_pixel(data, w);
660         data += w;
661     }
662 }
663
664 static void gradient_diagonal(RrSurface *sf, gint w, gint h)
665 {
666     register gint x, y;
667     RrPixel32 *data = sf->pixel_data;
668     RrColor left, right;
669     RrColor extracorner;
670
671     VARS(lefty);
672     VARS(righty);
673     VARS(x);
674
675     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
676     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
677     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
678
679     SETUP(lefty, sf->primary, (&extracorner), h);
680     SETUP(righty, (&extracorner), sf->secondary, h);
681
682     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
683         COLOR_RR(lefty, (&left));
684         COLOR_RR(righty, (&right));
685
686         SETUP(x, (&left), (&right), w);
687
688         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
689             *(data++) = COLOR(x);
690
691             NEXT(x);
692         }
693         *(data++) = COLOR(x);
694
695         NEXT(lefty);
696         NEXT(righty);
697     }
698     COLOR_RR(lefty, (&left));
699     COLOR_RR(righty, (&right));
700
701     SETUP(x, (&left), (&right), w);
702
703     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
704         *(data++) = COLOR(x);
705
706         NEXT(x);
707     }
708     *data = COLOR(x);
709 }
710
711 static void gradient_crossdiagonal(RrSurface *sf, gint w, gint h)
712 {
713     register gint x, y;
714     RrPixel32 *data = sf->pixel_data;
715     RrColor left, right;
716     RrColor extracorner;
717
718     VARS(lefty);
719     VARS(righty);
720     VARS(x);
721
722     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
723     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
724     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
725
726     SETUP(lefty, (&extracorner), sf->secondary, h);
727     SETUP(righty, sf->primary, (&extracorner), h);
728
729     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
730         COLOR_RR(lefty, (&left));
731         COLOR_RR(righty, (&right));
732
733         SETUP(x, (&left), (&right), w);
734
735         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
736             *(data++) = COLOR(x);
737
738             NEXT(x);
739         }
740         *(data++) = COLOR(x);
741
742         NEXT(lefty);
743         NEXT(righty);
744     }
745     COLOR_RR(lefty, (&left));
746     COLOR_RR(righty, (&right));
747
748     SETUP(x, (&left), (&right), w);
749
750     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
751         *(data++) = COLOR(x);
752
753         NEXT(x);
754     }
755     *data = COLOR(x);
756 }
757
758 static void gradient_pyramid(RrSurface *sf, gint w, gint h)
759 {
760     RrPixel32 *ldata, *rdata;
761     RrPixel32 *cp;
762     RrColor left, right;
763     RrColor extracorner;
764     register gint x, y, halfw, halfh, midx, midy;
765
766     VARS(lefty);
767     VARS(righty);
768     VARS(x);
769
770     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
771     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
772     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
773
774     halfw = w >> 1;
775     halfh = h >> 1;
776     midx = w - halfw - halfw; /* 0 or 1, depending if w is even or odd */
777     midy = h - halfh - halfh;   /* 0 or 1, depending if h is even or odd */
778
779     SETUP(lefty, sf->primary, (&extracorner), halfh + midy);
780     SETUP(righty, (&extracorner), sf->secondary, halfh + midy);
781
782     /* draw the top half
783
784        it is faster to draw both top quarters together than to draw one and
785        then copy it over to the other side.
786     */
787
788     ldata = sf->pixel_data;
789     rdata = ldata + w - 1;
790     for (y = halfh + midy; y > 0; --y) {  /* 0 -> (h+1)/2 */
791         RrPixel32 c;
792
793         COLOR_RR(lefty, (&left));
794         COLOR_RR(righty, (&right));
795
796         SETUP(x, (&left), (&right), halfw + midx);
797
798         for (x = halfw + midx - 1; x > 0; --x) {  /* 0 -> (w+1)/2 */
799             c = COLOR(x);
800             *(ldata++) = *(rdata--) = c;
801
802             NEXT(x);
803         }
804         c = COLOR(x);
805         *ldata = *rdata = c;
806         ldata += halfw + 1;
807         rdata += halfw - 1 + midx + w;
808
809         NEXT(lefty);
810         NEXT(righty);
811     }
812
813     /* copy the top half into the bottom half, mirroring it, so we can only
814        copy one row at a time
815
816        it is faster, to move the writing pointer forward, and the reading
817        pointer backward
818
819        this is the current code, moving the write pointer forward and read
820        pointer backward
821        41.78      4.26     1.78      504     3.53     3.53  gradient_pyramid2
822        this is the opposite, moving the read pointer forward and the write
823        pointer backward
824        42.27      4.40     1.86      504     3.69     3.69  gradient_pyramid2
825        
826     */
827     ldata = sf->pixel_data + (halfh - 1) * w;
828     cp = ldata + (midy + 1) * w;
829     for (y = halfh; y > 0; --y) {
830         memcpy(cp, ldata, w * sizeof(RrPixel32));
831         ldata -= w;
832         cp += w;
833     }
834 }