[Scummvm-cvs-logs] scummvm master -> a5992a08a02d86e33eb9c30dc8c6dd6fcb0bdf77

clone2727 clone2727 at gmail.com
Sat Apr 11 21:03:52 CEST 2015


This automated email contains information about 12 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
f6d7c5e176 IMAGE: Add functions to allow for videos to be dithered
cfc64157a0 VIDEO: Allow AVI tracks to be dithered via the Codec
ad32fb5832 IMAGE: Add support for Cinepak VFW dithering
f342d63431 IMAGE: Allow for choosing dither type
e5048ecffe IMAGE: Rewrite a bunch of Cinepak using templates
0ceb383cd3 IMAGE: Add a function for generating QuickTime dither tables
c402d9a959 IMAGE: Add an unknown dither type
1271fbde8f IMAGE: Add support for QuickTime dithered Cinepak
b170b08822 VIDEO: Add support for dithering in QuickTime videos
7f12db95c6 IMAGE: Implement QuickTime dithering in rpza
94b317b0cd IMAGE: Implement QTRLE dithering
a5992a08a0 MOHAWK: Run Myst in 8bpp


Commit: f6d7c5e176a08c32702cdfb80ec8a626c25ead67
    https://github.com/scummvm/scummvm/commit/f6d7c5e176a08c32702cdfb80ec8a626c25ead67
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:36:18-04:00

Commit Message:
IMAGE: Add functions to allow for videos to be dithered

Changed paths:
    image/codecs/codec.h
    video/video_decoder.cpp
    video/video_decoder.h



diff --git a/image/codecs/codec.h b/image/codecs/codec.h
index d87758e..3a2da9f 100644
--- a/image/codecs/codec.h
+++ b/image/codecs/codec.h
@@ -86,6 +86,20 @@ public:
 	 * Does the codec have a dirty palette?
 	 */
 	virtual bool hasDirtyPalette() const { return false; }
+
+	/**
+	 * Can the codec dither down to 8bpp?
+	 *
+	 * @note This should only be used for VFW codecs
+	 */
+	virtual bool canDither() const { return false; }
+
+	/**
+	 * Activate dithering mode with a palette
+	 *
+	 * @note This should only be used for VFW codecs
+	 */
+	virtual void setDither(const byte *palette) {}
 };
 
 /**
diff --git a/video/video_decoder.cpp b/video/video_decoder.cpp
index a4bc5b8..217b4c8 100644
--- a/video/video_decoder.cpp
+++ b/video/video_decoder.cpp
@@ -47,6 +47,7 @@ VideoDecoder::VideoDecoder() {
 	_endTimeSet = false;
 	_nextVideoTrack = 0;
 	_mainAudioTrack = 0;
+	_canSetDither = true;
 
 	// Find the best format for output
 	_defaultHighColorFormat = g_system->getScreenFormat();
@@ -77,6 +78,7 @@ void VideoDecoder::close() {
 	_endTimeSet = false;
 	_nextVideoTrack = 0;
 	_mainAudioTrack = 0;
+	_canSetDither = true;
 }
 
 bool VideoDecoder::loadFile(const Common::String &filename) {
@@ -171,6 +173,7 @@ Graphics::PixelFormat VideoDecoder::getPixelFormat() const {
 
 const Graphics::Surface *VideoDecoder::decodeNextFrame() {
 	_needsUpdate = false;
+	_canSetDither = false;
 
 	readNextPacket();
 
@@ -488,6 +491,23 @@ bool VideoDecoder::seekIntern(const Audio::Timestamp &time) {
 	return true;
 }
 
+bool VideoDecoder::setDitheringPalette(const byte *palette) {
+	// If a frame was already decoded, we can't set it now.
+	if (!_canSetDither)
+		return false;
+
+	bool result = false;
+
+	for (TrackList::iterator it = _tracks.begin(); it != _tracks.end(); it++) {
+		if ((*it)->getTrackType() == Track::kTrackTypeVideo && ((VideoTrack *)*it)->canDither()) {
+			((VideoTrack *)*it)->setDither(palette);
+			result = true;
+		}
+	}
+
+	return result;
+}
+
 VideoDecoder::Track::Track() {
 	_paused = false;
 }
diff --git a/video/video_decoder.h b/video/video_decoder.h
index c3879e9..eca15e7 100644
--- a/video/video_decoder.h
+++ b/video/video_decoder.h
@@ -377,6 +377,25 @@ public:
 	 */
 	bool setReverse(bool reverse);
 
+	/**
+	 * Tell the video to dither to a palette.
+	 *
+	 * By default, VideoDecoder will return surfaces in native, or in the case
+	 * of YUV-based videos, the format set by setDefaultHighColorFormat().
+	 * For video formats or codecs that support it, this will start outputting
+	 * its surfaces in 8bpp with this palette.
+	 *
+	 * This should be called after loadStream(), but before a decodeNextFrame()
+	 * call. This is enforced.
+	 *
+	 * The palette will be copied, so you do not need to worry about the pointer
+	 * going out-of-scope.
+	 *
+	 * @param palette The palette to use for dithering
+	 * @return true on success, false otherwise
+	 */
+	bool setDitheringPalette(const byte *palette);
+
 	/////////////////////////////////////////
 	// Audio Control
 	/////////////////////////////////////////
@@ -604,6 +623,16 @@ protected:
 		 * Is the video track set to play in reverse?
 		 */
 		virtual bool isReversed() const { return false; }
