Skip to content

Commit

Permalink
Merge pull request #278 from bluegr/pcxdecoder
Browse files Browse the repository at this point in the history
GRAPHICS: Add a PCX decoder
  • Loading branch information
bluegr committed Sep 16, 2012
2 parents dc20797 + ef671f2 commit c0babb0
Show file tree
Hide file tree
Showing 8 changed files with 352 additions and 156 deletions.
68 changes: 16 additions & 52 deletions engines/dreamweb/vgagrafx.cpp
Expand Up @@ -23,6 +23,7 @@
#include "dreamweb/dreamweb.h"
#include "engines/util.h"
#include "graphics/surface.h"
#include "graphics/decoders/pcx.h"

namespace DreamWeb {

Expand Down Expand Up @@ -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) {
Expand Down
82 changes: 18 additions & 64 deletions engines/hugo/file.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -87,67 +91,34 @@ const char *FileManager::getUifFilename() const {
return "uif.dat";
}

/**
* 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)
Expand All @@ -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;
}

Expand Down
5 changes: 1 addition & 4 deletions engines/hugo/file.h
Expand Up @@ -112,16 +112,13 @@ class FileManager {
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);
};

Expand Down
38 changes: 18 additions & 20 deletions engines/queen/display.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand Down
33 changes: 17 additions & 16 deletions engines/tucker/resource.cpp
Expand Up @@ -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"

Expand Down Expand Up @@ -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();
}
}
Expand Down

0 comments on commit c0babb0

Please sign in to comment.