/* 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 . * */ #include "graphics/surface.h" #include "graphics/managed_surface.h" #include "graphics/cursorman.h" #include "mtropolis/render.h" #include "mtropolis/runtime.h" namespace MTropolis { template struct OrderedDitherGenerator { static void generateOrderedDither(TNumber (&pattern)[TResolution][TResolution]); }; template struct OrderedDitherGenerator { static void generateOrderedDither(TNumber (&pattern)[1][1]); }; struct RenderItem { VisualElement *element; size_t sceneStackDepth; }; template void OrderedDitherGenerator::generateOrderedDither(TNumber (&pattern)[TResolution][TResolution]) { const int kHalfResolution = TResolution / 2; TNumber halfRes[kHalfResolution][kHalfResolution]; OrderedDitherGenerator::generateOrderedDither(halfRes); const int kHalfResNumSteps = kHalfResolution * kHalfResolution; for (int y = 0; y < kHalfResolution; y++) { for (int x = 0; x < kHalfResolution; x++) { pattern[y * 2][x * 2] = halfRes[y][x]; pattern[y * 2 + 1][x * 2 + 1] = halfRes[y][x] + kHalfResNumSteps * 1; pattern[y * 2][x * 2 + 1] = halfRes[y][x] + kHalfResNumSteps * 2; pattern[y * 2 + 1][x * 2] = halfRes[y][x] + kHalfResNumSteps * 3; } } } template void OrderedDitherGenerator::generateOrderedDither(TNumber (&pattern)[1][1]) { pattern[0][0] = 0; } inline int quantize8To5Byte(int value, byte orderedDither16x16) { return (value * 249 + (orderedDither16x16 << 3)) >> 11; } inline int quantize8To5UShort(int value, uint16 orderedDither16x16) { return (value * 249 + orderedDither16x16) >> 11; } inline int expand5To8(int value) { return (value * 33) >> 2; } TextStyleFlags::TextStyleFlags() : bold(false), italic(false), underline(false), outline(false), shadow(false), condensed(false), expanded(false) { } bool TextStyleFlags::load(uint8 dataStyleFlags) { bold = ((dataStyleFlags & 0x01) != 0); italic = ((dataStyleFlags & 0x02) != 0); underline = ((dataStyleFlags & 0x03) != 0); outline = ((dataStyleFlags & 0x04) != 0); shadow = ((dataStyleFlags & 0x10) != 0); condensed = ((dataStyleFlags & 0x20) != 0); expanded = ((dataStyleFlags & 0x40) != 0); return true; } MacFontFormatting::MacFontFormatting() : fontID(0), fontFlags(0), size(12) { } MacFontFormatting::MacFontFormatting(uint16 mff_fontID, uint8 mff_fontFlags, uint16 mff_size) : fontID(mff_fontID), fontFlags(mff_fontFlags), size(mff_size) { } WindowParameters::WindowParameters(Runtime *wp_runtime, int32 wp_x, int32 wp_y, int16 wp_width, int16 wp_height, const Graphics::PixelFormat &wp_format) : runtime(wp_runtime), x(wp_x), y(wp_y), width(wp_width), height(wp_height), format(wp_format) { } Window::Window(const WindowParameters &windowParams) : _runtime(windowParams.runtime), _x(windowParams.x), _y(windowParams.y), _strata(0), _isMouseTransparent(false) { _surface.reset(new Graphics::ManagedSurface(windowParams.width, windowParams.height, windowParams.format)); } Window::~Window() { } int32 Window::getX() const { return _x; } int32 Window::getY() const { return _y; } int32 Window::getWidth() const { return _surface->w; } int32 Window::getHeight() const { return _surface->h; } void Window::setPosition(int32 x, int32 y) { _x = x; _y = y; } void Window::resizeWindow(int32 width, int32 height) { Graphics::PixelFormat pixFmt = _surface->format; _surface.reset(); _surface.reset(new Graphics::ManagedSurface(width, height, pixFmt)); } const Common::SharedPtr &Window::getSurface() const { return _surface; } const Graphics::PixelFormat& Window::getPixelFormat() const { return _surface->format; } const Common::SharedPtr &Window::getCursorGraphic() const { return _cursor; } void Window::setCursorGraphic(const Common::SharedPtr& cursor) { _cursor = cursor; } void Window::setStrata(int strata) { _strata = strata; } int Window::getStrata() const { return _strata; } // Mouse transparency = ignores mouse events void Window::setMouseTransparent(bool isTransparent) { _isMouseTransparent = isTransparent; } bool Window::isMouseTransparent() const { return _isMouseTransparent; } void Window::close() { Runtime *runtime = _runtime; _runtime = nullptr; if (runtime) runtime->removeWindow(this); } void Window::detachFromRuntime() { _runtime = nullptr; } void Window::onMouseDown(int32 x, int32 y, int mouseButton) { } void Window::onMouseMove(int32 x, int32 y) { } void Window::onMouseUp(int32 x, int32 y, int mouseButton) { } void Window::onKeyboardEvent(const Common::EventType evtType, bool repeat, const Common::KeyState &keyEvt) { } namespace Render { uint32 resolveRGB(uint8 r, uint8 g, uint8 b, const Graphics::PixelFormat &fmt) { uint32 rPlaced = (static_cast(r) >> (8 - fmt.rBits())) << fmt.rShift; uint32 gPlaced = (static_cast(g) >> (8 - fmt.gBits())) << fmt.gShift; uint32 bPlaced = (static_cast(b) >> (8 - fmt.bBits())) << fmt.bShift; uint32 aPlaced = (static_cast(255) >> (8 - fmt.aBits())) << fmt.aShift; return rPlaced | gPlaced | bPlaced | aPlaced; } static void recursiveCollectDrawElementsAndUpdateOrigins(const Common::Point &parentOrigin, Structural *structural, size_t sceneStackDepth, Common::Array &normalBucket, Common::Array &directBucket) { Common::Point elementOrigin = parentOrigin; if (structural->isElement()) { Element *element = static_cast(structural); if (element->isVisual()) { VisualElement *visualElement = static_cast(element); const Common::Rect &elementRect = visualElement->getRelativeRect(); elementOrigin.x += elementRect.left; elementOrigin.y += elementRect.top; visualElement->setCachedAbsoluteOrigin(Common::Point(elementOrigin.x, elementOrigin.y)); RenderItem item; item.element = visualElement; item.sceneStackDepth = sceneStackDepth; if (visualElement->isVisible()) { if (visualElement->isDirectToScreen()) directBucket.push_back(item); else normalBucket.push_back(item); } } } for (Common::Array >::const_iterator it = structural->getChildren().begin(), itEnd = structural->getChildren().end(); it != itEnd; ++it) { recursiveCollectDrawElementsAndUpdateOrigins(elementOrigin, it->get(), sceneStackDepth, normalBucket, directBucket); } } static bool renderItemLess(const RenderItem &a, const RenderItem &b) { const uint16 aLayer = a.element->getLayer(); const uint16 bLayer = b.element->getLayer(); if (aLayer != bLayer) return aLayer < bLayer; return a.sceneStackDepth < b.sceneStackDepth; } static void renderNormalElement(const RenderItem &item, Window *mainWindow) { item.element->render(mainWindow); item.element->finalizeRender(); } static void renderDirectElement(const RenderItem &item, Window *mainWindow) { renderNormalElement(item, mainWindow); // Meh } void renderProject(Runtime *runtime, Window *mainWindow) { bool sceneChanged = runtime->isSceneGraphDirty(); Common::Array scenes; runtime->getScenesInRenderOrder(scenes); Common::Array normalBucket; Common::Array directBucket; size_t sceneStackDepth = 0; for (Common::Array::const_iterator it = scenes.begin(), itEnd = scenes.end(); it != itEnd; ++it) { recursiveCollectDrawElementsAndUpdateOrigins(Common::Point(0, 0), *it, sceneStackDepth, normalBucket, directBucket); sceneStackDepth++; } Common::sort(normalBucket.begin(), normalBucket.end(), renderItemLess); Common::sort(directBucket.begin(), directBucket.end(), renderItemLess); if (!sceneChanged) { for (Common::Array::const_iterator it = normalBucket.begin(), itEnd = normalBucket.end(); it != itEnd; ++it) { if (it->element->needsRender()) { sceneChanged = true; break; } } } if (!sceneChanged) { for (Common::Array::const_iterator it = directBucket.begin(), itEnd = directBucket.end(); it != itEnd; ++it) { if (it->element->needsRender()) { sceneChanged = true; break; } } } if (sceneChanged) { for (Common::Array::const_iterator it = normalBucket.begin(), itEnd = normalBucket.end(); it != itEnd; ++it) renderNormalElement(*it, mainWindow); for (Common::Array::const_iterator it = directBucket.begin(), itEnd = directBucket.end(); it != itEnd; ++it) renderDirectElement(*it, mainWindow); } runtime->clearSceneGraphDirty(); } void convert32To16(Graphics::Surface &destSurface, const Graphics::Surface &srcSurface) { const Graphics::PixelFormat srcFmt = srcSurface.format; const Graphics::PixelFormat destFmt = destSurface.format; assert(srcFmt.bytesPerPixel == 4); assert(destFmt.bytesPerPixel == 2); assert(destSurface.w == srcSurface.w); assert(srcSurface.h == destSurface.h); uint16 ditherPattern[16][16]; OrderedDitherGenerator::generateOrderedDither(ditherPattern); for (int x = 0; x < 16; x++) { for (int y = 0; y < 16; y++) ditherPattern[y][x] <<= 3; } size_t w = srcSurface.w; size_t h = srcSurface.h; for (size_t y = 0; y < h; y++) { const uint16 *ditherRow = ditherPattern[y % 16]; const uint32 *srcRow = static_cast(srcSurface.getBasePtr(0, y)); uint16 *destRow = static_cast(destSurface.getBasePtr(0, y)); for (size_t x = 0; x < w; x++) { uint16 ditherOffset = ditherRow[x % 16]; uint32 packed32 = srcRow[x]; uint8 r = (packed32 >> srcFmt.rShift) & 0xff; uint8 g = (packed32 >> srcFmt.gShift) & 0xff; uint8 b = (packed32 >> srcFmt.bShift) & 0xff; r = quantize8To5UShort(r, ditherOffset); g = quantize8To5UShort(g, ditherOffset); b = quantize8To5UShort(b, ditherOffset); destRow[x] = (r << destFmt.rShift) | (g << destFmt.gShift) | (b << destFmt.bShift); } } } void convert16To32(Graphics::Surface &destSurface, const Graphics::Surface &srcSurface) { const Graphics::PixelFormat srcFmt = srcSurface.format; const Graphics::PixelFormat destFmt = destSurface.format; assert(srcFmt.bytesPerPixel == 2); assert(destFmt.bytesPerPixel == 4); assert(destSurface.w == srcSurface.w); assert(srcSurface.h == destSurface.h); size_t w = srcSurface.w; size_t h = srcSurface.h; for (size_t y = 0; y < h; y++) { const uint32 *srcRow = static_cast(srcSurface.getBasePtr(0, y)); uint16 *destRow = static_cast(destSurface.getBasePtr(0, y)); for (size_t x = 0; x < w; x++) { uint32 packed16 = srcRow[x]; uint8 r = (packed16 >> srcFmt.rShift) & 0x1f; uint8 g = (packed16 >> srcFmt.gShift) & 0x1f; uint8 b = (packed16 >> srcFmt.bShift) & 0x1f; r = expand5To8(r); g = expand5To8(g); b = expand5To8(b); destRow[x] = (r << destFmt.rShift) | (g << destFmt.gShift) | (b << destFmt.bShift); } } } } // End of namespace Render } // End of namespace MTropolis