[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