1 // $Id: sdl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $
4 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
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.
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.
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.
22 #include "sdl_texture.hpp"
24 #include "gameconfig.hpp"
36 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
38 if(numerator == denominator)
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;
53 dstpixel[3] = srcpixel[3];
55 dstpixel[2] = srcpixel[2];
57 dstpixel[1] = srcpixel[1];
59 dstpixel[0] = srcpixel[0];
63 if(!src->format->Amask)
65 if(src->flags & SDL_SRCALPHA)
67 SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
69 if(src->flags & SDL_SRCCOLORKEY)
71 SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
80 void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
82 int bpp = src->format->BytesPerPixel;
91 Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
98 mapped = *(Uint16 *)srcpixel;
101 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
102 mapped |= srcpixel[0] << 16;
103 mapped |= srcpixel[1] << 8;
104 mapped |= srcpixel[2] << 0;
106 mapped |= srcpixel[0] << 0;
107 mapped |= srcpixel[1] << 8;
108 mapped |= srcpixel[2] << 16;
112 mapped = *(Uint32 *)srcpixel;
115 SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
118 void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
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;
126 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
128 if(numerator == denominator)
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]);
159 *(Uint16 *)dstpixel = mapped;
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;
167 dstpixel[0] = (mapped >> 0) & 0xff;
168 dstpixel[1] = (mapped >> 8) & 0xff;
169 dstpixel[2] = (mapped >> 16) & 0xff;
173 *(Uint32 *)dstpixel = mapped;
178 if(!src->format->Amask)
180 if(src->flags & SDL_SRCALPHA)
182 SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
184 if(src->flags & SDL_SRCCOLORKEY)
186 SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
194 // FIXME: Horizontal and vertical line problem
196 void accumulate(SDL_Surface *src, int srcx, int srcy, int color[4], int weight)
198 if(srcx < 0 || srcy < 0 || weight == 0) {
201 int bpp = src->format->BytesPerPixel;
202 Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
209 mapped = *(Uint16 *)srcpixel;
212 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
213 mapped |= srcpixel[0] << 16;
214 mapped |= srcpixel[1] << 8;
215 mapped |= srcpixel[2] << 0;
217 mapped |= srcpixel[0] << 0;
218 mapped |= srcpixel[1] << 8;
219 mapped |= srcpixel[2] << 16;
223 mapped = *(Uint32 *)srcpixel;
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;
234 void accumulate_line(SDL_Surface *src, int srcy, int line[][4], int linesize, int weight, int numerator, int denominator)
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);
241 for(int i = 0;i < intpart - 1;i++) {
242 accumulate(src, srcx, srcy, line[x], numerator * weight);
246 if(xe >= numerator) {
250 accumulate(src, srcx, srcy, line[x], xe * weight);
254 SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
256 if(numerator == denominator)
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++) {
269 memset(line, 0, sizeof(int) * dst->w * 4);
270 accumulate_line(src, srcy, line, dst->w, numerator - ye, numerator, denominator);
272 for(int i = 0;i < intpart - 1;i++) {
273 accumulate_line(src, srcy, line, dst->w, numerator, numerator, denominator);
277 if(ye >= numerator) {
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));
290 *(Uint16 *)dstpixel = mapped;
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;
298 dstpixel[0] = (mapped >> 0) & 0xff;
299 dstpixel[1] = (mapped >> 8) & 0xff;
300 dstpixel[2] = (mapped >> 16) & 0xff;
304 *(Uint32 *)dstpixel = mapped;
309 if(!src->format->Amask)
311 if(src->flags & SDL_SRCALPHA)
313 SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
315 if(src->flags & SDL_SRCCOLORKEY)
317 SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
325 SDL_Surface *horz_flip(SDL_Surface *src)
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;
335 dstpixel[3] = srcpixel[3];
337 dstpixel[2] = srcpixel[2];
339 dstpixel[1] = srcpixel[1];
341 dstpixel[0] = srcpixel[0];
345 if(!src->format->Amask)
347 if(src->flags & SDL_SRCALPHA)
349 SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
351 if(src->flags & SDL_SRCCOLORKEY)
353 SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
359 SDL_Surface *vert_flip(SDL_Surface *src)
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;
369 dstpixel[3] = srcpixel[3];
371 dstpixel[2] = srcpixel[2];
373 dstpixel[1] = srcpixel[1];
375 dstpixel[0] = srcpixel[0];
379 if(!src->format->Amask)
381 if(src->flags & SDL_SRCALPHA)
383 SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
385 if(src->flags & SDL_SRCCOLORKEY)
387 SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
393 SDL_Surface *colorize(SDL_Surface *src, const Color &color)
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;
412 mapped = *(Uint16 *)srcpixel;
415 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
416 mapped |= srcpixel[0] << 16;
417 mapped |= srcpixel[1] << 8;
418 mapped |= srcpixel[2] << 0;
420 mapped |= srcpixel[0] << 0;
421 mapped |= srcpixel[1] << 8;
422 mapped |= srcpixel[2] << 16;
426 mapped = *(Uint32 *)srcpixel;
429 if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
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);
440 *(Uint16 *)dstpixel = mapped;
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;
448 dstpixel[0] = (mapped >> 0) & 0xff;
449 dstpixel[1] = (mapped >> 8) & 0xff;
450 dstpixel[2] = (mapped >> 16) & 0xff;
454 *(Uint32 *)dstpixel = mapped;
459 if(!src->format->Amask)
461 if(src->flags & SDL_SRCALPHA)
463 SDL_SetAlpha(dst, SDL_SRCALPHA, src->format->alpha);
465 if(src->flags & SDL_SRCCOLORKEY)
467 SDL_SetColorKey(dst, SDL_SRCCOLORKEY, src->format->colorkey);
473 SDL_Surface *optimize(SDL_Surface *src)
475 if(!src->format->Amask)
477 return SDL_DisplayFormat(src);
483 int semitransparent = 0;
484 std::set<int> colors;
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;
496 mapped = *(Uint16 *)pixel;
499 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
500 mapped |= pixel[0] << 16;
501 mapped |= pixel[1] << 8;
502 mapped |= pixel[2] << 0;
504 mapped |= pixel[0] << 0;
505 mapped |= pixel[1] << 8;
506 mapped |= pixel[2] << 16;
510 mapped = *(Uint32 *)pixel;
513 Uint8 red, green, blue, alpha;
514 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
519 else if (alpha > 224)
527 colors.insert(((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4));
530 if(semitransparent > ((opaque + transparent + semitransparent) / 8))
532 // FIXME: try to approximate textures that have a fairly constant alpha
533 return SDL_DisplayFormatAlpha(src);
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;
549 mapped = *(Uint16 *)srcpixel;
552 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
553 mapped |= srcpixel[0] << 16;
554 mapped |= srcpixel[1] << 8;
555 mapped |= srcpixel[2] << 0;
557 mapped |= srcpixel[0] << 0;
558 mapped |= srcpixel[1] << 8;
559 mapped |= srcpixel[2] << 16;
563 mapped = *(Uint32 *)srcpixel;
566 Uint8 red, green, blue, alpha;
567 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
574 mapped = SDL_MapRGB(dst->format, red, green, blue);
581 *(Uint16 *)dstpixel = mapped;
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;
589 dstpixel[0] = (mapped >> 0) & 0xff;
590 dstpixel[1] = (mapped >> 8) & 0xff;
591 dstpixel[2] = (mapped >> 16) & 0xff;
595 *(Uint32 *)dstpixel = mapped;
600 SDL_SetColorKey(dst, SDL_SRCCOLORKEY, key);
601 SDL_Surface *convert = SDL_DisplayFormat(dst);
602 SDL_FreeSurface(dst);
610 Texture::Texture(SDL_Surface* image)
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)
620 numerator = config->screenwidth;
621 denominator = SCREEN_WIDTH;
625 numerator = config->screenheight;
626 denominator = SCREEN_HEIGHT;
628 cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
633 SDL_FreeSurface(texture);
636 SDL_Surface *Texture::get_transform(const Color &color, DrawingEffect effect)
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);
642 if(cache[effect][color] == 0) {
643 assert(cache[NO_EFFECT][color]);
647 case HORIZONTAL_FLIP:
648 cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
651 cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
657 return cache[effect][color];