+
+		/**
+		 * Can the video track dither?
+		 */
+		virtual bool canDither() const { return false; }
+
+		/**
+		 * Activate dithering mode with a palette
+		 */
+		virtual void setDither(const byte *palette) {}
 	};
 
 	/**
@@ -901,6 +930,9 @@ private:
 	mutable bool _dirtyPalette;
 	const byte *_palette;
 
+	// Enforcement of not being able to set dither
+	bool _canSetDither;
+
 	// Default PixelFormat settings
 	Graphics::PixelFormat _defaultHighColorFormat;
 


Commit: cfc64157a093bd369432169d069effc9e622ba21
    https://github.com/scummvm/scummvm/commit/cfc64157a093bd369432169d069effc9e622ba21
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:36:26-04:00

Commit Message:
VIDEO: Allow AVI tracks to be dithered via the Codec

Video for Windows made the Codec do the dithering work

Changed paths:
    video/avi_decoder.cpp
    video/avi_decoder.h



diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp
index 7119c72..7289253 100644
--- a/video/avi_decoder.cpp
+++ b/video/avi_decoder.cpp
@@ -815,6 +815,30 @@ void AVIDecoder::AVIVideoTrack::forceTrackEnd() {
 	_curFrame = _frameCount - 1;
 }
 
+const byte *AVIDecoder::AVIVideoTrack::getPalette() const {
+	if (_videoCodec && _videoCodec->containsPalette())
+		return _videoCodec->getPalette();
+
+	_dirtyPalette = false;
+	return _palette;
+}
+
+bool AVIDecoder::AVIVideoTrack::hasDirtyPalette() const {
+	if (_videoCodec && _videoCodec->containsPalette())
+		return _videoCodec->hasDirtyPalette();
+
+	return _dirtyPalette;
+}
+
+bool AVIDecoder::AVIVideoTrack::canDither() const {
+	return _videoCodec && _videoCodec->canDither();
+}
+
+void AVIDecoder::AVIVideoTrack::setDither(const byte *palette) {
+	assert(_videoCodec);
+	_videoCodec->setDither(palette);
+}
+
 AVIDecoder::AVIAudioTrack::AVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType)
 		: _audsHeader(streamHeader), _wvInfo(waveFormat), _soundType(soundType), _curChunk(0) {
 	_audStream = createAudioStream();
diff --git a/video/avi_decoder.h b/video/avi_decoder.h
index 8941ff4..6c1ce1a 100644
--- a/video/avi_decoder.h
+++ b/video/avi_decoder.h
@@ -179,11 +179,14 @@ protected:
 		int getCurFrame() const { return _curFrame; }
 		int getFrameCount() const { return _frameCount; }
 		const Graphics::Surface *decodeNextFrame() { return _lastFrame; }
-		const byte *getPalette() const { _dirtyPalette = false; return _palette; }
-		bool hasDirtyPalette() const { return _dirtyPalette; }
+
+		const byte *getPalette() const;
+		bool hasDirtyPalette() const;
 		void setCurFrame(int frame) { _curFrame = frame; }
 		void loadPaletteFromChunk(Common::SeekableReadStream *chunk);
 		void useInitialPalette();
+		bool canDither() const;
+		void setDither(const byte *palette);
 
 		bool isTruemotion1() const;
 		void forceDimensions(uint16 width, uint16 height);


Commit: ad32fb58326657b05731681b1a8945978928ef55
    https://github.com/scummvm/scummvm/commit/ad32fb58326657b05731681b1a8945978928ef55
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:36:31-04:00

Commit Message:
IMAGE: Add support for Cinepak VFW dithering

Changed paths:
  A image/codecs/cinepak_tables.h
    image/codecs/cinepak.cpp
    image/codecs/cinepak.h



diff --git a/image/codecs/cinepak.cpp b/image/codecs/cinepak.cpp
index 8464aa3..9760b5b 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 "common/debug.h"
 #include "common/stream.h"
@@ -47,10 +48,12 @@ namespace Image {
 	} else \
 		*((byte *)_curFrame.surface->getPixels() + offset) = lum
 
-CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec() {
-	_curFrame.surface = NULL;
-	_curFrame.strips = NULL;
+CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec(), _bitsPerPixel(bitsPerPixel) {
+	_curFrame.surface = 0;
+	_curFrame.strips = 0;
 	_y = 0;
+	_colorMap = 0;
+	_ditherPalette = 0;
 
 	if (bitsPerPixel == 8) {
 		_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
@@ -86,6 +89,9 @@ CinepakDecoder::~CinepakDecoder() {
 
 	delete[] _curFrame.strips;
 	delete[] _clipTableBuf;
+
+	delete[] _colorMap;
+	delete[] _ditherPalette;
 }
 
 const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream &stream) {
@@ -96,7 +102,7 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream
 	_curFrame.height = stream.readUint16BE();
 	_curFrame.stripCount = stream.readUint16BE();
 
-	if (_curFrame.strips == NULL)
+	if (!_curFrame.strips)
 		_curFrame.strips = new CinepakStrip[_curFrame.stripCount];
 
 	debug(4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount);
@@ -166,7 +172,10 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream
 			case 0x30:
 			case 0x31:
 			case 0x32:
-				decodeVectors(stream, i, chunkID, chunkSize);
+				if (_ditherPalette)
+					ditherVectors(stream, i, chunkID, chunkSize);
+				else
+					decodeVectors(stream, i, chunkID, chunkSize);
 				break;
 			default:
 				warning("Unknown Cinepak chunk ID %02x", chunkID);
@@ -311,4 +320,227 @@ void CinepakDecoder::decodeVectors(Common::SeekableReadStream &stream, uint16 st
 	}
 }
 
+bool CinepakDecoder::canDither() const {
+	return _bitsPerPixel == 24;
+}
+
+void CinepakDecoder::setDither(const byte *palette) {
+	assert(canDither());
+
+	delete[] _colorMap;
+	delete[] _ditherPalette;
+
+	_ditherPalette = new byte[256 * 3];
+	memcpy(_ditherPalette, palette, 256 * 3);
+
+	_dirtyPalette = true;
+	_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
+	_colorMap = new byte[221];
+
+	for (int i = 0; i < 221; i++)
+		_colorMap[i] = findNearestRGB(i);
+}
+
+byte CinepakDecoder::findNearestRGB(int index) const {
+	int r = s_defaultPalette[index * 3];
+	int g = s_defaultPalette[index * 3 + 1];
+	int b = s_defaultPalette[index * 3 + 2];
+
+	byte result = 0;
+	int diff = 0x7FFFFFFF;
+
+	for (int i = 0; i < 256; i++) {
+		int bDiff = b - (int)_ditherPalette[i * 3 + 2];
+		int curDiffB = diff - (bDiff * bDiff);
+
+		if (curDiffB > 0) {
+			int gDiff = g - (int)_ditherPalette[i * 3 + 1];
+			int curDiffG = curDiffB - (gDiff * gDiff);
+
+			if (curDiffG > 0) {
+				int rDiff = r - (int)_ditherPalette[i * 3];
+				int curDiffR = curDiffG - (rDiff * rDiff);
+
+				if (curDiffR > 0) {
+					diff -= curDiffR;
+					result = i;
+
+					if (diff == 0)
+						break;
+				}
+			}
+		}
+	}
+
+	return result;
+}
+
+void CinepakDecoder::ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
+	uint32 flag = 0, mask = 0;
+	byte *iy[4];
+	int32 startPos = stream.pos();
+
+	for (uint16 y = _curFrame.strips[strip].rect.top; y < _curFrame.strips[strip].rect.bottom; y += 4) {
+		iy[0] = (byte *)_curFrame.surface->getPixels() + _curFrame.strips[strip].rect.left + y * _curFrame.width;
+		iy[1] = iy[0] + _curFrame.width;
+		iy[2] = iy[1] + _curFrame.width;
+		iy[3] = iy[2] + _curFrame.width;
+
+		for (uint16 x = _curFrame.strips[strip].rect.left; x < _curFrame.strips[strip].rect.right; x += 4) {
+			if ((chunkID & 0x01) && !(mask >>= 1)) {
+				if ((stream.pos() - startPos + 4) > (int32)chunkSize)
+					return;
+
+				flag  = stream.readUint32BE();
+				mask  = 0x80000000;
+			}
+
+			if (!(chunkID & 0x01) || (flag & mask)) {
+				if (!(chunkID & 0x02) && !(mask >>= 1)) {
+					if ((stream.pos() - startPos + 4) > (int32)chunkSize)
+						return;
+
+					flag  = stream.readUint32BE();
+					mask  = 0x80000000;
+				}
+
+				byte blockBuffer[16];
+
+				if ((chunkID & 0x02) || (~flag & mask)) {
+					if ((stream.pos() - startPos + 1) > (int32)chunkSize)
+						return;
+
+					ditherCodebookSmooth(_curFrame.strips[strip].v1_codebook[stream.readByte()], blockBuffer);
+					iy[0][0] = blockBuffer[0];
+					iy[0][1] = blockBuffer[1];
+					iy[0][2] = blockBuffer[2];
+					iy[0][3] = blockBuffer[3];
+					iy[1][0] = blockBuffer[4];
+					iy[1][1] = blockBuffer[5];
+					iy[1][2] = blockBuffer[6];
+					iy[1][3] = blockBuffer[7];
+					iy[2][0] = blockBuffer[8];
+					iy[2][1] = blockBuffer[9];
+					iy[2][2] = blockBuffer[10];
+					iy[2][3] = blockBuffer[11];
+					iy[3][0] = blockBuffer[12];
+					iy[3][1] = blockBuffer[13];
+					iy[3][2] = blockBuffer[14];
+					iy[3][3] = blockBuffer[15];
+				} else if (flag & mask) {
+					if ((stream.pos() - startPos + 4) > (int32)chunkSize)
+						return;
+
+					ditherCodebookDetail(_curFrame.strips[strip].v4_codebook[stream.readByte()], blockBuffer);
+					iy[0][0] = blockBuffer[0];
+					iy[0][1] = blockBuffer[1];
+					iy[1][0] = blockBuffer[4];
+					iy[1][1] = blockBuffer[5];
+
+					ditherCodebookDetail(_curFrame.strips[strip].v4_codebook[stream.readByte()], blockBuffer);
+					iy[0][2] = blockBuffer[2];
+					iy[0][3] = blockBuffer[3];
+					iy[1][2] = blockBuffer[6];
+					iy[1][3] = blockBuffer[7];
+
+					ditherCodebookDetail(_curFrame.strips[strip].v4_codebook[stream.readByte()], blockBuffer);
+					iy[2][0] = blockBuffer[8];
+					iy[2][1] = blockBuffer[9];
+					iy[3][0] = blockBuffer[12];
+					iy[3][1] = blockBuffer[13];
+
+					ditherCodebookDetail(_curFrame.strips[strip].v4_codebook[stream.readByte()], blockBuffer);
+					iy[2][2] = blockBuffer[10];
+					iy[2][3] = blockBuffer[11];
+					iy[3][2] = blockBuffer[14];
+					iy[3][3] = blockBuffer[15];
+				}
+			}
+
+			for (byte i = 0; i < 4; i++)
+				iy[i] += 4;
+		}
+	}
+}
+
+void CinepakDecoder::ditherCodebookDetail(const CinepakCodebook &codebook, byte *dst) const {
+	int uLookup = (byte)codebook.u * 2;
+	int vLookup = (byte)codebook.v * 2;
+	uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup];
+	uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1];
+
+	int yLookup1 = codebook.y[0] * 2;
+	int yLookup2 = codebook.y[1] * 2;
+	int yLookup3 = codebook.y[2] * 2;
+	int yLookup4 = codebook.y[3] * 2;
+
+	uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1];
+	uint32 pixelGroup2 = uv2 | s_yLookup[yLookup2 + 1];
+	uint32 pixelGroup3 = uv1 | s_yLookup[yLookup3];
+	uint32 pixelGroup4 = uv1 | s_yLookup[yLookup4];
+	uint32 pixelGroup5 = uv1 | s_yLookup[yLookup1];
+	uint32 pixelGroup6 = uv1 | s_yLookup[yLookup2];
+	uint32 pixelGroup7 = uv2 | s_yLookup[yLookup3 + 1];
+	uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1];
+
+	dst[0] = getRGBLookupEntry(pixelGroup1 & 0xFFFF);
+	dst[1] = getRGBLookupEntry(pixelGroup2 >> 16);
+	dst[2] = getRGBLookupEntry(pixelGroup5 & 0xFFFF);
+	dst[3] = getRGBLookupEntry(pixelGroup6 >> 16);
+	dst[4] = getRGBLookupEntry(pixelGroup3 & 0xFFFF);
+	dst[5] = getRGBLookupEntry(pixelGroup4 >> 16);
+	dst[6] = getRGBLookupEntry(pixelGroup7 & 0xFFFF);
+	dst[7] = getRGBLookupEntry(pixelGroup8 >> 16);
+	dst[8] = getRGBLookupEntry(pixelGroup1 >> 16);
+	dst[9] = getRGBLookupEntry(pixelGroup6 & 0xFFFF);
+	dst[10] = getRGBLookupEntry(pixelGroup5 >> 16);
+	dst[11] = getRGBLookupEntry(pixelGroup2 & 0xFFFF);
+	dst[12] = getRGBLookupEntry(pixelGroup3 >> 16);
+	dst[13] = getRGBLookupEntry(pixelGroup8 & 0xFFFF);
+	dst[14] = getRGBLookupEntry(pixelGroup7 >> 16);
+	dst[15] = getRGBLookupEntry(pixelGroup4 & 0xFFFF);
+}
+
+void CinepakDecoder::ditherCodebookSmooth(const CinepakCodebook &codebook, byte *dst) const {
+	int uLookup = (byte)codebook.u * 2;
+	int vLookup = (byte)codebook.v * 2;
+	uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup];
+	uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1];
+
+	int yLookup1 = codebook.y[0] * 2;
+	int yLookup2 = codebook.y[1] * 2;
+	int yLookup3 = codebook.y[2] * 2;
+	int yLookup4 = codebook.y[3] * 2;
+
+	uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1];
+	uint32 pixelGroup2 = uv1 | s_yLookup[yLookup2];
+	uint32 pixelGroup3 = uv1 | s_yLookup[yLookup1];
+	uint32 pixelGroup4 = uv2 | s_yLookup[yLookup2 + 1];
+	uint32 pixelGroup5 = uv2 | s_yLookup[yLookup3 + 1];
+	uint32 pixelGroup6 = uv1 | s_yLookup[yLookup3];
+	uint32 pixelGroup7 = uv1 | s_yLookup[yLookup4];
+	uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1];
+
+	dst[0] = getRGBLookupEntry(pixelGroup1 & 0xFFFF);
+	dst[1] = getRGBLookupEntry(pixelGroup1 >> 16);
+	dst[2] = getRGBLookupEntry(pixelGroup2 & 0xFFFF);
+	dst[3] = getRGBLookupEntry(pixelGroup2 >> 16);
+	dst[4] = getRGBLookupEntry(pixelGroup3 & 0xFFFF);
+	dst[5] = getRGBLookupEntry(pixelGroup3 >> 16);
+	dst[6] = getRGBLookupEntry(pixelGroup4 & 0xFFFF);
+	dst[7] = getRGBLookupEntry(pixelGroup4 >> 16);
+	dst[8] = getRGBLookupEntry(pixelGroup5 >> 16);
+	dst[9] = getRGBLookupEntry(pixelGroup6 & 0xFFFF);
+	dst[10] = getRGBLookupEntry(pixelGroup7 >> 16);
+	dst[11] = getRGBLookupEntry(pixelGroup8 & 0xFFFF);
+	dst[12] = getRGBLookupEntry(pixelGroup6 >> 16);
+	dst[13] = getRGBLookupEntry(pixelGroup5 & 0xFFFF);
+	dst[14] = getRGBLookupEntry(pixelGroup8 >> 16);
+	dst[15] = getRGBLookupEntry(pixelGroup7 & 0xFFFF);
+}
+
+byte CinepakDecoder::getRGBLookupEntry(uint16 index) const {
+	return _colorMap[s_defaultPaletteLookup[CLIP<int>(index, 0, 1024)]];
+}
+
 } // End of namespace Image
diff --git a/image/codecs/cinepak.h b/image/codecs/cinepak.h
index e9cd437..ce8451b 100644
--- a/image/codecs/cinepak.h
+++ b/image/codecs/cinepak.h
@@ -72,14 +72,32 @@ public:
 	const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream);
 	Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; }
 
+	bool containsPalette() const { return _ditherPalette != 0; }
+	const byte *getPalette() { _dirtyPalette = false; return _ditherPalette; }
+	bool hasDirtyPalette() const { return _dirtyPalette; }
+	bool canDither() const;
+	void setDither(const byte *palette);
+
 private:
 	CinepakFrame _curFrame;
 	int32 _y;
+	int _bitsPerPixel;
 	Graphics::PixelFormat _pixelFormat;
 	byte *_clipTable, *_clipTableBuf;
 
+	byte *_ditherPalette;
+	bool _dirtyPalette;
+	byte *_rgbLookup;
+	byte *_colorMap;
+
 	void loadCodebook(Common::SeekableReadStream &stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize);
 	void decodeVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
+
+	byte findNearestRGB(int index) const;
+	void ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
+	void ditherCodebookDetail(const CinepakCodebook &codebook, byte *dst) const;
+	void ditherCodebookSmooth(const CinepakCodebook &codebook, byte *dst) const;
+	byte getRGBLookupEntry(uint16 index) const;
 };
 
 } // End of namespace Image
diff --git a/image/codecs/cinepak_tables.h b/image/codecs/cinepak_tables.h
new file mode 100644
index 0000000..03131ce
--- /dev/null
+++ b/image/codecs/cinepak_tables.h
@@ -0,0 +1,780 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef IMAGE_CODECS_CINEPAK_TABLES_H
+#define IMAGE_CODECS_CINEPAK_TABLES_H
+
+#include "common/scummsys.h"
+
+namespace Image {
+
+static const byte s_defaultPaletteLookup[1024] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+	0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+	0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+	0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+	0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+	0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
+	0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
+	0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
+	0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
+	0x04, 0x04, 0x04, 0x05, 0x06, 0x07, 0x07, 0x07,
+	0x08, 0x08, 0x08, 0x09, 0x0A, 0x07, 0x07, 0x07,
+	0x0B, 0x0B, 0x0B, 0x0C, 0x0D, 0x0D, 0x0D, 0x07,
+	0x0E, 0x0E, 0x0E, 0x0F, 0x0D, 0x0D, 0x0D, 0x0D,
+	0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x0D, 0x0D,
+	0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
+	0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
+	0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
+	0x12, 0x12, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16,
+	0x17, 0x17, 0x17, 0x18, 0x19, 0x1A, 0x16, 0x16,
+	0x1B, 0x1B, 0x1B, 0x1C, 0x1D, 0x1E, 0x1E, 0x1E,
+	0x1F, 0x1F, 0x1F, 0x20, 0x21, 0x1E, 0x1E, 0x1E,
+	0x22, 0x22, 0x22, 0x23, 0x24, 0x24, 0x24, 0x1E,
+	0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
+	0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
+	0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
+	0x25, 0x25, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29,
+	0x2A, 0x2A, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2E,
+	0x2F, 0x2F, 0x2F, 0x30, 0x31, 0x32, 0x2E, 0x2E,
+	0x33, 0x33, 0x33, 0x34, 0x35, 0x36, 0x36, 0x36,
+	0x33, 0x33, 0x33, 0x34, 0x35, 0x36, 0x36, 0x36,
+	0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
+	0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
+	0x37, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3B, 0x3B,
+	0x3C, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x40, 0x40,
+	0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x45, 0x45,
+	0x46, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4A, 0x4A,
+	0x4B, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x4F, 0x4F,
+	0x4B, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x4F, 0x4F,
+	0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
+	0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
+	0x50, 0x50, 0x51, 0x52, 0x53, 0x54, 0x54, 0x54,
+	0x55, 0x55, 0x56, 0x57, 0x58, 0x59, 0x59, 0x59,
+	0x5A, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5E, 0x5E,
+	0x5F, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x63, 0x63,
+	0x64, 0x64, 0x65, 0x66, 0x67, 0x68, 0x68, 0x68,
+	0x64, 0x64, 0x65, 0x66, 0x67, 0x68, 0x68, 0x68,
+	0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
+	0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
+	0x69, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6D, 0x6D,
+	0x6E, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x72, 0x72,
+	0x73, 0x73, 0x74, 0x75, 0x76, 0x77, 0x77, 0x77,
+	0x78, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7C, 0x7C,
+	0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x81, 0x81,
+	0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x81, 0x81,
+	0x82, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86, 0x86,
+	0x82, 0x82, 0x83, 0x84, 0x85, 0x86, 0x86, 0x86,
+	0x87, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8B, 0x8B,
+	0x8C, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x90, 0x90,
+	0x91, 0x91, 0x92, 0x93, 0x94, 0x95, 0x95, 0x95,
+	0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
+	0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
+	0x96, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9A, 0x9A,
+	0x9B, 0x9B, 0x9B, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D,
+	0x9E, 0x9B, 0x9B, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D,
+	0x9E, 0x9E, 0x9F, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1,
+	0xA2, 0xA2, 0xA3, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5,
+	0xA6, 0xA6, 0xA7, 0xA8, 0xA9, 0xA9, 0xA9, 0xA9,
+	0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
+	0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
+	0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD,
+	0xAE, 0xAE, 0xAE, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0,
+	0xAE, 0xAE, 0xAE, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0,
+	0xB4, 0xB1, 0xB1, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3,
+	0xB4, 0xB4, 0xB5, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7,
+	0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBB, 0xBB, 0xBB,
+	0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
+	0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
+	0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF,
+	0xC2, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1,
+	0xC2, 0xC2, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1,
+	0xC2, 0xC2, 0xC2, 0xC3, 0xC4, 0xC4, 0xC4, 0xC4,
+	0xC8, 0xC5, 0xC5, 0xC6, 0xC7, 0xC7, 0xC7, 0xC7,
+	0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCB, 0xCB, 0xCB,
+	0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
+	0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
+	0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
+	0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
+	0xD2, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
+	0xD2, 0xD2, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1,
+	0xD2, 0xD2, 0xD2, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3,
+	0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+	0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+	0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+	0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+	0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
+	0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
+	0xD8, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
+	0xD8, 0xD8, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+	0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+	0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+	0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+	0xD8, 0xD8, 0xD8, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9,
+	0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+	0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+	0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+	0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
+	0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+	0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+	0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+	0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+	0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC
+};
+
+static const byte s_defaultPalette[221 * 3] = {
+	0x02, 0x02, 0x02,
+	0x19, 0x19, 0x19,
+	0x47, 0x02, 0x19,
+	0x19, 0x0D, 0x47,
+	0x00, 0x51, 0x00,
+	0x2E, 0x3A, 0x00,
+	0x5C, 0x23, 0x00,
+	0x8F, 0x0A, 0x00,
+	0x00, 0x45, 0x2E,
+	0x2E, 0x2E, 0x2E,
+	0x5C, 0x17, 0x2E,
+	0x00, 0x3A, 0x5C,
+	0x2E, 0x23, 0x5C,
+	0x5C, 0x0C, 0x5C,
+	0x00, 0x2C, 0x95,
+	0x2E, 0x15, 0x95,
+	0x00, 0x1A, 0xDC,
+	0x2E, 0x03, 0xDC,
+	0x15, 0x66, 0x15,
+	0x43, 0x4F, 0x15,
+	0x71, 0x38, 0x15,
+	0xA4, 0x1E, 0x15,
+	0xDB, 0x02, 0x15,
+	0x15, 0x5A, 0x43,
+	0x43, 0x43, 0x43,
+	0x71, 0x2C, 0x43,
+	0xA4, 0x13, 0x43,
+	0x15, 0x4F, 0x71,
+	0x43, 0x38, 0x71,
+	0x71, 0x21, 0x71,
+	0xA4, 0x07, 0x71,
+	0x15, 0x40, 0xAA,
+	0x43, 0x29, 0xAA,
+	0x71, 0x12, 0xAA,
+	0x15, 0x2F, 0xF1,
+	0x43, 0x18, 0xF1,
+	0x71, 0x01, 0xF1,
+	0x29, 0x79, 0x29,
+	0x57, 0x62, 0x29,
+	0x85, 0x4B, 0x29,
+	0xB7, 0x32, 0x29,
+	0xEF, 0x16, 0x29,
+	0x29, 0x6E, 0x57,
+	0x57, 0x57, 0x57,
+	0x85, 0x40, 0x57,
+	0xB7, 0x27, 0x57,
+	0xEF, 0x0B, 0x57,
+	0x29, 0x62, 0x85,
+	0x57, 0x4B, 0x85,
+	0x85, 0x34, 0x85,
+	0xB7, 0x1B, 0x85,
+	0x29, 0x54, 0xBE,
+	0x57, 0x3D, 0xBE,
+	0x85, 0x26, 0xBE,
+	0xB7, 0x0D, 0xBE,
+	0x03, 0xB5, 0x09,
+	0x3C, 0x99, 0x09,
+	0x6A, 0x82, 0x09,
+	0x98, 0x6B, 0x09,
+	0xCA, 0x51, 0x09,
+	0x03, 0xA9, 0x3C,
+	0x3C, 0x8C, 0x3C,
+	0x6A, 0x75, 0x3C,
+	0x98, 0x5E, 0x3C,
+	0xCA, 0x45, 0x3C,
+	0x03, 0x9D, 0x6A,
+	0x3C, 0x81, 0x6A,
+	0x6A, 0x6A, 0x6A,
+	0x98, 0x53, 0x6A,
+	0xCA, 0x39, 0x6A,
+	0x03, 0x92, 0x98,
+	0x3C, 0x75, 0x98,
+	0x6A, 0x5E, 0x98,
+	0x98, 0x47, 0x98,
+	0xCA, 0x2E, 0x98,
+	0x03, 0x83, 0xD1,
+	0x3C, 0x67, 0xD1,
+	0x6A, 0x50, 0xD1,
+	0x98, 0x39, 0xD1,
+	0xCA, 0x20, 0xD1,
+	0x14, 0xC7, 0x1B,
+	0x4D, 0xAB, 0x1B,
+	0x7B, 0x94, 0x1B,
+	0xA9, 0x7D, 0x1B,
+	0xDC, 0x63, 0x1B,
+	0x14, 0xBA, 0x4D,
+	0x4D, 0x9E, 0x4D,
+	0x7B, 0x87, 0x4D,
+	0xA9, 0x70, 0x4D,
+	0xDC, 0x57, 0x4D,
+	0x14, 0xAF, 0x7B,
+	0x4D, 0x92, 0x7B,
+	0x7B, 0x7B, 0x7B,
+	0xA9, 0x64, 0x7B,
+	0xDC, 0x4B, 0x7B,
+	0x14, 0xA3, 0xA9,
+	0x4D, 0x87, 0xA9,
+	0x7B, 0x70, 0xA9,
+	0xA9, 0x59, 0xA9,
+	0xDC, 0x40, 0xA9,
+	0x14, 0x95, 0xE2,
+	0x4D, 0x79, 0xE2,
+	0x7B, 0x62, 0xE2,
+	0xA9, 0x4B, 0xE2,
+	0xDC, 0x31, 0xE2,
+	0x25, 0xD8, 0x2C,
+	0x5E, 0xBB, 0x2C,
+	0x8C, 0xA4, 0x2C,
+	0xBA, 0x8D, 0x2C,
+	0xED, 0x74, 0x2C,
+	0x25, 0xCB, 0x5E,
+	0x5E, 0xAF, 0x5E,
+	0x8C, 0x98, 0x5E,
+	0xBA, 0x81, 0x5E,
+	0xED, 0x67, 0x5E,
+	0x25, 0xC0, 0x8C,
+	0x5E, 0xA3, 0x8C,
+	0x8C, 0x8C, 0x8C,
+	0xBA, 0x75, 0x8C,
+	0xED, 0x5C, 0x8C,
+	0x25, 0xB4, 0xBA,
+	0x5E, 0x98, 0xBA,
+	0x8C, 0x81, 0xBA,
+	0xBA, 0x6A, 0xBA,
+	0xED, 0x50, 0xBA,
+	0x25, 0xA6, 0xF3,
+	0x5E, 0x8A, 0xF3,
+	0x8C, 0x73, 0xF3,
+	0xBA, 0x5C, 0xF3,
+	0xED, 0x42, 0xF3,
+	0x35, 0xF6, 0x04,
+	0x6E, 0xD9, 0x04,
+	0x9C, 0xC2, 0x04,
+	0xCA, 0xAB, 0x04,
+	0xFD, 0x92, 0x04,
+	0x35, 0xE8, 0x3C,
+	0x6E, 0xCB, 0x3C,
+	0x9C, 0xB4, 0x3C,
+	0xCA, 0x9D, 0x3C,
+	0xFD, 0x84, 0x3C,
+	0x35, 0xDB, 0x6E,
+	0x6E, 0xBF, 0x6E,
+	0x9C, 0xA8, 0x6E,
+	0xCA, 0x91, 0x6E,
+	0xFD, 0x78, 0x6E,
+	0x35, 0xD0, 0x9C,
+	0x6E, 0xB3, 0x9C,
+	0x9C, 0x9C, 0x9C,
+	0xCA, 0x85, 0x9C,
+	0xFD, 0x6C, 0x9C,
+	0x35, 0xC4, 0xCA,
+	0x6E, 0xA8, 0xCA,
+	0x9C, 0x91, 0xCA,
+	0xCA, 0x7A, 0xCA,
+	0xFD, 0x61, 0xCA,
+	0x7E, 0xE9, 0x13,
+	0xAC, 0xD2, 0x13,
+	0xDA, 0xBB, 0x13,
+	0x45, 0xF7, 0x4B,
+	0x7E, 0xDB, 0x4B,
+	0xAC, 0xC4, 0x4B,
+	0xDA, 0xAD, 0x4B,
+	0x45, 0xEB, 0x7E,
+	0x7E, 0xCE, 0x7E,
+	0xAC, 0xB7, 0x7E,
+	0xDA, 0xA0, 0x7E,
+	0x45, 0xDF, 0xAC,
+	0x7E, 0xC3, 0xAC,
+	0xAC, 0xAC, 0xAC,
+	0xDA, 0x95, 0xAC,
+	0x45, 0xD4, 0xDA,
+	0x7E, 0xB7, 0xDA,
+	0xAC, 0xA0, 0xDA,
+	0xDA, 0x89, 0xDA,
+	0x8C, 0xF7, 0x22,
+	0xBA, 0xE0, 0x22,
+	0xE8, 0xC9, 0x22,
+	0x8C, 0xE9, 0x59,
+	0xBA, 0xD2, 0x59,
+	0xE8, 0xBB, 0x59,
+	0x53, 0xF9, 0x8C,
+	0x8C, 0xDD, 0x8C,
+	0xBA, 0xC6, 0x8C,
+	0xE8, 0xAF, 0x8C,
+	0x53, 0xEE, 0xBA,
+	0x8C, 0xD1, 0xBA,
+	0xBA, 0xBA, 0xBA,
+	0xE8, 0xA3, 0xBA,
+	0x53, 0xE2, 0xE8,
+	0x8C, 0xC6, 0xE8,
+	0xBA, 0xAF, 0xE8,
+	0xE8, 0x98, 0xE8,
+	0xC8, 0xEE, 0x30,
+	0xF6, 0xD7, 0x30,
+	0x9A, 0xF7, 0x67,
+	0xC8, 0xE0, 0x67,
+	0xF6, 0xC9, 0x67,
+	0x9A, 0xEA, 0x9A,
+	0xC8, 0xD3, 0x9A,
+	0xF6, 0xBC, 0x9A,
+	0x61, 0xFB, 0xC8,
+	0x9A, 0xDF, 0xC8,
+	0xC8, 0xC8, 0xC8,
+	0xF6, 0xB1, 0xC8,
+	0x61, 0xF0, 0xF6,
+	0x9A, 0xD3, 0xF6,
+	0xC8, 0xBC, 0xF6,
+	0xF6, 0xA5, 0xF6,
+	0xD5, 0xFB, 0x3D,
+	0xD5, 0xED, 0x74,
+	0xA7, 0xF7, 0xA7,
+	0xD5, 0xE0, 0xA7,
+	0xA7, 0xEC, 0xD5,
+	0xD5, 0xD5, 0xD5,
+	0xE1, 0xFA, 0x81,
+	0xE1, 0xED, 0xB3,
+	0xB3, 0xF8, 0xE1,
+	0xE1, 0xE1, 0xE1,
+	0xED, 0xF9, 0xBF,
+	0xED, 0xED, 0xED,
+	0xF8, 0xF8, 0xF8
+};
+
+static const uint32 s_yLookup[512] = {
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000040, 0x00000000, 0x00000040, 0x00000000,
+	0x00000040, 0x00000000, 0x00000040, 0x00000000,
+	0x00000040, 0x00000000, 0x00000040, 0x00000040,
+	0x00000040, 0x00000040, 0x00000040, 0x00000040,
+	0x00000040, 0x00000040, 0x00400040, 0x00000040,
+	0x00400040, 0x00000040, 0x00400040, 0x00000040,
+	0x00400040, 0x00000040, 0x00400040, 0x00000040,
+	0x00400040, 0x00400040, 0x00400040, 0x00400040,
+	0x00400040, 0x00400040, 0x00400040, 0x00400040,
+	0x00400040, 0x00400040, 0x00400040, 0x00400040,
+	0x00400040, 0x00400040, 0x00400040, 0x00400040,
+	0x00400040, 0x00400040, 0x00400080, 0x00400040,
+	0x00400080, 0x00400040, 0x00400080, 0x00400040,
+	0x00400080, 0x00400040, 0x00400080, 0x00400080,
+	0x00400080, 0x00400080, 0x00400080, 0x00400080,
+	0x00400080, 0x00400080, 0x00400080, 0x00400080,
+	0x00800080, 0x00400080, 0x00800080, 0x00400080,
+	0x00800080, 0x00400080, 0x00800080, 0x00400080,
+	0x00800080, 0x00800080, 0x00800080, 0x00800080,
+	0x00800080, 0x00800080, 0x00800080, 0x00800080,
+	0x00800080, 0x00800080, 0x00800080, 0x00800080,
+	0x00800080, 0x00800080, 0x00800080, 0x00800080,
+	0x00800080, 0x00800080, 0x008000C0, 0x00800080,
+	0x008000C0, 0x00800080, 0x008000C0, 0x00800080,
+	0x008000C0, 0x00800080, 0x008000C0, 0x008000C0,
+	0x008000C0, 0x008000C0, 0x008000C0, 0x008000C0,
+	0x008000C0, 0x008000C0, 0x00C000C0, 0x008000C0,
+	0x00C000C0, 0x008000C0, 0x00C000C0, 0x008000C0,
+	0x00C000C0, 0x008000C0, 0x00C000C0, 0x00C000C0,
+	0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
+	0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
+	0x00C000C0, 0x00C000C0, 0x00C000C0, 0x00C000C0,
+	0x00C000C0, 0x00C000C0, 0x00C00100, 0x00C000C0,
+	0x00C00100, 0x00C000C0, 0x00C00100, 0x00C000C0,
+	0x00C00100, 0x00C000C0, 0x00C00100, 0x00C00100,
+	0x00C00100, 0x00C00100, 0x00C00100, 0x00C00100,
+	0x00C00100, 0x00C00100, 0x01000100, 0x00C00100,
+	0x01000100, 0x00C00100, 0x01000100, 0x00C00100,
+	0x01000100, 0x00C00100, 0x01000100, 0x01000100,
+	0x01000100, 0x01000100, 0x01000100, 0x01000100,
+	0x01000100, 0x01000100, 0x01000100, 0x01000100,
+	0x01000100, 0x01000100, 0x01000100, 0x01000100,
+	0x01000100, 0x01000100, 0x01000140, 0x01000100,
+	0x01000140, 0x01000100, 0x01000140, 0x01000100,
+	0x01000140, 0x01000140, 0x01000140, 0x01000140,
+	0x01000140, 0x01000140, 0x01000140, 0x01000140,
+	0x01400140, 0x01000140, 0x01400140, 0x01000140,
+	0x01400140, 0x01000140, 0x01400140, 0x01000140,
+	0x01400140, 0x01400140, 0x01400140, 0x01400140,
+	0x01400140, 0x01400140, 0x01400140, 0x01400140,
+	0x01400140, 0x01400140, 0x01400140, 0x01400140,
+	0x01400140, 0x01400140, 0x01400180, 0x01400140,
+	0x01400180, 0x01400140, 0x01400180, 0x01400140,
+	0x01400180, 0x01400140, 0x01400180, 0x01400180,
+	0x01400180, 0x01400180, 0x01400180, 0x01400180,
+	0x01800180, 0x01400180, 0x01800180, 0x01400180,
+	0x01800180, 0x01400180, 0x01800180, 0x01400180,
+	0x01800180, 0x01800180, 0x01800180, 0x01800180,
+	0x01800180, 0x01800180, 0x01800180, 0x01800180,
+	0x01800180, 0x01800180, 0x01800180, 0x01800180,
+	0x01800180, 0x01800180, 0x018001C0, 0x01800180,
+	0x018001C0, 0x01800180, 0x018001C0, 0x01800180,
+	0x018001C0, 0x018001C0, 0x018001C0, 0x018001C0,
+	0x018001C0, 0x018001C0, 0x018001C0, 0x018001C0,
+	0x01C001C0, 0x018001C0, 0x01C001C0, 0x018001C0,
+	0x01C001C0, 0x018001C0, 0x01C001C0, 0x01C001C0,
+	0x01C001C0, 0x01C001C0, 0x01C001C0, 0x01C001C0,
+	0x01C001C0, 0x01C001C0, 0x01C001C0, 0x01C001C0,
+	0x01C001C0, 0x01C001C0, 0x01C00200, 0x01C001C0,
+	0x01C00200, 0x01C001C0, 0x01C00200, 0x01C001C0,
+	0x01C00200, 0x01C001C0, 0x01C00200, 0x01C00200,
+	0x01C00200, 0x01C00200, 0x01C00200, 0x01C00200,
+	0x02000200, 0x01C00200, 0x02000200, 0x01C00200,
+	0x02000200, 0x01C00200, 0x02000200, 0x02000200,
+	0x02000200, 0x02000200, 0x02000200, 0x02000200,
+	0x02000200, 0x02000200, 0x02000200, 0x02000200,
+	0x02000200, 0x02000200, 0x02000240, 0x02000200,
+	0x02000240, 0x02000200, 0x02000240, 0x02000200,
+	0x02000240, 0x02000240, 0x02000240, 0x02000240,
+	0x02000240, 0x02000240, 0x02400240, 0x02000240,
+	0x02400240, 0x02000240, 0x02400240, 0x02000240,
+	0x02400240, 0x02000240, 0x02400240, 0x02400240,
+	0x02400240, 0x02400240, 0x02400240, 0x02400240,
+	0x02400240, 0x02400240, 0x02400240, 0x02400240,
+	0x02400280, 0x02400240, 0x02400280, 0x02400240,
+	0x02400280, 0x02400240, 0x02400280, 0x02400280,
+	0x02400280, 0x02400280, 0x02400280, 0x02400280,
+	0x02800280, 0x02400280, 0x02800280, 0x02400280,
+	0x02800280, 0x02400280, 0x02800280, 0x02800280,
+	0x02800280, 0x02800280, 0x02800280, 0x02800280,
+	0x02800280, 0x02800280, 0x02800280, 0x02800280,
+	0x02800280, 0x02800280, 0x028002C0, 0x02800280,
+	0x028002C0, 0x02800280, 0x028002C0, 0x02800280,
+	0x028002C0, 0x028002C0, 0x028002C0, 0x028002C0,
+	0x02C002C0, 0x028002C0, 0x02C002C0, 0x028002C0,
+	0x02C002C0, 0x028002C0, 0x02C002C0, 0x02C002C0,
+	0x02C002C0, 0x02C002C0, 0x02C002C0, 0x02C002C0,
+	0x02C002C0, 0x02C002C0, 0x02C002C0, 0x02C002C0,
+	0x02C00300, 0x02C002C0, 0x02C00300, 0x02C002C0,
+	0x02C00300, 0x02C002C0, 0x02C00300, 0x02C00300,
+	0x02C00300, 0x02C00300, 0x02C00300, 0x02C00300,
+	0x03000300, 0x02C00300, 0x03000300, 0x02C00300,
+	0x03000300, 0x03000300, 0x03000300, 0x03000300,
+	0x03000300, 0x03000300, 0x03000300, 0x03000300,
+	0x03000300, 0x03000300, 0x03000340, 0x03000300,
+	0x03000340, 0x03000300, 0x03000340, 0x03000300,
+	0x03000340, 0x03000340, 0x03000340, 0x03000340,
+	0x03400340, 0x03000340, 0x03400340, 0x03000340,
+	0x03400340, 0x03000340, 0x03400340, 0x03400340,
+	0x03400340, 0x03400340, 0x03400340, 0x03400340,
+	0x03400340, 0x03400340, 0x03400340, 0x03400340,
+	0x03400380, 0x03400340, 0x03400380, 0x03400340,
+	0x03400380, 0x03400380, 0x03400380, 0x03400380,
+	0x03800380, 0x03400380, 0x03800380, 0x03400380,
+	0x03800380, 0x03400380, 0x03800380, 0x03800380,
+	0x03800380, 0x03800380, 0x03800380, 0x03800380,
+	0x03800380, 0x03800380, 0x038003C0, 0x03800380,
+	0x038003C0, 0x03800380, 0x038003C0, 0x03800380,
+	0x038003C0, 0x038003C0, 0x038003C0, 0x038003C0,
+	0x03C003C0, 0x038003C0, 0x03C003C0, 0x038003C0,
+	0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
+	0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
+	0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
+	0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0,
+	0x03C003C0, 0x03C003C0, 0x03C003C0, 0x03C003C0
+};
+
+static const uint32 s_uLookup[512] = {
+	0x00200020, 0x00200020, 0x00200020, 0x00200020,
+	0x00200020, 0x00200020, 0x00200020, 0x00200020,
+	0x00200020, 0x00200020, 0x00280020, 0x00200020,
+	0x00280020, 0x00200020, 0x00280020, 0x00200020,
+	0x00280020, 0x00200020, 0x00280020, 0x00280020,
+	0x00280020, 0x00280020, 0x00280020, 0x00280020,
+	0x00280020, 0x00280020, 0x00280020, 0x00280020,
+	0x00280020, 0x00280028, 0x00280020, 0x00280028,
+	0x00280020, 0x00280028, 0x00280020, 0x00280028,
+	0x00280028, 0x00280028, 0x00280028, 0x00280028,
+	0x00280028, 0x00280028, 0x00280028, 0x00280028,
+	0x00280028, 0x00280028, 0x00280028, 0x00280028,
+	0x00280028, 0x00280028, 0x00280028, 0x00280028,
+	0x00280028, 0x00280028, 0x00280028, 0x00280028,
+	0x00280028, 0x00280028, 0x00300028, 0x00280028,
+	0x00300028, 0x00280028, 0x00300028, 0x00280028,
+	0x00300028, 0x00280028, 0x00300028, 0x00280028,
+	0x00300028, 0x00300028, 0x00300028, 0x00300028,
+	0x00300028, 0x00300028, 0x00300028, 0x00300028,
+	0x00300028, 0x00300028, 0x00300028, 0x00300028,
+	0x00300028, 0x00300030, 0x00300028, 0x00300030,
+	0x00300028, 0x00300030, 0x00300028, 0x00300030,
+	0x00300028, 0x00300030, 0x00300028, 0x00300030,
+	0x00300030, 0x00300030, 0x00300030, 0x00300030,
+	0x00300030, 0x00300030, 0x00300030, 0x00300030,
+	0x00300030, 0x00300030, 0x00300030, 0x00300030,
+	0x00300030, 0x00300030, 0x00300030, 0x00300030,
+	0x00300030, 0x00300030, 0x00300030, 0x00300030,
+	0x00300030, 0x00300030, 0x00300030, 0x00300030,
+	0x00300030, 0x00300030, 0x00380030, 0x00300030,
+	0x00380030, 0x00300030, 0x00380030, 0x00300030,
+	0x00380030, 0x00300030, 0x00380030, 0x00300030,
+	0x00380030, 0x00300030, 0x00380030, 0x00300030,
+	0x00380030, 0x00380030, 0x00380030, 0x00380030,
+	0x00380030, 0x00380030, 0x00380030, 0x00380030,
+	0x00380030, 0x00380030, 0x00380030, 0x00380030,
+	0x00380030, 0x00380030, 0x00380030, 0x00380038,
+	0x00380030, 0x00380038, 0x00380030, 0x00380038,
+	0x00380030, 0x00380038, 0x00380030, 0x00380038,
+	0x00380030, 0x00380038, 0x00380030, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00380038, 0x00380038, 0x00380038, 0x00380038,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00080000, 0x00000000,
+	0x00080000, 0x00000000, 0x00080000, 0x00000000,
+	0x00080000, 0x00000000, 0x00080000, 0x00000000,
+	0x00080000, 0x00000000, 0x00080000, 0x00000000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080008, 0x00080000, 0x00080008,
+	0x00080000, 0x00080008, 0x00080000, 0x00080008,
+	0x00080000, 0x00080008, 0x00080000, 0x00080008,
+	0x00080008, 0x00080008, 0x00080008, 0x00080008,
+	0x00080008, 0x00080008, 0x00080008, 0x00080008,
+	0x00080008, 0x00080008, 0x00080008, 0x00080008,
+	0x00080008, 0x00080008, 0x00080008, 0x00080008,
+	0x00080008, 0x00080008, 0x00080008, 0x00080008,
+	0x00080008, 0x00080008, 0x00100008, 0x00080008,
+	0x00100008, 0x00080008, 0x00100008, 0x00080008,
+	0x00100008, 0x00080008, 0x00100008, 0x00080008,
+	0x00100008, 0x00080008, 0x00100008, 0x00100008,
+	0x00100008, 0x00100008, 0x00100008, 0x00100008,
+	0x00100008, 0x00100008, 0x00100008, 0x00100008,
+	0x00100008, 0x00100008, 0x00100008, 0x00100010,
+	0x00100008, 0x00100010, 0x00100008, 0x00100010,
+	0x00100008, 0x00100010, 0x00100008, 0x00100010,
+	0x00100010, 0x00100010, 0x00100010, 0x00100010,
+	0x00100010, 0x00100010, 0x00100010, 0x00100010,
+	0x00100010, 0x00100010, 0x00100010, 0x00100010,
+	0x00100010, 0x00100010, 0x00100010, 0x00100010,
+	0x00100010, 0x00100010, 0x00100010, 0x00100010,
+	0x00100010, 0x00100010, 0x00180010, 0x00100010,
+	0x00180010, 0x00100010, 0x00180010, 0x00100010,
+	0x00180010, 0x00100010, 0x00180010, 0x00100010,
+	0x00180010, 0x00180010, 0x00180010, 0x00180010,
+	0x00180010, 0x00180010, 0x00180010, 0x00180010,
+	0x00180010, 0x00180010, 0x00180010, 0x00180018,
+	0x00180010, 0x00180018, 0x00180010, 0x00180018,
+	0x00180010, 0x00180018, 0x00180010, 0x00180018,
+	0x00180018, 0x00180018, 0x00180018, 0x00180018,
+	0x00180018, 0x00180018, 0x00180018, 0x00180018,
+	0x00180018, 0x00180018, 0x00180018, 0x00180018,
+	0x00180018, 0x00180018, 0x00180018, 0x00180018,
+	0x00180018, 0x00180018, 0x00180018, 0x00180018,
+	0x00200018, 0x00180018, 0x00200018, 0x00180018,
+	0x00200018, 0x00180018, 0x00200018, 0x00180018,
+	0x00200018, 0x00200018, 0x00200018, 0x00200018,
+	0x00200018, 0x00200018, 0x00200018, 0x00200018,
+	0x00200018, 0x00200018, 0x00200018, 0x00200020,
+	0x00200018, 0x00200020, 0x00200018, 0x00200020,
+	0x00200018, 0x00200020, 0x00200020, 0x00200020,
+	0x00200020, 0x00200020, 0x00200020, 0x00200020,
+	0x00200020, 0x00200020, 0x00200020, 0x00200020
+};
+
+static const uint32 s_vLookup[512] = {
+	0x00030003, 0x00030003, 0x00030003, 0x00030003,
+	0x00030003, 0x00030003, 0x00030003, 0x00030003,
+	0x00030003, 0x00030003, 0x00030003, 0x00030004,
+	0x00030003, 0x00030004, 0x00030003, 0x00030004,
+	0x00030003, 0x00030004, 0x00030004, 0x00030004,
+	0x00030004, 0x00030004, 0x00030004, 0x00030004,
+	0x00030004, 0x00030004, 0x00030004, 0x00030004,
+	0x00030004, 0x00040004, 0x00030004, 0x00040004,
+	0x00030004, 0x00040004, 0x00030004, 0x00040004,
+	0x00040004, 0x00040004, 0x00040004, 0x00040004,
+	0x00040004, 0x00040004, 0x00040004, 0x00040004,
+	0x00040004, 0x00040004, 0x00040004, 0x00040004,
+	0x00040004, 0x00040004, 0x00040004, 0x00040004,
+	0x00040004, 0x00040004, 0x00040004, 0x00040004,
+	0x00040004, 0x00040005, 0x00040004, 0x00040005,
+	0x00040004, 0x00040005, 0x00040004, 0x00040005,
+	0x00040004, 0x00040005, 0x00040005, 0x00040005,
+	0x00040005, 0x00040005, 0x00040005, 0x00040005,
+	0x00040005, 0x00040005, 0x00040005, 0x00040005,
+	0x00040005, 0x00050005, 0x00040005, 0x00050005,
+	0x00040005, 0x00050005, 0x00040005, 0x00050005,
+	0x00040005, 0x00050005, 0x00050005, 0x00050005,
+	0x00050005, 0x00050005, 0x00050005, 0x00050005,
+	0x00050005, 0x00050005, 0x00050005, 0x00050005,
+	0x00050005, 0x00050005, 0x00050005, 0x00050005,
+	0x00050005, 0x00050005, 0x00050005, 0x00050005,
+	0x00050005, 0x00050005, 0x00050005, 0x00050005,
+	0x00050005, 0x00050006, 0x00050005, 0x00050006,
+	0x00050005, 0x00050006, 0x00050005, 0x00050006,
+	0x00050005, 0x00050006, 0x00050006, 0x00050006,
+	0x00050006, 0x00050006, 0x00050006, 0x00050006,
+	0x00050006, 0x00050006, 0x00050006, 0x00050006,
+	0x00050006, 0x00050006, 0x00050006, 0x00060006,
+	0x00050006, 0x00060006, 0x00050006, 0x00060006,
+	0x00050006, 0x00060006, 0x00050006, 0x00060006,
+	0x00050006, 0x00060006, 0x00060006, 0x00060006,
+	0x00060006, 0x00060006, 0x00060006, 0x00060006,
+	0x00060006, 0x00060006, 0x00060006, 0x00060006,
+	0x00060006, 0x00060006, 0x00060006, 0x00060006,
+	0x00060006, 0x00060006, 0x00060006, 0x00060006,
+	0x00060006, 0x00060006, 0x00060006, 0x00060006,
+	0x00060006, 0x00060007, 0x00060006, 0x00060007,
+	0x00060006, 0x00060007, 0x00060006, 0x00060007,
+	0x00060006, 0x00060007, 0x00060006, 0x00060007,
+	0x00060007, 0x00060007, 0x00060007, 0x00060007,
+	0x00060007, 0x00060007, 0x00060007, 0x00060007,
+	0x00060007, 0x00060007, 0x00060007, 0x00060007,
+	0x00060007, 0x00070007, 0x00060007, 0x00070007,
+	0x00060007, 0x00070007, 0x00060007, 0x00070007,
+	0x00060007, 0x00070007, 0x00060007, 0x00070007,
+	0x00060007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00070007, 0x00070007, 0x00070007, 0x00070007,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000001, 0x00000000, 0x00000001,
+	0x00000000, 0x00000001, 0x00000000, 0x00000001,
+	0x00000000, 0x00000001, 0x00000000, 0x00000001,
+	0x00000000, 0x00000001, 0x00000001, 0x00000001,
+	0x00000001, 0x00000001, 0x00000001, 0x00000001,
+	0x00000001, 0x00000001, 0x00000001, 0x00000001,
+	0x00000001, 0x00000001, 0x00000001, 0x00000001,
+	0x00000001, 0x00010001, 0x00000001, 0x00010001,
+	0x00000001, 0x00010001, 0x00000001, 0x00010001,
+	0x00000001, 0x00010001, 0x00000001, 0x00010001,
+	0x00000001, 0x00010001, 0x00010001, 0x00010001,
+	0x00010001, 0x00010001, 0x00010001, 0x00010001,
+	0x00010001, 0x00010001, 0x00010001, 0x00010001,
+	0x00010001, 0x00010001, 0x00010001, 0x00010001,
+	0x00010001, 0x00010001, 0x00010001, 0x00010001,
+	0x00010001, 0x00010001, 0x00010001, 0x00010001,
+	0x00010001, 0x00010001, 0x00010001, 0x00010001,
+	0x00010001, 0x00010002, 0x00010001, 0x00010002,
+	0x00010001, 0x00010002, 0x00010001, 0x00010002,
+	0x00010001, 0x00010002, 0x00010001, 0x00010002,
+	0x00010002, 0x00010002, 0x00010002, 0x00010002,
+	0x00010002, 0x00010002, 0x00010002, 0x00010002,
+	0x00010002, 0x00010002, 0x00010002, 0x00010002,
+	0x00010002, 0x00020002, 0x00010002, 0x00020002,
+	0x00010002, 0x00020002, 0x00010002, 0x00020002,
+	0x00010002, 0x00020002, 0x00020002, 0x00020002,
+	0x00020002, 0x00020002, 0x00020002, 0x00020002,
+	0x00020002, 0x00020002, 0x00020002, 0x00020002,
+	0x00020002, 0x00020002, 0x00020002, 0x00020002,
+	0x00020002, 0x00020002, 0x00020002, 0x00020002,
+	0x00020002, 0x00020002, 0x00020002, 0x00020002,
+	0x00020002, 0x00020003, 0x00020002, 0x00020003,
+	0x00020002, 0x00020003, 0x00020002, 0x00020003,
+	0x00020003, 0x00020003, 0x00020003, 0x00020003,
+	0x00020003, 0x00020003, 0x00020003, 0x00020003,
+	0x00020003, 0x00020003, 0x00020003, 0x00030003,
+	0x00020003, 0x00030003, 0x00020003, 0x00030003,
+	0x00020003, 0x00030003, 0x00030003, 0x00030003,
+	0x00030003, 0x00030003, 0x00030003, 0x00030003,
+	0x00030003, 0x00030003, 0x00030003, 0x00030003
+};
+
+} // End of namespace Image
+
+#endif


Commit: f342d63431fc0784adc08648f17a6bbad934743f
    https://github.com/scummvm/scummvm/commit/f342d63431fc0784adc08648f17a6bbad934743f
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:36:37-04:00

Commit Message:
IMAGE: Allow for choosing dither type

Changed paths:
    image/codecs/cinepak.cpp
    image/codecs/cinepak.h
    image/codecs/codec.h
    video/avi_decoder.cpp



diff --git a/image/codecs/cinepak.cpp b/image/codecs/cinepak.cpp
index 9760b5b..de51988 100644
--- a/image/codecs/cinepak.cpp
+++ b/image/codecs/cinepak.cpp
@@ -320,12 +320,12 @@ void CinepakDecoder::decodeVectors(Common::SeekableReadStream &stream, uint16 st
 	}
 }
 
-bool CinepakDecoder::canDither() const {
-	return _bitsPerPixel == 24;
+bool CinepakDecoder::canDither(DitherType type) const {
+	return type == kDitherTypeVFW && _bitsPerPixel == 24;
 }
 
-void CinepakDecoder::setDither(const byte *palette) {
-	assert(canDither());
+void CinepakDecoder::setDither(DitherType type, const byte *palette) {
+	assert(canDither(type));
 
 	delete[] _colorMap;
 	delete[] _ditherPalette;
diff --git a/image/codecs/cinepak.h b/image/codecs/cinepak.h
index ce8451b..ccdad08 100644
--- a/image/codecs/cinepak.h
+++ b/image/codecs/cinepak.h
@@ -75,8 +75,8 @@ public:
 	bool containsPalette() const { return _ditherPalette != 0; }
 	const byte *getPalette() { _dirtyPalette = false; return _ditherPalette; }
 	bool hasDirtyPalette() const { return _dirtyPalette; }
-	bool canDither() const;
-	void setDither(const byte *palette);
+	bool canDither(DitherType type) const;
+	void setDither(DitherType type, const byte *palette);
 
 private:
 	CinepakFrame _curFrame;
diff --git a/image/codecs/codec.h b/image/codecs/codec.h
index 3a2da9f..9abbb3d 100644
--- a/image/codecs/codec.h
+++ b/image/codecs/codec.h
@@ -59,6 +59,17 @@ public:
 	virtual ~Codec() {}
 
 	/**
+	 * A type of dithering.
+	 */
+	enum DitherType {
+		/** Video for Windows dithering */
+		kDitherTypeVFW,
+
+		/** QuickTime dithering */
+		kDitherTypeQT
+	};
+
+	/**
 	 * Decode the frame for the given data and return a pointer to a surface
 	 * containing the decoded frame.
 	 *
@@ -89,17 +100,13 @@ public:
 
 	/**
 	 * Can the codec dither down to 8bpp?
-	 *
-	 * @note This should only be used for VFW codecs
 	 */
