SWORD25: Optimize graphics drawing code

This commit is contained in:
johndoe123 2013-04-17 12:41:49 +02:00
parent 6548104b96
commit 0a0b2f397b
27 changed files with 646 additions and 179 deletions

View File

@ -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

View File

@ -151,7 +151,7 @@ public:
void setCallbacks();
protected:
virtual bool doRender();
virtual bool doRender(RectangleList *updateRects);
private:
enum Direction {

View File

@ -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;
};

View File

@ -53,10 +53,13 @@ DynamicBitmap::DynamicBitmap(InputPersistenceBlock &reader, RenderObjectPtr<Rend
bool DynamicBitmap::createRenderedImage(uint width, uint height) {
bool result = false;
_image.reset(new RenderedImage(width, height, result));
_originalWidth = _width = width;
_originalHeight = _height = height;
_image->setIsTransparent(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);
}

View File

@ -59,7 +59,7 @@ public:
virtual bool unpersist(InputPersistenceBlock &reader);
protected:
virtual bool doRender();
virtual bool doRender(RectangleList *updateRects);
private:
DynamicBitmap(RenderObjectPtr<RenderObject> parentPtr, uint width, uint height);

View File

@ -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;

View File

@ -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; }
//@}
};

View File

@ -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

View File

@ -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);
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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<Common::Rect> {
};
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

View File

@ -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<RenderObject> parentPtr, int width, int height, uint color) :
@ -67,7 +69,7 @@ Panel::Panel(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> 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) {

View File

@ -59,7 +59,7 @@ public:
virtual bool unpersist(InputPersistenceBlock &reader);
protected:
virtual bool doRender();
virtual bool doRender(RectangleList *updateRects);
private:
uint _color;

View File

@ -48,6 +48,8 @@
namespace Sword25 {
int RenderObject::_nextGlobalVersion = 0;
RenderObject::RenderObject(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle) :
_managerPtr(0),
_parentPtr(parentPtr),
@ -65,7 +67,9 @@ RenderObject::RenderObject(RenderObjectPtr<RenderObject> 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<int> &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;
}

View File

@ -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.<br>
Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
*/
bool render();
bool render(RectangleList *updateRects, const Common::Array<int> &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
// ---------------------------

View File

@ -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<int> 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<TimedRenderObject> renderObjectPtr) {

View File

@ -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<RenderObjectQueueItem> {
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<RenderObjectPtr<TimedRenderObject> > RenderObjectList;
RenderObjectList _timedRenderObjects;
MicroTileArray *_uta;
RenderObjectQueue *_currQueue, *_prevQueue;
// RenderObject-Tree Variablen
// ---------------------------
// Der Baum legt die hierachische Ordnung der BS_RenderObjects fest.

View File

@ -59,7 +59,7 @@ private:
}
protected:
virtual bool doRender() {
virtual bool doRender(RectangleList *updateRects) {
return true;
}
};

View File

@ -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

View File

@ -65,7 +65,7 @@ public:
virtual bool unpersist(InputPersistenceBlock &reader);
protected:
virtual bool doRender();
virtual bool doRender(RectangleList *updateRects);
private:
Common::String _resourceFilename;

View File

@ -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;

View File

@ -136,7 +136,7 @@ public:
virtual bool unpersist(InputPersistenceBlock &reader);
protected:
virtual bool doRender();
virtual bool doRender(RectangleList *updateRects);
private:
Text(RenderObjectPtr<RenderObject> parentPtr);

View File

@ -16,6 +16,7 @@ MODULE_OBJS := \
gfx/fontresource.o \
gfx/graphicengine.o \
gfx/graphicengine_script.o \
gfx/microtiles.o \
gfx/panel.o \
gfx/renderobject.o \
gfx/renderobjectmanager.o \