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

lordhoto lordhoto at gmail.com
Tue Mar 20 01:42:03 CET 2012


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:
a004897252 GRAPHICS: Add a convertTo() function to Surface
29f7cc33fb MOHAWK: Use Surface::convertTo() for video 8bpp->true color conversion
426c81a7a7 GRAPHICS: Rewrite ImageDecoder to have an improved API
270f8077c2 MOHAWK: Make MystBitmap use BitmapDecoder
4516b5ea24 GRAPHICS: Convert PictDecoder to the ImageDecoder API
765a870445 GRAPHICS: Convert JPEG to the ImageDecoder API
b6d2a11432 GRAPHICS: Make the JPEG code use the new YUV to RGB converter
dd9790c1c8 GRAPHICS: Make PNG signature more readable
a658fc660e GRAPHICS: Move PNG to the ImageDecoder interface
4d65be8264 COMMON: Add a READ_UINT24 macro
a7740fb6de GRAPHICS: Add support for converting surfaces from 24bpp
a2ad364615 Merge pull request #199 from clone2727/image-decoder


Commit: a004897252fba00d48a6acb0dc5298897faefdfb
    https://github.com/scummvm/scummvm/commit/a004897252fba00d48a6acb0dc5298897faefdfb
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:47-07:00

Commit Message:
GRAPHICS: Add a convertTo() function to Surface

Changed paths:
    graphics/surface.cpp
    graphics/surface.h



diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index 79a7821..0a5b109 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -270,4 +270,77 @@ void Surface::move(int dx, int dy, int height) {
 	}
 }
 
+Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
+	assert(pixels);
+
+	Graphics::Surface *surface = new Graphics::Surface();
+
+	// If the target format is the same, just copy
+	if (format == dstFormat) {
+		surface->copyFrom(*this);
+		return surface;
+	}
+
+	if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
+		error("Surface::convertTo(): Only 2Bpp and 4Bpp supported");
+
+	surface->create(w, h, dstFormat);
+
+	if (format.bytesPerPixel == 1) {
+		// Converting from paletted to high color
+		assert(palette);
+
+		for (int y = 0; y < h; y++) {
+			const byte *srcRow = (byte *)getBasePtr(0, y);
+			byte *dstRow = (byte *)surface->getBasePtr(0, y);
+
+			for (int x = 0; x < w; x++) {
+				byte index = *srcRow++;
+				byte r = palette[index * 3];
+				byte g = palette[index * 3 + 1];
+				byte b = palette[index * 3 + 2];
+
+				uint32 color = dstFormat.RGBToColor(r, g, b);
+
+				if (dstFormat.bytesPerPixel == 2)
+					*((uint16 *)dstRow) = color;
+				else
+					*((uint32 *)dstRow) = color;
+
+				dstRow += dstFormat.bytesPerPixel;
+			}
+		}
+	} else {
+		// Converting from high color to high color
+		for (int y = 0; y < h; y++) {
+			const byte *srcRow = (byte *)getBasePtr(0, y);
+			byte *dstRow = (byte *)surface->getBasePtr(0, y);
+
+			for (int x = 0; x < w; x++) {
+				uint32 srcColor;
+				if (format.bytesPerPixel == 2)
+					srcColor = *((uint16 *)srcRow);
+				else
+					srcColor = *((uint32 *)srcRow);
+
+				srcRow += format.bytesPerPixel;
+
+				// Convert that color to the new format
+				byte r, g, b, a;
+				format.colorToARGB(srcColor, a, r, g, b);
+				uint32 color = dstFormat.ARGBToColor(a, r, g, b);
+
+				if (dstFormat.bytesPerPixel == 2)
+					*((uint16 *)dstRow) = color;
+				else
+					*((uint32 *)dstRow) = color;
+
+				dstRow += dstFormat.bytesPerPixel;
+			}
+		}
+	}
+
+	return surface;
+}
+
 } // End of namespace Graphics
diff --git a/graphics/surface.h b/graphics/surface.h
index 018a283..eb8d1ac 100644
--- a/graphics/surface.h
+++ b/graphics/surface.h
@@ -135,6 +135,17 @@ struct Surface {
 	void copyFrom(const Surface &surf);
 
 	/**
+	 * Convert the data to another pixel format.
+	 *
+	 * The calling code must call free on the returned surface and then delete
+	 * it.
+	 *
+	 * @param dstFormat The desired format
+	 * @param palette   The palette (in RGB888), if the source format has a Bpp of 1
+	 */
+	Graphics::Surface *convertTo(const PixelFormat &dstFormat, const byte *palette = 0) const;
+
+	/**
 	 * Draw a line.
 	 *
 	 * @param x0 The x coordinate of the start point.


Commit: 29f7cc33fb66ded97a8261beb8d16dd951bdec59
    https://github.com/scummvm/scummvm/commit/29f7cc33fb66ded97a8261beb8d16dd951bdec59
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:47-07:00

Commit Message:
MOHAWK: Use Surface::convertTo() for video 8bpp->true color conversion

Changed paths:
    engines/mohawk/video.cpp



diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
index 8d72fa3..80fa4bf 100644
--- a/engines/mohawk/video.cpp
+++ b/engines/mohawk/video.cpp
@@ -235,25 +235,7 @@ bool VideoManager::updateMovies() {
 						if (_videoStreams[i]->hasDirtyPalette())
 							_videoStreams[i]->setSystemPalette();
 					} else {
-						convertedFrame = new Graphics::Surface();
-						const byte *palette = _videoStreams[i]->getPalette();
-						assert(palette);
-
-						convertedFrame->create(frame->w, frame->h, pixelFormat);
-
-						for (uint16 j = 0; j < frame->h; j++) {
-							for (uint16 k = 0; k < frame->w; k++) {
-								byte palIndex = *((const byte *)frame->getBasePtr(k, j));
-								byte r = palette[palIndex * 3];
-								byte g = palette[palIndex * 3 + 1];
-								byte b = palette[palIndex * 3 + 2];
-								if (pixelFormat.bytesPerPixel == 2)
-									*((uint16 *)convertedFrame->getBasePtr(k, j)) = pixelFormat.RGBToColor(r, g, b);
-								else
-									*((uint32 *)convertedFrame->getBasePtr(k, j)) = pixelFormat.RGBToColor(r, g, b);
-							}
-						}
-
+						convertedFrame = frame->convertTo(pixelFormat, _videoStreams[i]->getPalette());
 						frame = convertedFrame;
 					}
 				}


Commit: 426c81a7a7a4d6f47b73db1a75908b548d250e4e
    https://github.com/scummvm/scummvm/commit/426c81a7a7a4d6f47b73db1a75908b548d250e4e
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:47-07:00

Commit Message:
GRAPHICS: Rewrite ImageDecoder to have an improved API

The new bitmap decoder class is based off the Mohawk one, and now has 8bpp decoding capability.

Changed paths:
  A graphics/decoders/bmp.cpp
  A graphics/decoders/bmp.h
  A graphics/decoders/image_decoder.h
  R graphics/imagedec.h
  R graphics/imagedec.cpp
    backends/vkeybd/virtual-keyboard-parser.cpp
    engines/hugo/dialogs.cpp
    graphics/module.mk
    gui/ThemeEngine.cpp



diff --git a/backends/vkeybd/virtual-keyboard-parser.cpp b/backends/vkeybd/virtual-keyboard-parser.cpp
index 1958113..bb8286d 100644
--- a/backends/vkeybd/virtual-keyboard-parser.cpp
+++ b/backends/vkeybd/virtual-keyboard-parser.cpp
@@ -34,7 +34,7 @@
 #include "common/tokenizer.h"
 #include "common/stream.h"
 
-#include "graphics/imagedec.h"
+#include "graphics/decoders/bmp.h"
 
 namespace Common {
 
@@ -266,11 +266,15 @@ bool VirtualKeyboardParser::parserCallback_layout(ParserNode *node) {
 
 	const Graphics::PixelFormat format = g_system->getOverlayFormat();
 
-	_mode->image = Graphics::ImageDecoder::loadFile(*file, format);
-	delete file;
+	{
+		Graphics::BitmapDecoder bmp;
+		if (!bmp.loadStream(*file))
+			return parserError("Error loading bitmap '" + _mode->bitmapName + "'");
+
+		_mode->image = bmp.getSurface()->convertTo(format);
+	}
 
-	if (!_mode->image)
-		return parserError("Error loading bitmap '" + _mode->bitmapName + "'");
+	delete file;
 
 	int r, g, b;
 	if (node->values.contains("transparent_color")) {
diff --git a/engines/hugo/dialogs.cpp b/engines/hugo/dialogs.cpp
index c43cdd7..e0b0198 100644
--- a/engines/hugo/dialogs.cpp
+++ b/engines/hugo/dialogs.cpp
@@ -21,7 +21,7 @@
  */
 
 #include "common/substream.h"
-#include "graphics/imagedec.h"
+#include "graphics/decoders/bmp.h"
 #include "gui/gui-manager.h"
 #include "gui/ThemeEval.h"
 
@@ -128,7 +128,16 @@ void TopMenu::loadBmpArr(Common::SeekableReadStream &in) {
 		uint16 bmpSize = in.readUint16BE();
 		uint32 filPos = in.pos();
 		Common::SeekableSubReadStream stream(&in, filPos, filPos + bmpSize);
-		arrayBmp[i * 2] = Graphics::ImageDecoder::loadFile(stream, g_system->getOverlayFormat());
+
+		Graphics::BitmapDecoder bitmapDecoder;
+		if (!bitmapDecoder.loadStream(stream))
+			error("TopMenu::loadBmpArr(): Could not load bitmap");
+
+		const Graphics::Surface *bitmapSrc = bitmapDecoder.getSurface();
+		if (bitmapSrc->format.bytesPerPixel == 1)
+			error("TopMenu::loadBmpArr(): Unhandled paletted image");
+
+		arrayBmp[i * 2] = bitmapSrc->convertTo(g_system->getOverlayFormat());
 		arrayBmp[i * 2 + 1] = new Graphics::Surface();
 		arrayBmp[i * 2 + 1]->create(arrayBmp[i * 2]->w * 2, arrayBmp[i * 2]->h * 2, g_system->getOverlayFormat());
 		byte *src = (byte *)arrayBmp[i * 2]->pixels;
@@ -154,7 +163,7 @@ void TopMenu::loadBmpArr(Common::SeekableReadStream &in) {
 			}
 		}
 
-		in.skip(bmpSize);
+		in.seek(filPos + bmpSize);
 	}
 }
 
diff --git a/graphics/decoders/bmp.cpp b/graphics/decoders/bmp.cpp
new file mode 100644
index 0000000..0d44881
--- /dev/null
+++ b/graphics/decoders/bmp.cpp
@@ -0,0 +1,159 @@
+/* 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.
+ */
+
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+#include "graphics/pixelformat.h"
+#include "graphics/surface.h"
+#include "graphics/decoders/bmp.h"
+
+namespace Graphics {
+
+BitmapDecoder::BitmapDecoder() {
+	_surface = 0;
+	_palette = 0;
+}
+
+BitmapDecoder::~BitmapDecoder() {
+	destroy();
+}
+
+void BitmapDecoder::destroy() {
+	if (_surface) {
+		_surface->free();
+		delete _surface; _surface = 0;
+	}
+
+	delete[] _palette; _palette = 0;
+}
+
+bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) {
+	destroy();
+
+	if (stream.readByte() != 'B')
+		return false;
+
+	if (stream.readByte() != 'M')
+		return false;
+
+	/* uint32 fileSize = */ stream.readUint32LE();
+	/* uint16 res1 = */ stream.readUint16LE();
+	/* uint16 res2 = */ stream.readUint16LE();
+	uint32 imageOffset = stream.readUint32LE();
+
+	uint32 infoSize = stream.readUint32LE();
+	if (infoSize != 40) {
+		warning("Only Windows v3 bitmaps are supported");
+		return false;
+	}
+
+	uint32 width = stream.readUint32LE();
+	int32 height = stream.readSint32LE();
+
+	if (width == 0 || height == 0)
+		return false;
+
+	if (height < 0) {
+		warning("Right-side up bitmaps not supported");
+		return false;
+	}
+
+	/* uint16 planes = */ stream.readUint16LE();
+	uint16 bitsPerPixel = stream.readUint16LE();
+
+	if (bitsPerPixel != 8 && bitsPerPixel != 24) {
+		warning("%dbpp bitmaps not supported", bitsPerPixel);
+		return false;
+	}
+
+	uint32 compression = stream.readUint32LE();
+
+	if (compression != 0) {
+		warning("Compressed bitmaps not supported");
+		return false;
+	}
+
+	/* uint32 imageSize = */ stream.readUint32LE();
+	/* uint32 pixelsPerMeterX = */ stream.readUint32LE();
+	/* uint32 pixelsPerMeterY = */ stream.readUint32LE();
+	uint32 colorsUsed = stream.readUint32LE();
+	/* uint32 colorsImportant = */ stream.readUint32LE();
+
+	if (colorsUsed == 0)
+		colorsUsed = 256;
+
+	if (bitsPerPixel == 8) {
+		// Read the palette
+		_palette = new byte[colorsUsed * 3];
+		for (uint16 i = 0; i < colorsUsed; i++) {
+			_palette[i * 3 + 2] = stream.readByte();
+			_palette[i * 3 + 1] = stream.readByte();
+			_palette[i * 3 + 0] = stream.readByte();
+			stream.readByte();
+		}
+	}
+
+	// Start us at the beginning of the image
+	stream.seek(imageOffset);
+
+	Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
+
+	// BGRA for 24bpp
+	if (bitsPerPixel == 24)
+		format = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0);
+
+	_surface = new Graphics::Surface();
+	_surface->create(width, height, format);
+
+	int srcPitch = width * (bitsPerPixel >> 3);
+	const int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0;
+
+	if (bitsPerPixel == 8) {
+		byte *dst = (byte *)_surface->pixels;
+
+		for (int32 i = 0; i < height; i++) {
+			stream.read(dst + (height - i - 1) * width, width);
+			stream.skip(extraDataLength);
+		}
+	} else {
+		byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch;
+
+		for (int32 i = 0; i < height; i++) {
+			for (uint32 j = 0; j < width; j++) {
+				byte b = stream.readByte();
+				byte g = stream.readByte();
+				byte r = stream.readByte();
+				uint32 color = format.RGBToColor(r, g, b);
+
+				*((uint32 *)dst) = color;
+				dst += format.bytesPerPixel;
+			}
+
+			stream.skip(extraDataLength);
+			dst -= _surface->pitch * 2;
+		}
+	}
+
+	return true;
+}
+
+} // End of namespace Graphics
diff --git a/graphics/decoders/bmp.h b/graphics/decoders/bmp.h
new file mode 100644
index 0000000..e11b12f
--- /dev/null
+++ b/graphics/decoders/bmp.h
@@ -0,0 +1,56 @@
+/* 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 GRAPHICS_DECODERS_BMP_H
+#define GRAPHICS_DECODERS_BMP_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "graphics/decoders/image_decoder.h"
+
+namespace Common{
+class SeekableReadStream;
+}
+
+namespace Graphics {
+
+struct PixelFormat;
+struct Surface;
+
+class BitmapDecoder : public ImageDecoder {
+public:
+	BitmapDecoder();
+	virtual ~BitmapDecoder();
+
+	// ImageDecoder API
+	void destroy();
+	virtual bool loadStream(Common::SeekableReadStream &stream);
+	virtual const Surface *getSurface() const { return _surface; }
+	virtual const byte *getPalette() { return _palette; }
+
+private:
+	Surface *_surface;
+	byte *_palette;
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/decoders/image_decoder.h b/graphics/decoders/image_decoder.h
new file mode 100644
index 0000000..e768f7f
--- /dev/null
+++ b/graphics/decoders/image_decoder.h
@@ -0,0 +1,85 @@
+/* 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 GRAPHICS_DECODERS_IMAGEDECODER_H
+#define GRAPHICS_DECODERS_IMAGEDECODER_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+
+namespace Common{
+class SeekableReadStream;
+}
+
+namespace Graphics {
+
+struct PixelFormat;
+struct Surface;
+
+/**
+ * A representation of an image decoder that maintains ownership of the surface
+ * and palette it decodes to.
+ */
+class ImageDecoder {
+public:
+	virtual ~ImageDecoder() {}
+
+	/**
+	 * Load an image from the specified stream
+	 *
+	 * @param stream the input stream
+	 * @return whether loading the file succeeded
+	 * @see getSurface
+	 * @see getPalette
+	 */
+	virtual bool loadStream(Common::SeekableReadStream &stream) = 0;
+
+	/**
+	 * Destroy this decoder's surface and palette
+	 */
+	virtual void destroy() = 0;
+
+	/**
+	 * Get the decoded surface
+	 *
+	 * This surface is owned by this ImageDecoder and will remain valid
+	 * until destroy() or loadStream() is called, or until this ImageDecoder's
+	 * destructor is called.
+	 *
+	 * @return the decoded surface, or 0 if no surface is present
+	 */
+	virtual const Surface *getSurface() const = 0;
+
+	/**
+	 * Get the decoded palette
+	 *
+	 * This palette is owned by this ImageDecoder and will remain valid
+	 * until destroy() or loadStream() is called, or until this ImageDecoder's
+	 * destructor is called.
+	 *
+	 * @return the decoded palette, or 0 if no palette is present
+	 */
+	virtual const byte *getPalette() const { return 0; }
+};
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/imagedec.cpp b/graphics/imagedec.cpp
deleted file mode 100644
index 9552f09..0000000
--- a/graphics/imagedec.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-/* 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.
- */
-
-#include "graphics/imagedec.h"
-#include "graphics/pixelformat.h"
-#include "graphics/surface.h"
-
-#include "common/file.h"
-
-namespace Graphics {
-//
-// BMP Decoder
-//
-class BMPDecoder : public ImageDecoder {
-public:
-	BMPDecoder() {}
-	virtual ~BMPDecoder() {}
-
-	bool decodeable(Common::SeekableReadStream &stream);
-	Surface *decodeImage(Common::SeekableReadStream &stream, const PixelFormat &format);
-
-	struct BitmapHeader {
-		uint16 type;
-		uint32 size;
-		uint16 res1;
-		uint16 res2;
-		uint32 imageOffset;
-	};
-
-	struct InfoHeader {
-		uint32 size;
-		uint32 width;
-		uint32 height;
-		uint16 planes;
-		uint16 bitsPerPixel;
-		uint32 compression;
-		uint32 imageSize;
-		uint32 pixelsPerMeterX;
-		uint32 pixelsPerMeterY;
-		uint32 colorsUsed;
-		uint32 colorsImportant;
-	};
-};
-
-bool BMPDecoder::decodeable(Common::SeekableReadStream &stream) {
-	BitmapHeader header;
-	stream.seek(0);
-	header.type = stream.readUint16BE();
-	header.size = stream.readUint32LE();
-
-	// TODO: maybe improve this detection
-	if (header.size == 0 || header.type != 'BM')
-		return false;
-
-	return true;
-}
-
-Surface *BMPDecoder::decodeImage(Common::SeekableReadStream &stream, const PixelFormat &format) {
-	if (!decodeable(stream)) {
-		return 0;
-	}
-
-	BitmapHeader header;
-	InfoHeader info;
-
-	stream.seek(0);
-	header.type = stream.readUint16BE();
-	header.size = stream.readUint32LE();
-	header.res1 = stream.readUint16LE();
-	header.res2 = stream.readUint16LE();
-	header.imageOffset = stream.readUint32LE();
-
-	if (header.size == 0 || header.type != 'BM') {
-		stream.seek(0);
-		return 0;
-	}
-
-	info.size = stream.readUint32LE();
-	info.width = stream.readUint32LE();
-	info.height = stream.readUint32LE();
-	info.planes = stream.readUint16LE();
-	info.bitsPerPixel = stream.readUint16LE();
-	info.compression = stream.readUint32LE();
-	info.imageSize = stream.readUint32LE();
-	info.pixelsPerMeterX = stream.readUint32LE();
-	info.pixelsPerMeterY = stream.readUint32LE();
-	info.colorsUsed = stream.readUint32LE();
-	info.colorsImportant = stream.readUint32LE();
-
-	stream.seek(header.imageOffset);
-
-	if (info.bitsPerPixel != 24) {
-		stream.seek(0);
-		return 0;
-	}
-
-	uint8 r = 0, g = 0, b = 0;
-	Surface *newSurf = new Surface;
-	assert(newSurf);
-	newSurf->create(info.width, info.height, format);
-	assert(newSurf->pixels);
-	OverlayColor *curPixel = (OverlayColor *)newSurf->pixels + (newSurf->h-1) * newSurf->w;
-	int pitchAdd = info.width % 4;
-	for (int i = 0; i < newSurf->h; ++i) {
-		for (int i2 = 0; i2 < newSurf->w; ++i2) {
-			b = stream.readByte();
-			g = stream.readByte();
-			r = stream.readByte();
-			*curPixel = format.RGBToColor(r, g, b);
-			++curPixel;
-		}
-		stream.seek(pitchAdd, SEEK_CUR);
-		curPixel -= newSurf->w*2;
-	}
-
-	stream.seek(0);
-	return newSurf;
-}
-
-#pragma mark -
-
-Surface *ImageDecoder::loadFile(const Common::String &name, const PixelFormat &format) {
-	Surface *newSurf = 0;
-
-	Common::File imageFile;
-	if (imageFile.open(name)) {
-		newSurf = loadFile(imageFile, format);
-	}
-
-	return newSurf;
-}
-
-Surface *ImageDecoder::loadFile(Common::SeekableReadStream &stream, const PixelFormat &format) {
-	// TODO: implement support for bzipped memory
-
-	// FIXME: this is not a very nice solution but it should work
-	// for the moment, we should use a different way to get all
-	// decoders
-	static BMPDecoder bmpDecoder;
-	static ImageDecoder *decoderList[] = {
-		&bmpDecoder,			// for uncompressed .BMP files
-		0
-	};
-
-	ImageDecoder *decoder = 0;
-	for (int i = 0; decoderList[i] != 0; ++i) {
-		if (decoderList[i]->decodeable(stream)) {
-			decoder = decoderList[i];
-			break;
-		}
-	}
-
-	if (!decoder)
-		return 0;
-
-	return decoder->decodeImage(stream, format);
-}
-} // End of namespace Graphics
diff --git a/graphics/imagedec.h b/graphics/imagedec.h
deleted file mode 100644
index b03b8bc..0000000
--- a/graphics/imagedec.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* 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 GRAPHICS_IMAGEDEC_H
-#define GRAPHICS_IMAGEDEC_H
-
-#include "common/scummsys.h"
-#include "common/str.h"
-
-namespace Common{
-class SeekableReadStream;
-}
-
-namespace Graphics {
-
-struct PixelFormat;
-struct Surface;
-
-class ImageDecoder {
-public:
-	virtual ~ImageDecoder() {}
-
-	static Surface *loadFile(const Common::String &name, const PixelFormat &format);
-	static Surface *loadFile(Common::SeekableReadStream &stream, const PixelFormat &format);
-
-	/**
-	 * checks if the data can be decoded by this decoder
-	 *
-	 * @param stream memory read stream
-	 * @return true if it can be decoded, otherwise false
-	 */
-	virtual bool decodeable(Common::SeekableReadStream &stream) = 0;
-
-	/**
-	 * decodes the data and returns an pointer to the resulting surface.
-	 * Surface::free() must be called by the user also it must be deleted
-	 * with delete;
-	 *
-	 * @param stream the memory stream which should be decoded
-	 * @param format the pixel format used to generate the surface
-	 * @return returns a new surface if the image could be decoded, otherwise 0
-	 */
-	virtual Surface *decodeImage(Common::SeekableReadStream &stream, const PixelFormat &format) = 0;
-};
-} // End of namespace Graphics
-
-#endif
diff --git a/graphics/module.mk b/graphics/module.mk
index 1e84b24..5c1d313 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -12,7 +12,6 @@ MODULE_OBJS := \
 	fonts/ttf.o \
 	fonts/winfont.o \
 	iff.o \
-	imagedec.o \
 	jpeg.o \
 	maccursor.o \
 	pict.o \
@@ -26,7 +25,8 @@ MODULE_OBJS := \
 	VectorRenderer.o \
 	VectorRendererSpec.o \
 	wincursor.o \
-	yuv_to_rgb.o
+	yuv_to_rgb.o \
+	decoders/bmp.o
 
 ifdef USE_SCALERS
 MODULE_OBJS += \
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 2cb1635..bcfd41e 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -30,11 +30,11 @@
 
 #include "graphics/cursorman.h"
 #include "graphics/fontman.h"
-#include "graphics/imagedec.h"
 #include "graphics/surface.h"
 #include "graphics/VectorRenderer.h"
 #include "graphics/fonts/bdf.h"
 #include "graphics/fonts/ttf.h"
+#include "graphics/decoders/bmp.h"
 
 #include "gui/widget.h"
 #include "gui/ThemeEngine.h"