-	virtual bool canDither() const { return false; }
+	virtual bool canDither(DitherType type) const { return false; }
 
 	/**
 	 * Activate dithering mode with a palette
-	 *
-	 * @note This should only be used for VFW codecs
 	 */
-	virtual void setDither(const byte *palette) {}
+	virtual void setDither(DitherType type, const byte *palette) {}
 };
 
 /**
diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp
index 7289253..700975d 100644
--- a/video/avi_decoder.cpp
+++ b/video/avi_decoder.cpp
@@ -831,12 +831,12 @@ bool AVIDecoder::AVIVideoTrack::hasDirtyPalette() const {
 }
 
 bool AVIDecoder::AVIVideoTrack::canDither() const {
-	return _videoCodec && _videoCodec->canDither();
+	return _videoCodec && _videoCodec->canDither(Image::Codec::kDitherTypeVFW);
 }
 
 void AVIDecoder::AVIVideoTrack::setDither(const byte *palette) {
 	assert(_videoCodec);
-	_videoCodec->setDither(palette);
+	_videoCodec->setDither(Image::Codec::kDitherTypeVFW, palette);
 }
 
 AVIDecoder::AVIAudioTrack::AVIAudioTrack(const AVIStreamHeader &streamHeader, const PCMWaveFormat &waveFormat, Audio::Mixer::SoundType soundType)


Commit: e5048ecffed49eb0ddb6b69488cfeebbc3820d8f
    https://github.com/scummvm/scummvm/commit/e5048ecffed49eb0ddb6b69488cfeebbc3820d8f
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:36:42-04:00

Commit Message:
IMAGE: Rewrite a bunch of Cinepak using templates

Changed paths:
    image/codecs/cinepak.cpp
    image/codecs/cinepak.h



diff --git a/image/codecs/cinepak.cpp b/image/codecs/cinepak.cpp
index de51988..f43c952 100644
--- a/image/codecs/cinepak.cpp
+++ b/image/codecs/cinepak.cpp
@@ -35,18 +35,290 @@
 
 namespace Image {
 
-#define PUT_PIXEL(offset, lum, u, v) \
-	if (_pixelFormat.bytesPerPixel != 1) { \
-		byte r = _clipTable[lum + (v << 1)]; \
-		byte g = _clipTable[lum - (u >> 1) - v]; \
-		byte b = _clipTable[lum + (u << 1)]; \
-		\
-		if (_pixelFormat.bytesPerPixel == 2) \
-			*((uint16 *)_curFrame.surface->getPixels() + offset) = _pixelFormat.RGBToColor(r, g, b); \
-		else \
-			*((uint32 *)_curFrame.surface->getPixels() + offset) = _pixelFormat.RGBToColor(r, g, b); \
-	} else \
-		*((byte *)_curFrame.surface->getPixels() + offset) = lum
+namespace {
+
+inline void convertYUVToRGB(const byte *clipTable, byte y, int8 u, int8 v, byte &r, byte &g, byte &b) {
+	r = clipTable[y + (v << 1)];
+	g = clipTable[y - (u >> 1) - v];
+	b = clipTable[y + (u << 1)];
+}
+
+inline uint32 convertYUVToColor(const byte *clipTable, const Graphics::PixelFormat &format, byte y, byte u, byte v) {
+	byte r, g, b;
+	convertYUVToRGB(clipTable, y, u, v, r, g, b);
+	return format.RGBToColor(r, g, b);
+}
+
+inline uint16 createDitherTableIndex(const byte *clipTable, byte y, int8 u, int8 v) {
+	byte r, g, b;
+	convertYUVToRGB(clipTable, y, u, v, r, g, b);
+	return ((r & 0xF8) << 6) |
+	       ((g & 0xF8) << 1) |
+	       ((b & 0xF0) >> 4);
+}
+
+/**
+ * Put a raw pixel to the destination surface
+ */
+template<typename PixelInt>
+inline void putPixelRaw(PixelInt *dst, const byte *clipTable, const Graphics::PixelFormat &format, byte y, byte u, byte v) {
+	*dst = convertYUVToColor(clipTable, format, y, u, v);
+}
+
+/**
+ * Specialized putPixelRaw for palettized 8bpp output
+ */
+template<>
+inline void putPixelRaw(byte *dst, const byte *clipTable, const Graphics::PixelFormat &format, byte y, byte u, byte v) {
+	*dst = y;
+}
+
+/**
+ * The default codebook converter: raw output.
+ */
+struct CodebookConverterRaw {
+	template<typename PixelInt>
+	static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, PixelInt *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+		const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex];
+		putPixelRaw(rows[0] + 0, clipTable, format, codebook.y[0], codebook.u, codebook.v);
+		putPixelRaw(rows[0] + 1, clipTable, format, codebook.y[0], codebook.u, codebook.v);
+		putPixelRaw(rows[1] + 0, clipTable, format, codebook.y[0], codebook.u, codebook.v);
+		putPixelRaw(rows[1] + 1, clipTable, format, codebook.y[0], codebook.u, codebook.v);
+
+		putPixelRaw(rows[0] + 2, clipTable, format, codebook.y[1], codebook.u, codebook.v);
+		putPixelRaw(rows[0] + 3, clipTable, format, codebook.y[1], codebook.u, codebook.v);
+		putPixelRaw(rows[1] + 2, clipTable, format, codebook.y[1], codebook.u, codebook.v);
+		putPixelRaw(rows[1] + 3, clipTable, format, codebook.y[1], codebook.u, codebook.v);
+
+		putPixelRaw(rows[2] + 0, clipTable, format, codebook.y[2], codebook.u, codebook.v);
+		putPixelRaw(rows[2] + 1, clipTable, format, codebook.y[2], codebook.u, codebook.v);
+		putPixelRaw(rows[3] + 0, clipTable, format, codebook.y[2], codebook.u, codebook.v);
+		putPixelRaw(rows[3] + 1, clipTable, format, codebook.y[2], codebook.u, codebook.v);
+
+		putPixelRaw(rows[2] + 2, clipTable, format, codebook.y[3], codebook.u, codebook.v);
+		putPixelRaw(rows[2] + 3, clipTable, format, codebook.y[3], codebook.u, codebook.v);
+		putPixelRaw(rows[3] + 2, clipTable, format, codebook.y[3], codebook.u, codebook.v);
+		putPixelRaw(rows[3] + 3, clipTable, format, codebook.y[3], codebook.u, codebook.v);
+	}
+
+	template<typename PixelInt>
+	static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, PixelInt *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+		const CinepakCodebook &codebook1 = strip.v4_codebook[codebookIndex[0]];
+		putPixelRaw(rows[0] + 0, clipTable, format, codebook1.y[0], codebook1.u, codebook1.v);
+		putPixelRaw(rows[0] + 1, clipTable, format, codebook1.y[1], codebook1.u, codebook1.v);
+		putPixelRaw(rows[1] + 0, clipTable, format, codebook1.y[2], codebook1.u, codebook1.v);
+		putPixelRaw(rows[1] + 1, clipTable, format, codebook1.y[3], codebook1.u, codebook1.v);
+
+		const CinepakCodebook &codebook2 = strip.v4_codebook[codebookIndex[1]];
+		putPixelRaw(rows[0] + 2, clipTable, format, codebook2.y[0], codebook2.u, codebook2.v);
+		putPixelRaw(rows[0] + 3, clipTable, format, codebook2.y[1], codebook2.u, codebook2.v);
+		putPixelRaw(rows[1] + 2, clipTable, format, codebook2.y[2], codebook2.u, codebook2.v);
+		putPixelRaw(rows[1] + 3, clipTable, format, codebook2.y[3], codebook2.u, codebook2.v);
+
+		const CinepakCodebook &codebook3 = strip.v4_codebook[codebookIndex[2]];
+		putPixelRaw(rows[2] + 0, clipTable, format, codebook3.y[0], codebook3.u, codebook3.v);
+		putPixelRaw(rows[2] + 1, clipTable, format, codebook3.y[1], codebook3.u, codebook3.v);
+		putPixelRaw(rows[3] + 0, clipTable, format, codebook3.y[2], codebook3.u, codebook3.v);
+		putPixelRaw(rows[3] + 1, clipTable, format, codebook3.y[3], codebook3.u, codebook3.v);
+
+		const CinepakCodebook &codebook4 = strip.v4_codebook[codebookIndex[3]];
+		putPixelRaw(rows[2] + 2, clipTable, format, codebook4.y[0], codebook4.u, codebook4.v);
+		putPixelRaw(rows[2] + 3, clipTable, format, codebook4.y[1], codebook4.u, codebook4.v);
+		putPixelRaw(rows[3] + 2, clipTable, format, codebook4.y[2], codebook4.u, codebook4.v);
+		putPixelRaw(rows[3] + 3, clipTable, format, codebook4.y[3], codebook4.u, codebook4.v);
+	}
+};
+
+/**
+ * Codebook converter that dithers in VFW-style
+ */
+struct CodebookConverterDitherVFW {
+	static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+		const CinepakCodebook &codebook = strip.v1_codebook[codebookIndex];
+		byte blockBuffer[16];
+		ditherCodebookSmooth(codebook, blockBuffer, colorMap);
+		rows[0][0] = blockBuffer[0];
+		rows[0][1] = blockBuffer[1];
+		rows[0][2] = blockBuffer[2];
+		rows[0][3] = blockBuffer[3];
+		rows[1][0] = blockBuffer[4];
+		rows[1][1] = blockBuffer[5];
+		rows[1][2] = blockBuffer[6];
+		rows[1][3] = blockBuffer[7];
+		rows[2][0] = blockBuffer[8];
+		rows[2][1] = blockBuffer[9];
+		rows[2][2] = blockBuffer[10];
+		rows[2][3] = blockBuffer[11];
+		rows[3][0] = blockBuffer[12];
+		rows[3][1] = blockBuffer[13];
+		rows[3][2] = blockBuffer[14];
+		rows[3][3] = blockBuffer[15];
+	}
+
+	static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+		byte blockBuffer[16];
+		ditherCodebookDetail(strip.v4_codebook[codebookIndex[0]], blockBuffer, colorMap);
+		rows[0][0] = blockBuffer[0];
+		rows[0][1] = blockBuffer[1];
+		rows[1][0] = blockBuffer[4];
+		rows[1][1] = blockBuffer[5];
+
+		ditherCodebookDetail(strip.v4_codebook[codebookIndex[1]], blockBuffer, colorMap);
+		rows[0][2] = blockBuffer[2];
+		rows[0][3] = blockBuffer[3];
+		rows[1][2] = blockBuffer[6];
+		rows[1][3] = blockBuffer[7];
+
+		ditherCodebookDetail(strip.v4_codebook[codebookIndex[2]], blockBuffer, colorMap);
+		rows[2][0] = blockBuffer[8];
+		rows[2][1] = blockBuffer[9];
+		rows[3][0] = blockBuffer[12];
+		rows[3][1] = blockBuffer[13];
+
+		ditherCodebookDetail(strip.v4_codebook[codebookIndex[3]], blockBuffer, colorMap);
+		rows[2][2] = blockBuffer[10];
+		rows[2][3] = blockBuffer[11];
+		rows[3][2] = blockBuffer[14];
+		rows[3][3] = blockBuffer[15];
+	}
+
+private:
+	static inline void ditherCodebookDetail(const CinepakCodebook &codebook, byte *dst, const byte *colorMap) {
+		int uLookup = (byte)codebook.u * 2;
+		int vLookup = (byte)codebook.v * 2;
+		uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup];
+		uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1];
+
+		int yLookup1 = codebook.y[0] * 2;
+		int yLookup2 = codebook.y[1] * 2;
+		int yLookup3 = codebook.y[2] * 2;
+		int yLookup4 = codebook.y[3] * 2;
+
+		uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1];
+		uint32 pixelGroup2 = uv2 | s_yLookup[yLookup2 + 1];
+		uint32 pixelGroup3 = uv1 | s_yLookup[yLookup3];
+		uint32 pixelGroup4 = uv1 | s_yLookup[yLookup4];
+		uint32 pixelGroup5 = uv1 | s_yLookup[yLookup1];
+		uint32 pixelGroup6 = uv1 | s_yLookup[yLookup2];
+		uint32 pixelGroup7 = uv2 | s_yLookup[yLookup3 + 1];
+		uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1];
+
+		dst[0] = getRGBLookupEntry(colorMap, pixelGroup1 & 0xFFFF);
+		dst[1] = getRGBLookupEntry(colorMap, pixelGroup2 >> 16);
+		dst[2] = getRGBLookupEntry(colorMap, pixelGroup5 & 0xFFFF);
+		dst[3] = getRGBLookupEntry(colorMap, pixelGroup6 >> 16);
+		dst[4] = getRGBLookupEntry(colorMap, pixelGroup3 & 0xFFFF);
+		dst[5] = getRGBLookupEntry(colorMap, pixelGroup4 >> 16);
+		dst[6] = getRGBLookupEntry(colorMap, pixelGroup7 & 0xFFFF);
+		dst[7] = getRGBLookupEntry(colorMap, pixelGroup8 >> 16);
+		dst[8] = getRGBLookupEntry(colorMap, pixelGroup1 >> 16);
+		dst[9] = getRGBLookupEntry(colorMap, pixelGroup6 & 0xFFFF);
+		dst[10] = getRGBLookupEntry(colorMap, pixelGroup5 >> 16);
+		dst[11] = getRGBLookupEntry(colorMap, pixelGroup2 & 0xFFFF);
+		dst[12] = getRGBLookupEntry(colorMap, pixelGroup3 >> 16);
+		dst[13] = getRGBLookupEntry(colorMap, pixelGroup8 & 0xFFFF);
+		dst[14] = getRGBLookupEntry(colorMap, pixelGroup7 >> 16);
+		dst[15] = getRGBLookupEntry(colorMap, pixelGroup4 & 0xFFFF);
+	}
+
+	static inline void ditherCodebookSmooth(const CinepakCodebook &codebook, byte *dst, const byte *colorMap) {
+		int uLookup = (byte)codebook.u * 2;
+		int vLookup = (byte)codebook.v * 2;
+		uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup];
+		uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1];
+
+		int yLookup1 = codebook.y[0] * 2;
+		int yLookup2 = codebook.y[1] * 2;
+		int yLookup3 = codebook.y[2] * 2;
+		int yLookup4 = codebook.y[3] * 2;
+
+		uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1];
+		uint32 pixelGroup2 = uv1 | s_yLookup[yLookup2];
+		uint32 pixelGroup3 = uv1 | s_yLookup[yLookup1];
+		uint32 pixelGroup4 = uv2 | s_yLookup[yLookup2 + 1];
+		uint32 pixelGroup5 = uv2 | s_yLookup[yLookup3 + 1];
+		uint32 pixelGroup6 = uv1 | s_yLookup[yLookup3];
+		uint32 pixelGroup7 = uv1 | s_yLookup[yLookup4];
+		uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1];
+
+		dst[0] = getRGBLookupEntry(colorMap, pixelGroup1 & 0xFFFF);
+		dst[1] = getRGBLookupEntry(colorMap, pixelGroup1 >> 16);
+		dst[2] = getRGBLookupEntry(colorMap, pixelGroup2 & 0xFFFF);
+		dst[3] = getRGBLookupEntry(colorMap, pixelGroup2 >> 16);
+		dst[4] = getRGBLookupEntry(colorMap, pixelGroup3 & 0xFFFF);
+		dst[5] = getRGBLookupEntry(colorMap, pixelGroup3 >> 16);
+		dst[6] = getRGBLookupEntry(colorMap, pixelGroup4 & 0xFFFF);
+		dst[7] = getRGBLookupEntry(colorMap, pixelGroup4 >> 16);
+		dst[8] = getRGBLookupEntry(colorMap, pixelGroup5 >> 16);
+		dst[9] = getRGBLookupEntry(colorMap, pixelGroup6 & 0xFFFF);
+		dst[10] = getRGBLookupEntry(colorMap, pixelGroup7 >> 16);
+		dst[11] = getRGBLookupEntry(colorMap, pixelGroup8 & 0xFFFF);
+		dst[12] = getRGBLookupEntry(colorMap, pixelGroup6 >> 16);
+		dst[13] = getRGBLookupEntry(colorMap, pixelGroup5 & 0xFFFF);
+		dst[14] = getRGBLookupEntry(colorMap, pixelGroup8 >> 16);
+		dst[15] = getRGBLookupEntry(colorMap, pixelGroup7 & 0xFFFF);
+	}
+
+	static inline byte getRGBLookupEntry(const byte *colorMap, uint16 index) {
+		return colorMap[s_defaultPaletteLookup[CLIP<int>(index, 0, 1024)]];
+	}
+};
+
+template<typename PixelInt, typename CodebookConverter>
+void decodeVectorsTmpl(CinepakFrame &frame, const byte *clipTable, const byte *colorMap, Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
+	uint32 flag = 0, mask = 0;
+	PixelInt *iy[4];
+	int32 startPos = stream.pos();
+
+	for (uint16 y = frame.strips[strip].rect.top; y < frame.strips[strip].rect.bottom; y += 4) {
+		iy[0] = (PixelInt *)frame.surface->getBasePtr(frame.strips[strip].rect.left, + y);
+		iy[1] = iy[0] + frame.width;
+		iy[2] = iy[1] + frame.width;
+		iy[3] = iy[2] + frame.width;
+
+		for (uint16 x = frame.strips[strip].rect.left; x < frame.strips[strip].rect.right; x += 4) {
+			if ((chunkID & 0x01) && !(mask >>= 1)) {
+				if ((stream.pos() - startPos + 4) > (int32)chunkSize)
+					return;
+
+				flag  = stream.readUint32BE();
+				mask  = 0x80000000;
+			}
+
+			if (!(chunkID & 0x01) || (flag & mask)) {
+				if (!(chunkID & 0x02) && !(mask >>= 1)) {
+					if ((stream.pos() - startPos + 4) > (int32)chunkSize)
+						return;
+
+					flag  = stream.readUint32BE();
+					mask  = 0x80000000;
+				}
+
+				if ((chunkID & 0x02) || (~flag & mask)) {
+					if ((stream.pos() - startPos + 1) > (int32)chunkSize)
+						return;
+
+					// Get the codebook
+					byte codebook = stream.readByte();
+					CodebookConverter::decodeBlock1(codebook, frame.strips[strip], iy, clipTable, colorMap, frame.surface->format);
+				} else if (flag & mask) {
+					if ((stream.pos() - startPos + 4) > (int32)chunkSize)
+						return;
+
+					byte codebook[4];
+					for (byte i = 0; i < 4; i++)
+						codebook[i] = stream.readByte();
+					CodebookConverter::decodeBlock4(codebook, frame.strips[strip], iy, clipTable, colorMap, frame.surface->format);
+				}
+			}
+
+			for (byte i = 0; i < 4; i++)
+				iy[i] += 4;
+		}
+	}
+}
+
+} // End of anonymous namespace
 
 CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec(), _bitsPerPixel(bitsPerPixel) {
 	_curFrame.surface = 0;
@@ -230,93 +502,12 @@ void CinepakDecoder::loadCodebook(Common::SeekableReadStream &stream, uint16 str
 }
 
 void CinepakDecoder::decodeVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
-	uint32 flag = 0, mask = 0;
-	uint32 iy[4];
-	int32 startPos = stream.pos();
-
-	for (uint16 y = _curFrame.strips[strip].rect.top; y < _curFrame.strips[strip].rect.bottom; y += 4) {
-		iy[0] = _curFrame.strips[strip].rect.left + y * _curFrame.width;
-		iy[1] = iy[0] + _curFrame.width;
-		iy[2] = iy[1] + _curFrame.width;
-		iy[3] = iy[2] + _curFrame.width;
-
-		for (uint16 x = _curFrame.strips[strip].rect.left; x < _curFrame.strips[strip].rect.right; x += 4) {
-			if ((chunkID & 0x01) && !(mask >>= 1)) {
-				if ((stream.pos() - startPos + 4) > (int32)chunkSize)
-					return;
-
-				flag  = stream.readUint32BE();
-				mask  = 0x80000000;
-			}
-
-			if (!(chunkID & 0x01) || (flag & mask)) {
-				if (!(chunkID & 0x02) && !(mask >>= 1)) {
-					if ((stream.pos() - startPos + 4) > (int32)chunkSize)
-						return;
-
-					flag  = stream.readUint32BE();
-					mask  = 0x80000000;
-				}
-
-				if ((chunkID & 0x02) || (~flag & mask)) {
-					if ((stream.pos() - startPos + 1) > (int32)chunkSize)
-						return;
-
-					// Get the codebook
-					CinepakCodebook codebook = _curFrame.strips[strip].v1_codebook[stream.readByte()];
-
-					PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v);
-					PUT_PIXEL(iy[0] + 1, codebook.y[0], codebook.u, codebook.v);
-					PUT_PIXEL(iy[1] + 0, codebook.y[0], codebook.u, codebook.v);
-					PUT_PIXEL(iy[1] + 1, codebook.y[0], codebook.u, codebook.v);
-
-					PUT_PIXEL(iy[0] + 2, codebook.y[1], codebook.u, codebook.v);
-					PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v);
-					PUT_PIXEL(iy[1] + 2, codebook.y[1], codebook.u, codebook.v);
-					PUT_PIXEL(iy[1] + 3, codebook.y[1], codebook.u, codebook.v);
-
-					PUT_PIXEL(iy[2] + 0, codebook.y[2], codebook.u, codebook.v);
-					PUT_PIXEL(iy[2] + 1, codebook.y[2], codebook.u, codebook.v);
-					PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v);
-					PUT_PIXEL(iy[3] + 1, codebook.y[2], codebook.u, codebook.v);
-
-					PUT_PIXEL(iy[2] + 2, codebook.y[3], codebook.u, codebook.v);
-					PUT_PIXEL(iy[2] + 3, codebook.y[3], codebook.u, codebook.v);
-					PUT_PIXEL(iy[3] + 2, codebook.y[3], codebook.u, codebook.v);
-					PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v);
-				} else if (flag & mask) {
-					if ((stream.pos() - startPos + 4) > (int32)chunkSize)
-						return;
-
-					CinepakCodebook codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()];
-					PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v);
-					PUT_PIXEL(iy[0] + 1, codebook.y[1], codebook.u, codebook.v);
-					PUT_PIXEL(iy[1] + 0, codebook.y[2], codebook.u, codebook.v);
-					PUT_PIXEL(iy[1] + 1, codebook.y[3], codebook.u, codebook.v);
-
-					codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()];
-					PUT_PIXEL(iy[0] + 2, codebook.y[0], codebook.u, codebook.v);
-					PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v);
-					PUT_PIXEL(iy[1] + 2, codebook.y[2], codebook.u, codebook.v);
-					PUT_PIXEL(iy[1] + 3, codebook.y[3], codebook.u, codebook.v);
-
-					codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()];
-					PUT_PIXEL(iy[2] + 0, codebook.y[0], codebook.u, codebook.v);
-					PUT_PIXEL(iy[2] + 1, codebook.y[1], codebook.u, codebook.v);
-					PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v);
-					PUT_PIXEL(iy[3] + 1, codebook.y[3], codebook.u, codebook.v);
-
-					codebook = _curFrame.strips[strip].v4_codebook[stream.readByte()];
-					PUT_PIXEL(iy[2] + 2, codebook.y[0], codebook.u, codebook.v);
-					PUT_PIXEL(iy[2] + 3, codebook.y[1], codebook.u, codebook.v);
-					PUT_PIXEL(iy[3] + 2, codebook.y[2], codebook.u, codebook.v);
-					PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v);
-				}
-			}
-
-			for (byte i = 0; i < 4; i++)
-				iy[i] += 4;
-		}
+	if (_curFrame.surface->format.bytesPerPixel == 1) {
+		decodeVectorsTmpl<byte, CodebookConverterRaw>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
+	} else if (_curFrame.surface->format.bytesPerPixel == 2) {
+		decodeVectorsTmpl<uint16, CodebookConverterRaw>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
+	} else if (_curFrame.surface->format.bytesPerPixel == 4) {
+		decodeVectorsTmpl<uint32, CodebookConverterRaw>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
 	}
 }
 
