diff options
author | Matthew Hoops | 2015-04-12 20:12:38 -0400 |
---|---|---|
committer | Matthew Hoops | 2015-04-12 20:12:38 -0400 |
commit | 49885d686edd522442b035986a4994c20fcc6cb2 (patch) | |
tree | c6363bc4131a9e31dc7f57af4a89e81ec5758ffe /video/qt_decoder.cpp | |
parent | 383c0bf3fa094ca3ea2935220ac9e5746ccf8a3d (diff) | |
download | scummvm-rg350-49885d686edd522442b035986a4994c20fcc6cb2.tar.gz scummvm-rg350-49885d686edd522442b035986a4994c20fcc6cb2.tar.bz2 scummvm-rg350-49885d686edd522442b035986a4994c20fcc6cb2.zip |
VIDEO: Implement fallback dithering for QuickTime videos
Used for any codec without direct dithering support
Diffstat (limited to 'video/qt_decoder.cpp')
-rw-r--r-- | video/qt_decoder.cpp | 105 |
1 files changed, 101 insertions, 4 deletions
diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp index cab7372a6d..324bf65294 100644 --- a/video/qt_decoder.cpp +++ b/video/qt_decoder.cpp @@ -292,6 +292,9 @@ QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder _curPalette = 0; _dirtyPalette = false; _reversed = false; + _forcedDitherPalette = 0; + _ditherTable = 0; + _ditherFrame = 0; } QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() { @@ -299,6 +302,14 @@ QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() { _scaledSurface->free(); delete _scaledSurface; } + + delete[] _forcedDitherPalette; + delete[] _ditherTable; + + if (_ditherFrame) { + _ditherFrame->free(); + delete _ditherFrame; + } } bool QuickTimeDecoder::VideoTrackHandler::endOfTrack() const { @@ -382,6 +393,9 @@ uint16 QuickTimeDecoder::VideoTrackHandler::getHeight() const { } Graphics::PixelFormat QuickTimeDecoder::VideoTrackHandler::getPixelFormat() const { + if (_forcedDitherPalette) + return Graphics::PixelFormat::createFormatCLUT8(); + return ((VideoSampleDesc *)_parent->sampleDescs[0])->_videoCodec->getPixelFormat(); } @@ -463,6 +477,10 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame() } } + // Handle forced dithering + if (frame && _forcedDitherPalette) + frame = forceDither(*frame); + if (frame && (_parent->scaleFactorX != 1 || _parent->scaleFactorY != 1)) { if (!_scaledSurface) { _scaledSurface = new Graphics::Surface(); @@ -476,6 +494,11 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame() return frame; } +const byte *QuickTimeDecoder::VideoTrackHandler::getPalette() const { + _dirtyPalette = false; + return _forcedDitherPalette ? _forcedDitherPalette : _curPalette; +} + bool QuickTimeDecoder::VideoTrackHandler::setReverse(bool reverse) { _reversed = reverse; @@ -757,9 +780,6 @@ bool QuickTimeDecoder::VideoTrackHandler::canDither() const { if (!desc || !desc->_videoCodec) return false; - - if (!desc->_videoCodec->canDither(Image::Codec::kDitherTypeQT)) - return false; } return true; @@ -770,8 +790,85 @@ void QuickTimeDecoder::VideoTrackHandler::setDither(const byte *palette) { for (uint i = 0; i < _parent->sampleDescs.size(); i++) { VideoSampleDesc *desc = (VideoSampleDesc *)_parent->sampleDescs[i]; - desc->_videoCodec->setDither(Image::Codec::kDitherTypeQT, palette); + + if (desc->_videoCodec->canDither(Image::Codec::kDitherTypeQT)) { + // Codec dither + desc->_videoCodec->setDither(Image::Codec::kDitherTypeQT, palette); + } else { + // Forced dither + _forcedDitherPalette = new byte[256 * 3]; + memcpy(_forcedDitherPalette, palette, 256 * 3); + _ditherTable = Image::Codec::createQuickTimeDitherTable(_forcedDitherPalette, 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; + } } } +} // 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, _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 |