mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
610 lines
20 KiB
C++
610 lines
20 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 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/>.
|
|
*
|
|
*/
|
|
|
|
#include "graphics/surface.h"
|
|
#include "graphics/managed_surface.h"
|
|
|
|
#include "graphics/cursorman.h"
|
|
|
|
#include "mtropolis/render.h"
|
|
#include "mtropolis/runtime.h"
|
|
|
|
namespace MTropolis {
|
|
|
|
template<class TNumber, int TResolution>
|
|
struct OrderedDitherGenerator {
|
|
static void generateOrderedDither(TNumber (&pattern)[TResolution][TResolution]);
|
|
};
|
|
|
|
template<class TNumber>
|
|
struct OrderedDitherGenerator<TNumber, 1> {
|
|
static void generateOrderedDither(TNumber (&pattern)[1][1]);
|
|
};
|
|
|
|
struct RenderItem {
|
|
VisualElement *element;
|
|
size_t sceneStackDepth;
|
|
};
|
|
|
|
template<class TNumber, int TResolution>
|
|
void OrderedDitherGenerator<TNumber, TResolution>::generateOrderedDither(TNumber (&pattern)[TResolution][TResolution]) {
|
|
const int kHalfResolution = TResolution / 2;
|
|
TNumber halfRes[kHalfResolution][kHalfResolution];
|
|
|
|
OrderedDitherGenerator<TNumber, kHalfResolution>::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<class TNumber>
|
|
void OrderedDitherGenerator<TNumber, 1>::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) {
|
|
}
|
|
|
|
MacFormattingSpan::MacFormattingSpan() : spanStart(0) {
|
|
}
|
|
|
|
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), _isMouseVisible(true) {
|
|
_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<Graphics::ManagedSurface> &Window::getSurface() const {
|
|
return _surface;
|
|
}
|
|
|
|
const Graphics::PixelFormat& Window::getPixelFormat() const {
|
|
return _surface->format;
|
|
}
|
|
|
|
const Common::SharedPtr<CursorGraphic> &Window::getCursorGraphic() const {
|
|
return _cursor;
|
|
}
|
|
|
|
void Window::setCursorGraphic(const Common::SharedPtr<CursorGraphic>& cursor) {
|
|
_cursor = cursor;
|
|
}
|
|
|
|
bool Window::getMouseVisible() const {
|
|
return _isMouseVisible;
|
|
}
|
|
|
|
void Window::setMouseVisible(bool visible) {
|
|
_isMouseVisible = visible;
|
|
}
|
|
|
|
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) {
|
|
}
|
|
|
|
void Window::onAction(Actions::Action action) {
|
|
}
|
|
|
|
namespace Render {
|
|
|
|
uint32 resolveRGB(uint8 r, uint8 g, uint8 b, const Graphics::PixelFormat &fmt) {
|
|
uint32 rPlaced = (static_cast<uint32>(r) >> (8 - fmt.rBits())) << fmt.rShift;
|
|
uint32 gPlaced = (static_cast<uint32>(g) >> (8 - fmt.gBits())) << fmt.gShift;
|
|
uint32 bPlaced = (static_cast<uint32>(b) >> (8 - fmt.bBits())) << fmt.bShift;
|
|
uint32 aPlaced = (static_cast<uint32>(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<RenderItem> &normalBucket, Common::Array<RenderItem> &directBucket) {
|
|
Common::Point elementOrigin = parentOrigin;
|
|
if (structural->isElement()) {
|
|
Element *element = static_cast<Element *>(structural);
|
|
if (element->isVisual()) {
|
|
VisualElement *visualElement = static_cast<VisualElement *>(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<Common::SharedPtr<Structural> >::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 *outSkipped) {
|
|
bool sceneChanged = runtime->isSceneGraphDirty();
|
|
|
|
Common::Array<Structural *> scenes;
|
|
runtime->getScenesInRenderOrder(scenes);
|
|
|
|
Common::Array<RenderItem> normalBucket;
|
|
Common::Array<RenderItem> directBucket;
|
|
|
|
size_t sceneStackDepth = 0;
|
|
for (Common::Array<Structural *>::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<RenderItem>::const_iterator it = normalBucket.begin(), itEnd = normalBucket.end(); it != itEnd; ++it) {
|
|
if (it->element->needsRender()) {
|
|
sceneChanged = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!sceneChanged) {
|
|
for (Common::Array<RenderItem>::const_iterator it = directBucket.begin(), itEnd = directBucket.end(); it != itEnd; ++it) {
|
|
if (it->element->needsRender()) {
|
|
sceneChanged = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sceneChanged) {
|
|
if (outSkipped)
|
|
*outSkipped = false;
|
|
|
|
for (Common::Array<RenderItem>::const_iterator it = normalBucket.begin(), itEnd = normalBucket.end(); it != itEnd; ++it)
|
|
renderNormalElement(*it, mainWindow);
|
|
|
|
for (Common::Array<RenderItem>::const_iterator it = directBucket.begin(), itEnd = directBucket.end(); it != itEnd; ++it)
|
|
renderDirectElement(*it, mainWindow);
|
|
|
|
for (const IPostEffect *postEffect : runtime->getPostEffects())
|
|
postEffect->renderPostEffect(*mainWindow->getSurface());
|
|
} else {
|
|
if (outSkipped)
|
|
*outSkipped = true;
|
|
}
|
|
|
|
runtime->clearSceneGraphDirty();
|
|
}
|
|
|
|
class DissolveOrderedDitherPatternGenerator {
|
|
public:
|
|
DissolveOrderedDitherPatternGenerator();
|
|
|
|
uint8 getNext();
|
|
void nextLine();
|
|
|
|
private:
|
|
uint8 _ditherPattern[16][16];
|
|
|
|
uint16 _x;
|
|
uint16 _y;
|
|
};
|
|
|
|
DissolveOrderedDitherPatternGenerator::DissolveOrderedDitherPatternGenerator() : _x(0), _y(0) {
|
|
OrderedDitherGenerator<uint8, 16>::generateOrderedDither(_ditherPattern);
|
|
}
|
|
|
|
inline uint8 DissolveOrderedDitherPatternGenerator::getNext() {
|
|
uint8 result = _ditherPattern[_y][_x];
|
|
|
|
uint16 newX = _x + 1;
|
|
if (newX == 16)
|
|
newX = 0;
|
|
_x = newX;
|
|
|
|
return result;
|
|
}
|
|
|
|
inline void DissolveOrderedDitherPatternGenerator::nextLine() {
|
|
_x = 0;
|
|
uint16 newY = _y + 1;
|
|
if (newY == 16)
|
|
newY = 0;
|
|
_y = newY;
|
|
}
|
|
|
|
class DissolveOrderedDitherRandomGenerator {
|
|
public:
|
|
DissolveOrderedDitherRandomGenerator();
|
|
|
|
uint8 getNext();
|
|
void nextLine();
|
|
|
|
private:
|
|
uint32 _lcgState;
|
|
};
|
|
|
|
|
|
DissolveOrderedDitherRandomGenerator::DissolveOrderedDitherRandomGenerator() : _lcgState(13) {
|
|
}
|
|
|
|
inline uint8 DissolveOrderedDitherRandomGenerator::getNext() {
|
|
_lcgState = ((_lcgState * 1103515245u) + 12345u) & 0x7fffffffu;
|
|
return (_lcgState >> 16) & 0xff;
|
|
}
|
|
|
|
inline void DissolveOrderedDitherRandomGenerator::nextLine() {
|
|
}
|
|
|
|
template<class TPixel, class TGenerator>
|
|
static void runDissolveTransitionWithType(Graphics::ManagedSurface &surface, const Graphics::ManagedSurface &oldFrame, const Graphics::ManagedSurface &newFrame, uint8 breakpoint) {
|
|
TGenerator generator;
|
|
|
|
assert(surface.format.bytesPerPixel == oldFrame.format.bytesPerPixel);
|
|
assert(surface.format.bytesPerPixel == newFrame.format.bytesPerPixel);
|
|
|
|
uint16 w = surface.w;
|
|
uint16 h = surface.h;
|
|
|
|
for (uint y = 0; y < h; y++) {
|
|
TPixel *destRow = static_cast<TPixel *>(surface.getBasePtr(0, y));
|
|
const TPixel *oldRow = static_cast<const TPixel *>(oldFrame.getBasePtr(0, y));
|
|
const TPixel *newRow = static_cast<const TPixel *>(newFrame.getBasePtr(0, y));
|
|
|
|
for (uint x = 0; x < w; x++) {
|
|
if (generator.getNext() <= breakpoint)
|
|
destRow[x] = newRow[x];
|
|
else
|
|
destRow[x] = oldRow[x];
|
|
}
|
|
|
|
generator.nextLine();
|
|
}
|
|
}
|
|
|
|
template<class TGenerator>
|
|
static void runDissolveTransition(Graphics::ManagedSurface &surface, const Graphics::ManagedSurface &oldFrame, const Graphics::ManagedSurface &newFrame, uint8 breakpoint) {
|
|
switch (surface.format.bytesPerPixel) {
|
|
case 1:
|
|
runDissolveTransitionWithType<uint8, TGenerator>(surface, oldFrame, newFrame, breakpoint);
|
|
break;
|
|
case 2:
|
|
runDissolveTransitionWithType<uint16, TGenerator>(surface, oldFrame, newFrame, breakpoint);
|
|
break;
|
|
case 4:
|
|
runDissolveTransitionWithType<uint32, TGenerator>(surface, oldFrame, newFrame, breakpoint);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void safeCopyRectToSurface(Graphics::ManagedSurface &surface, const Graphics::ManagedSurface &srcSurface, int destX, int destY, const Common::Rect subRect) {
|
|
if (subRect.width() == 0 || subRect.height() == 0)
|
|
return;
|
|
|
|
surface.copyRectToSurface(srcSurface, destX, destY, subRect);
|
|
}
|
|
|
|
void renderSceneTransition(Runtime *runtime, Window *mainWindow, const SceneTransitionEffect &effect, uint32 startTime, uint32 endTime, uint32 currentTime, const Graphics::ManagedSurface &oldFrame, const Graphics::ManagedSurface &newFrame) {
|
|
Graphics::ManagedSurface &surface = *mainWindow->getSurface();
|
|
|
|
assert(endTime > startTime);
|
|
|
|
uint32 duration = endTime - startTime;
|
|
|
|
uint16 w = surface.w;
|
|
uint16 h = surface.h;
|
|
|
|
if (effect._transitionType == SceneTransitionTypes::kSlide || effect._transitionType == SceneTransitionTypes::kWipe)
|
|
safeCopyRectToSurface(surface, oldFrame, 0, 0, Common::Rect(0, 0, w, h));
|
|
|
|
switch (effect._transitionType) {
|
|
case SceneTransitionTypes::kPatternDissolve:
|
|
runDissolveTransition<DissolveOrderedDitherPatternGenerator>(surface, oldFrame, newFrame, (currentTime - startTime) * 255 / duration);
|
|
break;
|
|
case SceneTransitionTypes::kRandomDissolve:
|
|
runDissolveTransition<DissolveOrderedDitherRandomGenerator>(surface, oldFrame, newFrame, (currentTime - startTime) * 255 / duration);
|
|
break;
|
|
case SceneTransitionTypes::kFade:
|
|
// Fade transitions fade to black and then fade from black in the new scene
|
|
warning("Fade transitions are not implemented");
|
|
break;
|
|
case SceneTransitionTypes::kSlide:
|
|
case SceneTransitionTypes::kPush: {
|
|
uint32 directionalOffset = 0;
|
|
switch (effect._transitionDirection) {
|
|
case SceneTransitionDirections::kUp:
|
|
directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(h) / duration;
|
|
|
|
if (effect._transitionType == SceneTransitionTypes::kPush)
|
|
safeCopyRectToSurface(surface, oldFrame, 0, 0, Common::Rect(0, directionalOffset, w, h));
|
|
|
|
safeCopyRectToSurface(surface, newFrame, 0, h - directionalOffset, Common::Rect(0, 0, w, directionalOffset));
|
|
break;
|
|
case SceneTransitionDirections::kDown:
|
|
directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(h) / duration;
|
|
|
|
if (effect._transitionType == SceneTransitionTypes::kPush)
|
|
safeCopyRectToSurface(surface, oldFrame, 0, directionalOffset, Common::Rect(0, 0, w, h - directionalOffset));
|
|
|
|
safeCopyRectToSurface(surface, newFrame, 0, 0, Common::Rect(0, h - directionalOffset, w, h));
|
|
break;
|
|
case SceneTransitionDirections::kLeft:
|
|
directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(w) / duration;
|
|
|
|
if (effect._transitionType == SceneTransitionTypes::kPush)
|
|
safeCopyRectToSurface(surface, oldFrame, 0, 0, Common::Rect(directionalOffset, 0, w, h));
|
|
|
|
safeCopyRectToSurface(surface, newFrame, w - directionalOffset, 0, Common::Rect(0, 0, directionalOffset, h));
|
|
break;
|
|
case SceneTransitionDirections::kRight:
|
|
directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(w) / duration;
|
|
|
|
if (effect._transitionType == SceneTransitionTypes::kPush)
|
|
safeCopyRectToSurface(surface, oldFrame, directionalOffset, 0, Common::Rect(0, 0, w - directionalOffset, h));
|
|
|
|
safeCopyRectToSurface(surface, newFrame, 0, 0, Common::Rect(w - directionalOffset, 0, w, h));
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
} break;
|
|
case SceneTransitionTypes::kZoom:
|
|
warning("Zoom transitions are not implemented");
|
|
break;
|
|
case SceneTransitionTypes::kWipe:{
|
|
uint32 directionalOffset = 0;
|
|
switch (effect._transitionDirection) {
|
|
case SceneTransitionDirections::kUp:
|
|
directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(h) / duration;
|
|
safeCopyRectToSurface(surface, newFrame, 0, h - directionalOffset, Common::Rect(0, h - directionalOffset, w, h));
|
|
break;
|
|
case SceneTransitionDirections::kDown:
|
|
directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(h) / duration;
|
|
safeCopyRectToSurface(surface, newFrame, 0, 0, Common::Rect(0, 0, w, directionalOffset));
|
|
break;
|
|
case SceneTransitionDirections::kLeft:
|
|
directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(w) / duration;
|
|
safeCopyRectToSurface(surface, newFrame, w - directionalOffset, 0, Common::Rect(w - directionalOffset, 0, w, h));
|
|
break;
|
|
case SceneTransitionDirections::kRight:
|
|
directionalOffset = static_cast<uint32>(currentTime - startTime) * static_cast<uint32>(w) / duration;
|
|
safeCopyRectToSurface(surface, newFrame, 0, 0, Common::Rect(0, 0, directionalOffset, h));
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
} break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void convert32To16(Graphics::ManagedSurface &destSurface, const Graphics::ManagedSurface &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<uint16, 16>::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<const uint32*>(srcSurface.getBasePtr(0, y));
|
|
uint16 *destRow = static_cast<uint16 *>(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::ManagedSurface &destSurface, const Graphics::ManagedSurface &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 uint16 *srcRow = static_cast<const uint16 *>(srcSurface.getBasePtr(0, y));
|
|
uint32 *destRow = static_cast<uint32 *>(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) | (0xffu << destFmt.aShift);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // End of namespace Render
|
|
|
|
} // End of namespace MTropolis
|