@@ -376,171 +567,7 @@ byte CinepakDecoder::findNearestRGB(int index) const {
 }
 
 void CinepakDecoder::ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
-	uint32 flag = 0, mask = 0;
-	byte *iy[4];
-	int32 startPos = stream.pos();
-
-	for (uint16 y = _curFrame.strips[strip].rect.top; y < _curFrame.strips[strip].rect.bottom; y += 4) {
-		iy[0] = (byte *)_curFrame.surface->getPixels() + _curFrame.strips[strip].rect.left + y * _curFrame.width;
-		iy[1] = iy[0] + _curFrame.width;
-		iy[2] = iy[1] + _curFrame.width;
-		iy[3] = iy[2] + _curFrame.width;
-
-		for (uint16 x = _curFrame.strips[strip].rect.left; x < _curFrame.strips[strip].rect.right; x += 4) {
-			if ((chunkID & 0x01) && !(mask >>= 1)) {
-				if ((stream.pos() - startPos + 4) > (int32)chunkSize)
-					return;
-
-				flag  = stream.readUint32BE();
-				mask  = 0x80000000;
-			}
-
-			if (!(chunkID & 0x01) || (flag & mask)) {
-				if (!(chunkID & 0x02) && !(mask >>= 1)) {
-					if ((stream.pos() - startPos + 4) > (int32)chunkSize)
-						return;
-
-					flag  = stream.readUint32BE();
-					mask  = 0x80000000;
-				}
-
-				byte blockBuffer[16];
-
-				if ((chunkID & 0x02) || (~flag & mask)) {
-					if ((stream.pos() - startPos + 1) > (int32)chunkSize)
-						return;
-
-					ditherCodebookSmooth(_curFrame.strips[strip].v1_codebook[stream.readByte()], blockBuffer);
-					iy[0][0] = blockBuffer[0];
-					iy[0][1] = blockBuffer[1];
-					iy[0][2] = blockBuffer[2];
-					iy[0][3] = blockBuffer[3];
-					iy[1][0] = blockBuffer[4];
-					iy[1][1] = blockBuffer[5];
-					iy[1][2] = blockBuffer[6];
-					iy[1][3] = blockBuffer[7];
-					iy[2][0] = blockBuffer[8];
-					iy[2][1] = blockBuffer[9];
-					iy[2][2] = blockBuffer[10];
-					iy[2][3] = blockBuffer[11];
-					iy[3][0] = blockBuffer[12];
-					iy[3][1] = blockBuffer[13];
-					iy[3][2] = blockBuffer[14];
-					iy[3][3] = blockBuffer[15];
-				} else if (flag & mask) {
-					if ((stream.pos() - startPos + 4) > (int32)chunkSize)
-						return;
-
-					ditherCodebookDetail(_curFrame.strips[strip].v4_codebook[stream.readByte()], blockBuffer);
-					iy[0][0] = blockBuffer[0];
-					iy[0][1] = blockBuffer[1];
-					iy[1][0] = blockBuffer[4];
-					iy[1][1] = blockBuffer[5];
-
-					ditherCodebookDetail(_curFrame.strips[strip].v4_codebook[stream.readByte()], blockBuffer);
-					iy[0][2] = blockBuffer[2];
-					iy[0][3] = blockBuffer[3];
-					iy[1][2] = blockBuffer[6];
-					iy[1][3] = blockBuffer[7];
-
-					ditherCodebookDetail(_curFrame.strips[strip].v4_codebook[stream.readByte()], blockBuffer);
-					iy[2][0] = blockBuffer[8];
-					iy[2][1] = blockBuffer[9];
-					iy[3][0] = blockBuffer[12];
-					iy[3][1] = blockBuffer[13];
-
-					ditherCodebookDetail(_curFrame.strips[strip].v4_codebook[stream.readByte()], blockBuffer);
-					iy[2][2] = blockBuffer[10];
-					iy[2][3] = blockBuffer[11];
-					iy[3][2] = blockBuffer[14];
-					iy[3][3] = blockBuffer[15];
-				}
-			}
-
-			for (byte i = 0; i < 4; i++)
-				iy[i] += 4;
-		}
-	}
-}
-
-void CinepakDecoder::ditherCodebookDetail(const CinepakCodebook &codebook, byte *dst) const {
-	int uLookup = (byte)codebook.u * 2;
-	int vLookup = (byte)codebook.v * 2;
-	uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup];
-	uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1];
-
-	int yLookup1 = codebook.y[0] * 2;
-	int yLookup2 = codebook.y[1] * 2;
-	int yLookup3 = codebook.y[2] * 2;
-	int yLookup4 = codebook.y[3] * 2;
-
-	uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1];
-	uint32 pixelGroup2 = uv2 | s_yLookup[yLookup2 + 1];
-	uint32 pixelGroup3 = uv1 | s_yLookup[yLookup3];
-	uint32 pixelGroup4 = uv1 | s_yLookup[yLookup4];
-	uint32 pixelGroup5 = uv1 | s_yLookup[yLookup1];
-	uint32 pixelGroup6 = uv1 | s_yLookup[yLookup2];
-	uint32 pixelGroup7 = uv2 | s_yLookup[yLookup3 + 1];
-	uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1];
-
-	dst[0] = getRGBLookupEntry(pixelGroup1 & 0xFFFF);
-	dst[1] = getRGBLookupEntry(pixelGroup2 >> 16);
-	dst[2] = getRGBLookupEntry(pixelGroup5 & 0xFFFF);
-	dst[3] = getRGBLookupEntry(pixelGroup6 >> 16);
-	dst[4] = getRGBLookupEntry(pixelGroup3 & 0xFFFF);
-	dst[5] = getRGBLookupEntry(pixelGroup4 >> 16);
-	dst[6] = getRGBLookupEntry(pixelGroup7 & 0xFFFF);
-	dst[7] = getRGBLookupEntry(pixelGroup8 >> 16);
-	dst[8] = getRGBLookupEntry(pixelGroup1 >> 16);
-	dst[9] = getRGBLookupEntry(pixelGroup6 & 0xFFFF);
-	dst[10] = getRGBLookupEntry(pixelGroup5 >> 16);
-	dst[11] = getRGBLookupEntry(pixelGroup2 & 0xFFFF);
-	dst[12] = getRGBLookupEntry(pixelGroup3 >> 16);
-	dst[13] = getRGBLookupEntry(pixelGroup8 & 0xFFFF);
-	dst[14] = getRGBLookupEntry(pixelGroup7 >> 16);
-	dst[15] = getRGBLookupEntry(pixelGroup4 & 0xFFFF);
-}
-
-void CinepakDecoder::ditherCodebookSmooth(const CinepakCodebook &codebook, byte *dst) const {
-	int uLookup = (byte)codebook.u * 2;
-	int vLookup = (byte)codebook.v * 2;
-	uint32 uv1 = s_uLookup[uLookup] | s_vLookup[vLookup];
-	uint32 uv2 = s_uLookup[uLookup + 1] | s_vLookup[vLookup + 1];
-
-	int yLookup1 = codebook.y[0] * 2;
-	int yLookup2 = codebook.y[1] * 2;
-	int yLookup3 = codebook.y[2] * 2;
-	int yLookup4 = codebook.y[3] * 2;
-
-	uint32 pixelGroup1 = uv2 | s_yLookup[yLookup1 + 1];
-	uint32 pixelGroup2 = uv1 | s_yLookup[yLookup2];
-	uint32 pixelGroup3 = uv1 | s_yLookup[yLookup1];
-	uint32 pixelGroup4 = uv2 | s_yLookup[yLookup2 + 1];
-	uint32 pixelGroup5 = uv2 | s_yLookup[yLookup3 + 1];
-	uint32 pixelGroup6 = uv1 | s_yLookup[yLookup3];
-	uint32 pixelGroup7 = uv1 | s_yLookup[yLookup4];
-	uint32 pixelGroup8 = uv2 | s_yLookup[yLookup4 + 1];
-
-	dst[0] = getRGBLookupEntry(pixelGroup1 & 0xFFFF);
-	dst[1] = getRGBLookupEntry(pixelGroup1 >> 16);
-	dst[2] = getRGBLookupEntry(pixelGroup2 & 0xFFFF);
-	dst[3] = getRGBLookupEntry(pixelGroup2 >> 16);
-	dst[4] = getRGBLookupEntry(pixelGroup3 & 0xFFFF);
-	dst[5] = getRGBLookupEntry(pixelGroup3 >> 16);
-	dst[6] = getRGBLookupEntry(pixelGroup4 & 0xFFFF);
-	dst[7] = getRGBLookupEntry(pixelGroup4 >> 16);
-	dst[8] = getRGBLookupEntry(pixelGroup5 >> 16);
-	dst[9] = getRGBLookupEntry(pixelGroup6 & 0xFFFF);
-	dst[10] = getRGBLookupEntry(pixelGroup7 >> 16);
-	dst[11] = getRGBLookupEntry(pixelGroup8 & 0xFFFF);
-	dst[12] = getRGBLookupEntry(pixelGroup6 >> 16);
-	dst[13] = getRGBLookupEntry(pixelGroup5 & 0xFFFF);
-	dst[14] = getRGBLookupEntry(pixelGroup8 >> 16);
-	dst[15] = getRGBLookupEntry(pixelGroup7 & 0xFFFF);
-}
-
-byte CinepakDecoder::getRGBLookupEntry(uint16 index) const {
-	return _colorMap[s_defaultPaletteLookup[CLIP<int>(index, 0, 1024)]];
+	decodeVectorsTmpl<byte, CodebookConverterDitherVFW>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
 }
 
 } // End of namespace Image
