32 #include "util/log/logger.h"
33 #include "util/structures/rect.h"
34 #include "video/renderbackend.h"
36 #include "renderbackendsdl.h"
37 #include "sdlblendingfunctions.h"
41 static Logger _log(LM_VIDEO);
43 SDLImage::SDLImage(SDL_Surface* surface):
48 SDLImage::SDLImage(
const uint8_t* data,
unsigned int width,
unsigned int height):
49 Image(data, width, height) {
53 void SDLImage::resetSdlimage() {
56 m_isalphaoptimized =
false;
57 m_colorkey = RenderBackend::instance()->getColorKey();
60 m_zoom_surface = NULL;
63 SDLImage::~SDLImage() {
65 SDL_FreeSurface(m_zoom_surface);
69 void SDL_BlitSurfaceWithAlpha(
const SDL_Surface* src,
const SDL_Rect* srcRect,
70 SDL_Surface* dst, SDL_Rect* dstRect,
unsigned char alpha ) {
80 screenX = dst->clip_rect.x;
81 screenY = dst->clip_rect.y;
84 int width, height, tX, tY;
91 tX = src->clip_rect.x;
92 tY = src->clip_rect.y;
93 width = src->clip_rect.w;
94 height = src->clip_rect.h;
98 if( ( screenX >= ( dst->clip_rect.x + dst->clip_rect.w ) ) ||
99 ( screenY >= ( dst->clip_rect.y + dst->clip_rect.h ) ) ||
100 ( ( screenX + width ) <= dst->clip_rect.x ) ||
101 ( ( screenY + height ) <= dst->clip_rect.y ) ) {
105 if( screenX < dst->clip_rect.x ) {
106 int dX = dst->clip_rect.x - screenX;
112 if( ( screenX + width ) > ( dst->clip_rect.x + dst->clip_rect.w ) ) {
113 int dX = ( screenX + width ) - ( dst->clip_rect.x + dst->clip_rect.w );
117 if( screenY < dst->clip_rect.y ) {
118 int dY = dst->clip_rect.y - screenY;
124 if( ( screenY + height ) > ( dst->clip_rect.y + dst->clip_rect.h ) ) {
125 int dY = ( screenY + height ) - ( dst->clip_rect.y + dst->clip_rect.h );
129 if( ( 0 >= height ) || ( 0 >= width ) ) {
133 SDL_LockSurface( dst );
135 unsigned char* srcData =
reinterpret_cast< unsigned char*
> ( src->pixels );
136 unsigned char* dstData =
reinterpret_cast< unsigned char*
> ( dst->pixels );
139 srcData += tY * src->pitch + tX * src->format->BytesPerPixel;
140 dstData += screenY * dst->pitch + screenX * dst->format->BytesPerPixel;
142 switch( src->format->BitsPerPixel ) {
144 switch( dst->format->BitsPerPixel ) {
146 if( 0xFFFF == ( dst->format->Rmask | dst->format->Gmask | dst->format->Bmask ) ) {
147 for(
int y = height; y > 0; --y ) {
149 srcData += src->pitch;
150 dstData += dst->pitch;
157 for(
int y = height; y > 0; --y ) {
159 srcData += src->pitch;
160 dstData += dst->pitch;
166 for(
int y = height; y > 0; --y ) {
168 srcData += src->pitch;
169 dstData += dst->pitch;
181 if( 0x000F == src->format->Amask ) {
182 if( ( 16 == dst->format->BitsPerPixel ) &&
183 ( 0xFFFF == ( dst->format->Rmask | dst->format->Gmask | dst->format->Bmask ) ) ) {
184 for(
int y = height; y > 0; --y ) {
186 srcData += src->pitch;
187 dstData += dst->pitch;
198 SDL_UnlockSurface( dst );
201 void zoomSurface(SDL_Surface* src, SDL_Surface* dst) {
202 SDL_Color* src_pointer = (SDL_Color*)src->pixels;
203 SDL_Color* src_help_pointer = src_pointer;
204 SDL_Color* dst_pointer = (SDL_Color*)dst->pixels;
206 int x, y, *sx_ca, *sy_ca;
207 int dst_gap = dst->pitch - dst->w * dst->format->BytesPerPixel;
208 int sx =
static_cast<int>(0xffff * src->w / dst->w);
209 int sy =
static_cast<int>(0xffff * src->h / dst->h);
214 int* sx_a = (
int*)malloc((dst->w + 1) *
sizeof(Uint32));
219 for (x = 0; x <= dst->w; x++) {
226 int* sy_a = (
int*)malloc((dst->h + 1) *
sizeof(Uint32));
232 for (y = 0; y <= dst->h; y++) {
243 if(SDL_MUSTLOCK(src))
244 SDL_LockSurface(src);
245 if(SDL_MUSTLOCK(dst))
246 SDL_LockSurface(dst);
248 for (y = 0; y < dst->h; y++) {
249 src_pointer = src_help_pointer;
251 for (x = 0; x < dst->w; x++) {
252 *dst_pointer = *src_pointer;
254 src_pointer += (*sx_ca >> 16);
258 src_help_pointer = (SDL_Color*)((Uint8*)src_help_pointer + (*sy_ca >> 16) * src->pitch);
259 dst_pointer = (SDL_Color*)((Uint8*)dst_pointer + dst_gap);
262 if(SDL_MUSTLOCK(dst))
263 SDL_UnlockSurface(dst);
264 if(SDL_MUSTLOCK(src))
265 SDL_UnlockSurface(src);
272 SDL_Surface* getZoomedSurface(SDL_Surface * src,
double zoomx,
double zoomy) {
276 SDL_Surface *zoom_src;
277 SDL_Surface *zoom_dst;
278 int dst_w =
static_cast<int>(round(src->w * zoomx));
279 int dst_h =
static_cast<int>(round(src->h * zoomy));
286 if (src->format->Amask == 0) {
287 zoom_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
290 SDL_BlitSurface(src, NULL, zoom_src, NULL);
295 zoom_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_w, dst_h, 32,
296 zoom_src->format->Rmask, zoom_src->format->Gmask,
297 zoom_src->format->Bmask, zoom_src->format->Amask);
300 zoomSurface(zoom_src, zoom_dst);
305 bool nearlyEqual(
float a,
float b) {
306 return ABS(a - b) <= 0.00001;
309 void SDLImage::render(
const Rect& rect, SDL_Surface* screen,
unsigned char alpha) {
314 if (rect.
right() < 0 || rect.
x >
static_cast<int>(screen->w) || rect.
bottom() < 0 || rect.
y >
static_cast<int>(screen->h)) {
319 SDL_Surface* surface = screen;
326 float scale_x =
static_cast<float>(rect.
w) / static_cast<float>(m_surface->w);
327 float scale_y =
static_cast<float>(rect.
h) / static_cast<float>(m_surface->h);
331 if (!nearlyEqual(scale_x, 1.0) && !nearlyEqual(scale_y, 1.0)) {
333 if(nearlyEqual(m_scale_x, scale_x) && nearlyEqual(m_scale_y, scale_y)) {
341 if (m_surface->format->Amask == 0) {
343 if (m_last_alpha != alpha) {
344 m_last_alpha = alpha;
345 SDL_SetAlpha(m_surface, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
348 SDL_BlitSurface(m_surface, 0, surface, &r);
349 }
else if (equal && m_zoom_surface) {
350 SDL_BlitSurface(m_zoom_surface, 0, surface, &r);
352 SDL_FreeSurface(m_zoom_surface);
353 m_zoom_surface = getZoomedSurface(m_surface, m_scale_x, m_scale_y);
354 SDL_BlitSurface(m_zoom_surface, 0, surface, &r);
361 SDL_BlitSurfaceWithAlpha( m_surface, 0, surface, &r, alpha );
362 }
else if (equal && m_zoom_surface) {
363 SDL_BlitSurfaceWithAlpha(m_zoom_surface, 0, surface, &r, alpha );
365 SDL_FreeSurface(m_zoom_surface);
366 m_zoom_surface = getZoomedSurface(m_surface, m_scale_x, m_scale_y);
367 SDL_BlitSurfaceWithAlpha(m_zoom_surface, 0, surface, &r, alpha );
371 SDL_BlitSurface(m_surface, 0, surface, &r);
372 }
else if (equal && m_zoom_surface) {
373 SDL_BlitSurface(m_zoom_surface, 0, surface, &r);
375 SDL_FreeSurface(m_zoom_surface);
376 m_zoom_surface = getZoomedSurface(m_surface, m_scale_x, m_scale_y);
377 SDL_BlitSurface(m_zoom_surface, 0, surface, &r);
383 void SDLImage::finalize() {
388 SDL_Surface *old_surface = m_surface;
389 Uint32 key = SDL_MapRGB(m_surface->format, m_colorkey.r, m_colorkey.g, m_colorkey.b);
391 if (m_surface->format->Amask == 0) {
393 if (RenderBackend::instance()->isColorKeyEnabled()) {
394 SDL_SetColorKey(m_surface, SDL_SRCCOLORKEY, key);
397 m_surface = SDL_DisplayFormat(m_surface);
399 RenderBackendSDL* be =
static_cast<RenderBackendSDL*
>(RenderBackend::instance());
400 m_isalphaoptimized = be->isAlphaOptimizerEnabled();
401 if( m_isalphaoptimized ) {
402 m_surface = optimize(m_surface);
404 SDL_SetAlpha(m_surface, SDL_SRCALPHA, 255);
407 if (RenderBackend::instance()->isColorKeyEnabled()) {
408 SDL_SetColorKey(m_surface, SDL_SRCCOLORKEY, key);
411 m_surface = SDL_DisplayFormatAlpha(m_surface);
414 SDL_FreeSurface(old_surface);
417 SDL_Surface* SDLImage::optimize(SDL_Surface* src) {
430 int semitransparent = 0;
432 int alphasquaresum = 0;
433 bool colors[(1 << 12)];
434 memset(colors, 0, (1 << 12) *
sizeof(
bool));
436 int bpp = src->format->BytesPerPixel;
437 if(SDL_MUSTLOCK(src)) {
438 SDL_LockSurface(src);
444 for(
int y = 0;y < src->h;y++) {
445 for(
int x = 0;x < src->w;x++) {
446 Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
453 mapped = *(Uint16 *)pixel;
456 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
457 mapped |= pixel[0] << 16;
458 mapped |= pixel[1] << 8;
459 mapped |= pixel[2] << 0;
461 mapped |= pixel[0] << 0;
462 mapped |= pixel[1] << 8;
463 mapped |= pixel[2] << 16;
467 mapped = *(Uint32 *)pixel;
470 Uint8 red, green, blue, alpha;
471 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
474 }
else if (alpha > 240) {
477 alphasquaresum += alpha*alpha;
481 alphasquaresum += alpha*alpha;
485 colors[((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4)] =
true;
489 int avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
490 int alphavariance = 0;
492 if(SDL_MUSTLOCK(src)) {
493 SDL_UnlockSurface(src);
495 alphasquaresum /= (opaque + semitransparent) ? (opaque + semitransparent) : 1;
496 alphavariance = alphasquaresum - avgalpha*avgalpha;
497 if(semitransparent > ((transparent + opaque + semitransparent) / 8)
498 && alphavariance > 16) {
499 FL_DBG(_log, LMsg(
"sdlimage")
500 <<
"Trying to alpha-optimize image. FAILED: real alpha usage. "
501 <<
" alphavariance=" << alphavariance
502 <<
" total=" << (transparent + opaque + semitransparent)
503 <<
" semitransparent=" << semitransparent
504 <<
"(" << (
float(semitransparent)/(transparent + opaque + semitransparent))
506 return SDL_DisplayFormatAlpha(src);
511 for(
int i = 0;i < (1 << 12);i++) {
518 FL_DBG(_log, LMsg(
"sdlimage") <<
"Trying to alpha-optimize image. FAILED: no free color");
519 return SDL_DisplayFormatAlpha(src);
522 SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA) | SDL_SWSURFACE,
524 src->format->BitsPerPixel,
525 src->format->Rmask, src->format->Gmask,
526 src->format->Bmask, 0);
527 bpp = dst->format->BytesPerPixel;
529 Uint32 key = SDL_MapRGB(dst->format, m_colorkey.r, m_colorkey.g, m_colorkey.b);
532 if (!RenderBackend::instance()->isColorKeyEnabled()) {
533 key = SDL_MapRGB(dst->format,
534 (((keycolor & 0xf00) >> 4) | 0xf),
535 ((keycolor & 0xf0) | 0xf),
536 (((keycolor & 0xf) << 4) | 0xf));
539 if(SDL_MUSTLOCK(src)) {
540 SDL_LockSurface(src);
542 if(SDL_MUSTLOCK(dst)) {
543 SDL_LockSurface(dst);
545 for(
int y = 0;y < dst->h;y++) {
546 for(
int x = 0;x < dst->w;x++) {
547 Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
548 Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
555 mapped = *(Uint16 *)srcpixel;
558 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
559 mapped |= srcpixel[0] << 16;
560 mapped |= srcpixel[1] << 8;
561 mapped |= srcpixel[2] << 0;
563 mapped |= srcpixel[0] << 0;
564 mapped |= srcpixel[1] << 8;
565 mapped |= srcpixel[2] << 16;
569 mapped = *(Uint32 *)srcpixel;
572 Uint8 red, green, blue, alpha;
573 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
574 if(alpha < (avgalpha / 4)) {
577 mapped = SDL_MapRGB(dst->format, red, green, blue);
584 *(Uint16 *)dstpixel = mapped;
587 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
588 dstpixel[0] = (mapped >> 16) & 0xff;
589 dstpixel[1] = (mapped >> 8) & 0xff;
590 dstpixel[2] = (mapped >> 0) & 0xff;
592 dstpixel[0] = (mapped >> 0) & 0xff;
593 dstpixel[1] = (mapped >> 8) & 0xff;
594 dstpixel[2] = (mapped >> 16) & 0xff;
598 *(Uint32 *)dstpixel = mapped;
603 if(SDL_MUSTLOCK(dst)) {
604 SDL_UnlockSurface(dst);
606 if(SDL_MUSTLOCK(src)) {
607 SDL_UnlockSurface(src);
616 SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, key);
617 SDL_Surface *convert = SDL_DisplayFormat(dst);
618 SDL_FreeSurface(dst);
619 FL_DBG(_log, LMsg(
"sdlimage ")
620 <<
"Trying to alpha-optimize image. SUCCESS: colorkey is " << key);
624 bool SDLImage::putPixel(
int x,
int y,
int r,
int g,
int b,
int a) {
625 if ((x < 0) || (x >= m_surface->w) || (y < 0) || (y >= m_surface->h)) {
629 int bpp = m_surface->format->BytesPerPixel;
630 SDL_LockSurface(m_surface);
631 Uint8* p = (Uint8*)m_surface->pixels + y * m_surface->pitch + x * bpp;
632 Uint32 pixel = SDL_MapRGB(m_surface->format, r, g, b);
640 *(Uint16 *)p = pixel;
644 if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
645 p[0] = (pixel >> 16) & 0xff;
646 p[1] = (pixel >> 8) & 0xff;
651 p[1] = (pixel >> 8) & 0xff;
652 p[2] = (pixel >> 16) & 0xff;
657 *(Uint32 *)p = pixel;
660 SDL_UnlockSurface(m_surface);
664 void SDLImage::drawLine(
const Point& p1,
const Point& p2,
int r,
int g,
int b,
int a) {
670 int dx = ABS(x2 - x1);
671 int dy = ABS(y2 - y1);
690 for (
int x = x1; x <= x2; x++) {
691 putPixel(x, y, r, g, b, a);
703 for (
int x = x1; x <= x2; x++) {
704 putPixel(x, y, r, g, b, a);
731 for (
int y = y1; y <= y2; y++) {
732 putPixel(x, y, r, g, b, a);
744 for (
int y = y1; y <= y2; y++) {
745 putPixel(x, y, r, g, b, a);
756 void SDLImage::drawTriangle(
const Point& p1,
const Point& p2,
const Point& p3,
int r,
int g,
int b,
int a) {
757 drawLine(p1, p2, r, g, b, a);
758 drawLine(p2, p3, r, g, b, a);
759 drawLine(p3, p1, r, g, b, a);
762 void SDLImage::drawRectangle(
const Point& p, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
763 Point p1, p2, p3, p4;
774 drawLine(p1, p2, r, g, b, a);
775 drawLine(p2, p3, r, g, b, a);
776 drawLine(p3, p4, r, g, b, a);
777 drawLine(p4, p1, r, g, b, a);
780 void SDLImage::fillRectangle(
const Point& p, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
787 Uint32 color = SDL_MapRGBA(m_surface->format, r, g, b, a);
788 SDL_FillRect(m_surface, &rect, color);
791 void SDLImage::drawQuad(
const Point& p1,
const Point& p2,
const Point& p3,
const Point& p4,
int r,
int g,
int b,
int a) {
792 drawLine(p1, p2, r, g, b, a);
793 drawLine(p2, p3, r, g, b, a);
794 drawLine(p3, p4, r, g, b, a);
795 drawLine(p4, p1, r, g, b, a);
798 void SDLImage::drawVertex(
const Point& p,
const uint8_t size,
int r,
int g,
int b,
int a){
799 Point p1 = Point(p.x-size, p.y+size);
800 Point p2 = Point(p.x+size, p.y+size);
801 Point p3 = Point(p.x+size, p.y-size);
802 Point p4 = Point(p.x-size, p.y-size);
804 drawLine(p1, p2, r, g, b, a);
805 drawLine(p2, p3, r, g, b, a);
806 drawLine(p3, p4, r, g, b, a);
807 drawLine(p4, p1, r, g, b, a);
810 void SDLImage::drawLightPrimitive(
const Point& p, uint8_t intensity,
float radius,
int subdivisions,
float xstretch,
float ystretch, uint8_t red, uint8_t green, uint8_t blue) {
813 void SDLImage::saveImage(
const std::string& filename) {
815 const unsigned int swidth = getWidth();
816 const unsigned int sheight = getHeight();
817 SDL_Surface *surface = NULL;
819 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, swidth,
821 RMASK, GMASK, BMASK, 0);
823 if(surface == NULL) {
827 SDL_BlitSurface(m_surface, NULL, surface, NULL);
829 saveAsPng(filename, *surface);
830 SDL_FreeSurface(surface);
834 void SDLImage::setClipArea(
const Rect& cliparea,
bool clear) {
840 SDL_SetClipRect(m_surface, &rect);
842 SDL_FillRect(m_surface, &rect, 0x00);