@@ -620,20 +620,25 @@ bool ThemeEngine::addBitmap(const Common::String &filename) {
 	if (surf)
 		return true;
 
-	// If not, try to load the bitmap via the ImageDecoder class.
+	// If not, try to load the bitmap via the BitmapDecoder class.
+	Graphics::BitmapDecoder bitmapDecoder;
+	const Graphics::Surface *srcSurface = 0;
 	Common::ArchiveMemberList members;
 	_themeFiles.listMatchingMembers(members, filename);
 	for (Common::ArchiveMemberList::const_iterator i = members.begin(), end = members.end(); i != end; ++i) {
 		Common::SeekableReadStream *stream = (*i)->createReadStream();
 		if (stream) {
-			surf = Graphics::ImageDecoder::loadFile(*stream, _overlayFormat);
+			bitmapDecoder.loadStream(*stream);
+			srcSurface = bitmapDecoder.getSurface();
 			delete stream;
-
-			if (surf)
+			if (srcSurface)
 				break;
 		}
 	}
 
+	if (srcSurface && srcSurface->format.bytesPerPixel != 1)
+		surf = srcSurface->convertTo(_overlayFormat);
+
 	// Store the surface into our hashmap (attention, may store NULL entries!)
 	_bitmaps[filename] = surf;
 


Commit: 270f8077c2fa4f226a693d95c7653b5d358b9556
    https://github.com/scummvm/scummvm/commit/270f8077c2fa4f226a693d95c7653b5d358b9556
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:47-07:00

Commit Message:
MOHAWK: Make MystBitmap use BitmapDecoder

Changed paths:
    engines/mohawk/bitmap.cpp
    engines/mohawk/bitmap.h



diff --git a/engines/mohawk/bitmap.cpp b/engines/mohawk/bitmap.cpp
index 4edde31..952b6da 100644
--- a/engines/mohawk/bitmap.cpp
+++ b/engines/mohawk/bitmap.cpp
@@ -29,6 +29,7 @@
 #include "common/substream.h"
 #include "common/system.h"
 #include "common/textconsole.h"
+#include "graphics/decoders/bmp.h"
 
 namespace Mohawk {
 
@@ -631,97 +632,37 @@ void MohawkBitmap::drawRLE8(Graphics::Surface *surface, bool isLE) {
 
 MohawkSurface *MystBitmap::decodeImage(Common::SeekableReadStream* stream) {
 	uint32 uncompressedSize = stream->readUint32LE();
-	Common::SeekableReadStream* bmpStream = decompressLZ(stream, uncompressedSize);
+	Common::SeekableReadStream *bmpStream = decompressLZ(stream, uncompressedSize);
 	delete stream;
 
-	_header.type = bmpStream->readUint16BE();
+	Graphics::BitmapDecoder bitmapDecoder;
+	if (!bitmapDecoder.loadStream(*bmpStream))
+		error("Could not decode Myst bitmap");
 
-	if (_header.type != 'BM')
-		error("BMP header not detected");
+	const Graphics::Surface *bmpSurface = bitmapDecoder.getSurface();
+	Graphics::Surface *newSurface = 0;
 
-	_header.size = bmpStream->readUint32LE();
-	assert (_header.size > 0);
-	_header.res1 = bmpStream->readUint16LE();
-	_header.res2 = bmpStream->readUint16LE();
-	_header.imageOffset = bmpStream->readUint32LE();
-
-	_info.size = bmpStream->readUint32LE();
-
-	if (_info.size != 40)
-		error("Only Windows v3 BMP's are supported");
-
-	_info.width = bmpStream->readUint32LE();
-	_info.height = bmpStream->readUint32LE();
-	_info.planes = bmpStream->readUint16LE();
-	_info.bitsPerPixel = bmpStream->readUint16LE();
-	_info.compression = bmpStream->readUint32LE();
-	_info.imageSize = bmpStream->readUint32LE();
-	_info.pixelsPerMeterX = bmpStream->readUint32LE();
-	_info.pixelsPerMeterY = bmpStream->readUint32LE();
-	_info.colorsUsed = bmpStream->readUint32LE();
-	_info.colorsImportant = bmpStream->readUint32LE();
-
-	if (_info.compression != 0)
-		error("Unhandled BMP compression %d", _info.compression);
-
-	if (_info.colorsUsed == 0)
-		_info.colorsUsed = 256;
-
-	if (_info.bitsPerPixel != 8 && _info.bitsPerPixel != 24)
-		error("%dbpp Bitmaps not supported", _info.bitsPerPixel);
-
-	byte *palData = NULL;
-
-	if (_info.bitsPerPixel == 8) {
-		palData = (byte *)malloc(256 * 3);
-		for (uint16 i = 0; i < _info.colorsUsed; i++) {
-			palData[i * 3 + 2] = bmpStream->readByte();
-			palData[i * 3 + 1] = bmpStream->readByte();
-			palData[i * 3 + 0] = bmpStream->readByte();
-			bmpStream->readByte();
-		}
-	}
-
-	bmpStream->seek(_header.imageOffset);
-
-	Graphics::Surface *surface = createSurface(_info.width, _info.height);
-	int srcPitch = _info.width * (_info.bitsPerPixel >> 3);
-	const int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0;
-
-	if (_info.bitsPerPixel == 8) {
-		byte *dst = (byte *)surface->pixels;
-
-		for (uint32 i = 0; i < _info.height; i++) {
-			bmpStream->read(dst + (_info.height - i - 1) * _info.width, _info.width);
-			bmpStream->skip(extraDataLength);
-		}
+	if (bmpSurface->format.bytesPerPixel == 1) {
+		_bitsPerPixel = 8;
+		newSurface = new Graphics::Surface();
+		newSurface->copyFrom(*bmpSurface);
 	} else {
-		Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
-
-		byte *dst = (byte *)surface->pixels + (surface->h - 1) * surface->pitch;
-
-		for (uint32 i = 0; i < _info.height; i++) {
-			for (uint32 j = 0; j < _info.width; j++) {
-				byte b = bmpStream->readByte();
-				byte g = bmpStream->readByte();
-				byte r = bmpStream->readByte();
+		_bitsPerPixel = 24;
+		newSurface = bmpSurface->convertTo(g_system->getScreenFormat());
+	}
 
-				if (pixelFormat.bytesPerPixel == 2)
-					*((uint16 *)dst) = pixelFormat.RGBToColor(r, g, b);
-				else
-					*((uint32 *)dst) = pixelFormat.RGBToColor(r, g, b);
+	// Copy the palette to one of our own
+	const byte *palette = bitmapDecoder.getPalette();
+	byte *newPal = 0;
 
-				dst += pixelFormat.bytesPerPixel;
-			}
-
-			bmpStream->skip(extraDataLength);
-			dst -= surface->pitch * 2;
-		}
+	if (palette) {
+		newPal = (byte *)malloc(256 * 3);
+		memcpy(newPal, palette, 256 * 3);
 	}
 
 	delete bmpStream;
 
-	return new MohawkSurface(surface, palData);
+	return new MohawkSurface(newSurface, newPal);
 }
 
 #endif
diff --git a/engines/mohawk/bitmap.h b/engines/mohawk/bitmap.h
index 7421888..73c117b5 100644
--- a/engines/mohawk/bitmap.h
+++ b/engines/mohawk/bitmap.h
@@ -154,30 +154,10 @@ public:
 	MohawkSurface *decodeImage(Common::SeekableReadStream *stream);
 
 protected:
-	byte getBitsPerPixel() { return _info.bitsPerPixel; }
+	byte getBitsPerPixel() { return _bitsPerPixel; }
 
 private:
-	struct BitmapHeader {
-		uint16 type;
-		uint32 size;
-		uint16 res1;
-		uint16 res2;
-		uint32 imageOffset;
-	} _header;
-
-	struct InfoHeader {
-		uint32 size;
-		uint32 width;
-		uint32 height;
-		uint16 planes;
-		uint16 bitsPerPixel;
-		uint32 compression;
-		uint32 imageSize;
-		uint32 pixelsPerMeterX;
-		uint32 pixelsPerMeterY;
-		uint32 colorsUsed;
-		uint32 colorsImportant;
-	} _info;
+	uint16 _bitsPerPixel;
 };
 
 #endif


Commit: 4516b5ea24e6c0056984a65fe4fff33553931487
    https://github.com/scummvm/scummvm/commit/4516b5ea24e6c0056984a65fe4fff33553931487
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:48-07:00

Commit Message:
GRAPHICS: Convert PictDecoder to the ImageDecoder API

Changed paths:
  A graphics/decoders/pict.cpp
  A graphics/decoders/pict.h
  R graphics/pict.cpp
  R graphics/pict.h
    engines/mohawk/graphics.cpp
    engines/mohawk/myst_graphics.cpp
    engines/mohawk/myst_graphics.h
    engines/sci/graphics/maciconbar.cpp
    engines/sci/graphics/maciconbar.h
    graphics/module.mk



diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp
index a08d034..0f9a62c 100644
--- a/engines/mohawk/graphics.cpp
+++ b/engines/mohawk/graphics.cpp
@@ -58,22 +58,7 @@ void MohawkSurface::convertToTrueColor() {
 
 	assert(_palette);
 
-	Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
-	Graphics::Surface *surface = new Graphics::Surface();
-	surface->create(_surface->w, _surface->h, pixelFormat);
-
-	for (uint16 i = 0; i < _surface->h; i++) {
-		for (uint16 j = 0; j < _surface->w; j++) {
-			byte palIndex = *((byte *)_surface->pixels + i * _surface->pitch + j);
-			byte r = _palette[palIndex * 3 + 0];
-			byte g = _palette[palIndex * 3 + 1];
-			byte b = _palette[palIndex * 3 + 2];
-			if (pixelFormat.bytesPerPixel == 2)
-				*((uint16 *)surface->getBasePtr(j, i)) = pixelFormat.RGBToColor(r, g, b);
-			else
-				*((uint32 *)surface->getBasePtr(j, i)) = pixelFormat.RGBToColor(r, g, b);
-		}
-	}
+	Graphics::Surface *surface = _surface->convertTo(g_system->getScreenFormat(), _palette);
 
 	// Free everything and set the new surface as the converted surface
 	_surface->free();
diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp
index 1513905..86eb4f2 100644
--- a/engines/mohawk/myst_graphics.cpp
+++ b/engines/mohawk/myst_graphics.cpp
@@ -29,7 +29,7 @@
 #include "common/textconsole.h"
 #include "engines/util.h"
 #include "graphics/jpeg.h"
-#include "graphics/pict.h"
+#include "graphics/decoders/pict.h"
 
 namespace Mohawk {
 
@@ -51,10 +51,8 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
 
 	if (_vm->getFeatures() & GF_ME) {
 		_jpegDecoder = new Graphics::JPEG();
-		_pictDecoder = new Graphics::PictDecoder(_pixelFormat);
 	} else {
 		_jpegDecoder = NULL;
-		_pictDecoder = NULL;
 	}
 
 	_pictureFile.entries = NULL;
@@ -70,7 +68,6 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
 MystGraphics::~MystGraphics() {
 	delete _bmpDecoder;
 	delete _jpegDecoder;
-	delete _pictDecoder;
 	delete[] _pictureFile.entries;
 
 	_backBuffer->free();
@@ -138,7 +135,13 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
 					mhkSurface = new MohawkSurface(_jpegDecoder->getSurface(_pixelFormat));
 					delete stream;
 				} else if (_pictureFile.entries[i].type == 1) {
-					mhkSurface = new MohawkSurface(_pictDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size)));
+					Graphics::PICTDecoder pict;
+					Common::SeekableSubReadStream subStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size);
+
+					if (!pict.loadStream(subStream))
+						error("Could not decode Myst ME Mac PICT");
+
+					mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat));
 				} else
 					error ("Unknown Picture File type %d", _pictureFile.entries[i].type);
 				break;
@@ -170,9 +173,14 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
 			dataStream->seek(0);
 		}
 
-		if (isPict)
-			mhkSurface = new MohawkSurface(_pictDecoder->decodeImage(dataStream));
-		else {
+		if (isPict) {
+			Graphics::PICTDecoder pict;
+
+			if (!pict.loadStream(*dataStream))
+				error("Could not decode Myst ME PICT");
+
+			mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat));
+		} else {
 			mhkSurface = _bmpDecoder->decodeImage(dataStream);
 			mhkSurface->convertToTrueColor();
 		}
diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h
index e2b02db..8074c98 100644
--- a/engines/mohawk/myst_graphics.h
+++ b/engines/mohawk/myst_graphics.h
@@ -29,7 +29,6 @@
 
 namespace Graphics {
 class JPEG;
-class PictDecoder;
 }
 
 namespace Mohawk {
@@ -70,7 +69,6 @@ protected:
 private:
 	MohawkEngine_Myst *_vm;
 	MystBitmap *_bmpDecoder;
-	Graphics::PictDecoder *_pictDecoder;
 	Graphics::JPEG *_jpegDecoder;
 
 	struct PictureFile {
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
index bff145a..7ecba5a 100644
--- a/engines/sci/graphics/maciconbar.cpp
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -31,8 +31,8 @@
 
 #include "common/memstream.h"
 #include "common/system.h"
-#include "graphics/pict.h"
 #include "graphics/surface.h"
+#include "graphics/decoders/pict.h"
 
 namespace Sci {
 
@@ -201,18 +201,20 @@ void GfxMacIconBar::setInventoryIcon(int16 icon) {
 }
 
 Graphics::Surface *GfxMacIconBar::loadPict(ResourceId id) {
-	Graphics::PictDecoder pictDecoder(Graphics::PixelFormat::createFormatCLUT8());
+	Graphics::PICTDecoder pictDecoder;
 	Resource *res = g_sci->getResMan()->findResource(id, false);
 
 	if (!res || res->size == 0)
 		return 0;
 
-	byte palette[256 * 3];
-	Common::SeekableReadStream *stream = new Common::MemoryReadStream(res->data, res->size);
-	Graphics::Surface *surface = pictDecoder.decodeImage(stream, palette);
-	remapColors(surface, palette);
+	Common::MemoryReadStream stream(res->data, res->size);
+	if (!pictDecoder.loadStream(stream))
+		return 0;
+
+	Graphics::Surface *surface = new Graphics::Surface();
+	surface->copyFrom(*pictDecoder.getSurface());
+	remapColors(surface, pictDecoder.getPalette());
 
-	delete stream;
 	return surface;
 }
 
@@ -221,7 +223,7 @@ Graphics::Surface *GfxMacIconBar::createImage(uint32 iconIndex, bool isSelected)
 	return loadPict(ResourceId(type, iconIndex + 1));
 }
 
-void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *palette) {
+void GfxMacIconBar::remapColors(Graphics::Surface *surf, const byte *palette) {
 	byte *pixels = (byte *)surf->pixels;
 
 	// Remap to the screen palette
diff --git a/engines/sci/graphics/maciconbar.h b/engines/sci/graphics/maciconbar.h
index 43de37a..eca10b8 100644
--- a/engines/sci/graphics/maciconbar.h
+++ b/engines/sci/graphics/maciconbar.h
@@ -61,7 +61,7 @@ private:
 
 	Graphics::Surface *loadPict(ResourceId id);
 	Graphics::Surface *createImage(uint32 iconIndex, bool isSelected);
-	void remapColors(Graphics::Surface *surf, byte *palette);
+	void remapColors(Graphics::Surface *surf, const byte *palette);
 
 	void drawIcon(uint16 index, bool selected);
 	void drawSelectedImage(uint16 index);
diff --git a/graphics/decoders/pict.cpp b/graphics/decoders/pict.cpp
new file mode 100644
index 0000000..9b28f43
--- /dev/null
+++ b/graphics/decoders/pict.cpp
@@ -0,0 +1,531 @@
+/* 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.
+ *
+ */
+
+#include "common/debug.h"
+#include "common/endian.h"
+#include "common/stream.h"
+#include "common/substream.h"
+#include "common/textconsole.h"
+
+#include "graphics/surface.h"
+#include "graphics/jpeg.h"
+#include "graphics/decoders/pict.h"
+
+namespace Graphics {
+
+// The PICT code is based off of the QuickDraw specs:
+// http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-461.html
+// http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-269.html
+
+PICTDecoder::PICTDecoder() {
+	_outputSurface = 0;
+}
+
+PICTDecoder::~PICTDecoder() {
+	destroy();
+}
+
+void PICTDecoder::destroy() {
+	if (_outputSurface) {
+		_outputSurface->free();
+		delete _outputSurface;
+		_outputSurface = 0;
+	}
+}
+
+#define OPCODE(a, b, c) _opcodes.push_back(PICTOpcode(a, &PICTDecoder::b, c))
+
+void PICTDecoder::setupOpcodesCommon() {
+	OPCODE(0x0000, o_nop, "NOP");
+	OPCODE(0x0001, o_clip, "Clip");
+	OPCODE(0x0003, o_txFont, "TxFont");
+	OPCODE(0x0004, o_txFace, "TxFace");
+	OPCODE(0x0007, o_pnSize, "PnSize");
+	OPCODE(0x000D, o_txSize, "TxSize");
+	OPCODE(0x0010, o_txRatio, "TxRatio");
+	OPCODE(0x0011, o_versionOp, "VersionOp");
+	OPCODE(0x001E, o_nop, "DefHilite");
+	OPCODE(0x0028, o_longText, "LongText");
+	OPCODE(0x00A1, o_longComment, "LongComment");
+	OPCODE(0x00FF, o_opEndPic, "OpEndPic");
+	OPCODE(0x0C00, o_headerOp, "HeaderOp");
+}
+
+void PICTDecoder::setupOpcodesNormal() {
+	setupOpcodesCommon();
+	OPCODE(0x0098, on_packBitsRect, "PackBitsRect");
+	OPCODE(0x009A, on_directBitsRect, "DirectBitsRect");
+	OPCODE(0x8200, on_compressedQuickTime, "CompressedQuickTime");
+}
+
+void PICTDecoder::setupOpcodesQuickTime() {
+	setupOpcodesCommon();
+	OPCODE(0x0098, oq_packBitsRect, "PackBitsRect");
+	OPCODE(0x009A, oq_directBitsRect, "DirectBitsRect");
+	OPCODE(0x8200, oq_compressedQuickTime, "CompressedQuickTime");
+}
+
+#undef OPCODE
+
+void PICTDecoder::o_nop(Common::SeekableReadStream &) {
+	// Nothing to do
+}
+
+void PICTDecoder::o_clip(Common::SeekableReadStream &stream) {
+	// Ignore
+	stream.skip(stream.readUint16BE() - 2);
+}
+
+void PICTDecoder::o_txFont(Common::SeekableReadStream &stream) {
+	// Ignore
+	stream.readUint16BE();
+}
+
+void PICTDecoder::o_txFace(Common::SeekableReadStream &stream) {
+	// Ignore
+	stream.readByte();
+}
+
+void PICTDecoder::o_pnSize(Common::SeekableReadStream &stream) {
+	// Ignore
+	stream.readUint16BE();
+	stream.readUint16BE();
+}
+
+void PICTDecoder::o_txSize(Common::SeekableReadStream &stream) {
+	// Ignore
+	stream.readUint16BE();
+}
+
+void PICTDecoder::o_txRatio(Common::SeekableReadStream &stream) {
+	// Ignore
+	stream.readUint16BE();
+	stream.readUint16BE();
+	stream.readUint16BE();
+	stream.readUint16BE();
+}
+
+void PICTDecoder::o_versionOp(Common::SeekableReadStream &stream) {
+	// We only support v2 extended
+	if (stream.readUint16BE() != 0x02FF)
+		error("Unknown PICT version");
+}
+
+void PICTDecoder::o_longText(Common::SeekableReadStream &stream) {
+	// Ignore
+	stream.readUint16BE();
+	stream.readUint16BE();
+	stream.skip(stream.readByte());
+}
+
+void PICTDecoder::o_longComment(Common::SeekableReadStream &stream) {
+	// Ignore
+	stream.readUint16BE();
+	stream.skip(stream.readUint16BE());
+}
+
+void PICTDecoder::o_opEndPic(Common::SeekableReadStream &stream) {
+	// We've reached the end of the picture
+	_continueParsing = false;
+}
+
+void PICTDecoder::o_headerOp(Common::SeekableReadStream &stream) {
+	// Read the basic header, but we don't really have to do anything with it
+	/* uint16 version = */ stream.readUint16BE();
+	stream.readUint16BE(); // Reserved
+	/* uint32 hRes = */ stream.readUint32BE();
+	/* uint32 vRes = */ stream.readUint32BE();
+	Common::Rect origResRect;
+	origResRect.top = stream.readUint16BE();
+	origResRect.left = stream.readUint16BE();
+	origResRect.bottom = stream.readUint16BE();
+	origResRect.right = stream.readUint16BE();
+	stream.readUint32BE(); // Reserved
+}
+
+void PICTDecoder::on_packBitsRect(Common::SeekableReadStream &stream) {
+	// Unpack data (8bpp or lower)
+	unpackBitsRect(stream, true);
+}
+
+void PICTDecoder::on_directBitsRect(Common::SeekableReadStream &stream) {
+	// Unpack data (16bpp or higher)
+	unpackBitsRect(stream, false);
+}
+
+void PICTDecoder::on_compressedQuickTime(Common::SeekableReadStream &stream) {
+	// OK, here's the fun. We get to completely change how QuickDraw draws
+	// the data in PICT files.
+
+	// Swap out the opcodes to the new ones
+	_opcodes.clear();
+	setupOpcodesQuickTime();
+
+	// We'll decode the first QuickTime data from here, but the QuickTime-specific
+	// opcodes will take over from here on out. Normal opcodes, signing off.
+	decodeCompressedQuickTime(stream);
+}
+
+void PICTDecoder::oq_packBitsRect(Common::SeekableReadStream &stream) {
+	// Skip any data here (8bpp or lower)
+	skipBitsRect(stream, true);
+}
+
+void PICTDecoder::oq_directBitsRect(Common::SeekableReadStream &stream) {
+	// Skip any data here (16bpp or higher)
+	skipBitsRect(stream, false);
+}
+
+void PICTDecoder::oq_compressedQuickTime(Common::SeekableReadStream &stream) {
+	// Just pass the data along
+	decodeCompressedQuickTime(stream);
+}
+
+bool PICTDecoder::loadStream(Common::SeekableReadStream &stream) {
+	destroy();
+
+	// Initialize opcodes to their normal state
+	_opcodes.clear();
+	setupOpcodesNormal();
+
+	_continueParsing = true;
+	memset(_palette, 0, sizeof(_palette));
+
+	uint16 fileSize = stream.readUint16BE();
+
+	// If we have no file size here, we probably have a PICT from a file
+	// and not a resource. The other two bytes are the fileSize which we
+	// don't actually need (and already read if from a resource).
+	if (!fileSize)
+		stream.seek(512 + 2);
+
+	_imageRect.top = stream.readUint16BE();
+	_imageRect.left = stream.readUint16BE();
+	_imageRect.bottom = stream.readUint16BE();
+	_imageRect.right = stream.readUint16BE();
+	_imageRect.debugPrint(0, "PICT Rect:");
+
+	// NOTE: This is only a subset of the full PICT format.
+	//     - Only V2 (Extended) Images Supported
+	//     - CompressedQuickTime (JPEG) compressed data is supported
+	//     - DirectBitsRect/PackBitsRect compressed data is supported
+	for (uint32 opNum = 0; !stream.eos() && !stream.err() && stream.pos() < stream.size() && _continueParsing; opNum++) {
+		// PICT v2 opcodes are two bytes
+		uint16 opcode = stream.readUint16BE();
+
+		if (opNum == 0 && opcode != 0x0011)
+			error("Cannot find PICT version opcode");
+		else if (opNum == 1 && opcode != 0x0C00)
+			error("Cannot find PICT header opcode");
+
+		// Since opcodes are word-aligned, we need to mark our starting
+		// position here.
+		uint32 startPos = stream.pos();
+
+		for (uint32 i = 0; i < _opcodes.size(); i++) {
+			if (_opcodes[i].op == opcode) {
+				debug(4, "Running PICT opcode %04x '%s'", opcode, _opcodes[i].desc);
+				(this->*(_opcodes[i].proc))(stream);
+				break;
+			} else if (i == _opcodes.size() - 1) {
+				// Unknown opcode; attempt to continue forward
+				warning("Unknown PICT opcode %04x", opcode);
+			}
+		}
+
+		// Align
+		stream.skip((stream.pos() - startPos) & 1);
+	}
+
+	return _outputSurface;
+}
+
+PICTDecoder::PixMap PICTDecoder::readPixMap(Common::SeekableReadStream &stream, bool hasBaseAddr) {
+	PixMap pixMap;
+	pixMap.baseAddr = hasBaseAddr ? stream.readUint32BE() : 0;
+	pixMap.rowBytes = stream.readUint16BE() & 0x3fff;
+	pixMap.bounds.top = stream.readUint16BE();
+	pixMap.bounds.left = stream.readUint16BE();
+	pixMap.bounds.bottom = stream.readUint16BE();
+	pixMap.bounds.right = stream.readUint16BE();
+	pixMap.pmVersion = stream.readUint16BE();
+	pixMap.packType = stream.readUint16BE();
+	pixMap.packSize = stream.readUint32BE();
+	pixMap.hRes = stream.readUint32BE();
+	pixMap.vRes = stream.readUint32BE();
+	pixMap.pixelType = stream.readUint16BE();
+	pixMap.pixelSize = stream.readUint16BE();
+	pixMap.cmpCount = stream.readUint16BE();
+	pixMap.cmpSize = stream.readUint16BE();
+	pixMap.planeBytes = stream.readUint32BE();
+	pixMap.pmTable = stream.readUint32BE();
+	pixMap.pmReserved = stream.readUint32BE();
+	return pixMap;
+}
+
+struct PackBitsRectData {
+	PICTDecoder::PixMap pixMap;
+	Common::Rect srcRect;
+	Common::Rect dstRect;
+	uint16 mode;
+};
+
+void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette) {
+	PackBitsRectData packBitsData;
+	packBitsData.pixMap = readPixMap(stream, !hasPalette);
+
+	// Read in the palette if there is one present
+	if (hasPalette) {
+		// See http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-267.html
+		stream.readUint32BE(); // seed
+		stream.readUint16BE(); // flags
+		uint16 colorCount = stream.readUint16BE() + 1;
+
+		for (uint32 i = 0; i < colorCount; i++) {
+			stream.readUint16BE();
+			_palette[i * 3] = stream.readUint16BE() >> 8;
+			_palette[i * 3 + 1] = stream.readUint16BE() >> 8;
+			_palette[i * 3 + 2] = stream.readUint16BE() >> 8;
+		}
+	}
+
+	packBitsData.srcRect.top = stream.readUint16BE();
+	packBitsData.srcRect.left = stream.readUint16BE();
+	packBitsData.srcRect.bottom = stream.readUint16BE();
+	packBitsData.srcRect.right = stream.readUint16BE();
+	packBitsData.dstRect.top = stream.readUint16BE();
+	packBitsData.dstRect.left = stream.readUint16BE();
+	packBitsData.dstRect.bottom = stream.readUint16BE();
+	packBitsData.dstRect.right = stream.readUint16BE();
+	packBitsData.mode = stream.readUint16BE();
+
+	uint16 width = packBitsData.srcRect.width();
+	uint16 height = packBitsData.srcRect.height();
+
+	byte bytesPerPixel = 0;
+
+	if (packBitsData.pixMap.pixelSize <= 8)
+		bytesPerPixel = 1;
+	else if (packBitsData.pixMap.pixelSize == 32)
+		bytesPerPixel = packBitsData.pixMap.cmpCount;
+	else
+		bytesPerPixel = packBitsData.pixMap.pixelSize / 8;
+
+	// Ensure we have enough space in the buffer to hold an entire line's worth of pixels
+	uint32 lineSize = MAX<int>(width * bytesPerPixel + (8 * 2 / packBitsData.pixMap.pixelSize), packBitsData.pixMap.rowBytes);
+	byte *buffer = new byte[lineSize * height];
+
+	// Read in amount of data per row
+	for (uint16 i = 0; i < packBitsData.pixMap.bounds.height(); i++) {
+		// NOTE: Compression 0 is "default". The format in SCI games is packed when 0.
+		// In the future, we may need to have something to set the  "default" packing
+		// format, but this is good for now.
+
+		if (packBitsData.pixMap.packType == 1 || packBitsData.pixMap.rowBytes < 8) { // Unpacked, Pad-Byte (on 24-bit)
+			// TODO: Finish this. Hasn't been needed (yet).
+			error("Unpacked DirectBitsRect data (padded)");
+		} else if (packBitsData.pixMap.packType == 2) { // Unpacked, No Pad-Byte (on 24-bit)
+			// TODO: Finish this. Hasn't been needed (yet).
+			error("Unpacked DirectBitsRect data (not padded)");
+		} else if (packBitsData.pixMap.packType == 0 || packBitsData.pixMap.packType > 2) { // Packed
+			uint16 byteCount = (packBitsData.pixMap.rowBytes > 250) ? stream.readUint16BE() : stream.readByte();
+			unpackBitsLine(buffer + i * width * bytesPerPixel, packBitsData.pixMap.rowBytes, stream.readStream(byteCount), packBitsData.pixMap.pixelSize, bytesPerPixel);
+		}
+	}
+
+	_outputSurface = new Graphics::Surface();
+
+	switch (bytesPerPixel) {
+	case 1:
+		// Just copy to the image
+		_outputSurface->create(width, height, PixelFormat::createFormatCLUT8());
+		memcpy(_outputSurface->pixels, buffer, _outputSurface->w * _outputSurface->h);
+		break;
+	case 2:
+		// Convert from 16-bit to whatever surface we need
+		_outputSurface->create(width, height, PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0));
+		for (uint16 y = 0; y < _outputSurface->h; y++)
+			for (uint16 x = 0; x < _outputSurface->w; x++)
+				WRITE_UINT16(_outputSurface->getBasePtr(x, y), READ_BE_UINT16(buffer + (y * _outputSurface->w + x) * 2));
+		break;
+	case 3:
+		// Convert from 24-bit (planar!) to whatever surface we need
+		_outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+		for (uint16 y = 0; y < _outputSurface->h; y++) {
+			for (uint16 x = 0; x < _outputSurface->w; x++) {
+				byte r = *(buffer + y * _outputSurface->w * 3 + x);
+				byte g = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w + x);
+				byte b = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w * 2 + x);
+				*((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.RGBToColor(r, g, b);
+			}
+		}
+		break;
+	case 4:
+		// Convert from 32-bit (planar!) to whatever surface we need
+		_outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+		for (uint16 y = 0; y < _outputSurface->h; y++) {
+			for (uint16 x = 0; x < _outputSurface->w; x++) {
+				byte r = *(buffer + y * _outputSurface->w * 4 + x);
+				byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x);
+				byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x);
+				byte a = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x);
+				*((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.ARGBToColor(r, g, b, a);
+			}
+		}
+		break;
+	}
+
+	delete[] buffer;
+}
+
+void PICTDecoder::unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel) {
+	uint32 dataDecoded = 0;
+	byte bytesPerDecode = (bytesPerPixel == 2) ? 2 : 1;
+
+	while (data->pos() < data->size() && dataDecoded < length) {
+		byte op = data->readByte();
+
+		if (op & 0x80) {
+			uint32 runSize = (op ^ 255) + 2;
+			uint16 value = (bytesPerDecode == 2) ? data->readUint16BE() : data->readByte();
+
+			for (uint32 i = 0; i < runSize; i++) {
+				if (bytesPerDecode == 2) {
+					WRITE_UINT16(out, value);
+					out += 2;
+				} else {
+					outputPixelBuffer(out, value, bitsPerPixel);
+				}
+			}
+			dataDecoded += runSize * bytesPerDecode;
+		} else {
+			uint32 runSize = op + 1;
+
+			if (bytesPerDecode == 1) {
+				for (uint32 i = 0; i < runSize; i++)
+					outputPixelBuffer(out, data->readByte(), bitsPerPixel);
+			} else {
+				for (uint32 i = 0; i < runSize; i++) {
+					WRITE_UINT16(out, data->readUint16BE());
+					out += 2;
+				}
+			}
+
+			dataDecoded += runSize * bytesPerDecode;
+		}
+	}
+
+	// HACK: Even if the data is 24-bit, rowBytes is still 32-bit
+	if (bytesPerPixel == 3)
+		dataDecoded += length / 4;
+
+	if (length != dataDecoded)
+		warning("Mismatched PackBits read (%d/%d)", dataDecoded, length);
+
+	delete data;
+}
+
+void PICTDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) {
+	switch (bitsPerPixel) {
+	case 1:
+		for (int i = 7; i >= 0; i--)
+			*out++ = (value >> i) & 1;
+		break;
+	case 2:
+		for (int i = 6; i >= 0; i -= 2)
+			*out++ = (value >> i) & 3;
+		break;
+	case 4:
+		*out++ = (value >> 4) & 0xf;
+		*out++ = value & 0xf;
+		break;
+	default:
+		*out++ = value;
+	}
+}
+
+void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette) {
+	// Step through a PackBitsRect/DirectBitsRect function
+
+	if (!hasPalette)
+		stream.readUint32BE();
+
+	uint16 rowBytes = stream.readUint16BE();
+	uint16 height = stream.readUint16BE();
+	stream.readUint16BE();
+	height = stream.readUint16BE() - height;
+	stream.readUint16BE();
+
+	uint16 packType;
+
+	// Top two bits signify PixMap vs BitMap
+	if (rowBytes & 0xC000) {
+		// PixMap
+		stream.readUint16BE();
+		packType = stream.readUint16BE();
+		stream.skip(14);
+		stream.readUint16BE(); // pixelSize
+		stream.skip(16);
+
+		if (hasPalette) {
+			stream.readUint32BE();
+			stream.readUint16BE();
+			stream.skip((stream.readUint16BE() + 1) * 8);
+		}
+
+		rowBytes &= 0x3FFF;
+	} else {
+		// BitMap
+		packType = 0;
+	}
+
+	stream.skip(18);
+
+	for (uint16 i = 0; i < height; i++) {
+		if (packType == 1 || packType == 2 || rowBytes < 8)
+			error("Unpacked PackBitsRect data");
+		else if (packType == 0 || packType > 2)
+			stream.skip((rowBytes > 250) ? stream.readUint16BE() : stream.readByte());
+	}
+}
+
+// Compressed QuickTime details can be found here:
+// http://developer.apple.com/legacy/mac/library/#documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/B-Chapter/2TheImageCompression.html
+// http://developer.apple.com/legacy/mac/library/#documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/F-Chapter/6WorkingwiththeImage.html
+void PICTDecoder::decodeCompressedQuickTime(Common::SeekableReadStream &stream) {
+	JPEG jpeg;
+
+	uint32 dataSize = stream.readUint32BE();
+	uint32 startPos = stream.pos();
+
+	Common::SeekableReadStream *jpegStream = new Common::SeekableSubReadStream(&stream, stream.pos() + 156, stream.pos() + dataSize);
+
+	if (!jpeg.read(jpegStream))
+		error("PICTDecoder::decodeCompressedQuickTime(): Could not decode JPEG data");
+
+	_outputSurface = jpeg.getSurface(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+
+	stream.seek(startPos + dataSize);
+	delete jpegStream;
+}
+
+} // End of namespace Graphics
diff --git a/graphics/decoders/pict.h b/graphics/decoders/pict.h
new file mode 100644
index 0000000..b1e45a6
--- /dev/null
+++ b/graphics/decoders/pict.h
@@ -0,0 +1,130 @@
+/* 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 GRAPHICS_PICT_H
+#define GRAPHICS_PICT_H
+
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/scummsys.h"
+
+#include "graphics/decoders/image_decoder.h"
+#include "graphics/pixelformat.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Graphics {
+
+struct Surface;
+
+#define DECLARE_OPCODE(x) void x(Common::SeekableReadStream &stream)
+
+class PICTDecoder : public ImageDecoder {
+public:
+	PICTDecoder();
+	~PICTDecoder();
+
+	// ImageDecoder API
+	bool loadStream(Common::SeekableReadStream &stream);
+	void destroy();
+	const Surface *getSurface() const { return _outputSurface; }
+	const byte *getPalette() const { return _palette; }
+
+	struct PixMap {
+		uint32 baseAddr;
+		uint16 rowBytes;
+		Common::Rect bounds;
+		uint16 pmVersion;
+		uint16 packType;
+		uint32 packSize;
+		uint32 hRes;
+		uint32 vRes;
+		uint16 pixelType;
+		uint16 pixelSize;
+		uint16 cmpCount;
+		uint16 cmpSize;
+		uint32 planeBytes;
+		uint32 pmTable;
+		uint32 pmReserved;
+	};
+
+	static PixMap readPixMap(Common::SeekableReadStream &stream, bool hasBaseAddr = true);
+
+private:
+	Common::Rect _imageRect;
+	byte _palette[256 * 3];
+	Graphics::Surface *_outputSurface;
+	bool _continueParsing;
+
+	// Utility Functions
+	void unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette);
+	void unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *stream, byte bitsPerPixel, byte bytesPerPixel);
+	void skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette);
+	void decodeCompressedQuickTime(Common::SeekableReadStream &stream);
+	void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel);
+
+	// Opcodes
+	typedef void (PICTDecoder::*OpcodeProcPICT)(Common::SeekableReadStream &stream);
+	struct PICTOpcode {
+		PICTOpcode() { op = 0; proc = 0; desc = 0; }
+		PICTOpcode(uint16 o, OpcodeProcPICT p, const char *d) { op = o; proc = p; desc = d; }
+		uint16 op;
+		OpcodeProcPICT proc;
+		const char *desc;
+	};
+	Common::Array<PICTOpcode> _opcodes;
+
+	// Common Opcodes
+	void setupOpcodesCommon();
+	DECLARE_OPCODE(o_nop);
+	DECLARE_OPCODE(o_clip);
+	DECLARE_OPCODE(o_txFont);
+	DECLARE_OPCODE(o_txFace);
+	DECLARE_OPCODE(o_pnSize);
+	DECLARE_OPCODE(o_txSize);
+	DECLARE_OPCODE(o_txRatio);
+	DECLARE_OPCODE(o_versionOp);
+	DECLARE_OPCODE(o_longText);
+	DECLARE_OPCODE(o_longComment);
+	DECLARE_OPCODE(o_opEndPic);
+	DECLARE_OPCODE(o_headerOp);
+
+	// Regular-mode Opcodes
+	void setupOpcodesNormal();
+	DECLARE_OPCODE(on_packBitsRect);
+	DECLARE_OPCODE(on_directBitsRect);
+	DECLARE_OPCODE(on_compressedQuickTime);
+
+	// QuickTime-mode Opcodes
+	void setupOpcodesQuickTime();
+	DECLARE_OPCODE(oq_packBitsRect);
+	DECLARE_OPCODE(oq_directBitsRect);
+	DECLARE_OPCODE(oq_compressedQuickTime);
+};
+
+#undef DECLARE_OPCODE
+
+} // End of namespace Graphics
+
+#endif
diff --git a/graphics/module.mk b/graphics/module.mk
index 5c1d313..08f6d0b 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -14,7 +14,6 @@ MODULE_OBJS := \
 	iff.o \
 	jpeg.o \
 	maccursor.o \
-	pict.o \
 	png.o \
 	primitives.o \
 	scaler.o \
@@ -26,7 +25,8 @@ MODULE_OBJS := \
 	VectorRendererSpec.o \
 	wincursor.o \
 	yuv_to_rgb.o \
-	decoders/bmp.o
+	decoders/bmp.o \
+	decoders/pict.o
 
 ifdef USE_SCALERS
 MODULE_OBJS += \
diff --git a/graphics/pict.cpp b/graphics/pict.cpp
deleted file mode 100644
index 872f2f2..0000000
--- a/graphics/pict.cpp
+++ /dev/null
@@ -1,572 +0,0 @@
-/* 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.
- *
- */
-
-#include "common/debug.h"
-#include "common/endian.h"
-#include "common/stream.h"
-#include "common/substream.h"
-#include "common/textconsole.h"
-
-#include "graphics/jpeg.h"
-#include "graphics/pict.h"
-#include "graphics/surface.h"
-
-namespace Graphics {
-
-// The PICT code is based off of the QuickDraw specs:
-// http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-461.html
-// http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-269.html
-
-PictDecoder::PictDecoder(PixelFormat pixelFormat) {
-	_jpeg = new JPEG();
-	_pixelFormat = pixelFormat;
-}
-
-PictDecoder::~PictDecoder() {
-	delete _jpeg;
-}
-
-#define OPCODE(a, b, c) _opcodes.push_back(PICTOpcode(a, &PictDecoder::b, c))
-
-void PictDecoder::setupOpcodesCommon() {
-	OPCODE(0x0000, o_nop, "NOP");
-	OPCODE(0x0001, o_clip, "Clip");
-	OPCODE(0x0003, o_txFont, "TxFont");
-	OPCODE(0x0004, o_txFace, "TxFace");
-	OPCODE(0x0007, o_pnSize, "PnSize");
-	OPCODE(0x000D, o_txSize, "TxSize");
-	OPCODE(0x0010, o_txRatio, "TxRatio");
-	OPCODE(0x0011, o_versionOp, "VersionOp");
-	OPCODE(0x001E, o_nop, "DefHilite");
-	OPCODE(0x0028, o_longText, "LongText");
-	OPCODE(0x00A1, o_longComment, "LongComment");
-	OPCODE(0x00FF, o_opEndPic, "OpEndPic");
-	OPCODE(0x0C00, o_headerOp, "HeaderOp");
-}
-
-void PictDecoder::setupOpcodesNormal() {
-	setupOpcodesCommon();
-	OPCODE(0x0098, on_packBitsRect, "PackBitsRect");
-	OPCODE(0x009A, on_directBitsRect, "DirectBitsRect");
-	OPCODE(0x8200, on_compressedQuickTime, "CompressedQuickTime");
-}
-
-void PictDecoder::setupOpcodesQuickTime() {
-	setupOpcodesCommon();
-	OPCODE(0x0098, oq_packBitsRect, "PackBitsRect");
-	OPCODE(0x009A, oq_directBitsRect, "DirectBitsRect");
-	OPCODE(0x8200, oq_compressedQuickTime, "CompressedQuickTime");
-}
-
-#undef OPCODE
-
-void PictDecoder::o_nop(Common::SeekableReadStream *) {
-	// Nothing to do
-}
-
-void PictDecoder::o_clip(Common::SeekableReadStream *stream) {
-	// Ignore
-	stream->skip(stream->readUint16BE() - 2);
-}
-
-void PictDecoder::o_txFont(Common::SeekableReadStream *stream) {
-	// Ignore
-	stream->readUint16BE();
-}
-
-void PictDecoder::o_txFace(Common::SeekableReadStream *stream) {
-	// Ignore
-	stream->readByte();
-}
-
-void PictDecoder::o_pnSize(Common::SeekableReadStream *stream) {
-	// Ignore
-	stream->readUint16BE();
-	stream->readUint16BE();
-}
-
-void PictDecoder::o_txSize(Common::SeekableReadStream *stream) {
-	// Ignore
-	stream->readUint16BE();
-}
-
-void PictDecoder::o_txRatio(Common::SeekableReadStream *stream) {
-	// Ignore
-	stream->readUint16BE();
-	stream->readUint16BE();
-	stream->readUint16BE();
-	stream->readUint16BE();
-}
-
-void PictDecoder::o_versionOp(Common::SeekableReadStream *stream) {
-	// We only support v2 extended
-	if (stream->readUint16BE() != 0x02FF)
-		error("Unknown PICT version");
-}
-
-void PictDecoder::o_longText(Common::SeekableReadStream *stream) {
-	// Ignore
-	stream->readUint16BE();
-	stream->readUint16BE();
-	stream->skip(stream->readByte());
-}
-
-void PictDecoder::o_longComment(Common::SeekableReadStream *stream) {
-	// Ignore
-	stream->readUint16BE();
-	stream->skip(stream->readUint16BE());
-}
-
-void PictDecoder::o_opEndPic(Common::SeekableReadStream *stream) {
-	// We've reached the end of the picture
-	_continueParsing = false;
-}
-
-void PictDecoder::o_headerOp(Common::SeekableReadStream *stream) {
-	// Read the basic header, but we don't really have to do anything with it
-	/* uint16 version = */ stream->readUint16BE();
-	stream->readUint16BE(); // Reserved
-	/* uint32 hRes = */ stream->readUint32BE();
-	/* uint32 vRes = */ stream->readUint32BE();
-	Common::Rect origResRect;
-	origResRect.top = stream->readUint16BE();
-	origResRect.left = stream->readUint16BE();
-	origResRect.bottom = stream->readUint16BE();
-	origResRect.right = stream->readUint16BE();
-	stream->readUint32BE(); // Reserved
-}
-
-void PictDecoder::on_packBitsRect(Common::SeekableReadStream *stream) {
-	// Unpack data (8bpp or lower)
-	unpackBitsRect(stream, true);
-}
-
-void PictDecoder::on_directBitsRect(Common::SeekableReadStream *stream) {
-	// Unpack data (16bpp or higher)
-	unpackBitsRect(stream, false);
-}
-
-void PictDecoder::on_compressedQuickTime(Common::SeekableReadStream *stream) {
-	// OK, here's the fun. We get to completely change how QuickDraw draws
-	// the data in PICT files.
-
-	// Swap out the opcodes to the new ones
-	_opcodes.clear();
-	setupOpcodesQuickTime();
-
-	// We set up the surface for JPEG here too
-	if (!_outputSurface)
-		_outputSurface = new Graphics::Surface();
-	_outputSurface->create(_imageRect.width(), _imageRect.height(), _pixelFormat);
-
-	// We'll decode the first QuickTime data from here, but the QuickTime-specific
-	// opcodes will take over from here on out. Normal opcodes, signing off.
-	decodeCompressedQuickTime(stream);
-}
-
-void PictDecoder::oq_packBitsRect(Common::SeekableReadStream *stream) {
-	// Skip any data here (8bpp or lower)
-	skipBitsRect(stream, true);
-}
-
-void PictDecoder::oq_directBitsRect(Common::SeekableReadStream *stream) {
-	// Skip any data here (16bpp or higher)
-	skipBitsRect(stream, false);
-}
-
-void PictDecoder::oq_compressedQuickTime(Common::SeekableReadStream *stream) {
-	// Just pass the data along
-	decodeCompressedQuickTime(stream);
-}
-
-Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *palette) {
-	assert(stream);
-
-	// Initialize opcodes to their normal state
-	_opcodes.clear();
-	setupOpcodesNormal();
-
-	_outputSurface = 0;
-	_continueParsing = true;
-	memset(_palette, 0, sizeof(_palette));
-
-	uint16 fileSize = stream->readUint16BE();
-
-	// If we have no file size here, we probably have a PICT from a file
-	// and not a resource. The other two bytes are the fileSize which we
-	// don't actually need (and already read if from a resource).
-	if (!fileSize)
-		stream->seek(512 + 2);
-
-	_imageRect.top = stream->readUint16BE();
-	_imageRect.left = stream->readUint16BE();
-	_imageRect.bottom = stream->readUint16BE();
-	_imageRect.right = stream->readUint16BE();
-	_imageRect.debugPrint(0, "PICT Rect:");
-
-	// NOTE: This is only a subset of the full PICT format.
-	//     - Only V2 (Extended) Images Supported
-	//     - CompressedQuickTime (JPEG) compressed data is supported
-	//     - DirectBitsRect/PackBitsRect compressed data is supported
-	for (uint32 opNum = 0; !stream->eos() && !stream->err() && stream->pos() < stream->size() && _continueParsing; opNum++) {
-		// PICT v2 opcodes are two bytes
-		uint16 opcode = stream->readUint16BE();
-
-		if (opNum == 0 && opcode != 0x0011)
-			error("Cannot find PICT version opcode");
-		else if (opNum == 1 && opcode != 0x0C00)
-			error("Cannot find PICT header opcode");
-
-		// Since opcodes are word-aligned, we need to mark our starting
-		// position here.
-		uint32 startPos = stream->pos();
-
-		for (uint32 i = 0; i < _opcodes.size(); i++) {
-			if (_opcodes[i].op == opcode) {
-				debug(4, "Running PICT opcode %04x '%s'", opcode, _opcodes[i].desc);
-				(this->*(_opcodes[i].proc))(stream);
-				break;
-			} else if (i == _opcodes.size() - 1) {
-				// Unknown opcode; attempt to continue forward
-				warning("Unknown PICT opcode %04x", opcode);
-			}
-		}
-
-		// Align
-		stream->skip((stream->pos() - startPos) & 1);
-	}
-
-	// If we got a palette throughout this nonsense, go and grab it
-	if (palette)
-		memcpy(palette, _palette, 256 * 3);
-
-	return _outputSurface;
-}
-
-PictDecoder::PixMap PictDecoder::readPixMap(Common::SeekableReadStream *stream, bool hasBaseAddr) {
-	PixMap pixMap;
-	pixMap.baseAddr = hasBaseAddr ? stream->readUint32BE() : 0;
-	pixMap.rowBytes = stream->readUint16BE() & 0x3fff;
-	pixMap.bounds.top = stream->readUint16BE();
-	pixMap.bounds.left = stream->readUint16BE();
-	pixMap.bounds.bottom = stream->readUint16BE();
-	pixMap.bounds.right = stream->readUint16BE();
-	pixMap.pmVersion = stream->readUint16BE();
-	pixMap.packType = stream->readUint16BE();
-	pixMap.packSize = stream->readUint32BE();
-	pixMap.hRes = stream->readUint32BE();
-	pixMap.vRes = stream->readUint32BE();
-	pixMap.pixelType = stream->readUint16BE();
-	pixMap.pixelSize = stream->readUint16BE();
-	pixMap.cmpCount = stream->readUint16BE();
-	pixMap.cmpSize = stream->readUint16BE();
-	pixMap.planeBytes = stream->readUint32BE();
-	pixMap.pmTable = stream->readUint32BE();
-	pixMap.pmReserved = stream->readUint32BE();
-	return pixMap;
-}
-
-struct PackBitsRectData {
-	PictDecoder::PixMap pixMap;
-	Common::Rect srcRect;
-	Common::Rect dstRect;
-	uint16 mode;
-};
-
-void PictDecoder::unpackBitsRect(Common::SeekableReadStream *stream, bool hasPalette) {
-	static const PixelFormat directBitsFormat16 = PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
-
-	PackBitsRectData packBitsData;
-	packBitsData.pixMap = readPixMap(stream, !hasPalette);
-
-	// Read in the palette if there is one present
-	if (hasPalette) {
-		// See http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-267.html
-		stream->readUint32BE(); // seed
-		stream->readUint16BE(); // flags
-		uint16 colorCount = stream->readUint16BE() + 1;
-
-		for (uint32 i = 0; i < colorCount; i++) {
-			stream->readUint16BE();
-			_palette[i * 3] = stream->readUint16BE() >> 8;
-			_palette[i * 3 + 1] = stream->readUint16BE() >> 8;
-			_palette[i * 3 + 2] = stream->readUint16BE() >> 8;
-		}
-	}
-
-	packBitsData.srcRect.top = stream->readUint16BE();
-	packBitsData.srcRect.left = stream->readUint16BE();
-	packBitsData.srcRect.bottom = stream->readUint16BE();
-	packBitsData.srcRect.right = stream->readUint16BE();
-	packBitsData.dstRect.top = stream->readUint16BE();
-	packBitsData.dstRect.left = stream->readUint16BE();
-	packBitsData.dstRect.bottom = stream->readUint16BE();
-	packBitsData.dstRect.right = stream->readUint16BE();
-	packBitsData.mode = stream->readUint16BE();
-
-	uint16 width = packBitsData.srcRect.width();
-	uint16 height = packBitsData.srcRect.height();
-
-	byte bytesPerPixel = 0;
-
-	if (packBitsData.pixMap.pixelSize <= 8)
-		bytesPerPixel = 1;
-	else if (packBitsData.pixMap.pixelSize == 32)
-		bytesPerPixel = packBitsData.pixMap.cmpCount;
-	else
-		bytesPerPixel = packBitsData.pixMap.pixelSize / 8;
-
-	_outputSurface = new Graphics::Surface();
-	_outputSurface->create(width, height, (bytesPerPixel == 1) ? PixelFormat::createFormatCLUT8() : _pixelFormat);
-
-	// Ensure we have enough space in the buffer to hold an entire line's worth of pixels
-	uint32 lineSize = MAX<int>(width * bytesPerPixel + (8 * 2 / packBitsData.pixMap.pixelSize), packBitsData.pixMap.rowBytes);
-	byte *buffer = new byte[lineSize * height];
-
-	// Read in amount of data per row
-	for (uint16 i = 0; i < packBitsData.pixMap.bounds.height(); i++) {
-		// NOTE: Compression 0 is "default". The format in SCI games is packed when 0.
-		// In the future, we may need to have something to set the  "default" packing
-		// format, but this is good for now.
-
-		if (packBitsData.pixMap.packType == 1 || packBitsData.pixMap.rowBytes < 8) { // Unpacked, Pad-Byte (on 24-bit)
-			// TODO: Finish this. Hasn't been needed (yet).
-			error("Unpacked DirectBitsRect data (padded)");
-		} else if (packBitsData.pixMap.packType == 2) { // Unpacked, No Pad-Byte (on 24-bit)
-			// TODO: Finish this. Hasn't been needed (yet).
-			error("Unpacked DirectBitsRect data (not padded)");
-		} else if (packBitsData.pixMap.packType == 0 || packBitsData.pixMap.packType > 2) { // Packed
-			uint16 byteCount = (packBitsData.pixMap.rowBytes > 250) ? stream->readUint16BE() : stream->readByte();
-			unpackBitsLine(buffer + i * _outputSurface->w * bytesPerPixel, packBitsData.pixMap.rowBytes, stream->readStream(byteCount), packBitsData.pixMap.pixelSize, bytesPerPixel);
-		}
-	}
-
-	switch (bytesPerPixel) {
-	case 1:
-		// Just copy to the image
-		memcpy(_outputSurface->pixels, buffer, _outputSurface->w * _outputSurface->h);
-		break;
-	case 2:
-		// Convert from 16-bit to whatever surface we need
-		for (uint16 y = 0; y < _outputSurface->h; y++) {
-			for (uint16 x = 0; x < _outputSurface->w; x++) {
-				byte r = 0, g = 0, b = 0;
-				uint32 color = READ_BE_UINT16(buffer + (y * _outputSurface->w + x) * bytesPerPixel);
-				directBitsFormat16.colorToRGB(color, r, g, b);
-				if (_pixelFormat.bytesPerPixel == 2)
-					*((uint16 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
-				else
-					*((uint32 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
-			}
-		}
-		break;
-	case 3:
-		// Convert from 24-bit (planar!) to whatever surface we need
-		for (uint16 y = 0; y < _outputSurface->h; y++) {
-			for (uint16 x = 0; x < _outputSurface->w; x++) {
-				byte r = *(buffer + y * _outputSurface->w * 3 + x);
-				byte g = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w + x);
-				byte b = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w * 2 + x);
-				if (_pixelFormat.bytesPerPixel == 2)
-					*((uint16 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
-				else
-					*((uint32 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b);
-			}
-		}
-		break;
-	case 4:
-		// Convert from 32-bit (planar!) to whatever surface we need
-		for (uint16 y = 0; y < _outputSurface->h; y++) {
-			for (uint16 x = 0; x < _outputSurface->w; x++) {
-				byte r = *(buffer + y * _outputSurface->w * 4 + x);
-				byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x);
-				byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x);
-				byte a = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x);
-				if (_pixelFormat.bytesPerPixel == 2)
-					*((uint16 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.ARGBToColor(r, g, b, a);
-				else
-					*((uint32 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.ARGBToColor(r, g, b, a);
-			}
-		}
-		break;
-	}
-
-	delete[] buffer;
-}
-
-void PictDecoder::unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel) {
-	uint32 dataDecoded = 0;
-	byte bytesPerDecode = (bytesPerPixel == 2) ? 2 : 1;
-
-	while (data->pos() < data->size() && dataDecoded < length) {
-		byte op = data->readByte();
-
-		if (op & 0x80) {
-			uint32 runSize = (op ^ 255) + 2;
-			uint16 value = (bytesPerDecode == 2) ? data->readUint16BE() : data->readByte();
-
-			for (uint32 i = 0; i < runSize; i++) {
-				if (bytesPerDecode == 2) {
-					WRITE_BE_UINT16(out, value);
-					out += 2;
-				} else {
-					outputPixelBuffer(out, value, bitsPerPixel);
-				}
-			}
-			dataDecoded += runSize * bytesPerDecode;
-		} else {
-			uint32 runSize = (op + 1) * bytesPerDecode;
-
-			for (uint32 i = 0; i < runSize; i++)
-				outputPixelBuffer(out, data->readByte(), bitsPerPixel);
-
-			dataDecoded += runSize;
-		}
-	}
-
-	// HACK: Even if the data is 24-bit, rowBytes is still 32-bit
-	if (bytesPerPixel == 3)
-		dataDecoded += length / 4;
-
-	if (length != dataDecoded)
-		warning("Mismatched PackBits read (%d/%d)", dataDecoded, length);
-
-	delete data;
-}
-
-void PictDecoder::skipBitsRect(Common::SeekableReadStream *stream, bool hasPalette) {
-	// Step through a PackBitsRect/DirectBitsRect function
-
-	if (!hasPalette)
-		stream->readUint32BE();
-
-	uint16 rowBytes = stream->readUint16BE();
-	uint16 height = stream->readUint16BE();
-	stream->readUint16BE();
-	height = stream->readUint16BE() - height;
-	stream->readUint16BE();
-
-	uint16 packType;
-
-	// Top two bits signify PixMap vs BitMap
-	if (rowBytes & 0xC000) {
-		// PixMap
-		stream->readUint16BE();
-		packType = stream->readUint16BE();
-		stream->skip(14);
-		stream->readUint16BE(); // pixelSize
-		stream->skip(16);
-
-		if (hasPalette) {
-			stream->readUint32BE();
-			stream->readUint16BE();
-			stream->skip((stream->readUint16BE() + 1) * 8);
-		}
-
-		rowBytes &= 0x3FFF;
-	} else {
-		// BitMap
-		packType = 0;
-	}
-
-	stream->skip(18);
-
-	for (uint16 i = 0; i < height; i++) {
-		if (packType == 1 || packType == 2 || rowBytes < 8)
-			error("Unpacked PackBitsRect data");
-		else if (packType == 0 || packType > 2)
-			stream->skip((rowBytes > 250) ? stream->readUint16BE() : stream->readByte());
-	}
-}
-
-void PictDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) {
-	switch (bitsPerPixel) {
-	case 1:
-		for (int i = 7; i >= 0; i--)
-			*out++ = (value >> i) & 1;
-		break;
-	case 2:
-		for (int i = 6; i >= 0; i -= 2)
-			*out++ = (value >> i) & 3;
-		break;
-	case 4:
-		*out++ = (value >> 4) & 0xf;
-		*out++ = value & 0xf;
-		break;
-	default:
-		*out++ = value;
-	}
-}
-
-// Compressed QuickTime details can be found here:
-// http://developer.apple.com/legacy/mac/library/#documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/B-Chapter/2TheImageCompression.html
-// http://developer.apple.com/legacy/mac/library/#documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/F-Chapter/6WorkingwiththeImage.html
-
-void PictDecoder::decodeCompressedQuickTime(Common::SeekableReadStream *stream) {
-	// First, read all the fields from the opcode
-	uint32 dataSize = stream->readUint32BE();
-	uint32 startPos = stream->pos();
-
-	/* uint16 version = */ stream->readUint16BE();
-
-	// Read in the display matrix
-	uint32 matrix[3][3];
-	for (uint32 i = 0; i < 3; i++)
-		for (uint32 j = 0; j < 3; j++)
-			matrix[i][j] = stream->readUint32BE();
-
-	// We currently only support offseting images vertically from the matrix
-	uint16 xOffset = 0;
-	uint16 yOffset = matrix[2][1] >> 16;
-
-	uint32 matteSize = stream->readUint32BE();
-	stream->skip(8); // matte rect
-	/* uint16 transferMode = */ stream->readUint16BE();
-	stream->skip(8); // src rect
-	/* uint32 accuracy = */ stream->readUint32BE();
-	uint32 maskSize = stream->readUint32BE();
-
-	// Skip the matte and mask
-	stream->skip(matteSize + maskSize);
-	
-	// Now we've reached the image descriptor, so read the relevant data from that
-	uint32 idStart = stream->pos();
-	uint32 idSize = stream->readUint32BE();
-	stream->skip(40); // miscellaneous stuff
-	uint32 jpegSize = stream->readUint32BE();
-	stream->skip(idSize - (stream->pos() - idStart)); // more useless stuff
-
-	Common::SeekableReadStream *jpegStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + jpegSize);
-
-	if (!_jpeg->read(jpegStream))
-		error("PictDecoder::decodeCompressedQuickTime(): Could not decode JPEG data");
-
-	Graphics::Surface *jpegSurface = _jpeg->getSurface(_pixelFormat);
-
-	for (uint16 y = 0; y < jpegSurface->h; y++)
-		memcpy(_outputSurface->getBasePtr(0 + xOffset, y + yOffset), jpegSurface->getBasePtr(0, y), jpegSurface->w * _pixelFormat.bytesPerPixel);
-
-	stream->seek(startPos + dataSize);
-	delete jpegStream;
-}
-
-} // End of namespace Graphics
diff --git a/graphics/pict.h b/graphics/pict.h
deleted file mode 100644
index b426c6e..0000000
--- a/graphics/pict.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/* 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 GRAPHICS_PICT_H
-#define GRAPHICS_PICT_H
-
-#include "common/array.h"
-#include "common/rect.h"
-#include "common/scummsys.h"
-
-#include "graphics/pixelformat.h"
-
-namespace Common {
-class SeekableReadStream;
-}
-
-namespace Graphics {
-
-class JPEG;
-struct Surface;
-
-#define DECLARE_OPCODE(x) void x(Common::SeekableReadStream *stream)
-
-class PictDecoder {
-public:
-	PictDecoder(Graphics::PixelFormat pixelFormat);
-	~PictDecoder();
-	Surface *decodeImage(Common::SeekableReadStream *stream, byte *palette = 0);
-
-	struct PixMap {
-		uint32 baseAddr;
-		uint16 rowBytes;
-		Common::Rect bounds;
-		uint16 pmVersion;
-		uint16 packType;
-		uint32 packSize;
-		uint32 hRes;
-		uint32 vRes;
-		uint16 pixelType;
-		uint16 pixelSize;
-		uint16 cmpCount;
-		uint16 cmpSize;
-		uint32 planeBytes;
-		uint32 pmTable;
-		uint32 pmReserved;
-	};
-
-	static PixMap readPixMap(Common::SeekableReadStream *stream, bool hasBaseAddr = true);
-
-private:
-	Common::Rect _imageRect;
-	PixelFormat _pixelFormat;
-	JPEG *_jpeg;
-	byte _palette[256 * 3];
-	bool _isPaletted;
-	Graphics::Surface *_outputSurface;
-	bool _continueParsing;
-
-	// Utility Functions
-	void unpackBitsRect(Common::SeekableReadStream *stream, bool hasPalette);
-	void unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel);
-	void skipBitsRect(Common::SeekableReadStream *stream, bool hasPalette);
-	void decodeCompressedQuickTime(Common::SeekableReadStream *stream);
-	void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel);
-
-	// Opcodes
-	typedef void (PictDecoder::*OpcodeProcPICT)(Common::SeekableReadStream *stream);
-	struct PICTOpcode {
-		PICTOpcode() { op = 0; proc = 0; desc = 0; }
-		PICTOpcode(uint16 o, OpcodeProcPICT p, const char *d) { op = o; proc = p; desc = d; }
-		uint16 op;
-		OpcodeProcPICT proc;
-		const char *desc;
-	};
-	Common::Array<PICTOpcode> _opcodes;
-
-	// Common Opcodes
-	void setupOpcodesCommon();
-	DECLARE_OPCODE(o_nop);
-	DECLARE_OPCODE(o_clip);
-	DECLARE_OPCODE(o_txFont);
-	DECLARE_OPCODE(o_txFace);
-	DECLARE_OPCODE(o_pnSize);
-	DECLARE_OPCODE(o_txSize);
-	DECLARE_OPCODE(o_txRatio);
-	DECLARE_OPCODE(o_versionOp);
-	DECLARE_OPCODE(o_longText);
-	DECLARE_OPCODE(o_longComment);
-	DECLARE_OPCODE(o_opEndPic);
-	DECLARE_OPCODE(o_headerOp);
-
-	// Regular-mode Opcodes
-	void setupOpcodesNormal();
-	DECLARE_OPCODE(on_packBitsRect);
-	DECLARE_OPCODE(on_directBitsRect);
-	DECLARE_OPCODE(on_compressedQuickTime);
-
-	// QuickTime-mode Opcodes
-	void setupOpcodesQuickTime();
-	DECLARE_OPCODE(oq_packBitsRect);
-	DECLARE_OPCODE(oq_directBitsRect);
-	DECLARE_OPCODE(oq_compressedQuickTime);
-};
-
-#undef DECLARE_OPCODE
-
-} // End of namespace Graphics
-
-#endif


Commit: 765a8704454d705acc9ce5aa7a90c1c3079fb989
    https://github.com/scummvm/scummvm/commit/765a8704454d705acc9ce5aa7a90c1c3079fb989
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:48-07:00

Commit Message:
GRAPHICS: Convert JPEG to the ImageDecoder API

Changed paths:
  A graphics/decoders/jpeg.cpp
  A graphics/decoders/jpeg.h
  R graphics/jpeg.cpp
  R graphics/jpeg.h
    engines/groovie/roq.cpp
    engines/mohawk/myst_graphics.cpp
    engines/mohawk/myst_graphics.h
    graphics/decoders/pict.cpp
    graphics/module.mk
    video/codecs/mjpeg.cpp
    video/codecs/mjpeg.h



diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp
index 53f335c..8da8671 100644
--- a/engines/groovie/roq.cpp
+++ b/engines/groovie/roq.cpp
@@ -30,8 +30,8 @@
 #include "common/debug.h"
 #include "common/textconsole.h"
 
-#include "graphics/jpeg.h"
 #include "graphics/palette.h"
+#include "graphics/decoders/jpeg.h"
 
 #ifdef USE_RGB_COLOR
 // Required for the YUV to RGB conversion
@@ -435,8 +435,8 @@ bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
 
 	warning("Groovie::ROQ: JPEG frame (unfinshed)");
 
-	Graphics::JPEG *jpg = new Graphics::JPEG();
-	jpg->read(_file);
+	Graphics::JPEGDecoder *jpg = new Graphics::JPEGDecoder();
+	jpg->loadStream(*_file);
 	byte *y = (byte *)jpg->getComponent(1)->getBasePtr(0, 0);
 	byte *u = (byte *)jpg->getComponent(2)->getBasePtr(0, 0);
 	byte *v = (byte *)jpg->getComponent(3)->getBasePtr(0, 0);
diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp
index 86eb4f2..484e49d 100644
--- a/engines/mohawk/myst_graphics.cpp
+++ b/engines/mohawk/myst_graphics.cpp
@@ -28,7 +28,7 @@
 #include "common/system.h"
 #include "common/textconsole.h"
 #include "engines/util.h"
-#include "graphics/jpeg.h"
+#include "graphics/decoders/jpeg.h"
 #include "graphics/decoders/pict.h"
 
 namespace Mohawk {
@@ -49,12 +49,6 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
 	if (_pixelFormat.bytesPerPixel == 1)
 		error("Myst requires greater than 256 colors to run");
 
-	if (_vm->getFeatures() & GF_ME) {
-		_jpegDecoder = new Graphics::JPEG();
-	} else {
-		_jpegDecoder = NULL;
-	}
-
 	_pictureFile.entries = NULL;
 
 	// Initialize our buffer
@@ -67,7 +61,6 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
 
 MystGraphics::~MystGraphics() {
 	delete _bmpDecoder;
-	delete _jpegDecoder;
 	delete[] _pictureFile.entries;
 
 	_backBuffer->free();
@@ -127,13 +120,13 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
 		for (uint32 i = 0; i < _pictureFile.pictureCount; i++)
 			if (_pictureFile.entries[i].id == id) {
 				if (_pictureFile.entries[i].type == 0) {
-					Common::SeekableReadStream *stream = new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size);
+					Graphics::JPEGDecoder jpeg;
+					Common::SeekableSubReadStream subStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size);
 
-					if (!_jpegDecoder->read(stream))
+					if (!jpeg.loadStream(subStream))
 						error("Could not decode Myst ME Mac JPEG");
 
-					mhkSurface = new MohawkSurface(_jpegDecoder->getSurface(_pixelFormat));
-					delete stream;
+					mhkSurface = new MohawkSurface(jpeg.getSurface()->convertTo(_pixelFormat));
 				} else if (_pictureFile.entries[i].type == 1) {
 					Graphics::PICTDecoder pict;
 					Common::SeekableSubReadStream subStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size);
diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h
index 8074c98..20fd46c 100644
--- a/engines/mohawk/myst_graphics.h
+++ b/engines/mohawk/myst_graphics.h
@@ -27,10 +27,6 @@
 
 #include "common/file.h"
 
-namespace Graphics {
-class JPEG;
-}
-
 namespace Mohawk {
 
 class MystBitmap;
@@ -69,7 +65,6 @@ protected:
 private:
 	MohawkEngine_Myst *_vm;
 	MystBitmap *_bmpDecoder;
-	Graphics::JPEG *_jpegDecoder;
 
 	struct PictureFile {
 		uint32 pictureCount;
diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp
new file mode 100644
index 0000000..0cd2388
--- /dev/null
+++ b/graphics/decoders/jpeg.cpp
@@ -0,0 +1,787 @@
+/* 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.
+ *
+ */
+
+#include "graphics/conversion.h"
+#include "graphics/pixelformat.h"
+#include "graphics/decoders/jpeg.h"
+
+#include "common/debug.h"
+#include "common/endian.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+namespace Graphics {
+
+// Order used to traverse the quantization tables
+static const uint8 _zigZagOrder[64] = {
+	0,   1,  8, 16,  9,  2,  3, 10,
+	17, 24, 32, 25, 18, 11,  4,  5,
+	12, 19, 26, 33, 40, 48, 41, 34,
+	27, 20, 13,  6,  7, 14, 21, 28,
+	35, 42, 49, 56, 57, 50, 43, 36,
+	29, 22, 15, 23, 30, 37, 44, 51,
+	58, 59, 52, 45, 38, 31, 39, 46,
+	53, 60, 61, 54, 47, 55, 62, 63
+};
+
+JPEGDecoder::JPEGDecoder() : ImageDecoder(),
+	_stream(NULL), _w(0), _h(0), _numComp(0), _components(NULL), _numScanComp(0),
+	_scanComp(NULL), _currentComp(NULL), _rgbSurface(0) {
+
+	// Initialize the quantization tables
+	for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++)
+		_quant[i] = NULL;
+
+	// Initialize the Huffman tables
+	for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
+		_huff[i].count = 0;
+		_huff[i].values = NULL;
+		_huff[i].sizes = NULL;
+		_huff[i].codes = NULL;
+	}
+}
+
+JPEGDecoder::~JPEGDecoder() {
+	destroy();
+}
+
+const Surface *JPEGDecoder::getSurface() const {
+	// Make sure we have loaded data
+	if (!isLoaded())
+		return 0;
+
+	if (_rgbSurface)
+		return _rgbSurface;
+
+	// Create an RGBA8888 surface
+	_rgbSurface = new Graphics::Surface();
+	_rgbSurface->create(_w, _h, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+
+	// Get our component surfaces
+	const Graphics::Surface *yComponent = getComponent(1);
+	const Graphics::Surface *uComponent = getComponent(2);
+	const Graphics::Surface *vComponent = getComponent(3);
+
+	for (uint16 i = 0; i < _h; i++) {
+		for (uint16 j = 0; j < _w; j++) {
+			byte r = 0, g = 0, b = 0;
+			YUV2RGB(*((const byte *)yComponent->getBasePtr(j, i)), *((const byte *)uComponent->getBasePtr(j, i)), *((const byte *)vComponent->getBasePtr(j, i)), r, g, b);
+			*((uint32 *)_rgbSurface->getBasePtr(j, i)) = _rgbSurface->format.RGBToColor(r, g, b);
+		}
+	}
+
+	return _rgbSurface;
+}
+
+void JPEGDecoder::destroy() {
+	// Reset member variables
+	_stream = NULL;
+	_w = _h = 0;
+	_restartInterval = 0;
+
+	// Free the components
+	for (int c = 0; c < _numComp; c++)
+		_components[c].surface.free();
+	delete[] _components; _components = NULL;
+	_numComp = 0;
+
+	// Free the scan components
+	delete[] _scanComp; _scanComp = NULL;
+	_numScanComp = 0;
+	_currentComp = NULL;
+
+	// Free the quantization tables
+	for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) {
+		delete[] _quant[i];
+		_quant[i] = NULL;
+	}
+
+	// Free the Huffman tables
+	for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
+		_huff[i].count = 0;
+		delete[] _huff[i].values; _huff[i].values = NULL;
+		delete[] _huff[i].sizes; _huff[i].sizes = NULL;
+		delete[] _huff[i].codes; _huff[i].codes = NULL;
+	}
+
+	if (_rgbSurface) {
+		_rgbSurface->free();
+		delete _rgbSurface;
+	}
+}
+
+bool JPEGDecoder::loadStream(Common::SeekableReadStream &stream) {
+	// Reset member variables and tables from previous reads
+	destroy();
+
+	// Save the input stream
+	_stream = &stream;
+
+	bool ok = true;
+	bool done = false;
+	while (!_stream->eos() && ok && !done) {
+		// Read the marker
+
+		// WORKAROUND: While each and every JPEG file should end with
+		// an EOI (end of image) tag, in reality this may not be the
+		// case. For instance, at least one image in the Masterpiece
+		// edition of Myst doesn't, yet other programs are able to read
+		// the image without complaining.
+		//
+		// Apparently, the customary workaround is to insert a fake
+		// EOI tag.
+
+		uint16 marker = _stream->readByte();
+		bool fakeEOI = false;
+
+		if (_stream->eos()) {
+			fakeEOI = true;
+			marker = 0xFF;
+		}
+
+		if (marker != 0xFF) {
+			error("JPEG: Invalid marker[0]: 0x%02X", marker);
+			ok = false;
+			break;
+		}
+
+		while (marker == 0xFF && !_stream->eos())
+			marker = _stream->readByte();
+
+		if (_stream->eos()) {
+			fakeEOI = true;
+			marker = 0xD9;
+		}
+
+		if (fakeEOI)
+			warning("JPEG: Inserted fake EOI");
+
+		// Process the marker data
+		switch (marker) {
+		case 0xC0: // Start Of Frame
+			ok = readSOF0();
+			break;
+		case 0xC4: // Define Huffman Tables
+			ok = readDHT();
+			break;
+		case 0xD8: // Start Of Image
+			break;
+		case 0xD9: // End Of Image
+			done = true;
+			break;
+		case 0xDA: // Start Of Scan
+			ok = readSOS();
+			break;
+		case 0xDB: // Define Quantization Tables
+			ok = readDQT();
+			break;
+		case 0xE0: // JFIF/JFXX segment
+			ok = readJFIF();
+			break;
+		case 0xDD: // Define Restart Interval
+			ok = readDRI();
+			break;
+		case 0xFE: // Comment
+			_stream->seek(_stream->readUint16BE() - 2, SEEK_CUR);
+			break;
+		default: { // Unknown marker
+			uint16 size = _stream->readUint16BE();
+
+			if ((marker & 0xE0) != 0xE0)
+				warning("JPEG: Unknown marker %02X, skipping %d bytes", marker, size - 2);
+
+			_stream->seek(size - 2, SEEK_CUR);
+		}
+		}
+	}
+
+	_stream = 0;
+	return ok;
+}
+
+bool JPEGDecoder::readJFIF() {
+	uint16 length = _stream->readUint16BE();
+	uint32 tag = _stream->readUint32BE();
+	if (tag != MKTAG('J', 'F', 'I', 'F')) {
+		warning("JPEGDecoder::readJFIF() tag mismatch");
+		return false;
+	}
+	if (_stream->readByte() != 0)  { // NULL
+		warning("JPEGDecoder::readJFIF() NULL mismatch");
+		return false;
+	}
+	byte majorVersion = _stream->readByte();
+	byte minorVersion = _stream->readByte();
+	if (majorVersion != 1 || minorVersion != 1)
+		warning("JPEGDecoder::readJFIF() Non-v1.1 JPEGs may not be handled correctly");
+	/* byte densityUnits = */_stream->readByte();
+	/* uint16 xDensity = */_stream->readUint16BE();
+	/* uint16 yDensity = */_stream->readUint16BE();
+	byte thumbW = _stream->readByte();
+	byte thumbH = _stream->readByte();
+	_stream->seek(thumbW * thumbH * 3, SEEK_CUR); // Ignore thumbnail
+	if (length != (thumbW * thumbH * 3) + 16) {
+		warning("JPEGDecoder::readJFIF() length mismatch");
+		return false;
+	}
+	return true;
+}
+
+// Marker 0xC0 (Start Of Frame, Baseline DCT)
+bool JPEGDecoder::readSOF0() {
+	debug(5, "JPEG: readSOF0");
+	uint16 size = _stream->readUint16BE();
+
+	// Read the sample precision
+	uint8 precision = _stream->readByte();
+	if (precision != 8) {
+		warning("JPEG: Just 8 bit precision supported at the moment");
+		return false;
+	}
+
+	// Image size
+	_h = _stream->readUint16BE();
+	_w = _stream->readUint16BE();
+
+	// Number of components
+	_numComp = _stream->readByte();
+	if (size != 8 + 3 * _numComp) {
+		warning("JPEG: Invalid number of components");
+		return false;
+	}
+
+	// Allocate the new components
+	delete[] _components;
+	_components = new Component[_numComp];
+
+	// Read the components details
+	for (int c = 0; c < _numComp; c++) {
+		_components[c].id = _stream->readByte();
+		_components[c].factorH = _stream->readByte();
+		_components[c].factorV = _components[c].factorH & 0xF;
+		_components[c].factorH >>= 4;
+		_components[c].quantTableSelector = _stream->readByte();
+	}
+
+	return true;
+}
+
+// Marker 0xC4 (Define Huffman Tables)
+bool JPEGDecoder::readDHT() {
+	debug(5, "JPEG: readDHT");
+	uint16 size = _stream->readUint16BE() - 2;
+	uint32 pos = _stream->pos();
+
+	while ((uint32)_stream->pos() < (size + pos)) {
+		// Read the table type and id
+		uint8 tableId = _stream->readByte();
+		uint8 tableType = tableId >> 4; // type 0: DC, 1: AC
+		tableId &= 0xF;
+		uint8 tableNum = (tableId << 1) + tableType;
+
+		// Free the Huffman table
+		delete[] _huff[tableNum].values; _huff[tableNum].values = NULL;
+		delete[] _huff[tableNum].sizes; _huff[tableNum].sizes = NULL;
+		delete[] _huff[tableNum].codes; _huff[tableNum].codes = NULL;
+
+		// Read the number of values for each length
+		uint8 numValues[16];
+		_huff[tableNum].count = 0;
+		for (int len = 0; len < 16; len++) {
+			numValues[len] = _stream->readByte();
+			_huff[tableNum].count += numValues[len];
+		}
+
+		// Allocate memory for the current table
+		_huff[tableNum].values = new uint8[_huff[tableNum].count];
+		_huff[tableNum].sizes = new uint8[_huff[tableNum].count];
+		_huff[tableNum].codes = new uint16[_huff[tableNum].count];
+
+		// Read the table contents
+		int cur = 0;
+		for (int len = 0; len < 16; len++) {
+			for (int i = 0; i < numValues[len]; i++) {
+				_huff[tableNum].values[cur] = _stream->readByte();
+				_huff[tableNum].sizes[cur] = len + 1;
+				cur++;
+			}
+		}
+
+		// Fill the table of Huffman codes
+		cur = 0;
+		uint16 curCode = 0;
+		uint8 curCodeSize = _huff[tableNum].sizes[0];
+		while (cur < _huff[tableNum].count) {
+			// Increase the code size to fit the request
+			while (_huff[tableNum].sizes[cur] != curCodeSize) {
+				curCode <<= 1;
+				curCodeSize++;
+			}
+
+			// Assign the current code
+			_huff[tableNum].codes[cur] = curCode;
+			curCode++;
+			cur++;
+		}
+	}
+
+	return true;
+}
+
+// Marker 0xDA (Start Of Scan)
+bool JPEGDecoder::readSOS() {
+	debug(5, "JPEG: readSOS");
+	uint16 size = _stream->readUint16BE();
+
+	// Number of scan components
+	_numScanComp = _stream->readByte();
+	if (size != 6 + 2 * _numScanComp) {
+		warning("JPEG: Invalid number of components");
+		return false;
+	}
+
+	// Allocate the new scan components
+	delete[] _scanComp;
+	_scanComp = new Component *[_numScanComp];
+
+	// Reset the maximum sampling factors
+	_maxFactorV = 0;
+	_maxFactorH = 0;
+
+	// Component-specification parameters
+	for (int c = 0; c < _numScanComp; c++) {
+		// Read the desired component id
+		uint8 id = _stream->readByte();
+
+		// Search the component with the specified id
+		bool found = false;
+		for (int i = 0; !found && i < _numComp; i++) {
+			if (_components[i].id == id) {
+				// We found the desired component
+				found = true;
+
+				// Assign the found component to the c'th scan component
+				_scanComp[c] = &_components[i];
+			}
+		}
+
+		if (!found) {
+			warning("JPEG: Invalid component");
+			return false;
+		}
+
+		// Read the entropy table selectors
+		_scanComp[c]->DCentropyTableSelector = _stream->readByte();
+		_scanComp[c]->ACentropyTableSelector = _scanComp[c]->DCentropyTableSelector & 0xF;
+		_scanComp[c]->DCentropyTableSelector >>= 4;
+
+		// Calculate the maximum sampling factors
+		if (_scanComp[c]->factorV > _maxFactorV)
+			_maxFactorV = _scanComp[c]->factorV;
+
+		if (_scanComp[c]->factorH > _maxFactorH)
+			_maxFactorH = _scanComp[c]->factorH;
+
+		// Initialize the DC predictor
+		_scanComp[c]->DCpredictor = 0;
+	}
+
+	// Start of spectral selection
+	if (_stream->readByte() != 0) {
+		warning("JPEG: Progressive scanning not supported");
+		return false;
+	}
+
+	// End of spectral selection
+	if (_stream->readByte() != 63) {
+		warning("JPEG: Progressive scanning not supported");
+		return false;
+	}
+
+	// Successive approximation parameters
+	if (_stream->readByte() != 0) {
+		warning("JPEG: Progressive scanning not supported");
+		return false;
+	}
+
+	// Entropy coded sequence starts, initialize Huffman decoder
+	_bitsNumber = 0;
+
+	// Read all the scan MCUs
+	uint16 xMCU = _w / (_maxFactorH * 8);
+	uint16 yMCU = _h / (_maxFactorV * 8);
+
+	// Check for non- multiple-of-8 dimensions
+	if (_w % (_maxFactorH * 8) != 0)
+		xMCU++;
+	if (_h % (_maxFactorV * 8) != 0)
+		yMCU++;
+
+	// Initialize the scan surfaces
+	for (uint16 c = 0; c < _numScanComp; c++) {
+		_scanComp[c]->surface.create(xMCU * _maxFactorH * 8, yMCU * _maxFactorV * 8, PixelFormat::createFormatCLUT8());
+	}
+
+	bool ok = true;
+	uint16 interval = _restartInterval;
+
+	for (int y = 0; ok && (y < yMCU); y++) {
+		for (int x = 0; ok && (x < xMCU); x++) {
+			ok = readMCU(x, y);
+
+			// If we have a restart interval, we'll need to reset a couple
+			// variables
+			if (_restartInterval != 0) {
+				interval--;
+
+				if (interval == 0) {
+					interval = _restartInterval;
+					_bitsNumber = 0;
+
+					for (byte i = 0; i < _numScanComp; i++)
+						_scanComp[i]->DCpredictor = 0;					
+				}
+			}
+		}
+	}
+
+	// Trim Component surfaces back to image height and width
+	// Note: Code using jpeg must use surface.pitch correctly...
+	for (uint16 c = 0; c < _numScanComp; c++) {
+		_scanComp[c]->surface.w = _w;
+		_scanComp[c]->surface.h = _h;
+	}
+
+	return ok;
+}
+
+// Marker 0xDB (Define Quantization Tables)
+bool JPEGDecoder::readDQT() {
+	debug(5, "JPEG: readDQT");
+	uint16 size = _stream->readUint16BE() - 2;
+	uint32 pos = _stream->pos();
+
+	while ((uint32)_stream->pos() < (pos + size)) {
+		// Read the table precision and id
+		uint8 tableId = _stream->readByte();
+		bool highPrecision = (tableId & 0xF0) != 0;
+
+		// Validate the table id
+		tableId &= 0xF;
+		if (tableId > JPEG_MAX_QUANT_TABLES) {
+			warning("JPEG: Invalid number of components");
+			return false;
+		}
+
+		// Create the new table if necessary
+		if (!_quant[tableId])
+			_quant[tableId] = new uint16[64];
+
+		// Read the table (stored in Zig-Zag order)
+		for (int i = 0; i < 64; i++)
+			_quant[tableId][i] = highPrecision ? _stream->readUint16BE() : _stream->readByte();
+	}
+
+	return true;
+}
+
+// Marker 0xDD (Define Restart Interval)
+bool JPEGDecoder::readDRI() {
+	debug(5, "JPEG: readDRI");
+	uint16 size = _stream->readUint16BE() - 2;
+
+	if (size != 2) {
+		warning("JPEG: Invalid DRI size %d", size);
+		return false;
+	}
+
+	_restartInterval = _stream->readUint16BE();
+	debug(5, "Restart interval: %d", _restartInterval);
+	return true;
+}
+
+bool JPEGDecoder::readMCU(uint16 xMCU, uint16 yMCU) {
+	bool ok = true;
+	for (int c = 0; ok && (c < _numComp); c++) {
+		// Set the current component
+		_currentComp = _scanComp[c];
+
+		// Read the data units of the current component
+		for (int y = 0; ok && (y < _scanComp[c]->factorV); y++)
+			for (int x = 0; ok && (x < _scanComp[c]->factorH); x++)
+				ok = readDataUnit(xMCU * _scanComp[c]->factorH + x, yMCU * _scanComp[c]->factorV + y);
+	}
+
+	return ok;
+}
+
+// triple-butterfly-add (and possible rounding)
+#define xadd3(xa, xb, xc, xd, h) \
+	p = xa + xb; \
+	n = xa - xb; \
+	xa = p + xc + h; \
+	xb = n + xd + h; \
+	xc = p - xc + h; \
+	xd = n - xd + h;
+
+// butterfly-mul
+#define xmul(xa, xb, k1, k2, sh) \
+	n = k1 * (xa + xb); \
+	p = xa; \
+	xa = (n + (k2 - k1) * xb) >> sh; \
+	xb = (n - (k2 + k1) * p) >> sh;
+
+// IDCT based on public domain code from http://halicery.com/jpeg/idct.html
+void JPEGDecoder::idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half) {
+	int p, n;
+
+	src[0] <<= 9;
+	src[1] <<= 7;
+	src[3] *= 181;
+	src[4] <<= 9;
+	src[5] *= 181;
+	src[7] <<= 7;
+
+	// Even part
+	xmul(src[6], src[2], 277, 669, 0)
+	xadd3(src[0], src[4], src[6], src[2], half)
+
+	// Odd part
+	xadd3(src[1], src[7], src[3], src[5], 0)
+	xmul(src[5], src[3], 251, 50, 6)
+	xmul(src[1], src[7], 213, 142, 6)
+
+	dest[0 * 8] = (src[0] + src[1]) >> ps;
+	dest[1 * 8] = (src[4] + src[5]) >> ps;
+	dest[2 * 8] = (src[2] + src[3]) >> ps;
+	dest[3 * 8] = (src[6] + src[7]) >> ps;
+	dest[4 * 8] = (src[6] - src[7]) >> ps;
+	dest[5 * 8] = (src[2] - src[3]) >> ps;
+	dest[6 * 8] = (src[4] - src[5]) >> ps;
+	dest[7 * 8] = (src[0] - src[1]) >> ps;
+}
+
+void JPEGDecoder::idct2D8x8(int32 block[64]) {
+	int32 tmp[64];
+
+	// Apply 1D IDCT to rows
+	for (int i = 0; i < 8; i++)
+		idct1D8x8(&block[i * 8], &tmp[i], 9, 1 << 8);
+
+	// Apply 1D IDCT to columns
+	for (int i = 0; i < 8; i++)
+		idct1D8x8(&tmp[i * 8], &block[i], 12, 1 << 11);
+ }
+
+bool JPEGDecoder::readDataUnit(uint16 x, uint16 y) {
+	// Prepare an empty data array
+	int16 readData[64];
+	for (int i = 1; i < 64; i++)
+		readData[i] = 0;
+
+	// Read the DC component
+	readData[0] = _currentComp->DCpredictor + readDC();
+	_currentComp->DCpredictor = readData[0];
+
+	// Read the AC components (stored in Zig-Zag)
+	readAC(readData);
+
+	// Calculate the DCT coefficients from the input sequence
+	int32 block[64];
+	for (uint8 i = 0; i < 64; i++) {
+		// Dequantize
+		int32 val = readData[i];
+		int16 quant = _quant[_currentComp->quantTableSelector][i];
+		val *= quant;
+
+		// Store the normalized coefficients, undoing the Zig-Zag
+		block[_zigZagOrder[i]] = val;
+	}
+
+	// Apply the IDCT
+	idct2D8x8(block);
+
+	// Level shift to make the values unsigned
+	for (int i = 0; i < 64; i++) {
+		block[i] = block[i] + 128;
+
+		if (block[i] < 0)
+			block[i] = 0;
+
+		if (block[i] > 255)
+			block[i] = 255;
+	}
+
+	// Paint the component surface
+	uint8 scalingV = _maxFactorV / _currentComp->factorV;
+	uint8 scalingH = _maxFactorH / _currentComp->factorH;
+
+	// Convert coordinates from MCU blocks to pixels
+	x <<= 3;
+	y <<= 3;
+
+	for (uint8 j = 0; j < 8; j++) {
+		for (uint16 sV = 0; sV < scalingV; sV++) {
+			// Get the beginning of the block line
+			byte *ptr = (byte *)_currentComp->surface.getBasePtr(x * scalingH, (y + j) * scalingV + sV);
+
+			for (uint8 i = 0; i < 8; i++) {
+				for (uint16 sH = 0; sH < scalingH; sH++) {
+					*ptr = (byte)(block[j * 8 + i]);
+					ptr++;
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+int16 JPEGDecoder::readDC() {
+	// DC is type 0
+	uint8 tableNum = _currentComp->DCentropyTableSelector << 1;
+
+	// Get the number of bits to read
+	uint8 numBits = readHuff(tableNum);
+
+	// Read the requested bits
+	return readSignedBits(numBits);
+}
+
+void JPEGDecoder::readAC(int16 *out) {
+	// AC is type 1
+	uint8 tableNum = (_currentComp->ACentropyTableSelector << 1) + 1;
+
+	// Start reading AC element 1
+	uint8 cur = 1;
+	while (cur < 64) {
+		uint8 s = readHuff(tableNum);
+		uint8 r = s >> 4;
+		s &= 0xF;
+
+		if (s == 0) {
+			if (r == 15) {
+				// Skip 16 values
+				cur += 16;
+			} else {
+				// EOB: end of block
+				cur = 64;
+			}
+		} else {
+			// Skip r values
+			cur += r;
+
+			// Read the next value
+			out[cur] = readSignedBits(s);
+			cur++;
+		}
+	}
+}
+
+int16 JPEGDecoder::readSignedBits(uint8 numBits) {
+	uint16 ret = 0;
+	if (numBits > 16)
+		error("requested %d bits", numBits); //XXX
+
+	// MSB=0 for negatives, 1 for positives
+	for (int i = 0; i < numBits; i++)
+		ret = (ret << 1) + readBit();
+
+	// Extend sign bits (PAG109)
+	if (!(ret >> (numBits - 1))) {
+		uint16 tmp = ((uint16)-1 << numBits) + 1;
+		ret = ret + tmp;
+	}
+	return ret;
+}
+
+// TODO: optimize?
+uint8 JPEGDecoder::readHuff(uint8 table) {
+	bool foundCode = false;
+	uint8 val = 0;
+
+	uint8 cur = 0;
+	uint8 codeSize = 1;
+	uint16 code = readBit();
+	while (!foundCode) {
+		// Prepare a code of the current size
+		while (codeSize < _huff[table].sizes[cur]) {
+			code = (code << 1) + readBit();
+			codeSize++;
+		}
+
+		// Compare the codes of the current size
+		while (!foundCode && (codeSize == _huff[table].sizes[cur])) {
+			if (code == _huff[table].codes[cur]) {
+				// Found the code
+				val = _huff[table].values[cur];
+				foundCode = true;
+			} else {
+				// Continue reading
+				cur++;
+			}
+		}
+	}
+
+	return val;
+}
+
+uint8 JPEGDecoder::readBit() {
+	// Read a whole byte if necessary
+	if (_bitsNumber == 0) {
+		_bitsData = _stream->readByte();
+		_bitsNumber = 8;
+
+		// Detect markers
+		if (_bitsData == 0xFF) {
+			uint8 byte2 = _stream->readByte();
+
+			// A stuffed 0 validates the previous byte
+			if (byte2 != 0) {
+				if (byte2 == 0xDC) {
+					// DNL marker: Define Number of Lines
+					// TODO: terminate scan
+					warning("DNL marker detected: terminate scan");
+				} else if (byte2 >= 0xD0 && byte2 <= 0xD7) {
+					debug(7, "RST%d marker detected", byte2 & 7);
+					_bitsData = _stream->readByte();
+				} else {
+					warning("Error: marker 0x%02X read in entropy data", byte2);
+				}
+			}
+		}
+	}
+	_bitsNumber--;
+
+	return (_bitsData & (1 << _bitsNumber)) ? 1 : 0;
+}
+
+const Surface *JPEGDecoder::getComponent(uint c) const {
+	for (int i = 0; i < _numComp; i++)
+		if (_components[i].id == c) // We found the desired component
+			return &_components[i].surface;
+
+	error("JPEGDecoder::getComponent: No component %d present", c);
+	return NULL;
+}
+
+} // End of Graphics namespace
diff --git a/graphics/decoders/jpeg.h b/graphics/decoders/jpeg.h
new file mode 100644
index 0000000..c566d5a
--- /dev/null
+++ b/graphics/decoders/jpeg.h
@@ -0,0 +1,133 @@
+/* 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 GRAPHICS_JPEG_H
+#define GRAPHICS_JPEG_H
+
+#include "graphics/surface.h"
+#include "graphics/decoders/image_decoder.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Graphics {
+
+struct PixelFormat;
+
+#define JPEG_MAX_QUANT_TABLES 4
+#define JPEG_MAX_HUFF_TABLES 2
+
+class JPEGDecoder : public ImageDecoder {
+public:
+	JPEGDecoder();
+	~JPEGDecoder();
+
+	// ImageDecoder API
+	void destroy();
+	bool loadStream(Common::SeekableReadStream &str);
+	const Surface *getSurface() const;
+
+	bool isLoaded() const { return _numComp && _w && _h; }
+	uint16 getWidth() const { return _w; }
+	uint16 getHeight() const { return _h; }
+	const Surface *getComponent(uint c) const;
+
+private:
+	Common::SeekableReadStream *_stream;
+	uint16 _w, _h;
+	uint16 _restartInterval;
+
+	// mutable so that we can convert to RGB only during
+	// a getSurface() call while still upholding the
+	// const requirement in other ImageDecoders
+	mutable Graphics::Surface *_rgbSurface;
+
+	// Image components
+	uint8 _numComp;
+	struct Component {
+		// Global values
+		uint8 id;
+		uint8 factorH;
+		uint8 factorV;
+		uint8 quantTableSelector;
+
+		// Scan specific values
+		uint8 DCentropyTableSelector;
+		uint8 ACentropyTableSelector;
+		int16 DCpredictor;
+
+		// Result image for this component
+		Surface surface;
+	};
+
+	Component *_components;
+
+	// Scan components
+	uint8 _numScanComp;
+	Component **_scanComp;
+	Component *_currentComp;
+
+	// Maximum sampling factors, used to calculate the interleaving of the MCU
+	uint8 _maxFactorV;
+	uint8 _maxFactorH;
+
+	// Quantization tables
+	uint16 *_quant[JPEG_MAX_QUANT_TABLES];
+
+	// Huffman tables
+	struct HuffmanTable {
+		uint8 count;
+		uint8 *values;
+		uint8 *sizes;
+		uint16 *codes;
+	} _huff[2 * JPEG_MAX_HUFF_TABLES];
+
+	// Marker read functions
+	bool readJFIF();
+	bool readSOF0();
+	bool readDHT();
+	bool readSOS();
+	bool readDQT();
+	bool readDRI();
+
+	// Helper functions
+	bool readMCU(uint16 xMCU, uint16 yMCU);
+	bool readDataUnit(uint16 x, uint16 y);
+	int16 readDC();
+	void readAC(int16 *out);
+	int16 readSignedBits(uint8 numBits);
+
+	// Huffman decoding
+	uint8 readHuff(uint8 table);
+	uint8 readBit();
+	uint8 _bitsData;
+	uint8 _bitsNumber;
+
+	// Inverse Discrete Cosine Transformation
+	static void idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half);
+	static void idct2D8x8(int32 block[64]);
+};
+
+} // End of Graphics namespace
+
+#endif // GRAPHICS_JPEG_H
diff --git a/graphics/decoders/pict.cpp b/graphics/decoders/pict.cpp
index 9b28f43..f8b2553 100644
--- a/graphics/decoders/pict.cpp
+++ b/graphics/decoders/pict.cpp
@@ -27,7 +27,7 @@
 #include "common/textconsole.h"
 
 #include "graphics/surface.h"
-#include "graphics/jpeg.h"
+#include "graphics/decoders/jpeg.h"
 #include "graphics/decoders/pict.h"
 
 namespace Graphics {
@@ -512,20 +512,20 @@ void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalet
 // http://developer.apple.com/legacy/mac/library/#documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/B-Chapter/2TheImageCompression.html
 // http://developer.apple.com/legacy/mac/library/#documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/F-Chapter/6WorkingwiththeImage.html
 void PICTDecoder::decodeCompressedQuickTime(Common::SeekableReadStream &stream) {
-	JPEG jpeg;
+	JPEGDecoder jpeg;
 
 	uint32 dataSize = stream.readUint32BE();
 	uint32 startPos = stream.pos();
 
-	Common::SeekableReadStream *jpegStream = new Common::SeekableSubReadStream(&stream, stream.pos() + 156, stream.pos() + dataSize);
+	Common::SeekableSubReadStream jpegStream(&stream, stream.pos() + 156, stream.pos() + dataSize);
 
-	if (!jpeg.read(jpegStream))
+	if (!jpeg.loadStream(jpegStream))
 		error("PICTDecoder::decodeCompressedQuickTime(): Could not decode JPEG data");
 
-	_outputSurface = jpeg.getSurface(Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
+	_outputSurface = new Graphics::Surface();
+	_outputSurface->copyFrom(*jpeg.getSurface());
 
 	stream.seek(startPos + dataSize);
-	delete jpegStream;
 }
 
 } // End of namespace Graphics
diff --git a/graphics/jpeg.cpp b/graphics/jpeg.cpp
deleted file mode 100644
index 53e693a..0000000
--- a/graphics/jpeg.cpp
+++ /dev/null
@@ -1,785 +0,0 @@
-/* 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.
- *
- */
-
-#include "graphics/conversion.h"
-#include "graphics/jpeg.h"
-#include "graphics/pixelformat.h"
-
-#include "common/debug.h"
-#include "common/endian.h"
-#include "common/stream.h"
-#include "common/textconsole.h"
-
-namespace Graphics {
-
-// Order used to traverse the quantization tables
-static const uint8 _zigZagOrder[64] = {
-	0,   1,  8, 16,  9,  2,  3, 10,
-	17, 24, 32, 25, 18, 11,  4,  5,
-	12, 19, 26, 33, 40, 48, 41, 34,
-	27, 20, 13,  6,  7, 14, 21, 28,
-	35, 42, 49, 56, 57, 50, 43, 36,
-	29, 22, 15, 23, 30, 37, 44, 51,
-	58, 59, 52, 45, 38, 31, 39, 46,
-	53, 60, 61, 54, 47, 55, 62, 63
-};
-
-JPEG::JPEG() :
-	_stream(NULL), _w(0), _h(0), _numComp(0), _components(NULL), _numScanComp(0),
-	_scanComp(NULL), _currentComp(NULL) {
-
-	// Initialize the quantization tables
-	for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++)
-		_quant[i] = NULL;
-
-	// Initialize the Huffman tables
-	for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
-		_huff[i].count = 0;
-		_huff[i].values = NULL;
-		_huff[i].sizes = NULL;
-		_huff[i].codes = NULL;
-	}
-}
-
-JPEG::~JPEG() {
-	reset();
-}
-
-Surface *JPEG::getSurface(const PixelFormat &format) {
-	// Make sure we have loaded data
-	if (!isLoaded())
-		return 0;
-
-	// Only accept >8bpp surfaces
-	if (format.bytesPerPixel == 1)
-		return 0;
-
-	// Get our component surfaces
-	Graphics::Surface *yComponent = getComponent(1);
-	Graphics::Surface *uComponent = getComponent(2);
-	Graphics::Surface *vComponent = getComponent(3);
-
-	Graphics::Surface *output = new Graphics::Surface();
-	output->create(yComponent->w, yComponent->h, format);
-
-	for (uint16 i = 0; i < output->h; i++) {
-		for (uint16 j = 0; j < output->w; j++) {
-			byte r = 0, g = 0, b = 0;
-			YUV2RGB(*((byte *)yComponent->getBasePtr(j, i)), *((byte *)uComponent->getBasePtr(j, i)), *((byte *)vComponent->getBasePtr(j, i)), r, g, b);
-			if (format.bytesPerPixel == 2)
-				*((uint16 *)output->getBasePtr(j, i)) = format.RGBToColor(r, g, b);
-			else
-				*((uint32 *)output->getBasePtr(j, i)) = format.RGBToColor(r, g, b);
-		}
-	}
-
-	return output;
-}
-
-void JPEG::reset() {
-	// Reset member variables
-	_stream = NULL;
-	_w = _h = 0;
-	_restartInterval = 0;
-
-	// Free the components
-	for (int c = 0; c < _numComp; c++)
-		_components[c].surface.free();
-	delete[] _components; _components = NULL;
-	_numComp = 0;
-
-	// Free the scan components
-	delete[] _scanComp; _scanComp = NULL;
-	_numScanComp = 0;
-	_currentComp = NULL;
-
-	// Free the quantization tables
-	for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) {
-		delete[] _quant[i];
-		_quant[i] = NULL;
-	}
-
-	// Free the Huffman tables
-	for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) {
-		_huff[i].count = 0;
-		delete[] _huff[i].values; _huff[i].values = NULL;
-		delete[] _huff[i].sizes; _huff[i].sizes = NULL;
-		delete[] _huff[i].codes; _huff[i].codes = NULL;
-	}
-}
-
-bool JPEG::read(Common::SeekableReadStream *stream) {
-	// Reset member variables and tables from previous reads
-	reset();
-
-	// Save the input stream
-	_stream = stream;
-
-	bool ok = true;
-	bool done = false;
-	while (!_stream->eos() && ok && !done) {
-		// Read the marker
-
-		// WORKAROUND: While each and every JPEG file should end with
-		// an EOI (end of image) tag, in reality this may not be the
-		// case. For instance, at least one image in the Masterpiece
-		// edition of Myst doesn't, yet other programs are able to read
-		// the image without complaining.
-		//
-		// Apparently, the customary workaround is to insert a fake
-		// EOI tag.
-
-		uint16 marker = _stream->readByte();
-		bool fakeEOI = false;
-
-		if (_stream->eos()) {
-			fakeEOI = true;
-			marker = 0xFF;
-		}
-
-		if (marker != 0xFF) {
-			error("JPEG: Invalid marker[0]: 0x%02X", marker);
-			ok = false;
-			break;
-		}
-
-		while (marker == 0xFF && !_stream->eos())
-			marker = _stream->readByte();
-
-		if (_stream->eos()) {
-			fakeEOI = true;
-			marker = 0xD9;
-		}
-
-		if (fakeEOI)
-			warning("JPEG: Inserted fake EOI");
-
-		// Process the marker data
-		switch (marker) {
-		case 0xC0: // Start Of Frame
-			ok = readSOF0();
-			break;
-		case 0xC4: // Define Huffman Tables
-			ok = readDHT();
-			break;
-		case 0xD8: // Start Of Image
-			break;
-		case 0xD9: // End Of Image
-			done = true;
-			break;
-		case 0xDA: // Start Of Scan
-			ok = readSOS();
-			break;
-		case 0xDB: // Define Quantization Tables
-			ok = readDQT();
-			break;
-		case 0xE0: // JFIF/JFXX segment
-			ok = readJFIF();
-			break;
-		case 0xDD: // Define Restart Interval
-			ok = readDRI();
-			break;
-		case 0xFE: // Comment
-			_stream->seek(_stream->readUint16BE() - 2, SEEK_CUR);
-			break;
-		default: { // Unknown marker
-			uint16 size = _stream->readUint16BE();
-
-			if ((marker & 0xE0) != 0xE0)
-				warning("JPEG: Unknown marker %02X, skipping %d bytes", marker, size - 2);
-
-			_stream->seek(size - 2, SEEK_CUR);
-		}
-		}
-	}
-	return ok;
-}
-
-bool JPEG::readJFIF() {
-	uint16 length = _stream->readUint16BE();
-	uint32 tag = _stream->readUint32BE();
-	if (tag != MKTAG('J', 'F', 'I', 'F')) {
-		warning("JPEG::readJFIF() tag mismatch");
-		return false;
-	}
-	if (_stream->readByte() != 0)  { // NULL
-		warning("JPEG::readJFIF() NULL mismatch");
-		return false;
-	}
-	byte majorVersion = _stream->readByte();
-	byte minorVersion = _stream->readByte();
-
-	if (majorVersion != 1 || (minorVersion != 1 && minorVersion != 2))
-		warning("JPEG::readJFIF() Non-v1.1/1.2 JPEGs may not be handled correctly");
-
-	/* byte densityUnits = */ _stream->readByte();
-	/* uint16 xDensity = */ _stream->readUint16BE();
-	/* uint16 yDensity = */ _stream->readUint16BE();
-	byte thumbW = _stream->readByte();
-	byte thumbH = _stream->readByte();
-	_stream->seek(thumbW * thumbH * 3, SEEK_CUR); // Ignore thumbnail
-	if (length != (thumbW * thumbH * 3) + 16) {
-		warning("JPEG::readJFIF() length mismatch");
-		return false;
-	}
-	return true;
-}
-
-// Marker 0xC0 (Start Of Frame, Baseline DCT)
-bool JPEG::readSOF0() {
-	debug(5, "JPEG: readSOF0");
-	uint16 size = _stream->readUint16BE();
-
-	// Read the sample precision
-	uint8 precision = _stream->readByte();
-	if (precision != 8) {
-		warning("JPEG: Just 8 bit precision supported at the moment");
-		return false;
-	}
-
-	// Image size
-	_h = _stream->readUint16BE();
-	_w = _stream->readUint16BE();
-
-	// Number of components
-	_numComp = _stream->readByte();
-	if (size != 8 + 3 * _numComp) {
-		warning("JPEG: Invalid number of components");
-		return false;
-	}
-
-	// Allocate the new components
-	delete[] _components;
-	_components = new Component[_numComp];
-
-	// Read the components details
-	for (int c = 0; c < _numComp; c++) {
-		_components[c].id = _stream->readByte();
-		_components[c].factorH = _stream->readByte();
-		_components[c].factorV = _components[c].factorH & 0xF;
-		_components[c].factorH >>= 4;
-		_components[c].quantTableSelector = _stream->readByte();
-	}
-
-	return true;
-}
-
-// Marker 0xC4 (Define Huffman Tables)
-bool JPEG::readDHT() {
-	debug(5, "JPEG: readDHT");
-	uint16 size = _stream->readUint16BE() - 2;
-	uint32 pos = _stream->pos();
-
-	while ((uint32)_stream->pos() < (size + pos)) {
-		// Read the table type and id
-		uint8 tableId = _stream->readByte();
-		uint8 tableType = tableId >> 4; // type 0: DC, 1: AC
-		tableId &= 0xF;
-		uint8 tableNum = (tableId << 1) + tableType;
-
-		// Free the Huffman table
-		delete[] _huff[tableNum].values; _huff[tableNum].values = NULL;
-		delete[] _huff[tableNum].sizes; _huff[tableNum].sizes = NULL;
-		delete[] _huff[tableNum].codes; _huff[tableNum].codes = NULL;
-
-		// Read the number of values for each length
-		uint8 numValues[16];
-		_huff[tableNum].count = 0;
-		for (int len = 0; len < 16; len++) {
-			numValues[len] = _stream->readByte();
-			_huff[tableNum].count += numValues[len];
-		}
-
-		// Allocate memory for the current table
-		_huff[tableNum].values = new uint8[_huff[tableNum].count];
-		_huff[tableNum].sizes = new uint8[_huff[tableNum].count];
-		_huff[tableNum].codes = new uint16[_huff[tableNum].count];
-
-		// Read the table contents
-		int cur = 0;
-		for (int len = 0; len < 16; len++) {
-			for (int i = 0; i < numValues[len]; i++) {
-				_huff[tableNum].values[cur] = _stream->readByte();
-				_huff[tableNum].sizes[cur] = len + 1;
-				cur++;
-			}
-		}
-
-		// Fill the table of Huffman codes
-		cur = 0;
-		uint16 curCode = 0;
-		uint8 curCodeSize = _huff[tableNum].sizes[0];
-		while (cur < _huff[tableNum].count) {
-			// Increase the code size to fit the request
-			while (_huff[tableNum].sizes[cur] != curCodeSize) {
-				curCode <<= 1;
-				curCodeSize++;
-			}
-
-			// Assign the current code
-			_huff[tableNum].codes[cur] = curCode;
-			curCode++;
-			cur++;
-		}
-	}
-
-	return true;
-}
-
-// Marker 0xDA (Start Of Scan)
-bool JPEG::readSOS() {
-	debug(5, "JPEG: readSOS");
-	uint16 size = _stream->readUint16BE();
-
-	// Number of scan components
-	_numScanComp = _stream->readByte();
-	if (size != 6 + 2 * _numScanComp) {
-		warning("JPEG: Invalid number of components");
-		return false;
-	}
-
-	// Allocate the new scan components
-	delete[] _scanComp;
-	_scanComp = new Component *[_numScanComp];
-
-	// Reset the maximum sampling factors
-	_maxFactorV = 0;
-	_maxFactorH = 0;
-
-	// Component-specification parameters
-	for (int c = 0; c < _numScanComp; c++) {
-		// Read the desired component id
-		uint8 id = _stream->readByte();
-
-		// Search the component with the specified id
-		bool found = false;
-		for (int i = 0; !found && i < _numComp; i++) {
-			if (_components[i].id == id) {
-				// We found the desired component
-				found = true;
-
-				// Assign the found component to the c'th scan component
-				_scanComp[c] = &_components[i];
-			}
-		}
-
-		if (!found) {
-			warning("JPEG: Invalid component");
-			return false;
-		}
-
-		// Read the entropy table selectors
-		_scanComp[c]->DCentropyTableSelector = _stream->readByte();
-		_scanComp[c]->ACentropyTableSelector = _scanComp[c]->DCentropyTableSelector & 0xF;
-		_scanComp[c]->DCentropyTableSelector >>= 4;
-
-		// Calculate the maximum sampling factors
-		if (_scanComp[c]->factorV > _maxFactorV)
-			_maxFactorV = _scanComp[c]->factorV;
-
-		if (_scanComp[c]->factorH > _maxFactorH)
-			_maxFactorH = _scanComp[c]->factorH;
-
-		// Initialize the DC predictor
-		_scanComp[c]->DCpredictor = 0;
-	}
-
-	// Start of spectral selection
-	if (_stream->readByte() != 0) {
-		warning("JPEG: Progressive scanning not supported");
-		return false;
-	}
-
-	// End of spectral selection
-	if (_stream->readByte() != 63) {
-		warning("JPEG: Progressive scanning not supported");
-		return false;
-	}
-
-	// Successive approximation parameters
-	if (_stream->readByte() != 0) {
-		warning("JPEG: Progressive scanning not supported");
-		return false;
-	}
-
-	// Entropy coded sequence starts, initialize Huffman decoder
-	_bitsNumber = 0;
-
-	// Read all the scan MCUs
-	uint16 xMCU = _w / (_maxFactorH * 8);
-	uint16 yMCU = _h / (_maxFactorV * 8);
-
-	// Check for non- multiple-of-8 dimensions
-	if (_w % (_maxFactorH * 8) != 0)
-		xMCU++;
-	if (_h % (_maxFactorV * 8) != 0)
-		yMCU++;
-
-	// Initialize the scan surfaces
-	for (uint16 c = 0; c < _numScanComp; c++) {
-		_scanComp[c]->surface.create(xMCU * _maxFactorH * 8, yMCU * _maxFactorV * 8, PixelFormat::createFormatCLUT8());
-	}
-
-	bool ok = true;
-	uint16 interval = _restartInterval;
-
-	for (int y = 0; ok && (y < yMCU); y++) {
-		for (int x = 0; ok && (x < xMCU); x++) {
-			ok = readMCU(x, y);
-
-			// If we have a restart interval, we'll need to reset a couple
-			// variables
-			if (_restartInterval != 0) {
-				interval--;
-
-				if (interval == 0) {
-					interval = _restartInterval;
-					_bitsNumber = 0;
-
-					for (byte i = 0; i < _numScanComp; i++)
-						_scanComp[i]->DCpredictor = 0;					
-				}
-			}
-		}
-	}
-
-	// Trim Component surfaces back to image height and width
-	// Note: Code using jpeg must use surface.pitch correctly...
-	for (uint16 c = 0; c < _numScanComp; c++) {
-		_scanComp[c]->surface.w = _w;
-		_scanComp[c]->surface.h = _h;
-	}
-
-	return ok;
-}
-
-// Marker 0xDB (Define Quantization Tables)
-bool JPEG::readDQT() {
-	debug(5, "JPEG: readDQT");
-	uint16 size = _stream->readUint16BE() - 2;
-	uint32 pos = _stream->pos();
-
-	while ((uint32)_stream->pos() < (pos + size)) {
-		// Read the table precision and id
-		uint8 tableId = _stream->readByte();
-		bool highPrecision = (tableId & 0xF0) != 0;
-
-		// Validate the table id
-		tableId &= 0xF;
-		if (tableId > JPEG_MAX_QUANT_TABLES) {
-			warning("JPEG: Invalid number of components");
-			return false;
-		}
-
-		// Create the new table if necessary
-		if (!_quant[tableId])
-			_quant[tableId] = new uint16[64];
-
-		// Read the table (stored in Zig-Zag order)
-		for (int i = 0; i < 64; i++)
-			_quant[tableId][i] = highPrecision ? _stream->readUint16BE() : _stream->readByte();
-	}
-
-	return true;
-}
-
-// Marker 0xDD (Define Restart Interval)
-bool JPEG::readDRI() {
-	debug(5, "JPEG: readDRI");
-	uint16 size = _stream->readUint16BE() - 2;
-
-	if (size != 2) {
-		warning("JPEG: Invalid DRI size %d", size);
-		return false;
-	}
-
-	_restartInterval = _stream->readUint16BE();
-	debug(5, "Restart interval: %d", _restartInterval);
-	return true;
-}
-
-bool JPEG::readMCU(uint16 xMCU, uint16 yMCU) {
-	bool ok = true;
-	for (int c = 0; ok && (c < _numComp); c++) {
-		// Set the current component
-		_currentComp = _scanComp[c];
-
-		// Read the data units of the current component
-		for (int y = 0; ok && (y < _scanComp[c]->factorV); y++)
-			for (int x = 0; ok && (x < _scanComp[c]->factorH); x++)
-				ok = readDataUnit(xMCU * _scanComp[c]->factorH + x, yMCU * _scanComp[c]->factorV + y);
-	}
-
-	return ok;
-}
-
-// triple-butterfly-add (and possible rounding)
-#define xadd3(xa, xb, xc, xd, h) \
-	p = xa + xb; \
-	n = xa - xb; \
-	xa = p + xc + h; \
-	xb = n + xd + h; \
-	xc = p - xc + h; \
-	xd = n - xd + h;
-
-// butterfly-mul
-#define xmul(xa, xb, k1, k2, sh) \
-	n = k1 * (xa + xb); \
-	p = xa; \
-	xa = (n + (k2 - k1) * xb) >> sh; \
-	xb = (n - (k2 + k1) * p) >> sh;
-
-// IDCT based on public domain code from http://halicery.com/jpeg/idct.html
-void JPEG::idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half) {
-	int p, n;
-
-	src[0] <<= 9;
-	src[1] <<= 7;
-	src[3] *= 181;
-	src[4] <<= 9;
-	src[5] *= 181;
-	src[7] <<= 7;
-
-	// Even part
-	xmul(src[6], src[2], 277, 669, 0)
-	xadd3(src[0], src[4], src[6], src[2], half)
-
-	// Odd part
-	xadd3(src[1], src[7], src[3], src[5], 0)
-	xmul(src[5], src[3], 251, 50, 6)
-	xmul(src[1], src[7], 213, 142, 6)
-
-	dest[0 * 8] = (src[0] + src[1]) >> ps;
-	dest[1 * 8] = (src[4] + src[5]) >> ps;
-	dest[2 * 8] = (src[2] + src[3]) >> ps;
-	dest[3 * 8] = (src[6] + src[7]) >> ps;
-	dest[4 * 8] = (src[6] - src[7]) >> ps;
-	dest[5 * 8] = (src[2] - src[3]) >> ps;
-	dest[6 * 8] = (src[4] - src[5]) >> ps;
-	dest[7 * 8] = (src[0] - src[1]) >> ps;
-}
-
-void JPEG::idct2D8x8(int32 block[64]) {
-	int32 tmp[64];
-
-	// Apply 1D IDCT to rows
-	for (int i = 0; i < 8; i++)
-		idct1D8x8(&block[i * 8], &tmp[i], 9, 1 << 8);
-
-	// Apply 1D IDCT to columns
-	for (int i = 0; i < 8; i++)
-		idct1D8x8(&tmp[i * 8], &block[i], 12, 1 << 11);
- }
-
-bool JPEG::readDataUnit(uint16 x, uint16 y) {
-	// Prepare an empty data array
-	int16 readData[64];
-	for (int i = 1; i < 64; i++)
-		readData[i] = 0;
-
-	// Read the DC component
-	readData[0] = _currentComp->DCpredictor + readDC();
-	_currentComp->DCpredictor = readData[0];
-
-	// Read the AC components (stored in Zig-Zag)
-	readAC(readData);
-
-	// Calculate the DCT coefficients from the input sequence
-	int32 block[64];
-	for (uint8 i = 0; i < 64; i++) {
-		// Dequantize
-		int32 val = readData[i];
-		int16 quant = _quant[_currentComp->quantTableSelector][i];
-		val *= quant;
-
-		// Store the normalized coefficients, undoing the Zig-Zag
-		block[_zigZagOrder[i]] = val;
-	}
-
-	// Apply the IDCT
-	idct2D8x8(block);
-
-	// Level shift to make the values unsigned
-	for (int i = 0; i < 64; i++) {
-		block[i] = block[i] + 128;
-
-		if (block[i] < 0)
-			block[i] = 0;
-
-		if (block[i] > 255)
-			block[i] = 255;
-	}
-
-	// Paint the component surface
-	uint8 scalingV = _maxFactorV / _currentComp->factorV;
-	uint8 scalingH = _maxFactorH / _currentComp->factorH;
-
-	// Convert coordinates from MCU blocks to pixels
-	x <<= 3;
-	y <<= 3;
-
-	for (uint8 j = 0; j < 8; j++) {
-		for (uint16 sV = 0; sV < scalingV; sV++) {
-			// Get the beginning of the block line
-			byte *ptr = (byte *)_currentComp->surface.getBasePtr(x * scalingH, (y + j) * scalingV + sV);
-
-			for (uint8 i = 0; i < 8; i++) {
-				for (uint16 sH = 0; sH < scalingH; sH++) {
-					*ptr = (byte)(block[j * 8 + i]);
-					ptr++;
-				}
-			}
-		}
-	}
-
-	return true;
-}
-
-int16 JPEG::readDC() {
-	// DC is type 0
-	uint8 tableNum = _currentComp->DCentropyTableSelector << 1;
-
-	// Get the number of bits to read
-	uint8 numBits = readHuff(tableNum);
-
-	// Read the requested bits
-	return readSignedBits(numBits);
-}
-
-void JPEG::readAC(int16 *out) {
-	// AC is type 1
-	uint8 tableNum = (_currentComp->ACentropyTableSelector << 1) + 1;
-
-	// Start reading AC element 1
-	uint8 cur = 1;
-	while (cur < 64) {
-		uint8 s = readHuff(tableNum);
-		uint8 r = s >> 4;
-		s &= 0xF;
-
-		if (s == 0) {
-			if (r == 15) {
-				// Skip 16 values
-				cur += 16;
-			} else {
-				// EOB: end of block
-				cur = 64;
-			}
-		} else {
-			// Skip r values
-			cur += r;
-
-			// Read the next value
-			out[cur] = readSignedBits(s);
-			cur++;
-		}
-	}
-}
-
-int16 JPEG::readSignedBits(uint8 numBits) {
-	uint16 ret = 0;
-	if (numBits > 16)
-		error("requested %d bits", numBits); //XXX
-
-	// MSB=0 for negatives, 1 for positives
-	for (int i = 0; i < numBits; i++)
-		ret = (ret << 1) + readBit();
-
-	// Extend sign bits (PAG109)
-	if (!(ret >> (numBits - 1))) {
-		uint16 tmp = ((uint16)-1 << numBits) + 1;
-		ret = ret + tmp;
-	}
-	return ret;
-}
-
-// TODO: optimize?
-uint8 JPEG::readHuff(uint8 table) {
-	bool foundCode = false;
-	uint8 val = 0;
-
-	uint8 cur = 0;
-	uint8 codeSize = 1;
-	uint16 code = readBit();
-	while (!foundCode) {
-		// Prepare a code of the current size
-		while (codeSize < _huff[table].sizes[cur]) {
-			code = (code << 1) + readBit();
-			codeSize++;
-		}
-
-		// Compare the codes of the current size
-		while (!foundCode && (codeSize == _huff[table].sizes[cur])) {
-			if (code == _huff[table].codes[cur]) {
-				// Found the code
-				val = _huff[table].values[cur];
-				foundCode = true;
-			} else {
-				// Continue reading
-				cur++;
-			}
-		}
-	}
-
-	return val;
-}
-
-uint8 JPEG::readBit() {
-	// Read a whole byte if necessary
-	if (_bitsNumber == 0) {
-		_bitsData = _stream->readByte();
-		_bitsNumber = 8;
-
-		// Detect markers
-		if (_bitsData == 0xFF) {
-			uint8 byte2 = _stream->readByte();
-
-			// A stuffed 0 validates the previous byte
-			if (byte2 != 0) {
-				if (byte2 == 0xDC) {
-					// DNL marker: Define Number of Lines
-					// TODO: terminate scan
-					warning("DNL marker detected: terminate scan");
-				} else if (byte2 >= 0xD0 && byte2 <= 0xD7) {
-					debug(7, "RST%d marker detected", byte2 & 7);
-					_bitsData = _stream->readByte();
-				} else {
-					warning("Error: marker 0x%02X read in entropy data", byte2);
-				}
-			}
-		}
-	}
-	_bitsNumber--;
-
-	return (_bitsData & (1 << _bitsNumber)) ? 1 : 0;
-}
-
-Surface *JPEG::getComponent(uint c) {
-	for (int i = 0; i < _numComp; i++)
-		if (_components[i].id == c) // We found the desired component
-			return &_components[i].surface;
-
-	error("JPEG::getComponent: No component %d present", c);
-	return NULL;
-}
-
-} // End of Graphics namespace
diff --git a/graphics/jpeg.h b/graphics/jpeg.h
deleted file mode 100644
index b877914..0000000
--- a/graphics/jpeg.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* 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 GRAPHICS_JPEG_H
-#define GRAPHICS_JPEG_H
-
-#include "graphics/surface.h"
-
-namespace Common {
-class SeekableReadStream;
-}
-
-namespace Graphics {
-
-struct PixelFormat;
-
-#define JPEG_MAX_QUANT_TABLES 4
-#define JPEG_MAX_HUFF_TABLES 2
-
-class JPEG {
-public:
-	JPEG();
-	~JPEG();
-
-	bool read(Common::SeekableReadStream *str);
-	bool isLoaded() const { return _numComp && _w && _h; }
-	uint16 getWidth() const { return _w; }
-	uint16 getHeight() const { return _h; }
-
-	Surface *getComponent(uint c);
-	Surface *getSurface(const PixelFormat &format);
-
-private:
-	void reset();
-
-	Common::SeekableReadStream *_stream;
-	uint16 _w, _h;
-	uint16 _restartInterval;
-
-	// Image components
-	uint8 _numComp;
-	struct Component {
-		// Global values
-		uint8 id;
-		uint8 factorH;
-		uint8 factorV;
-		uint8 quantTableSelector;
-
-		// Scan specific values
-		uint8 DCentropyTableSelector;
-		uint8 ACentropyTableSelector;
-		int16 DCpredictor;
-
-		// Result image for this component
-		Surface surface;
-	};
-
-	Component *_components;
-
-	// Scan components
-	uint8 _numScanComp;
-	Component **_scanComp;
-	Component *_currentComp;
-
-	// Maximum sampling factors, used to calculate the interleaving of the MCU
-	uint8 _maxFactorV;
-	uint8 _maxFactorH;
-
-	// Quantization tables
-	uint16 *_quant[JPEG_MAX_QUANT_TABLES];
-
-	// Huffman tables
-	struct HuffmanTable {
-		uint8 count;
-		uint8 *values;
-		uint8 *sizes;
-		uint16 *codes;
-	} _huff[2 * JPEG_MAX_HUFF_TABLES];
-
-	// Marker read functions
-	bool readJFIF();
-	bool readSOF0();
-	bool readDHT();
-	bool readSOS();
-	bool readDQT();
-	bool readDRI();
-
-	// Helper functions
-	bool readMCU(uint16 xMCU, uint16 yMCU);
-	bool readDataUnit(uint16 x, uint16 y);
-	int16 readDC();
-	void readAC(int16 *out);
-	int16 readSignedBits(uint8 numBits);
-
-	// Huffman decoding
-	uint8 readHuff(uint8 table);
-	uint8 readBit();
-	uint8 _bitsData;
-	uint8 _bitsNumber;
-
-	// Inverse Discrete Cosine Transformation
-	static void idct1D8x8(int32 src[8], int32 dest[64], int32 ps, int32 half);
-	static void idct2D8x8(int32 block[64]);
-};
-
-} // End of Graphics namespace
-
-#endif // GRAPHICS_JPEG_H
diff --git a/graphics/module.mk b/graphics/module.mk
index 08f6d0b..d9f3802 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -12,7 +12,6 @@ MODULE_OBJS := \
 	fonts/ttf.o \
 	fonts/winfont.o \
 	iff.o \