diff --git a/image/codecs/cinepak.h b/image/codecs/cinepak.h
index ccdad08..02c09b6 100644
--- a/image/codecs/cinepak.h
+++ b/image/codecs/cinepak.h
@@ -95,9 +95,6 @@ private:
 
 	byte findNearestRGB(int index) const;
 	void ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
-	void ditherCodebookDetail(const CinepakCodebook &codebook, byte *dst) const;
-	void ditherCodebookSmooth(const CinepakCodebook &codebook, byte *dst) const;
-	byte getRGBLookupEntry(uint16 index) const;
 };
 
 } // End of namespace Image


Commit: 0ceb383cd3f24bde6e9855f3b86f161eae3fb14b
    https://github.com/scummvm/scummvm/commit/0ceb383cd3f24bde6e9855f3b86f161eae3fb14b
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:36:46-04:00

Commit Message:
IMAGE: Add a function for generating QuickTime dither tables

Much thanks to fuzzie for her assistance.

Changed paths:
    image/codecs/codec.cpp
    image/codecs/codec.h



diff --git a/image/codecs/codec.cpp b/image/codecs/codec.cpp
index 6b0c7eb..398e9c5 100644
--- a/image/codecs/codec.cpp
+++ b/image/codecs/codec.cpp
@@ -20,6 +20,7 @@
  *
  */
 
+#include "common/list.h"
 #include "common/scummsys.h"
 
 #include "image/codecs/codec.h"
@@ -44,6 +45,153 @@
 
 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];
