56eeb1179203d209f1c1eeee4609f135843983d6
[supertux.git] / src / video / sdl_texture.cpp
1 //  $Id: sdl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <config.h>
21
22 #include "sdl_texture.hpp"
23 #include "color.hpp"
24 #include "gameconfig.hpp"
25 #include "main.hpp"
26
27 #include <assert.h>
28
29 #include <SDL.h>
30
31 namespace
32 {
33 #define BILINEAR
34
35 #ifdef NAIVE
36   SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
37   {
38     if(numerator == denominator)
39     {
40       src->refcount++;
41       return src;
42     }
43     else
44     {
45       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);
46       int bpp = dst->format->BytesPerPixel;
47       for(int y = 0;y < dst->h;y++) {
48         for(int x = 0;x < dst->w;x++) {
49           Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp;
50           Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
51           switch(bpp) {
52             case 4:
53               dstpixel[3] = srcpixel[3];
54             case 3:
55               dstpixel[2] = srcpixel[2];
56             case 2:
57               dstpixel[1] = srcpixel[1];
58             case 1:
59               dstpixel[0] = srcpixel[0];
60           }
61         }
62       }
63       if(!src->format->Amask)
64       {
65         if(src->flags & SDL_SRCALPHA)
66         {
67           SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
68         }
69         if(src->flags & SDL_SRCCOLORKEY)
70         {
71           SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
72         }
73       }
74       return dst;
75     }
76   }
77 #endif
78
79 #ifdef BILINEAR
80   void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
81   {
82     int bpp = src->format->BytesPerPixel;
83     if(srcx == src->w)
84     {
85       srcx--;
86     }
87     if(srcy == src->h)
88     {
89       srcy--;
90     }
91     Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
92     Uint32 mapped = 0;
93     switch(bpp) {
94       case 1:
95         mapped = *srcpixel;
96         break;
97       case 2:
98         mapped = *(Uint16 *)srcpixel;
99         break;
100       case 3:
101 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
102         mapped |= srcpixel[0] << 16;
103         mapped |= srcpixel[1] << 8;
104         mapped |= srcpixel[2] << 0;
105 #else
106         mapped |= srcpixel[0] << 0;
107         mapped |= srcpixel[1] << 8;
108         mapped |= srcpixel[2] << 16;
109 #endif
110         break;
111       case 4:
112         mapped = *(Uint32 *)srcpixel;
113         break;
114     }
115     SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
116   }
117
118   void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
119   {
120     color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
121     color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
122     color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
123     color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
124   }
125
126   SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
127   {
128     if(numerator == denominator)
129     {
130       src->refcount++;
131       return src;
132     }
133     else
134     {
135       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);
136       int bpp = dst->format->BytesPerPixel;
137       for(int y = 0;y < dst->h;y++) {
138         for(int x = 0;x < dst->w;x++) {
139           int srcx = x * denominator / numerator;
140           int srcy = y * denominator / numerator;
141           Uint8 color00[4], color01[4], color10[4], color11[4];
142           getpixel(src, srcx, srcy, color00);
143           getpixel(src, srcx + 1, srcy, color01);
144           getpixel(src, srcx, srcy + 1, color10);
145           getpixel(src, srcx + 1, srcy + 1, color11);
146           Uint8 color0[4], color1[4], color[4];
147           int remx = x * denominator % numerator;
148           merge(color0, color00, color01, remx, numerator);
149           merge(color1, color10, color11, remx, numerator);
150           int remy = y * denominator % numerator;
151           merge(color, color0, color1, remy, numerator);
152           Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
153           Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
154           switch(bpp) {
155             case 1:
156               *dstpixel = mapped;
157               break;
158             case 2:
159               *(Uint16 *)dstpixel = mapped;
160               break;
161             case 3:
162 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
163               dstpixel[0] = (mapped >> 16) & 0xff;
164               dstpixel[1] = (mapped >> 8) & 0xff;
165               dstpixel[2] = (mapped >> 0) & 0xff;
166 #else
167               dstpixel[0] = (mapped >> 0) & 0xff;
168               dstpixel[1] = (mapped >> 8) & 0xff;
169               dstpixel[2] = (mapped >> 16) & 0xff;
170 #endif
171               break;
172             case 4:
173               *(Uint32 *)dstpixel = mapped;
174               break;
175           }
176         }
177       }
178       if(!src->format->Amask)
179       {
180         if(src->flags & SDL_SRCALPHA)
181         {
182           SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
183         }
184         if(src->flags & SDL_SRCCOLORKEY)
185         {
186           SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
187         }
188       }
189       return dst;
190     }
191   }
192 #endif
193
194   // FIXME: Horizontal and vertical line problem
195 #ifdef BRESENHAM
196   void accumulate(SDL_Surface *src, int srcx, int srcy, int color[4], int weight)
197   {
198     if(srcx < 0 || srcy < 0 || weight == 0) {
199       return;
200     }
201     int bpp = src->format->BytesPerPixel;
202     Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
203     Uint32 mapped = 0;
204     switch(bpp) {
205       case 1:
206         mapped = *srcpixel;
207         break;
208       case 2:
209         mapped = *(Uint16 *)srcpixel;
210         break;
211       case 3:
212 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
213         mapped |= srcpixel[0] << 16;
214         mapped |= srcpixel[1] << 8;
215         mapped |= srcpixel[2] << 0;
216 #else
217         mapped |= srcpixel[0] << 0;
218         mapped |= srcpixel[1] << 8;
219         mapped |= srcpixel[2] << 16;
220 #endif
221         break;
222       case 4:
223         mapped = *(Uint32 *)srcpixel;
224         break;
225     }
226     Uint8 red, green, blue, alpha;
227     SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
228     color[0] += red * weight;
229     color[1] += green * weight;
230     color[2] += blue * weight;
231     color[3] += alpha * weight;
232   }
233
234   void accumulate_line(SDL_Surface *src, int srcy, int line[][4], int linesize, int weight, int numerator, int denominator)
235   {
236     int intpart = denominator / numerator;
237     int fractpart = denominator % numerator;
238     for(int x = 0, xe = 0, srcx = 0;x < linesize;x++) {
239       accumulate(src, srcx, srcy, line[x], (numerator - xe) * weight);
240       srcx++;
241       for(int i = 0;i < intpart - 1;i++) {
242         accumulate(src, srcx, srcy, line[x], numerator * weight);
243         srcx++;
244       }
245       xe += fractpart;
246       if(xe >= numerator) {
247         xe -= numerator;
248         srcx++;
249       }
250       accumulate(src, srcx, srcy, line[x], xe * weight);
251     }
252   }
253
254   SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
255   {
256     if(numerator == denominator)
257     {
258       src->refcount++;
259       return src;
260     }
261     else
262     {
263       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);
264       int bpp = dst->format->BytesPerPixel;
265       int intpart = denominator / numerator;
266       int fractpart = denominator % numerator;
267       for(int y = 0, ye = 0, srcy = 0;y < dst->h;y++) {
268         int line[dst->w][4];
269         memset(line, 0, sizeof(int) * dst->w * 4);
270         accumulate_line(src, srcy, line, dst->w, numerator - ye, numerator, denominator);
271         srcy++;
272         for(int i = 0;i < intpart - 1;i++) {
273           accumulate_line(src, srcy, line, dst->w, numerator, numerator, denominator);
274           srcy++;
275         }
276         ye += fractpart;
277         if(ye >= numerator) {
278           ye -= numerator;
279           srcy++;
280         }
281         accumulate_line(src, srcy, line, dst->w, ye, numerator, denominator);
282         for(int x = 0;x < dst->w;x++) {
283           Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
284           Uint32 mapped = SDL_MapRGBA(dst->format, line[x][0] / (denominator * denominator), line[x][1] / (denominator * denominator), line[x][2] / (denominator * denominator), line[x][3] / (denominator * denominator));
285           switch(bpp) {
286             case 1:
287               *dstpixel = mapped;
288               break;
289             case 2:
290               *(Uint16 *)dstpixel = mapped;
291               break;
292             case 3:
293 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
294               dstpixel[0] = (mapped >> 16) & 0xff;
295               dstpixel[1] = (mapped >> 8) & 0xff;
296               dstpixel[2] = (mapped >> 0) & 0xff;
297 #else
298               dstpixel[0] = (mapped >> 0) & 0xff;
299               dstpixel[1] = (mapped >> 8) & 0xff;
300               dstpixel[2] = (mapped >> 16) & 0xff;
301 #endif
302               break;
303             case 4:
304               *(Uint32 *)dstpixel = mapped;
305               break;
306           }
307         }
308       }
309       if(!src->format->Amask)
310       {
311         if(src->flags & SDL_SRCALPHA)
312         {
313           SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
314         }
315         if(src->flags & SDL_SRCCOLORKEY)
316         {
317           SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
318         }
319       }
320       return dst;
321     }
322   }
323 #endif
324
325   SDL_Surface *horz_flip(SDL_Surface *src)
326   {
327     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);
328     int bpp = dst->format->BytesPerPixel;
329     for(int y = 0;y < dst->h;y++) {
330       for(int x = 0;x < dst->w;x++) {
331         Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
332         Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
333         switch(bpp) {
334           case 4:
335             dstpixel[3] = srcpixel[3];
336           case 3:
337             dstpixel[2] = srcpixel[2];
338           case 2:
339             dstpixel[1] = srcpixel[1];
340           case 1:
341             dstpixel[0] = srcpixel[0];
342         }
343       }
344     }
345     if(!src->format->Amask)
346     {
347       if(src->flags & SDL_SRCALPHA)
348       {
349         SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
350       }
351       if(src->flags & SDL_SRCCOLORKEY)
352       {
353         SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
354       }
355     }
356     return dst;
357   }
358
359   SDL_Surface *vert_flip(SDL_Surface *src)
360   {
361     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);
362     int bpp = dst->format->BytesPerPixel;
363     for(int y = 0;y < dst->h;y++) {
364       for(int x = 0;x < dst->w;x++) {
365         Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
366         Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
367         switch(bpp) {
368           case 4:
369             dstpixel[3] = srcpixel[3];
370           case 3:
371             dstpixel[2] = srcpixel[2];
372           case 2:
373             dstpixel[1] = srcpixel[1];
374           case 1:
375             dstpixel[0] = srcpixel[0];
376         }
377       }
378     }
379     if(!src->format->Amask)
380     {
381       if(src->flags & SDL_SRCALPHA)
382       {
383         SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
384       }
385       if(src->flags & SDL_SRCCOLORKEY)
386       {
387         SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
388       }
389     }
390     return dst;
391   }
392
393   SDL_Surface *colorize(SDL_Surface *src, const Color &color)
394   {
395     // FIXME: This is really slow
396     assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
397     int red = (int) (color.red * 256);
398     int green = (int) (color.green * 256);
399     int blue = (int) (color.blue * 256);
400     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);
401     int bpp = dst->format->BytesPerPixel;
402     for(int y = 0;y < dst->h;y++) {
403       for(int x = 0;x < dst->w;x++) {
404         Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
405         Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
406         Uint32 mapped = 0;
407         switch(bpp) {
408           case 1:
409             mapped = *srcpixel;
410             break;
411           case 2:
412             mapped = *(Uint16 *)srcpixel;
413             break;
414           case 3:
415 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
416             mapped |= srcpixel[0] << 16;
417             mapped |= srcpixel[1] << 8;
418             mapped |= srcpixel[2] << 0;
419 #else
420             mapped |= srcpixel[0] << 0;
421             mapped |= srcpixel[1] << 8;
422             mapped |= srcpixel[2] << 16;
423 #endif
424             break;
425           case 4:
426             mapped = *(Uint32 *)srcpixel;
427             break;
428         }
429         if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
430         {
431           Uint8 r, g, b, a;
432           SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
433           mapped = SDL_MapRGBA(dst->format, (r * red) >> 8, (g * green) >> 8, (b * blue) >> 8, a);
434         }
435         switch(bpp) {
436           case 1:
437             *dstpixel = mapped;
438             break;
439           case 2:
440             *(Uint16 *)dstpixel = mapped;
441             break;
442           case 3:
443 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
444             dstpixel[0] = (mapped >> 16) & 0xff;
445             dstpixel[1] = (mapped >> 8) & 0xff;
446             dstpixel[2] = (mapped >> 0) & 0xff;
447 #else
448             dstpixel[0] = (mapped >> 0) & 0xff;
449             dstpixel[1] = (mapped >> 8) & 0xff;
450             dstpixel[2] = (mapped >> 16) & 0xff;
451 #endif
452             break;
453           case 4:
454             *(Uint32 *)dstpixel = mapped;
455             break;
456         }
457       }
458     }
459     if(!src->format->Amask)
460     {
461       if(src->flags & SDL_SRCALPHA)
462       {
463         SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
464       }
465       if(src->flags & SDL_SRCCOLORKEY)
466       {
467         SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
468       }
469     }
470     return dst;
471   }
472
473   SDL_Surface *optimize(SDL_Surface *src)
474   {
475     if(!src->format->Amask)
476     {
477       return SDL_DisplayFormat(src);
478     }
479     else
480     {
481       int opaque = 0;
482       int transparent = 0;
483       int semitransparent = 0;
484       std::set<int> colors;
485
486       int bpp = src->format->BytesPerPixel;
487       for(int y = 0;y < src->h;y++) {
488         for(int x = 0;x < src->w;x++) {
489           Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
490           Uint32 mapped = 0;
491           switch(bpp) {
492             case 1:
493               mapped = *pixel;
494               break;
495             case 2:
496               mapped = *(Uint16 *)pixel;
497               break;
498             case 3:
499 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
500               mapped |= pixel[0] << 16;
501               mapped |= pixel[1] << 8;
502               mapped |= pixel[2] << 0;
503 #else
504               mapped |= pixel[0] << 0;
505               mapped |= pixel[1] << 8;
506               mapped |= pixel[2] << 16;
507 #endif
508               break;
509             case 4:
510               mapped = *(Uint32 *)pixel;
511               break;
512           }
513           Uint8 red, green, blue, alpha;
514           SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
515           if(alpha < 32)
516           {
517             transparent++;
518           }
519           else if (alpha > 224)
520           {
521             opaque++;
522           }
523           else
524           {
525             semitransparent++;
526           }
527           colors.insert(((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4));
528         }
529       }
530       if(semitransparent > ((opaque + transparent + semitransparent) / 8))
531       {
532         // FIXME: try to approximate textures that have a fairly constant alpha
533         return SDL_DisplayFormatAlpha(src);
534       }
535       // FIXME: pick a color key by examining "colors"
536       SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA), src->w, src->h, src->format->BitsPerPixel, src->format->Rmask,  src->format->Gmask, src->format->Bmask, 0);
537       bpp = dst->format->BytesPerPixel;
538       Uint32 key = SDL_MapRGB(dst->format, 0xff, 0x00, 0xff);
539       for(int y = 0;y < dst->h;y++) {
540         for(int x = 0;x < dst->w;x++) {
541           Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
542           Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
543           Uint32 mapped = 0;
544           switch(bpp) {
545             case 1:
546               mapped = *srcpixel;
547               break;
548             case 2:
549               mapped = *(Uint16 *)srcpixel;
550               break;
551             case 3:
552 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
553               mapped |= srcpixel[0] << 16;
554               mapped |= srcpixel[1] << 8;
555               mapped |= srcpixel[2] << 0;
556 #else
557               mapped |= srcpixel[0] << 0;
558               mapped |= srcpixel[1] << 8;
559               mapped |= srcpixel[2] << 16;
560 #endif
561               break;
562             case 4:
563               mapped = *(Uint32 *)srcpixel;
564               break;
565           }
566           Uint8 red, green, blue, alpha;
567           SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
568           if(alpha < 64)
569           {
570             mapped = key;
571           }
572           else
573           {
574             mapped = SDL_MapRGB(dst->format, red, green, blue);
575           }
576           switch(bpp) {
577             case 1:
578               *dstpixel = mapped;
579               break;
580             case 2:
581               *(Uint16 *)dstpixel = mapped;
582               break;
583             case 3:
584 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
585               dstpixel[0] = (mapped >> 16) & 0xff;
586               dstpixel[1] = (mapped >> 8) & 0xff;
587               dstpixel[2] = (mapped >> 0) & 0xff;
588 #else
589               dstpixel[0] = (mapped >> 0) & 0xff;
590               dstpixel[1] = (mapped >> 8) & 0xff;
591               dstpixel[2] = (mapped >> 16) & 0xff;
592 #endif
593               break;
594             case 4:
595               *(Uint32 *)dstpixel = mapped;
596               break;
597           }
598         }
599       }
600       SDL_SetColorKey(dst, SDL_SRCCOLORKEY, key);
601       SDL_Surface *convert = SDL_DisplayFormat(dst);
602       SDL_FreeSurface(dst);
603       return convert;
604     }
605   }
606 }
607
608 namespace SDL
609 {
610   Texture::Texture(SDL_Surface* image)
611   {
612     texture = optimize(image);
613     //width = texture->w;
614     //height = texture->h;
615     int numerator, denominator;
616     float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
617     float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
618     if(xfactor < yfactor)
619     {
620       numerator = config->screenwidth;
621       denominator = SCREEN_WIDTH;
622     }
623     else
624     {
625       numerator = config->screenheight;
626       denominator = SCREEN_HEIGHT;
627     }
628     cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
629   }
630
631   Texture::~Texture()
632   {
633     SDL_FreeSurface(texture);
634   }
635
636   SDL_Surface *Texture::get_transform(const Color &color, DrawingEffect effect)
637   {
638     if(cache[NO_EFFECT][color] == 0) {
639       assert(cache[NO_EFFECT][Color::WHITE]);
640       cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
641     }
642     if(cache[effect][color] == 0) {
643       assert(cache[NO_EFFECT][color]);
644       switch(effect) {
645         case NO_EFFECT:
646           break;
647         case HORIZONTAL_FLIP:
648           cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
649           break;
650         case VERTICAL_FLIP:
651           cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
652           break;
653         default:
654           return 0;
655       }
656     }
657     return cache[effect][color];
658   }
659 }