Initial commit
This commit is contained in:
580
engines/wintermute/base/gfx/osystem/base_render_osystem.cpp
Normal file
580
engines/wintermute/base/gfx/osystem/base_render_osystem.cpp
Normal file
@@ -0,0 +1,580 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME Lite.
|
||||
* http://dead-code.org/redir.php?target=wmelite
|
||||
* Copyright (c) 2011 Jan Nedoma
|
||||
*/
|
||||
|
||||
#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h"
|
||||
#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h"
|
||||
#include "engines/wintermute/base/gfx/osystem/render_ticket.h"
|
||||
#include "engines/wintermute/base/base_surface_storage.h"
|
||||
#include "engines/wintermute/base/gfx/base_image.h"
|
||||
#include "engines/wintermute/math/math_util.h"
|
||||
#include "engines/wintermute/base/base_game.h"
|
||||
#include "engines/wintermute/base/base_sprite.h"
|
||||
#include "engines/util.h"
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/queue.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#define DIRTY_RECT_LIMIT 800
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
BaseRenderer *makeOSystemRenderer(BaseGame *inGame) {
|
||||
return new BaseRenderOSystem(inGame);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
BaseRenderOSystem::BaseRenderOSystem(BaseGame *inGame) : BaseRenderer(inGame) {
|
||||
_renderSurface = new Graphics::ManagedSurface();
|
||||
_lastFrameIter = _renderQueue.end();
|
||||
_needsFlip = true;
|
||||
_skipThisFrame = false;
|
||||
|
||||
_borderLeft = _borderRight = _borderTop = _borderBottom = 0;
|
||||
_ratioX = _ratioY = 1.0f;
|
||||
_dirtyRect = nullptr;
|
||||
_disableDirtyRects = false;
|
||||
if (ConfMan.hasKey("dirty_rects")) {
|
||||
_disableDirtyRects = !ConfMan.getBool("dirty_rects");
|
||||
}
|
||||
|
||||
_lastScreenChangeID = g_system->getScreenChangeID();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
BaseRenderOSystem::~BaseRenderOSystem() {
|
||||
RenderQueueIterator it = _renderQueue.begin();
|
||||
while (it != _renderQueue.end()) {
|
||||
RenderTicket *ticket = *it;
|
||||
it = _renderQueue.erase(it);
|
||||
delete ticket;
|
||||
}
|
||||
|
||||
delete _dirtyRect;
|
||||
|
||||
_renderSurface->free();
|
||||
delete _renderSurface;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseRenderOSystem::initRenderer(int width, int height, bool windowed) {
|
||||
_width = width;
|
||||
_height = height;
|
||||
_renderRect.setWidth(_width);
|
||||
_renderRect.setHeight(_height);
|
||||
|
||||
_realWidth = width;
|
||||
_realHeight = height;
|
||||
|
||||
float origAspect = (float)_width / (float)_height;
|
||||
float realAspect = (float)_realWidth / (float)_realHeight;
|
||||
|
||||
float ratio;
|
||||
if (origAspect < realAspect) {
|
||||
// normal to wide
|
||||
ratio = (float)_realHeight / (float)_height;
|
||||
} else {
|
||||
// wide to normal
|
||||
ratio = (float)_realWidth / (float)_width;
|
||||
}
|
||||
|
||||
_borderLeft = (int)((_realWidth - (_width * ratio)) / 2);
|
||||
_borderRight = (int)(_realWidth - (_width * ratio) - _borderLeft);
|
||||
|
||||
_borderTop = (int)((_realHeight - (_height * ratio)) / 2);
|
||||
_borderBottom = (int)(_realHeight - (_height * ratio) - _borderTop);
|
||||
|
||||
_ratioX = (float)(_realWidth - _borderLeft - _borderRight) / (float)_width;
|
||||
_ratioY = (float)(_realHeight - _borderTop - _borderBottom) / (float)_height;
|
||||
|
||||
_windowed = !ConfMan.getBool("fullscreen");
|
||||
|
||||
Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0);
|
||||
initGraphics(_width, _height, &format);
|
||||
|
||||
if (g_system->getScreenFormat() != format) {
|
||||
warning("Couldn't setup GFX-backend for %dx%dx%d", _width, _height, format.bytesPerPixel * 8);
|
||||
return STATUS_FAILED;
|
||||
}
|
||||
|
||||
g_system->showMouse(false);
|
||||
|
||||
_renderSurface->create(g_system->getWidth(), g_system->getHeight(), g_system->getScreenFormat());
|
||||
_active = true;
|
||||
|
||||
_clearColor = _renderSurface->format.ARGBToColor(255, 0, 0, 0);
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
bool BaseRenderOSystem::indicatorFlip(int32 x, int32 y, int32 width, int32 height) {
|
||||
if (width > 0 && height > 0) {
|
||||
g_system->copyRectToScreen(_renderSurface->getBasePtr(x, y), _renderSurface->pitch, x, y, width, height);
|
||||
g_system->updateScreen();
|
||||
}
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
bool BaseRenderOSystem::forcedFlip() {
|
||||
g_system->copyRectToScreen(_renderSurface->getPixels(), _renderSurface->pitch, 0, 0, _renderSurface->w, _renderSurface->h);
|
||||
g_system->updateScreen();
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
bool BaseRenderOSystem::flip() {
|
||||
if (_skipThisFrame) {
|
||||
_skipThisFrame = false;
|
||||
delete _dirtyRect;
|
||||
_dirtyRect = nullptr;
|
||||
g_system->updateScreen();
|
||||
_needsFlip = false;
|
||||
|
||||
// Reset ticketing state
|
||||
_lastFrameIter = _renderQueue.end();
|
||||
RenderQueueIterator it;
|
||||
for (it = _renderQueue.begin(); it != _renderQueue.end(); ++it) {
|
||||
(*it)->_wantsDraw = false;
|
||||
}
|
||||
|
||||
addDirtyRect(_renderRect);
|
||||
return true;
|
||||
}
|
||||
if (!_disableDirtyRects) {
|
||||
drawTickets();
|
||||
} else {
|
||||
// Clear the scale-buffered tickets that wasn't reused.
|
||||
RenderQueueIterator it = _renderQueue.begin();
|
||||
while (it != _renderQueue.end()) {
|
||||
if ((*it)->_wantsDraw == false) {
|
||||
RenderTicket *ticket = *it;
|
||||
it = _renderQueue.erase(it);
|
||||
delete ticket;
|
||||
} else {
|
||||
(*it)->_wantsDraw = false;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int oldScreenChangeID = _lastScreenChangeID;
|
||||
_lastScreenChangeID = g_system->getScreenChangeID();
|
||||
bool screenChanged = _lastScreenChangeID != oldScreenChangeID;
|
||||
|
||||
if (_needsFlip || _disableDirtyRects || screenChanged) {
|
||||
if (_disableDirtyRects || screenChanged) {
|
||||
g_system->copyRectToScreen(_renderSurface->getPixels(), _renderSurface->pitch, 0, 0, _renderSurface->w, _renderSurface->h);
|
||||
}
|
||||
delete _dirtyRect;
|
||||
_dirtyRect = nullptr;
|
||||
_needsFlip = false;
|
||||
}
|
||||
_lastFrameIter = _renderQueue.end();
|
||||
|
||||
g_system->updateScreen();
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void BaseRenderOSystem::onWindowChange() {
|
||||
_windowed = !g_system->getFeatureState(OSystem::kFeatureFullscreenMode);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
void BaseRenderOSystem::setWindowed(bool windowed) {
|
||||
ConfMan.setBool("fullscreen", !windowed);
|
||||
g_system->beginGFXTransaction();
|
||||
g_system->setFeatureState(OSystem::kFeatureFullscreenMode, !windowed);
|
||||
g_system->endGFXTransaction();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseRenderOSystem::clear() {
|
||||
if (!_disableDirtyRects) {
|
||||
return STATUS_OK;
|
||||
}
|
||||
// TODO: This doesn't work with dirty rects
|
||||
_renderSurface->fillRect(_renderRect, _clearColor);
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseRenderOSystem::fade(uint16 alpha) {
|
||||
byte dwAlpha = (byte)(255 - alpha);
|
||||
return fadeToColor(0, 0, 0, dwAlpha);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseRenderOSystem::fadeToColor(byte r, byte g, byte b, byte a) {
|
||||
Common::Rect fillRect;
|
||||
|
||||
Common::Rect32 rc;
|
||||
_game->getCurrentViewportRect(&rc);
|
||||
fillRect.left = (int16)rc.left;
|
||||
fillRect.top = (int16)rc.top;
|
||||
fillRect.setWidth((int16)(rc.right - rc.left));
|
||||
fillRect.setHeight((int16)(rc.bottom - rc.top));
|
||||
|
||||
modTargetRect(&fillRect);
|
||||
|
||||
Common::Rect sizeRect(fillRect.width(), fillRect.height());
|
||||
Graphics::TransformStruct temp = Graphics::TransformStruct();
|
||||
temp._rgbaMod = MS_ARGB(a, r, g, b);
|
||||
temp._alphaDisable = (a == 0xff);
|
||||
drawSurface(nullptr, nullptr, &sizeRect, &fillRect, temp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Graphics::PixelFormat BaseRenderOSystem::getPixelFormat() const {
|
||||
return _renderSurface->format;
|
||||
}
|
||||
|
||||
void BaseRenderOSystem::drawSurface(BaseSurfaceOSystem *owner, const Graphics::Surface *surf,
|
||||
Common::Rect *srcRect, Common::Rect *dstRect, Graphics::TransformStruct &transform) {
|
||||
if (_disableDirtyRects) {
|
||||
RenderTicket *ticket = new RenderTicket(owner, surf, srcRect, dstRect, transform);
|
||||
ticket->_wantsDraw = true;
|
||||
_renderQueue.push_back(ticket);
|
||||
drawFromSurface(ticket);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip rects that are completely outside the screen:
|
||||
if ((dstRect->left < 0 && dstRect->right < 0) || (dstRect->top < 0 && dstRect->bottom < 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner) { // Fade-tickets are owner-less
|
||||
RenderTicket compare(owner, nullptr, srcRect, dstRect, transform);
|
||||
RenderQueueIterator it = _lastFrameIter;
|
||||
++it;
|
||||
// Avoid calling end() and operator* every time, when potentially going through
|
||||
// LOTS of tickets.
|
||||
RenderQueueIterator endIterator = _renderQueue.end();
|
||||
RenderTicket *compareTicket = nullptr;
|
||||
for (; it != endIterator; ++it) {
|
||||
compareTicket = *it;
|
||||
if (*(compareTicket) == compare && compareTicket->_isValid) {
|
||||
if (_disableDirtyRects) {
|
||||
drawFromSurface(compareTicket);
|
||||
} else {
|
||||
drawFromQueuedTicket(it);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
RenderTicket *ticket = new RenderTicket(owner, surf, srcRect, dstRect, transform);
|
||||
if (!_disableDirtyRects) {
|
||||
drawFromTicket(ticket);
|
||||
} else {
|
||||
ticket->_wantsDraw = true;
|
||||
_renderQueue.push_back(ticket);
|
||||
drawFromSurface(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseRenderOSystem::invalidateTicket(RenderTicket *renderTicket) {
|
||||
addDirtyRect(renderTicket->_dstRect);
|
||||
renderTicket->_isValid = false;
|
||||
// renderTicket->_canDelete = true; // TODO: Maybe readd this, to avoid even more duplicates.
|
||||
}
|
||||
|
||||
void BaseRenderOSystem::invalidateTicketsFromSurface(BaseSurfaceOSystem *surf) {
|
||||
RenderQueueIterator it;
|
||||
for (it = _renderQueue.begin(); it != _renderQueue.end(); ++it) {
|
||||
if ((*it)->_owner == surf) {
|
||||
invalidateTicket(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BaseRenderOSystem::drawFromTicket(RenderTicket *renderTicket) {
|
||||
renderTicket->_wantsDraw = true;
|
||||
|
||||
++_lastFrameIter;
|
||||
// In-order
|
||||
if (_renderQueue.empty() || _lastFrameIter == _renderQueue.end()) {
|
||||
_lastFrameIter--;
|
||||
_renderQueue.push_back(renderTicket);
|
||||
++_lastFrameIter;
|
||||
addDirtyRect(renderTicket->_dstRect);
|
||||
} else {
|
||||
// Before something
|
||||
RenderQueueIterator pos = _lastFrameIter;
|
||||
_renderQueue.insert(pos, renderTicket);
|
||||
--_lastFrameIter;
|
||||
addDirtyRect(renderTicket->_dstRect);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseRenderOSystem::drawFromQueuedTicket(const RenderQueueIterator &ticket) {
|
||||
RenderTicket *renderTicket = *ticket;
|
||||
assert(!renderTicket->_wantsDraw);
|
||||
renderTicket->_wantsDraw = true;
|
||||
|
||||
++_lastFrameIter;
|
||||
// Not in the same order?
|
||||
if (*_lastFrameIter != renderTicket) {
|
||||
--_lastFrameIter;
|
||||
// Remove the ticket from the list
|
||||
assert(*_lastFrameIter != renderTicket);
|
||||
_renderQueue.erase(ticket);
|
||||
// Is not in order, so readd it as if it was a new ticket
|
||||
drawFromTicket(renderTicket);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseRenderOSystem::addDirtyRect(const Common::Rect &rect) {
|
||||
if (!_dirtyRect) {
|
||||
_dirtyRect = new Common::Rect(rect);
|
||||
} else {
|
||||
_dirtyRect->extend(rect);
|
||||
}
|
||||
_dirtyRect->clip(_renderRect);
|
||||
}
|
||||
|
||||
void BaseRenderOSystem::drawTickets() {
|
||||
RenderQueueIterator it = _renderQueue.begin();
|
||||
// Clean out the old tickets
|
||||
// Note: We draw invalid tickets too, otherwise we wouldn't be honoring
|
||||
// the draw request they obviously made BEFORE becoming invalid, either way
|
||||
// we have a copy of their data, so their invalidness won't affect us.
|
||||
while (it != _renderQueue.end()) {
|
||||
if ((*it)->_wantsDraw == false) {
|
||||
RenderTicket *ticket = *it;
|
||||
addDirtyRect((*it)->_dstRect);
|
||||
it = _renderQueue.erase(it);
|
||||
delete ticket;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (!_dirtyRect || _dirtyRect->width() == 0 || _dirtyRect->height() == 0) {
|
||||
it = _renderQueue.begin();
|
||||
while (it != _renderQueue.end()) {
|
||||
RenderTicket *ticket = *it;
|
||||
ticket->_wantsDraw = false;
|
||||
++it;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
it = _renderQueue.begin();
|
||||
_lastFrameIter = _renderQueue.end();
|
||||
// A special case: If the screen has one giant OPAQUE rect to be drawn, then we skip filling
|
||||
// the background color. Typical use-case: Fullscreen FMVs.
|
||||
// Caveat: The FPS-counter will invalidate this.
|
||||
if (it != _lastFrameIter && _renderQueue.front() == _renderQueue.back() && (*it)->_transform._alphaDisable == true) {
|
||||
// If our single opaque rect fills the dirty rect, we can skip filling.
|
||||
if (*_dirtyRect != (*it)->_dstRect) {
|
||||
// Apply the clear-color to the dirty rect.
|
||||
_renderSurface->fillRect(*_dirtyRect, _clearColor);
|
||||
}
|
||||
// Otherwise Do NOT fill.
|
||||
} else {
|
||||
// Apply the clear-color to the dirty rect.
|
||||
_renderSurface->fillRect(*_dirtyRect, _clearColor);
|
||||
}
|
||||
for (; it != _renderQueue.end(); ++it) {
|
||||
RenderTicket *ticket = *it;
|
||||
if (ticket->_dstRect.intersects(*_dirtyRect)) {
|
||||
// dstClip is the area we want redrawn.
|
||||
Common::Rect dstClip(ticket->_dstRect);
|
||||
// reduce it to the dirty rect
|
||||
dstClip.clip(*_dirtyRect);
|
||||
// we need to keep track of the position to redraw the dirty rect
|
||||
Common::Rect pos(dstClip);
|
||||
int16 offsetX = ticket->_dstRect.left;
|
||||
int16 offsetY = ticket->_dstRect.top;
|
||||
// convert from screen-coords to surface-coords.
|
||||
dstClip.translate(-offsetX, -offsetY);
|
||||
|
||||
drawFromSurface(ticket, &pos, &dstClip);
|
||||
_needsFlip = true;
|
||||
}
|
||||
// Some tickets want redraw but don't actually clip the dirty area (typically the ones that shouldn't become clear-color)
|
||||
ticket->_wantsDraw = false;
|
||||
}
|
||||
g_system->copyRectToScreen(_renderSurface->getBasePtr(_dirtyRect->left, _dirtyRect->top), _renderSurface->pitch, _dirtyRect->left, _dirtyRect->top, _dirtyRect->width(), _dirtyRect->height());
|
||||
|
||||
it = _renderQueue.begin();
|
||||
// Clean out the old tickets
|
||||
while (it != _renderQueue.end()) {
|
||||
if ((*it)->_isValid == false) {
|
||||
RenderTicket *ticket = *it;
|
||||
addDirtyRect((*it)->_dstRect);
|
||||
it = _renderQueue.erase(it);
|
||||
delete ticket;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Replacement for SDL2's SDL_RenderCopy
|
||||
void BaseRenderOSystem::drawFromSurface(RenderTicket *ticket) {
|
||||
ticket->drawToSurface(_renderSurface);
|
||||
}
|
||||
|
||||
void BaseRenderOSystem::drawFromSurface(RenderTicket *ticket, Common::Rect *dstRect, Common::Rect *clipRect) {
|
||||
ticket->drawToSurface(_renderSurface, dstRect, clipRect);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseRenderOSystem::drawLine(int x1, int y1, int x2, int y2, uint32 color) {
|
||||
#if 0
|
||||
byte r = RGBCOLGetR(color);
|
||||
byte g = RGBCOLGetG(color);
|
||||
byte b = RGBCOLGetB(color);
|
||||
byte a = RGBCOLGetA(color);
|
||||
#endif
|
||||
|
||||
Common::Point32 point1, point2;
|
||||
point1.x = x1;
|
||||
point1.y = y1;
|
||||
pointToScreen(&point1);
|
||||
|
||||
point2.x = x2;
|
||||
point2.y = y2;
|
||||
pointToScreen(&point2);
|
||||
|
||||
// TODO
|
||||
#if 0
|
||||
uint32 colorVal = _renderSurface->format.ARGBToColor(a, r, g, b);
|
||||
_renderSurface->drawLine(point1.x, point1.y, point2.x + 1, point2.y + 1, colorVal);
|
||||
#endif
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseRenderOSystem::fillRect(int x, int y, int w, int h, uint32 color) {
|
||||
// This function isn't used outside of indicator-displaying, and thus quite unused in
|
||||
// BaseRenderOSystem when dirty-rects are enabled.
|
||||
if (!_disableDirtyRects && !_game->_indicatorDisplay) {
|
||||
error("BaseRenderOSystem::fillRect - doesn't work for dirty rects yet");
|
||||
}
|
||||
|
||||
byte r = RGBCOLGetR(color);
|
||||
byte g = RGBCOLGetG(color);
|
||||
byte b = RGBCOLGetB(color);
|
||||
byte a = RGBCOLGetA(color);
|
||||
|
||||
Common::Rect fillRect(x, y, x + w, y + w);
|
||||
modTargetRect(&fillRect);
|
||||
|
||||
uint32 colorVal = _renderSurface->format.ARGBToColor(a, r, g, b);
|
||||
_renderSurface->fillRect(fillRect, colorVal);
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
BaseImage *BaseRenderOSystem::takeScreenshot(int newWidth, int newHeight) {
|
||||
// TODO: Clip by viewport.
|
||||
BaseImage *screenshot = new BaseImage();
|
||||
screenshot->copyFrom(_renderSurface->surfacePtr(), newWidth, newHeight);
|
||||
return screenshot;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
Common::String BaseRenderOSystem::getName() const {
|
||||
return "ScummVM-OSystem-renderer";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseRenderOSystem::setViewport(int left, int top, int right, int bottom) {
|
||||
Common::Rect rect;
|
||||
rect.left = (int16)(left + _borderLeft);
|
||||
rect.top = (int16)(top + _borderTop);
|
||||
rect.setWidth((int16)((right - left) * _ratioX));
|
||||
rect.setHeight((int16)((bottom - top) * _ratioY));
|
||||
|
||||
_renderRect = rect;
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void BaseRenderOSystem::modTargetRect(Common::Rect *rect) {
|
||||
return;
|
||||
int newWidth = (int16)MathUtil::roundUp(rect->width() * _ratioX);
|
||||
int newHeight = (int16)MathUtil::roundUp(rect->height() * _ratioY);
|
||||
rect->left = (int16)MathUtil::round(rect->left * _ratioX + _borderLeft);
|
||||
rect->top = (int16)MathUtil::round(rect->top * _ratioY + _borderTop);
|
||||
rect->setWidth(newWidth);
|
||||
rect->setHeight(newHeight);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void BaseRenderOSystem::pointFromScreen(Common::Point32 *point) {
|
||||
point->x = (int16)(point->x / _ratioX - _borderLeft / _ratioX + _renderRect.left);
|
||||
point->y = (int16)(point->y / _ratioY - _borderTop / _ratioY + _renderRect.top);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void BaseRenderOSystem::pointToScreen(Common::Point32 *point) {
|
||||
point->x = (int16)MathUtil::roundUp(point->x * _ratioX) + _borderLeft - _renderRect.left;
|
||||
point->y = (int16)MathUtil::roundUp(point->y * _ratioY) + _borderTop - _renderRect.top;
|
||||
}
|
||||
|
||||
BaseSurface *BaseRenderOSystem::createSurface() {
|
||||
return new BaseSurfaceOSystem(_game);
|
||||
}
|
||||
|
||||
void BaseRenderOSystem::endSaveLoad() {
|
||||
BaseRenderer::endSaveLoad();
|
||||
|
||||
// Clear the scale-buffered tickets as we just loaded.
|
||||
RenderQueueIterator it = _renderQueue.begin();
|
||||
while (it != _renderQueue.end()) {
|
||||
RenderTicket *ticket = *it;
|
||||
it = _renderQueue.erase(it);
|
||||
delete ticket;
|
||||
}
|
||||
// HACK: After a save the buffer will be drawn before the scripts get to update it,
|
||||
// so just skip this single frame.
|
||||
_skipThisFrame = true;
|
||||
_lastFrameIter = _renderQueue.end();
|
||||
|
||||
_renderSurface->fillRect(Common::Rect(0, 0, _renderSurface->w, _renderSurface->h), _renderSurface->format.ARGBToColor(255, 0, 0, 0));
|
||||
g_system->fillScreen(Common::Rect(0, 0, _renderSurface->w, _renderSurface->h), _renderSurface->format.ARGBToColor(255, 0, 0, 0));
|
||||
g_system->updateScreen();
|
||||
}
|
||||
|
||||
bool BaseRenderOSystem::startSpriteBatch() {
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
bool BaseRenderOSystem::endSpriteBatch() {
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
} // End of namespace Wintermute
|
||||
155
engines/wintermute/base/gfx/osystem/base_render_osystem.h
Normal file
155
engines/wintermute/base/gfx/osystem/base_render_osystem.h
Normal file
@@ -0,0 +1,155 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME Lite.
|
||||
* http://dead-code.org/redir.php?target=wmelite
|
||||
* Copyright (c) 2011 Jan Nedoma
|
||||
*/
|
||||
|
||||
#ifndef WINTERMUTE_BASE_RENDERER_SDL_H
|
||||
#define WINTERMUTE_BASE_RENDERER_SDL_H
|
||||
|
||||
#include "engines/wintermute/base/gfx/base_renderer.h"
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "common/list.h"
|
||||
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/transform_struct.h"
|
||||
|
||||
namespace Wintermute {
|
||||
class BaseSurfaceOSystem;
|
||||
class RenderTicket;
|
||||
/**
|
||||
* A 2D-renderer implementation for WME.
|
||||
* This renderer makes use of a "ticket"-system, where all draw-calls
|
||||
* are stored as tickets until flip() is called, and compared against the tickets
|
||||
* from last frame, to determine which calls were the same as last round
|
||||
* (i.e. in the exact same order, with the exact same arguments), and thus
|
||||
* figure out which parts of the screen need to be redrawn.
|
||||
*
|
||||
* Important concepts to handle here, is the ordered number of any ticket
|
||||
* which is called the "drawNum", every frame this starts from scratch, and
|
||||
* then the incoming tickets created from the draw-calls are checked to see whether
|
||||
* they came before, on, or after the drawNum they had last frame. Everything else
|
||||
* being equal, this information is then used to check whether the draw order changed,
|
||||
* which will then create a need for redrawing, as we draw with an alpha-channel here.
|
||||
*
|
||||
* There is also a draw path that draws without tickets, for debugging purposes,
|
||||
* as well as to accommodate situations with large enough amounts of draw calls,
|
||||
* that there will be too much overhead involved with comparing the generated tickets.
|
||||
*/
|
||||
class BaseRenderOSystem : public BaseRenderer {
|
||||
public:
|
||||
BaseRenderOSystem(BaseGame *inGame);
|
||||
~BaseRenderOSystem() override;
|
||||
|
||||
typedef Common::List<RenderTicket *>::iterator RenderQueueIterator;
|
||||
|
||||
Common::String getName() const override;
|
||||
|
||||
bool initRenderer(int width, int height, bool windowed) override;
|
||||
bool flip() override;
|
||||
bool indicatorFlip(int32 x, int32 y, int32 width, int32 height) override;
|
||||
bool forcedFlip() override;
|
||||
bool clear() override;
|
||||
Graphics::PixelFormat getPixelFormat() const override;
|
||||
bool fade(uint16 alpha) override;
|
||||
bool fadeToColor(byte r, byte g, byte b, byte a) override;
|
||||
|
||||
bool drawLine(int x1, int y1, int x2, int y2, uint32 color) override;
|
||||
bool fillRect(int x, int y, int w, int h, uint32 color) override;
|
||||
|
||||
BaseImage *takeScreenshot(int newWidth = 0, int newHeight = 0) override;
|
||||
void onWindowChange() override;
|
||||
void setWindowed(bool windowed) override;
|
||||
|
||||
void invalidateTicket(RenderTicket *renderTicket);
|
||||
void invalidateTicketsFromSurface(BaseSurfaceOSystem *surf);
|
||||
/**
|
||||
* Insert a new ticket into the queue, adding a dirty rect
|
||||
* @param renderTicket the ticket to be added.
|
||||
*/
|
||||
void drawFromTicket(RenderTicket *renderTicket);
|
||||
/**
|
||||
* Re-insert an existing ticket into the queue, adding a dirty rect
|
||||
* out-of-order from last draw from the ticket.
|
||||
* @param ticket iterator pointing to the ticket to be added.
|
||||
*/
|
||||
void drawFromQueuedTicket(const RenderQueueIterator &ticket);
|
||||
|
||||
bool setViewport(int left, int top, int right, int bottom) override;
|
||||
bool setViewport(Common::Rect32 *rect) override { return BaseRenderer::setViewport(rect); }
|
||||
void modTargetRect(Common::Rect *rect);
|
||||
void pointFromScreen(Common::Point32 *point);
|
||||
void pointToScreen(Common::Point32 *point);
|
||||
|
||||
float getScaleRatioX() const override {
|
||||
return _ratioX;
|
||||
}
|
||||
float getScaleRatioY() const override {
|
||||
return _ratioY;
|
||||
}
|
||||
bool startSpriteBatch() override;
|
||||
bool endSpriteBatch() override;
|
||||
void endSaveLoad() override;
|
||||
void drawSurface(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, Graphics::TransformStruct &transform);
|
||||
BaseSurface *createSurface() override;
|
||||
private:
|
||||
/**
|
||||
* Mark a specified rect of the screen as dirty.
|
||||
* @param rect the region to be marked as dirty
|
||||
*/
|
||||
void addDirtyRect(const Common::Rect &rect);
|
||||
/**
|
||||
* Traverse the tickets that are dirty, and draw them
|
||||
*/
|
||||
void drawTickets();
|
||||
// Non-dirty-rects:
|
||||
void drawFromSurface(RenderTicket *ticket);
|
||||
// Dirty-rects:
|
||||
void drawFromSurface(RenderTicket *ticket, Common::Rect *dstRect, Common::Rect *clipRect);
|
||||
Common::Rect *_dirtyRect;
|
||||
Common::List<RenderTicket *> _renderQueue;
|
||||
|
||||
bool _needsFlip;
|
||||
RenderQueueIterator _lastFrameIter;
|
||||
Common::Rect _renderRect;
|
||||
Graphics::ManagedSurface *_renderSurface;
|
||||
|
||||
int _borderLeft;
|
||||
int _borderTop;
|
||||
int _borderRight;
|
||||
int _borderBottom;
|
||||
|
||||
bool _disableDirtyRects;
|
||||
float _ratioX;
|
||||
float _ratioY;
|
||||
uint32 _clearColor;
|
||||
|
||||
bool _skipThisFrame;
|
||||
int _lastScreenChangeID; // previous value of OSystem::getScreenChangeID()
|
||||
};
|
||||
|
||||
} // End of namespace Wintermute
|
||||
|
||||
#endif
|
||||
477
engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp
Normal file
477
engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp
Normal file
@@ -0,0 +1,477 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME Lite.
|
||||
* http://dead-code.org/redir.php?target=wmelite
|
||||
* Copyright (c) 2011 Jan Nedoma
|
||||
*/
|
||||
|
||||
#include "engines/wintermute/base/base_engine.h"
|
||||
#include "engines/wintermute/base/base_file_manager.h"
|
||||
#include "engines/wintermute/base/base_game.h"
|
||||
#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h"
|
||||
#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h"
|
||||
#include "engines/wintermute/base/gfx/base_image.h"
|
||||
#include "engines/wintermute/platform_osystem.h"
|
||||
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/transform_tools.h"
|
||||
#include "graphics/pixelformat.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#define TS_COLOR(wmeColor) \
|
||||
MS_ARGB(RGBCOLGetA(wmeColor), RGBCOLGetR(wmeColor), RGBCOLGetG(wmeColor), RGBCOLGetB(wmeColor))
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
BaseSurfaceOSystem::BaseSurfaceOSystem(BaseGame *inGame) : BaseSurface(inGame) {
|
||||
_surface = new Graphics::Surface();
|
||||
_pixelOpReady = false;
|
||||
_alphaMask = nullptr;
|
||||
_alphaType = Graphics::ALPHA_FULL;
|
||||
_alphaMaskType = Graphics::ALPHA_OPAQUE;
|
||||
_rotation = 0;
|
||||
_surfaceModified = false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
BaseSurfaceOSystem::~BaseSurfaceOSystem() {
|
||||
if (_surface) {
|
||||
if (_valid)
|
||||
_game->addMem(-_width * _height * 4);
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
_surface = nullptr;
|
||||
}
|
||||
|
||||
if (_alphaMask) {
|
||||
_alphaMask->free();
|
||||
delete _alphaMask;
|
||||
_alphaMask = nullptr;
|
||||
}
|
||||
|
||||
BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_game->_renderer);
|
||||
renderer->invalidateTicketsFromSurface(this);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::create(const char *filename, bool texture2D, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) {
|
||||
if (defaultCK) {
|
||||
ckRed = 255;
|
||||
ckGreen = 0;
|
||||
ckBlue = 255;
|
||||
}
|
||||
|
||||
BaseImage img = BaseImage();
|
||||
if (!img.getImageInfo(filename, _width, _height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lifeTime != -1 && _lifeTime == 0) {
|
||||
_valid = false;
|
||||
}
|
||||
|
||||
_ckDefault = defaultCK;
|
||||
_ckRed = ckRed;
|
||||
_ckGreen = ckGreen;
|
||||
_ckBlue = ckBlue;
|
||||
|
||||
if (!_filename || scumm_stricmp(_filename, filename) != 0) {
|
||||
setFilename(filename);
|
||||
}
|
||||
|
||||
if (_lifeTime == 0 || lifeTime == -1 || lifeTime > _lifeTime) {
|
||||
_lifeTime = lifeTime;
|
||||
}
|
||||
|
||||
_keepLoaded = keepLoaded;
|
||||
if (_keepLoaded) {
|
||||
_lifeTime = -1;
|
||||
}
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
bool BaseSurfaceOSystem::loadImage() {
|
||||
if (!_filename || !_filename[0]) {
|
||||
return false;
|
||||
}
|
||||
Common::String filename = _filename;
|
||||
|
||||
BaseImage *image = new BaseImage();
|
||||
if (!image->loadFile(filename)) {
|
||||
delete image;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_surface) {
|
||||
if (_valid)
|
||||
_game->addMem(-_width * _height * 4);
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
_surface = nullptr;
|
||||
}
|
||||
|
||||
_width = image->getSurface()->w;
|
||||
_height = image->getSurface()->h;
|
||||
|
||||
bool needsColorKey = false;
|
||||
bool replaceAlpha = true;
|
||||
if (image->getSurface()->format.bytesPerPixel == 1) {
|
||||
if (!image->getPalette()) {
|
||||
error("Missing palette while loading 8bit image %s", _filename);
|
||||
}
|
||||
_surface = image->getSurface()->convertTo(g_system->getScreenFormat(), image->getPalette(), image->getPaletteCount());
|
||||
} else if (image->getSurface()->format != g_system->getScreenFormat()) {
|
||||
_surface = image->getSurface()->convertTo(g_system->getScreenFormat());
|
||||
} else {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->copyFrom(*image->getSurface());
|
||||
}
|
||||
|
||||
_game->addMem(_width * _height * 4);
|
||||
|
||||
if (filename.matchString("savegame:*g", true)) {
|
||||
uint8 r, g, b, a;
|
||||
for (int x = 0; x < _surface->w; x++) {
|
||||
for (int y = 0; y < _surface->h; y++) {
|
||||
_surface->format.colorToARGB(_surface->getPixel(x, y), a, r, g, b);
|
||||
uint8 grey = (uint8)((0.2126f * r + 0.7152f * g + 0.0722f * b) + 0.5f);
|
||||
_surface->setPixel(x, y, _surface->format.ARGBToColor(a, grey, grey, grey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filename.hasSuffix(".bmp")) {
|
||||
// Ignores alpha channel for BMPs
|
||||
needsColorKey = true;
|
||||
} else if (filename.hasSuffix(".jpg")) {
|
||||
// Ignores alpha channel for JPEGs
|
||||
needsColorKey = true;
|
||||
} else if (BaseEngine::instance().getTargetExecutable() < WME_LITE) {
|
||||
// WME 1.x always use colorkey, even for images with transparency
|
||||
needsColorKey = true;
|
||||
replaceAlpha = false;
|
||||
} else if (BaseEngine::instance().isFoxTail()) {
|
||||
// FoxTail does not use colorkey
|
||||
needsColorKey = false;
|
||||
} else if (image->getSurface()->format.aBits() == 0) {
|
||||
// generic WME Lite does not use colorkey for non-BMPs with transparency
|
||||
needsColorKey = true;
|
||||
}
|
||||
|
||||
if (needsColorKey) {
|
||||
// We set the pixel color to transparent black,
|
||||
// like D3DX, if it matches the color key.
|
||||
bool applied = _surface->applyColorKey(_ckRed, _ckGreen, _ckBlue, replaceAlpha, 0, 0, 0);
|
||||
|
||||
if (replaceAlpha || image->getSurface()->format.aBits() == 0 || image->getSurface()->format.isCLUT8())
|
||||
_alphaType = applied ? Graphics::ALPHA_BINARY : Graphics::ALPHA_OPAQUE;
|
||||
else
|
||||
_alphaType = _surface->detectAlpha();
|
||||
} else {
|
||||
_alphaType = image->getSurface()->detectAlpha();
|
||||
}
|
||||
|
||||
_valid = true;
|
||||
|
||||
delete image;
|
||||
|
||||
// Bug #6572 WME: Rosemary - Sprite flaw on going upwards
|
||||
// Some Rosemary sprites have non-fully transparent pixels
|
||||
// In original WME it wasn't seen because sprites were downscaled
|
||||
// Let's set alpha to 0 if it is smaller then some treshold
|
||||
if (BaseEngine::instance().getGameId() == "rosemary" && filename.hasPrefix("actors") &&
|
||||
_alphaType == Graphics::ALPHA_FULL && _surface->format.aBits() > 4) {
|
||||
uint32 mask = _surface->format.ARGBToColor(255, 0, 0, 0);
|
||||
uint32 treshold = _surface->format.ARGBToColor(16, 0, 0, 0);
|
||||
uint32 blank = _surface->format.ARGBToColor(0, 0, 0, 0);
|
||||
|
||||
for (int x = 0; x < _surface->w; x++) {
|
||||
for (int y = 0; y < _surface->h; y++) {
|
||||
uint32 pixel = _surface->getPixel(x, y);
|
||||
if ((pixel & mask) > blank && (pixel & mask) < treshold) {
|
||||
_surface->setPixel(x, y, blank);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::create(int width, int height) {
|
||||
if (_valid)
|
||||
_game->addMem(-_width * _height * 4);
|
||||
_surface->free();
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
|
||||
_surface->create(_width, _height, g_system->getScreenFormat());
|
||||
_game->addMem(_width * _height * 4);
|
||||
|
||||
_valid = true;
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::invalidate() {
|
||||
if (_pixelOpReady) {
|
||||
return STATUS_FAILED;
|
||||
}
|
||||
|
||||
if (_valid) {
|
||||
_game->addMem(-_width * _height * 4);
|
||||
_surface->free();
|
||||
_valid = false;
|
||||
}
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::isTransparentAtLite(int x, int y) const {
|
||||
if (!_pixelOpReady) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (x < 0 || x >= _surface->w || y < 0 || y >= _surface->h) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 pixel = _surface->getPixel(x, y);
|
||||
uint8 r, g, b, a;
|
||||
_surface->format.colorToARGB(pixel, a, r, g, b);
|
||||
// This implements the WME Lite logic by comparing alpha against 128.
|
||||
// This differs from the original WME1 sources, where alpha is compared against 0.
|
||||
// The likely reason for this discrepancy is a difference in how bitmaps are
|
||||
// converted after loading.
|
||||
if (a <= 128) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::startPixelOp() {
|
||||
if (!_valid) {
|
||||
if (DID_FAIL(loadImage())) {
|
||||
return STATUS_FAILED;
|
||||
}
|
||||
}
|
||||
_pixelOpReady = true;
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::endPixelOp() {
|
||||
_lastUsedTime = _game->_liveTimer;
|
||||
_pixelOpReady = false;
|
||||
if (_surfaceModified) {
|
||||
BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_game->_renderer);
|
||||
renderer->invalidateTicketsFromSurface(this);
|
||||
_surfaceModified = false;
|
||||
}
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::display(int x, int y, Common::Rect32 rect, Graphics::TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
|
||||
_rotation = 0;
|
||||
return drawSprite(x, y, &rect, nullptr, Graphics::TransformStruct(Graphics::kDefaultZoomX, Graphics::kDefaultZoomY, mirrorX, mirrorY));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::displayTrans(int x, int y, Common::Rect32 rect, uint32 alpha, Graphics::TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX, int offsetY) {
|
||||
_rotation = 0;
|
||||
return drawSprite(x, y, &rect, nullptr, Graphics::TransformStruct(Graphics::kDefaultZoomX, Graphics::kDefaultZoomY, Graphics::kDefaultAngle, Graphics::kDefaultHotspotX, Graphics::kDefaultHotspotY, blendMode, TS_COLOR(alpha), mirrorX, mirrorY, offsetX, offsetY));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::displayTransZoom(int x, int y, Common::Rect32 rect, float zoomX, float zoomY, uint32 alpha, Graphics::TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
|
||||
_rotation = 0;
|
||||
return drawSprite(x, y, &rect, nullptr, Graphics::TransformStruct((int32)zoomX, (int32)zoomY, blendMode, TS_COLOR(alpha), mirrorX, mirrorY));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::displayTransRotate(int x, int y, float rotate, int32 hotspotX, int32 hotspotY, Common::Rect32 rect, float zoomX, float zoomY, uint32 alpha, Graphics::TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) {
|
||||
Common::Point newHotspot;
|
||||
Common::Rect oldRect(rect.left, rect.top, rect.right, rect.bottom);
|
||||
Graphics::TransformStruct transform = Graphics::TransformStruct(zoomX, zoomY, rotate, hotspotX, hotspotY, blendMode, TS_COLOR(alpha), mirrorX, mirrorY, 0, 0);
|
||||
Common::Rect newRect = Graphics::TransformTools::newRect(oldRect, transform, &newHotspot);
|
||||
Common::Rect32 newRect32(newRect.left, newRect.top, newRect.right, newRect.bottom);
|
||||
|
||||
x -= newHotspot.x;
|
||||
y -= newHotspot.y;
|
||||
|
||||
_rotation = transform._angle;
|
||||
if (transform._angle < 0.0f) {
|
||||
warning("Negative rotation: %d %d", (int32)transform._angle, (int32)_rotation);
|
||||
_rotation = 360.0f + transform._angle;
|
||||
warning("Negative post rotation: %d %d", (int32)transform._angle, (int32)_rotation);
|
||||
}
|
||||
return drawSprite(x, y, &rect, &newRect32, transform);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::displayTiled(int x, int y, Common::Rect32 rect, int numTimesX, int numTimesY) {
|
||||
assert(numTimesX > 0 && numTimesY > 0);
|
||||
Graphics::TransformStruct transform(numTimesX, numTimesY);
|
||||
return drawSprite(x, y, &rect, nullptr, transform);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::drawSprite(int x, int y, Common::Rect32 *rect, Common::Rect32 *newRect, Graphics::TransformStruct transform) {
|
||||
BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_game->_renderer);
|
||||
|
||||
_lastUsedTime = _game->_liveTimer;
|
||||
|
||||
// TODO: Skip this check if we can reuse an existing ticket?
|
||||
if (!_valid) {
|
||||
loadImage();
|
||||
}
|
||||
|
||||
if (renderer->_forceAlphaColor != 0) {
|
||||
transform._rgbaMod = TS_COLOR(renderer->_forceAlphaColor);
|
||||
}
|
||||
|
||||
// TODO: This _might_ miss the intended behaviour by 1 in each direction
|
||||
// But I think it fits the model used in Wintermute.
|
||||
Common::Rect srcRect;
|
||||
srcRect.left = rect->left;
|
||||
srcRect.top = rect->top;
|
||||
srcRect.setWidth(rect->right - rect->left);
|
||||
srcRect.setHeight(rect->bottom - rect->top);
|
||||
|
||||
Common::Rect position;
|
||||
|
||||
if (newRect) {
|
||||
position.top = y;
|
||||
position.left = x;
|
||||
position.setWidth(newRect->width());
|
||||
position.setHeight(newRect->height());
|
||||
} else {
|
||||
|
||||
Common::Rect r;
|
||||
r.top = 0;
|
||||
r.left = 0;
|
||||
r.setWidth(rect->width());
|
||||
r.setHeight(rect->height());
|
||||
|
||||
r = Graphics::TransformTools::newRect(r, transform, 0);
|
||||
|
||||
position.top = r.top + y + transform._offset.y;
|
||||
position.left = r.left + x + transform._offset.x;
|
||||
position.setWidth(r.width() * transform._numTimesX);
|
||||
position.setHeight(r.height() * transform._numTimesY);
|
||||
}
|
||||
renderer->modTargetRect(&position);
|
||||
|
||||
// TODO: This actually requires us to have the SAME source-offsets every time,
|
||||
// But no checking is in place for that yet.
|
||||
|
||||
// Optimize by not doing alpha-blits if we lack alpha
|
||||
// If angle is not 0, then transparent regions are added near the corners
|
||||
if (_alphaType == Graphics::ALPHA_OPAQUE && _alphaMaskType == Graphics::ALPHA_OPAQUE &&
|
||||
transform._angle == 0) {
|
||||
transform._alphaDisable = true;
|
||||
}
|
||||
|
||||
renderer->drawSurface(this, _surface, &srcRect, &position, transform);
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
bool BaseSurfaceOSystem::putSurface(const Graphics::Surface &surface, bool hasAlpha) {
|
||||
_surface->copyRectToSurface(surface, 0, 0, Common::Rect(surface.w, surface.h));
|
||||
writeAlpha(_surface, _alphaMask);
|
||||
|
||||
if (hasAlpha) {
|
||||
_alphaType = _surface->detectAlpha();
|
||||
} else {
|
||||
_alphaType = _alphaMaskType;
|
||||
}
|
||||
BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_game->_renderer);
|
||||
renderer->invalidateTicketsFromSurface(this);
|
||||
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool BaseSurfaceOSystem::setAlphaImage(const char *filename) {
|
||||
BaseImage *alphaImage = new BaseImage();
|
||||
if (!alphaImage->loadFile(filename)) {
|
||||
delete alphaImage;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_alphaMask) {
|
||||
_alphaMask->free();
|
||||
delete _alphaMask;
|
||||
_alphaMask = nullptr;
|
||||
}
|
||||
|
||||
_alphaMaskType = alphaImage->getSurface()->detectAlpha();
|
||||
if (_alphaMaskType != Graphics::ALPHA_OPAQUE) {
|
||||
_alphaMask = alphaImage->getSurface()->convertTo(g_system->getScreenFormat());
|
||||
}
|
||||
|
||||
delete alphaImage;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BaseSurfaceOSystem::writeAlpha(Graphics::Surface *surface, const Graphics::Surface *mask) {
|
||||
if (mask && surface->w == mask->w && surface->h == mask->h) {
|
||||
assert(mask->pitch == mask->w * 4);
|
||||
assert(mask->format.bytesPerPixel == 4);
|
||||
assert(surface->pitch == surface->w * 4);
|
||||
assert(surface->format.bytesPerPixel == 4);
|
||||
const byte *alphaData = (const byte *)mask->getPixels();
|
||||
#ifdef SCUMM_LITTLE_ENDIAN
|
||||
int alphaPlace = (mask->format.aShift / 8);
|
||||
#else
|
||||
int alphaPlace = 3 - (mask->format.aShift / 8);
|
||||
#endif
|
||||
alphaData += alphaPlace;
|
||||
byte *imgData = (byte *)surface->getPixels();
|
||||
#ifdef SCUMM_LITTLE_ENDIAN
|
||||
imgData += (surface->format.aShift / 8);
|
||||
#else
|
||||
imgData += 3 - (surface->format.aShift / 8);
|
||||
#endif
|
||||
for (int i = 0; i < surface->w * surface->h; i++) {
|
||||
*imgData = *alphaData;
|
||||
alphaData += 4;
|
||||
imgData += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Wintermute
|
||||
107
engines/wintermute/base/gfx/osystem/base_surface_osystem.h
Normal file
107
engines/wintermute/base/gfx/osystem/base_surface_osystem.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME Lite.
|
||||
* http://dead-code.org/redir.php?target=wmelite
|
||||
* Copyright (c) 2011 Jan Nedoma
|
||||
*/
|
||||
|
||||
#ifndef WINTERMUTE_BASE_SURFACESDL_H
|
||||
#define WINTERMUTE_BASE_SURFACESDL_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/transform_struct.h" // for Graphics::AlphaType
|
||||
|
||||
#include "engines/wintermute/base/gfx/base_surface.h"
|
||||
|
||||
#include "common/list.h"
|
||||
|
||||
namespace Wintermute {
|
||||
class BaseImage;
|
||||
class BaseSurfaceOSystem : public BaseSurface {
|
||||
public:
|
||||
BaseSurfaceOSystem(BaseGame *inGame);
|
||||
~BaseSurfaceOSystem() override;
|
||||
|
||||
bool create(const char *filename, bool texture2D, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime = -1, bool keepLoaded = false) override;
|
||||
bool create(int width, int height) override;
|
||||
|
||||
bool setAlphaImage(const char *filename) override;
|
||||
|
||||
bool invalidate() override;
|
||||
|
||||
bool isTransparentAtLite(int x, int y) const override;
|
||||
bool startPixelOp() override;
|
||||
bool endPixelOp() override;
|
||||
|
||||
bool displayTransRotate(int x, int y, float rotate, int32 hotspotX, int32 hotspotY, Common::Rect32 rect, float zoomX, float zoomY, uint32 alpha = Graphics::kDefaultRgbaMod, Graphics::TSpriteBlendMode blendMode = Graphics::BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) override;
|
||||
bool displayTransZoom(int x, int y, Common::Rect32 rect, float zoomX, float zoomY, uint32 alpha = Graphics::kDefaultRgbaMod, Graphics::TSpriteBlendMode blendMode = Graphics::BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) override;
|
||||
bool displayTrans(int x, int y, Common::Rect32 rect, uint32 alpha = Graphics::kDefaultRgbaMod, Graphics::TSpriteBlendMode blendMode = Graphics::BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false, int offsetX = 0, int offsetY = 0) override;
|
||||
bool display(int x, int y, Common::Rect32 rect, Graphics::TSpriteBlendMode blendMode = Graphics::BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) override;
|
||||
bool displayTiled(int x, int y, Common::Rect32 rect, int numTimesX, int numTimesY) override;
|
||||
bool putSurface(const Graphics::Surface &surface, bool hasAlpha = false) override;
|
||||
int getWidth() override {
|
||||
return _width;
|
||||
}
|
||||
int getHeight() override {
|
||||
return _height;
|
||||
}
|
||||
bool putPixel(int x, int y, byte r, byte g, byte b, byte a) override {
|
||||
if (!_pixelOpReady) {
|
||||
return STATUS_FAILED;
|
||||
}
|
||||
if (_surface) {
|
||||
_surface->setPixel(x, y, _surface->format.ARGBToColor(a, r, g, b));
|
||||
_surfaceModified = true;
|
||||
return STATUS_OK;
|
||||
}
|
||||
return STATUS_FAILED;
|
||||
}
|
||||
bool getPixel(int x, int y, byte *r, byte *g, byte *b, byte *a) const override {
|
||||
if (!_pixelOpReady) {
|
||||
return STATUS_FAILED;
|
||||
}
|
||||
if (_surface) {
|
||||
_surface->format.colorToARGB(_surface->getPixel(x, y), *a, *r, *g, *b);
|
||||
return STATUS_OK;
|
||||
}
|
||||
return STATUS_FAILED;
|
||||
}
|
||||
|
||||
Graphics::AlphaType getAlphaType() const { return _alphaType; }
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
bool loadImage();
|
||||
bool drawSprite(int x, int y, Common::Rect32 *rect, Common::Rect32 *newRect, Graphics::TransformStruct transformStruct);
|
||||
void writeAlpha(Graphics::Surface *surface, const Graphics::Surface *mask);
|
||||
|
||||
bool _pixelOpReady;
|
||||
bool _surfaceModified;
|
||||
float _rotation;
|
||||
Graphics::AlphaType _alphaType;
|
||||
Graphics::Surface *_alphaMask;
|
||||
Graphics::AlphaType _alphaMaskType;
|
||||
};
|
||||
|
||||
} // End of namespace Wintermute
|
||||
|
||||
#endif
|
||||
201
engines/wintermute/base/gfx/osystem/render_ticket.cpp
Normal file
201
engines/wintermute/base/gfx/osystem/render_ticket.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME Lite.
|
||||
* http://dead-code.org/redir.php?target=wmelite
|
||||
* Copyright (c) 2011 Jan Nedoma
|
||||
*/
|
||||
|
||||
|
||||
#include "engines/wintermute/base/base_game.h"
|
||||
#include "engines/wintermute/base/gfx/osystem/render_ticket.h"
|
||||
#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h"
|
||||
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
RenderTicket::RenderTicket(BaseSurfaceOSystem *owner, const Graphics::Surface *surf,
|
||||
Common::Rect *srcRect, Common::Rect *dstRect, Graphics::TransformStruct transform) :
|
||||
_owner(owner),
|
||||
_srcRect(*srcRect),
|
||||
_dstRect(*dstRect),
|
||||
_isValid(true),
|
||||
_wantsDraw(true),
|
||||
_transform(transform) {
|
||||
if (surf) {
|
||||
assert(surf->format.bytesPerPixel == 4);
|
||||
|
||||
// Get a clipped view of the surface
|
||||
const Graphics::Surface temp = surf->getSubArea(*srcRect);
|
||||
|
||||
// Then copy and scale it as necessary
|
||||
//
|
||||
// NB: The numTimesX/numTimesY properties don't yet mix well with
|
||||
// scaling and rotation, but there is no need for that functionality at
|
||||
// the moment.
|
||||
// NB: Mirroring and rotation are probably done in the wrong order.
|
||||
// (Mirroring should most likely be done before rotation. See also
|
||||
// TransformTools.)
|
||||
if (_transform._angle != Graphics::kDefaultAngle) {
|
||||
_surface = temp.rotoscale(transform, owner->_game->getBilinearFiltering());
|
||||
} else if ((dstRect->width() != srcRect->width() ||
|
||||
dstRect->height() != srcRect->height()) &&
|
||||
_transform._numTimesX * _transform._numTimesY == 1) {
|
||||
_surface = temp.scale(dstRect->width(), dstRect->height(), owner->_game->getBilinearFiltering());
|
||||
} else {
|
||||
_surface = new Graphics::Surface();
|
||||
_surface->copyFrom(temp);
|
||||
}
|
||||
} else {
|
||||
_surface = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RenderTicket::~RenderTicket() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderTicket::operator==(const RenderTicket &t) const {
|
||||
if ((t._owner != _owner) ||
|
||||
(t._transform != _transform) ||
|
||||
(t._dstRect != _dstRect) ||
|
||||
(t._srcRect != _srcRect)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Replacement for SDL2's SDL_RenderCopy
|
||||
void RenderTicket::drawToSurface(Graphics::ManagedSurface *_targetSurface) const {
|
||||
if (!getSurface()) {
|
||||
_targetSurface->blendFillRect(_dstRect, _transform._rgbaMod, Graphics::BLEND_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Rect clipRect;
|
||||
clipRect.setWidth(getSurface()->w);
|
||||
clipRect.setHeight(getSurface()->h);
|
||||
|
||||
Graphics::AlphaType alphaMode = Graphics::ALPHA_FULL;
|
||||
|
||||
if (_owner) {
|
||||
if (_transform._alphaDisable) {
|
||||
alphaMode = Graphics::ALPHA_OPAQUE;
|
||||
} else if (_transform._angle) {
|
||||
alphaMode = Graphics::ALPHA_FULL;
|
||||
} else {
|
||||
alphaMode = _owner->getAlphaType();
|
||||
}
|
||||
}
|
||||
|
||||
int y = _dstRect.top;
|
||||
int w = _dstRect.width() / _transform._numTimesX;
|
||||
int h = _dstRect.height() / _transform._numTimesY;
|
||||
|
||||
for (int ry = 0; ry < _transform._numTimesY; ++ry) {
|
||||
int x = _dstRect.left;
|
||||
for (int rx = 0; rx < _transform._numTimesX; ++rx) {
|
||||
_targetSurface->blendBlitFrom(*getSurface(), clipRect, Common::Point(x, y),
|
||||
_transform._flip, _transform._rgbaMod, Graphics::BLEND_NORMAL, alphaMode);
|
||||
x += w;
|
||||
}
|
||||
y += h;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTicket::drawToSurface(Graphics::ManagedSurface *_targetSurface, Common::Rect *dstRect, Common::Rect *clipRect) const {
|
||||
if (!getSurface()) {
|
||||
_targetSurface->blendFillRect(*dstRect, _transform._rgbaMod, _transform._blendMode);
|
||||
return;
|
||||
}
|
||||
|
||||
bool doDelete = false;
|
||||
if (!clipRect) {
|
||||
doDelete = true;
|
||||
clipRect = new Common::Rect();
|
||||
clipRect->setWidth(getSurface()->w * _transform._numTimesX);
|
||||
clipRect->setHeight(getSurface()->h * _transform._numTimesY);
|
||||
}
|
||||
|
||||
Graphics::AlphaType alphaMode = Graphics::ALPHA_FULL;
|
||||
|
||||
if (_owner) {
|
||||
if (_transform._alphaDisable) {
|
||||
alphaMode = Graphics::ALPHA_OPAQUE;
|
||||
} else if (_transform._angle) {
|
||||
alphaMode = Graphics::ALPHA_FULL;
|
||||
} else {
|
||||
alphaMode = _owner->getAlphaType();
|
||||
}
|
||||
}
|
||||
|
||||
if (_transform._numTimesX * _transform._numTimesY == 1) {
|
||||
_targetSurface->blendBlitFrom(*getSurface(), *clipRect, Common::Point(dstRect->left, dstRect->top),
|
||||
_transform._flip, _transform._rgbaMod, _transform._blendMode, alphaMode);
|
||||
} else {
|
||||
// clipRect is a subrect of the full numTimesX*numTimesY rect
|
||||
Common::Rect subRect;
|
||||
|
||||
int y = 0;
|
||||
int w = getSurface()->w;
|
||||
int h = getSurface()->h;
|
||||
assert(w == _dstRect.width() / _transform._numTimesX);
|
||||
assert(h == _dstRect.height() / _transform._numTimesY);
|
||||
|
||||
int basex = dstRect->left - clipRect->left;
|
||||
int basey = dstRect->top - clipRect->top;
|
||||
|
||||
for (int ry = 0; ry < _transform._numTimesY; ++ry) {
|
||||
int x = 0;
|
||||
for (int rx = 0; rx < _transform._numTimesX; ++rx) {
|
||||
subRect.left = x;
|
||||
subRect.top = y;
|
||||
subRect.setWidth(w);
|
||||
subRect.setHeight(h);
|
||||
|
||||
if (subRect.intersects(*clipRect)) {
|
||||
subRect.clip(*clipRect);
|
||||
subRect.translate(-x, -y);
|
||||
_targetSurface->blendBlitFrom(*getSurface(), subRect,
|
||||
Common::Point(basex + x + subRect.left, basey + y + subRect.top),
|
||||
_transform._flip, _transform._rgbaMod, _transform._blendMode, alphaMode);
|
||||
}
|
||||
|
||||
x += w;
|
||||
}
|
||||
y += h;
|
||||
}
|
||||
}
|
||||
|
||||
if (doDelete) {
|
||||
delete clipRect;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Wintermute
|
||||
79
engines/wintermute/base/gfx/osystem/render_ticket.h
Normal file
79
engines/wintermute/base/gfx/osystem/render_ticket.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is based on WME Lite.
|
||||
* http://dead-code.org/redir.php?target=wmelite
|
||||
* Copyright (c) 2011 Jan Nedoma
|
||||
*/
|
||||
|
||||
#ifndef WINTERMUTE_RENDER_TICKET_H
|
||||
#define WINTERMUTE_RENDER_TICKET_H
|
||||
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Wintermute {
|
||||
|
||||
class BaseSurfaceOSystem;
|
||||
/**
|
||||
* A single RenderTicket.
|
||||
* A render ticket is a collection of the data and draw specifications made
|
||||
* for a single draw-call in the OSystem-backend for WME. The ticket additionally
|
||||
* holds the order in which this call was made, so that it can be detected if
|
||||
* the same call is done in the following frame. Thus allowing us to potentially
|
||||
* skip drawing the same region again, unless anything has changed. Since a surface
|
||||
* can have a potentially large amount of draw-calls made to it, at varying rotation,
|
||||
* zoom, and crop-levels we also need to hold a copy of the necessary data.
|
||||
* (Video-surfaces may even change their data). The promise that is made when a ticket
|
||||
* is created is that what the state was of the surface at THAT point, is what will end
|
||||
* up on screen at flip() time.
|
||||
*/
|
||||
class RenderTicket {
|
||||
public:
|
||||
RenderTicket(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRest, Graphics::TransformStruct transform);
|
||||
RenderTicket() : _isValid(true), _wantsDraw(false), _transform(Graphics::TransformStruct()) {}
|
||||
~RenderTicket();
|
||||
const Graphics::Surface *getSurface() const { return _surface; }
|
||||
// Non-dirty-rects:
|
||||
void drawToSurface(Graphics::ManagedSurface *_targetSurface) const;
|
||||
// Dirty-rects:
|
||||
void drawToSurface(Graphics::ManagedSurface *_targetSurface, Common::Rect *dstRect, Common::Rect *clipRect) const;
|
||||
|
||||
Common::Rect _dstRect;
|
||||
|
||||
bool _isValid;
|
||||
bool _wantsDraw;
|
||||
|
||||
Graphics::TransformStruct _transform;
|
||||
|
||||
BaseSurfaceOSystem *_owner;
|
||||
bool operator==(const RenderTicket &a) const;
|
||||
const Common::Rect *getSrcRect() const { return &_srcRect; }
|
||||
private:
|
||||
Graphics::Surface *_surface;
|
||||
Common::Rect _srcRect;
|
||||
};
|
||||
|
||||
} // End of namespace Wintermute
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user