From f8c32b07e7bc219e81df6d1f27209ac28d3ced6b Mon Sep 17 00:00:00 2001 From: Daniel Wolf Date: Fri, 16 Mar 2018 11:57:26 +0100 Subject: SCI: Use LarryScale cel scaler instead of nearest-neighbor --- engines/sci/graphics/celobj32.cpp | 106 +++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 23 deletions(-) (limited to 'engines/sci/graphics/celobj32.cpp') diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index fb7abe8a77..fbf8f81d07 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -31,6 +31,7 @@ #include "sci/graphics/text32.h" #include "sci/engine/workarounds.h" #include "sci/util.h" +#include "graphics/larryScale.h" namespace Sci { #pragma mark CelScaler @@ -154,6 +155,9 @@ struct SCALER_Scale { #endif const byte *_row; READER _reader; + // If _sourceBuffer is set, it contains the full (possibly scaled) source + // image and takes precedence over _reader. + Common::SharedPtr _sourceBuffer; int16 _x; static int16 _valuesX[kCelScalerTableSize]; static int16 _valuesY[kCelScalerTableSize]; @@ -167,7 +171,8 @@ struct SCALER_Scale { // The maximum width of the scaled object may not be as wide as the source // data it requires if downscaling, so just always make the reader // decompress an entire line of source data when scaling - _reader(celObj, celObj._width) { + _reader(celObj, celObj._width), + _sourceBuffer() { #ifndef NDEBUG assert(_minX <= _maxX); #endif @@ -196,43 +201,98 @@ struct SCALER_Scale { const CelScalerTable &table = CelObj::_scaler->getScalerTable(scaleX, scaleY); - if (g_sci->_gfxFrameout->getScriptWidth() == kLowResX) { - const int16 unscaledX = (scaledPosition.x / scaleX).toInt(); - if (FLIP) { - const int lastIndex = celObj._width - 1; - for (int16 x = targetRect.left; x < targetRect.right; ++x) { - _valuesX[x] = lastIndex - (table.valuesX[x] - unscaledX); + const bool useLarryScale = true; + if (useLarryScale) { + // LarryScale is an alternative, high-quality cel scaler implemented + // for ScummVM. Due to the nature of smooth upscaling, it does *not* + // respect the global scaling pattern. Instead, it simply scales the + // cel to the extent of targetRect. + + class Copier: public Graphics::RowReader, public Graphics::RowWriter { + READER &_souceReader; + Buffer &_targetBuffer; + public: + Copier(READER& souceReader, Buffer& targetBuffer) : + _souceReader(souceReader), + _targetBuffer(targetBuffer) {} + const Graphics::LarryScaleColor* readRow(int y) { + return _souceReader.getRow(y); } - } else { - for (int16 x = targetRect.left; x < targetRect.right; ++x) { - _valuesX[x] = table.valuesX[x] - unscaledX; + void writeRow(int y, const Graphics::LarryScaleColor* row) { + memcpy(_targetBuffer.getBasePtr(0, y), row, _targetBuffer.w); } + }; + + // Scale the cel using LarryScale and write it to _sourceBuffer + // scaledImageRect is not necessarily identical to targetRect + // because targetRect may be cropped to render only a segment. + Common::Rect scaledImageRect( + scaledPosition.x, + scaledPosition.y, + scaledPosition.x + (celObj._width * scaleX).toInt(), + scaledPosition.y + (celObj._height * scaleY).toInt()); + _sourceBuffer = Common::SharedPtr(new Buffer(), Graphics::SurfaceDeleter()); + _sourceBuffer->create( + scaledImageRect.width(), scaledImageRect.height(), + Graphics::PixelFormat::createFormatCLUT8()); + Copier copier(_reader, *_sourceBuffer); + Graphics::larryScale( + celObj._width, celObj._height, celObj._skipColor, copier, + scaledImageRect.width(), scaledImageRect.height(), copier); + + // Set _valuesX and _valuesY to reference the scaled image without additional scaling + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + const int16 unsafeValue = FLIP + ? scaledImageRect.right - x - 1 + : x - scaledImageRect.left; + _valuesX[x] = CLIP(unsafeValue, 0, scaledImageRect.width() - 1); } - - const int16 unscaledY = (scaledPosition.y / scaleY).toInt(); for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { - _valuesY[y] = table.valuesY[y] - unscaledY; + const int16 unsafeValue = y - scaledImageRect.top; + _valuesY[y] = CLIP(unsafeValue, 0, scaledImageRect.height() - 1); } } else { - if (FLIP) { - const int lastIndex = celObj._width - 1; - for (int16 x = targetRect.left; x < targetRect.right; ++x) { - _valuesX[x] = lastIndex - table.valuesX[x - scaledPosition.x]; + const bool useGlobalScaling = g_sci->_gfxFrameout->getScriptWidth() == kLowResX; + if (useGlobalScaling) { + const int16 unscaledX = (scaledPosition.x / scaleX).toInt(); + if (FLIP) { + const int lastIndex = celObj._width - 1; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = lastIndex - (table.valuesX[x] - unscaledX); + } + } else { + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = table.valuesX[x] - unscaledX; + } + } + + const int16 unscaledY = (scaledPosition.y / scaleY).toInt(); + for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { + _valuesY[y] = table.valuesY[y] - unscaledY; } } else { - for (int16 x = targetRect.left; x < targetRect.right; ++x) { - _valuesX[x] = table.valuesX[x - scaledPosition.x]; + if (FLIP) { + const int lastIndex = celObj._width - 1; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = lastIndex - table.valuesX[x - scaledPosition.x]; + } + } else { + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = table.valuesX[x - scaledPosition.x]; + } } - } - for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { - _valuesY[y] = table.valuesY[y - scaledPosition.y]; + for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { + _valuesY[y] = table.valuesY[y - scaledPosition.y]; + } } } } inline void setTarget(const int16 x, const int16 y) { - _row = _reader.getRow(_valuesY[y]); + _row = _sourceBuffer + ? static_cast( _sourceBuffer->getBasePtr(0, _valuesY[y])) + : _reader.getRow(_valuesY[y]); _x = x; assert(_x >= _minX && _x <= _maxX); } -- cgit v1.2.3