[Scummvm-git-logs] scummvm master -> 7294a5eff2da4732453f4e3d06391ab9f455c561
sev-
noreply at scummvm.org
Wed Apr 30 11:20:35 UTC 2025
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
1e62c54dcd IMAGE: Improved dithering support for QuickTime RLE
9bbb019950 IMAGE: Split the generic QuickTime dithering into a separate class
320865e802 IMAGE: Use setOutputPixelFormat to avoid extra conversion when dithering
7294a5eff2 IMAGE: Use the palette class in the dithered codec wrapper
Commit: 1e62c54dcd8f7f79c207c60f4badfb46ad5a8e3d
https://github.com/scummvm/scummvm/commit/1e62c54dcd8f7f79c207c60f4badfb46ad5a8e3d
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-04-30T19:20:29+08:00
Commit Message:
IMAGE: Improved dithering support for QuickTime RLE
Changed paths:
image/codecs/qtrle.cpp
image/codecs/qtrle.h
diff --git a/image/codecs/qtrle.cpp b/image/codecs/qtrle.cpp
index 269fbe3be60..cd5428146e6 100644
--- a/image/codecs/qtrle.cpp
+++ b/image/codecs/qtrle.cpp
@@ -260,6 +260,68 @@ void QTRLEDecoder::decode16(Common::SeekableReadStream &stream, uint32 rowPtr, u
}
}
+namespace {
+
+inline uint16 readDitherColor16(Common::ReadStream &stream) {
+ return stream.readUint16BE() >> 1;
+}
+
+} // End of anonymous namespace
+
+void QTRLEDecoder::dither16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
+ uint32 pixelPtr = 0;
+ byte *output = (byte *)_surface->getPixels();
+
+ static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
+
+ // clone2727 thinks this should be startLine & 3, but the original definitely
+ // isn't doing this. Unless startLine & 3 is always 0? Kinda defeats the
+ // purpose of the compression then.
+ byte curColorTableOffset = 0;
+
+ while (linesToChange--) {
+ CHECK_STREAM_PTR(2);
+
+ byte rowOffset = stream.readByte() - 1;
+ pixelPtr = rowPtr + rowOffset;
+ uint16 colorTableOffset = colorTableOffsets[curColorTableOffset] + (rowOffset << 14);
+
+ for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
+ if (rleCode == 0) {
+ // there's another skip code in the stream
+ CHECK_STREAM_PTR(1);
+ pixelPtr += stream.readByte() - 1;
+ } else if (rleCode < 0) {
+ // decode the run length code
+ rleCode = -rleCode;
+ CHECK_STREAM_PTR(2);
+
+ uint16 color = readDitherColor16(stream);
+
+ CHECK_PIXEL_PTR(rleCode);
+
+ while (rleCode--) {
+ output[pixelPtr++] = _colorMap[colorTableOffset + color];
+ colorTableOffset += 0x4000;
+ }
+ } else {
+ CHECK_STREAM_PTR(rleCode * 2);
+ CHECK_PIXEL_PTR(rleCode);
+
+ // copy pixels directly to output
+ while (rleCode--) {
+ uint16 color = readDitherColor16(stream);
+ output[pixelPtr++] = _colorMap[colorTableOffset + color];
+ colorTableOffset += 0x4000;
+ }
+ }
+ }
+
+ rowPtr += _paddedWidth;
+ curColorTableOffset = (curColorTableOffset + 1) & 3;
+ }
+}
+
void QTRLEDecoder::decode24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
uint32 pixelPtr = 0;
uint32 *rgb = (uint32 *)_surface->getPixels();
@@ -309,9 +371,12 @@ void QTRLEDecoder::decode24(Common::SeekableReadStream &stream, uint32 rowPtr, u
namespace {
inline uint16 readDitherColor24(Common::ReadStream &stream) {
- uint16 color = (stream.readByte() & 0xF8) << 6;
- color |= (stream.readByte() & 0xF8) << 1;
- color |= stream.readByte() >> 4;
+ uint8 rgb[3];
+ stream.read(rgb, 3);
+
+ uint16 color = (rgb[0] & 0xF8) << 6;
+ color |= (rgb[1] & 0xF8) << 1;
+ color |= rgb[2] >> 4;
return color;
}
@@ -343,12 +408,12 @@ void QTRLEDecoder::dither24(Common::SeekableReadStream &stream, uint32 rowPtr, u
} else if (rleCode < 0) {
// decode the run length code
rleCode = -rleCode;
-
CHECK_STREAM_PTR(3);
- CHECK_PIXEL_PTR(rleCode);
uint16 color = readDitherColor24(stream);
+ CHECK_PIXEL_PTR(rleCode);
+
while (rleCode--) {
output[pixelPtr++] = _colorMap[colorTableOffset + color];
colorTableOffset += 0x4000;
@@ -419,6 +484,74 @@ void QTRLEDecoder::decode32(Common::SeekableReadStream &stream, uint32 rowPtr, u
}
}
+namespace {
+
+inline uint16 readDitherColor32(Common::ReadStream &stream) {
+ uint8 argb[4];
+ stream.read(argb, 4);
+
+ uint16 color = (argb[1] & 0xF8) << 6;
+ color |= (argb[2] & 0xF8) << 1;
+ color |= argb[3] >> 4;
+ return color;
+}
+
+} // End of anonymous namespace
+
+void QTRLEDecoder::dither32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
+ uint32 pixelPtr = 0;
+ byte *output = (byte *)_surface->getPixels();
+
+ static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
+
+ // clone2727 thinks this should be startLine & 3, but the original definitely
+ // isn't doing this. Unless startLine & 3 is always 0? Kinda defeats the
+ // purpose of the compression then.
+ byte curColorTableOffset = 0;
+
+ while (linesToChange--) {
+ CHECK_STREAM_PTR(2);
+
+ byte rowOffset = stream.readByte() - 1;
+ pixelPtr = rowPtr + rowOffset;
+ uint16 colorTableOffset = colorTableOffsets[curColorTableOffset] + (rowOffset << 14);
+
+ for (int rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
+ if (rleCode == 0) {
+ // there's another skip code in the stream
+ CHECK_STREAM_PTR(1);
+ pixelPtr += stream.readByte() - 1;
+ } else if (rleCode < 0) {
+ // decode the run length code
+ rleCode = -rleCode;
+ CHECK_STREAM_PTR(4);
+
+ uint16 color = readDitherColor32(stream);
+
+ CHECK_PIXEL_PTR(rleCode);
+
+ while (rleCode--) {
+ output[pixelPtr++] = _colorMap[colorTableOffset + color];
+ colorTableOffset += 0x4000;
+ }
+ } else {
+ CHECK_STREAM_PTR(rleCode * 4);
+ CHECK_PIXEL_PTR(rleCode);
+
+ // copy pixels directly to output
+ while (rleCode--) {
+ uint16 color = readDitherColor32(stream);
+ output[pixelPtr++] = _colorMap[colorTableOffset + color];
+ colorTableOffset += 0x4000;
+ }
+ }
+ }
+
+ rowPtr += _paddedWidth;
+ curColorTableOffset = (curColorTableOffset + 1) & 3;
+ }
+}
+
const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &stream) {
if (!_surface)
createSurface();
@@ -467,7 +600,10 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
decode8(stream, rowPtr, height);
break;
case 16:
- decode16(stream, rowPtr, height);
+ if (_ditherPalette.size() > 0)
+ dither16(stream, rowPtr, height);
+ else
+ decode16(stream, rowPtr, height);
break;
case 24:
if (_ditherPalette.size() > 0)
@@ -476,7 +612,10 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
decode24(stream, rowPtr, height);
break;
case 32:
- decode32(stream, rowPtr, height);
+ if (_ditherPalette.size() > 0)
+ dither32(stream, rowPtr, height);
+ else
+ decode32(stream, rowPtr, height);
break;
default:
error("Unsupported QTRLE bits per pixel %d", _bitsPerPixel);
@@ -512,8 +651,7 @@ Graphics::PixelFormat QTRLEDecoder::getPixelFormat() const {
}
bool QTRLEDecoder::canDither(DitherType type) const {
- // Only 24-bit dithering is implemented at the moment
- return type == kDitherTypeQT && _bitsPerPixel == 24;
+ return type == kDitherTypeQT && (_bitsPerPixel == 16 || _bitsPerPixel == 24 || _bitsPerPixel == 32);
}
void QTRLEDecoder::setDither(DitherType type, const byte *palette) {
diff --git a/image/codecs/qtrle.h b/image/codecs/qtrle.h
index 12d37ab5e81..dc9290d229a 100644
--- a/image/codecs/qtrle.h
+++ b/image/codecs/qtrle.h
@@ -62,9 +62,11 @@ private:
void decode2_4(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange, byte bpp);
void decode8(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
void decode16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
+ void dither16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
void decode24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
void dither24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
void decode32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
+ void dither32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
};
} // End of namespace Image
Commit: 9bbb019950141308e16fd8df657310cb9392abfb
https://github.com/scummvm/scummvm/commit/9bbb019950141308e16fd8df657310cb9392abfb
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-04-30T19:20:29+08:00
Commit Message:
IMAGE: Split the generic QuickTime dithering into a separate class
Changed paths:
A image/codecs/dither.cpp
A image/codecs/dither.h
image/codecs/cinepak.cpp
image/codecs/codec.cpp
image/codecs/codec.h
image/codecs/qtrle.cpp
image/codecs/rpza.cpp
image/module.mk
video/qt_decoder.cpp
video/qt_decoder.h
diff --git a/image/codecs/cinepak.cpp b/image/codecs/cinepak.cpp
index b2701bc97a8..e3cf84537d9 100644
--- a/image/codecs/cinepak.cpp
+++ b/image/codecs/cinepak.cpp
@@ -21,6 +21,7 @@
#include "image/codecs/cinepak.h"
#include "image/codecs/cinepak_tables.h"
+#include "image/codecs/dither.h"
#include "common/debug.h"
#include "common/stream.h"
@@ -680,7 +681,7 @@ void CinepakDecoder::setDither(DitherType type, const byte *palette) {
} else {
// Generate QuickTime dither table
// 4 blocks of 0x4000 bytes (RGB554 lookup)
- _colorMap = createQuickTimeDitherTable(palette, 256);
+ _colorMap = DitherCodec::createQuickTimeDitherTable(palette, 256);
}
}
diff --git a/image/codecs/codec.cpp b/image/codecs/codec.cpp
index 94103390763..be7858183e1 100644
--- a/image/codecs/codec.cpp
+++ b/image/codecs/codec.cpp
@@ -19,9 +19,6 @@
*
*/
-#include "common/list.h"
-#include "common/scummsys.h"
-
#include "image/codecs/codec.h"
#include "image/jpeg.h"
@@ -55,152 +52,6 @@
namespace Image {
-namespace {
-
-/**
- * Add a color to the QuickTime dither table check queue if it hasn't already been found.
- */
-inline void addColorToQueue(uint16 color, uint16 index, byte *checkBuffer, Common::List<uint16> &checkQueue) {
- if ((READ_UINT16(checkBuffer + color * 2) & 0xFF) == 0) {
- // Previously unfound color
- WRITE_UINT16(checkBuffer + color * 2, index);
- checkQueue.push_back(color);
- }
-}
-
-inline byte adjustColorRange(byte currentColor, byte correctColor, byte palColor) {
- return CLIP<int>(currentColor - palColor + correctColor, 0, 255);
-}
-
-inline uint16 makeQuickTimeDitherColor(byte r, byte g, byte b) {
- // RGB554
- return ((r & 0xF8) << 6) | ((g & 0xF8) << 1) | (b >> 4);
-}
-
-} // End of anonymous namespace
-
-byte *Codec::createQuickTimeDitherTable(const byte *palette, uint colorCount) {
- byte *buf = new byte[0x10000]();
-
- Common::List<uint16> checkQueue;
-
- bool foundBlack = false;
- bool foundWhite = false;
-
- const byte *palPtr = palette;
-
- // See what colors we have, and add them to the queue to check
- for (uint i = 0; i < colorCount; i++) {
- byte r = *palPtr++;
- byte g = *palPtr++;
- byte b = *palPtr++;
- uint16 n = (i << 8) | 1;
- uint16 col = makeQuickTimeDitherColor(r, g, b);
-
- if (col == 0) {
- // Special case for close-to-black
- // The original did more here, but it effectively discarded the value
- // due to a poor if-check (whole 16-bit value instead of lower 8-bits).
- WRITE_UINT16(buf, n);
- foundBlack = true;
- } else if (col == 0x3FFF) {
- // Special case for close-to-white
- // The original did more here, but it effectively discarded the value
- // due to a poor if-check (whole 16-bit value instead of lower 8-bits).
- WRITE_UINT16(buf + 0x7FFE, n);
- foundWhite = true;
- } else {
- // Previously unfound color
- addColorToQueue(col, n, buf, checkQueue);
- }
- }
-
- // More special handling for white
- if (foundWhite)
- checkQueue.push_front(0x3FFF);
-
- // More special handling for black
- if (foundBlack)
- checkQueue.push_front(0);
-
- // Go through the list of colors we have and match up similar colors
- // to fill in the table as best as we can.
- while (!checkQueue.empty()) {
- uint16 col = checkQueue.front();
- checkQueue.pop_front();
- uint16 index = READ_UINT16(buf + col * 2);
-
- uint32 x = col << 4;
- if ((x & 0xFF) < 0xF0)
- addColorToQueue((x + 0x10) >> 4, index, buf, checkQueue);
- if ((x & 0xFF) >= 0x10)
- addColorToQueue((x - 0x10) >> 4, index, buf, checkQueue);
-
- uint32 y = col << 7;
- if ((y & 0xFF00) < 0xF800)
- addColorToQueue((y + 0x800) >> 7, index, buf, checkQueue);
- if ((y & 0xFF00) >= 0x800)
- addColorToQueue((y - 0x800) >> 7, index, buf, checkQueue);
-
- uint32 z = col << 2;
- if ((z & 0xFF00) < 0xF800)
- addColorToQueue((z + 0x800) >> 2, index, buf, checkQueue);
- if ((z & 0xFF00) >= 0x800)
- addColorToQueue((z - 0x800) >> 2, index, buf, checkQueue);
- }
-
- // Contract the table back to just palette entries
- for (int i = 0; i < 0x4000; i++)
- buf[i] = READ_UINT16(buf + i * 2) >> 8;
-
- // Now go through and distribute the error to three more pixels
- byte *bufPtr = buf;
- for (uint realR = 0; realR < 0x100; realR += 8) {
- for (uint realG = 0; realG < 0x100; realG += 8) {
- for (uint realB = 0; realB < 0x100; realB += 16) {
- byte palIndex = *bufPtr;
- byte r = realR;
- byte g = realG;
- byte b = realB;
-
- byte palR = palette[palIndex * 3] & 0xF8;
- byte palG = palette[palIndex * 3 + 1] & 0xF8;
- byte palB = palette[palIndex * 3 + 2] & 0xF0;
-
- r = adjustColorRange(r, realR, palR);
- g = adjustColorRange(g, realG, palG);
- b = adjustColorRange(b, realB, palB);
- palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
- bufPtr[0x4000] = palIndex;
-
- palR = palette[palIndex * 3] & 0xF8;
- palG = palette[palIndex * 3 + 1] & 0xF8;
- palB = palette[palIndex * 3 + 2] & 0xF0;
-
- r = adjustColorRange(r, realR, palR);
- g = adjustColorRange(g, realG, palG);
- b = adjustColorRange(b, realB, palB);
- palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
- bufPtr[0x8000] = palIndex;
-
- palR = palette[palIndex * 3] & 0xF8;
- palG = palette[palIndex * 3 + 1] & 0xF8;
- palB = palette[palIndex * 3 + 2] & 0xF0;
-
- r = adjustColorRange(r, realR, palR);
- g = adjustColorRange(g, realG, palG);
- b = adjustColorRange(b, realB, palB);
- palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
- bufPtr[0xC000] = palIndex;
-
- bufPtr++;
- }
- }
- }
-
- return buf;
-}
-
Codec *createBitmapCodec(uint32 tag, uint32 streamTag, int width, int height, int bitsPerPixel) {
// Crusader videos are special cased here because the frame type is not in the "compression"
// tag but in the "stream handler" tag for these files
diff --git a/image/codecs/codec.h b/image/codecs/codec.h
index a8dd7107a3a..50916aaebc0 100644
--- a/image/codecs/codec.h
+++ b/image/codecs/codec.h
@@ -122,11 +122,6 @@ public:
* Set the decoding accuracy of the codec, if supported
*/
virtual void setCodecAccuracy(CodecAccuracy accuracy) {}
-
- /**
- * Create a dither table, as used by QuickTime codecs.
- */
- static byte *createQuickTimeDitherTable(const byte *palette, uint colorCount);
};
/**
diff --git a/image/codecs/dither.cpp b/image/codecs/dither.cpp
new file mode 100644
index 00000000000..88dc5c96bd5
--- /dev/null
+++ b/image/codecs/dither.cpp
@@ -0,0 +1,317 @@
+/* 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 "image/codecs/dither.h"
+
+#include "common/list.h"
+
+namespace Image {
+
+namespace {
+
+/**
+ * Add a color to the QuickTime dither table check queue if it hasn't already been found.
+ */
+inline void addColorToQueue(uint16 color, uint16 index, byte *checkBuffer, Common::List<uint16> &checkQueue) {
+ if ((READ_UINT16(checkBuffer + color * 2) & 0xFF) == 0) {
+ // Previously unfound color
+ WRITE_UINT16(checkBuffer + color * 2, index);
+ checkQueue.push_back(color);
+ }
+}
+
+inline byte adjustColorRange(byte currentColor, byte correctColor, byte palColor) {
+ return CLIP<int>(currentColor - palColor + correctColor, 0, 255);
+}
+
+inline uint16 makeQuickTimeDitherColor(byte r, byte g, byte b) {
+ // RGB554
+ return ((r & 0xF8) << 6) | ((g & 0xF8) << 1) | (b >> 4);
+}
+
+} // End of anonymous namespace
+
+DitherCodec::DitherCodec(Codec *codec, DisposeAfterUse::Flag disposeAfterUse)
+ : _codec(codec), _disposeAfterUse(disposeAfterUse), _dirtyPalette(false),
+ _forcedDitherPalette(0), _ditherTable(0), _ditherFrame(0) {
+}
+
+DitherCodec::~DitherCodec() {
+ if (_disposeAfterUse == DisposeAfterUse::YES)
+ delete _codec;
+
+ delete[] _forcedDitherPalette;
+ delete[] _ditherTable;
+
+ if (_ditherFrame) {
+ _ditherFrame->free();
+ delete _ditherFrame;
+ }
+}
+
+namespace {
+
+// Default template to convert a dither color
+template<typename PixelInt>
+inline uint16 readQuickTimeDitherColor(PixelInt srcColor, const Graphics::PixelFormat& format, const byte *palette) {
+ byte r, g, b;
+ format.colorToRGB(srcColor, r, g, b);
+ return makeQuickTimeDitherColor(r, g, b);
+}
+
+// Specialized version for 8bpp
+template<>
+inline uint16 readQuickTimeDitherColor(byte srcColor, const Graphics::PixelFormat& format, const byte *palette) {
+ return makeQuickTimeDitherColor(palette[srcColor * 3], palette[srcColor * 3 + 1], palette[srcColor * 3 + 2]);
+}
+
+template<typename PixelInt>
+void ditherQuickTimeFrame(const Graphics::Surface &src, Graphics::Surface &dst, const byte *ditherTable, const byte *palette = 0) {
+ static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
+
+ for (int y = 0; y < dst.h; y++) {
+ const PixelInt *srcPtr = (const PixelInt *)src.getBasePtr(0, y);
+ byte *dstPtr = (byte *)dst.getBasePtr(0, y);
+ uint16 colorTableOffset = colorTableOffsets[y & 3];
+
+ for (int x = 0; x < dst.w; x++) {
+ uint16 color = readQuickTimeDitherColor(*srcPtr++, src.format, palette);
+ *dstPtr++ = ditherTable[colorTableOffset + color];
+ colorTableOffset += 0x4000;
+ }
+ }
+}
+
+} // End of anonymous namespace
+
+const Graphics::Surface *DitherCodec::decodeFrame(Common::SeekableReadStream &stream) {
+ const Graphics::Surface *frame = _codec->decodeFrame(stream);
+ if (!frame || !_forcedDitherPalette)
+ return frame;
+
+ // TODO: Handle palettes that are owned by the container instead of the codec
+ const byte *curPalette = _codec->getPalette();
+
+ if (frame->format.isCLUT8() && curPalette) {
+ // This should always be true, but this is for sanity
+ if (!curPalette)
+ return frame;
+
+ // If the palettes match, bail out
+ if (memcmp(_forcedDitherPalette, curPalette, 256 * 3) == 0)
+ return frame;
+ }
+
+ // Need to create a new one
+ if (!_ditherFrame) {
+ _ditherFrame = new Graphics::Surface();
+ _ditherFrame->create(frame->w, frame->h, Graphics::PixelFormat::createFormatCLUT8());
+ }
+
+ if (frame->format.isCLUT8() && curPalette)
+ ditherQuickTimeFrame<byte>(*frame, *_ditherFrame, _ditherTable, curPalette);
+ else if (frame->format.bytesPerPixel == 2)
+ ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable);
+ else if (frame->format.bytesPerPixel == 4)
+ ditherQuickTimeFrame<uint32>(*frame, *_ditherFrame, _ditherTable);
+
+ return _ditherFrame;
+}
+
+Graphics::PixelFormat DitherCodec::getPixelFormat() const {
+ if (!_forcedDitherPalette)
+ return _codec->getPixelFormat();
+ return Graphics::PixelFormat::createFormatCLUT8();
+}
+
+bool DitherCodec::setOutputPixelFormat(const Graphics::PixelFormat &format) {
+ if (!_forcedDitherPalette)
+ return _codec->setOutputPixelFormat(format);
+ return format.isCLUT8();
+}
+
+bool DitherCodec::containsPalette() const {
+ if (!_forcedDitherPalette)
+ return _codec->containsPalette();
+ return true;
+}
+
+const byte *DitherCodec::getPalette() {
+ if (!_forcedDitherPalette)
+ return _codec->getPalette();
+ _dirtyPalette = false;
+ return _forcedDitherPalette;
+}
+
+bool DitherCodec::hasDirtyPalette() const {
+ if (!_forcedDitherPalette)
+ return _codec->hasDirtyPalette();
+ return _dirtyPalette;
+}
+
+bool DitherCodec::canDither(DitherType type) const {
+ return _codec->canDither(type) || (type == kDitherTypeQT);
+}
+
+void DitherCodec::setDither(DitherType type, const byte *palette) {
+ if (_codec->canDither(type)) {
+ _codec->setDither(type, palette);
+ } else {
+ assert(type == kDitherTypeQT);
+ assert(!_forcedDitherPalette);
+
+ // Forced dither
+ _forcedDitherPalette = new byte[256 * 3];
+ memcpy(_forcedDitherPalette, palette, 256 * 3);
+ _dirtyPalette = true;
+
+ _ditherTable = createQuickTimeDitherTable(_forcedDitherPalette, 256);
+ }
+}
+
+void DitherCodec::setCodecAccuracy(CodecAccuracy accuracy) {
+ return _codec->setCodecAccuracy(accuracy);
+}
+
+byte *DitherCodec::createQuickTimeDitherTable(const byte *palette, uint colorCount) {
+ byte *buf = new byte[0x10000]();
+
+ Common::List<uint16> checkQueue;
+
+ bool foundBlack = false;
+ bool foundWhite = false;
+
+ const byte *palPtr = palette;
+
+ // See what colors we have, and add them to the queue to check
+ for (uint i = 0; i < colorCount; i++) {
+ byte r = *palPtr++;
+ byte g = *palPtr++;
+ byte b = *palPtr++;
+ uint16 n = (i << 8) | 1;
+ uint16 col = makeQuickTimeDitherColor(r, g, b);
+
+ if (col == 0) {
+ // Special case for close-to-black
+ // The original did more here, but it effectively discarded the value
+ // due to a poor if-check (whole 16-bit value instead of lower 8-bits).
+ WRITE_UINT16(buf, n);
+ foundBlack = true;
+ } else if (col == 0x3FFF) {
+ // Special case for close-to-white
+ // The original did more here, but it effectively discarded the value
+ // due to a poor if-check (whole 16-bit value instead of lower 8-bits).
+ WRITE_UINT16(buf + 0x7FFE, n);
+ foundWhite = true;
+ } else {
+ // Previously unfound color
+ addColorToQueue(col, n, buf, checkQueue);
+ }
+ }
+
+ // More special handling for white
+ if (foundWhite)
+ checkQueue.push_front(0x3FFF);
+
+ // More special handling for black
+ if (foundBlack)
+ checkQueue.push_front(0);
+
+ // Go through the list of colors we have and match up similar colors
+ // to fill in the table as best as we can.
+ while (!checkQueue.empty()) {
+ uint16 col = checkQueue.front();
+ checkQueue.pop_front();
+ uint16 index = READ_UINT16(buf + col * 2);
+
+ uint32 x = col << 4;
+ if ((x & 0xFF) < 0xF0)
+ addColorToQueue((x + 0x10) >> 4, index, buf, checkQueue);
+ if ((x & 0xFF) >= 0x10)
+ addColorToQueue((x - 0x10) >> 4, index, buf, checkQueue);
+
+ uint32 y = col << 7;
+ if ((y & 0xFF00) < 0xF800)
+ addColorToQueue((y + 0x800) >> 7, index, buf, checkQueue);
+ if ((y & 0xFF00) >= 0x800)
+ addColorToQueue((y - 0x800) >> 7, index, buf, checkQueue);
+
+ uint32 z = col << 2;
+ if ((z & 0xFF00) < 0xF800)
+ addColorToQueue((z + 0x800) >> 2, index, buf, checkQueue);
+ if ((z & 0xFF00) >= 0x800)
+ addColorToQueue((z - 0x800) >> 2, index, buf, checkQueue);
+ }
+
+ // Contract the table back to just palette entries
+ for (int i = 0; i < 0x4000; i++)
+ buf[i] = READ_UINT16(buf + i * 2) >> 8;
+
+ // Now go through and distribute the error to three more pixels
+ byte *bufPtr = buf;
+ for (uint realR = 0; realR < 0x100; realR += 8) {
+ for (uint realG = 0; realG < 0x100; realG += 8) {
+ for (uint realB = 0; realB < 0x100; realB += 16) {
+ byte palIndex = *bufPtr;
+ byte r = realR;
+ byte g = realG;
+ byte b = realB;
+
+ byte palR = palette[palIndex * 3] & 0xF8;
+ byte palG = palette[palIndex * 3 + 1] & 0xF8;
+ byte palB = palette[palIndex * 3 + 2] & 0xF0;
+
+ r = adjustColorRange(r, realR, palR);
+ g = adjustColorRange(g, realG, palG);
+ b = adjustColorRange(b, realB, palB);
+ palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
+ bufPtr[0x4000] = palIndex;
+
+ palR = palette[palIndex * 3] & 0xF8;
+ palG = palette[palIndex * 3 + 1] & 0xF8;
+ palB = palette[palIndex * 3 + 2] & 0xF0;
+
+ r = adjustColorRange(r, realR, palR);
+ g = adjustColorRange(g, realG, palG);
+ b = adjustColorRange(b, realB, palB);
+ palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
+ bufPtr[0x8000] = palIndex;
+
+ palR = palette[palIndex * 3] & 0xF8;
+ palG = palette[palIndex * 3 + 1] & 0xF8;
+ palB = palette[palIndex * 3 + 2] & 0xF0;
+
+ r = adjustColorRange(r, realR, palR);
+ g = adjustColorRange(g, realG, palG);
+ b = adjustColorRange(b, realB, palB);
+ palIndex = buf[makeQuickTimeDitherColor(r, g, b)];
+ bufPtr[0xC000] = palIndex;
+
+ bufPtr++;
+ }
+ }
+ }
+
+ return buf;
+}
+
+} // End of namespace Image
+
diff --git a/image/codecs/dither.h b/image/codecs/dither.h
new file mode 100644
index 00000000000..1275154c5cc
--- /dev/null
+++ b/image/codecs/dither.h
@@ -0,0 +1,64 @@
+/* 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/>.
+ *
+ */
+
+#ifndef IMAGE_CODECS_DITHER_H
+#define IMAGE_CODECS_DITHER_H
+
+#include "image/codecs/codec.h"
+
+#include "common/types.h"
+
+namespace Image {
+
+class DitherCodec : public Codec {
+public:
+ DitherCodec(Codec *codec, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
+ virtual ~DitherCodec() override;
+
+ const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream) override;
+
+ Graphics::PixelFormat getPixelFormat() const override;
+ bool setOutputPixelFormat(const Graphics::PixelFormat &format) override;
+ bool containsPalette() const override;
+ const byte *getPalette() override;
+ bool hasDirtyPalette() const override;
+ bool canDither(DitherType type) const override;
+ void setDither(DitherType type, const byte *palette) override;
+ void setCodecAccuracy(CodecAccuracy accuracy) override;
+
+ /**
+ * Create a dither table, as used by QuickTime codecs.
+ */
+ static byte *createQuickTimeDitherTable(const byte *palette, uint colorCount);
+
+private:
+ DisposeAfterUse::Flag _disposeAfterUse;
+ Codec *_codec;
+
+ Graphics::Surface *_ditherFrame;
+ byte *_forcedDitherPalette;
+ byte *_ditherTable;
+ bool _dirtyPalette;
+};
+
+} // End of namespace Image
+
+#endif
diff --git a/image/codecs/qtrle.cpp b/image/codecs/qtrle.cpp
index cd5428146e6..c780bcb7021 100644
--- a/image/codecs/qtrle.cpp
+++ b/image/codecs/qtrle.cpp
@@ -23,6 +23,7 @@
// Based off ffmpeg's QuickTime RLE decoder (written by Mike Melanson)
#include "image/codecs/qtrle.h"
+#include "image/codecs/dither.h"
#include "common/debug.h"
#include "common/scummsys.h"
@@ -662,7 +663,7 @@ void QTRLEDecoder::setDither(DitherType type, const byte *palette) {
_dirtyPalette = true;
delete[] _colorMap;
- _colorMap = createQuickTimeDitherTable(palette, 256);
+ _colorMap = DitherCodec::createQuickTimeDitherTable(palette, 256);
}
void QTRLEDecoder::createSurface() {
diff --git a/image/codecs/rpza.cpp b/image/codecs/rpza.cpp
index 595cd2f8403..d33c17627d2 100644
--- a/image/codecs/rpza.cpp
+++ b/image/codecs/rpza.cpp
@@ -22,6 +22,7 @@
// Based off ffmpeg's RPZA decoder
#include "image/codecs/rpza.h"
+#include "image/codecs/dither.h"
#include "common/debug.h"
#include "common/system.h"
@@ -358,7 +359,7 @@ void RPZADecoder::setDither(DitherType type, const byte *palette) {
_format = Graphics::PixelFormat::createFormatCLUT8();
delete[] _colorMap;
- _colorMap = createQuickTimeDitherTable(palette, 256);
+ _colorMap = DitherCodec::createQuickTimeDitherTable(palette, 256);
}
} // End of namespace Image
diff --git a/image/module.mk b/image/module.mk
index 60e74270b65..67d0c733c13 100644
--- a/image/module.mk
+++ b/image/module.mk
@@ -19,6 +19,7 @@ MODULE_OBJS := \
codecs/cdtoons.o \
codecs/cinepak.o \
codecs/codec.o \
+ codecs/dither.o \
codecs/hlz.o \
codecs/jyv1.o \
codecs/mjpeg.o \
diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp
index 917474b4436..980cd8a0777 100644
--- a/video/qt_decoder.cpp
+++ b/video/qt_decoder.cpp
@@ -46,6 +46,7 @@
// Video codecs
#include "image/codecs/codec.h"
+#include "image/codecs/dither.h"
namespace Video {
@@ -325,7 +326,7 @@ Audio::SeekableAudioStream *QuickTimeDecoder::AudioTrackHandler::getSeekableAudi
return _audioTrack;
}
-QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder, Common::QuickTimeParser::Track *parent) : _decoder(decoder), _parent(parent), _forcedDitherPalette(0) {
+QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder, Common::QuickTimeParser::Track *parent) : _decoder(decoder), _parent(parent) {
if (decoder->_enableEditListBoundsCheckQuirk) {
checkEditListBounds();
}
@@ -343,8 +344,6 @@ QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder
_curPalette = 0;
_dirtyPalette = false;
_reversed = false;
- _ditherTable = 0;
- _ditherFrame = 0;
}
// FIXME: This check breaks valid QuickTime movies, such as the KQ6 Mac opening.
@@ -389,13 +388,6 @@ QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() {
_scaledSurface->free();
delete _scaledSurface;
}
-
- delete[] _ditherTable;
-
- if (_ditherFrame) {
- _ditherFrame->free();
- delete _ditherFrame;
- }
}
bool QuickTimeDecoder::VideoTrackHandler::endOfTrack() const {
@@ -492,17 +484,11 @@ uint16 QuickTimeDecoder::VideoTrackHandler::getHeight() const {
}
Graphics::PixelFormat QuickTimeDecoder::VideoTrackHandler::getPixelFormat() const {
- if (_forcedDitherPalette.size() > 0)
- return Graphics::PixelFormat::createFormatCLUT8();
-
// TODO: What should happen if there are multiple codecs with different formats?
return ((VideoSampleDesc *)_parent->sampleDescs[0])->_videoCodec->getPixelFormat();
}
bool QuickTimeDecoder::VideoTrackHandler::setOutputPixelFormat(const Graphics::PixelFormat &format) {
- if (_forcedDitherPalette.size() > 0)
- return false;
-
bool success = true;
for (uint i = 0; i < _parent->sampleDescs.size(); i++) {
@@ -597,10 +583,6 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame()
}
}
- // Handle forced dithering
- if (frame && _forcedDitherPalette.size() > 0)
- frame = forceDither(*frame);
-
if (frame && (_parent->scaleFactorX != 1 || _parent->scaleFactorY != 1)) {
if (!_scaledSurface) {
_scaledSurface = new Graphics::Surface();
@@ -641,7 +623,7 @@ Audio::Timestamp QuickTimeDecoder::VideoTrackHandler::getFrameTime(uint frame) c
const byte *QuickTimeDecoder::VideoTrackHandler::getPalette() const {
_dirtyPalette = false;
- return _forcedDitherPalette.size() > 0 ? _forcedDitherPalette.data() : _curPalette;
+ return _curPalette;
}
bool QuickTimeDecoder::VideoTrackHandler::setReverse(bool reverse) {
@@ -976,79 +958,10 @@ void QuickTimeDecoder::VideoTrackHandler::setDither(const byte *palette) {
desc->_videoCodec->setDither(Image::Codec::kDitherTypeQT, palette);
} else {
// Forced dither
- _forcedDitherPalette.resize(256, false);
- _forcedDitherPalette.set(palette, 0, 256);
- _ditherTable = Image::Codec::createQuickTimeDitherTable(_forcedDitherPalette.data(), 256);
- _dirtyPalette = true;
- }
- }
-}
-
-namespace {
-
-// Return a pixel in RGB554
-uint16 makeDitherColor(byte r, byte g, byte b) {
- return ((r & 0xF8) << 6) | ((g & 0xF8) << 1) | (b >> 4);
-}
-
-// Default template to convert a dither color
-template<typename PixelInt>
-inline uint16 readDitherColor(PixelInt srcColor, const Graphics::PixelFormat& format, const byte *palette) {
- byte r, g, b;
- format.colorToRGB(srcColor, r, g, b);
- return makeDitherColor(r, g, b);
-}
-
-// Specialized version for 8bpp
-template<>
-inline uint16 readDitherColor(byte srcColor, const Graphics::PixelFormat& format, const byte *palette) {
- return makeDitherColor(palette[srcColor * 3], palette[srcColor * 3 + 1], palette[srcColor * 3 + 2]);
-}
-
-template<typename PixelInt>
-void ditherFrame(const Graphics::Surface &src, Graphics::Surface &dst, const byte *ditherTable, const byte *palette = 0) {
- static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
-
- for (int y = 0; y < dst.h; y++) {
- const PixelInt *srcPtr = (const PixelInt *)src.getBasePtr(0, y);
- byte *dstPtr = (byte *)dst.getBasePtr(0, y);
- uint16 colorTableOffset = colorTableOffsets[y & 3];
-
- for (int x = 0; x < dst.w; x++) {
- uint16 color = readDitherColor(*srcPtr++, src.format, palette);
- *dstPtr++ = ditherTable[colorTableOffset + color];
- colorTableOffset += 0x4000;
+ desc->_videoCodec = new Image::DitherCodec(desc->_videoCodec);
+ desc->_videoCodec->setDither(Image::Codec::kDitherTypeQT, palette);
}
}
}
-} // End of anonymous namespace
-
-const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::forceDither(const Graphics::Surface &frame) {
- if (frame.format.bytesPerPixel == 1) {
- // This should always be true, but this is for sanity
- if (!_curPalette)
- return &frame;
-
- // If the palettes match, bail out
- if (memcmp(_forcedDitherPalette.data(), _curPalette, 256 * 3) == 0)
- return &frame;
- }
-
- // Need to create a new one
- if (!_ditherFrame) {
- _ditherFrame = new Graphics::Surface();
- _ditherFrame->create(frame.w, frame.h, Graphics::PixelFormat::createFormatCLUT8());
- }
-
- if (frame.format.bytesPerPixel == 1)
- ditherFrame<byte>(frame, *_ditherFrame, _ditherTable, _curPalette);
- else if (frame.format.bytesPerPixel == 2)
- ditherFrame<uint16>(frame, *_ditherFrame, _ditherTable);
- else if (frame.format.bytesPerPixel == 4)
- ditherFrame<uint32>(frame, *_ditherFrame, _ditherTable);
-
- return _ditherFrame;
-}
-
} // End of namespace Video
diff --git a/video/qt_decoder.h b/video/qt_decoder.h
index 02b4a595450..3a17adf65bb 100644
--- a/video/qt_decoder.h
+++ b/video/qt_decoder.h
@@ -369,12 +369,6 @@ private:
mutable bool _dirtyPalette;
bool _reversed;
- // Forced dithering of frames
- Graphics::Palette _forcedDitherPalette;
- byte *_ditherTable;
- Graphics::Surface *_ditherFrame;
- const Graphics::Surface *forceDither(const Graphics::Surface &frame);
-
Common::SeekableReadStream *getNextFramePacket(uint32 &descId);
uint32 getCurFrameDuration(); // media time
uint32 findKeyFrame(uint32 frame) const;
Commit: 320865e802c4d7143285d99e282296ec2c02f54e
https://github.com/scummvm/scummvm/commit/320865e802c4d7143285d99e282296ec2c02f54e
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-04-30T19:20:29+08:00
Commit Message:
IMAGE: Use setOutputPixelFormat to avoid extra conversion when dithering
Changed paths:
image/codecs/dither.cpp
diff --git a/image/codecs/dither.cpp b/image/codecs/dither.cpp
index 88dc5c96bd5..87a97a19410 100644
--- a/image/codecs/dither.cpp
+++ b/image/codecs/dither.cpp
@@ -70,21 +70,29 @@ DitherCodec::~DitherCodec() {
namespace {
// Default template to convert a dither color
-template<typename PixelInt>
-inline uint16 readQuickTimeDitherColor(PixelInt srcColor, const Graphics::PixelFormat& format, const byte *palette) {
+inline uint16 readQT_RGB(uint32 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
byte r, g, b;
format.colorToRGB(srcColor, r, g, b);
return makeQuickTimeDitherColor(r, g, b);
}
// Specialized version for 8bpp
-template<>
-inline uint16 readQuickTimeDitherColor(byte srcColor, const Graphics::PixelFormat& format, const byte *palette) {
+inline uint16 readQT_Palette(uint8 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
return makeQuickTimeDitherColor(palette[srcColor * 3], palette[srcColor * 3 + 1], palette[srcColor * 3 + 2]);
}
-template<typename PixelInt>
-void ditherQuickTimeFrame(const Graphics::Surface &src, Graphics::Surface &dst, const byte *ditherTable, const byte *palette = 0) {
+// Specialized version for RGB554
+inline uint16 readQT_RGB554(uint16 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
+ return srcColor;
+}
+
+// Specialized version for RGB555 and ARGB1555
+inline uint16 readQT_RGB555(uint16 srcColor, const Graphics::PixelFormat& format, const byte *palette) {
+ return (srcColor >> 1) & 0x3FFF;
+}
+
+template<typename PixelInt, class Fn>
+void ditherQuickTimeFrame(const Graphics::Surface &src, Graphics::Surface &dst, const byte *ditherTable, Fn fn, const byte *palette = 0) {
static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
for (int y = 0; y < dst.h; y++) {
@@ -93,7 +101,7 @@ void ditherQuickTimeFrame(const Graphics::Surface &src, Graphics::Surface &dst,
uint16 colorTableOffset = colorTableOffsets[y & 3];
for (int x = 0; x < dst.w; x++) {
- uint16 color = readQuickTimeDitherColor(*srcPtr++, src.format, palette);
+ uint16 color = fn(*srcPtr++, src.format, palette);
*dstPtr++ = ditherTable[colorTableOffset + color];
colorTableOffset += 0x4000;
}
@@ -127,11 +135,16 @@ const Graphics::Surface *DitherCodec::decodeFrame(Common::SeekableReadStream &st
}
if (frame->format.isCLUT8() && curPalette)
- ditherQuickTimeFrame<byte>(*frame, *_ditherFrame, _ditherTable, curPalette);
+ ditherQuickTimeFrame<byte>(*frame, *_ditherFrame, _ditherTable, readQT_Palette, curPalette);
+ else if (frame->format == Graphics::PixelFormat(2, 5, 5, 4, 0, 9, 4, 0, 0))
+ ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable, readQT_RGB554);
+ else if (frame->format == Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0) ||
+ frame->format == Graphics::PixelFormat(2, 5, 5, 5, 1, 10, 5, 0, 1))
+ ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable, readQT_RGB555);
else if (frame->format.bytesPerPixel == 2)
- ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable);
+ ditherQuickTimeFrame<uint16>(*frame, *_ditherFrame, _ditherTable, readQT_RGB);
else if (frame->format.bytesPerPixel == 4)
- ditherQuickTimeFrame<uint32>(*frame, *_ditherFrame, _ditherTable);
+ ditherQuickTimeFrame<uint32>(*frame, *_ditherFrame, _ditherTable, readQT_RGB);
return _ditherFrame;
}
@@ -184,6 +197,10 @@ void DitherCodec::setDither(DitherType type, const byte *palette) {
_dirtyPalette = true;
_ditherTable = createQuickTimeDitherTable(_forcedDitherPalette, 256);
+
+ // Prefer RGB554 or RGB555 to avoid extra conversion when dithering
+ if (!_codec->setOutputPixelFormat(Graphics::PixelFormat(2, 5, 5, 4, 0, 9, 4, 0, 0)))
+ _codec->setOutputPixelFormat(Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
}
}
Commit: 7294a5eff2da4732453f4e3d06391ab9f455c561
https://github.com/scummvm/scummvm/commit/7294a5eff2da4732453f4e3d06391ab9f455c561
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-04-30T19:20:29+08:00
Commit Message:
IMAGE: Use the palette class in the dithered codec wrapper
Changed paths:
image/codecs/dither.cpp
image/codecs/dither.h
diff --git a/image/codecs/dither.cpp b/image/codecs/dither.cpp
index 87a97a19410..b0480cac49e 100644
--- a/image/codecs/dither.cpp
+++ b/image/codecs/dither.cpp
@@ -58,7 +58,6 @@ DitherCodec::~DitherCodec() {
if (_disposeAfterUse == DisposeAfterUse::YES)
delete _codec;
- delete[] _forcedDitherPalette;
delete[] _ditherTable;
if (_ditherFrame) {
@@ -112,7 +111,7 @@ void ditherQuickTimeFrame(const Graphics::Surface &src, Graphics::Surface &dst,
const Graphics::Surface *DitherCodec::decodeFrame(Common::SeekableReadStream &stream) {
const Graphics::Surface *frame = _codec->decodeFrame(stream);
- if (!frame || !_forcedDitherPalette)
+ if (!frame || _forcedDitherPalette.empty())
return frame;
// TODO: Handle palettes that are owned by the container instead of the codec
@@ -124,7 +123,7 @@ const Graphics::Surface *DitherCodec::decodeFrame(Common::SeekableReadStream &st
return frame;
// If the palettes match, bail out
- if (memcmp(_forcedDitherPalette, curPalette, 256 * 3) == 0)
+ if (memcmp(_forcedDitherPalette.data(), curPalette, 256 * 3) == 0)
return frame;
}
@@ -150,32 +149,32 @@ const Graphics::Surface *DitherCodec::decodeFrame(Common::SeekableReadStream &st
}
Graphics::PixelFormat DitherCodec::getPixelFormat() const {
- if (!_forcedDitherPalette)
+ if (_forcedDitherPalette.empty())
return _codec->getPixelFormat();
return Graphics::PixelFormat::createFormatCLUT8();
}
bool DitherCodec::setOutputPixelFormat(const Graphics::PixelFormat &format) {
- if (!_forcedDitherPalette)
+ if (_forcedDitherPalette.empty())
return _codec->setOutputPixelFormat(format);
return format.isCLUT8();
}
bool DitherCodec::containsPalette() const {
- if (!_forcedDitherPalette)
+ if (_forcedDitherPalette.empty())
return _codec->containsPalette();
return true;
}
const byte *DitherCodec::getPalette() {
- if (!_forcedDitherPalette)
+ if (_forcedDitherPalette.empty())
return _codec->getPalette();
_dirtyPalette = false;
- return _forcedDitherPalette;
+ return _forcedDitherPalette.data();
}
bool DitherCodec::hasDirtyPalette() const {
- if (!_forcedDitherPalette)
+ if (_forcedDitherPalette.empty())
return _codec->hasDirtyPalette();
return _dirtyPalette;
}
@@ -189,14 +188,14 @@ void DitherCodec::setDither(DitherType type, const byte *palette) {
_codec->setDither(type, palette);
} else {
assert(type == kDitherTypeQT);
- assert(!_forcedDitherPalette);
+ assert(_forcedDitherPalette.empty());
// Forced dither
- _forcedDitherPalette = new byte[256 * 3];
- memcpy(_forcedDitherPalette, palette, 256 * 3);
+ _forcedDitherPalette.resize(256, false);
+ _forcedDitherPalette.set(palette, 0, 256);
_dirtyPalette = true;
- _ditherTable = createQuickTimeDitherTable(_forcedDitherPalette, 256);
+ _ditherTable = createQuickTimeDitherTable(_forcedDitherPalette.data(), 256);
// Prefer RGB554 or RGB555 to avoid extra conversion when dithering
if (!_codec->setOutputPixelFormat(Graphics::PixelFormat(2, 5, 5, 4, 0, 9, 4, 0, 0)))
diff --git a/image/codecs/dither.h b/image/codecs/dither.h
index 1275154c5cc..b4aef09c049 100644
--- a/image/codecs/dither.h
+++ b/image/codecs/dither.h
@@ -25,6 +25,7 @@
#include "image/codecs/codec.h"
#include "common/types.h"
+#include "graphics/palette.h"
namespace Image {
@@ -54,7 +55,7 @@ private:
Codec *_codec;
Graphics::Surface *_ditherFrame;
- byte *_forcedDitherPalette;
+ Graphics::Palette _forcedDitherPalette;
byte *_ditherTable;
bool _dirtyPalette;
};
More information about the Scummvm-git-logs
mailing list