+	memset(buf, 0, 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, int width, int height, int bitsPerPixel) {
 	switch (tag) {
 	case SWAP_CONSTANT_32(0):
diff --git a/image/codecs/codec.h b/image/codecs/codec.h
index 9abbb3d..ad7e437 100644
--- a/image/codecs/codec.h
+++ b/image/codecs/codec.h
@@ -107,6 +107,12 @@ public:
 	 * Activate dithering mode with a palette
 	 */
 	virtual void setDither(DitherType type, const byte *palette) {}
+
+protected:
+	/**
+	 * Create a dither table, as used by QuickTime codecs.
+	 */
+	static byte *createQuickTimeDitherTable(const byte *palette, uint colorCount);
 };
 
 /**


Commit: c402d9a9594e5a2b38f834578b010d5cf9bb771e
    https://github.com/scummvm/scummvm/commit/c402d9a9594e5a2b38f834578b010d5cf9bb771e
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:36:54-04:00

Commit Message:
IMAGE: Add an unknown dither type

Changed paths:
    image/codecs/codec.h



diff --git a/image/codecs/codec.h b/image/codecs/codec.h
index ad7e437..8845754 100644
--- a/image/codecs/codec.h
+++ b/image/codecs/codec.h
@@ -62,6 +62,9 @@ public:
 	 * A type of dithering.
 	 */
 	enum DitherType {
+		/** Unknown */
+		kDitherTypeUnknown,
+
 		/** Video for Windows dithering */
 		kDitherTypeVFW,
 


Commit: 1271fbde8f30fdf2d0664bb1e3382928380f9aa9
    https://github.com/scummvm/scummvm/commit/1271fbde8f30fdf2d0664bb1e3382928380f9aa9
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:36:58-04:00

Commit Message:
IMAGE: Add support for QuickTime dithered Cinepak

Changed paths:
    image/codecs/cinepak.cpp
    image/codecs/cinepak.h



diff --git a/image/codecs/cinepak.cpp b/image/codecs/cinepak.cpp
index f43c952..32f6be2 100644
--- a/image/codecs/cinepak.cpp
+++ b/image/codecs/cinepak.cpp
@@ -264,6 +264,37 @@ private:
 	}
 };
 
+/**
+ * Codebook converter that dithers in QT-style
+ */
+struct CodebookConverterDitherQT {
+	static inline void decodeBlock1(byte codebookIndex, const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+		const byte *colorPtr = strip.v1_dither + (codebookIndex << 2);
+		WRITE_UINT32(rows[0], READ_UINT32(colorPtr));
+		WRITE_UINT32(rows[1], READ_UINT32(colorPtr + 1024));
+		WRITE_UINT32(rows[2], READ_UINT32(colorPtr + 2048));
+		WRITE_UINT32(rows[3], READ_UINT32(colorPtr + 3072));
+	}
+
+	static inline void decodeBlock4(const byte (&codebookIndex)[4], const CinepakStrip &strip, byte *(&rows)[4], const byte *clipTable, const byte *colorMap, const Graphics::PixelFormat &format) {
+		const byte *colorPtr = strip.v4_dither + (codebookIndex[0] << 2);
+		WRITE_UINT16(rows[0] + 0, READ_UINT16(colorPtr + 0));
+		WRITE_UINT16(rows[1] + 0, READ_UINT16(colorPtr + 2));
+
+		colorPtr = strip.v4_dither + (codebookIndex[1] << 2);
+		WRITE_UINT16(rows[0] + 2, READ_UINT16(colorPtr + 1024));
+		WRITE_UINT16(rows[1] + 2, READ_UINT16(colorPtr + 1026));
+
+		colorPtr = strip.v4_dither + (codebookIndex[2] << 2);
+		WRITE_UINT16(rows[2] + 0, READ_UINT16(colorPtr + 2048));
+		WRITE_UINT16(rows[3] + 0, READ_UINT16(colorPtr + 2050));
+
+		colorPtr = strip.v4_dither + (codebookIndex[3] << 2);
+		WRITE_UINT16(rows[2] + 2, READ_UINT16(colorPtr + 3072));
+		WRITE_UINT16(rows[3] + 2, READ_UINT16(colorPtr + 3074));
+	}
+};
+
 template<typename PixelInt, typename CodebookConverter>
 void decodeVectorsTmpl(CinepakFrame &frame, const byte *clipTable, const byte *colorMap, Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
 	uint32 flag = 0, mask = 0;
@@ -306,8 +337,7 @@ void decodeVectorsTmpl(CinepakFrame &frame, const byte *clipTable, const byte *c
 						return;
 
 					byte codebook[4];
-					for (byte i = 0; i < 4; i++)
-						codebook[i] = stream.readByte();
+					stream.read(codebook, 4);
 					CodebookConverter::decodeBlock4(codebook, frame.strips[strip], iy, clipTable, colorMap, frame.surface->format);
 				}
 			}
@@ -326,6 +356,7 @@ CinepakDecoder::CinepakDecoder(int bitsPerPixel) : Codec(), _bitsPerPixel(bitsPe
 	_y = 0;
 	_colorMap = 0;
 	_ditherPalette = 0;
+	_ditherType = kDitherTypeUnknown;
 
 	if (bitsPerPixel == 8) {
 		_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
@@ -398,9 +429,12 @@ const Graphics::Surface *CinepakDecoder::decodeFrame(Common::SeekableReadStream
 
 	for (uint16 i = 0; i < _curFrame.stripCount; i++) {
 		if (i > 0 && !(_curFrame.flags & 1)) { // Use codebooks from last strip
+
 			for (uint16 j = 0; j < 256; j++) {
 				_curFrame.strips[i].v1_codebook[j] = _curFrame.strips[i - 1].v1_codebook[j];
 				_curFrame.strips[i].v4_codebook[j] = _curFrame.strips[i - 1].v4_codebook[j];
+				memcpy(_curFrame.strips[i].v1_dither, _curFrame.strips[i - 1].v1_dither, 256 * 4 * 4 * 4);
+				memcpy(_curFrame.strips[i].v4_dither, _curFrame.strips[i - 1].v4_dither, 256 * 4 * 4 * 4);
 			}
 		}
 
@@ -484,8 +518,7 @@ void CinepakDecoder::loadCodebook(Common::SeekableReadStream &stream, uint16 str
 			if ((stream.pos() - startPos + n) > (int32)chunkSize)
 				break;
 
-			for (byte j = 0; j < 4; j++)
-				codebook[i].y[j] = stream.readByte();
+			stream.read(codebook[i].y, 4);
 
 			if (n == 6) {
 				codebook[i].u = stream.readSByte();
@@ -497,10 +530,72 @@ void CinepakDecoder::loadCodebook(Common::SeekableReadStream &stream, uint16 str
 				codebook[i].u = 0;
 				codebook[i].v = 0;
 			}
+
+			// Dither the codebook if we're dithering for QuickTime
+			if (_ditherType == kDitherTypeQT)
+				ditherCodebookQT(strip, codebookType, i);
 		}
 	}
 }
 
+void CinepakDecoder::ditherCodebookQT(uint16 strip, byte codebookType, uint16 codebookIndex) {
+	if (codebookType == 1) {
+		const CinepakCodebook &codebook = _curFrame.strips[strip].v1_codebook[codebookIndex];
+		byte *output = _curFrame.strips[strip].v1_dither + (codebookIndex << 2);
+
+		byte *ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[0], codebook.u, codebook.v);
+		output[0x000] = ditherEntry[0x0000];
+		output[0x001] = ditherEntry[0x4000];
+		output[0x400] = ditherEntry[0xC000];
+		output[0x401] = ditherEntry[0x0000];
+
+		ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[1], codebook.u, codebook.v);
+		output[0x002] = ditherEntry[0x8000];
+		output[0x003] = ditherEntry[0xC000];
+		output[0x402] = ditherEntry[0x4000];
+		output[0x403] = ditherEntry[0x8000];
+
+		ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[2], codebook.u, codebook.v);
+		output[0x800] = ditherEntry[0x4000];
+		output[0x801] = ditherEntry[0x8000];
+		output[0xC00] = ditherEntry[0x8000];
+		output[0xC01] = ditherEntry[0xC000];
+
+		ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[3], codebook.u, codebook.v);
+		output[0x802] = ditherEntry[0xC000];
+		output[0x803] = ditherEntry[0x0000];
+		output[0xC02] = ditherEntry[0x0000];
+		output[0xC03] = ditherEntry[0x4000];
+	} else {
+		const CinepakCodebook &codebook = _curFrame.strips[strip].v4_codebook[codebookIndex];
+		byte *output = _curFrame.strips[strip].v4_dither + (codebookIndex << 2);
+
+		byte *ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[0], codebook.u, codebook.v);
+		output[0x000] = ditherEntry[0x0000];
+		output[0x400] = ditherEntry[0x8000];
+		output[0x800] = ditherEntry[0x4000];
+		output[0xC00] = ditherEntry[0xC000];
+
+		ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[1], codebook.u, codebook.v);
+		output[0x001] = ditherEntry[0x4000];
+		output[0x401] = ditherEntry[0xC000];
+		output[0x801] = ditherEntry[0x8000];
+		output[0xC01] = ditherEntry[0x0000];
+
+		ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[2], codebook.u, codebook.v);
+		output[0x002] = ditherEntry[0xC000];
+		output[0x402] = ditherEntry[0x4000];
+		output[0x802] = ditherEntry[0x8000];
+		output[0xC02] = ditherEntry[0x0000];
+
+		ditherEntry = _colorMap + createDitherTableIndex(_clipTable, codebook.y[3], codebook.u, codebook.v);
+		output[0x003] = ditherEntry[0x0000];
+		output[0x403] = ditherEntry[0x8000];
+		output[0x803] = ditherEntry[0xC000];
+		output[0xC03] = ditherEntry[0x4000];
+	}
+}
+
 void CinepakDecoder::decodeVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
 	if (_curFrame.surface->format.bytesPerPixel == 1) {
 		decodeVectorsTmpl<byte, CodebookConverterRaw>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
@@ -512,7 +607,7 @@ void CinepakDecoder::decodeVectors(Common::SeekableReadStream &stream, uint16 st
 }
 
 bool CinepakDecoder::canDither(DitherType type) const {
-	return type == kDitherTypeVFW && _bitsPerPixel == 24;
+	return (type == kDitherTypeVFW || type == kDitherTypeQT) && _bitsPerPixel == 24;
 }
 
 void CinepakDecoder::setDither(DitherType type, const byte *palette) {
@@ -526,10 +621,18 @@ void CinepakDecoder::setDither(DitherType type, const byte *palette) {
 
 	_dirtyPalette = true;
 	_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
-	_colorMap = new byte[221];
+	_ditherType = type;
+
+	if (type == kDitherTypeVFW) {
+		_colorMap = new byte[221];
 
-	for (int i = 0; i < 221; i++)
-		_colorMap[i] = findNearestRGB(i);
+		for (int i = 0; i < 221; i++)
+			_colorMap[i] = findNearestRGB(i);
+	} else {
+		// Generate QuickTime dither table
+		// 4 blocks of 0x4000 bytes (RGB554 lookup)
+		_colorMap = createQuickTimeDitherTable(palette, 256);
+	}
 }
 
 byte CinepakDecoder::findNearestRGB(int index) const {
@@ -567,7 +670,10 @@ byte CinepakDecoder::findNearestRGB(int index) const {
 }
 
 void CinepakDecoder::ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize) {
-	decodeVectorsTmpl<byte, CodebookConverterDitherVFW>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
+	if (_ditherType == kDitherTypeVFW)
+		decodeVectorsTmpl<byte, CodebookConverterDitherVFW>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
+	else
+		decodeVectorsTmpl<byte, CodebookConverterDitherQT>(_curFrame, _clipTable, _colorMap, stream, strip, chunkID, chunkSize);
 }
 
 } // End of namespace Image
diff --git a/image/codecs/cinepak.h b/image/codecs/cinepak.h
index 02c09b6..dc8172e 100644
--- a/image/codecs/cinepak.h
+++ b/image/codecs/cinepak.h
@@ -46,6 +46,7 @@ struct CinepakStrip {
 	uint16 length;
 	Common::Rect rect;
 	CinepakCodebook v1_codebook[256], v4_codebook[256];
+	byte v1_dither[256 * 4 * 4 * 4], v4_dither[256 * 4 * 4 * 4];
 };
 
 struct CinepakFrame {
@@ -89,12 +90,14 @@ private:
 	bool _dirtyPalette;
 	byte *_rgbLookup;
 	byte *_colorMap;
+	DitherType _ditherType;
 
 	void loadCodebook(Common::SeekableReadStream &stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize);
 	void decodeVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
 
 	byte findNearestRGB(int index) const;
 	void ditherVectors(Common::SeekableReadStream &stream, uint16 strip, byte chunkID, uint32 chunkSize);
+	void ditherCodebookQT(uint16 strip, byte codebookType, uint16 codebookIndex);
 };
 
 } // End of namespace Image


Commit: b170b0882237a4c8b8119d2875bf2e05c03168e5
    https://github.com/scummvm/scummvm/commit/b170b0882237a4c8b8119d2875bf2e05c03168e5
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:37:04-04:00

Commit Message:
VIDEO: Add support for dithering in QuickTime videos

Changed paths:
    video/qt_decoder.cpp
    video/qt_decoder.h



diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp
index 9b77ef7..cab7372 100644
--- a/video/qt_decoder.cpp
+++ b/video/qt_decoder.cpp
@@ -751,4 +751,27 @@ bool QuickTimeDecoder::VideoTrackHandler::endOfCurEdit() const {
 	return getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration();
 }
 
+bool QuickTimeDecoder::VideoTrackHandler::canDither() const {
+	for (uint i = 0; i < _parent->sampleDescs.size(); i++) {
+		VideoSampleDesc *desc = (VideoSampleDesc *)_parent->sampleDescs[i];
+
+		if (!desc || !desc->_videoCodec)
+			return false;
+
+		if (!desc->_videoCodec->canDither(Image::Codec::kDitherTypeQT))
+			return false;
+	}
+
+	return true;
+}
+
+void QuickTimeDecoder::VideoTrackHandler::setDither(const byte *palette) {
+	assert(canDither());
+
+	for (uint i = 0; i < _parent->sampleDescs.size(); i++) {
+		VideoSampleDesc *desc = (VideoSampleDesc *)_parent->sampleDescs[i];
+		desc->_videoCodec->setDither(Image::Codec::kDitherTypeQT, palette);
+	}
+}
+
 } // End of namespace Video
diff --git a/video/qt_decoder.h b/video/qt_decoder.h
index 99ac9ff..99980a3 100644
--- a/video/qt_decoder.h
+++ b/video/qt_decoder.h
@@ -140,6 +140,8 @@ private:
 		bool hasDirtyPalette() const { return _curPalette; }
 		bool setReverse(bool reverse);
 		bool isReversed() const { return _reversed; }
+		bool canDither() const;
+		void setDither(const byte *palette);
 
 		Common::Rational getScaledWidth() const;
 		Common::Rational getScaledHeight() const;


Commit: 7f12db95c6c7689f7f7f8905aaf122db6de13e4f
    https://github.com/scummvm/scummvm/commit/7f12db95c6c7689f7f7f8905aaf122db6de13e4f
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:37:09-04:00

Commit Message:
IMAGE: Implement QuickTime dithering in rpza

Changed paths:
    image/codecs/rpza.cpp
    image/codecs/rpza.h



