sdl_texture.cpp: Rewrite the optimize() function.
[supertux.git] / src / video / sdl / sdl_texture.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include <config.h>
18
19 #include "supertux/gameconfig.hpp"
20 #include "supertux/globals.hpp"
21 #include "video/color.hpp"
22 #include "video/sdl/sdl_texture.hpp"
23 #include "util/log.hpp"
24 #include "math/random_generator.hpp"
25
26 #include <assert.h>
27
28 #include <SDL.h>
29
30 namespace {
31 #define BILINEAR
32
33 static Uint32 get_pixel_mapping (SDL_Surface *src, void *pixel)
34 {
35   Uint32 mapped = 0;
36
37   switch (src->format->BytesPerPixel)
38   {
39     case 1:
40       mapped = *((Uint8 *) pixel);
41       break;
42     case 2:
43       mapped = *((Uint16 *) pixel);
44       break;
45     case 3:
46     {
47       Uint8 *tmp = (Uint8 *) pixel;
48 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
49       mapped |= tmp[0] << 16;
50       mapped |= tmp[1] << 8;
51       mapped |= tmp[2] << 0;
52 #else
53       mapped |= tmp[0] << 0;
54       mapped |= tmp[1] << 8;
55       mapped |= tmp[2] << 16;
56 #endif
57       break;
58     }
59     case 4:
60       mapped = *((Uint32 *) pixel);
61       break;
62
63     default:
64       log_warning << "Unknown BytesPerPixel value: "
65         << src->format->BytesPerPixel << std::endl;
66       mapped = 0;
67   } /* switch (bpp) */
68
69   return (mapped);
70 } /* Uint32 get_pixel_mapping */
71
72 static Uint32 get_random_color (SDL_Surface *src)
73 {
74   Uint32 r;
75
76   r = (Uint32) graphicsRandom.rand ();
77   /* rand() returns 31bit random numbers. So call it twice to get full 32 bit. */
78   r <<= 1;
79   r |= (Uint32) graphicsRandom.rand ();
80
81   switch (src->format->BytesPerPixel)
82   {
83     case 1:
84       r &= 0x000000ff;
85       break;
86
87     case 2:
88       r &= 0x0000ffff;
89       break;
90
91     case 3:
92       r &= 0x0000ffff;
93       break;
94   }
95
96   return (r);
97 } /* Uint32 get_random_color */
98
99 static bool color_is_used (SDL_Surface *src, Uint32 color)
100 {
101   if(SDL_MUSTLOCK(src))
102     SDL_LockSurface(src);
103
104   for(int y = 0; y < src->h; y++) {
105     for(int x = 0; x < src->w; x++) {
106       Uint8 *pixel = (Uint8 *) src->pixels
107         + (y * src->pitch) + (x * src->format->BytesPerPixel);
108       Uint32 mapped = get_pixel_mapping (src, pixel);
109
110       if (color == mapped)
111         return (true);
112     }
113   }
114
115   return (false);
116 } /* bool color_is_used */
117
118 static Uint32 get_unused_color (SDL_Surface *src)
119 {
120   Uint32 random_color;
121
122   do
123   {
124     random_color = get_random_color (src);
125   } while (color_is_used (src, random_color));
126
127   return (random_color);
128 } /* Uint32 get_unused_color */
129
130 #ifdef NAIVE
131 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
132 {
133   if(numerator == denominator)
134   {
135     src->refcount++;
136     return src;
137   }
138   else
139   {
140     SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
141     int bpp = dst->format->BytesPerPixel;
142     if(SDL_MUSTLOCK(src))
143     {
144       SDL_LockSurface(src);
145     }
146     if(SDL_MUSTLOCK(dst))
147     {
148       SDL_LockSurface(dst);
149     }
150     for(int y = 0;y < dst->h;y++) {
151       for(int x = 0;x < dst->w;x++) {
152         Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp;
153         Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
154         switch(bpp) {
155           case 4:
156             dstpixel[3] = srcpixel[3];
157           case 3:
158             dstpixel[2] = srcpixel[2];
159           case 2:
160             dstpixel[1] = srcpixel[1];
161           case 1:
162             dstpixel[0] = srcpixel[0];
163         }
164       }
165     }
166     if(SDL_MUSTLOCK(dst))
167     {
168       SDL_UnlockSurface(dst);
169     }
170     if(SDL_MUSTLOCK(src))
171     {
172       SDL_UnlockSurface(src);
173     }
174     if(!src->format->Amask)
175     {
176       if(src->flags & SDL_SRCALPHA)
177       {
178         SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
179       }
180       if(src->flags & SDL_SRCCOLORKEY)
181       {
182         SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
183       }
184     }
185     return dst;
186   }
187 } // namespace
188 #endif
189
190 #ifdef BILINEAR
191 void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
192 {
193   int bpp = src->format->BytesPerPixel;
194   if(srcx == src->w)
195   {
196     srcx--;
197   }
198   if(srcy == src->h)
199   {
200     srcy--;
201   }
202   Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
203   Uint32 mapped = get_pixel_mapping (src, srcpixel);
204   SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
205 }
206
207 void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
208 {
209   color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
210   color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
211   color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
212   color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
213 }
214
215 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
216 {
217   if(numerator == denominator)
218   {
219     src->refcount++;
220     return src;
221   }
222   else
223   {
224     SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
225     int bpp = dst->format->BytesPerPixel;
226     if(SDL_MUSTLOCK(src))
227     {
228       SDL_LockSurface(src);
229     }
230     if(SDL_MUSTLOCK(dst))
231     {
232       SDL_LockSurface(dst);
233     }
234     for(int y = 0;y < dst->h;y++) {
235       for(int x = 0;x < dst->w;x++) {
236         int srcx = x * denominator / numerator;
237         int srcy = y * denominator / numerator;
238         Uint8 color00[4], color01[4], color10[4], color11[4];
239         getpixel(src, srcx, srcy, color00);
240         getpixel(src, srcx + 1, srcy, color01);
241         getpixel(src, srcx, srcy + 1, color10);
242         getpixel(src, srcx + 1, srcy + 1, color11);
243         Uint8 color0[4], color1[4], color[4];
244         int remx = x * denominator % numerator;
245         merge(color0, color00, color01, remx, numerator);
246         merge(color1, color10, color11, remx, numerator);
247         int remy = y * denominator % numerator;
248         merge(color, color0, color1, remy, numerator);
249         Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
250         Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
251         switch(bpp) {
252           case 1:
253             *dstpixel = mapped;
254             break;
255           case 2:
256             *(Uint16 *)dstpixel = mapped;
257             break;
258           case 3:
259 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
260             dstpixel[0] = (mapped >> 16) & 0xff;
261             dstpixel[1] = (mapped >> 8) & 0xff;
262             dstpixel[2] = (mapped >> 0) & 0xff;
263 #else
264             dstpixel[0] = (mapped >> 0) & 0xff;
265             dstpixel[1] = (mapped >> 8) & 0xff;
266             dstpixel[2] = (mapped >> 16) & 0xff;
267 #endif
268             break;
269           case 4:
270             *(Uint32 *)dstpixel = mapped;
271             break;
272         }
273       }
274     }
275     if(SDL_MUSTLOCK(dst))
276     {
277       SDL_UnlockSurface(dst);
278     }
279     if(SDL_MUSTLOCK(src))
280     {
281       SDL_UnlockSurface(src);
282     }
283     if(!src->format->Amask)
284     {
285       if(src->flags & SDL_SRCALPHA)
286       {
287         SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
288       }
289       if(src->flags & SDL_SRCCOLORKEY)
290       {
291         SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
292       }
293     }
294     return dst;
295   }
296 }
297 #endif
298
299 SDL_Surface *horz_flip(SDL_Surface *src)
300 {
301   SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
302   int bpp = dst->format->BytesPerPixel;
303   if(SDL_MUSTLOCK(src))
304   {
305     SDL_LockSurface(src);
306   }
307   if(SDL_MUSTLOCK(dst))
308   {
309     SDL_LockSurface(dst);
310   }
311   for(int y = 0;y < dst->h;y++) {
312     for(int x = 0;x < dst->w;x++) {
313       Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
314       Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
315       switch(bpp) {
316         case 4:
317           dstpixel[3] = srcpixel[3];
318         case 3:
319           dstpixel[2] = srcpixel[2];
320         case 2:
321           dstpixel[1] = srcpixel[1];
322         case 1:
323           dstpixel[0] = srcpixel[0];
324       }
325     }
326   }
327   if(SDL_MUSTLOCK(dst))
328   {
329     SDL_UnlockSurface(dst);
330   }
331   if(SDL_MUSTLOCK(src))
332   {
333     SDL_UnlockSurface(src);
334   }
335   if(!src->format->Amask)
336   {
337     if(src->flags & SDL_SRCALPHA)
338     {
339       SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
340     }
341     if(src->flags & SDL_SRCCOLORKEY)
342     {
343       SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
344     }
345   }
346   return dst;
347 }
348
349 SDL_Surface *vert_flip(SDL_Surface *src)
350 {
351   SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
352   int bpp = dst->format->BytesPerPixel;
353   if(SDL_MUSTLOCK(src))
354   {
355     SDL_LockSurface(src);
356   }
357   if(SDL_MUSTLOCK(dst))
358   {
359     SDL_LockSurface(dst);
360   }
361   for(int y = 0;y < dst->h;y++) {
362     for(int x = 0;x < dst->w;x++) {
363       Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
364       Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
365       switch(bpp) {
366         case 4:
367           dstpixel[3] = srcpixel[3];
368         case 3:
369           dstpixel[2] = srcpixel[2];
370         case 2:
371           dstpixel[1] = srcpixel[1];
372         case 1:
373           dstpixel[0] = srcpixel[0];
374       }
375     }
376   }
377   if(SDL_MUSTLOCK(dst))
378   {
379     SDL_UnlockSurface(dst);
380   }
381   if(SDL_MUSTLOCK(src))
382   {
383     SDL_UnlockSurface(src);
384   }
385   if(!src->format->Amask)
386   {
387     if(src->flags & SDL_SRCALPHA)
388     {
389       SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
390     }
391     if(src->flags & SDL_SRCCOLORKEY)
392     {
393       SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
394     }
395   }
396   return dst;
397 }
398
399 SDL_Surface *colorize(SDL_Surface *src, const Color &color)
400 {
401   // FIXME: This is really slow
402   assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
403   int red = (int) (color.red * 256);
404   int green = (int) (color.green * 256);
405   int blue = (int) (color.blue * 256);
406   SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, src->format->Amask);
407   int bpp = dst->format->BytesPerPixel;
408   if(SDL_MUSTLOCK(src))
409   {
410     SDL_LockSurface(src);
411   }
412   if(SDL_MUSTLOCK(dst))
413   {
414     SDL_LockSurface(dst);
415   }
416   for(int y = 0;y < dst->h;y++) {
417     for(int x = 0;x < dst->w;x++) {
418       Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
419       Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
420       Uint32 mapped = 0;
421       switch(bpp) {
422         case 1:
423           mapped = *srcpixel;
424           break;
425         case 2:
426           mapped = *(Uint16 *)srcpixel;
427           break;
428         case 3:
429 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
430           mapped |= srcpixel[0] << 16;
431           mapped |= srcpixel[1] << 8;
432           mapped |= srcpixel[2] << 0;
433 #else
434           mapped |= srcpixel[0] << 0;
435           mapped |= srcpixel[1] << 8;
436           mapped |= srcpixel[2] << 16;
437 #endif
438           break;
439         case 4:
440           mapped = *(Uint32 *)srcpixel;
441           break;
442       }
443       if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
444       {
445         Uint8 r, g, b, a;
446         SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
447         mapped = SDL_MapRGBA(dst->format, (r * red) >> 8, (g * green) >> 8, (b * blue) >> 8, a);
448       }
449       switch(bpp) {
450         case 1:
451           *dstpixel = mapped;
452           break;
453         case 2:
454           *(Uint16 *)dstpixel = mapped;
455           break;
456         case 3:
457 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
458           dstpixel[0] = (mapped >> 16) & 0xff;
459           dstpixel[1] = (mapped >> 8) & 0xff;
460           dstpixel[2] = (mapped >> 0) & 0xff;
461 #else
462           dstpixel[0] = (mapped >> 0) & 0xff;
463           dstpixel[1] = (mapped >> 8) & 0xff;
464           dstpixel[2] = (mapped >> 16) & 0xff;
465 #endif
466           break;
467         case 4:
468           *(Uint32 *)dstpixel = mapped;
469           break;
470       }
471     }
472   }
473   if(SDL_MUSTLOCK(dst))
474   {
475     SDL_UnlockSurface(dst);
476   }
477   if(SDL_MUSTLOCK(src))
478   {
479     SDL_UnlockSurface(src);
480   }
481   if(!src->format->Amask)
482   {
483     if(src->flags & SDL_SRCALPHA)
484     {
485       SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
486     }
487     if(src->flags & SDL_SRCCOLORKEY)
488     {
489       SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
490     }
491   }
492   return dst;
493 }
494
495 /** Optimizes a SDL_Surface surface and returns it in the "display format". If
496  *  the surface does not have an alpha channel, simply calls
497  *  "SDL_DisplayFormat". If the surface has an alpha channel, examines all the
498  *  pixels. If in fact semi-transparent pixels are found, calls
499  *  "SDL_DisplayFormatAlpha". If only fully transparent and fully opaque pixels
500  *  are found, converts the surface to a 1-bit alpha surface with
501  *  "SDL_SetColorKey". */
502 SDL_Surface *optimize(SDL_Surface *src)
503 {
504   bool have_transparent = false;
505   bool have_semi_trans = false;
506   bool have_opaque = false;
507
508   if(!src->format->Amask)
509     return SDL_DisplayFormat(src);
510
511   if(SDL_MUSTLOCK(src))
512     SDL_LockSurface(src);
513
514   /* Iterate over all the pixels and record which ones we found. */
515   for(int y = 0; y < src->h; y++) {
516     for(int x = 0; x < src->w; x++) {
517       Uint8 *pixel = (Uint8 *) src->pixels
518         + (y * src->pitch) + (x * src->format->BytesPerPixel);
519       Uint32 mapped = get_pixel_mapping (src, pixel);
520       Uint8 red, green, blue, alpha;
521       SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
522
523       if (alpha < 16)
524         have_transparent = true;
525       else if (alpha > 240)
526         have_opaque = true;
527       else
528         have_semi_trans = true;
529     } /* for (x) */
530   } /* for (y) */
531
532   if(SDL_MUSTLOCK(src))
533     SDL_UnlockSurface(src);
534
535   if (have_semi_trans)
536     return SDL_DisplayFormatAlpha(src);
537
538   if (!have_transparent /* && !have_semi_trans */)
539     return SDL_DisplayFormat(src);
540
541   /* The surface is totally transparent. We shouldn't return a surface at all,
542    * but since the calling code can't cope with that, use the alpha channel in
543    * this case. */
544   if (!have_opaque /* && !have_semi_trans */)
545     return SDL_DisplayFormatAlpha(src);
546
547   /* If we get here, the surface has fully transparent pixels and fully opaque
548    * pixels, but no semi-transparent pixels. We can therefore use a one bit
549    * transparency, which is pretty fast to draw. This code path is a bit bulky
550    * and rarely used (~25 surfaces when starting the game and entering a
551    * level), so it could be removed for readabilities sake. -octo */
552
553   /* Create a new surface without alpha channel */
554   SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA),
555       src->w, src->h, src->format->BitsPerPixel,
556       src->format->Rmask,  src->format->Gmask, src->format->Bmask, /* Amask = */ 0);
557   /* Get a color that's not in the source surface. It is used to mark
558    * transparent pixels. There's a possible race condition: Maybe we should
559    * lock the surface before calling this function and add a "bool have_lock"
560    * argument to "get_unused_color"? -octo */
561   Uint32 color_key = get_unused_color (src);
562
563   if(SDL_MUSTLOCK(src))
564     SDL_LockSurface(src);
565   if(SDL_MUSTLOCK(dst))
566     SDL_LockSurface(dst);
567
568   /* Copy all the pixels to the new surface */
569   for(int y = 0; y < src->h; y++) {
570     for(int x = 0; x < src->w; x++) {
571       Uint8 *src_pixel = (Uint8 *) src->pixels
572         + (y * src->pitch) + (x * src->format->BytesPerPixel);
573       Uint8 *dst_pixel = (Uint8 *) dst->pixels
574         + (y * dst->pitch) + (x * dst->format->BytesPerPixel);
575       Uint32 mapped = get_pixel_mapping (src, src_pixel);
576       Uint8 red, green, blue, alpha;
577       SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
578
579       /* "alpha" should either be smaller than 16 or greater than 240. We
580        * unlocked the surface in between though, so use 128 to play it save,
581        * i.e. don't leave any unspecified code paths. */
582       if (alpha < 128)
583         mapped = color_key;
584
585       switch (dst->format->BytesPerPixel)
586       {
587         case 1:
588           *dst_pixel = (Uint8) mapped;
589           break;
590
591         case 2:
592           *((Uint16 *) dst_pixel) = (Uint16) mapped;
593           break;
594
595         case 3:
596 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
597             dst_pixel[0] = (mapped >> 16) & 0xff;
598             dst_pixel[1] = (mapped >> 8) & 0xff;
599             dst_pixel[2] = (mapped >> 0) & 0xff;
600 #else
601             dst_pixel[0] = (mapped >> 0) & 0xff;
602             dst_pixel[1] = (mapped >> 8) & 0xff;
603             dst_pixel[2] = (mapped >> 16) & 0xff;
604 #endif
605             break;
606
607         case 4:
608             *((Uint32 *) dst_pixel) = mapped;
609       } /* switch (dst->format->BytesPerPixel) */
610     } /* for (x) */
611   } /* for (y) */
612
613   if(SDL_MUSTLOCK(src))
614     SDL_UnlockSurface(src);
615   if(SDL_MUSTLOCK(dst))
616     SDL_UnlockSurface(dst);
617
618   /* Tell SDL that the "color_key" color is supposed to be transparent. */
619   SDL_SetColorKey (dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, color_key);
620   SDL_Surface *convert = SDL_DisplayFormat(dst);
621   SDL_FreeSurface(dst);
622   return convert;
623 } /* SDL_Surface *optimize */
624
625 } /* namespace */
626
627 SDLTexture::SDLTexture(SDL_Surface* image) :
628   texture()
629 {
630   texture = optimize(image);
631   //width = texture->w;
632   //height = texture->h;
633   int numerator   = 1;
634   int denominator = 1;
635   //FIXME: float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
636   //FIXME: float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
637   /* FIXME: 
638      if(xfactor < yfactor)
639      {
640      numerator = config->screenwidth;
641      denominator = SCREEN_WIDTH;
642      }
643      else
644      {
645      numerator = config->screenheight;
646      denominator = SCREEN_HEIGHT;
647      }
648   */
649   cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
650 }
651
652 SDLTexture::~SDLTexture()
653 {
654   SDL_FreeSurface(texture);
655 }
656
657 SDL_Surface*
658 SDLTexture::get_transform(const Color &color, DrawingEffect effect)
659 {
660   if(cache[NO_EFFECT][color] == 0) {
661     assert(cache[NO_EFFECT][Color::WHITE]);
662     cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
663   }
664   if(cache[effect][color] == 0) {
665     assert(cache[NO_EFFECT][color]);
666     switch(effect) {
667       case NO_EFFECT:
668         break;
669       case HORIZONTAL_FLIP:
670         cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
671         break;
672       case VERTICAL_FLIP:
673         cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
674         break;
675       default:
676         return 0;
677     }
678   }
679   return cache[effect][color];
680 }
681
682 /* vim: set sw=2 sts=2 et : */
683 /* EOF */