-	jpeg.o \
 	maccursor.o \
 	png.o \
 	primitives.o \
@@ -26,6 +25,7 @@ MODULE_OBJS := \
 	wincursor.o \
 	yuv_to_rgb.o \
 	decoders/bmp.o \
+	decoders/jpeg.o \
 	decoders/pict.o
 
 ifdef USE_SCALERS
diff --git a/video/codecs/mjpeg.cpp b/video/codecs/mjpeg.cpp
index 248a80d..10fd9d7 100644
--- a/video/codecs/mjpeg.cpp
+++ b/video/codecs/mjpeg.cpp
@@ -22,8 +22,8 @@
 
 #include "common/system.h"
 #include "common/textconsole.h"
-#include "graphics/jpeg.h"
 #include "graphics/surface.h"
+#include "graphics/decoders/jpeg.h"
 
 #include "video/codecs/mjpeg.h"
 
@@ -34,38 +34,31 @@ class SeekableReadStream;
 namespace Video {
 
 JPEGDecoder::JPEGDecoder() : Codec() {
-	_jpeg = new Graphics::JPEG();
 	_pixelFormat = g_system->getScreenFormat();
 	_surface = NULL;
 }
 
 JPEGDecoder::~JPEGDecoder() {
-	delete _jpeg;
-
 	if (_surface) {
 		_surface->free();
 		delete _surface;
 	}
 }
 
-const Graphics::Surface *JPEGDecoder::decodeImage(Common::SeekableReadStream* stream) {
-	if (!_jpeg->read(stream)) {
+const Graphics::Surface *JPEGDecoder::decodeImage(Common::SeekableReadStream *stream) {
+	Graphics::JPEGDecoder jpeg;
+
+	if (!jpeg.loadStream(*stream)) {
 		warning("Failed to decode JPEG frame");
 		return 0;
 	}
 
-	if (!_surface) {
-		_surface = new Graphics::Surface();
-		_surface->create(_jpeg->getWidth(), _jpeg->getHeight(), _pixelFormat);
+	if (_surface) {
+		_surface->free();
+		delete _surface;
 	}
 
-	Graphics::Surface *frame = _jpeg->getSurface(_pixelFormat);
-	assert(frame);
-
-	_surface->copyFrom(*frame);
-
-	frame->free();
-	delete frame;
+	_surface = jpeg.getSurface()->convertTo(_pixelFormat);
 
 	return _surface;
 }
diff --git a/video/codecs/mjpeg.h b/video/codecs/mjpeg.h
index 45cb57d..0c3b668 100644
--- a/video/codecs/mjpeg.h
+++ b/video/codecs/mjpeg.h
@@ -31,7 +31,7 @@ class SeekableReadStream;
 }
 
 namespace Graphics {
-class JPEG;
+struct Surface;
 }
 
 namespace Video {
@@ -50,7 +50,6 @@ public:
 
 private:
 	Graphics::PixelFormat _pixelFormat;
-	Graphics::JPEG *_jpeg;
 	Graphics::Surface *_surface;
 };
 


Commit: b6d2a11432c16b12fea48830f7aa131dba091ef7
    https://github.com/scummvm/scummvm/commit/b6d2a11432c16b12fea48830f7aa131dba091ef7
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:48-07:00

Commit Message:
GRAPHICS: Make the JPEG code use the new YUV to RGB converter

Changed paths:
    graphics/decoders/jpeg.cpp
    graphics/yuv_to_rgb.cpp
    graphics/yuv_to_rgb.h



diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp
index 0cd2388..a871377 100644
--- a/graphics/decoders/jpeg.cpp
+++ b/graphics/decoders/jpeg.cpp
@@ -20,8 +20,8 @@
  *
  */
 
-#include "graphics/conversion.h"
 #include "graphics/pixelformat.h"
+#include "graphics/yuv_to_rgb.h"
 #include "graphics/decoders/jpeg.h"
 
 #include "common/debug.h"
@@ -81,13 +81,7 @@ const Surface *JPEGDecoder::getSurface() const {
 	const Graphics::Surface *uComponent = getComponent(2);
 	const Graphics::Surface *vComponent = getComponent(3);
 
-	for (uint16 i = 0; i < _h; i++) {
-		for (uint16 j = 0; j < _w; j++) {
-			byte r = 0, g = 0, b = 0;
-			YUV2RGB(*((const byte *)yComponent->getBasePtr(j, i)), *((const byte *)uComponent->getBasePtr(j, i)), *((const byte *)vComponent->getBasePtr(j, i)), r, g, b);
-			*((uint32 *)_rgbSurface->getBasePtr(j, i)) = _rgbSurface->format.RGBToColor(r, g, b);
-		}
-	}
+	convertYUV444ToRGB(_rgbSurface, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch);
 
 	return _rgbSurface;
 }
diff --git a/graphics/yuv_to_rgb.cpp b/graphics/yuv_to_rgb.cpp
index feda48b..ac7f217 100644
--- a/graphics/yuv_to_rgb.cpp
+++ b/graphics/yuv_to_rgb.cpp
@@ -199,6 +199,52 @@ namespace Graphics {
 	*((PixelInt *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b])
 
 template<typename PixelInt>
+void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+	// Keep the tables in pointers here to avoid a dereference on each pixel
+	const int16 *Cr_r_tab = lookup->_colorTab;
+	const int16 *Cr_g_tab = Cr_r_tab + 256;
+	const int16 *Cb_g_tab = Cr_g_tab + 256;
+	const int16 *Cb_b_tab = Cb_g_tab + 256;
+	const uint32 *rgbToPix = lookup->_rgbToPix;
+
+	for (int h = 0; h < yHeight; h++) {
+		for (int w = 0; w < yWidth; w++) {
+			register const uint32 *L;
+
+			int16 cr_r  = Cr_r_tab[*vSrc];
+			int16 crb_g = Cr_g_tab[*vSrc] + Cb_g_tab[*uSrc];
+			int16 cb_b  = Cb_b_tab[*uSrc];
+			++uSrc;
+			++vSrc;
+
+			PUT_PIXEL(*ySrc, dstPtr);
+			ySrc++;
+			dstPtr += sizeof(PixelInt);
+		}
+
+		dstPtr += dstPitch - yWidth * sizeof(PixelInt);
+		ySrc += yPitch - yWidth;
+		uSrc += uvPitch - yWidth;
+		vSrc += uvPitch - yWidth;
+	}
+}
+
+void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
+	// Sanity checks
+	assert(dst && dst->pixels);
+	assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
+	assert(ySrc && uSrc && vSrc);
+
+	const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format);
+
+	// Use a templated function to avoid an if check on every pixel
+	if (dst->format.bytesPerPixel == 2)
+		convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+	else
+		convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch);
+}
+
+template<typename PixelInt>
 void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) {
 	int halfHeight = yHeight >> 1;
 	int halfWidth = yWidth >> 1;
diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h
index 259ba09..8e02504 100644
--- a/graphics/yuv_to_rgb.h
+++ b/graphics/yuv_to_rgb.h
@@ -23,6 +23,7 @@
 /**
  * @file
  * YUV to RGB conversion used in engines:
+ * - mohawk
  * - scumm (he)
  * - sword25
  */
@@ -36,6 +37,20 @@
 namespace Graphics {
 
 /**
+ * Convert a YUV444 image to an RGB surface
+ *
+ * @param dst     the destination surface
+ * @param ySrc    the source of the y component
+ * @param uSrc    the source of the u component
+ * @param vSrc    the source of the v component
+ * @param yWidth  the width of the y surface
+ * @param yHeight the height of the y surface
+ * @param yPitch  the pitch of the y surface
+ * @param uvPitch the pitch of the u and v surfaces
+ */
+void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch);
+
+/**
  * Convert a YUV420 image to an RGB surface
  *
  * @param dst     the destination surface


Commit: dd9790c1c8a10c8a9ffdef4d45a30ce078f0f939
    https://github.com/scummvm/scummvm/commit/dd9790c1c8a10c8a9ffdef4d45a30ce078f0f939
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:48-07:00

Commit Message:
GRAPHICS: Make PNG signature more readable

Changed paths:
    graphics/png.cpp



diff --git a/graphics/png.cpp b/graphics/png.cpp
index cea8b57..156c405 100644
--- a/graphics/png.cpp
+++ b/graphics/png.cpp
@@ -207,7 +207,7 @@ bool PNG::read(Common::SeekableReadStream *str) {
 	_stream = str;
 
 	// First, check the PNG signature
-	if (_stream->readUint32BE() != MKTAG(0x89, 0x50, 0x4e, 0x47)) {
+	if (_stream->readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) {
 		delete _stream;
 		return false;
 	}


Commit: a658fc660e5932410b91c00d3eef81963ed02972
    https://github.com/scummvm/scummvm/commit/a658fc660e5932410b91c00d3eef81963ed02972
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:48-07:00

Commit Message:
GRAPHICS: Move PNG to the ImageDecoder interface

Changed paths:
  A graphics/decoders/png.cpp
  A graphics/decoders/png.h
  R graphics/png.cpp
  R graphics/png.h
    engines/sword25/gfx/image/imgloader.cpp
    graphics/module.mk



diff --git a/engines/sword25/gfx/image/imgloader.cpp b/engines/sword25/gfx/image/imgloader.cpp
index 1df0fba..e103626 100644
--- a/engines/sword25/gfx/image/imgloader.cpp
+++ b/engines/sword25/gfx/image/imgloader.cpp
@@ -33,18 +33,19 @@
 #include "sword25/gfx/image/image.h"
 #include "sword25/gfx/image/imgloader.h"
 #include "graphics/pixelformat.h"
-#include "graphics/png.h"
+#include "graphics/decoders/png.h"
 
 namespace Sword25 {
 
 bool ImgLoader::decodePNGImage(const byte *fileDataPtr, uint fileSize, byte *&uncompressedDataPtr, int &width, int &height, int &pitch) {
 	Common::MemoryReadStream *fileStr = new Common::MemoryReadStream(fileDataPtr, fileSize, DisposeAfterUse::NO);
-	Graphics::PNG *png = new Graphics::PNG();
-	if (!png->read(fileStr))	// the fileStr pointer, and thus pFileData will be deleted after this is done
+
+	Graphics::PNGDecoder png;
+	if (!png.loadStream(*fileStr)) // the fileStr pointer, and thus pFileData will be deleted after this is done
 		error("Error while reading PNG image");
 
-	Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24);
-	Graphics::Surface *pngSurface = png->getSurface(format);
+	const Graphics::Surface *sourceSurface = png.getSurface();
+	Graphics::Surface *pngSurface = sourceSurface->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24), png.getPalette());
 
 	width = pngSurface->w;
 	height = pngSurface->h;
@@ -53,7 +54,7 @@ bool ImgLoader::decodePNGImage(const byte *fileDataPtr, uint fileSize, byte *&un
 	pngSurface->free();
 
 	delete pngSurface;
-	delete png;
+	delete fileStr;
 
 	// Signal success
 	return true;
diff --git a/graphics/decoders/png.cpp b/graphics/decoders/png.cpp
new file mode 100644
index 0000000..b87b6fd
--- /dev/null
+++ b/graphics/decoders/png.cpp
@@ -0,0 +1,503 @@
+/* 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.
+ *
+ */
+
+#include "graphics/decoders/png.h"
+
+#include "graphics/pixelformat.h"
+#include "graphics/surface.h"
+
+#include "common/endian.h"
+#include "common/memstream.h"
+#include "common/stream.h"
+#include "common/types.h"
+#include "common/util.h"
+#include "common/zlib.h"
+
+// PNG decoder, based on the W3C specs:
+// http://www.w3.org/TR/PNG/
+// Parts of the code have been adapted from LodePNG, by Lode Vandevenne:
+// http://members.gamedev.net/lode/projects/LodePNG/
+
+/*
+LodePNG version 20101211
+
+Copyright (c) 2005-2010 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+    1. The origin of this software must not be misrepresented; you must not
+    claim that you wrote the original software. If you use this software
+    in a product, an acknowledgment in the product documentation would be
+    appreciated but is not required.
+
+    2. Altered source versions must be plainly marked as such, and must not be
+    misrepresented as being the original software.
+
+    3. This notice may not be removed or altered from any source
+    distribution.
+*/
+
+namespace Graphics {
+
+enum PNGChunks {
+	// == Critical chunks =====================================================
+	kChunkIHDR = MKTAG('I','H','D','R'),	// Image header
+	kChunkIDAT = MKTAG('I','D','A','T'),	// Image data
+	kChunkPLTE = MKTAG('P','L','T','E'),	// Palette
+	kChunkIEND = MKTAG('I','E','N','D'),	// Image trailer
+	// == Ancillary chunks ====================================================
+	kChunktRNS = MKTAG('t','R','N','S')	// Transparency
+	// All of the other ancillary chunks are ignored. They're added here for
+	// reference only.
+	// cHRM - Primary chromacities and white point
+	// gAMA - Image gamma
+	// iCCP - Embedded ICC profile
+	// sBIT - Significant bits
+	// sRGB - Standard RGB color space
+	// tEXT - Textual data
+	// sTXt - Compressed textual data
+	// iTXt - International textual data
+	// bKGD - Background color
+	// hIST - Image histogram
+	// pHYs - Physical pixel dimensions
+	// sPLT - Suggested palette
+	// tIME - Image last-modification time
+};
+
+// Refer to http://www.w3.org/TR/PNG/#9Filters
+enum PNGFilters {
+	kFilterNone    = 0,
+	kFilterSub     = 1,
+	kFilterUp      = 2,
+	kFilterAverage = 3,
+	kFilterPaeth   = 4
+};
+
+PNGDecoder::PNGDecoder() : _compressedBuffer(0), _compressedBufferSize(0),
+			_transparentColorSpecified(false), _outputSurface(0) {
+}
+
+PNGDecoder::~PNGDecoder() {
+	destroy();
+}
+
+void PNGDecoder::destroy() {
+	if (_outputSurface) {
+		_outputSurface->free();
+		delete _outputSurface;
+		_outputSurface = 0;
+	}
+}
+
+bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
+	destroy();
+
+	uint32 chunkLength = 0, chunkType = 0;
+	_stream = &stream;
+
+	// First, check the PNG signature
+	if (_stream->readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) {
+		delete _stream;
+		return false;
+	}
+	if (_stream->readUint32BE() != MKTAG(0x0d, 0x0a, 0x1a, 0x0a)) {
+		delete _stream;
+		return false;
+	}
+
+	// Start reading chunks till we reach an IEND chunk
+	while (chunkType != kChunkIEND) {
+		// The chunk length does not include the type or CRC bytes
+		chunkLength = _stream->readUint32BE();
+		chunkType = _stream->readUint32BE();
+
+		switch (chunkType) {
+		case kChunkIHDR:
+			readHeaderChunk();
+			break;
+		case kChunkIDAT:
+			if (_compressedBufferSize == 0) {
+				_compressedBufferSize += chunkLength;
+				_compressedBuffer = (byte *)malloc(_compressedBufferSize);
+				_stream->read(_compressedBuffer, chunkLength);
+			} else {
+				// Expand the buffer
+				uint32 prevSize = _compressedBufferSize;
+				_compressedBufferSize += chunkLength;
+				byte *tmp = new byte[prevSize];
+				memcpy(tmp, _compressedBuffer, prevSize);
+				free(_compressedBuffer);
+				_compressedBuffer = (byte *)malloc(_compressedBufferSize);
+				memcpy(_compressedBuffer, tmp, prevSize);
+				delete[] tmp;
+				_stream->read(_compressedBuffer + prevSize, chunkLength);
+			}
+			break;
+		case kChunkPLTE:	// only available in indexed PNGs
+			if (_header.colorType != kIndexed)
+				error("A palette chunk has been found in a non-indexed PNG file");
+			if (chunkLength % 3 != 0)
+				error("Palette chunk not divisible by 3");
+
+			_paletteEntries = chunkLength / 3;
+			_stream->read(_palette, _paletteEntries * 3);
+			memset(_paletteTransparency, 0xff, sizeof(_paletteTransparency));
+			break;
+		case kChunkIEND:
+			// End of stream
+			break;
+		case kChunktRNS:
+			readTransparencyChunk(chunkLength);
+			break;
+		default:
+			// Skip the chunk content
+			_stream->skip(chunkLength);
+			break;
+		}
+
+		if (chunkType != kChunkIEND)
+			_stream->skip(4);	// skip the chunk CRC checksum
+	}
+
+	// We no longer need the file stream, thus close it here
+	_stream = 0;
+
+	// Unpack the compressed buffer
+	Common::MemoryReadStream *compData = new Common::MemoryReadStream(_compressedBuffer, _compressedBufferSize, DisposeAfterUse::YES);
+	_imageData = Common::wrapCompressedReadStream(compData);
+
+	// Construct the final image
+	constructImage();
+
+	// Close the uncompressed stream, which will also delete the memory stream,
+	// and thus the original compressed buffer
+	delete _imageData;
+
+	return true;
+}
+
+/**
+ * Paeth predictor, used by PNG filter type 4
+ * The parameters are of signed 16-bit integers, but should come
+ * from unsigned chars. The integers  are only needed to make
+ * the paeth calculation correct.
+ *
+ * Taken from lodePNG, with a slight patch:
+ * http://www.atalasoft.com/cs/blogs/stevehawley/archive/2010/02/23/libpng-you-re-doing-it-wrong.aspx
+ */
+byte PNGDecoder::paethPredictor(int16 a, int16 b, int16 c) {
+  int16 pa = ABS<int16>(b - c);
+  int16 pb = ABS<int16>(a - c);
+  int16 pc = ABS<int16>(a + b - c - c);
+
+  if (pa <= MIN<int16>(pb, pc))
+	  return (byte)a;
+  else if (pb <= pc)
+	  return (byte)b;
+  else
+	  return (byte)c;
+}
+
+/**
+ * Unfilters a filtered PNG scan line.
+ * PNG filters are defined in: http://www.w3.org/TR/PNG/#9Filters
+ * Note that filters are always applied to bytes
+ *
+ * Taken from lodePNG
+ */
+void PNGDecoder::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length) {
+	uint16 i;
+
+	switch (filterType) {
+	case kFilterNone:		// no change
+		for (i = 0; i < length; i++)
+			dest[i] = scanLine[i];
+		break;
+	case kFilterSub:		// add the bytes to the left
+		for (i = 0; i < byteWidth; i++)
+			dest[i] = scanLine[i];
+		for (i = byteWidth; i < length; i++)
+			dest[i] = scanLine[i] + dest[i - byteWidth];
+		break;
+	case kFilterUp:			// add the bytes of the above scanline
+		if (prevLine) {
+			for (i = 0; i < length; i++)
+				dest[i] = scanLine[i] + prevLine[i];
+		} else {
+			for (i = 0; i < length; i++)
+				dest[i] = scanLine[i];
+		}
+		break;
+	case kFilterAverage:	// average value of the left and top left
+		if (prevLine) {
+			for (i = 0; i < byteWidth; i++)
+				dest[i] = scanLine[i] + prevLine[i] / 2;
+			for (i = byteWidth; i < length; i++)
+				dest[i] = scanLine[i] + ((dest[i - byteWidth] + prevLine[i]) / 2);
+		} else {
+			for (i = 0; i < byteWidth; i++)
+				dest[i] = scanLine[i];
+			for (i = byteWidth; i < length; i++)
+				dest[i] = scanLine[i] + dest[i - byteWidth] / 2;
+		}
+		break;
+	case kFilterPaeth:		// Paeth filter: http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth
+		if (prevLine) {
+			for(i = 0; i < byteWidth; i++)
+				dest[i] = (scanLine[i] + prevLine[i]); // paethPredictor(0, prevLine[i], 0) is always prevLine[i]
+			for(i = byteWidth; i < length; i++)
+				dest[i] = (scanLine[i] + paethPredictor(dest[i - byteWidth], prevLine[i], prevLine[i - byteWidth]));
+		} else {
+			for(i = 0; i < byteWidth; i++)
+				dest[i] = scanLine[i];
+			for(i = byteWidth; i < length; i++)
+				dest[i] = (scanLine[i] + dest[i - byteWidth]); // paethPredictor(dest[i - byteWidth], 0, 0) is always dest[i - byteWidth]
+		}
+		break;
+	default:
+		error("Unknown line filter");
+	}
+
+}
+
+int PNGDecoder::getBytesPerPixel() const {
+	return (getNumColorChannels() * _header.bitDepth + 7) / 8;
+}
+
+void PNGDecoder::constructImage() {
+	assert (_header.bitDepth != 0);
+
+	int bytesPerPixel = getBytesPerPixel();
+	int pitch = bytesPerPixel * _header.width;
+	byte *unfilteredSurface = new byte[pitch * _header.height];
+	byte *dest = unfilteredSurface;
+	uint16 scanLineWidth = (_header.width * getNumColorChannels() * _header.bitDepth + 7) / 8;
+	byte *scanLine = new byte[scanLineWidth];
+	byte *prevLine = 0;
+
+	switch(_header.interlaceType) {
+	case kNonInterlaced:
+		for (uint16 y = 0; y < _header.height; y++) {
+			byte filterType = _imageData->readByte();
+			_imageData->read(scanLine, scanLineWidth);
+			unfilterScanLine(dest, scanLine, prevLine, bytesPerPixel, filterType, scanLineWidth);
+			prevLine = dest;
+			dest += pitch;
+		}
+		break;
+	case kInterlaced:
+		// Theoretically, this shouldn't be needed, as interlacing is only
+		// useful for web images. Interlaced PNG images require more complex
+		// handling, so unless having support for such images is needed, there
+		// is no reason to add support for them.
+		error("TODO: Support for interlaced PNG images");
+		break;
+	}
+
+	delete[] scanLine;
+
+	constructOutput(unfilteredSurface);
+	delete[] unfilteredSurface;
+}
+
+Graphics::PixelFormat PNGDecoder::findPixelFormat() const {
+	// Try to find the best pixel format based on what we have here
+	// Which is basically 8bpp for paletted non-transparent
+	// and 32bpp for everything else
+
+	switch (_header.colorType) {
+	case kIndexed:
+		if (!_transparentColorSpecified)
+			return Graphics::PixelFormat::createFormatCLUT8();
+		// fall through
+	case kGrayScale:
+	case kTrueColor:
+	case kGrayScaleWithAlpha:
+	case kTrueColorWithAlpha:
+		// We'll go with standard RGBA 32-bit
+		return Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
+	}
+
+	error("Unknown PNG color type");
+	return Graphics::PixelFormat();
+}
+
+void PNGDecoder::constructOutput(const byte *surface) {
+	_outputSurface = new Graphics::Surface();
+	_outputSurface->create(_header.width, _header.height, findPixelFormat());
+
+	const byte *src = surface;
+	byte a = 0xFF;
+	int bytesPerPixel = getBytesPerPixel();
+
+	if (_header.colorType != kIndexed) {
+		if (_header.colorType == kTrueColor || 
+			_header.colorType == kTrueColorWithAlpha) {
+			if (bytesPerPixel != 3 && bytesPerPixel != 4)
+				error("Unsupported truecolor PNG format");
+		} else if (_header.colorType == kGrayScale ||
+				   _header.colorType == kGrayScaleWithAlpha) {
+			if (bytesPerPixel != 1 && bytesPerPixel != 2)
+				error("Unsupported grayscale PNG format");
+		}
+
+		for (uint16 i = 0; i < _outputSurface->h; i++) {
+			for (uint16 j = 0; j < _outputSurface->w; j++) {
+				uint32 result = 0;
+
+				switch (bytesPerPixel) {
+				case 1:	// Grayscale
+					if (_transparentColorSpecified)
+						a = (src[0] == _transparentColor[0]) ? 0 : 0xFF;
+					result = _outputSurface->format.ARGBToColor(a, src[0], src[0], src[0]);
+					break;
+				case 2: // Grayscale + alpha
+					result = _outputSurface->format.ARGBToColor(src[1], src[0], src[0], src[0]);
+					break;
+				case 3: // RGB
+					if (_transparentColorSpecified) {
+						bool isTransparentColor = (src[0] == _transparentColor[0] &&
+												   src[1] == _transparentColor[1] &&
+												   src[2] == _transparentColor[2]);
+						a = isTransparentColor ? 0 : 0xFF;
+					}
+
+					result = _outputSurface->format.ARGBToColor(a, src[0], src[1], src[2]);
+					break;
+				case 4: // RGBA
+					result = _outputSurface->format.ARGBToColor(src[3], src[0], src[1], src[2]);
+					break;
+				}
+
+				*((uint32 *)_outputSurface->getBasePtr(j, i)) = result;
+				src += bytesPerPixel;
+			}
+		}
+	} else {
+		uint32 mask = (0xff >> (8 - _header.bitDepth)) << (8 - _header.bitDepth);
+
+		// Convert the indexed surface to the target pixel format
+		for (uint16 i = 0; i < _outputSurface->h; i++) {
+			int data = 0;
+			int bitCount = 8;
+			const byte *src1 = src;
+
+			for (uint16 j = 0; j < _outputSurface->w; j++) {
+				if (bitCount == 8) {
+					data = *src;
+					src++;
+				}
+
+				byte index = (data & mask) >> (8 - _header.bitDepth);
+				data = (data << _header.bitDepth) & 0xff;
+				bitCount -= _header.bitDepth;
+
+				if (bitCount == 0)
+					bitCount = 8;
+
+				if (_transparentColorSpecified) {
+					byte r = _palette[index * 3 + 0];
+					byte g = _palette[index * 3 + 1];
+					byte b = _palette[index * 3 + 2];
+					a = _paletteTransparency[index];
+					*((uint32 *)_outputSurface->getBasePtr(j, i)) = _outputSurface->format.ARGBToColor(a, r, g, b);
+				} else {
+					*((byte *)_outputSurface->getBasePtr(j, i)) = index;
+				}
+			}
+
+			src = src1 + _outputSurface->w;
+		}
+	}
+}
+
+void PNGDecoder::readHeaderChunk() {
+	_header.width = _stream->readUint32BE();
+	_header.height = _stream->readUint32BE();
+	_header.bitDepth = _stream->readByte();
+	if (_header.bitDepth > 8)
+		error("Only PNGs with a bit depth of 1-8 bits are supported (i.e. PNG24)");
+	_header.colorType = (PNGColorType)_stream->readByte();
+	_header.compressionMethod = _stream->readByte();
+	// Compression methods: http://www.w3.org/TR/PNG/#10Compression
+	// Only compression method 0 (deflate) is documented and supported
+	if (_header.compressionMethod != 0)
+		error("Unknown PNG compression method: %d", _header.compressionMethod);
+	_header.filterMethod = _stream->readByte();
+	// Filter methods: http://www.w3.org/TR/PNG/#9Filters
+	// Only filter method 0 is documented and supported
+	if (_header.filterMethod != 0)
+		error("Unknown PNG filter method: %d", _header.filterMethod);
+	_header.interlaceType = (PNGInterlaceType)_stream->readByte();
+}
+
+byte PNGDecoder::getNumColorChannels() const {
+	switch (_header.colorType) {
+	case kGrayScale:
+		return 1; // Gray
+	case kTrueColor:
+		return 3; // RGB
+	case kIndexed:
+		return 1; // Indexed
+	case kGrayScaleWithAlpha:
+		return 2; // Gray + Alpha
+	case kTrueColorWithAlpha:
+		return 4; // RGBA
+	default:
+		error("Unknown color type");
+	}
+}
+
+void PNGDecoder::readTransparencyChunk(uint32 chunkLength) {
+	_transparentColorSpecified = true;
+
+	switch(_header.colorType) {
+	case kGrayScale:
+		_transparentColor[0] = _stream->readUint16BE();
+		_transparentColor[1] = _transparentColor[0];
+		_transparentColor[2] = _transparentColor[0];
+		break;
+	case kTrueColor:
+		_transparentColor[0] = _stream->readUint16BE();
+		_transparentColor[1] = _stream->readUint16BE();
+		_transparentColor[2] = _stream->readUint16BE();
+		break;
+	case kIndexed:
+		_stream->read(_paletteTransparency, chunkLength);
+
+		// A transparency chunk may have less entries
+		// than the palette entries. The remaining ones
+		// are unmodified (set to 255). Check here:
+		// http://www.w3.org/TR/PNG/#11tRNS
+		break;
+	default:
+		error("Transparency chunk found in a PNG that has a separate transparency channel");
+	}
+}
+
+} // End of Graphics namespace
diff --git a/graphics/decoders/png.h b/graphics/decoders/png.h
new file mode 100644
index 0000000..1da0bea
--- /dev/null
+++ b/graphics/decoders/png.h
@@ -0,0 +1,135 @@
+/* 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.
+ *
+ */
+
+/*
+ * PNG decoder used in engines:
+ *  - sword25
+ * Dependencies:
+ *  - zlib
+ */
+
+#ifndef GRAPHICS_PNG_H
+#define GRAPHICS_PNG_H
+
+// PNG decoder, based on the W3C specs:
+// http://www.w3.org/TR/PNG/
+// Parts of the code have been adapted from LodePNG, by Lode Vandevenne:
+// http://members.gamedev.net/lode/projects/LodePNG/
+
+// All the numbers are BE: http://www.w3.org/TR/PNG/#7Integers-and-byte-order
+
+// Note: At the moment, this decoder only supports non-interlaced images, and
+// does not support truecolor/grayscale images with 16bit depth.
+//
+// Theoretically, interlaced images shouldn't be needed for games, as
+// interlacing is only useful for images in websites.
+//
+// PNG images with 16bit depth (i.e. 48bit images) are quite rare, and can
+// theoretically contain more than 16.7 millions of colors (the so-called "deep
+// color" representation). In essence, each of the R, G, B and A components in
+// them is specified with 2 bytes, instead of 1. However, the PNG specification
+// always refers to color components with 1 byte each, so this part of the spec
+// is a bit unclear. For now, these won't be supported, until a suitable sample
+// is found.
+
+#include "common/scummsys.h"
+#include "common/textconsole.h"
+#include "graphics/decoders/image_decoder.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Graphics {
+
+struct Surface;
+struct PixelFormat;
+
+class PNGDecoder : public ImageDecoder {
+public:
+	PNGDecoder();
+	~PNGDecoder();
+
+	bool loadStream(Common::SeekableReadStream &stream);
+	void destroy();
+	const Graphics::Surface *getSurface() const { return _outputSurface; }
+	const byte *getPalette() const { return _palette; }
+
+private:
+	enum PNGColorType {
+		kGrayScale          = 0,	// bit depths: 1, 2, 4, 8, 16
+		kTrueColor          = 2,	// bit depths: 8, 16
+		kIndexed            = 3,	// bit depths: 1, 2, 4, 8
+		kGrayScaleWithAlpha = 4,	// bit depths: 8, 16
+		kTrueColorWithAlpha = 6		// bit depths: 8, 16
+	};
+
+	enum PNGInterlaceType {
+		kNonInterlaced      = 0,
+		kInterlaced         = 1
+	};
+
+	struct PNGHeader {
+		uint32 width;
+		uint32 height;
+		byte bitDepth;
+		PNGColorType colorType;
+		byte compressionMethod;
+		byte filterMethod;
+		PNGInterlaceType interlaceType;
+	};
+
+	void readHeaderChunk();
+	byte getNumColorChannels() const;
+
+	void readPaletteChunk();
+	void readTransparencyChunk(uint32 chunkLength);
+
+	void constructImage();
+	void unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length);
+	byte paethPredictor(int16 a, int16 b, int16 c);
+
+	// The original file stream
+	Common::SeekableReadStream *_stream;
+	// The unzipped image data stream
+	Common::SeekableReadStream *_imageData;
+
+	PNGHeader _header;
+
+	byte _palette[256 * 3];	// RGB
+	byte _paletteTransparency[256];
+	uint16 _paletteEntries;
+	uint16 _transparentColor[3];
+	bool _transparentColorSpecified;
+
+	byte *_compressedBuffer;
+	uint32 _compressedBufferSize;
+
+	Graphics::Surface *_outputSurface;
+	Graphics::PixelFormat findPixelFormat() const;
+	int getBytesPerPixel() const;
+	void constructOutput(const byte *surface);
+};
+
+} // End of namespace Graphics
+
+#endif // GRAPHICS_PNG_H
diff --git a/graphics/module.mk b/graphics/module.mk
index d9f3802..281f904 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -13,7 +13,6 @@ MODULE_OBJS := \
 	fonts/winfont.o \
 	iff.o \
 	maccursor.o \
