diff options
-rw-r--r-- | engines/dreamweb/vgagrafx.cpp | 68 | ||||
-rw-r--r-- | engines/hugo/file.cpp | 82 | ||||
-rw-r--r-- | engines/hugo/file.h | 5 | ||||
-rw-r--r-- | engines/queen/display.cpp | 38 | ||||
-rw-r--r-- | engines/tucker/resource.cpp | 33 | ||||
-rw-r--r-- | graphics/decoders/pcx.cpp | 213 | ||||
-rw-r--r-- | graphics/decoders/pcx.h | 68 | ||||
-rw-r--r-- | graphics/module.mk | 1 |
8 files changed, 352 insertions, 156 deletions
diff --git a/engines/dreamweb/vgagrafx.cpp b/engines/dreamweb/vgagrafx.cpp index ec306c4924..50c0ef3161 100644 --- a/engines/dreamweb/vgagrafx.cpp +++ b/engines/dreamweb/vgagrafx.cpp @@ -23,6 +23,7 @@ #include "dreamweb/dreamweb.h" #include "engines/util.h" #include "graphics/surface.h" +#include "graphics/decoders/pcx.h" namespace DreamWeb { @@ -152,70 +153,33 @@ void DreamWebEngine::setMode() { void DreamWebEngine::showPCX(const Common::String &suffix) { Common::String name = getDatafilePrefix() + suffix; Common::File pcxFile; - if (!pcxFile.open(name)) { warning("showpcx: Could not open '%s'", name.c_str()); return; } - uint8 *mainGamePal; - int i, j; + Graphics::PCXDecoder pcx; + if (!pcx.loadStream(pcxFile)) { + warning("showpcx: Could not process '%s'", name.c_str()); + return; + } // Read the 16-color palette into the 'maingamepal' buffer. Note that // the color components have to be adjusted from 8 to 6 bits. - - pcxFile.seek(16, SEEK_SET); - mainGamePal = _mainPal; - pcxFile.read(mainGamePal, 48); - - memset(mainGamePal + 48, 0xff, 720); - for (i = 0; i < 48; i++) { - mainGamePal[i] >>= 2; + memset(_mainPal, 0xff, 256 * 3); + memcpy(_mainPal, pcx.getPalette(), 48); + for (int i = 0; i < 48; i++) { + _mainPal[i] >>= 2; } - // Decode the image data. - Graphics::Surface *s = g_system->lockScreen(); - Common::Rect rect(640, 480); - - s->fillRect(rect, 0); - pcxFile.seek(128, SEEK_SET); - - for (int y = 0; y < 480; y++) { - byte *dst = (byte *)s->getBasePtr(0, y); - int decoded = 0; - - while (decoded < 320) { - byte col = pcxFile.readByte(); - byte len; - - if ((col & 0xc0) == 0xc0) { - len = col & 0x3f; - col = pcxFile.readByte(); - } else { - len = 1; - } - - // The image uses 16 colors and is stored as four bit - // planes, one for each bit of the color, least - // significant bit plane first. - - for (i = 0; i < len; i++) { - int plane = decoded / 80; - int pos = decoded % 80; - - for (j = 0; j < 8; j++) { - byte bit = (col >> (7 - j)) & 1; - dst[8 * pos + j] |= (bit << plane); - } - - decoded++; - } - } - } - + s->fillRect(Common::Rect(640, 480), 0); + const Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy((byte *)s->getBasePtr(0, y), pcxSurface->getBasePtr(0, y), pcxSurface->w); g_system->unlockScreen(); - pcxFile.close(); } void DreamWebEngine::frameOutV(uint8 *dst, const uint8 *src, uint16 pitch, uint16 width, uint16 height, int16 x, int16 y) { diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp index 15ee06c82a..1758f3f6a5 100644 --- a/engines/hugo/file.cpp +++ b/engines/hugo/file.cpp @@ -32,7 +32,11 @@ #include "common/savefile.h" #include "common/textconsole.h" #include "common/config-manager.h" + +#include "graphics/surface.h" +#include "graphics/decoders/pcx.h" #include "graphics/thumbnail.h" + #include "gui/saveload.h" #include "hugo/hugo.h" @@ -88,66 +92,33 @@ const char *FileManager::getUifFilename() const { } /** - * Convert 4 planes (RGBI) data to 8-bit DIB format - * Return original plane data ptr - */ -byte *FileManager::convertPCC(byte *p, const uint16 y, const uint16 bpl, ImagePtr dataPtr) const { - debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, ImagePtr dataPtr)", y, bpl); - - dataPtr += y * bpl * 8; // Point to correct DIB line - for (int16 r = 0, g = bpl, b = g + bpl, i = b + bpl; r < bpl; r++, g++, b++, i++) { // Each byte in all planes - for (int8 bit = 7; bit >= 0; bit--) { // Each bit in byte - *dataPtr++ = (((p[r] >> bit & 1) << 0) | - ((p[g] >> bit & 1) << 1) | - ((p[b] >> bit & 1) << 2) | - ((p[i] >> bit & 1) << 3)); - } - } - return p; -} - -/** * Read a pcx file of length len. Use supplied seqPtr and image_p or * allocate space if NULL. Name used for errors. Returns address of seqPtr * Set first TRUE to initialize b_index (i.e. not reading a sequential image in file). */ -Seq *FileManager::readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name) { +Seq *FileManager::readPCX(Common::SeekableReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name) { debugC(1, kDebugFile, "readPCX(..., %s)", name); - // Read in the PCC header and check consistency - _PCCHeader._mfctr = f.readByte(); - _PCCHeader._vers = f.readByte(); - _PCCHeader._enc = f.readByte(); - _PCCHeader._bpx = f.readByte(); - _PCCHeader._x1 = f.readUint16LE(); - _PCCHeader._y1 = f.readUint16LE(); - _PCCHeader._x2 = f.readUint16LE(); - _PCCHeader._y2 = f.readUint16LE(); - _PCCHeader._xres = f.readUint16LE(); - _PCCHeader._yres = f.readUint16LE(); - f.read(_PCCHeader._palette, sizeof(_PCCHeader._palette)); - _PCCHeader._vmode = f.readByte(); - _PCCHeader._planes = f.readByte(); - _PCCHeader._bytesPerLine = f.readUint16LE(); - f.read(_PCCHeader._fill2, sizeof(_PCCHeader._fill2)); - - if (_PCCHeader._mfctr != 10) - error("Bad data file format: %s", name); - // Allocate memory for Seq if 0 if (seqPtr == 0) { if ((seqPtr = (Seq *)malloc(sizeof(Seq))) == 0) error("Insufficient memory to run game."); } + Graphics::PCXDecoder pcx; + if (!pcx.loadStream(f)) + error("Error while reading PCX image"); + + const Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + // Find size of image data in 8-bit DIB format // Note save of x2 - marks end of valid data before garbage - uint16 bytesPerLine4 = _PCCHeader._bytesPerLine * 4; // 4-bit bpl - seqPtr->_bytesPerLine8 = bytesPerLine4 * 2; // 8-bit bpl - seqPtr->_lines = _PCCHeader._y2 - _PCCHeader._y1 + 1; - seqPtr->_x2 = _PCCHeader._x2 - _PCCHeader._x1 + 1; + seqPtr->_lines = pcxSurface->h; + seqPtr->_x2 = seqPtr->_bytesPerLine8 = pcxSurface->w; // Size of the image - uint16 size = seqPtr->_lines * seqPtr->_bytesPerLine8; + uint16 size = pcxSurface->w * pcxSurface->h; // Allocate memory for image data if NULL if (imagePtr == 0) @@ -156,26 +127,9 @@ Seq *FileManager::readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, co assert(imagePtr); seqPtr->_imagePtr = imagePtr; + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy(imagePtr + y * pcxSurface->w, pcxSurface->getBasePtr(0, y), pcxSurface->w); - // Process the image data, converting to 8-bit DIB format - uint16 y = 0; // Current line index - byte pline[kXPix]; // Hold 4 planes of data - byte *p = pline; // Ptr to above - while (y < seqPtr->_lines) { - byte c = f.readByte(); - if ((c & kRepeatMask) == kRepeatMask) { - byte d = f.readByte(); // Read data byte - for (int i = 0; i < (c & kLengthMask); i++) { - *p++ = d; - if ((uint16)(p - pline) == bytesPerLine4) - p = convertPCC(pline, y++, _PCCHeader._bytesPerLine, imagePtr); - } - } else { - *p++ = c; - if ((uint16)(p - pline) == bytesPerLine4) - p = convertPCC(pline, y++, _PCCHeader._bytesPerLine, imagePtr); - } - } return seqPtr; } diff --git a/engines/hugo/file.h b/engines/hugo/file.h index 1438bd2054..44f257a2af 100644 --- a/engines/hugo/file.h +++ b/engines/hugo/file.h @@ -112,16 +112,13 @@ protected: Common::File _sceneryArchive1; // Handle for scenery file Common::File _objectsArchive; // Handle for objects file - PCCHeader _PCCHeader; - - Seq *readPCX(Common::ReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name); + Seq *readPCX(Common::SeekableReadStream &f, Seq *seqPtr, byte *imagePtr, const bool firstFl, const char *name); // If this is the first call, read the lookup table bool _hasReadHeader; SoundHdr _soundHdr[kMaxSounds]; // Sound lookup table private: - byte *convertPCC(byte *p, const uint16 y, const uint16 bpl, ImagePtr dataPtr) const; UifHdr *getUIFHeader(const Uif id); }; diff --git a/engines/queen/display.cpp b/engines/queen/display.cpp index 83dc1a9f60..cd9a1075fa 100644 --- a/engines/queen/display.cpp +++ b/engines/queen/display.cpp @@ -23,9 +23,13 @@ #include "common/system.h" #include "common/events.h" +#include "common/stream.h" +#include "common/memstream.h" #include "graphics/cursorman.h" #include "graphics/palette.h" +#include "graphics/surface.h" +#include "graphics/decoders/pcx.h" #include "queen/display.h" #include "queen/input.h" @@ -806,28 +810,22 @@ void Display::fill(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, uint16 w, } void Display::decodePCX(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd) { - *w = READ_LE_UINT16(src + 12); - *h = READ_LE_UINT16(src + 14); + Common::MemoryReadStream str(src, srcSize); + + ::Graphics::PCXDecoder pcx; + if (!pcx.loadStream(str)) + error("Error while reading PCX image"); + + const ::Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + *w = pcxSurface->w; + *h = pcxSurface->h; assert(palStart <= palEnd && palEnd <= 256); - const uint8 *palData = src + srcSize - 768; - memcpy(pal, palData + palStart * 3, (palEnd - palStart) * 3); - - src += 128; - for (int y = 0; y < *h; ++y) { - uint8 *p = dst; - while (p < dst + *w) { - uint8 col = *src++; - if ((col & 0xC0) == 0xC0) { - uint8 len = col & 0x3F; - memset(p, *src++, len); - p += len; - } else { - *p++ = col; - } - } - dst += dstPitch; - } + memcpy(pal, pcx.getPalette() + palStart * 3, (palEnd - palStart) * 3); + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy(dst + y * dstPitch, pcxSurface->getBasePtr(0, y), pcxSurface->w); } void Display::decodeLBM(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd, uint8 colorBase) { diff --git a/engines/tucker/resource.cpp b/engines/tucker/resource.cpp index bee09f7391..1b04f3fae9 100644 --- a/engines/tucker/resource.cpp +++ b/engines/tucker/resource.cpp @@ -29,6 +29,9 @@ #include "audio/decoders/vorbis.h" #include "audio/decoders/wave.h" +#include "graphics/surface.h" +#include "graphics/decoders/pcx.h" + #include "tucker/tucker.h" #include "tucker/graphics.h" @@ -298,23 +301,21 @@ void TuckerEngine::loadImage(const char *fname, uint8 *dst, int type) { return; } } - f.seek(128, SEEK_SET); - int size = 0; - while (size < 64000) { - int code = f.readByte(); - if (code >= 0xC0) { - const int sz = code - 0xC0; - code = f.readByte(); - memset(dst + size, code, sz); - size += sz; - } else { - dst[size++] = code; - } - } + + ::Graphics::PCXDecoder pcx; + if (!pcx.loadStream(f)) + error("Error while reading PCX image"); + + const ::Graphics::Surface *pcxSurface = pcx.getSurface(); + if (pcxSurface->format.bytesPerPixel != 1) + error("Invalid bytes per pixel in PCX surface (%d)", pcxSurface->format.bytesPerPixel); + if (pcxSurface->w != 320 || pcxSurface->h != 200) + error("Invalid PCX surface size (%d x %d)", pcxSurface->w, pcxSurface->h); + for (uint16 y = 0; y < pcxSurface->h; y++) + memcpy(dst + y * 320, pcxSurface->getBasePtr(0, y), pcxSurface->w); + if (type != 0) { - if (f.readByte() != 12) - return; - f.read(_currentPalette, 768); + memcpy(_currentPalette, pcx.getPalette(), 3 * 256); setBlackPalette(); } } diff --git a/graphics/decoders/pcx.cpp b/graphics/decoders/pcx.cpp new file mode 100644 index 0000000000..f5c1c24bb0 --- /dev/null +++ b/graphics/decoders/pcx.cpp @@ -0,0 +1,213 @@ +/* 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/pcx.h" + +/** + * Based on the PCX specs: + * http://www.fileformat.info/format/pcx/spec/a10e75307b3a4cc49c3bbe6db4c41fa2/view.htm + * and the PCX decoder of FFmpeg (libavcodec/pcx.c): + * http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavcodec/pcx.c + */ + +namespace Graphics { + +PCXDecoder::PCXDecoder() { + _surface = 0; + _palette = 0; + _paletteColorCount = 0; +} + +PCXDecoder::~PCXDecoder() { + destroy(); +} + +void PCXDecoder::destroy() { + if (_surface) { + _surface->free(); + delete _surface; + _surface = 0; + } + + delete[] _palette; + _palette = 0; + _paletteColorCount = 0; +} + +bool PCXDecoder::loadStream(Common::SeekableReadStream &stream) { + destroy(); + + if (stream.readByte() != 0x0a) // ZSoft PCX + return false; + + byte version = stream.readByte(); // 0 - 5 + if (version > 5) + return false; + + bool compressed = stream.readByte(); // encoding, 1 = run length encoding + byte bitsPerPixel = stream.readByte(); // 1, 2, 4 or 8 + + // Window + uint16 xMin = stream.readUint16LE(); + uint16 yMin = stream.readUint16LE(); + uint16 xMax = stream.readUint16LE(); + uint16 yMax = stream.readUint16LE(); + + uint16 width = xMax - xMin + 1; + uint16 height = yMax - yMin + 1; + + if (xMax < xMin || yMax < yMin) { + warning("Invalid PCX image dimensions"); + return false; + } + + stream.skip(4); // HDpi, VDpi + + // Read the EGA palette (colormap) + _palette = new byte[16 * 3]; + for (uint16 i = 0; i < 16; i++) { + _palette[i * 3 + 0] = stream.readByte(); + _palette[i * 3 + 1] = stream.readByte(); + _palette[i * 3 + 2] = stream.readByte(); + } + + if (stream.readByte() != 0) // reserved, should be set to 0 + return false; + + byte nPlanes = stream.readByte(); + uint16 bytesPerLine = stream.readUint16LE(); + uint16 bytesPerscanLine = nPlanes * bytesPerLine; + + if (bytesPerscanLine < width * bitsPerPixel * nPlanes / 8) { + warning("PCX data is corrupted"); + return false; + } + + stream.skip(60); // PaletteInfo, HscreenSize, VscreenSize, Filler + + _surface = new Graphics::Surface(); + + byte *scanLine = new byte[bytesPerscanLine]; + byte *dst; + int x, y; + + if (nPlanes == 3 && bitsPerPixel == 8) { // 24bpp + Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + _surface->create(width, height, format); + dst = (byte *)_surface->pixels; + _paletteColorCount = 0; + + for (y = 0; y < height; y++) { + decodeRLE(stream, scanLine, bytesPerscanLine, compressed); + + for (x = 0; x < width; x++) { + byte b = scanLine[x]; + byte g = scanLine[x + bytesPerLine]; + byte r = scanLine[x + (bytesPerLine << 1)]; + uint32 color = format.RGBToColor(r, g, b); + + *((uint32 *)dst) = color; + dst += format.bytesPerPixel; + } + } + } else if (nPlanes == 1 && bitsPerPixel == 8) { // 8bpp indexed + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + dst = (byte *)_surface->pixels; + _paletteColorCount = 16; + + for (y = 0; y < height; y++, dst += _surface->pitch) { + decodeRLE(stream, scanLine, bytesPerscanLine, compressed); + memcpy(dst, scanLine, width); + } + + if (version == 5) { + if (stream.readByte() != 12) { + warning("Expected a palette after the PCX image data"); + delete[] scanLine; + return false; + } + + // Read the VGA palette + delete[] _palette; + _palette = new byte[256 * 3]; + for (uint16 i = 0; i < 256; i++) { + _palette[i * 3 + 0] = stream.readByte(); + _palette[i * 3 + 1] = stream.readByte(); + _palette[i * 3 + 2] = stream.readByte(); + } + + _paletteColorCount = 256; + } + } else if ((nPlanes == 2 || nPlanes == 3 || nPlanes == 4) && bitsPerPixel == 1) { // planar, 4, 8 or 16 colors + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + dst = (byte *)_surface->pixels; + _paletteColorCount = 16; + + for (y = 0; y < height; y++, dst += _surface->pitch) { + decodeRLE(stream, scanLine, bytesPerscanLine, compressed); + + for (x = 0; x < width; x++) { + int m = 0x80 >> (x & 7), v = 0; + for (int i = nPlanes - 1; i >= 0; i--) { + v <<= 1; + v += (scanLine[i * bytesPerLine + (x >> 3)] & m) == 0 ? 0 : 1; + } + dst[x] = v; + } + } + } else { + // Known unsupported case: 1 plane and bpp < 8 (1, 2 or 4) + warning("Invalid PCX file (%d planes, %d bpp)", nPlanes, bitsPerPixel); + delete[] scanLine; + return false; + } + + delete[] scanLine; + + return true; +} + +void PCXDecoder::decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerscanLine, bool compressed) { + uint32 i = 0; + byte run, value; + + if (compressed) { + while (i < bytesPerscanLine) { + run = 1; + value = stream.readByte(); + if (value >= 0xc0) { + run = value & 0x3f; + value = stream.readByte(); + } + while (i < bytesPerscanLine && run--) + dst[i++] = value; + } + } else { + stream.read(dst, bytesPerscanLine); + } +} + +} // End of namespace Graphics diff --git a/graphics/decoders/pcx.h b/graphics/decoders/pcx.h new file mode 100644 index 0000000000..bcff754a2d --- /dev/null +++ b/graphics/decoders/pcx.h @@ -0,0 +1,68 @@ +/* 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. + */ + +/** + * PCX decoder used in engines: + * - dreamweb + * - hugo + * - queen + * - tucker + */ + +#ifndef GRAPHICS_DECODERS_PCX_H +#define GRAPHICS_DECODERS_PCX_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 PCXDecoder : public ImageDecoder { +public: + PCXDecoder(); + virtual ~PCXDecoder(); + + // ImageDecoder API + void destroy(); + virtual bool loadStream(Common::SeekableReadStream &stream); + virtual const Surface *getSurface() const { return _surface; } + const byte *getPalette() const { return _palette; } + uint16 getPaletteColorCount() const { return _paletteColorCount; } + +private: + void decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerScanline, bool compressed); + + Surface *_surface; + byte *_palette; + uint16 _paletteColorCount; +}; + +} // End of namespace Graphics + +#endif diff --git a/graphics/module.mk b/graphics/module.mk index e67efd2cf5..f560d9dc97 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -25,6 +25,7 @@ MODULE_OBJS := \ yuv_to_rgb.o \ decoders/bmp.o \ decoders/jpeg.o \ + decoders/pcx.o \ decoders/pict.o \ decoders/png.o \ decoders/tga.o |