32 #include "util/log/logger.h"
33 #include "util/structures/rect.h"
34 #include "video/imagemanager.h"
35 #include "video/renderbackend.h"
37 #include "renderbackendsdl.h"
38 #include "sdlblendingfunctions.h"
42 static Logger _log(LM_VIDEO);
44 SDLImage::SDLImage(IResourceLoader* loader):
49 SDLImage::SDLImage(
const std::string& name, IResourceLoader* loader):
54 SDLImage::SDLImage(SDL_Surface* surface):
59 SDLImage::SDLImage(
const std::string& name, SDL_Surface* surface):
60 Image(name, surface) {
64 SDLImage::SDLImage(
const uint8_t* data, uint32_t width, uint32_t height):
65 Image(data, width, height) {
69 SDLImage::SDLImage(
const std::string& name,
const uint8_t* data, uint32_t width, uint32_t height):
70 Image(name, data, width, height) {
74 void SDLImage::resetSdlimage() {
77 m_colorkey = RenderBackend::instance()->getColorKey();
80 m_zoom_surface = NULL;
83 SDLImage::~SDLImage() {
85 SDL_FreeSurface(m_zoom_surface);
89 void SDLImage::setSurface(SDL_Surface* surface) {
91 SDL_FreeSurface(m_zoom_surface);
92 m_zoom_surface = NULL;
98 void SDL_BlitSurfaceWithAlpha(
const SDL_Surface* src,
const SDL_Rect* srcRect,
99 SDL_Surface* dst, SDL_Rect* dstRect, uint8_t alpha ) {
104 int32_t screenX, screenY;
106 screenX = dstRect->x;
107 screenY = dstRect->y;
109 screenX = dst->clip_rect.x;
110 screenY = dst->clip_rect.y;
113 int32_t width, height, tX, tY;
120 tX = src->clip_rect.x;
121 tY = src->clip_rect.y;
122 width = src->clip_rect.w;
123 height = src->clip_rect.h;
127 if( ( screenX >= ( dst->clip_rect.x + dst->clip_rect.w ) ) ||
128 ( screenY >= ( dst->clip_rect.y + dst->clip_rect.h ) ) ||
129 ( ( screenX + width ) <= dst->clip_rect.x ) ||
130 ( ( screenY + height ) <= dst->clip_rect.y ) ) {
134 if( screenX < dst->clip_rect.x ) {
135 int32_t dX = dst->clip_rect.x - screenX;
141 if( ( screenX + width ) > ( dst->clip_rect.x + dst->clip_rect.w ) ) {
142 int32_t dX = ( screenX + width ) - ( dst->clip_rect.x + dst->clip_rect.w );
146 if( screenY < dst->clip_rect.y ) {
147 int32_t dY = dst->clip_rect.y - screenY;
153 if( ( screenY + height ) > ( dst->clip_rect.y + dst->clip_rect.h ) ) {
154 int32_t dY = ( screenY + height ) - ( dst->clip_rect.y + dst->clip_rect.h );
158 if( ( 0 >= height ) || ( 0 >= width ) ) {
162 SDL_LockSurface( dst );
164 uint8_t* srcData =
reinterpret_cast< uint8_t*
> ( src->pixels );
165 uint8_t* dstData =
reinterpret_cast< uint8_t*
> ( dst->pixels );
168 srcData += tY * src->pitch + tX * src->format->BytesPerPixel;
169 dstData += screenY * dst->pitch + screenX * dst->format->BytesPerPixel;
171 switch( src->format->BitsPerPixel ) {
173 switch( dst->format->BitsPerPixel ) {
175 if( 0xFFFF == ( dst->format->Rmask | dst->format->Gmask | dst->format->Bmask ) ) {
176 for( int32_t y = height; y > 0; --y ) {
178 srcData += src->pitch;
179 dstData += dst->pitch;
186 for( int32_t y = height; y > 0; --y ) {
188 srcData += src->pitch;
189 dstData += dst->pitch;
195 for( int32_t y = height; y > 0; --y ) {
197 srcData += src->pitch;
198 dstData += dst->pitch;
210 if( 0x000F == src->format->Amask ) {
211 if( ( 16 == dst->format->BitsPerPixel ) &&
212 ( 0xFFFF == ( dst->format->Rmask | dst->format->Gmask | dst->format->Bmask ) ) ) {
213 for( int32_t y = height; y > 0; --y ) {
215 srcData += src->pitch;
216 dstData += dst->pitch;
227 SDL_UnlockSurface( dst );
230 void zoomSurface(SDL_Surface* src, SDL_Surface* dst) {
231 uint32_t* src_pointer =
static_cast<uint32_t*
>(src->pixels);
232 uint32_t* src_help_pointer = src_pointer;
233 uint32_t* dst_pointer =
static_cast<uint32_t*
>(dst->pixels);
235 int32_t x, y, *sx_ca, *sy_ca;
236 int32_t sx =
static_cast<int32_t
>(0xffff * src->w / dst->w);
237 int32_t sy =
static_cast<int32_t
>(0xffff * src->h / dst->h);
242 int32_t* sx_a =
new int32_t[dst->w + 1];
244 for (x = 0; x <= dst->w; x++) {
251 int32_t* sy_a =
new int32_t[dst->h + 1];
253 for (y = 0; y <= dst->h; y++) {
263 if (SDL_MUSTLOCK(src)) {
264 SDL_LockSurface(src);
266 if (SDL_MUSTLOCK(dst)) {
267 SDL_LockSurface(dst);
270 for (y = 0; y < dst->h; y++) {
271 src_pointer = src_help_pointer;
273 for (x = 0; x < dst->w; x++) {
274 *dst_pointer = *src_pointer;
276 src_pointer += (*sx_ca >> 16);
280 src_help_pointer = (uint32_t*)((uint8_t*)src_help_pointer + (*sy_ca >> 16) * src->pitch);
283 if (SDL_MUSTLOCK(dst)) {
284 SDL_UnlockSurface(dst);
286 if (SDL_MUSTLOCK(src)) {
287 SDL_UnlockSurface(src);
295 SDL_Surface* getZoomedSurface(SDL_Surface * src,
double zoomx,
double zoomy) {
299 SDL_Surface *zoom_src;
300 SDL_Surface *zoom_dst;
301 int32_t dst_w =
static_cast<int32_t
>(round(src->w * zoomx));
302 int32_t dst_h =
static_cast<int32_t
>(round(src->h * zoomy));
309 if (src->format->Amask == 0) {
310 zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
313 SDL_BlitSurface(src, NULL, zoom_src, NULL);
318 zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_w, dst_h, 32,
319 zoom_src->format->Rmask, zoom_src->format->Gmask,
320 zoom_src->format->Bmask, zoom_src->format->Amask);
323 zoomSurface(zoom_src, zoom_dst);
328 bool nearlyEqual(
float a,
float b) {
329 return ABS(a - b) <= 0.00001;
332 void SDLImage::render(
const Rect& rect, uint8_t alpha, uint8_t
const* ) {
337 SDL_Surface* target = RenderBackend::instance()->getRenderTargetSurface();
338 assert(target != m_surface);
340 if (rect.
right() < 0 || rect.
x >
static_cast<int32_t
>(target->w) ||
341 rect.
bottom() < 0 || rect.
y >
static_cast<int32_t
>(target->h)) {
352 float scale_x =
static_cast<float>(rect.
w) / static_cast<float>(m_surface->w);
353 float scale_y =
static_cast<float>(rect.
h) / static_cast<float>(m_surface->h);
357 if (!nearlyEqual(scale_x, 1.0) && !nearlyEqual(scale_y, 1.0)) {
359 if(nearlyEqual(m_scale_x, scale_x) && nearlyEqual(m_scale_y, scale_y)) {
367 if (m_surface->format->Amask == 0) {
369 if (m_last_alpha != alpha) {
370 m_last_alpha = alpha;
371 SDL_SetAlpha(m_surface, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
374 SDL_BlitSurface(m_surface, 0, target, &r);
375 }
else if (equal && m_zoom_surface) {
376 SDL_BlitSurface(m_zoom_surface, 0, target, &r);
378 SDL_FreeSurface(m_zoom_surface);
379 m_zoom_surface = getZoomedSurface(m_surface, m_scale_x, m_scale_y);
380 SDL_BlitSurface(m_zoom_surface, 0, target, &r);
387 SDL_BlitSurfaceWithAlpha( m_surface, 0, target, &r, alpha );
388 }
else if (equal && m_zoom_surface) {
389 SDL_BlitSurfaceWithAlpha(m_zoom_surface, 0, target, &r, alpha );
391 SDL_FreeSurface(m_zoom_surface);
392 m_zoom_surface = getZoomedSurface(m_surface, m_scale_x, m_scale_y);
393 SDL_BlitSurfaceWithAlpha(m_zoom_surface, 0, target, &r, alpha );
397 SDL_BlitSurface(m_surface, 0, target, &r);
398 }
else if (equal && m_zoom_surface) {
399 SDL_BlitSurface(m_zoom_surface, 0, target, &r);
401 SDL_FreeSurface(m_zoom_surface);
402 m_zoom_surface = getZoomedSurface(m_surface, m_scale_x, m_scale_y);
403 SDL_BlitSurface(m_zoom_surface, 0, target, &r);
409 void SDLImage::finalize() {
414 SDL_Surface *old_surface = m_surface;
415 Uint32 key = SDL_MapRGB(m_surface->format, m_colorkey.r, m_colorkey.g, m_colorkey.b);
417 if (m_surface->format->Amask == 0) {
419 if (RenderBackend::instance()->isColorKeyEnabled()) {
420 SDL_SetColorKey(m_surface, SDL_SRCCOLORKEY, key);
423 m_surface = SDL_DisplayFormat(m_surface);
425 RenderBackendSDL* be =
static_cast<RenderBackendSDL*
>(RenderBackend::instance());
426 bool m_isalphaoptimized = be->isAlphaOptimizerEnabled();
427 if( m_isalphaoptimized ) {
428 m_surface = optimize(m_surface);
430 SDL_SetAlpha(m_surface, SDL_SRCALPHA, 255);
433 if (RenderBackend::instance()->isColorKeyEnabled()) {
434 SDL_SetColorKey(m_surface, SDL_SRCCOLORKEY, key);
437 m_surface = SDL_DisplayFormatAlpha(m_surface);
440 SDL_FreeSurface(old_surface);
443 SDL_Surface* SDLImage::optimize(SDL_Surface* src) {
454 int32_t transparent = 0;
456 int32_t semitransparent = 0;
457 int32_t alphasum = 0;
458 int32_t alphasquaresum = 0;
459 bool colors[(1 << 12)];
460 memset(colors, 0, (1 << 12) *
sizeof(
bool));
462 int32_t bpp = src->format->BytesPerPixel;
463 if(SDL_MUSTLOCK(src)) {
464 SDL_LockSurface(src);
470 for(int32_t y = 0;y < src->h;y++) {
471 for(int32_t x = 0;x < src->w;x++) {
472 Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
479 mapped = *(Uint16 *)pixel;
482 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
483 mapped |= pixel[0] << 16;
484 mapped |= pixel[1] << 8;
485 mapped |= pixel[2] << 0;
487 mapped |= pixel[0] << 0;
488 mapped |= pixel[1] << 8;
489 mapped |= pixel[2] << 16;
493 mapped = *(Uint32 *)pixel;
496 Uint8 red, green, blue, alpha;
497 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
500 }
else if (alpha > 240) {
503 alphasquaresum += alpha*alpha;
507 alphasquaresum += alpha*alpha;
511 colors[((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4)] =
true;
515 int32_t avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
516 int32_t alphavariance = 0;
518 if(SDL_MUSTLOCK(src)) {
519 SDL_UnlockSurface(src);
521 alphasquaresum /= (opaque + semitransparent) ? (opaque + semitransparent) : 1;
522 alphavariance = alphasquaresum - avgalpha*avgalpha;
523 if(semitransparent > ((transparent + opaque + semitransparent) / 8)
524 && alphavariance > 16) {
525 FL_DBG(_log, LMsg(
"sdlimage")
526 <<
"Trying to alpha-optimize image. FAILED: real alpha usage. "
527 <<
" alphavariance=" << alphavariance
528 <<
" total=" << (transparent + opaque + semitransparent)
529 <<
" semitransparent=" << semitransparent
530 <<
"(" << (
float(semitransparent)/(transparent + opaque + semitransparent))
532 return SDL_DisplayFormatAlpha(src);
536 int32_t keycolor = -1;
537 for(int32_t i = 0;i < (1 << 12);i++) {
544 FL_DBG(_log, LMsg(
"sdlimage") <<
"Trying to alpha-optimize image. FAILED: no free color");
545 return SDL_DisplayFormatAlpha(src);
548 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA) | SDL_SWSURFACE,
550 src->format->BitsPerPixel,
551 src->format->Rmask, src->format->Gmask,
552 src->format->Bmask, 0);
553 bpp = dst->format->BytesPerPixel;
555 Uint32 key = SDL_MapRGB(dst->format, m_colorkey.r, m_colorkey.g, m_colorkey.b);
558 if (!RenderBackend::instance()->isColorKeyEnabled()) {
559 key = SDL_MapRGB(dst->format,
560 (((keycolor & 0xf00) >> 4) | 0xf),
561 ((keycolor & 0xf0) | 0xf),
562 (((keycolor & 0xf) << 4) | 0xf));
565 if(SDL_MUSTLOCK(src)) {
566 SDL_LockSurface(src);
568 if(SDL_MUSTLOCK(dst)) {
569 SDL_LockSurface(dst);
571 for(int32_t y = 0;y < dst->h;y++) {
572 for(int32_t x = 0;x < dst->w;x++) {
573 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
574 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
581 mapped = *(Uint16 *)srcpixel;
584 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
585 mapped |= srcpixel[0] << 16;
586 mapped |= srcpixel[1] << 8;
587 mapped |= srcpixel[2] << 0;
589 mapped |= srcpixel[0] << 0;
590 mapped |= srcpixel[1] << 8;
591 mapped |= srcpixel[2] << 16;
595 mapped = *(Uint32 *)srcpixel;
598 Uint8 red, green, blue, alpha;
599 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
600 if(alpha < (avgalpha / 4)) {
603 mapped = SDL_MapRGB(dst->format, red, green, blue);
610 *(Uint16 *)dstpixel = mapped;
613 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
614 dstpixel[0] = (mapped >> 16) & 0xff;
615 dstpixel[1] = (mapped >> 8) & 0xff;
616 dstpixel[2] = (mapped >> 0) & 0xff;
618 dstpixel[0] = (mapped >> 0) & 0xff;
619 dstpixel[1] = (mapped >> 8) & 0xff;
620 dstpixel[2] = (mapped >> 16) & 0xff;
624 *(Uint32 *)dstpixel = mapped;
629 if(SDL_MUSTLOCK(dst)) {
630 SDL_UnlockSurface(dst);
632 if(SDL_MUSTLOCK(src)) {
633 SDL_UnlockSurface(src);
642 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, key);
643 SDL_Surface *convert = SDL_DisplayFormat(dst);
644 SDL_FreeSurface(dst);
645 FL_DBG(_log, LMsg(
"sdlimage ")
646 <<
"Trying to alpha-optimize image. SUCCESS: colorkey is " << key);
650 size_t SDLImage::getSize() {
652 if (m_zoom_surface) {
653 zoomSize = m_zoom_surface->h * m_zoom_surface->pitch;
656 zoomSize += m_surface->h * m_surface->pitch;
662 void SDLImage::useSharedImage(
const ImagePtr& shared,
const Rect& region) {
663 if(shared->getState() != IResource::RES_LOADED) {
666 SDL_Surface* src_surface = shared->getSurface();
667 SDL_Surface* surface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, region.
w, region.
h,
668 src_surface->format->BitsPerPixel, src_surface->format->Rmask, src_surface->format->Gmask,
669 src_surface->format->Bmask, src_surface->format->Amask);
671 SDL_SetAlpha(src_surface, 0, 0);
672 SDL_Rect srcrect = { region.
x, region.
y, region.
w, region.
h };
673 SDL_BlitSurface(src_surface, &srcrect, surface, NULL);
674 SDL_SetAlpha(src_surface, SDL_SRCALPHA, 0);
678 m_subimagerect = region;
679 m_atlas_img = shared;
680 m_atlas_name = shared->getName();
681 setState(IResource::RES_LOADED);
684 void SDLImage::forceLoadInternal() {
688 void SDLImage::validateShared() {
689 if (m_atlas_name.empty()) {
693 if (m_atlas_img->getState() == IResource::RES_NOT_LOADED ||
694 getState() == IResource::RES_NOT_LOADED) {
699 void SDLImage::load() {
700 if (!m_atlas_name.empty()) {
703 if (!ImageManager::instance()->exists(m_atlas_name)) {
704 ImagePtr newAtlas = ImageManager::instance()->create(m_atlas_name);
705 m_atlas_img = newAtlas;
707 useSharedImage(m_atlas_img , m_subimagerect);
713 void SDLImage::free() {
715 m_state = IResource::RES_NOT_LOADED;
void SDL_BlendRow_RGBA8_to_RGB8(const uint8_t *src, uint8_t *dst, uint32_t alpha, int32_t n)
void SDL_BlendRow_RGBA8_to_RGBA8(const uint8_t *src, uint8_t *dst, uint32_t alpha, int32_t n)
void SDL_BlendRow_RGBA4_to_RGB565(const uint8_t *src, uint8_t *dst, uint32_t alpha, int32_t n)
void SDL_BlendRow_RGBA8_to_RGB565(const uint8_t *src, uint8_t *dst, uint32_t alpha, int32_t n)