Buttons seem to work!
[dana/openbox.git] / obrender / 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 & 1);
511         y3sz = MAX(h/2, 1);
512     }
513     else {
514         y1sz = h/2 - (1 - (h & 1));
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     if (y2sz) {
538         for (y2 = y2sz-1; y2 > 0; --y2) {
539             *data = COLOR(y2);
540             data += w;
541             NEXT(y2);
542         }
543         *data = COLOR(y2);
544         data += w;
545     }
546     for (y3 = y3sz-1; y3 > 0; --y3) {
547         *data = COLOR(y3);
548         data += w;
549         NEXT(y3);
550     }
551     *data = COLOR(y3);
552
553     /* copy the first pixels into the whole rows */
554     data = sf->pixel_data;
555     for (y1 = h; y1 > 0; --y1) {
556         repeat_pixel(data, w);
557         data += w;
558     }
559 }
560
561 static void gradient_horizontal(RrSurface *sf, gint w, gint h)
562 {
563     register gint x, y, cpbytes;
564     RrPixel32 *data = sf->pixel_data, *datav;
565     gchar *datac;
566
567     VARS(x);
568     SETUP(x, sf->primary, sf->secondary, w);
569
570     /* set the color values for the first row */
571     datav = data;
572     for (x = w - 1; x > 0; --x) {  /* 0 -> w - 1 */
573         *datav = COLOR(x);
574         ++datav;
575         NEXT(x);
576     }
577     *datav = COLOR(x);
578     ++datav;
579
580     /* copy the first row to the rest in O(logn) copies */
581     datac = (gchar*)datav;
582     cpbytes = 1 * w * sizeof(RrPixel32);
583     for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) {
584         memcpy(datac, data, cpbytes);
585         y -= cpbytes;
586         datac += cpbytes;
587         cpbytes <<= 1;
588         if (cpbytes > y)
589             cpbytes = y;
590     }
591 }
592
593 static void gradient_mirrorhorizontal(RrSurface *sf, gint w, gint h)
594 {
595     register gint x, y, half1, half2, cpbytes;
596     RrPixel32 *data = sf->pixel_data, *datav;
597     gchar *datac;
598
599     VARS(x);
600
601     half1 = (w + 1) / 2;
602     half2 = w / 2;
603
604     /* set the color values for the first row */
605
606     SETUP(x, sf->primary, sf->secondary, half1);
607     datav = data;
608     for (x = half1 - 1; x > 0; --x) {  /* 0 -> half1 - 1 */
609         *datav = COLOR(x);
610         ++datav;
611         NEXT(x);
612     }
613     *datav = COLOR(x);
614     ++datav;
615
616     if (half2 > 0) {
617         SETUP(x, sf->secondary, sf->primary, half2);
618         for (x = half2 - 1; x > 0; --x) {  /* 0 -> half2 - 1 */
619             *datav = COLOR(x);
620             ++datav;
621             NEXT(x);
622         }
623         *datav = COLOR(x);
624         ++datav;
625     }
626
627     /* copy the first row to the rest in O(logn) copies */
628     datac = (gchar*)datav;
629     cpbytes = 1 * w * sizeof(RrPixel32);
630     for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) {
631         memcpy(datac, data, cpbytes);
632         y -= cpbytes;
633         datac += cpbytes;
634         cpbytes <<= 1;
635         if (cpbytes > y)
636             cpbytes = y;
637     }
638 }
639
640 static void gradient_vertical(RrSurface *sf, gint w, gint h)
641 {
642     register gint y;
643     RrPixel32 *data;
644
645     VARS(y);
646     SETUP(y, sf->primary, sf->secondary, h);
647
648     /* find the color for the first pixel of each row first */
649     data = sf->pixel_data;
650
651     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
652         *data = COLOR(y);
653         data += w;
654         NEXT(y);
655     }
656     *data = COLOR(y);
657
658     /* copy the first pixels into the whole rows */
659     data = sf->pixel_data;
660     for (y = h; y > 0; --y) {
661         repeat_pixel(data, w);
662         data += w;
663     }
664 }
665
666 static void gradient_diagonal(RrSurface *sf, gint w, gint h)
667 {
668     register gint x, y;
669     RrPixel32 *data = sf->pixel_data;
670     RrColor left, right;
671     RrColor extracorner;
672
673     VARS(lefty);
674     VARS(righty);
675     VARS(x);
676
677     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
678     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
679     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
680
681     SETUP(lefty, sf->primary, (&extracorner), h);
682     SETUP(righty, (&extracorner), sf->secondary, h);
683
684     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
685         COLOR_RR(lefty, (&left));
686         COLOR_RR(righty, (&right));
687
688         SETUP(x, (&left), (&right), w);
689
690         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
691             *(data++) = COLOR(x);
692
693             NEXT(x);
694         }
695         *(data++) = COLOR(x);
696
697         NEXT(lefty);
698         NEXT(righty);
699     }
700     COLOR_RR(lefty, (&left));
701     COLOR_RR(righty, (&right));
702
703     SETUP(x, (&left), (&right), w);
704
705     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
706         *(data++) = COLOR(x);
707
708         NEXT(x);
709     }
710     *data = COLOR(x);
711 }
712
713 static void gradient_crossdiagonal(RrSurface *sf, gint w, gint h)
714 {
715     register gint x, y;
716     RrPixel32 *data = sf->pixel_data;
717     RrColor left, right;
718     RrColor extracorner;
719
720     VARS(lefty);
721     VARS(righty);
722     VARS(x);
723
724     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
725     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
726     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
727
728     SETUP(lefty, (&extracorner), sf->secondary, h);
729     SETUP(righty, sf->primary, (&extracorner), h);
730
731     for (y = h - 1; y > 0; --y) {  /* 0 -> h-1 */
732         COLOR_RR(lefty, (&left));
733         COLOR_RR(righty, (&right));
734
735         SETUP(x, (&left), (&right), w);
736
737         for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
738             *(data++) = COLOR(x);
739
740             NEXT(x);
741         }
742         *(data++) = COLOR(x);
743
744         NEXT(lefty);
745         NEXT(righty);
746     }
747     COLOR_RR(lefty, (&left));
748     COLOR_RR(righty, (&right));
749
750     SETUP(x, (&left), (&right), w);
751
752     for (x = w - 1; x > 0; --x) {  /* 0 -> w-1 */
753         *(data++) = COLOR(x);
754
755         NEXT(x);
756     }
757     *data = COLOR(x);
758 }
759
760 static void gradient_pyramid(RrSurface *sf, gint w, gint h)
761 {
762     RrPixel32 *ldata, *rdata;
763     RrPixel32 *cp;
764     RrColor left, right;
765     RrColor extracorner;
766     register gint x, y, halfw, halfh, midx, midy;
767
768     VARS(lefty);
769     VARS(righty);
770     VARS(x);
771
772     extracorner.r = (sf->primary->r + sf->secondary->r) / 2;
773     extracorner.g = (sf->primary->g + sf->secondary->g) / 2;
774     extracorner.b = (sf->primary->b + sf->secondary->b) / 2;
775
776     halfw = w >> 1;
777     halfh = h >> 1;
778     midx = w - halfw - halfw; /* 0 or 1, depending if w is even or odd */
779     midy = h - halfh - halfh;   /* 0 or 1, depending if h is even or odd */
780
781     SETUP(lefty, sf->primary, (&extracorner), halfh + midy);
782     SETUP(righty, (&extracorner), sf->secondary, halfh + midy);
783
784     /* draw the top half
785
786        it is faster to draw both top quarters together than to draw one and
787        then copy it over to the other side.
788     */
789
790     ldata = sf->pixel_data;
791     rdata = ldata + w - 1;
792     for (y = halfh + midy; y > 0; --y) {  /* 0 -> (h+1)/2 */
793         RrPixel32 c;
794
795         COLOR_RR(lefty, (&left));
796         COLOR_RR(righty, (&right));
797
798         SETUP(x, (&left), (&right), halfw + midx);
799
800         for (x = halfw + midx - 1; x > 0; --x) {  /* 0 -> (w+1)/2 */
801             c = COLOR(x);
802             *(ldata++) = *(rdata--) = c;
803
804             NEXT(x);
805         }
806         c = COLOR(x);
807         *ldata = *rdata = c;
808         ldata += halfw + 1;
809         rdata += halfw - 1 + midx + w;
810
811         NEXT(lefty);
812         NEXT(righty);
813     }
814
815     /* copy the top half into the bottom half, mirroring it, so we can only
816        copy one row at a time
817
818        it is faster, to move the writing pointer forward, and the reading
819        pointer backward
820
821        this is the current code, moving the write pointer forward and read
822        pointer backward
823        41.78      4.26     1.78      504     3.53     3.53  gradient_pyramid2
824        this is the opposite, moving the read pointer forward and the write
825        pointer backward
826        42.27      4.40     1.86      504     3.69     3.69  gradient_pyramid2
827        
828     */
829     ldata = sf->pixel_data + (halfh - 1) * w;
830     cp = ldata + (midy + 1) * w;
831     for (y = halfh; y > 0; --y) {
832         memcpy(cp, ldata, w * sizeof(RrPixel32));
833         ldata -= w;
834         cp += w;
835     }
836 }