-	png.o \
 	primitives.o \
 	scaler.o \
 	scaler/thumbnail_intern.o \
@@ -26,7 +25,8 @@ MODULE_OBJS := \
 	yuv_to_rgb.o \
 	decoders/bmp.o \
 	decoders/jpeg.o \
-	decoders/pict.o
+	decoders/pict.o \
+	decoders/png.o
 
 ifdef USE_SCALERS
 MODULE_OBJS += \
diff --git a/graphics/png.cpp b/graphics/png.cpp
deleted file mode 100644
index 156c405..0000000
--- a/graphics/png.cpp
+++ /dev/null
@@ -1,487 +0,0 @@
-/* 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.
- *
- */
-
-#include "graphics/png.h"
-
-#include "graphics/pixelformat.h"
-#include "graphics/surface.h"
-
-#include "common/endian.h"
-#include "common/memstream.h"
-#include "common/stream.h"
-#include "common/types.h"
-#include "common/util.h"
-#include "common/zlib.h"
-
-// PNG decoder, based on the W3C specs:
-// http://www.w3.org/TR/PNG/
-// Parts of the code have been adapted from LodePNG, by Lode Vandevenne:
-// http://members.gamedev.net/lode/projects/LodePNG/
-
-/*
-LodePNG version 20101211
-
-Copyright (c) 2005-2010 Lode Vandevenne
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-    1. The origin of this software must not be misrepresented; you must not
-    claim that you wrote the original software. If you use this software
-    in a product, an acknowledgment in the product documentation would be
-    appreciated but is not required.
-
-    2. Altered source versions must be plainly marked as such, and must not be
-    misrepresented as being the original software.
-
-    3. This notice may not be removed or altered from any source
-    distribution.
-*/
-
-namespace Graphics {
-
-enum PNGChunks {
-	// == Critical chunks =====================================================
-	kChunkIHDR = MKTAG('I','H','D','R'),	// Image header
-	kChunkIDAT = MKTAG('I','D','A','T'),	// Image data
-	kChunkPLTE = MKTAG('P','L','T','E'),	// Palette
-	kChunkIEND = MKTAG('I','E','N','D'),	// Image trailer
-	// == Ancillary chunks ====================================================
-	kChunktRNS = MKTAG('t','R','N','S')	// Transparency
-	// All of the other ancillary chunks are ignored. They're added here for
-	// reference only.
-	// cHRM - Primary chromacities and white point
-	// gAMA - Image gamma
-	// iCCP - Embedded ICC profile
-	// sBIT - Significant bits
-	// sRGB - Standard RGB color space
-	// tEXT - Textual data
-	// sTXt - Compressed textual data
-	// iTXt - International textual data
-	// bKGD - Background color
-	// hIST - Image histogram
-	// pHYs - Physical pixel dimensions
-	// sPLT - Suggested palette
-	// tIME - Image last-modification time
-};
-
-// Refer to http://www.w3.org/TR/PNG/#9Filters
-enum PNGFilters {
-	kFilterNone    = 0,
-	kFilterSub     = 1,
-	kFilterUp      = 2,
-	kFilterAverage = 3,
-	kFilterPaeth   = 4
-};
-
-PNG::PNG() : _compressedBuffer(0), _compressedBufferSize(0),
-			_unfilteredSurface(0), _transparentColorSpecified(false) {
-}
-
-PNG::~PNG() {
-	if (_unfilteredSurface) {
-		_unfilteredSurface->free();
-		delete _unfilteredSurface;
-	}
-}
-
-Graphics::Surface *PNG::getSurface(const PixelFormat &format) {
-	Graphics::Surface *output = new Graphics::Surface();
-	output->create(_unfilteredSurface->w, _unfilteredSurface->h, format);
-	byte *src = (byte *)_unfilteredSurface->pixels;
-	byte a = 0xFF;
-	byte bpp = _unfilteredSurface->format.bytesPerPixel;
-
-	if (_header.colorType != kIndexed) {
-		if (_header.colorType == kTrueColor || 
-			_header.colorType == kTrueColorWithAlpha) {
-			if (bpp != 3 && bpp != 4)
-				error("Unsupported truecolor PNG format");
-		} else if (_header.colorType == kGrayScale ||
-				   _header.colorType == kGrayScaleWithAlpha) {
-			if (bpp != 1 && bpp != 2)
-				error("Unsupported grayscale PNG format");
-		}
-
-		for (uint16 i = 0; i < output->h; i++) {
-			for (uint16 j = 0; j < output->w; j++) {
-				uint32 result = 0;
-
-				switch (bpp) {
-				case 1:	// Grayscale
-					if (_transparentColorSpecified)
-						a = (src[0] == _transparentColor[0]) ? 0 : 0xFF;
-					result = format.ARGBToColor(    a, src[0], src[0], src[0]);
-					break;
-				case 2: // Grayscale + alpha
-					result = format.ARGBToColor(src[1], src[0], src[0], src[0]);
-					break;
-				case 3: // RGB
-					if (_transparentColorSpecified) {
-						bool isTransparentColor = (src[0] == _transparentColor[0] &&
-												   src[1] == _transparentColor[1] &&
-												   src[2] == _transparentColor[2]);
-						a = isTransparentColor ? 0 : 0xFF;
-					}
-					result = format.ARGBToColor(     a, src[0], src[1], src[2]);
-					break;
-				case 4: // RGBA
-					result = format.ARGBToColor(src[3], src[0], src[1], src[2]);
-					break;
-				}
-
-				if (format.bytesPerPixel == 2) 	// 2bpp
-					*((uint16 *)output->getBasePtr(j, i)) = (uint16)result;
-				else	// 4bpp
-					*((uint32 *)output->getBasePtr(j, i)) = result;
-
-				src += bpp;
-			}
-		}
-	} else {
-		byte index, r, g, b;
-		uint32 mask = (0xff >> (8 - _header.bitDepth)) << (8 - _header.bitDepth);
-
-		// Convert the indexed surface to the target pixel format
-		for (uint16 i = 0; i < output->h; i++) {
-			int data = 0;
-			int bitCount = 8;
-			byte *src1 = src;
-
-			for (uint16 j = 0; j < output->w; j++) {
-				if (bitCount == 8) {
-					data = *src;
-					src++;
-				}
-
-				index = (data & mask) >> (8 - _header.bitDepth);
-				data = (data << _header.bitDepth) & 0xff;
-				bitCount -= _header.bitDepth;
-
-				if (bitCount == 0)
-					bitCount = 8;
-
-				r = _palette[index * 4 + 0];
-				g = _palette[index * 4 + 1];
-				b = _palette[index * 4 + 2];
-				a = _palette[index * 4 + 3];
-
-				if (format.bytesPerPixel == 2)
-					*((uint16 *)output->getBasePtr(j, i)) = format.ARGBToColor(a, r, g, b);
-				else
-					*((uint32 *)output->getBasePtr(j, i)) = format.ARGBToColor(a, r, g, b);
-			}
-			src = src1 + output->w;
-		}
-	}
-
-	return output;
-}
-
-bool PNG::read(Common::SeekableReadStream *str) {
-	uint32 chunkLength = 0, chunkType = 0;
-	_stream = str;
-
-	// First, check the PNG signature
-	if (_stream->readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) {
-		delete _stream;
-		return false;
-	}
-	if (_stream->readUint32BE() != MKTAG(0x0d, 0x0a, 0x1a, 0x0a)) {
-		delete _stream;
-		return false;
-	}
-
-	// Start reading chunks till we reach an IEND chunk
-	while (chunkType != kChunkIEND) {
-		// The chunk length does not include the type or CRC bytes
-		chunkLength = _stream->readUint32BE();
-		chunkType = _stream->readUint32BE();
-
-		switch (chunkType) {
-		case kChunkIHDR:
-			readHeaderChunk();
-			break;
-		case kChunkIDAT:
-			if (_compressedBufferSize == 0) {
-				_compressedBufferSize += chunkLength;
-				_compressedBuffer = (byte *)malloc(_compressedBufferSize);
-				_stream->read(_compressedBuffer, chunkLength);
-			} else {
-				// Expand the buffer
-				uint32 prevSize = _compressedBufferSize;
-				_compressedBufferSize += chunkLength;
-				byte *tmp = new byte[prevSize];
-				memcpy(tmp, _compressedBuffer, prevSize);
-				free(_compressedBuffer);
-				_compressedBuffer = (byte *)malloc(_compressedBufferSize);
-				memcpy(_compressedBuffer, tmp, prevSize);
-				delete[] tmp;
-				_stream->read(_compressedBuffer + prevSize, chunkLength);
-			}
-			break;
-		case kChunkPLTE:	// only available in indexed PNGs
-			if (_header.colorType != kIndexed)
-				error("A palette chunk has been found in a non-indexed PNG file");
-			if (chunkLength % 3 != 0)
-				error("Palette chunk not divisible by 3");
-			_paletteEntries = chunkLength / 3;
-			readPaletteChunk();
-			break;
-		case kChunkIEND:
-			// End of stream
-			break;
-		case kChunktRNS:
-			readTransparencyChunk(chunkLength);
-			break;
-		default:
-			// Skip the chunk content
-			_stream->skip(chunkLength);
-			break;
-		}
-
-		if (chunkType != kChunkIEND)
-			_stream->skip(4);	// skip the chunk CRC checksum
-	}
-
-	// We no longer need the file stream, thus close it here
-	delete _stream;
-	_stream = 0;
-
-	// Unpack the compressed buffer
-	Common::MemoryReadStream *compData = new Common::MemoryReadStream(_compressedBuffer, _compressedBufferSize, DisposeAfterUse::YES);
-	_imageData = Common::wrapCompressedReadStream(compData);
-
-	// Construct the final image
-	constructImage();
-
-	// Close the uncompressed stream, which will also delete the memory stream,
-	// and thus the original compressed buffer
-	delete _imageData;
-
-	return true;
-}
-
-/**
- * Paeth predictor, used by PNG filter type 4
- * The parameters are of signed 16-bit integers, but should come
- * from unsigned chars. The integers  are only needed to make
- * the paeth calculation correct.
- *
- * Taken from lodePNG, with a slight patch:
- * http://www.atalasoft.com/cs/blogs/stevehawley/archive/2010/02/23/libpng-you-re-doing-it-wrong.aspx
- */
-byte PNG::paethPredictor(int16 a, int16 b, int16 c) {
-  int16 pa = ABS<int16>(b - c);
-  int16 pb = ABS<int16>(a - c);
-  int16 pc = ABS<int16>(a + b - c - c);
-
-  if (pa <= MIN<int16>(pb, pc))
-	  return (byte)a;
-  else if (pb <= pc)
-	  return (byte)b;
-  else
-	  return (byte)c;
-}
-
-/**
- * Unfilters a filtered PNG scan line.
- * PNG filters are defined in: http://www.w3.org/TR/PNG/#9Filters
- * Note that filters are always applied to bytes
- *
- * Taken from lodePNG
- */
-void PNG::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length) {
-	uint16 i;
-
-	switch (filterType) {
-	case kFilterNone:		// no change
-		for (i = 0; i < length; i++)
-			dest[i] = scanLine[i];
-		break;
-	case kFilterSub:		// add the bytes to the left
-		for (i = 0; i < byteWidth; i++)
-			dest[i] = scanLine[i];
-		for (i = byteWidth; i < length; i++)
-			dest[i] = scanLine[i] + dest[i - byteWidth];
-		break;
-	case kFilterUp:			// add the bytes of the above scanline
-		if (prevLine) {
-			for (i = 0; i < length; i++)
-				dest[i] = scanLine[i] + prevLine[i];
-		} else {
-			for (i = 0; i < length; i++)
-				dest[i] = scanLine[i];
-		}
-		break;
-	case kFilterAverage:	// average value of the left and top left
-		if (prevLine) {
-			for (i = 0; i < byteWidth; i++)
-				dest[i] = scanLine[i] + prevLine[i] / 2;
-			for (i = byteWidth; i < length; i++)
-				dest[i] = scanLine[i] + ((dest[i - byteWidth] + prevLine[i]) / 2);
-		} else {
-			for (i = 0; i < byteWidth; i++)
-				dest[i] = scanLine[i];
-			for (i = byteWidth; i < length; i++)
-				dest[i] = scanLine[i] + dest[i - byteWidth] / 2;
-		}
-		break;
-	case kFilterPaeth:		// Paeth filter: http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth
-		if (prevLine) {
-			for(i = 0; i < byteWidth; i++)
-				dest[i] = (scanLine[i] + prevLine[i]); // paethPredictor(0, prevLine[i], 0) is always prevLine[i]
-			for(i = byteWidth; i < length; i++)
-				dest[i] = (scanLine[i] + paethPredictor(dest[i - byteWidth], prevLine[i], prevLine[i - byteWidth]));
-		} else {
-			for(i = 0; i < byteWidth; i++)
-				dest[i] = scanLine[i];
-			for(i = byteWidth; i < length; i++)
-				dest[i] = (scanLine[i] + dest[i - byteWidth]); // paethPredictor(dest[i - byteWidth], 0, 0) is always dest[i - byteWidth]
-		}
-		break;
-	default:
-		error("Unknown line filter");
-	}
-
-}
-
-void PNG::constructImage() {
-	assert (_header.bitDepth != 0);
-
-	byte *dest;
-	byte *scanLine;
-	byte *prevLine = 0;
-	byte filterType;
-	uint16 scanLineWidth = (_header.width * getNumColorChannels() * _header.bitDepth + 7) / 8;
-
-	if (_unfilteredSurface) {
-		_unfilteredSurface->free();
-		delete _unfilteredSurface;
-	}
-	_unfilteredSurface = new Graphics::Surface();
-	// TODO/FIXME: It seems we can not properly determine the format here. But maybe there is a way...
-	_unfilteredSurface->create(_header.width, _header.height, PixelFormat((getNumColorChannels() * _header.bitDepth + 7) / 8, 0, 0, 0, 0, 0, 0, 0, 0));
-	scanLine = new byte[_unfilteredSurface->pitch];
-	dest = (byte *)_unfilteredSurface->getBasePtr(0, 0);
-
-	switch(_header.interlaceType) {
-	case kNonInterlaced:
-		for (uint16 y = 0; y < _unfilteredSurface->h; y++) {
-			filterType = _imageData->readByte();
-			_imageData->read(scanLine, scanLineWidth);
-			unfilterScanLine(dest, scanLine, prevLine, _unfilteredSurface->format.bytesPerPixel, filterType, scanLineWidth);
-			prevLine = dest;
-			dest += _unfilteredSurface->pitch;
-		}
-		break;
-	case kInterlaced:
-		// Theoretically, this shouldn't be needed, as interlacing is only
-		// useful for web images. Interlaced PNG images require more complex
-		// handling, so unless having support for such images is needed, there
-		// is no reason to add support for them.
-		error("TODO: Support for interlaced PNG images");
-		break;
-	}
-
-	delete[] scanLine;
-}
-
-void PNG::readHeaderChunk() {
-	_header.width = _stream->readUint32BE();
-	_header.height = _stream->readUint32BE();
-	_header.bitDepth = _stream->readByte();
-	if (_header.bitDepth > 8)
-		error("Only PNGs with a bit depth of 1-8 bits are supported (i.e. PNG24)");
-	_header.colorType = (PNGColorType)_stream->readByte();
-	_header.compressionMethod = _stream->readByte();
-	// Compression methods: http://www.w3.org/TR/PNG/#10Compression
-	// Only compression method 0 (deflate) is documented and supported
-	if (_header.compressionMethod != 0)
-		error("Unknown PNG compression method: %d", _header.compressionMethod);
-	_header.filterMethod = _stream->readByte();
-	// Filter methods: http://www.w3.org/TR/PNG/#9Filters
-	// Only filter method 0 is documented and supported
-	if (_header.filterMethod != 0)
-		error("Unknown PNG filter method: %d", _header.filterMethod);
-	_header.interlaceType = (PNGInterlaceType)_stream->readByte();
-}
-
-byte PNG::getNumColorChannels() {
-	switch (_header.colorType) {
-	case kGrayScale:
-		return 1; // Gray
-	case kTrueColor:
-		return 3; // RGB
-	case kIndexed:
-		return 1; // Indexed
-	case kGrayScaleWithAlpha:
-		return 2; // Gray + Alpha
-	case kTrueColorWithAlpha:
-		return 4; // RGBA
-	default:
-		error("Unknown color type");
-	}
-}
-
-void PNG::readPaletteChunk() {
-	for (uint16 i = 0; i < _paletteEntries; i++) {
-		_palette[i * 4 + 0] = _stream->readByte();	// R
-		_palette[i * 4 + 1] = _stream->readByte();	// G
-		_palette[i * 4 + 2] = _stream->readByte();	// B
-		_palette[i * 4 + 3] = 0xFF;	// Alpha, set in the tRNS chunk
-	}
-}
-
-void PNG::readTransparencyChunk(uint32 chunkLength) {
-	_transparentColorSpecified = true;
-
-	switch(_header.colorType) {
-	case kGrayScale:
-		_transparentColor[0] = _stream->readUint16BE();
-		_transparentColor[1] = _transparentColor[0];
-		_transparentColor[2] = _transparentColor[0];
-		break;
-	case kTrueColor:
-		_transparentColor[0] = _stream->readUint16BE();
-		_transparentColor[1] = _stream->readUint16BE();
-		_transparentColor[2] = _stream->readUint16BE();
-		break;
-	case kIndexed:
-		for (uint32 i = 0; i < chunkLength; i++)
-			_palette[i * 4 + 3] = _stream->readByte();
-		// A transparency chunk may have less entries
-		// than the palette entries. The remaining ones
-		// are unmodified (set to 255). Check here:
-		// http://www.w3.org/TR/PNG/#11tRNS
-		break;
-	default:
-		error("Transparency chunk found in a PNG that has a separate transparency channel");
-	}
-}
-
-} // End of Graphics namespace
diff --git a/graphics/png.h b/graphics/png.h
deleted file mode 100644
index 078c76f..0000000
--- a/graphics/png.h
+++ /dev/null
@@ -1,168 +0,0 @@
-/* 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.
- *
- */
-
-/*
- * PNG decoder used in engines:
- *  - sword25
- * Dependencies:
- *  - zlib
- */
-
-#ifndef GRAPHICS_PNG_H
-#define GRAPHICS_PNG_H
-
-// PNG decoder, based on the W3C specs:
-// http://www.w3.org/TR/PNG/
-// Parts of the code have been adapted from LodePNG, by Lode Vandevenne:
-// http://members.gamedev.net/lode/projects/LodePNG/
-
-// All the numbers are BE: http://www.w3.org/TR/PNG/#7Integers-and-byte-order
-
-// Note: At the moment, this decoder only supports non-interlaced images, and
-// does not support truecolor/grayscale images with 16bit depth.
-//
-// Theoretically, interlaced images shouldn't be needed for games, as
-// interlacing is only useful for images in websites.
-//
-// PNG images with 16bit depth (i.e. 48bit images) are quite rare, and can
-// theoretically contain more than 16.7 millions of colors (the so-called "deep
-// color" representation). In essence, each of the R, G, B and A components in
-// them is specified with 2 bytes, instead of 1. However, the PNG specification
-// always refers to color components with 1 byte each, so this part of the spec
-// is a bit unclear. For now, these won't be supported, until a suitable sample
-// is found.
-
-#include "common/scummsys.h"
-#include "common/textconsole.h"
-
-namespace Common {
-class SeekableReadStream;
-}
-
-namespace Graphics {
-
-struct Surface;
-
-enum PNGColorType {
-	kGrayScale          = 0,	// bit depths: 1, 2, 4, 8, 16
-	kTrueColor          = 2,	// bit depths: 8, 16
-	kIndexed            = 3,	// bit depths: 1, 2, 4, 8
-	kGrayScaleWithAlpha = 4,	// bit depths: 8, 16
-	kTrueColorWithAlpha = 6		// bit depths: 8, 16
-};
-
-enum PNGInterlaceType {
-	kNonInterlaced      = 0,
-	kInterlaced         = 1
-};
-
-struct PNGHeader {
-	uint32 width;
-	uint32 height;
-	byte bitDepth;
-	PNGColorType colorType;
-	byte compressionMethod;
-	byte filterMethod;
-	PNGInterlaceType interlaceType;
-};
-
-struct PixelFormat;
-
-class PNG {
-public:
-	PNG();
-	~PNG();
-
-	/**
-	 * Reads a PNG image from the specified stream
-	 */
-	bool read(Common::SeekableReadStream *str);
-
-	/**
-	 * Returns the information obtained from the PNG header.
-	 */
-	PNGHeader getHeader() const { return _header; }
-
-	/**
-	 * Returns the PNG image, formatted for the specified pixel format.
-	 */
-	Graphics::Surface *getSurface(const PixelFormat &format);
-
-	/**
-	 * Returns the indexed PNG8 image. Used for PNGs with an indexed 256 color
-	 * palette, when they're shown on an 8-bit color screen, as no translation
-	 * is taking place.
-	 */
-	Graphics::Surface *getIndexedSurface() {
-		if (_header.colorType != kIndexed)
-			error("Indexed surface requested for a non-indexed PNG");
-		return _unfilteredSurface;
-	}
-
-	/**
-	 * Returns the palette of the specified PNG8 image, given a pointer to
-	 * an RGBA palette array (4 x 256).
-	 */
-	void getPalette(byte *palette, uint16 &entries) {
-		if (_header.colorType != kIndexed)
-			error("Palette requested for a non-indexed PNG");
-		for (int i = 0; i < 256; i++) {
-			palette[0 + i * 4] = _palette[0 + i * 4];	// R
-			palette[1 + i * 4] = _palette[1 + i * 4];	// G
-			palette[2 + i * 4] = _palette[2 + i * 4];	// B
-			palette[3 + i * 4] = _palette[3 + i * 4];	// A
-		}
-		entries = _paletteEntries;
-	}
-
-private:
-	void readHeaderChunk();
-	byte getNumColorChannels();
-
-	void readPaletteChunk();
-	void readTransparencyChunk(uint32 chunkLength);
-
-	void constructImage();
-	void unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length);
-	byte paethPredictor(int16 a, int16 b, int16 c);
-
-	// The original file stream
-	Common::SeekableReadStream *_stream;
-	// The unzipped image data stream
-	Common::SeekableReadStream *_imageData;
-
-	PNGHeader _header;
-
-	byte _palette[256 * 4];	// RGBA
-	uint16 _paletteEntries;
-	uint16 _transparentColor[3];
-	bool _transparentColorSpecified;
-
-	byte *_compressedBuffer;
-	uint32 _compressedBufferSize;
-
-	Graphics::Surface *_unfilteredSurface;
-};
-
-} // End of Graphics namespace
-
-#endif // GRAPHICS_PNG_H


