comparison engine/core/video/sdl/sdlimage.cpp @ 0:4a0efb7baf70

* Datasets becomes the new trunk and retires after that :-)
author mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222
date Sun, 29 Jun 2008 18:44:17 +0000
parents
children 90005975cdbb
comparison
equal deleted inserted replaced
-1:000000000000 0:4a0efb7baf70
1 /***************************************************************************
2 * Copyright (C) 2005-2008 by the FIFE team *
3 * http://www.fifengine.de *
4 * This file is part of FIFE. *
5 * *
6 * FIFE is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (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 *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
20 ***************************************************************************/
21
22 // Standard C++ library includes
23 #include <cassert>
24 #include <iostream>
25
26 // 3rd party library includes
27
28 // FIFE includes
29 // These includes are split up in two parts, separated by one empty line
30 // First block: files included from the FIFE root src directory
31 // Second block: files included from the same folder
32 #include "util/log/logger.h"
33 #include "util/structures/rect.h"
34
35 #include "renderbackendsdl.h"
36 #include "sdlblendingfunctions.h"
37 #include "sdlimage.h"
38
39 namespace FIFE {
40 static Logger _log(LM_VIDEO);
41
42 SDLImage::SDLImage(SDL_Surface* surface):
43 Image(surface) {
44 resetSdlimage();
45 }
46
47 SDLImage::SDLImage(const uint8_t* data, unsigned int width, unsigned int height):
48 Image(data, width, height) {
49 resetSdlimage();
50 }
51
52 void SDLImage::resetSdlimage() {
53 m_last_alpha = 255;
54 m_finalized = false;
55 m_isalphaoptimized = false;
56 }
57
58 SDLImage::~SDLImage() { }
59
60
61 void SDL_BlitSurfaceWithAlpha( const SDL_Surface* src, const SDL_Rect* srcRect,
62 SDL_Surface* dst, SDL_Rect* dstRect, unsigned char alpha ) {
63 if( 0 == alpha ) {
64 return;
65 }
66
67 int screenX, screenY;
68 if( dstRect ) {
69 screenX = dstRect->x;
70 screenY = dstRect->y;
71 } else {
72 screenX = dst->clip_rect.x;
73 screenY = dst->clip_rect.y;
74 }
75
76 int width, height, tX, tY;
77 if( srcRect ) {
78 tX = srcRect->x;
79 tY = srcRect->y;
80 width = srcRect->w;
81 height = srcRect->h;
82 } else {
83 tX = src->clip_rect.x;
84 tY = src->clip_rect.y;
85 width = src->clip_rect.w;
86 height = src->clip_rect.h;
87 }
88
89 // Clipping.
90 if( ( screenX >= ( dst->clip_rect.x + dst->clip_rect.w ) ) ||
91 ( screenY >= ( dst->clip_rect.y + dst->clip_rect.h ) ) ||
92 ( ( screenX + width ) <= dst->clip_rect.x ) ||
93 ( ( screenY + height ) <= dst->clip_rect.y ) ) {
94 return;
95 }
96
97 if( screenX < dst->clip_rect.x ) {
98 int dX = dst->clip_rect.x - screenX;
99 screenX += dX;
100 width -= dX;
101 tX += dX;
102 }
103
104 if( ( screenX + width ) > ( dst->clip_rect.x + dst->clip_rect.w ) ) {
105 int dX = ( screenX + width ) - ( dst->clip_rect.x + dst->clip_rect.w );
106 width -= dX;
107 }
108
109 if( screenY < dst->clip_rect.y ) {
110 int dY = dst->clip_rect.y - screenY;
111 screenY += dY;
112 height -= dY;
113 tY += dY;
114 }
115
116 if( ( screenY + height ) > ( dst->clip_rect.y + dst->clip_rect.h ) ) {
117 int dY = ( screenY + height ) - ( dst->clip_rect.y + dst->clip_rect.h );
118 height -= dY;
119 }
120
121 if( ( 0 >= height ) || ( 0 >= width ) ) {
122 return;
123 }
124
125 SDL_LockSurface( dst );
126
127 unsigned char* srcData = reinterpret_cast< unsigned char* > ( src->pixels );
128 unsigned char* dstData = reinterpret_cast< unsigned char* > ( dst->pixels );
129
130 // move data pointers to the start of the pixels we're copying
131 srcData += tY * src->pitch + tX * src->format->BytesPerPixel;
132 dstData += screenY * dst->pitch + screenX * dst->format->BytesPerPixel;
133
134 switch( src->format->BitsPerPixel ) {
135 case 32: {
136 switch( dst->format->BitsPerPixel ) {
137 case 16: {
138 if( 0xFFFF == ( dst->format->Rmask | dst->format->Gmask | dst->format->Bmask ) ) {
139 for( int y = height; y > 0; --y ) {
140 SDL_BlendRow_RGBA8_to_RGB565( srcData, dstData, alpha, width );
141 srcData += src->pitch;
142 dstData += dst->pitch;
143 }
144 }
145 }
146 break;
147
148 case 24: {
149 for( int y = height; y > 0; --y ) {
150 SDL_BlendRow_RGBA8_to_RGB8( srcData, dstData, alpha, width );
151 srcData += src->pitch;
152 dstData += dst->pitch;
153 }
154 }
155 break;
156
157 case 32: {
158 for( int y = height; y > 0; --y ) {
159 SDL_BlendRow_RGBA8_to_RGBA8( srcData, dstData, alpha, width );
160 srcData += src->pitch;
161 dstData += dst->pitch;
162 }
163 }
164 break;
165
166 default:
167 break;
168 } ///< switch( dst->format->BitsPerPixel )
169 }
170 break;
171
172 case 16: {
173 if( 0x000F == src->format->Amask ) {
174 if( ( 16 == dst->format->BitsPerPixel ) &&
175 ( 0xFFFF == ( dst->format->Rmask | dst->format->Gmask | dst->format->Bmask ) ) ) {
176 for( int y = height; y > 0; --y ) {
177 SDL_BlendRow_RGBA4_to_RGB565( srcData, dstData, alpha, width );
178 srcData += src->pitch;
179 dstData += dst->pitch;
180 }
181 }
182 }
183 }
184 break;
185
186 default:
187 break;
188 } ///< switch( src->format->BitsPerPixel )
189
190 SDL_UnlockSurface( dst );
191 }
192
193 void SDLImage::render(const Rect& rect, SDL_Surface* screen, unsigned char alpha) {
194 if (alpha == 0) {
195 return;
196 }
197
198 if (rect.right() < 0 || rect.x > static_cast<int>(screen->w) || rect.bottom() < 0 || rect.y > static_cast<int>(screen->h)) {
199 return;
200 }
201 finalize();
202
203 SDL_Surface* surface = screen;
204 SDL_Rect r;
205 r.x = rect.x;
206 r.y = rect.y;
207 r.w = rect.w;
208 r.h = rect.h;
209
210 if (m_surface->format->Amask == 0) {
211 // Image has no alpha channel. This allows us to use the per-surface alpha.
212 if (m_last_alpha != alpha) {
213 m_last_alpha = alpha;
214 SDL_SetAlpha(m_surface, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
215 }
216 SDL_BlitSurface(m_surface, 0, surface, &r);
217 } else {
218 if( 255 != alpha ) {
219 // Special blitting routine with alpha blending:
220 // dst.rgb = ( src.rgb * src.a * alpha ) + ( dst.rgb * (255 - ( src.a * alpha ) ) );
221 SDL_BlitSurfaceWithAlpha( m_surface, 0, surface, &r, alpha );
222 } else {
223 SDL_BlitSurface(m_surface, 0, surface, &r);
224 }
225 }
226 }
227
228 void SDLImage::finalize() {
229 if( m_finalized ) {
230 return;
231 }
232 m_finalized = true;
233 SDL_Surface *old_surface = m_surface;
234
235 if (m_surface->format->Amask == 0) {
236 SDL_SetAlpha(m_surface, SDL_SRCALPHA | SDL_RLEACCEL, 255);
237 m_surface = SDL_DisplayFormat(m_surface);
238 } else {
239 RenderBackendSDL* be = static_cast<RenderBackendSDL*>(RenderBackend::instance());
240 m_isalphaoptimized &= be->isAlphaOptimizerEnabled();
241 if( m_isalphaoptimized ) {
242 m_surface = optimize(m_surface);
243 } else {
244 SDL_SetAlpha(m_surface, SDL_SRCALPHA, 255);
245 m_surface = SDL_DisplayFormatAlpha(m_surface);
246 }
247 }
248 SDL_FreeSurface(old_surface);
249 }
250
251 SDL_Surface* SDLImage::optimize(SDL_Surface* src) {
252 // The algorithm is originally by "Tim Goya" <tuxdev103@gmail.com>
253 // Few modifications and adaptions by the FIFE team.
254 //
255 // It tries to determine whether an image with a alpha channel
256 // actually uses that. Often PNGs contains an alpha channels
257 // as they don't provide a colorkey feature(?) - so to speed
258 // up SDL rendering we try to remove the alpha channel.
259
260 // As a reminder: src->format->Amask != 0 here
261
262 int transparent = 0;
263 int opaque = 0;
264 int semitransparent = 0;
265 int alphasum = 0;
266 int alphasquaresum = 0;
267 bool colors[(1 << 12)];
268 memset(colors, 0, (1 << 12) * sizeof(bool));
269
270 int bpp = src->format->BytesPerPixel;
271 if(SDL_MUSTLOCK(src)) {
272 SDL_LockSurface(src);
273 }
274 /* In the first pass through we calculate avg(alpha), avg(alpha^2)
275 and the number of semitransparent pixels.
276 We also try to find a useable color.
277 */
278 for(int y = 0;y < src->h;y++) {
279 for(int x = 0;x < src->w;x++) {
280 Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
281 Uint32 mapped = 0;
282 switch(bpp) {
283 case 1:
284 mapped = *pixel;
285 break;
286 case 2:
287 mapped = *(Uint16 *)pixel;
288 break;
289 case 3:
290 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
291 mapped |= pixel[0] << 16;
292 mapped |= pixel[1] << 8;
293 mapped |= pixel[2] << 0;
294 #else
295 mapped |= pixel[0] << 0;
296 mapped |= pixel[1] << 8;
297 mapped |= pixel[2] << 16;
298 #endif
299 break;
300 case 4:
301 mapped = *(Uint32 *)pixel;
302 break;
303 }
304 Uint8 red, green, blue, alpha;
305 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
306 if(alpha < 16) {
307 transparent++;
308 } else if (alpha > 240) {
309 opaque++;
310 alphasum += alpha;
311 alphasquaresum += alpha*alpha;
312 } else {
313 semitransparent++;
314 alphasum += alpha;
315 alphasquaresum += alpha*alpha;
316 }
317 // mark the color as used.
318 if( alpha != 0 ) {
319 colors[((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4)] = true;
320 }
321 }
322 }
323 int avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
324 int alphavariance = 0;
325
326 if(SDL_MUSTLOCK(src)) {
327 SDL_UnlockSurface(src);
328 }
329 alphasquaresum /= (opaque + semitransparent) ? (opaque + semitransparent) : 1;
330 alphavariance = alphasquaresum - avgalpha*avgalpha;
331 if(semitransparent > ((transparent + opaque + semitransparent) / 8)
332 && alphavariance > 16) {
333 FL_DBG(_log, LMsg("sdlimage")
334 << "Trying to alpha-optimize image. FAILED: real alpha usage. "
335 << " alphavariance=" << alphavariance
336 << " total=" << (transparent + opaque + semitransparent)
337 << " semitransparent=" << semitransparent
338 << "(" << (float(semitransparent)/(transparent + opaque + semitransparent))
339 << ")");
340 return SDL_DisplayFormatAlpha(src);
341 }
342
343 // check availability of a suitable color as colorkey
344 int keycolor = -1;
345 for(int i = 0;i < (1 << 12);i++) {
346 if(!colors[i]) {
347 keycolor = i;
348 break;
349 }
350 }
351 if(keycolor == -1) {
352 FL_DBG(_log, LMsg("sdlimage") << "Trying to alpha-optimize image. FAILED: no free color");
353 return SDL_DisplayFormatAlpha(src);
354 }
355
356 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA) | SDL_SWSURFACE,
357 src->w, src->h,
358 src->format->BitsPerPixel,
359 src->format->Rmask, src->format->Gmask,
360 src->format->Bmask, 0);
361 bpp = dst->format->BytesPerPixel;
362 Uint32 key = SDL_MapRGB(dst->format,
363 (((keycolor & 0xf00) >> 4) | 0xf),
364 ((keycolor & 0xf0) | 0xf),
365 (((keycolor & 0xf) << 4) | 0xf));
366 if(SDL_MUSTLOCK(src)) {
367 SDL_LockSurface(src);
368 }
369 if(SDL_MUSTLOCK(dst)) {
370 SDL_LockSurface(dst);
371 }
372 for(int y = 0;y < dst->h;y++) {
373 for(int x = 0;x < dst->w;x++) {
374 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
375 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
376 Uint32 mapped = 0;
377 switch(bpp) {
378 case 1:
379 mapped = *srcpixel;
380 break;
381 case 2:
382 mapped = *(Uint16 *)srcpixel;
383 break;
384 case 3:
385 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
386 mapped |= srcpixel[0] << 16;
387 mapped |= srcpixel[1] << 8;
388 mapped |= srcpixel[2] << 0;
389 #else
390 mapped |= srcpixel[0] << 0;
391 mapped |= srcpixel[1] << 8;
392 mapped |= srcpixel[2] << 16;
393 #endif
394 break;
395 case 4:
396 mapped = *(Uint32 *)srcpixel;
397 break;
398 }
399 Uint8 red, green, blue, alpha;
400 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
401 if(alpha < (avgalpha / 4)) {
402 mapped = key;
403 } else {
404 mapped = SDL_MapRGB(dst->format, red, green, blue);
405 }
406 switch(bpp) {
407 case 1:
408 *dstpixel = mapped;
409 break;
410 case 2:
411 *(Uint16 *)dstpixel = mapped;
412 break;
413 case 3:
414 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
415 dstpixel[0] = (mapped >> 16) & 0xff;
416 dstpixel[1] = (mapped >> 8) & 0xff;
417 dstpixel[2] = (mapped >> 0) & 0xff;
418 #else
419 dstpixel[0] = (mapped >> 0) & 0xff;
420 dstpixel[1] = (mapped >> 8) & 0xff;
421 dstpixel[2] = (mapped >> 16) & 0xff;
422 #endif
423 break;
424 case 4:
425 *(Uint32 *)dstpixel = mapped;
426 break;
427 }
428 }
429 }
430 if(SDL_MUSTLOCK(dst)) {
431 SDL_UnlockSurface(dst);
432 }
433 if(SDL_MUSTLOCK(src)) {
434 SDL_UnlockSurface(src);
435 }
436 // Using the per surface alpha value does not
437 // work out for mostly transparent pixels.
438 // Thus disabling the part here - this needs a
439 // more complex refactoring.
440 // if(avgalpha < 240) {
441 // SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, avgalpha);
442 //}
443 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, key);
444 SDL_Surface *convert = SDL_DisplayFormat(dst);
445 SDL_FreeSurface(dst);
446 FL_DBG(_log, LMsg("sdlimage ")
447 << "Trying to alpha-optimize image. SUCCESS: colorkey is " << key);
448 return convert;
449 } // end optimize
450
451 bool SDLImage::putPixel(int x, int y, int r, int g, int b) {
452 if ((x < 0) || (x >= m_surface->w) || (y < 0) || (y >= m_surface->h)) {
453 return false;
454 }
455
456 int bpp = m_surface->format->BytesPerPixel;
457 SDL_LockSurface(m_surface);
458 Uint8* p = (Uint8*)m_surface->pixels + y * m_surface->pitch + x * bpp;
459 Uint32 pixel = SDL_MapRGB(m_surface->format, r, g, b);
460 switch(bpp)
461 {
462 case 1:
463 *p = pixel;
464 break;
465
466 case 2:
467 *(Uint16 *)p = pixel;
468 break;
469
470 case 3:
471 if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
472 p[0] = (pixel >> 16) & 0xff;
473 p[1] = (pixel >> 8) & 0xff;
474 p[2] = pixel & 0xff;
475 }
476 else {
477 p[0] = pixel & 0xff;
478 p[1] = (pixel >> 8) & 0xff;
479 p[2] = (pixel >> 16) & 0xff;
480 }
481 break;
482
483 case 4:
484 *(Uint32 *)p = pixel;
485 break;
486 }
487 SDL_UnlockSurface(m_surface);
488 return true;
489 }
490
491 void SDLImage::drawLine(const Point& p1, const Point& p2, int r, int g, int b) {
492 // Draw a line with Bresenham, imitated from guichan
493 int x1 = p1.x;
494 int x2 = p2.x;
495 int y1 = p1.y;
496 int y2 = p2.y;
497 int dx = ABS(x2 - x1);
498 int dy = ABS(y2 - y1);
499
500 if (dx > dy) {
501 if (x1 > x2) {
502 // swap x1, x2
503 x1 ^= x2;
504 x2 ^= x1;
505 x1 ^= x2;
506
507 // swap y1, y2
508 y1 ^= y2;
509 y2 ^= y1;
510 y1 ^= y2;
511 }
512
513 if (y1 < y2) {
514 int y = y1;
515 int p = 0;
516
517 for (int x = x1; x <= x2; x++) {
518 putPixel(x, y, r, g, b);
519 p += dy;
520 if (p * 2 >= dx) {
521 y++;
522 p -= dx;
523 }
524 }
525 }
526 else {
527 int y = y1;
528 int p = 0;
529
530 for (int x = x1; x <= x2; x++) {
531 putPixel(x, y, r, g, b);
532
533 p += dy;
534 if (p * 2 >= dx) {
535 y--;
536 p -= dx;
537 }
538 }
539 }
540 }
541 else {
542 if (y1 > y2) {
543 // swap y1, y2
544 y1 ^= y2;
545 y2 ^= y1;
546 y1 ^= y2;
547
548 // swap x1, x2
549 x1 ^= x2;
550 x2 ^= x1;
551 x1 ^= x2;
552 }
553
554 if (x1 < x2) {
555 int x = x1;
556 int p = 0;
557
558 for (int y = y1; y <= y2; y++) {
559 putPixel(x, y, r, g, b);
560 p += dx;
561 if (p * 2 >= dy) {
562 x++;
563 p -= dy;
564 }
565 }
566 }
567 else {
568 int x = x1;
569 int p = 0;
570
571 for (int y = y1; y <= y2; y++) {
572 putPixel(x, y, r, g, b);
573 p += dx;
574 if (p * 2 >= dy) {
575 x--;
576 p -= dy;
577 }
578 }
579 }
580 }
581 }
582
583 void SDLImage::drawQuad(const Point& p1, const Point& p2, const Point& p3, const Point& p4, int r, int g, int b) {
584 drawLine(p1, p2, r, g, b);
585 drawLine(p2, p3, r, g, b);
586 drawLine(p3, p4, r, g, b);
587 drawLine(p4, p1, r, g, b);
588 }
589
590 void SDLImage::saveImage(const std::string& filename) {
591 if(m_surface) {
592 const unsigned int swidth = getWidth();
593 const unsigned int sheight = getHeight();
594 Uint32 rmask, gmask, bmask, amask;
595 SDL_Surface *surface = NULL;
596
597 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
598 rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff;
599 #else
600 rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000;
601 #endif
602
603 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth,
604 sheight, 24,
605 rmask, gmask, bmask, 0);
606
607 if(surface == NULL) {
608 return;
609 }
610
611 SDL_BlitSurface(m_surface, NULL, surface, NULL);
612
613 saveAsPng(filename, *surface);
614 SDL_FreeSurface(surface);
615 }
616 }
617
618 void SDLImage::setClipArea(const Rect& cliparea, bool clear) {
619 SDL_Rect rect;
620 rect.x = cliparea.x;
621 rect.y = cliparea.y;
622 rect.w = cliparea.w;
623 rect.h = cliparea.h;
624 SDL_SetClipRect(m_surface, &rect);
625 if (clear) {
626 SDL_FillRect(m_surface, &rect, 0x00);
627 }
628 }
629 }