00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <cassert>
00024 #include <iostream>
00025
00026
00027
00028
00029
00030
00031
00032 #include "util/log/logger.h"
00033 #include "util/structures/rect.h"
00034 #include "video/renderbackend.h"
00035
00036 #include "renderbackendsdl.h"
00037 #include "sdlblendingfunctions.h"
00038 #include "sdlimage.h"
00039
00040 namespace FIFE {
00041 static Logger _log(LM_VIDEO);
00042
00043 SDLImage::SDLImage(SDL_Surface* surface):
00044 Image(surface) {
00045 resetSdlimage();
00046 }
00047
00048 SDLImage::SDLImage(const uint8_t* data, unsigned int width, unsigned int height):
00049 Image(data, width, height) {
00050 resetSdlimage();
00051 }
00052
00053 void SDLImage::resetSdlimage() {
00054 m_last_alpha = 255;
00055 m_finalized = false;
00056 m_isalphaoptimized = false;
00057 m_colorkey = RenderBackend::instance()->getColorKey();
00058 }
00059
00060 SDLImage::~SDLImage() { }
00061
00062
00063 void SDL_BlitSurfaceWithAlpha( const SDL_Surface* src, const SDL_Rect* srcRect,
00064 SDL_Surface* dst, SDL_Rect* dstRect, unsigned char alpha ) {
00065 if( 0 == alpha ) {
00066 return;
00067 }
00068
00069 int screenX, screenY;
00070 if( dstRect ) {
00071 screenX = dstRect->x;
00072 screenY = dstRect->y;
00073 } else {
00074 screenX = dst->clip_rect.x;
00075 screenY = dst->clip_rect.y;
00076 }
00077
00078 int width, height, tX, tY;
00079 if( srcRect ) {
00080 tX = srcRect->x;
00081 tY = srcRect->y;
00082 width = srcRect->w;
00083 height = srcRect->h;
00084 } else {
00085 tX = src->clip_rect.x;
00086 tY = src->clip_rect.y;
00087 width = src->clip_rect.w;
00088 height = src->clip_rect.h;
00089 }
00090
00091
00092 if( ( screenX >= ( dst->clip_rect.x + dst->clip_rect.w ) ) ||
00093 ( screenY >= ( dst->clip_rect.y + dst->clip_rect.h ) ) ||
00094 ( ( screenX + width ) <= dst->clip_rect.x ) ||
00095 ( ( screenY + height ) <= dst->clip_rect.y ) ) {
00096 return;
00097 }
00098
00099 if( screenX < dst->clip_rect.x ) {
00100 int dX = dst->clip_rect.x - screenX;
00101 screenX += dX;
00102 width -= dX;
00103 tX += dX;
00104 }
00105
00106 if( ( screenX + width ) > ( dst->clip_rect.x + dst->clip_rect.w ) ) {
00107 int dX = ( screenX + width ) - ( dst->clip_rect.x + dst->clip_rect.w );
00108 width -= dX;
00109 }
00110
00111 if( screenY < dst->clip_rect.y ) {
00112 int dY = dst->clip_rect.y - screenY;
00113 screenY += dY;
00114 height -= dY;
00115 tY += dY;
00116 }
00117
00118 if( ( screenY + height ) > ( dst->clip_rect.y + dst->clip_rect.h ) ) {
00119 int dY = ( screenY + height ) - ( dst->clip_rect.y + dst->clip_rect.h );
00120 height -= dY;
00121 }
00122
00123 if( ( 0 >= height ) || ( 0 >= width ) ) {
00124 return;
00125 }
00126
00127 SDL_LockSurface( dst );
00128
00129 unsigned char* srcData = reinterpret_cast< unsigned char* > ( src->pixels );
00130 unsigned char* dstData = reinterpret_cast< unsigned char* > ( dst->pixels );
00131
00132
00133 srcData += tY * src->pitch + tX * src->format->BytesPerPixel;
00134 dstData += screenY * dst->pitch + screenX * dst->format->BytesPerPixel;
00135
00136 switch( src->format->BitsPerPixel ) {
00137 case 32: {
00138 switch( dst->format->BitsPerPixel ) {
00139 case 16: {
00140 if( 0xFFFF == ( dst->format->Rmask | dst->format->Gmask | dst->format->Bmask ) ) {
00141 for( int y = height; y > 0; --y ) {
00142 SDL_BlendRow_RGBA8_to_RGB565( srcData, dstData, alpha, width );
00143 srcData += src->pitch;
00144 dstData += dst->pitch;
00145 }
00146 }
00147 }
00148 break;
00149
00150 case 24: {
00151 for( int y = height; y > 0; --y ) {
00152 SDL_BlendRow_RGBA8_to_RGB8( srcData, dstData, alpha, width );
00153 srcData += src->pitch;
00154 dstData += dst->pitch;
00155 }
00156 }
00157 break;
00158
00159 case 32: {
00160 for( int y = height; y > 0; --y ) {
00161 SDL_BlendRow_RGBA8_to_RGBA8( srcData, dstData, alpha, width );
00162 srcData += src->pitch;
00163 dstData += dst->pitch;
00164 }
00165 }
00166 break;
00167
00168 default:
00169 break;
00170 }
00171 }
00172 break;
00173
00174 case 16: {
00175 if( 0x000F == src->format->Amask ) {
00176 if( ( 16 == dst->format->BitsPerPixel ) &&
00177 ( 0xFFFF == ( dst->format->Rmask | dst->format->Gmask | dst->format->Bmask ) ) ) {
00178 for( int y = height; y > 0; --y ) {
00179 SDL_BlendRow_RGBA4_to_RGB565( srcData, dstData, alpha, width );
00180 srcData += src->pitch;
00181 dstData += dst->pitch;
00182 }
00183 }
00184 }
00185 }
00186 break;
00187
00188 default:
00189 break;
00190 }
00191
00192 SDL_UnlockSurface( dst );
00193 }
00194
00195 void SDLImage::render(const Rect& rect, SDL_Surface* screen, unsigned char alpha) {
00196 if (alpha == 0) {
00197 return;
00198 }
00199
00200 if (rect.right() < 0 || rect.x > static_cast<int>(screen->w) || rect.bottom() < 0 || rect.y > static_cast<int>(screen->h)) {
00201 return;
00202 }
00203 finalize();
00204
00205 SDL_Surface* surface = screen;
00206 SDL_Rect r;
00207 r.x = rect.x;
00208 r.y = rect.y;
00209 r.w = rect.w;
00210 r.h = rect.h;
00211
00212 if (m_surface->format->Amask == 0) {
00213
00214 if (m_last_alpha != alpha) {
00215 m_last_alpha = alpha;
00216 SDL_SetAlpha(m_surface, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
00217 }
00218 SDL_BlitSurface(m_surface, 0, surface, &r);
00219 } else {
00220 if( 255 != alpha ) {
00221
00222
00223 SDL_BlitSurfaceWithAlpha( m_surface, 0, surface, &r, alpha );
00224 } else {
00225 SDL_BlitSurface(m_surface, 0, surface, &r);
00226 }
00227 }
00228 }
00229
00230 void SDLImage::finalize() {
00231 if( m_finalized ) {
00232 return;
00233 }
00234 m_finalized = true;
00235 SDL_Surface *old_surface = m_surface;
00236 Uint32 key = SDL_MapRGB(m_surface->format, m_colorkey.r, m_colorkey.g, m_colorkey.b);
00237
00238 if (m_surface->format->Amask == 0) {
00239 SDL_SetAlpha(m_surface, SDL_SRCALPHA | SDL_RLEACCEL, 255);
00240
00241
00242 if (RenderBackend::instance()->isColorKeyEnabled()) {
00243 SDL_SetColorKey(m_surface, SDL_SRCCOLORKEY, key);
00244 }
00245
00246 m_surface = SDL_DisplayFormat(m_surface);
00247 } else {
00248 RenderBackendSDL* be = static_cast<RenderBackendSDL*>(RenderBackend::instance());
00249 m_isalphaoptimized &= be->isAlphaOptimizerEnabled();
00250 if( m_isalphaoptimized ) {
00251 m_surface = optimize(m_surface);
00252 } else {
00253 SDL_SetAlpha(m_surface, SDL_SRCALPHA, 255);
00254
00255
00256 if (RenderBackend::instance()->isColorKeyEnabled()) {
00257 SDL_SetColorKey(m_surface, SDL_SRCCOLORKEY, key);
00258 }
00259
00260 m_surface = SDL_DisplayFormatAlpha(m_surface);
00261 }
00262 }
00263 SDL_FreeSurface(old_surface);
00264 }
00265
00266 SDL_Surface* SDLImage::optimize(SDL_Surface* src) {
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277 int transparent = 0;
00278 int opaque = 0;
00279 int semitransparent = 0;
00280 int alphasum = 0;
00281 int alphasquaresum = 0;
00282 bool colors[(1 << 12)];
00283 memset(colors, 0, (1 << 12) * sizeof(bool));
00284
00285 int bpp = src->format->BytesPerPixel;
00286 if(SDL_MUSTLOCK(src)) {
00287 SDL_LockSurface(src);
00288 }
00289
00290
00291
00292
00293 for(int y = 0;y < src->h;y++) {
00294 for(int x = 0;x < src->w;x++) {
00295 Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
00296 Uint32 mapped = 0;
00297 switch(bpp) {
00298 case 1:
00299 mapped = *pixel;
00300 break;
00301 case 2:
00302 mapped = *(Uint16 *)pixel;
00303 break;
00304 case 3:
00305 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00306 mapped |= pixel[0] << 16;
00307 mapped |= pixel[1] << 8;
00308 mapped |= pixel[2] << 0;
00309 #else
00310 mapped |= pixel[0] << 0;
00311 mapped |= pixel[1] << 8;
00312 mapped |= pixel[2] << 16;
00313 #endif
00314 break;
00315 case 4:
00316 mapped = *(Uint32 *)pixel;
00317 break;
00318 }
00319 Uint8 red, green, blue, alpha;
00320 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
00321 if(alpha < 16) {
00322 transparent++;
00323 } else if (alpha > 240) {
00324 opaque++;
00325 alphasum += alpha;
00326 alphasquaresum += alpha*alpha;
00327 } else {
00328 semitransparent++;
00329 alphasum += alpha;
00330 alphasquaresum += alpha*alpha;
00331 }
00332
00333 if( alpha != 0 ) {
00334 colors[((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4)] = true;
00335 }
00336 }
00337 }
00338 int avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
00339 int alphavariance = 0;
00340
00341 if(SDL_MUSTLOCK(src)) {
00342 SDL_UnlockSurface(src);
00343 }
00344 alphasquaresum /= (opaque + semitransparent) ? (opaque + semitransparent) : 1;
00345 alphavariance = alphasquaresum - avgalpha*avgalpha;
00346 if(semitransparent > ((transparent + opaque + semitransparent) / 8)
00347 && alphavariance > 16) {
00348 FL_DBG(_log, LMsg("sdlimage")
00349 << "Trying to alpha-optimize image. FAILED: real alpha usage. "
00350 << " alphavariance=" << alphavariance
00351 << " total=" << (transparent + opaque + semitransparent)
00352 << " semitransparent=" << semitransparent
00353 << "(" << (float(semitransparent)/(transparent + opaque + semitransparent))
00354 << ")");
00355 return SDL_DisplayFormatAlpha(src);
00356 }
00357
00358
00359 int keycolor = -1;
00360 for(int i = 0;i < (1 << 12);i++) {
00361 if(!colors[i]) {
00362 keycolor = i;
00363 break;
00364 }
00365 }
00366 if(keycolor == -1) {
00367 FL_DBG(_log, LMsg("sdlimage") << "Trying to alpha-optimize image. FAILED: no free color");
00368 return SDL_DisplayFormatAlpha(src);
00369 }
00370
00371 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA) | SDL_SWSURFACE,
00372 src->w, src->h,
00373 src->format->BitsPerPixel,
00374 src->format->Rmask, src->format->Gmask,
00375 src->format->Bmask, 0);
00376 bpp = dst->format->BytesPerPixel;
00377
00378 Uint32 key = SDL_MapRGB(dst->format, m_colorkey.r, m_colorkey.g, m_colorkey.b);
00379
00380
00381 if (!RenderBackend::instance()->isColorKeyEnabled()) {
00382 key = SDL_MapRGB(dst->format,
00383 (((keycolor & 0xf00) >> 4) | 0xf),
00384 ((keycolor & 0xf0) | 0xf),
00385 (((keycolor & 0xf) << 4) | 0xf));
00386 }
00387
00388 if(SDL_MUSTLOCK(src)) {
00389 SDL_LockSurface(src);
00390 }
00391 if(SDL_MUSTLOCK(dst)) {
00392 SDL_LockSurface(dst);
00393 }
00394 for(int y = 0;y < dst->h;y++) {
00395 for(int x = 0;x < dst->w;x++) {
00396 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
00397 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
00398 Uint32 mapped = 0;
00399 switch(bpp) {
00400 case 1:
00401 mapped = *srcpixel;
00402 break;
00403 case 2:
00404 mapped = *(Uint16 *)srcpixel;
00405 break;
00406 case 3:
00407 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00408 mapped |= srcpixel[0] << 16;
00409 mapped |= srcpixel[1] << 8;
00410 mapped |= srcpixel[2] << 0;
00411 #else
00412 mapped |= srcpixel[0] << 0;
00413 mapped |= srcpixel[1] << 8;
00414 mapped |= srcpixel[2] << 16;
00415 #endif
00416 break;
00417 case 4:
00418 mapped = *(Uint32 *)srcpixel;
00419 break;
00420 }
00421 Uint8 red, green, blue, alpha;
00422 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
00423 if(alpha < (avgalpha / 4)) {
00424 mapped = key;
00425 } else {
00426 mapped = SDL_MapRGB(dst->format, red, green, blue);
00427 }
00428 switch(bpp) {
00429 case 1:
00430 *dstpixel = mapped;
00431 break;
00432 case 2:
00433 *(Uint16 *)dstpixel = mapped;
00434 break;
00435 case 3:
00436 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00437 dstpixel[0] = (mapped >> 16) & 0xff;
00438 dstpixel[1] = (mapped >> 8) & 0xff;
00439 dstpixel[2] = (mapped >> 0) & 0xff;
00440 #else
00441 dstpixel[0] = (mapped >> 0) & 0xff;
00442 dstpixel[1] = (mapped >> 8) & 0xff;
00443 dstpixel[2] = (mapped >> 16) & 0xff;
00444 #endif
00445 break;
00446 case 4:
00447 *(Uint32 *)dstpixel = mapped;
00448 break;
00449 }
00450 }
00451 }
00452 if(SDL_MUSTLOCK(dst)) {
00453 SDL_UnlockSurface(dst);
00454 }
00455 if(SDL_MUSTLOCK(src)) {
00456 SDL_UnlockSurface(src);
00457 }
00458
00459
00460
00461
00462
00463
00464
00465 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, key);
00466 SDL_Surface *convert = SDL_DisplayFormat(dst);
00467 SDL_FreeSurface(dst);
00468 FL_DBG(_log, LMsg("sdlimage ")
00469 << "Trying to alpha-optimize image. SUCCESS: colorkey is " << key);
00470 return convert;
00471 }
00472
00473 bool SDLImage::putPixel(int x, int y, int r, int g, int b) {
00474 if ((x < 0) || (x >= m_surface->w) || (y < 0) || (y >= m_surface->h)) {
00475 return false;
00476 }
00477
00478 int bpp = m_surface->format->BytesPerPixel;
00479 SDL_LockSurface(m_surface);
00480 Uint8* p = (Uint8*)m_surface->pixels + y * m_surface->pitch + x * bpp;
00481 Uint32 pixel = SDL_MapRGB(m_surface->format, r, g, b);
00482 switch(bpp)
00483 {
00484 case 1:
00485 *p = pixel;
00486 break;
00487
00488 case 2:
00489 *(Uint16 *)p = pixel;
00490 break;
00491
00492 case 3:
00493 if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
00494 p[0] = (pixel >> 16) & 0xff;
00495 p[1] = (pixel >> 8) & 0xff;
00496 p[2] = pixel & 0xff;
00497 }
00498 else {
00499 p[0] = pixel & 0xff;
00500 p[1] = (pixel >> 8) & 0xff;
00501 p[2] = (pixel >> 16) & 0xff;
00502 }
00503 break;
00504
00505 case 4:
00506 *(Uint32 *)p = pixel;
00507 break;
00508 }
00509 SDL_UnlockSurface(m_surface);
00510 return true;
00511 }
00512
00513 void SDLImage::drawLine(const Point& p1, const Point& p2, int r, int g, int b) {
00514
00515 int x1 = p1.x;
00516 int x2 = p2.x;
00517 int y1 = p1.y;
00518 int y2 = p2.y;
00519 int dx = ABS(x2 - x1);
00520 int dy = ABS(y2 - y1);
00521
00522 if (dx > dy) {
00523 if (x1 > x2) {
00524
00525 x1 ^= x2;
00526 x2 ^= x1;
00527 x1 ^= x2;
00528
00529
00530 y1 ^= y2;
00531 y2 ^= y1;
00532 y1 ^= y2;
00533 }
00534
00535 if (y1 < y2) {
00536 int y = y1;
00537 int p = 0;
00538
00539 for (int x = x1; x <= x2; x++) {
00540 putPixel(x, y, r, g, b);
00541 p += dy;
00542 if (p * 2 >= dx) {
00543 y++;
00544 p -= dx;
00545 }
00546 }
00547 }
00548 else {
00549 int y = y1;
00550 int p = 0;
00551
00552 for (int x = x1; x <= x2; x++) {
00553 putPixel(x, y, r, g, b);
00554
00555 p += dy;
00556 if (p * 2 >= dx) {
00557 y--;
00558 p -= dx;
00559 }
00560 }
00561 }
00562 }
00563 else {
00564 if (y1 > y2) {
00565
00566 y1 ^= y2;
00567 y2 ^= y1;
00568 y1 ^= y2;
00569
00570
00571 x1 ^= x2;
00572 x2 ^= x1;
00573 x1 ^= x2;
00574 }
00575
00576 if (x1 < x2) {
00577 int x = x1;
00578 int p = 0;
00579
00580 for (int y = y1; y <= y2; y++) {
00581 putPixel(x, y, r, g, b);
00582 p += dx;
00583 if (p * 2 >= dy) {
00584 x++;
00585 p -= dy;
00586 }
00587 }
00588 }
00589 else {
00590 int x = x1;
00591 int p = 0;
00592
00593 for (int y = y1; y <= y2; y++) {
00594 putPixel(x, y, r, g, b);
00595 p += dx;
00596 if (p * 2 >= dy) {
00597 x--;
00598 p -= dy;
00599 }
00600 }
00601 }
00602 }
00603 }
00604
00605 void SDLImage::drawQuad(const Point& p1, const Point& p2, const Point& p3, const Point& p4, int r, int g, int b) {
00606 drawLine(p1, p2, r, g, b);
00607 drawLine(p2, p3, r, g, b);
00608 drawLine(p3, p4, r, g, b);
00609 drawLine(p4, p1, r, g, b);
00610 }
00611
00612 void SDLImage::drawVertex(const Point& p, const uint8_t size, int r, int g, int b){
00613 Point p1 = Point(p.x-size, p.y+size);
00614 Point p2 = Point(p.x+size, p.y+size);
00615 Point p3 = Point(p.x+size, p.y-size);
00616 Point p4 = Point(p.x-size, p.y-size);
00617
00618 drawLine(p1, p2, r, g, b);
00619 drawLine(p2, p3, r, g, b);
00620 drawLine(p3, p4, r, g, b);
00621 drawLine(p4, p1, r, g, b);
00622 }
00623
00624 void SDLImage::saveImage(const std::string& filename) {
00625 if(m_surface) {
00626 const unsigned int swidth = getWidth();
00627 const unsigned int sheight = getHeight();
00628 Uint32 rmask, gmask, bmask, amask;
00629 SDL_Surface *surface = NULL;
00630
00631 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00632 rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff;
00633 #else
00634 rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000;
00635 #endif
00636
00637 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth,
00638 sheight, 24,
00639 rmask, gmask, bmask, 0);
00640
00641 if(surface == NULL) {
00642 return;
00643 }
00644
00645 SDL_BlitSurface(m_surface, NULL, surface, NULL);
00646
00647 saveAsPng(filename, *surface);
00648 SDL_FreeSurface(surface);
00649 }
00650 }
00651
00652 void SDLImage::setClipArea(const Rect& cliparea, bool clear) {
00653 SDL_Rect rect;
00654 rect.x = cliparea.x;
00655 rect.y = cliparea.y;
00656 rect.w = cliparea.w;
00657 rect.h = cliparea.h;
00658 SDL_SetClipRect(m_surface, &rect);
00659 if (clear) {
00660 SDL_FillRect(m_surface, &rect, 0x00);
00661 }
00662 }
00663 }