diff --git a/image/codecs/rpza.cpp b/image/codecs/rpza.cpp
index 5aeee7c..8d648e1 100644
--- a/image/codecs/rpza.cpp
+++ b/image/codecs/rpza.cpp
@@ -32,43 +32,185 @@
 namespace Image {
 
 RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec() {
-	// We need to increase the surface size to a multiple of 4
-	uint16 wMod = width % 4;
-	if (wMod != 0)
-		width += 4 - wMod;
-
-	_surface = new Graphics::Surface();
-	_surface->create(width, height, getPixelFormat());
+	_format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
+	_ditherPalette = 0;
+	_dirtyPalette = false;
+	_colorMap = 0;
+	_width = width;
+	_height = height;
+	_blockWidth = (width + 3) / 4;
+	_blockHeight = (height + 3) / 4;
+	_surface = 0;
 }
 
 RPZADecoder::~RPZADecoder() {
-	_surface->free();
-	delete _surface;
+	if (_surface) {
+		_surface->free();
+		delete _surface;
+	}
+
+	delete[] _ditherPalette;
 }
 
 #define ADVANCE_BLOCK() \
-	pixelPtr += 4; \
-	if (pixelPtr >= _surface->w) { \
-		pixelPtr = 0; \
-		rowPtr += _surface->w * 4; \
+	blockPtr += 4; \
+	if (blockPtr >= endPtr) { \
+		blockPtr += pitch * 3; \
+		endPtr = blockPtr + pitch; \
 	} \
 	totalBlocks--; \
 	if (totalBlocks < 0) \
 		error("rpza block counter just went negative (this should not happen)") \
 
-#define PUT_PIXEL(color) \
-	if ((int32)blockPtr < _surface->w * _surface->h) \
-		WRITE_UINT16((uint16 *)_surface->getPixels() + blockPtr, color); \
-	blockPtr++
+struct BlockDecoderRaw {
+	static inline void drawFillBlock(uint16 *blockPtr, uint16 pitch, uint16 color, const byte *colorMap) {
+		blockPtr[0] = color;
+		blockPtr[1] = color;
+		blockPtr[2] = color;
+		blockPtr[3] = color;
+		blockPtr += pitch;
+		blockPtr[0] = color;
+		blockPtr[1] = color;
+		blockPtr[2] = color;
+		blockPtr[3] = color;
+		blockPtr += pitch;
+		blockPtr[0] = color;
+		blockPtr[1] = color;
+		blockPtr[2] = color;
+		blockPtr[3] = color;
+		blockPtr += pitch;
+		blockPtr[0] = color;
+		blockPtr[1] = color;
+		blockPtr[2] = color;
+		blockPtr[3] = color;
+	}
 
-const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &stream) {
+	static inline void drawRawBlock(uint16 *blockPtr, uint16 pitch, const uint16 (&colors)[16], const byte *colorMap) {
+		blockPtr[0] = colors[0];
+		blockPtr[1] = colors[1];
+		blockPtr[2] = colors[2];
+		blockPtr[3] = colors[3];
+		blockPtr += pitch;
+		blockPtr[0] = colors[4];
+		blockPtr[1] = colors[5];
+		blockPtr[2] = colors[6];
+		blockPtr[3] = colors[7];
+		blockPtr += pitch;
+		blockPtr[0] = colors[8];
+		blockPtr[1] = colors[9];
+		blockPtr[2] = colors[10];
+		blockPtr[3] = colors[11];
+		blockPtr += pitch;
+		blockPtr[0] = colors[12];
+		blockPtr[1] = colors[13];
+		blockPtr[2] = colors[14];
+		blockPtr[3] = colors[15];
+	}
+
+	static inline void drawBlendBlock(uint16 *blockPtr, uint16 pitch, const uint16 (&colors)[4], const byte (&indexes)[4], const byte *colorMap) {
+		blockPtr[0] = colors[(indexes[0] >> 6) & 0x03];
+		blockPtr[1] = colors[(indexes[0] >> 4) & 0x03];
+		blockPtr[2] = colors[(indexes[0] >> 2) & 0x03];
+		blockPtr[3] = colors[(indexes[0] >> 0) & 0x03];
+		blockPtr += pitch;
+		blockPtr[0] = colors[(indexes[1] >> 6) & 0x03];
+		blockPtr[1] = colors[(indexes[1] >> 4) & 0x03];
+		blockPtr[2] = colors[(indexes[1] >> 2) & 0x03];
+		blockPtr[3] = colors[(indexes[1] >> 0) & 0x03];
+		blockPtr += pitch;
+		blockPtr[0] = colors[(indexes[2] >> 6) & 0x03];
+		blockPtr[1] = colors[(indexes[2] >> 4) & 0x03];
+		blockPtr[2] = colors[(indexes[2] >> 2) & 0x03];
+		blockPtr[3] = colors[(indexes[2] >> 0) & 0x03];
+		blockPtr += pitch;
+		blockPtr[0] = colors[(indexes[3] >> 6) & 0x03];
+		blockPtr[1] = colors[(indexes[3] >> 4) & 0x03];
+		blockPtr[2] = colors[(indexes[3] >> 2) & 0x03];
+		blockPtr[3] = colors[(indexes[3] >> 0) & 0x03];
+	}
+};
+
+struct BlockDecoderDither {
+	static inline void drawFillBlock(byte *blockPtr, uint16 pitch, uint16 color, const byte *colorMap) {
+		const byte *mapOffset = colorMap + (color >> 1);
+		byte pixel1 = mapOffset[0x0000];
+		byte pixel2 = mapOffset[0x4000];
+		byte pixel3 = mapOffset[0x8000];
+		byte pixel4 = mapOffset[0xC000];
+
+		blockPtr[0] = pixel1;
+		blockPtr[1] = pixel2;
+		blockPtr[2] = pixel3;
+		blockPtr[3] = pixel4;
+		blockPtr += pitch;
+		blockPtr[0] = pixel4;
+		blockPtr[1] = pixel1;
+		blockPtr[2] = pixel2;
+		blockPtr[3] = pixel3;
+		blockPtr += pitch;
+		blockPtr[0] = pixel2;
+		blockPtr[1] = pixel3;
+		blockPtr[2] = pixel4;
+		blockPtr[3] = pixel1;
+		blockPtr += pitch;
+		blockPtr[0] = pixel3;
+		blockPtr[1] = pixel4;
+		blockPtr[2] = pixel1;
+		blockPtr[3] = pixel2;
+	}
+
+	static inline void drawRawBlock(byte *blockPtr, uint16 pitch, const uint16 (&colors)[16], const byte *colorMap) {
+		blockPtr[0] = colorMap[(colors[0] >> 1) + 0x0000];
+		blockPtr[1] = colorMap[(colors[1] >> 1) + 0x4000];
+		blockPtr[2] = colorMap[(colors[2] >> 1) + 0x8000];
+		blockPtr[3] = colorMap[(colors[3] >> 1) + 0xC000];
+		blockPtr += pitch;
+		blockPtr[0] = colorMap[(colors[4] >> 1) + 0xC000];
+		blockPtr[1] = colorMap[(colors[5] >> 1) + 0x0000];
+		blockPtr[2] = colorMap[(colors[6] >> 1) + 0x4000];
+		blockPtr[3] = colorMap[(colors[7] >> 1) + 0x8000];
+		blockPtr += pitch;
+		blockPtr[0] = colorMap[(colors[8] >> 1) + 0x4000];
+		blockPtr[1] = colorMap[(colors[9] >> 1) + 0x8000];
+		blockPtr[2] = colorMap[(colors[10] >> 1) + 0xC000];
+		blockPtr[3] = colorMap[(colors[11] >> 1) + 0x0000];
+		blockPtr += pitch;
+		blockPtr[0] = colorMap[(colors[12] >> 1) + 0x8000];
+		blockPtr[1] = colorMap[(colors[13] >> 1) + 0xC000];
+		blockPtr[2] = colorMap[(colors[14] >> 1) + 0x0000];
+		blockPtr[3] = colorMap[(colors[15] >> 1) + 0x4000];
+	}
+
+	static inline void drawBlendBlock(byte *blockPtr, uint16 pitch, const uint16 (&colors)[4], const byte (&indexes)[4], const byte *colorMap) {
+		blockPtr[0] = colorMap[(colors[(indexes[0] >> 6) & 0x03] >> 1) + 0x0000];
+		blockPtr[1] = colorMap[(colors[(indexes[0] >> 4) & 0x03] >> 1) + 0x4000];
+		blockPtr[2] = colorMap[(colors[(indexes[0] >> 2) & 0x03] >> 1) + 0x8000];
+		blockPtr[3] = colorMap[(colors[(indexes[0] >> 0) & 0x03] >> 1) + 0xC000];
+		blockPtr += pitch;
+		blockPtr[0] = colorMap[(colors[(indexes[1] >> 6) & 0x03] >> 1) + 0xC000];
+		blockPtr[1] = colorMap[(colors[(indexes[1] >> 4) & 0x03] >> 1) + 0x0000];
+		blockPtr[2] = colorMap[(colors[(indexes[1] >> 2) & 0x03] >> 1) + 0x4000];
+		blockPtr[3] = colorMap[(colors[(indexes[1] >> 0) & 0x03] >> 1) + 0x8000];
+		blockPtr += pitch;
+		blockPtr[0] = colorMap[(colors[(indexes[2] >> 6) & 0x03] >> 1) + 0x4000];
+		blockPtr[1] = colorMap[(colors[(indexes[2] >> 4) & 0x03] >> 1) + 0x8000];
+		blockPtr[2] = colorMap[(colors[(indexes[2] >> 2) & 0x03] >> 1) + 0xC000];
+		blockPtr[3] = colorMap[(colors[(indexes[2] >> 0) & 0x03] >> 1) + 0x0000];
+		blockPtr += pitch;
+		blockPtr[0] = colorMap[(colors[(indexes[3] >> 6) & 0x03] >> 1) + 0x8000];
+		blockPtr[1] = colorMap[(colors[(indexes[3] >> 4) & 0x03] >> 1) + 0xC000];
+		blockPtr[2] = colorMap[(colors[(indexes[3] >> 2) & 0x03] >> 1) + 0x0000];
+		blockPtr[3] = colorMap[(colors[(indexes[3] >> 0) & 0x03] >> 1) + 0x4000];
+	}
+};
+
+template<typename PixelInt, typename BlockDecoder>
+static inline void decodeFrameTmpl(Common::SeekableReadStream &stream, PixelInt *ptr, uint16 pitch, uint16 blockWidth, uint16 blockHeight, const byte *colorMap) {
 	uint16 colorA = 0, colorB = 0;
 	uint16 color4[4];
 
-	uint32 rowPtr = 0;
-	uint32 pixelPtr = 0;
-	uint32 blockPtr = 0;
-	uint32 rowInc = _surface->w - 4;
+	PixelInt *blockPtr = ptr;
+	PixelInt *endPtr = ptr + pitch;
 	uint16 ta;
 	uint16 tb;
 
@@ -88,7 +230,7 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
 	}
 
 	// Number of 4x4 blocks in frame
-	int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4);
+	int32 totalBlocks = blockWidth * blockHeight;
 
 	// Process chunk data
 	while ((uint32)stream.pos() < chunkSize) {
@@ -117,14 +259,9 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
 			break;
 		case 0xa0: // Fill blocks with one color
 			colorA = stream.readUint16BE();
+
 			while (numBlocks--) {
-				blockPtr = rowPtr + pixelPtr;
-				for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
-					for (byte pixel_x = 0; pixel_x < 4; pixel_x++) {
-						PUT_PIXEL(colorA);
-					}
-					blockPtr += rowInc;
-				}
+				BlockDecoder::drawFillBlock(blockPtr, pitch, colorA, colorMap);
 				ADVANCE_BLOCK();
 			}
 			break;
@@ -136,10 +273,10 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
 			colorB = stream.readUint16BE();
 
 			// Sort out the colors
-			color4[0] = colorB;
+			color4[0] = colorB & 0x7FFF;
 			color4[1] = 0;
 			color4[2] = 0;
-			color4[3] = colorA;
+			color4[3] = colorA & 0x7FFF;
 
 			// Red components
 			ta = (colorA >> 10) & 0x1F;
@@ -160,42 +297,69 @@ const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &st
 			color4[2] |= ((21 * ta + 11 * tb) >> 5);
 
 			while (numBlocks--) {
-				blockPtr = rowPtr + pixelPtr;
-				for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
-					byte index = stream.readByte();
-					for (byte pixel_x = 0; pixel_x < 4; pixel_x++) {
-						byte idx = (index >> (2 * (3 - pixel_x))) & 0x03;
-						PUT_PIXEL(color4[idx]);
-					}
-					blockPtr += rowInc;
-				}
+				byte indexes[4];
+				stream.read(indexes, 4);
+
+				BlockDecoder::drawBlendBlock(blockPtr, pitch, color4, indexes, colorMap);
 				ADVANCE_BLOCK();
 			}
 			break;
 
 		// Fill block with 16 colors
-		case 0x00:
-			blockPtr = rowPtr + pixelPtr;
-			for (byte pixel_y = 0; pixel_y < 4; pixel_y++) {
-				for (byte pixel_x = 0; pixel_x < 4; pixel_x++) {
-					// We already have color of upper left pixel
-					if (pixel_y != 0 || pixel_x != 0)
-						colorA = stream.readUint16BE();
-
-					PUT_PIXEL(colorA);
-				}
-				blockPtr += rowInc;
-			}
+		case 0x00: {
+			uint16 colors[16];
+			colors[0] = colorA;
+
+			for (int i = 0; i < 15; i++)
+				colors[i + 1] = stream.readUint16BE();
+
+			BlockDecoder::drawRawBlock(blockPtr, pitch, colors, colorMap);
 			ADVANCE_BLOCK();
 			break;
+		}
 
 		// Unknown opcode
 		default:
 			error("Unknown opcode %02x in rpza chunk", opcode);
 		}
 	}
+}
+
+const Graphics::Surface *RPZADecoder::decodeFrame(Common::SeekableReadStream &stream) {
+	if (!_surface) {
+		_surface = new Graphics::Surface();
+
+		// Allocate enough space in the surface for the blocks
+		_surface->create(_blockWidth * 4, _blockHeight * 4, getPixelFormat());
+
+		// Adjust width/height to be the right ones
+		_surface->w = _width;
+		_surface->h = _height;
+	}
+
+	if (_colorMap)
+		decodeFrameTmpl<byte, BlockDecoderDither>(stream, (byte *)_surface->getPixels(), _surface->pitch, _blockWidth, _blockHeight, _colorMap);
+	else
+		decodeFrameTmpl<uint16, BlockDecoderRaw>(stream, (uint16 *)_surface->getPixels(), _surface->pitch / 2, _blockWidth, _blockHeight, _colorMap);
 
 	return _surface;
 }
 
+bool RPZADecoder::canDither(DitherType type) const {
+	return type == kDitherTypeQT;
+}
+
+void RPZADecoder::setDither(DitherType type, const byte *palette) {
+	assert(canDither(type));
+
+	_ditherPalette = new byte[256 * 3];
+	memcpy(_ditherPalette, palette, 256 * 3);
+
+	_dirtyPalette = true;
+	_format = Graphics::PixelFormat::createFormatCLUT8();
+
+	delete[] _colorMap;
+	_colorMap = createQuickTimeDitherTable(palette, 256);
+}
+
 } // End of namespace Image
diff --git a/image/codecs/rpza.h b/image/codecs/rpza.h
index d1dbbdb..d62b385 100644
--- a/image/codecs/rpza.h
+++ b/image/codecs/rpza.h
@@ -39,10 +39,22 @@ public:
 	~RPZADecoder();
 
 	const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream);
-	Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); }
+	Graphics::PixelFormat getPixelFormat() const { return _format; }
+
+	bool containsPalette() const { return _ditherPalette != 0; }
+	const byte *getPalette() { _dirtyPalette = false; return _ditherPalette; }
+	bool hasDirtyPalette() const { return _dirtyPalette; }
+	bool canDither(DitherType type) const;
+	void setDither(DitherType type, const byte *palette);
 
 private:
+	Graphics::PixelFormat _format;
 	Graphics::Surface *_surface;
+	byte *_ditherPalette;
+	bool _dirtyPalette;
+	byte *_colorMap;
+	uint16 _width, _height;
+	uint16 _blockWidth, _blockHeight;
 };
 
 } // End of namespace Image


Commit: 94b317b0cd979bd924da14d8e3925c6c324692e6
    https://github.com/scummvm/scummvm/commit/94b317b0cd979bd924da14d8e3925c6c324692e6
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:37:13-04:00

Commit Message:
IMAGE: Implement QTRLE dithering

Changed paths:
    image/codecs/qtrle.cpp
    image/codecs/qtrle.h



diff --git a/image/codecs/qtrle.cpp b/image/codecs/qtrle.cpp
index 94744ef..8a83cfa 100644
--- a/image/codecs/qtrle.cpp
+++ b/image/codecs/qtrle.cpp
@@ -37,27 +37,45 @@ namespace Image {
 
 QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec() {
 	_bitsPerPixel = bitsPerPixel;
+	_ditherPalette = 0;
+	_width = width;
+	_height = height;
+	_surface = 0;
+	_dirtyPalette = false;
+	_colorMap = 0;
 
 	// We need to ensure the width is a multiple of 4
+	_paddedWidth = width;
 	uint16 wMod = width % 4;
 	if (wMod != 0)
-		width += 4 - wMod;
+		_paddedWidth += 4 - wMod;
+}
 
-	_surface = new Graphics::Surface();
-	_surface->create(width, height, getPixelFormat());
+QTRLEDecoder::~QTRLEDecoder() {
+	if (_surface) {
+		_surface->free();
+		delete _surface;
+	}
+
+	delete[] _colorMap;
+	delete[] _ditherPalette;
 }
 
 #define CHECK_STREAM_PTR(n) \
-  if ((stream.pos() + n) > stream.size()) { \
-    warning("QTRLE Problem: stream out of bounds (%d > %d)", stream.pos() + n, stream.size()); \
-    return; \
-  }
+	do { \
+		if ((stream.pos() + n) > stream.size()) { \
+			warning("QTRLE Problem: stream out of bounds (%d > %d)", stream.pos() + n, stream.size()); \
+			return; \
+		} \
+	} while (0)
 
 #define CHECK_PIXEL_PTR(n) \
-  if ((int32)pixelPtr + n > _surface->w * _surface->h) { \
-    warning("QTRLE Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _surface->w * _surface->h); \
-    return; \
-  } \
+	do { \
+		if ((int32)pixelPtr + n > (int)_paddedWidth * _surface->h) { \
+			warning("QTRLE Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _paddedWidth * _surface->h); \
+			return; \
+		} \
+	} while (0)
 
 void QTRLEDecoder::decode1(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
 	uint32 pixelPtr = 0;
@@ -73,7 +91,7 @@ void QTRLEDecoder::decode1(Common::SeekableReadStream &stream, uint32 rowPtr, ui
 
 		if (skip & 0x80) {
 			linesToChange--;
-			rowPtr += _surface->w;
+			rowPtr += _paddedWidth;
 			pixelPtr = rowPtr + 2 * (skip & 0x7f);
 		} else
 			pixelPtr += 2 * skip;
@@ -159,7 +177,7 @@ void QTRLEDecoder::decode2_4(Common::SeekableReadStream &stream, uint32 rowPtr,
 			}
 		}
 
-		rowPtr += _surface->w;
+		rowPtr += _paddedWidth;
 	}
 }
 
@@ -204,7 +222,7 @@ void QTRLEDecoder::decode8(Common::SeekableReadStream &stream, uint32 rowPtr, ui
 			}
 		}
 
-		rowPtr += _surface->w;
+		rowPtr += _paddedWidth;
 	}
 }
 
@@ -242,7 +260,7 @@ void QTRLEDecoder::decode16(Common::SeekableReadStream &stream, uint32 rowPtr, u
 			}
 		}
 
-		rowPtr += _surface->w;
+		rowPtr += _paddedWidth;
 	}
 }
 
@@ -288,7 +306,72 @@ void QTRLEDecoder::decode24(Common::SeekableReadStream &stream, uint32 rowPtr, u
 			}
 		}
 
-		rowPtr += _surface->w;
+		rowPtr += _paddedWidth;
+	}
+}
+
+namespace {
+
+inline uint16 readDitherColor24(Common::ReadStream &stream) {
+	uint16 color = (stream.readByte() & 0xF8) << 6;
+	color |= (stream.readByte() & 0xF8) << 1;
+	color |= stream.readByte() >> 4;
+	return color;
+}
+
+} // End of anonymous namespace
+
+void QTRLEDecoder::dither24(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 (int8 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(3);
+				CHECK_PIXEL_PTR(rleCode);
+
+				uint16 color = readDitherColor24(stream);
+
+				while (rleCode--) {
+					output[pixelPtr++] = _colorMap[colorTableOffset + color];
+					colorTableOffset += 0x4000;
+				}
+			} else {
+				CHECK_STREAM_PTR(rleCode * 3);
+				CHECK_PIXEL_PTR(rleCode);
+
+				// copy pixels directly to output
+				while (rleCode--) {
+					uint16 color = readDitherColor24(stream);
+					output[pixelPtr++] = _colorMap[colorTableOffset + color];
+					colorTableOffset += 0x4000;
+				}
+			}
+		}
+
+		rowPtr += _paddedWidth;
+		curColorTableOffset = (curColorTableOffset + 1) & 3;
 	}
 }
 
@@ -336,13 +419,16 @@ void QTRLEDecoder::decode32(Common::SeekableReadStream &stream, uint32 rowPtr, u
 			}
 		}
 
-		rowPtr += _surface->w;
+		rowPtr += _paddedWidth;
 	}
 }
 
 const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &stream) {
+	if (!_surface)
+		createSurface();
+
 	uint16 startLine = 0;
-	uint16 height = _surface->h;
+	uint16 height = _height;
 
 	// check if this frame is even supposed to change
 	if (stream.size() < 8)
@@ -365,7 +451,7 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
 		stream.readUint16BE(); // Unknown
 	}
 
-	uint32 rowPtr = _surface->w * startLine;
+	uint32 rowPtr = _paddedWidth * startLine;
 
 	switch (_bitsPerPixel) {
 	case 1:
@@ -388,7 +474,10 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
 		decode16(stream, rowPtr, height);
 		break;
 	case 24:
-		decode24(stream, rowPtr, height);
+		if (_ditherPalette)
+			dither24(stream, rowPtr, height);
+		else
+			decode24(stream, rowPtr, height);
 		break;
 	case 32:
 		decode32(stream, rowPtr, height);
@@ -400,12 +489,10 @@ const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &s
 	return _surface;
 }
 
-QTRLEDecoder::~QTRLEDecoder() {
-	_surface->free();
-	delete _surface;
-}
-
 Graphics::PixelFormat QTRLEDecoder::getPixelFormat() const {
+	if (_ditherPalette)
+		return Graphics::PixelFormat::createFormatCLUT8();
+
 	switch (_bitsPerPixel) {
 	case 1:
 	case 33:
@@ -428,4 +515,31 @@ Graphics::PixelFormat QTRLEDecoder::getPixelFormat() const {
 	return Graphics::PixelFormat();
 }
 
+bool QTRLEDecoder::canDither(DitherType type) const {
+	// Only 24-bit dithering is implemented at the moment
+	return type == kDitherTypeQT && _bitsPerPixel == 24;
+}
+
+void QTRLEDecoder::setDither(DitherType type, const byte *palette) {
+	assert(canDither(type));
+
+	_ditherPalette = new byte[256 * 3];
+	memcpy(_ditherPalette, palette, 256 * 3);
+	_dirtyPalette = true;
+
+	delete[] _colorMap;
+	_colorMap = createQuickTimeDitherTable(palette, 256);
+}
+
+void QTRLEDecoder::createSurface() {
+	if (_surface) {
+		_surface->free();
+		delete _surface;
+	}	
+
+	_surface = new Graphics::Surface();
+	_surface->create(_paddedWidth, _height, getPixelFormat());
+	_surface->w = _width;
+}
+
 } // End of namespace Image
diff --git a/image/codecs/qtrle.h b/image/codecs/qtrle.h
index b44a46c..e345fbe 100644
--- a/image/codecs/qtrle.h
+++ b/image/codecs/qtrle.h
@@ -41,16 +41,29 @@ public:
 	const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream);
 	Graphics::PixelFormat getPixelFormat() const;
 
+	bool containsPalette() const { return _ditherPalette != 0; }
+	const byte *getPalette() { _dirtyPalette = false; return _ditherPalette; }
+	bool hasDirtyPalette() const { return _dirtyPalette; }
+	bool canDither(DitherType type) const;
+	void setDither(DitherType type, const byte *palette);
+
 private:
 	byte _bitsPerPixel;
-
 	Graphics::Surface *_surface;
+	uint16 _width, _height;
+	uint32 _paddedWidth;
+	byte *_ditherPalette;
+	bool _dirtyPalette;
+	byte *_colorMap;
+
+	void createSurface();
 
 	void decode1(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange);
 	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 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);
 };
 


Commit: a5992a08a02d86e33eb9c30dc8c6dd6fcb0bdf77
    https://github.com/scummvm/scummvm/commit/a5992a08a02d86e33eb9c30dc8c6dd6fcb0bdf77
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2015-04-11T14:37:17-04:00

