From 2e3aec48adc591d6496823f234631332e2f4af59 Mon Sep 17 00:00:00 2001 From: Einar Johan Trøan Sømåen Date: Tue, 12 Jun 2012 19:49:58 +0200 Subject: WINTERMUTE: Optimize the blitting in BSurfaceSDL by adding scale-caching, and alpha-less blits for completely opaque surfaces. --- engines/wintermute/Base/BRenderSDL.cpp | 45 ++++++++++++++++++++ engines/wintermute/Base/BRenderSDL.h | 2 +- engines/wintermute/Base/BSurfaceSDL.cpp | 73 ++++++++++++++++++++++++++++++--- engines/wintermute/Base/BSurfaceSDL.h | 4 +- 4 files changed, 116 insertions(+), 8 deletions(-) diff --git a/engines/wintermute/Base/BRenderSDL.cpp b/engines/wintermute/Base/BRenderSDL.cpp index 2457de8f51..14c2d2a6e4 100644 --- a/engines/wintermute/Base/BRenderSDL.cpp +++ b/engines/wintermute/Base/BRenderSDL.cpp @@ -301,6 +301,51 @@ void CBRenderSDL::drawFromSurface(Graphics::Surface *surf, Common::Rect *srcRect mirror |= TransparentSurface::FLIP_H; src.blit(*_renderSurface, dstRect->left, dstRect->top, mirror, srcRect,BS_ARGB(a, r, g, b), dstRect->width(), dstRect->height() ); } + +void CBRenderSDL::drawOpaqueFromSurface(Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, byte r, byte g, byte b, byte a, bool mirrorX, bool mirrorY) { + TransparentSurface src(*surf, false); + TransparentSurface *img = NULL; + TransparentSurface *imgScaled = NULL; + byte *savedPixels = NULL; + if ((dstRect->width() != surf->w) || (dstRect->height() != surf->h)) { + img = imgScaled = src.scale(dstRect->width(), dstRect->height()); + savedPixels = (byte *)img->pixels; + } else { + img = &src; + } + + int posX = dstRect->left; + int posY = dstRect->top; + + // Handle off-screen clipping + if (posY < 0) { + img->h = MAX(0, (int)img->h - -posY); + img->pixels = (byte *)img->pixels + img->pitch * -posY; + posY = 0; + } + + if (posX < 0) { + img->w = MAX(0, (int)img->w - -posX); + img->pixels = (byte *)img->pixels + (-posX * 4); + posX = 0; + } + + img->w = CLIP((int)img->w, 0, (int)MAX((int)_renderSurface->w - posX, 0)); + img->h = CLIP((int)img->h, 0, (int)MAX((int)_renderSurface->h - posY, 0)); + + for (int i = 0; i < img->h; i++) { + void *destPtr = _renderSurface->getBasePtr(posX, posY + i); + void *srcPtr = img->getBasePtr(0, i); + memcpy(destPtr, srcPtr, _renderSurface->format.bytesPerPixel * img->w); + } + + if (imgScaled) { + imgScaled->pixels = savedPixels; + imgScaled->free(); + delete imgScaled; + imgScaled = NULL; + } +} ////////////////////////////////////////////////////////////////////////// HRESULT CBRenderSDL::DrawLine(int X1, int Y1, int X2, int Y2, uint32 Color) { static bool hasWarned = false; diff --git a/engines/wintermute/Base/BRenderSDL.h b/engines/wintermute/Base/BRenderSDL.h index ccdca89be5..efcdc5cc4a 100644 --- a/engines/wintermute/Base/BRenderSDL.h +++ b/engines/wintermute/Base/BRenderSDL.h @@ -59,7 +59,7 @@ public: CBImage *TakeScreenshot(); void drawFromSurface(Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRest, byte r = 255, byte g = 255, byte b = 255, byte a = 255, bool mirrorX = false, bool mirrorY = false); - + void drawOpaqueFromSurface(Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRest, byte r = 255, byte g = 255, byte b = 255, byte a = 255, bool mirrorX = false, bool mirrorY = false); HRESULT SetViewport(int left, int top, int right, int bottom); diff --git a/engines/wintermute/Base/BSurfaceSDL.cpp b/engines/wintermute/Base/BSurfaceSDL.cpp index 200a34166a..025019ae8c 100644 --- a/engines/wintermute/Base/BSurfaceSDL.cpp +++ b/engines/wintermute/Base/BSurfaceSDL.cpp @@ -47,6 +47,7 @@ namespace WinterMute { ////////////////////////////////////////////////////////////////////////// CBSurfaceSDL::CBSurfaceSDL(CBGame *inGame) : CBSurface(inGame) { _surface = new Graphics::Surface(); + _scaledSurface = NULL; _alphaMask = NULL; _lockPixels = NULL; @@ -56,14 +57,41 @@ CBSurfaceSDL::CBSurfaceSDL(CBGame *inGame) : CBSurface(inGame) { ////////////////////////////////////////////////////////////////////////// CBSurfaceSDL::~CBSurfaceSDL() { //TODO - delete _surface; + if (_surface) { + _surface->free(); + delete _surface; + _surface = NULL; + } + + if (_scaledSurface) { + _scaledSurface->free(); + delete _scaledSurface; + _scaledSurface = NULL; + } #if 0 if (_texture) SDL_DestroyTexture(_texture); delete[] _alphaMask; _alphaMask = NULL; - - Game->AddMem(-_width * _height * 4); #endif + Game->AddMem(-_width * _height * 4); +} + +bool hasTransparency(Graphics::Surface *surf) { + if (surf->format.bytesPerPixel != 4) { + warning("hasTransparency:: non 32 bpp surface passed as argument"); + return false; + } + uint8 r,g,b,a; + for (int i = 0; i < surf->h; i++) { + for (int j = 0; j < surf->w; j++) { + uint32 pix = *(uint32*)surf->getBasePtr(j, i); + surf->format.colorToARGB(pix, a, r, g, b); + if (a != 255) { + return true; + } + } + } + return false; } ////////////////////////////////////////////////////////////////////////// @@ -154,6 +182,8 @@ HRESULT CBSurfaceSDL::create(const char *filename, bool default_ck, byte ck_red, _surface = new Graphics::Surface(); _surface->copyFrom(*surface); } + + _hasAlpha = hasTransparency(_surface); //SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best"); //TODO //_texture = SdlUtil::CreateTextureFromSurface(renderer->GetSdlRenderer(), surf); @@ -293,9 +323,9 @@ HRESULT CBSurfaceSDL::create(int width, int height) { #endif _width = width; _height = height; -#if 0 + Game->AddMem(_width * _height * 4); -#endif + _valid = true; return S_OK; @@ -492,7 +522,38 @@ HRESULT CBSurfaceSDL::drawSprite(int x, int y, RECT *Rect, float ZoomX, float Zo position.left += offsetX; position.top += offsetY; - renderer->drawFromSurface(_surface, &srcRect, &position, r, g, b, a, mirrorX, mirrorY); + // TODO: This actually requires us to have the SAME source-offsets every time, + // But no checking is in place for that yet. + Graphics::Surface drawSrc; + drawSrc.w = position.width(); + drawSrc.h = position.height(); + drawSrc.format = _surface->format; + + if (position.width() != srcRect.width() || position.height() != srcRect.height()) { + if (_scaledSurface && position.width() == _scaledSurface->w && position.height() == _scaledSurface->h) { + drawSrc.pixels = _scaledSurface->pixels; + drawSrc.pitch = _scaledSurface->pitch; + } else { + delete _scaledSurface; + TransparentSurface src(*_surface, false); + _scaledSurface = src.scale(position.width(), position.height()); + drawSrc.pixels = _scaledSurface->pixels; + drawSrc.pitch = _scaledSurface->pitch; + } + } else { // No scaling + drawSrc.pitch = _surface->pitch; + drawSrc.pixels = &((char *)_surface->pixels)[srcRect.top * _surface->pitch + srcRect.left * 4]; + } + srcRect.left = 0; + srcRect.top = 0; + srcRect.setWidth(drawSrc.w); + srcRect.setHeight(drawSrc.h); + + if (_hasAlpha && !AlphaDisable) { + renderer->drawFromSurface(&drawSrc, &srcRect, &position, r, g, b, a, mirrorX, mirrorY); + } else { + renderer->drawOpaqueFromSurface(&drawSrc, &srcRect, &position, r, g, b, a, mirrorX, mirrorY); + } #if 0 SDL_RenderCopy(renderer->GetSdlRenderer(), _texture, &srcRect, &position); #endif diff --git a/engines/wintermute/Base/BSurfaceSDL.h b/engines/wintermute/Base/BSurfaceSDL.h index 961d7e0cdd..34d25b43b5 100644 --- a/engines/wintermute/Base/BSurfaceSDL.h +++ b/engines/wintermute/Base/BSurfaceSDL.h @@ -34,7 +34,7 @@ #include "graphics/surface.h" namespace WinterMute { - +class TransparentSurface; class CBSurfaceSDL : public CBSurface { public: CBSurfaceSDL(CBGame *inGame); @@ -78,11 +78,13 @@ public: private: // SDL_Texture *_texture; Graphics::Surface *_surface; + TransparentSurface *_scaledSurface; HRESULT drawSprite(int x, int y, RECT *Rect, float zoomX, float zoomY, uint32 alpha, bool alphaDisable, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX = 0, int offsetY = 0); void genAlphaMask(Graphics::Surface *surface); uint32 getPixel(Graphics::Surface *surface, int x, int y); + bool _hasAlpha; void *_lockPixels; int _lockPitch; byte *_alphaMask; -- cgit v1.2.3