scummvm/engines/nancy/graphics.cpp
fracturehill a16c9b90ae NANCY: Clean up graphics loop
Removed some leftovers from an older version of the graphics manager and made the drawing code only loop once through all render objects instead of twice.
2021-03-24 17:20:46 +01:00

264 lines
9.4 KiB
C++

/* 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 "engines/nancy/graphics.h"
#include "engines/nancy/renderobject.h"
#include "engines/nancy/resource.h"
#include "engines/nancy/nancy.h"
#include "engines/nancy/resource.h"
#include "engines/nancy/cursor.h"
#include "engines/nancy/state/scene.h"
#include "engines/nancy/ui/viewport.h"
#include "common/file.h"
#include "common/system.h"
#include "image/bmp.h"
#include "engines/util.h"
namespace Nancy {
const Graphics::PixelFormat GraphicsManager::inputPixelFormat = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
const Graphics::PixelFormat GraphicsManager::screenPixelFormat = Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
const Graphics::PixelFormat GraphicsManager::clut8Format = Graphics::PixelFormat::createFormatCLUT8();
void GraphicsManager::init() {
initGraphics(640, 480, &screenPixelFormat);
_screen.create(640, 480, screenPixelFormat);
_screen.setTransparentColor(getTransColor());
_screen.clear();
Common::SeekableReadStream *ob = NanEngine.getBootChunkStream("OB0");
ob->seek(0);
NanEngine.resource->loadImage(ob->readString(), object0);
loadFonts();
}
void GraphicsManager::draw() {
for (auto it : _objects) {
RenderObject &current = *it;
current.updateGraphics();
if (current._isVisible && current._needsRedraw) {
// object is visible and updated
if (current._redrawFrom) {
if (current.hasMoved() && !current.getPreviousScreenPosition().isEmpty()) {
// Redraw previous location if moved
blitToScreen(*current._redrawFrom, current.getPreviousScreenPosition());
}
if (current._drawSurface.hasTransparentColor()) {
// Redraw below if transparent
blitToScreen(*current._redrawFrom, current.getScreenPosition());
}
}
// Draw the object itself
blitToScreen(current, current.getScreenPosition());
} else if (!current._isVisible && current._needsRedraw && current._redrawFrom && !current.getPreviousScreenPosition().isEmpty()) {
// Object just turned invisible, redraw below
blitToScreen(*current._redrawFrom, current.getPreviousScreenPosition());
}
current._needsRedraw = false;
current._previousScreenPosition = current._screenPosition;
}
// Draw the screen
_screen.update();
}
void GraphicsManager::addObject(RenderObject *object) {
for (auto &r : _objects) {
if (r == object) {
return;
}
if (r->getZOrder() > object->getZOrder()) {
break;
}
}
_objects.insert(object);
}
void GraphicsManager::removeObject(RenderObject *object) {
for (auto &r : _objects) {
if (r == object) {
_objects.erase(&r);
break;
}
}
}
void GraphicsManager::clearObjects() {
_objects.clear();
}
void GraphicsManager::redrawAll() {
for (auto &obj : _objects) {
obj->_needsRedraw = true;
}
}
void GraphicsManager::loadSurfacePalette(Graphics::ManagedSurface &inSurf, const Common::String paletteFilename) {
Common::File f;
if (f.open(paletteFilename + ".bmp")) {
Image::BitmapDecoder dec;
if (dec.loadStream(f)) {
inSurf.setPalette(dec.getPalette(), dec.getPaletteStartIndex(), MIN<uint>(256, dec.getPaletteColorCount()));
}
}
}
void GraphicsManager::copyToManaged(const Graphics::Surface &src, Graphics::ManagedSurface &dst, bool verticalFlip, bool doubleSize) {
if (dst.w != doubleSize ? src.w * 2 : src.w || dst.h != doubleSize ? src.h * 2 : src.h) {
const uint32 *palette = dst.getPalette();
bool hasTransColor = dst.hasTransparentColor();
dst.create(doubleSize ? src.w * 2 : src.w, doubleSize ? src.h * 2 : src.h, src.format);
if (palette) {
// free() clears the _hasPalette flag but doesn't clear the palette itself, so
// we just set it to itself; hopefully this doesn't cause any issues
dst.setPalette(palette, 0, 256);
}
if (hasTransColor) {
// Do the same trick with the transparent color
dst.setTransparentColor(dst.getTransparentColor());
}
}
if (!verticalFlip && !doubleSize) {
dst.copyRectToSurface(src, 0, 0, Common::Rect(0, 0, src.w, src.h));
return;
}
for (uint y = 0; y < src.h; ++y) {
if (!doubleSize) {
// Copy single line bottom to top
memcpy(dst.getBasePtr(0, y), src.getBasePtr(0, src.h - y - 1), src.w * src.format.bytesPerPixel);
} else {
// Make four copies of each source pixel
for (uint x = 0; x < src.w; ++x) {
switch (src.format.bytesPerPixel) {
case 1: {
const byte *srcP = (const byte *)src.getBasePtr(x, y);
uint dstX = x * 2;
uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
*((byte *)dst.getBasePtr(dstX, dstY)) = *srcP;
*((byte *)dst.getBasePtr(dstX + 1, dstY)) = *srcP;
*((byte *)dst.getBasePtr(dstX, dstY + 1)) = *srcP;
*((byte *)dst.getBasePtr(dstX + 1, dstY + 1)) = *srcP;
break;
}
case 2: {
const uint16 *srcP = (const uint16 *)src.getBasePtr(x, y);
uint dstX = x * 2;
uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
*((uint16 *)dst.getBasePtr(dstX, dstY)) = *srcP;
*((uint16 *)dst.getBasePtr(dstX + 1, dstY)) = *srcP;
*((uint16 *)dst.getBasePtr(dstX, dstY + 1)) = *srcP;
*((uint16 *)dst.getBasePtr(dstX + 1, dstY + 1)) = *srcP;
break;
}
case 4: {
const uint32 *srcP = (const uint32 *)src.getBasePtr(x, y);
uint dstX = x * 2;
uint dstY = verticalFlip ? (src.h - y - 1) * 2 : src.h - y - 1;
*((uint32 *)dst.getBasePtr(dstX, dstY)) = *srcP;
*((uint32 *)dst.getBasePtr(dstX + 1, dstY)) = *srcP;
*((uint32 *)dst.getBasePtr(dstX, dstY + 1)) = *srcP;
*((uint32 *)dst.getBasePtr(dstX + 1, dstY + 1)) = *srcP;
break;
}
default:
return;
}
}
}
}
}
void GraphicsManager::copyToManaged(void *src, Graphics::ManagedSurface &dst, uint srcW, uint srcH, const Graphics::PixelFormat &format, bool verticalFlip, bool doubleSize) {
// Do things the lazy way and simply create a Surface and pass it to the other overload
// We do NOT free the surface since it's a temporary object and does not own the pixels
Graphics::Surface surf;
surf.w = srcW;
surf.h = srcH;
surf.format = format;
surf.pitch = srcW * format.bytesPerPixel;
surf.setPixels(src);
copyToManaged(surf, dst, verticalFlip, doubleSize);
}
const Graphics::PixelFormat &GraphicsManager::getInputPixelFormat() {
if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR) {
return clut8Format;
} else {
return inputPixelFormat;
}
}
uint GraphicsManager::getTransColor() {
if (NanEngine._gameDescription->desc.flags & NGF_8BITCOLOR) {
return 1; // If this isn't correct, try picking the pixel at [0, 0] inside the palette bitmap
} else {
return inputPixelFormat.ARGBToColor(0, 0, 255, 0);
}
}
void GraphicsManager::loadFonts() {
Common::SeekableReadStream *chunk = NanEngine.getBootChunkStream("FONT");
chunk->seek(0);
while (chunk->pos() < chunk->size() - 1) {
_fonts.push_back(Font());
_fonts.back().read(*chunk);
}
}
// Draw a given screen-space rectangle to the screen
void GraphicsManager::blitToScreen(const RenderObject &src, Common::Rect screenRect) {
Common::Point pointDest(screenRect.left, screenRect.top);
_screen.blitFrom(src._drawSurface, src.convertToLocal(screenRect), pointDest);
}
int GraphicsManager::objectComparator(const void *a, const void *b) {
if (((const RenderObject*)a)->getZOrder() < ((const RenderObject*)b)->getZOrder()) {
return -1;
} else if (((const RenderObject*)a)->getZOrder() > ((const RenderObject*)b)->getZOrder()) {
return 1;
} else {
return 0;
}
}
} // End of namespace Nancy