Commit Message:
MOHAWK: Run Myst in 8bpp

Changed paths:
    engines/mohawk/cursors.cpp
    engines/mohawk/myst.cpp
    engines/mohawk/myst_graphics.cpp
    engines/mohawk/myst_graphics.h
    engines/mohawk/myst_stacks/myst.cpp
    engines/mohawk/video.cpp
    engines/mohawk/video.h



diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp
index f1baac0..4b66829 100644
--- a/engines/mohawk/cursors.cpp
+++ b/engines/mohawk/cursors.cpp
@@ -122,7 +122,11 @@ void MystCursorManager::setCursor(uint16 id) {
 	// Myst ME stores some cursors as 24bpp images instead of 8bpp
 	if (surface->format.bytesPerPixel == 1) {
 		CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, 0);
-		CursorMan.replaceCursorPalette(mhkSurface->getPalette(), 0, 256);
+
+		// We're using the screen palette for the original game, but we need
+		// to use this for any 8bpp cursor in ME.
+		if (_vm->getFeatures() & GF_ME)
+			CursorMan.replaceCursorPalette(mhkSurface->getPalette(), 0, 256);
 	} else {
 		Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
 		CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, pixelFormat.RGBToColor(255, 255, 255), false, &pixelFormat);
diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp
index 7634e8d..b6a6c27 100644
--- a/engines/mohawk/myst.cpp
+++ b/engines/mohawk/myst.cpp
@@ -413,7 +413,12 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS
 
 	// Fill screen with black and empty cursor
 	_cursor->setCursor(0);
-	_system->fillScreen(_system->getScreenFormat().RGBToColor(0, 0, 0));
+
+	if (getFeatures() & GF_ME)
+		_system->fillScreen(_system->getScreenFormat().RGBToColor(0, 0, 0));
+	else
+		_gfx->clearScreenPalette();
+
 	_system->updateScreen();
 
 	_sound->stopSound();
@@ -495,9 +500,10 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS
 	_cache.clear();
 	_gfx->clearCache();
 
-	// Play Flyby Entry Movie on Masterpiece Edition.
-	const char *flyby = 0;
 	if (getFeatures() & GF_ME) {
+		// Play Flyby Entry Movie on Masterpiece Edition.
+		const char *flyby = 0;
+
 		switch (_curStack) {
 		case kSeleniticStack:
 			flyby = "selenitic flyby";
diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp
index 9ea9f15..49f97cc 100644
--- a/engines/mohawk/myst_graphics.cpp
+++ b/engines/mohawk/myst_graphics.cpp
@@ -28,6 +28,7 @@
 #include "common/system.h"
 #include "common/textconsole.h"
 #include "engines/util.h"
+#include "graphics/palette.h"
 #include "image/pict.h"
 
 namespace Mohawk {
@@ -37,16 +38,20 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
 
 	_viewport = Common::Rect(544, 332);
 
-	// The original version of Myst could run in 8bpp color too.
-	// However, it dithered videos to 8bpp and they looked considerably
-	// worse (than they already did :P). So we're not even going to
-	// support 8bpp mode in Myst (Myst ME required >8bpp anyway).
-	initGraphics(_viewport.width(), _viewport.height(), true, NULL); // What an odd screen size!
+	if (_vm->getFeatures() & GF_ME) {
+		// High color
+		initGraphics(_viewport.width(), _viewport.height(), true, NULL);
 
-	_pixelFormat = _vm->_system->getScreenFormat();
+		if (_vm->_system->getScreenFormat().bytesPerPixel == 1)
+			error("Myst ME requires greater than 256 colors to run");
+	} else {
+		// Paletted
+		initGraphics(_viewport.width(), _viewport.height(), true);
+		setBasePalette();
+		setPaletteToScreen();
+	}
 
-	if (_pixelFormat.bytesPerPixel == 1)
-		error("Myst requires greater than 256 colors to run");
+	_pixelFormat = _vm->_system->getScreenFormat();
 
 	// Initialize our buffer
 	_backBuffer = new Graphics::Surface();
@@ -101,7 +106,9 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
 		mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat));
 	} else {
 		mhkSurface = _bmpDecoder->decodeImage(dataStream);
-		mhkSurface->convertToTrueColor();
+
+		if (_vm->getFeatures() & GF_ME)
+			mhkSurface->convertToTrueColor();
 	}
 
 	assert(mhkSurface);
@@ -151,7 +158,8 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm
 }
 
 void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest) {
-	Graphics::Surface *surface = findImage(image)->getSurface();
+	MohawkSurface *mhkSurface = findImage(image);
+	Graphics::Surface *surface = mhkSurface->getSurface();
 
 	// Make sure the image is bottom aligned in the dest rect
 	dest.top = dest.bottom - MIN<int>(surface->h, dest.height());
@@ -190,6 +198,13 @@ void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src,
 
 	for (uint16 i = 0; i < height; i++)
 		memcpy(_backBuffer->getBasePtr(dest.left, i + dest.top), surface->getBasePtr(src.left, top + i), width * surface->format.bytesPerPixel);
+
+	if (!(_vm->getFeatures() & GF_ME)) {
+		// Make sure the palette is set
+		assert(mhkSurface->getPalette());
+		memcpy(_palette + 10 * 3, mhkSurface->getPalette() + 10 * 3, (256 - 10 * 2) * 3);
+		setPaletteToScreen();
+	}
 }
 
 void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) {
@@ -419,12 +434,16 @@ void MystGraphics::transitionDissolve(Common::Rect rect, uint step) {
 
 		for (uint16 x = rect.left; x < rect.right; x++) {
 			if (linePattern[x % 4]) {
-				if (_pixelFormat.bytesPerPixel == 2) {
-					uint16 *dst = (uint16 *)screen->getBasePtr(x, y);
-					*dst = *(const uint16 *)_backBuffer->getBasePtr(x, y);
-				} else {
-					uint32 *dst = (uint32 *)screen->getBasePtr(x, y);
-					*dst = *(const uint32 *)_backBuffer->getBasePtr(x, y);
+				switch (_pixelFormat.bytesPerPixel) {
+				case 1:
+					*((byte *)screen->getBasePtr(x, y)) = *((const byte *)_backBuffer->getBasePtr(x, y));
+					break;
+				case 2:
+					*((uint16 *)screen->getBasePtr(x, y)) = *((const uint16 *)_backBuffer->getBasePtr(x, y));
+					break;
+				case 4:
+					*((uint32 *)screen->getBasePtr(x, y)) = *((const uint32 *)_backBuffer->getBasePtr(x, y));
+					break;
 				}
 			}
 		}
@@ -588,11 +607,11 @@ void MystGraphics::drawRect(Common::Rect rect, RectState state) {
 	Graphics::Surface *screen = _vm->_system->lockScreen();
 
 	if (state == kRectEnabled)
-		screen->frameRect(rect, _pixelFormat.RGBToColor(0, 255, 0));
+		screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(0, 255, 0) : 250);
 	else if (state == kRectUnreachable)
-		screen->frameRect(rect, _pixelFormat.RGBToColor(0, 0, 255));
+		screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(0, 0, 255) : 252);
 	else
-		screen->frameRect(rect, _pixelFormat.RGBToColor(255, 0, 0));
+		screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(255, 0, 0) : 249);
 
 	_vm->_system->unlockScreen();
 }
@@ -629,50 +648,94 @@ void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) {
 	_nextAllowedDrawTime = time + _constantDrawDelay + dest.height() * dest.width() / _proportionalDrawDelay;
 }
 
-void MystGraphics::copyBackBufferToScreenWithSaturation(int16 saturation) {
-	Graphics::Surface *screen = _vm->_system->lockScreen();
+void MystGraphics::fadeToBlack() {
+	// This is only for the demo
+	assert(!(_vm->getFeatures() & GF_ME));
 
-	for (uint16 y = 0; y < _viewport.height(); y++)
-		for (uint16 x = 0; x < _viewport.width(); x++) {
-			uint32 color;
-			uint8 r, g, b;
+	// Linear fade in 64 steps
+	for (int i = 63; i >= 0; i--) {
+		byte palette[256 * 3];
+		byte *src = _palette;
+		byte *dst = palette;
 
-			if (_pixelFormat.bytesPerPixel == 2)
-				color = *(const uint16 *)_backBuffer->getBasePtr(x, y);
-			else
-				color = *(const uint32 *)_backBuffer->getBasePtr(x, y);
+		for (uint j = 0; j < sizeof(palette); j++)
+			*dst++ = *src++ * i / 64;
 
-			_pixelFormat.colorToRGB(color, r, g, b);
+		_vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
+		_vm->_system->updateScreen();
+	}
+}
 
-			r = CLIP<int16>((int16)r - saturation, 0, 255);
-			g = CLIP<int16>((int16)g - saturation, 0, 255);
-			b = CLIP<int16>((int16)b - saturation, 0, 255);
+void MystGraphics::fadeFromBlack() {
+	// This is only for the demo
+	assert(!(_vm->getFeatures() & GF_ME));
 
-			color = _pixelFormat.RGBToColor(r, g, b);
+	copyBackBufferToScreen(_viewport);
 
-			if (_pixelFormat.bytesPerPixel == 2) {
-				uint16 *dst = (uint16 *)screen->getBasePtr(x, y);
-				*dst = color;
-			} else {
-				uint32 *dst = (uint32 *)screen->getBasePtr(x, y);
-				*dst = color;
-			}
-		}
+	// Linear fade in 64 steps
+	for (int i = 0; i < 64; i++) {
+		byte palette[256 * 3];
+		byte *src = _palette;
+		byte *dst = palette;
 
-	_vm->_system->unlockScreen();
+		for (uint j = 0; j < sizeof(palette); j++)
+			*dst++ = *src++ * i / 64;
+
+		_vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
+		_vm->_system->updateScreen();
+	}
+
+	// Set the full palette
+	_vm->_system->getPaletteManager()->setPalette(_palette, 0, 256);
 	_vm->_system->updateScreen();
 }
 
-void MystGraphics::fadeToBlack() {
-	for (int16 i = 0; i < 256; i += 32) {
-		copyBackBufferToScreenWithSaturation(i);
-	}
+void MystGraphics::clearScreenPalette() {
+	// Set the palette to all black
+	byte palette[256 * 3];
+	memset(palette, 0, sizeof(palette));
+	_vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
 }
 
-void MystGraphics::fadeFromBlack() {
-	for (int16 i = 256; i >= 0; i -= 32) {
-		copyBackBufferToScreenWithSaturation(i);
-	}
+void MystGraphics::setBasePalette() {
+	// Entries [0, 9] of the palette
+	static const byte lowPalette[] = {
+		0xFF, 0xFF, 0xFF,
+		0x80, 0x00, 0x00,
+		0x00, 0x80, 0x00,
+		0x80, 0x80, 0x00,
+		0x00, 0x00, 0x80,
+		0x80, 0x00, 0x80,
+		0x00, 0x80, 0x80,
+		0xC0, 0xC0, 0xC0,
+		0xC0, 0xDC, 0xC0,
+		0xA6, 0xCA, 0xF0
+	};
+
+	// Entries [246, 255] of the palette
+	static const byte highPalette[] = {
+		0xFF, 0xFB, 0xF0,
+		0xA0, 0xA0, 0xA4,
+		0x80, 0x80, 0x80,
+		0xFF, 0x00, 0x00,
+		0x00, 0xFF, 0x00,
+		0xFF, 0xFF, 0x00,
+		0x00, 0x00, 0xFF,
+		0xFF, 0x00, 0xFF,
+		0x00, 0xFF, 0xFF,
+		0x00, 0x00, 0x00
+	};
+
+	// Note that 0 and 255 are different from normal Windows.
+	// Myst seems to hack that to white, resp. black (probably for Mac compat).
+
+	memcpy(_palette, lowPalette, sizeof(lowPalette));
+	memset(_palette + sizeof(lowPalette), 0, sizeof(_palette) - sizeof(lowPalette) - sizeof(highPalette));
+	memcpy(_palette + sizeof(_palette) - sizeof(highPalette), highPalette, sizeof(highPalette));
+}
+
+void MystGraphics::setPaletteToScreen() {
+	_vm->_system->getPaletteManager()->setPalette(_palette, 0, 256);
 }
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h
index 1f70320..6281c94 100644
--- a/engines/mohawk/myst_graphics.h
+++ b/engines/mohawk/myst_graphics.h
@@ -40,7 +40,7 @@ enum RectState {
 
 class MystGraphics : public GraphicsManager {
 public:
-	MystGraphics(MohawkEngine_Myst*);
+	MystGraphics(MohawkEngine_Myst *vm);
 	~MystGraphics();
 
 	void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest);
@@ -55,18 +55,15 @@ public:
 	void fadeToBlack();
 	void fadeFromBlack();
 
+	void clearScreenPalette();
+	void setBasePalette();
+	void setPaletteToScreen();
+	const byte *getPalette() const { return _palette; }
+
 protected:
 	MohawkSurface *decodeImage(uint16 id);
 	MohawkEngine *getVM() { return (MohawkEngine *)_vm; }
-	void simulatePreviousDrawDelay(const Common::Rect &dest);
-	void copyBackBufferToScreenWithSaturation(int16 saturation);
-	void transitionDissolve(Common::Rect rect, uint step);
-	void transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay);
-	void transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay);
-	void transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay);
-	void transitionSlideToBottom(Common::Rect rect, uint16 steps, uint16 delay);
-	void transitionPartialToRight(Common::Rect rect, uint32 width, uint32 steps);
-	void transitionPartialToLeft(Common::Rect rect, uint32 width, uint32 steps);
+
 private:
 	MohawkEngine_Myst *_vm;
 	MystBitmap *_bmpDecoder;
@@ -74,11 +71,21 @@ private:
 	Graphics::Surface *_backBuffer;
 	Graphics::PixelFormat _pixelFormat;
 	Common::Rect _viewport;
+	byte _palette[256 * 3];
 
 	int _enableDrawingTimeSimulation;
 	uint32 _nextAllowedDrawTime;
 	static const uint _constantDrawDelay = 10; // ms
 	static const uint _proportionalDrawDelay = 500; // pixels per ms
+
+	void simulatePreviousDrawDelay(const Common::Rect &dest);
+	void transitionDissolve(Common::Rect rect, uint step);
+	void transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay);
+	void transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay);
+	void transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay);
+	void transitionSlideToBottom(Common::Rect rect, uint16 steps, uint16 delay);
+	void transitionPartialToRight(Common::Rect rect, uint32 width, uint32 steps);
+	void transitionPartialToLeft(Common::Rect rect, uint32 width, uint32 steps);
 };
 
 } // End of namespace Mohawk
diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp
index c500df5..ca6e7c0 100644
--- a/engines/mohawk/myst_stacks/myst.cpp
+++ b/engines/mohawk/myst_stacks/myst.cpp
@@ -3201,13 +3201,21 @@ Common::Point Myst::towerRotationMapComputeCoords(const Common::Point &center, u
 }
 
 void Myst::towerRotationMapDrawLine(const Common::Point &center, const Common::Point &end) {
-	Graphics::PixelFormat pf = _vm->_system->getScreenFormat();
-	uint32 color = 0;
+	uint32 color;
 
-	if (!_towerRotationOverSpot)
-		color = pf.RGBToColor(0xFF, 0xFF, 0xFF); // White
-	else
-		color = pf.RGBToColor(0xFF, 0, 0); // Red
+	if (_vm->getFeatures() & GF_ME) {
+		Graphics::PixelFormat pf = _vm->_system->getScreenFormat();
+
+		if (!_towerRotationOverSpot)
+			color = pf.RGBToColor(0xFF, 0xFF, 0xFF); // White
+		else
+			color = pf.RGBToColor(0xFF, 0, 0); // Red
+	} else {
+		if (!_towerRotationOverSpot)
+			color = 0x00; // White
+		else
+			color = 0xF9; // Red
+	}
 
 	const Common::Rect rect = Common::Rect(106, 42, 459, 273);
 
diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
index cebb72e..ca38f22 100644
--- a/engines/mohawk/video.cpp
+++ b/engines/mohawk/video.cpp
@@ -53,6 +53,8 @@ bool VideoEntry::endOfVideo() {
 }
 
 VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) {
+	// Set dithering enabled, if required
+	_enableDither = _vm->getGameType() == GType_MYST && !(_vm->getFeatures() & GF_ME);
 }
 
 VideoManager::~VideoManager() {
@@ -230,16 +232,23 @@ bool VideoManager::updateMovies() {
 				Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat();
 
 				if (frame->format != pixelFormat) {
-					// We don't support downconverting to 8bpp
-					if (pixelFormat.bytesPerPixel == 1)
-						error("Cannot convert high color video frame to 8bpp");
+					// We don't support downconverting to 8bpp without having
+					// support in the codec. Set _enableDither if shows up.
+					if (pixelFormat.bytesPerPixel == 1) {
+						warning("Cannot convert high color video frame to 8bpp");
+						delete _videoStreams[i].video;
+						_videoStreams[i].clear();
+						continue;
+					}
 
 					// Convert to the current screen format
 					convertedFrame = frame->convertTo(pixelFormat, _videoStreams[i]->getPalette());
 					frame = convertedFrame;
 				} else if (pixelFormat.bytesPerPixel == 1 && _videoStreams[i]->hasDirtyPalette()) {
 					// Set the palette when running in 8bpp mode only
-					_vm->_system->getPaletteManager()->setPalette(_videoStreams[i]->getPalette(), 0, 256);
+					// Don't do this for Myst, which has its own per-stack handling
+					if (_vm->getGameType() != GType_MYST)
+						_vm->_system->getPaletteManager()->setPalette(_videoStreams[i]->getPalette(), 0, 256);
 				}
 
 				// Clip the width/height to make sure we stay on the screen (Myst does this a few times)
@@ -394,6 +403,9 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool
 	entry.loop = loop;
 	entry.enabled = true;
 
+	// Enable dither if necessary
+	checkEnableDither(entry);
+
 	entry->start();
 
 	// Search for any deleted videos so we can take a formerly used slot
@@ -431,6 +443,10 @@ VideoHandle VideoManager::createVideoHandle(const Common::String &filename, uint
 	}
 
 	entry->loadStream(file);
+
+	// Enable dither if necessary
+	checkEnableDither(entry);
+
 	entry->setVolume(volume);
 	entry->start();
 
@@ -551,4 +567,26 @@ void VideoManager::pauseMovie(VideoHandle handle, bool pause) {
 	_videoStreams[handle]->pauseVideo(pause);
 }
 
+void VideoManager::checkEnableDither(VideoEntry &entry) {
+	// If we're not dithering, bail out
+	if (!_enableDither)
+		return;
+
+	// Ignore any video which is already 8bpp
+	if (entry->getPixelFormat().bytesPerPixel == 1)
+		return;
+
+	// Set the palette
+	byte palette[256 * 3];
+	g_system->getPaletteManager()->grabPalette(palette, 0, 256);
+	entry->setDitheringPalette(palette);
+
+	if (entry->getPixelFormat().bytesPerPixel != 1) {
+		if (entry.filename.empty())
+			error("Failed to set dither for video %d", entry.id);
+		else
+			error("Failed to set dither for video %s", entry.filename.c_str());
+	}
+}
+
 } // End of namespace Mohawk
diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h
index 43181e3..deb09af 100644
--- a/engines/mohawk/video.h
+++ b/engines/mohawk/video.h
@@ -124,6 +124,10 @@ private:
 
 	VideoHandle createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop, uint16 volume = 0xff);
 	VideoHandle createVideoHandle(const Common::String &filename, uint16 x, uint16 y, bool loop, byte volume = 0xff);
+
+	// Dithering control
+	bool _enableDither;
+	void checkEnableDither(VideoEntry &entry);
 };
 
 } // End of namespace Mohawk






More information about the Scummvm-git-logs mailing list