Commit: 4d65be826412e8a41fb714f19deb804bfd7599a0
    https://github.com/scummvm/scummvm/commit/4d65be826412e8a41fb714f19deb804bfd7599a0
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:48-07:00

Commit Message:
COMMON: Add a READ_UINT24 macro

Complements READ_UINT16 and READ_UINT32

Changed paths:
    common/endian.h



diff --git a/common/endian.h b/common/endian.h
index 1760dc7..394437e 100644
--- a/common/endian.h
+++ b/common/endian.h
@@ -396,4 +396,10 @@ inline uint32 READ_BE_UINT24(const void *ptr) {
 	return (b[0] << 16) | (b[1] << 8) | (b[2]);
 }
 
+#ifdef SCUMM_LITTLE_ENDIAN
+#define READ_UINT24(a) READ_LE_UINT24(a)
+#else
+#define READ_UINT24(a) READ_BE_UINT24(a)
+#endif
+
 #endif


Commit: a7740fb6de17967e0187b606e94d8b7f91951f79
    https://github.com/scummvm/scummvm/commit/a7740fb6de17967e0187b606e94d8b7f91951f79
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-03-19T17:06:48-07:00

Commit Message:
GRAPHICS: Add support for converting surfaces from 24bpp

Changed paths:
    graphics/surface.cpp



diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index 0a5b109..fcd7022 100644
--- a/graphics/surface.cpp
+++ b/graphics/surface.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/algorithm.h"
+#include "common/endian.h"
 #include "common/util.h"
 #include "common/rect.h"
 #include "common/textconsole.h"
@@ -281,8 +282,11 @@ Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *
 		return surface;
 	}
 
+	if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
+		error("Surface::convertTo(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
+
 	if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
-		error("Surface::convertTo(): Only 2Bpp and 4Bpp supported");
+		error("Surface::convertTo(): Can only convert to 2Bpp and 4Bpp");
 
 	surface->create(w, h, dstFormat);
 
@@ -319,9 +323,11 @@ Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *
 			for (int x = 0; x < w; x++) {
 				uint32 srcColor;
 				if (format.bytesPerPixel == 2)
-					srcColor = *((uint16 *)srcRow);
+					srcColor = READ_UINT16(srcRow);
+				else if (format.bytesPerPixel == 3)
+					srcColor = READ_UINT24(srcRow);
 				else
-					srcColor = *((uint32 *)srcRow);
+					srcColor = READ_UINT32(srcRow);
 
 				srcRow += format.bytesPerPixel;
 


Commit: a2ad364615784c3ceffd5533b548ccff195c32ae
    https://github.com/scummvm/scummvm/commit/a2ad364615784c3ceffd5533b548ccff195c32ae
Author: Johannes Schickel (lordhoto at gmail.com)
Date: 2012-03-19T17:41:40-07:00

Commit Message:
Merge pull request #199 from clone2727/image-decoder

Introduce an ImageDecoder class

Changed paths:
  A graphics/decoders/bmp.cpp
  A graphics/decoders/bmp.h
  A graphics/decoders/image_decoder.h
  A graphics/decoders/jpeg.cpp
  A graphics/decoders/jpeg.h
  A graphics/decoders/pict.cpp
  A graphics/decoders/pict.h
  A graphics/decoders/png.cpp
  A graphics/decoders/png.h
  R graphics/imagedec.h
  R graphics/jpeg.cpp
  R graphics/jpeg.h
  R graphics/pict.cpp
  R graphics/pict.h
  R graphics/png.cpp
  R graphics/png.h
  R graphics/imagedec.cpp
    backends/vkeybd/virtual-keyboard-parser.cpp
    common/endian.h
    engines/groovie/roq.cpp
    engines/hugo/dialogs.cpp
    engines/mohawk/bitmap.cpp
    engines/mohawk/bitmap.h
    engines/mohawk/graphics.cpp
    engines/mohawk/myst_graphics.cpp
    engines/mohawk/myst_graphics.h
    engines/mohawk/video.cpp
    engines/sci/graphics/maciconbar.cpp
    engines/sci/graphics/maciconbar.h
    engines/sword25/gfx/image/imgloader.cpp
    graphics/module.mk
    graphics/surface.cpp
    graphics/surface.h
    graphics/yuv_to_rgb.cpp
    graphics/yuv_to_rgb.h
    gui/ThemeEngine.cpp
    video/codecs/mjpeg.cpp
    video/codecs/mjpeg.h









More information about the Scummvm-git-logs mailing list