From 0a0b2f397b95e6e219f2ac8c5b79bbe6300ef432 Mon Sep 17 00:00:00 2001 From: johndoe123 Date: Wed, 17 Apr 2013 12:41:49 +0200 Subject: SWORD25: Optimize graphics drawing code --- engines/sword25/gfx/animation.cpp | 8 +- engines/sword25/gfx/animation.h | 2 +- engines/sword25/gfx/bitmapresource.h | 10 +- engines/sword25/gfx/dynamicbitmap.cpp | 16 +- engines/sword25/gfx/dynamicbitmap.h | 2 +- engines/sword25/gfx/graphicengine.cpp | 1 - engines/sword25/gfx/image/image.h | 7 +- engines/sword25/gfx/image/renderedimage.cpp | 321 ++++++++++++++++------------ engines/sword25/gfx/image/renderedimage.h | 8 +- engines/sword25/gfx/image/swimage.cpp | 3 +- engines/sword25/gfx/image/swimage.h | 3 +- engines/sword25/gfx/image/vectorimage.cpp | 5 +- engines/sword25/gfx/image/vectorimage.h | 3 +- engines/sword25/gfx/microtiles.cpp | 164 ++++++++++++++ engines/sword25/gfx/microtiles.h | 65 ++++++ engines/sword25/gfx/panel.cpp | 14 +- engines/sword25/gfx/panel.h | 2 +- engines/sword25/gfx/renderobject.cpp | 43 +++- engines/sword25/gfx/renderobject.h | 30 ++- engines/sword25/gfx/renderobjectmanager.cpp | 77 ++++++- engines/sword25/gfx/renderobjectmanager.h | 20 ++ engines/sword25/gfx/rootrenderobject.h | 2 +- engines/sword25/gfx/staticbitmap.cpp | 10 +- engines/sword25/gfx/staticbitmap.h | 2 +- engines/sword25/gfx/text.cpp | 4 +- engines/sword25/gfx/text.h | 2 +- 26 files changed, 645 insertions(+), 179 deletions(-) create mode 100644 engines/sword25/gfx/microtiles.cpp create mode 100644 engines/sword25/gfx/microtiles.h (limited to 'engines/sword25/gfx') diff --git a/engines/sword25/gfx/animation.cpp b/engines/sword25/gfx/animation.cpp index 22a4dfb71b..1660c393c0 100644 --- a/engines/sword25/gfx/animation.cpp +++ b/engines/sword25/gfx/animation.cpp @@ -170,7 +170,7 @@ void Animation::setFrame(uint nr) { forceRefresh(); } -bool Animation::doRender() { +bool Animation::doRender(RectangleList *updateRects) { AnimationDescription *animationDescriptionPtr = getAnimationDescription(); assert(animationDescriptionPtr); assert(_currentFrame < animationDescriptionPtr->getFrameCount()); @@ -191,12 +191,14 @@ bool Animation::doRender() { result = pBitmapResource->blit(_absoluteX, _absoluteY, (animationDescriptionPtr->getFrame(_currentFrame).flipV ? BitmapResource::FLIP_V : 0) | (animationDescriptionPtr->getFrame(_currentFrame).flipH ? BitmapResource::FLIP_H : 0), - 0, _modulationColor, _width, _height); + 0, _modulationColor, _width, _height, + updateRects); } else { result = pBitmapResource->blit(_absoluteX, _absoluteY, (animationDescriptionPtr->getFrame(_currentFrame).flipV ? BitmapResource::FLIP_V : 0) | (animationDescriptionPtr->getFrame(_currentFrame).flipH ? BitmapResource::FLIP_H : 0), - 0, _modulationColor, -1, -1); + 0, _modulationColor, -1, -1, + updateRects); } // Resource freigeben diff --git a/engines/sword25/gfx/animation.h b/engines/sword25/gfx/animation.h index 60070be214..44255e3b64 100644 --- a/engines/sword25/gfx/animation.h +++ b/engines/sword25/gfx/animation.h @@ -151,7 +151,7 @@ public: void setCallbacks(); protected: - virtual bool doRender(); + virtual bool doRender(RectangleList *updateRects); private: enum Direction { diff --git a/engines/sword25/gfx/bitmapresource.h b/engines/sword25/gfx/bitmapresource.h index f1278f3885..72b8a3ea1f 100644 --- a/engines/sword25/gfx/bitmapresource.h +++ b/engines/sword25/gfx/bitmapresource.h @@ -123,9 +123,10 @@ public: int flipping = FLIP_NONE, Common::Rect *pSrcPartRect = NULL, uint color = BS_ARGB(255, 255, 255, 255), - int width = -1, int height = -1) { + int width = -1, int height = -1, + RectangleList *updateRects = 0) { assert(_pImage); - return _pImage->blit(posX, posY, flipping, pSrcPartRect, color, width, height); + return _pImage->blit(posX, posY, flipping, pSrcPartRect, color, width, height, updateRects); } /** @@ -202,6 +203,11 @@ public: return _pImage->isColorModulationAllowed(); } + bool isSolid() { + assert(_pImage); + return _pImage->isSolid(); + } + private: Image *_pImage; }; diff --git a/engines/sword25/gfx/dynamicbitmap.cpp b/engines/sword25/gfx/dynamicbitmap.cpp index 137d943575..aef04fe1ef 100644 --- a/engines/sword25/gfx/dynamicbitmap.cpp +++ b/engines/sword25/gfx/dynamicbitmap.cpp @@ -53,10 +53,13 @@ DynamicBitmap::DynamicBitmap(InputPersistenceBlock &reader, RenderObjectPtrsetIsTransparent(false); + _isSolid = true; + return result; } @@ -70,10 +73,12 @@ uint DynamicBitmap::getPixel(int x, int y) const { return _image->getPixel(x, y); } -bool DynamicBitmap::doRender() { +bool DynamicBitmap::doRender(RectangleList *updateRects) { // Get the frame buffer object GraphicEngine *pGfx = Kernel::getInstance()->getGfx(); assert(pGfx); + + //return true;//DEBUG // Draw the bitmap bool result; @@ -85,7 +90,8 @@ bool DynamicBitmap::doRender() { result = _image->blit(_absoluteX, _absoluteY, (_flipV ? BitmapResource::FLIP_V : 0) | (_flipH ? BitmapResource::FLIP_H : 0), - 0, _modulationColor, -1, -1); + 0, _modulationColor, -1, -1, + updateRects); #else // WIP: A bit faster code @@ -103,13 +109,15 @@ bool DynamicBitmap::doRender() { result = _image->blit(_absoluteX, _absoluteY, (_flipV ? BitmapResource::FLIP_V : 0) | (_flipH ? BitmapResource::FLIP_H : 0), - 0, _modulationColor, _width, _height); + 0, _modulationColor, _width, _height, + updateRects); } return result; } bool DynamicBitmap::setContent(const byte *pixeldata, uint size, uint offset, uint stride) { + ++_version; // Update version to display the new video image return _image->setContent(pixeldata, size, offset, stride); } diff --git a/engines/sword25/gfx/dynamicbitmap.h b/engines/sword25/gfx/dynamicbitmap.h index 35ed9a9341..ed53776577 100644 --- a/engines/sword25/gfx/dynamicbitmap.h +++ b/engines/sword25/gfx/dynamicbitmap.h @@ -59,7 +59,7 @@ public: virtual bool unpersist(InputPersistenceBlock &reader); protected: - virtual bool doRender(); + virtual bool doRender(RectangleList *updateRects); private: DynamicBitmap(RenderObjectPtr parentPtr, uint width, uint height); diff --git a/engines/sword25/gfx/graphicengine.cpp b/engines/sword25/gfx/graphicengine.cpp index 216d801f3e..bfdb51481b 100644 --- a/engines/sword25/gfx/graphicengine.cpp +++ b/engines/sword25/gfx/graphicengine.cpp @@ -216,7 +216,6 @@ bool GraphicEngine::fill(const Common::Rect *fillRectPtr, uint color) { } } - g_system->copyRectToScreen(_backSurface.getBasePtr(rect.left, rect.top), _backSurface.pitch, rect.left, rect.top, rect.width(), rect.height()); } return true; diff --git a/engines/sword25/gfx/image/image.h b/engines/sword25/gfx/image/image.h index f1934656da..9d7fc43251 100644 --- a/engines/sword25/gfx/image/image.h +++ b/engines/sword25/gfx/image/image.h @@ -46,6 +46,8 @@ namespace Sword25 { +class RectangleList; + class Image { public: virtual ~Image() {} @@ -129,7 +131,8 @@ public: int flipping = FLIP_NONE, Common::Rect *pPartRect = NULL, uint color = BS_ARGB(255, 255, 255, 255), - int width = -1, int height = -1) = 0; + int width = -1, int height = -1, + RectangleList *updateRects = 0) = 0; /** @brief fills a rectangular section of the image with a color. @@ -202,6 +205,8 @@ public: @brief Returns true, if the content of the BS_Image is allowed to be replaced by call of SetContent(). */ virtual bool isSetContentAllowed() const = 0; + + virtual bool isSolid() const { return false; } //@} }; diff --git a/engines/sword25/gfx/image/renderedimage.cpp b/engines/sword25/gfx/image/renderedimage.cpp index 27ee4ef182..1de7a04471 100644 --- a/engines/sword25/gfx/image/renderedimage.cpp +++ b/engines/sword25/gfx/image/renderedimage.cpp @@ -38,6 +38,8 @@ #include "sword25/gfx/image/imgloader.h" #include "sword25/gfx/image/renderedimage.h" +#include "sword25/gfx/renderobjectmanager.h" + #include "common/system.h" namespace Sword25 { @@ -99,7 +101,8 @@ static byte *readSavegameThumbnail(const Common::String &filename, uint &fileSiz RenderedImage::RenderedImage(const Common::String &filename, bool &result) : _data(0), _width(0), - _height(0) { + _height(0), + _isTransparent(true) { result = false; PackageManager *pPackage = Kernel::getInstance()->getPackage(); @@ -142,6 +145,8 @@ RenderedImage::RenderedImage(const Common::String &filename, bool &result) : _doCleanup = true; + checkForTransparency(); + return; } @@ -149,7 +154,8 @@ RenderedImage::RenderedImage(const Common::String &filename, bool &result) : RenderedImage::RenderedImage(uint width, uint height, bool &result) : _width(width), - _height(height) { + _height(height), + _isTransparent(true) { _data = new byte[width * height * 4]; Common::fill(_data, &_data[width * height * 4], 0); @@ -162,7 +168,7 @@ RenderedImage::RenderedImage(uint width, uint height, bool &result) : return; } -RenderedImage::RenderedImage() : _width(0), _height(0), _data(0) { +RenderedImage::RenderedImage() : _width(0), _height(0), _data(0), _isTransparent(true) { _backSurface = Kernel::getInstance()->getGfx()->getSurface(); _doCleanup = false; @@ -219,7 +225,7 @@ uint RenderedImage::getPixel(int x, int y) { // ----------------------------------------------------------------------------- -bool RenderedImage::blit(int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height) { +bool RenderedImage::blit(int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, RectangleList *updateRects) { int ca = (color >> 24) & 0xff; // Check if we need to draw anything at all @@ -282,156 +288,175 @@ bool RenderedImage::blit(int posX, int posY, int flipping, Common::Rect *pPartRe img = &srcImage; } - // 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)_backSurface->w - posX, 0)); - img->h = CLIP((int)img->h, 0, (int)MAX((int)_backSurface->h - posY, 0)); - - if ((img->w > 0) && (img->h > 0)) { - int xp = 0, yp = 0; - - int inStep = 4; - int inoStep = img->pitch; - if (flipping & Image::FLIP_V) { - inStep = -inStep; - xp = img->w - 1; + for (RectangleList::iterator it = updateRects->begin(); it != updateRects->end(); ++it) { + const Common::Rect &clipRect = *it; + + int skipLeft = 0, skipTop = 0; + int drawX = posX, drawY = posY; + int drawWidth = img->w; + int drawHeight = img->h; + + // Handle clipping + if (drawY < clipRect.top) { + skipTop = clipRect.top - drawY; + drawHeight = MAX(0, (int)drawHeight - (clipRect.top - drawY)); + drawY = clipRect.top; } - - if (flipping & Image::FLIP_H) { - inoStep = -inoStep; - yp = img->h - 1; + + if (drawX < clipRect.left) { + skipLeft = clipRect.left - drawX; + drawWidth = MAX(0, (int)drawWidth - skipLeft); + drawX = clipRect.left; } + + if (drawX + drawWidth >= clipRect.right) + drawWidth = clipRect.right - drawX; + + if (drawY + drawHeight >= clipRect.bottom) + drawHeight = clipRect.bottom - drawY; + + if ((drawWidth > 0) && (drawHeight > 0)) { + int xp = 0, yp = 0; + + int inStep = 4; + int inoStep = img->pitch; + if (flipping & Image::FLIP_V) { + inStep = -inStep; + xp = img->w - 1 - skipLeft; + } else { + xp = skipLeft; + } + + if (flipping & Image::FLIP_H) { + inoStep = -inoStep; + yp = img->h - 1 - skipTop; + } else { + yp = skipTop; + } + + byte *ino = (byte *)img->getBasePtr(xp, yp); + byte *outo = (byte *)_backSurface->getBasePtr(drawX, drawY); - byte *ino = (byte *)img->getBasePtr(xp, yp); - byte *outo = (byte *)_backSurface->getBasePtr(posX, posY); - byte *in, *out; - - for (int i = 0; i < img->h; i++) { - out = outo; - in = ino; - for (int j = 0; j < img->w; j++) { - uint32 pix = *(uint32 *)in; - int b = (pix >> 0) & 0xff; - int g = (pix >> 8) & 0xff; - int r = (pix >> 16) & 0xff; - int a = (pix >> 24) & 0xff; - in += inStep; - - if (ca != 255) { - a = a * ca >> 8; +#if defined(SCUMM_LITTLE_ENDIAN) + // Simple memcpy if the source bitmap doesn't have transparent pixels and the drawing transparency is 255 + // NOTE Only possible with LE-machines at the moment, maybe it would be feasible to convert the bitmap pixels at loading time? + if (!_isTransparent && ca == 255) { + for (int i = 0; i < drawHeight; i++) { + memcpy(outo, ino, drawWidth * 4); + outo += _backSurface->pitch; + ino += inoStep; } - - switch (a) { - case 0: // Full transparency - out += 4; - break; - case 255: // Full opacity + } else +#endif + { + byte *in, *out; + for (int i = 0; i < drawHeight; i++) { + out = outo; + in = ino; + for (int j = 0; j < drawWidth; j++) { + uint32 pix = *(uint32 *)in; + int a = (pix >> 24) & 0xff; + in += inStep; + + if (a == 0) { + // Full transparency + out += 4; + continue; + } + + if (ca != 255) { + a = a * ca >> 8; + } + + int b = (pix >> 0) & 0xff; + int g = (pix >> 8) & 0xff; + int r = (pix >> 16) & 0xff; + + if (a == 255) { #if defined(SCUMM_LITTLE_ENDIAN) - if (cb != 255) - *out++ = (b * cb) >> 8; - else - *out++ = b; - - if (cg != 255) - *out++ = (g * cg) >> 8; - else - *out++ = g; - - if (cr != 255) - *out++ = (r * cr) >> 8; - else - *out++ = r; - - *out++ = a; + if (cb != 255) + b = (b * cb) >> 8; + if (cg != 255) + g = (g * cg) >> 8; + if (cr != 255) + r = (r * cr) >> 8; + *(uint32 *)out = (255 << 24) | (r << 16) | (g << 8) | b; + out += 4; #else - *out++ = a; - - if (cr != 255) - *out++ = (r * cr) >> 8; - else - *out++ = r; - - if (cg != 255) - *out++ = (g * cg) >> 8; - else - *out++ = g; - - if (cb != 255) - *out++ = (b * cb) >> 8; - else - *out++ = b; + *out++ = a; + if (cr != 255) + *out++ = (r * cr) >> 8; + else + *out++ = r; + if (cg != 255) + *out++ = (g * cg) >> 8; + else + *out++ = g; + if (cb != 255) + *out++ = (b * cb) >> 8; + else + *out++ = b; #endif - break; - - default: // alpha blending + } else { #if defined(SCUMM_LITTLE_ENDIAN) - if (cb == 0) - *out = 0; - else if (cb != 255) - *out += ((b - *out) * a * cb) >> 16; - else - *out += ((b - *out) * a) >> 8; - out++; - if (cg == 0) - *out = 0; - else if (cg != 255) - *out += ((g - *out) * a * cg) >> 16; - else - *out += ((g - *out) * a) >> 8; - out++; - if (cr == 0) - *out = 0; - else if (cr != 255) - *out += ((r - *out) * a * cr) >> 16; - else - *out += ((r - *out) * a) >> 8; - out++; - *out = 255; - out++; + pix = *(uint32 *)out; + int outb = (pix >> 0) & 0xff; + int outg = (pix >> 8) & 0xff; + int outr = (pix >> 16) & 0xff; + if (cb == 0) + outb = 0; + else if (cb != 255) + outb += ((b - outb) * a * cb) >> 16; + else + outb += ((b - outb) * a) >> 8; + if (cg == 0) + outg = 0; + else if (cg != 255) + outg += ((g - outg) * a * cg) >> 16; + else + outg += ((g - outg) * a) >> 8; + if (cr == 0) + outr = 0; + else if (cr != 255) + outr += ((r - outr) * a * cr) >> 16; + else + outr += ((r - outr) * a) >> 8; + *(uint32 *)out = (255 << 24) | (outr << 16) | (outg << 8) | outb; + out += 4; #else - *out = 255; - out++; - if (cr == 0) - *out = 0; - else if (cr != 255) - *out += ((r - *out) * a * cr) >> 16; - else - *out += ((r - *out) * a) >> 8; - out++; - if (cg == 0) - *out = 0; - else if (cg != 255) - *out += ((g - *out) * a * cg) >> 16; - else - *out += ((g - *out) * a) >> 8; - out++; - if (cb == 0) - *out = 0; - else if (cb != 255) - *out += ((b - *out) * a * cb) >> 16; - else - *out += ((b - *out) * a) >> 8; - out++; + *out = 255; + out++; + if (cr == 0) + *out = 0; + else if (cr != 255) + *out += ((r - *out) * a * cr) >> 16; + else + *out += ((r - *out) * a) >> 8; + out++; + if (cg == 0) + *out = 0; + else if (cg != 255) + *out += ((g - *out) * a * cg) >> 16; + else + *out += ((g - *out) * a) >> 8; + out++; + if (cb == 0) + *out = 0; + else if (cb != 255) + *out += ((b - *out) * a * cb) >> 16; + else + *out += ((b - *out) * a) >> 8; + out++; #endif + } + } + outo += _backSurface->pitch; + ino += inoStep; } } - outo += _backSurface->pitch; - ino += inoStep; - } - g_system->copyRectToScreen(_backSurface->getBasePtr(posX, posY), _backSurface->pitch, posX, posY, - img->w, img->h); + } + } if (imgScaled) { @@ -467,6 +492,20 @@ void RenderedImage::copyDirectly(int posX, int posY) { g_system->copyRectToScreen(data, _backSurface->pitch, posX, posY, w, h); } +void RenderedImage::checkForTransparency() { + // Check if the source bitmap has any transparent pixels at all + _isTransparent = false; + byte *data = _data; + for (int i = 0; i < _height; i++) { + for (int j = 0; j < _width; j++) { + _isTransparent = data[3] != 0xff; + if (_isTransparent) + return; + data += 4; + } + } +} + /** * Scales a passed surface, creating a new surface with the result * @param srcImage Source image to scale diff --git a/engines/sword25/gfx/image/renderedimage.h b/engines/sword25/gfx/image/renderedimage.h index f92a5b39b4..a25b258592 100644 --- a/engines/sword25/gfx/image/renderedimage.h +++ b/engines/sword25/gfx/image/renderedimage.h @@ -75,7 +75,8 @@ public: int flipping = Image::FLIP_NONE, Common::Rect *pPartRect = NULL, uint color = BS_ARGB(255, 255, 255, 255), - int width = -1, int height = -1); + int width = -1, int height = -1, + RectangleList *updateRects = 0); virtual bool fill(const Common::Rect *pFillRect, uint color); virtual bool setContent(const byte *pixeldata, uint size, uint offset = 0, uint stride = 0); void replaceContent(byte *pixeldata, int width, int height); @@ -105,14 +106,19 @@ public: static Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize); + void setIsTransparent(bool isTransparent) { _isTransparent = isTransparent; } + virtual bool isSolid() const { return !_isTransparent; } + private: byte *_data; int _width; int _height; bool _doCleanup; + bool _isTransparent; Graphics::Surface *_backSurface; + void checkForTransparency(); static int *scaleLine(int size, int srcSize); }; diff --git a/engines/sword25/gfx/image/swimage.cpp b/engines/sword25/gfx/image/swimage.cpp index 0b9cc11df2..7e4c3143f5 100644 --- a/engines/sword25/gfx/image/swimage.cpp +++ b/engines/sword25/gfx/image/swimage.cpp @@ -79,7 +79,8 @@ bool SWImage::blit(int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, - int width, int height) { + int width, int height, + RectangleList *updateRects) { error("Blit() is not supported."); return false; } diff --git a/engines/sword25/gfx/image/swimage.h b/engines/sword25/gfx/image/swimage.h index 5f348958b2..a35127a41d 100644 --- a/engines/sword25/gfx/image/swimage.h +++ b/engines/sword25/gfx/image/swimage.h @@ -58,7 +58,8 @@ public: int flipping = Image::FLIP_NONE, Common::Rect *pPartRect = NULL, uint color = BS_ARGB(255, 255, 255, 255), - int width = -1, int height = -1); + int width = -1, int height = -1, + RectangleList *updateRects = 0); virtual bool fill(const Common::Rect *fillRectPtr, uint color); virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride); virtual uint getPixel(int x, int y); diff --git a/engines/sword25/gfx/image/vectorimage.cpp b/engines/sword25/gfx/image/vectorimage.cpp index 81f4fc2ad5..471081cfbf 100644 --- a/engines/sword25/gfx/image/vectorimage.cpp +++ b/engines/sword25/gfx/image/vectorimage.cpp @@ -602,7 +602,8 @@ bool VectorImage::blit(int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, - int width, int height) { + int width, int height, + RectangleList *updateRects) { static VectorImage *oldThis = 0; static int oldWidth = -2; static int oldHeight = -2; @@ -623,7 +624,7 @@ bool VectorImage::blit(int posX, int posY, RenderedImage *rend = new RenderedImage(); rend->replaceContent(_pixelData, width, height); - rend->blit(posX, posY, flipping, pPartRect, color, width, height); + rend->blit(posX, posY, flipping, pPartRect, color, width, height, updateRects); delete rend; diff --git a/engines/sword25/gfx/image/vectorimage.h b/engines/sword25/gfx/image/vectorimage.h index 959f251c14..d1760a433e 100644 --- a/engines/sword25/gfx/image/vectorimage.h +++ b/engines/sword25/gfx/image/vectorimage.h @@ -212,7 +212,8 @@ public: int flipping = FLIP_NONE, Common::Rect *pPartRect = NULL, uint color = BS_ARGB(255, 255, 255, 255), - int width = -1, int height = -1); + int width = -1, int height = -1, + RectangleList *updateRects = 0); class SWFBitStream; diff --git a/engines/sword25/gfx/microtiles.cpp b/engines/sword25/gfx/microtiles.cpp new file mode 100644 index 0000000000..8dceed5348 --- /dev/null +++ b/engines/sword25/gfx/microtiles.cpp @@ -0,0 +1,164 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "sword25/gfx/microtiles.h" + +namespace Sword25 { + +MicroTileArray::MicroTileArray(int16 width, int16 height) { + _tilesW = (width / TileSize) + ((width % TileSize) > 0 ? 1 : 0); + _tilesH = (height / TileSize) + ((height % TileSize) > 0 ? 1 : 0); + _tiles = new BoundingBox[_tilesW * _tilesH]; + clear(); +} + +MicroTileArray::~MicroTileArray() { + delete[] _tiles; +} + +void MicroTileArray::addRect(Common::Rect r) { + + int ux0, uy0, ux1, uy1; + int tx0, ty0, tx1, ty1; + int ix0, iy0, ix1, iy1; + + r.clip(Common::Rect(0, 0, 799, 599)); + + ux0 = r.left / TileSize; + uy0 = r.top / TileSize; + ux1 = r.right / TileSize; + uy1 = r.bottom / TileSize; + + tx0 = r.left % TileSize; + ty0 = r.top % TileSize; + tx1 = r.right % TileSize; + ty1 = r.bottom % TileSize; + + for (int yc = uy0; yc <= uy1; yc++) { + for (int xc = ux0; xc <= ux1; xc++) { + ix0 = (xc == ux0) ? tx0 : 0; + ix1 = (xc == ux1) ? tx1 : TileSize - 1; + iy0 = (yc == uy0) ? ty0 : 0; + iy1 = (yc == uy1) ? ty1 : TileSize - 1; + updateBoundingBox(_tiles[xc + yc * _tilesW], ix0, iy0, ix1, iy1); + } + } + +} + +void MicroTileArray::clear() { + memset(_tiles, 0, _tilesW * _tilesH * sizeof(BoundingBox)); +} + +byte MicroTileArray::TileX0(const BoundingBox &boundingBox) { + return (boundingBox >> 24) & 0xFF; +} + +byte MicroTileArray::TileY0(const BoundingBox &boundingBox) { + return (boundingBox >> 16) & 0xFF; +} + +byte MicroTileArray::TileX1(const BoundingBox &boundingBox) { + return (boundingBox >> 8) & 0xFF; +} + +byte MicroTileArray::TileY1(const BoundingBox &boundingBox) { + return boundingBox & 0xFF; +} + +bool MicroTileArray::isBoundingBoxEmpty(const BoundingBox &boundingBox) { + return boundingBox == EmptyBoundingBox; +} + +bool MicroTileArray::isBoundingBoxFull(const BoundingBox &boundingBox) { + return boundingBox == FullBoundingBox; +} + +void MicroTileArray::setBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1) { + boundingBox = (x0 << 24) | (y0 << 16) | (x1 << 8) | y1; +} + +void MicroTileArray::updateBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1) { + if (!isBoundingBoxEmpty(boundingBox)) { + x0 = MIN(TileX0(boundingBox), x0); + y0 = MIN(TileY0(boundingBox), y0); + x1 = MAX(TileX1(boundingBox), x1); + y1 = MAX(TileY1(boundingBox), y1); + } + setBoundingBox(boundingBox, x0, y0, x1, y1); +} + +RectangleList *MicroTileArray::getRectangles() { + + RectangleList *rects = new RectangleList(); + + int x, y; + int x0, y0, x1, y1; + int i = 0; + + for (y = 0; y < _tilesH; ++y) { + for (x = 0; x < _tilesW; ++x) { + + int start; + int finish = 0; + BoundingBox boundingBox = _tiles[i]; + + if (isBoundingBoxEmpty(boundingBox)) { + ++i; + continue; + } + + x0 = (x * TileSize) + TileX0(boundingBox); + y0 = (y * TileSize) + TileY0(boundingBox); + y1 = (y * TileSize) + TileY1(boundingBox); + + start = i; + + if (TileX1(boundingBox) == TileSize - 1 && x != _tilesW - 1) { // check if the tile continues + while (!finish) { + ++x; + ++i; + if (x == _tilesW || i >= _tilesW * _tilesH || + TileY0(_tiles[i]) != TileY0(boundingBox) || + TileY1(_tiles[i]) != TileY1(boundingBox) || + TileX0(_tiles[i]) != 0) + { + --x; + --i; + finish = 1; + } + } + } + + x1 = (x * TileSize) + TileX1(_tiles[i]); + + rects->push_back(Common::Rect(x0, y0, x1 + 1, y1 + 1)); + + ++i; + } + } + + return rects; +} + +} // End of namespace Sword25 diff --git a/engines/sword25/gfx/microtiles.h b/engines/sword25/gfx/microtiles.h new file mode 100644 index 0000000000..454fc39f37 --- /dev/null +++ b/engines/sword25/gfx/microtiles.h @@ -0,0 +1,65 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef SWORD25_MICROTILES_H +#define SWORD25_MICROTILES_H + +#include "common/scummsys.h" +#include "common/list.h" +#include "common/util.h" +#include "common/rect.h" + +namespace Sword25 { + +typedef uint32 BoundingBox; + +const BoundingBox FullBoundingBox = 0x00001F1F; +const BoundingBox EmptyBoundingBox = 0x00000000; +const int TileSize = 32; + +class RectangleList : public Common::List { +}; + +class MicroTileArray { +public: + MicroTileArray(int16 width, int16 height); + ~MicroTileArray(); + void addRect(Common::Rect r); + void clear(); + RectangleList *getRectangles(); +protected: + BoundingBox *_tiles; + int16 _tilesW, _tilesH; + byte TileX0(const BoundingBox &boundingBox); + byte TileY0(const BoundingBox &boundingBox); + byte TileX1(const BoundingBox &boundingBox); + byte TileY1(const BoundingBox &boundingBox); + bool isBoundingBoxEmpty(const BoundingBox &boundingBox); + bool isBoundingBoxFull(const BoundingBox &boundingBox); + void setBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1); + void updateBoundingBox(BoundingBox &boundingBox, byte x0, byte y0, byte x1, byte y1); +}; + +} // namespace Sword25 + +#endif // SWORD25_MICROTILES_H diff --git a/engines/sword25/gfx/panel.cpp b/engines/sword25/gfx/panel.cpp index 6d5b2a623d..931b9cdbe7 100644 --- a/engines/sword25/gfx/panel.cpp +++ b/engines/sword25/gfx/panel.cpp @@ -36,6 +36,8 @@ #include "sword25/gfx/graphicengine.h" #include "sword25/gfx/image/image.h" +#include "sword25/gfx/renderobjectmanager.h" + namespace Sword25 { Panel::Panel(RenderObjectPtr parentPtr, int width, int height, uint color) : @@ -67,7 +69,7 @@ Panel::Panel(InputPersistenceBlock &reader, RenderObjectPtr parent Panel::~Panel() { } -bool Panel::doRender() { +bool Panel::doRender(RectangleList *updateRects) { // Falls der Alphawert 0 ist, ist das Panel komplett durchsichtig und es muss nichts gezeichnet werden. if (_color >> 24 == 0) return true; @@ -75,7 +77,15 @@ bool Panel::doRender() { GraphicEngine *gfxPtr = Kernel::getInstance()->getGfx(); assert(gfxPtr); - return gfxPtr->fill(&_bbox, _color); + for (RectangleList::iterator it = updateRects->begin(); it != updateRects->end(); ++it) { + const Common::Rect &clipRect = *it; + if (_bbox.intersects(clipRect)) { + Common::Rect intersectionRect = _bbox.findIntersectingRect(clipRect); + gfxPtr->fill(&intersectionRect, _color); + } + } + + return true; } bool Panel::persist(OutputPersistenceBlock &writer) { diff --git a/engines/sword25/gfx/panel.h b/engines/sword25/gfx/panel.h index cbf04ce40f..cb94048360 100644 --- a/engines/sword25/gfx/panel.h +++ b/engines/sword25/gfx/panel.h @@ -59,7 +59,7 @@ public: virtual bool unpersist(InputPersistenceBlock &reader); protected: - virtual bool doRender(); + virtual bool doRender(RectangleList *updateRects); private: uint _color; diff --git a/engines/sword25/gfx/renderobject.cpp b/engines/sword25/gfx/renderobject.cpp index a977eb80ba..f2d25771a9 100644 --- a/engines/sword25/gfx/renderobject.cpp +++ b/engines/sword25/gfx/renderobject.cpp @@ -48,6 +48,8 @@ namespace Sword25 { +int RenderObject::_nextGlobalVersion = 0; + RenderObject::RenderObject(RenderObjectPtr parentPtr, TYPES type, uint handle) : _managerPtr(0), _parentPtr(parentPtr), @@ -65,7 +67,9 @@ RenderObject::RenderObject(RenderObjectPtr parentPtr, TYPES type, _type(type), _initSuccess(false), _refreshForced(true), - _handle(0) { + _handle(0), + _version(++_nextGlobalVersion), + _isSolid(false) { // Renderobject registrieren, abhängig vom Handle-Parameter entweder mit beliebigem oder vorgegebenen Handle. if (handle == 0) @@ -106,7 +110,7 @@ RenderObject::~RenderObject() { RenderObjectRegistry::instance().deregisterObject(this); } -bool RenderObject::render() { +bool RenderObject::render(RectangleList *updateRects, const Common::Array &updateRectsMinZ) { // Objektänderungen validieren validateObject(); @@ -121,17 +125,39 @@ bool RenderObject::render() { } // Objekt zeichnen. - doRender(); + bool needRender = false; + int index = 0; + + // Only draw if the bounding box intersects any update rectangle and + // the object is in front of the minimum Z value. + for (RectangleList::iterator rectIt = updateRects->begin(); !needRender && rectIt != updateRects->end(); ++rectIt, ++index) + needRender = (_bbox.contains(*rectIt) || _bbox.intersects(*rectIt)) && getAbsoluteZ() >= updateRectsMinZ[index]; + + if (needRender) + doRender(updateRects); // Dann müssen die Kinder gezeichnet werden RENDEROBJECT_ITER it = _children.begin(); for (; it != _children.end(); ++it) - if (!(*it)->render()) + if (!(*it)->render(updateRects, updateRectsMinZ)) return false; return true; } +void RenderObject::collectRenderQueue(RenderObjectQueue *renderQueue) { + + if (!_visible) + return; + + renderQueue->add(this); + + RENDEROBJECT_ITER it = _children.begin(); + for (; it != _children.end(); ++it) + (*it)->collectRenderQueue(renderQueue); + +} + void RenderObject::validateObject() { // Die Veränderungen in den Objektvariablen aufheben _oldBbox = _bbox; @@ -157,6 +183,8 @@ bool RenderObject::updateObjectState() { // Die Bounding-Box neu berechnen und Update-Regions registrieren. updateBoxes(); + + ++_version; // Änderungen Validieren validateObject(); @@ -289,6 +317,13 @@ void RenderObject::setZ(int z) { _z = z; } +int RenderObject::getAbsoluteZ() const { + if (_parentPtr.isValid()) + return _parentPtr->getAbsoluteZ() + _z; + else + return _z; +} + void RenderObject::setVisible(bool visible) { _visible = visible; } diff --git a/engines/sword25/gfx/renderobject.h b/engines/sword25/gfx/renderobject.h index f963ccaeb3..a22893a69e 100644 --- a/engines/sword25/gfx/renderobject.h +++ b/engines/sword25/gfx/renderobject.h @@ -53,6 +53,8 @@ namespace Sword25 { class Kernel; class RenderObjectManager; +class RenderObjectQueue; +class RectangleList; class Bitmap; class Animation; class AnimationTemplate; @@ -218,7 +220,10 @@ public: Dieses kann entweder direkt geschehen oder durch den Aufruf von UpdateObjectState() an einem Vorfahren-Objekt.
Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden. */ - bool render(); + bool render(RectangleList *updateRects, const Common::Array &updateRectsMinZ); + + void collectRenderQueue(RenderObjectQueue *renderQueue); + /** @brief Bereitet das Objekt und alle seine Unterobjekte auf einen Rendervorgang vor. Hierbei werden alle Dirty-Rectangles berechnet und die Renderreihenfolge aktualisiert. @@ -230,7 +235,7 @@ public: @brief Löscht alle Kinderobjekte. */ void deleteAllChildren(); - + // Accessor-Methoden // ----------------- /** @@ -299,6 +304,9 @@ public: int getZ() const { return _z; } + + int getAbsoluteZ() const; + /** @brief Gibt die Breite des Objektes zurück. */ @@ -352,6 +360,15 @@ public: return _handle; } + // Get the RenderObjects current version + int getVersion() const { + return _version; + } + + bool isSolid() const { + return _isSolid; + } + // Persistenz-Methoden // ------------------- virtual bool persist(OutputPersistenceBlock &writer); @@ -388,6 +405,13 @@ protected: int _oldZ; bool _oldVisible; + static int _nextGlobalVersion; + + int _version; + + // This should be set to true if the RenderObject is NOT alpha-blended to optimize drawing + bool _isSolid; + /// Ein Pointer auf den BS_RenderObjektManager, der das Objekt verwaltet. RenderObjectManager *_managerPtr; @@ -402,7 +426,7 @@ protected: @return Gibt false zurück, falls das Rendern fehlgeschlagen ist. @remark */ - virtual bool doRender() = 0; // { return true; } + virtual bool doRender(RectangleList *updateRects) = 0; // { return true; } // RenderObject-Baum Variablen // --------------------------- diff --git a/engines/sword25/gfx/renderobjectmanager.cpp b/engines/sword25/gfx/renderobjectmanager.cpp index 38289991eb..a8f8c3682c 100644 --- a/engines/sword25/gfx/renderobjectmanager.cpp +++ b/engines/sword25/gfx/renderobjectmanager.cpp @@ -41,17 +41,37 @@ #include "sword25/gfx/timedrenderobject.h" #include "sword25/gfx/rootrenderobject.h" +#include "common/system.h" + namespace Sword25 { +void RenderObjectQueue::add(RenderObject *renderObject) { + push_back(RenderObjectQueueItem(renderObject, renderObject->getBbox(), renderObject->getVersion())); +} + +bool RenderObjectQueue::exists(const RenderObjectQueueItem &renderObjectQueueItem) { + for (RenderObjectQueue::iterator it = begin(); it != end(); ++it) + if ((*it)._renderObject == renderObjectQueueItem._renderObject && + (*it)._version == renderObjectQueueItem._version) + return true; + return false; +} + RenderObjectManager::RenderObjectManager(int width, int height, int framebufferCount) : _frameStarted(false) { // Wurzel des BS_RenderObject-Baumes erzeugen. _rootPtr = (new RootRenderObject(this, width, height))->getHandle(); + _uta = new MicroTileArray(width, height); + _currQueue = new RenderObjectQueue(); + _prevQueue = new RenderObjectQueue(); } RenderObjectManager::~RenderObjectManager() { // Die Wurzel des Baumes löschen, damit werden alle BS_RenderObjects mitgelöscht. _rootPtr.erase(); + delete _uta; + delete _currQueue; + delete _prevQueue; } void RenderObjectManager::startFrame() { @@ -76,7 +96,62 @@ bool RenderObjectManager::render() { _frameStarted = false; // Die Render-Methode der Wurzel aufrufen. Dadurch wird das rekursive Rendern der Baumelemente angestoßen. - return _rootPtr->render(); + + uint32 renderDuration = g_system->getMillis(); + + _currQueue->clear(); + _rootPtr->collectRenderQueue(_currQueue); + + _uta->clear(); + // Add rectangles of objects which don't exist in this frame any more + for (RenderObjectQueue::iterator it = _prevQueue->begin(); it != _prevQueue->end(); ++it) + if (!_currQueue->exists(*it)) + _uta->addRect((*it)._bbox); + // Add rectangles of objects which are different from the previous frame + for (RenderObjectQueue::iterator it = _currQueue->begin(); it != _currQueue->end(); ++it) + if (!_prevQueue->exists(*it)) + _uta->addRect((*it)._bbox); + + RectangleList *updateRects = _uta->getRectangles(); + Common::Array updateRectsMinZ; + + updateRectsMinZ.reserve(updateRects->size()); + + // Calculate the minimum drawing Z value of each update rectangle + // Solid bitmaps with a Z order less than the value calculated here would be overdrawn again and + // so don't need to be drawn in the first place which speeds things up a bit. + for (RectangleList::iterator rectIt = updateRects->begin(); rectIt != updateRects->end(); ++rectIt) { + int minZ = 0; + for (RenderObjectQueue::iterator it = _currQueue->reverse_begin(); it != _currQueue->end(); --it) { + if ((*it)._renderObject->isVisible() && (*it)._renderObject->isSolid() && + (*it)._renderObject->getBbox().contains(*rectIt)) { + minZ = (*it)._renderObject->getAbsoluteZ(); + break; + } + } + updateRectsMinZ.push_back(minZ); + } + + if (_rootPtr->render(updateRects, updateRectsMinZ)) { + // Copy updated rectangles to the video screen + Graphics::Surface *backSurface = Kernel::getInstance()->getGfx()->getSurface(); + for (RectangleList::iterator rectIt = updateRects->begin(); rectIt != updateRects->end(); ++rectIt) { + const int x = (*rectIt).left; + const int y = (*rectIt).top; + const int width = (*rectIt).width(); + const int height = (*rectIt).height(); + g_system->copyRectToScreen(backSurface->getBasePtr(x, y), backSurface->pitch, x, y, width, height); + } + } + + delete updateRects; + + SWAP(_currQueue, _prevQueue); + + renderDuration = g_system->getMillis() - renderDuration; + //debug("renderDuration: %3.5f", renderDuration / 1000.0f); + + return true; } void RenderObjectManager::attatchTimedRenderObject(RenderObjectPtr renderObjectPtr) { diff --git a/engines/sword25/gfx/renderobjectmanager.h b/engines/sword25/gfx/renderobjectmanager.h index 9e7efd8e60..1db91dfbe6 100644 --- a/engines/sword25/gfx/renderobjectmanager.h +++ b/engines/sword25/gfx/renderobjectmanager.h @@ -47,11 +47,28 @@ #include "sword25/gfx/renderobjectptr.h" #include "sword25/kernel/persistable.h" +#include "sword25/gfx/microtiles.h" + namespace Sword25 { class Kernel; class RenderObject; class TimedRenderObject; +class RenderObjectManager; + +struct RenderObjectQueueItem { + RenderObject *_renderObject; + Common::Rect _bbox; + int _version; + RenderObjectQueueItem(RenderObject *renderObject, const Common::Rect &bbox, int version) + : _renderObject(renderObject), _bbox(bbox), _version(version) {} +}; + +class RenderObjectQueue : public Common::List { +public: + void add(RenderObject *renderObject); + bool exists(const RenderObjectQueueItem &renderObjectQueueItem); +}; /** @brief Diese Klasse ist für die Verwaltung von BS_RenderObjects zuständig. @@ -114,6 +131,9 @@ private: typedef Common::Array > RenderObjectList; RenderObjectList _timedRenderObjects; + MicroTileArray *_uta; + RenderObjectQueue *_currQueue, *_prevQueue; + // RenderObject-Tree Variablen // --------------------------- // Der Baum legt die hierachische Ordnung der BS_RenderObjects fest. diff --git a/engines/sword25/gfx/rootrenderobject.h b/engines/sword25/gfx/rootrenderobject.h index 4782fad175..f1b19d6bae 100644 --- a/engines/sword25/gfx/rootrenderobject.h +++ b/engines/sword25/gfx/rootrenderobject.h @@ -59,7 +59,7 @@ private: } protected: - virtual bool doRender() { + virtual bool doRender(RectangleList *updateRects) { return true; } }; diff --git a/engines/sword25/gfx/staticbitmap.cpp b/engines/sword25/gfx/staticbitmap.cpp index 0ae07b36b5..91b93e8910 100644 --- a/engines/sword25/gfx/staticbitmap.cpp +++ b/engines/sword25/gfx/staticbitmap.cpp @@ -71,6 +71,8 @@ bool StaticBitmap::initBitmapResource(const Common::String &filename) { // RenderObject Eigenschaften aktualisieren _originalWidth = _width = bitmapPtr->getWidth(); _originalHeight = _height = bitmapPtr->getHeight(); + + _isSolid = bitmapPtr->isSolid(); // Bild-Resource freigeben bitmapPtr->release(); @@ -81,7 +83,7 @@ bool StaticBitmap::initBitmapResource(const Common::String &filename) { StaticBitmap::~StaticBitmap() { } -bool StaticBitmap::doRender() { +bool StaticBitmap::doRender(RectangleList *updateRects) { // Bitmap holen Resource *resourcePtr = Kernel::getInstance()->getResourceManager()->requestResource(_resourceFilename); assert(resourcePtr); @@ -98,12 +100,14 @@ bool StaticBitmap::doRender() { result = bitmapResourcePtr->blit(_absoluteX, _absoluteY, (_flipV ? BitmapResource::FLIP_V : 0) | (_flipH ? BitmapResource::FLIP_H : 0), - 0, _modulationColor, -1, -1); + 0, _modulationColor, -1, -1, + updateRects); } else { result = bitmapResourcePtr->blit(_absoluteX, _absoluteY, (_flipV ? BitmapResource::FLIP_V : 0) | (_flipH ? BitmapResource::FLIP_H : 0), - 0, _modulationColor, _width, _height); + 0, _modulationColor, _width, _height, + updateRects); } // Resource freigeben diff --git a/engines/sword25/gfx/staticbitmap.h b/engines/sword25/gfx/staticbitmap.h index e66ede02b4..90b92b4331 100644 --- a/engines/sword25/gfx/staticbitmap.h +++ b/engines/sword25/gfx/staticbitmap.h @@ -65,7 +65,7 @@ public: virtual bool unpersist(InputPersistenceBlock &reader); protected: - virtual bool doRender(); + virtual bool doRender(RectangleList *updateRects); private: Common::String _resourceFilename; diff --git a/engines/sword25/gfx/text.cpp b/engines/sword25/gfx/text.cpp index 82bb7cdff7..a5cfb0b0b5 100644 --- a/engines/sword25/gfx/text.cpp +++ b/engines/sword25/gfx/text.cpp @@ -129,7 +129,7 @@ void Text::setAutoWrapThreshold(uint autoWrapThreshold) { } } -bool Text::doRender() { +bool Text::doRender(RectangleList *updateRects) { // Font-Resource locken. FontResource *fontPtr = lockFontResource(); if (!fontPtr) @@ -171,7 +171,7 @@ bool Text::doRender() { Common::Rect renderRect(curX, curY, curX + curRect.width(), curY + curRect.height()); renderRect.translate(curRect.left - curX, curRect.top - curY); - result = charMapPtr->blit(curX, curY, Image::FLIP_NONE, &renderRect, _modulationColor); + result = charMapPtr->blit(curX, curY, Image::FLIP_NONE, &renderRect, _modulationColor, -1, -1, updateRects); if (!result) break; diff --git a/engines/sword25/gfx/text.h b/engines/sword25/gfx/text.h index a0d668014f..94e7a30865 100644 --- a/engines/sword25/gfx/text.h +++ b/engines/sword25/gfx/text.h @@ -136,7 +136,7 @@ public: virtual bool unpersist(InputPersistenceBlock &reader); protected: - virtual bool doRender(); + virtual bool doRender(RectangleList *updateRects); private: Text(RenderObjectPtr parentPtr); -- cgit v1.2.3