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