/* 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 "made/graphics.h" #include "common/endian.h" #include "common/textconsole.h" #include "common/debug.h" #include "common/util.h" #include "graphics/surface.h" namespace Made { byte ValueReader::readPixel() { byte value; if (_nibbleMode) { if (_nibbleSwitch) { value = (_buffer[0] >> 4) & 0x0F; _buffer++; } else { value = _buffer[0] & 0x0F; } _nibbleSwitch = !_nibbleSwitch; } else { value = _buffer[0]; _buffer++; } return value; } uint16 ValueReader::readUint16() { uint16 value = READ_LE_UINT16(_buffer); _buffer += 2; return value; } uint32 ValueReader::readUint32() { uint32 value = READ_LE_UINT32(_buffer); _buffer += 4; return value; } void ValueReader::resetNibbleSwitch() { _nibbleSwitch = false; } void decompressImage(byte *source, Graphics::Surface &surface, uint16 cmdOffs, uint16 pixelOffs, uint16 maskOffs, uint16 lineSize, byte cmdFlags, byte pixelFlags, byte maskFlags, bool deltaFrame) { const int offsets[] = { 0, 1, 2, 3, 320, 321, 322, 323, 640, 641, 642, 643, 960, 961, 962, 963 }; uint16 width = surface.w; uint16 height = surface.h; byte *cmdBuffer = source + cmdOffs; ValueReader maskReader(source + maskOffs, (maskFlags & 2) != 0); ValueReader pixelReader(source + pixelOffs, (pixelFlags & 2) != 0); if ((maskFlags != 0) && (maskFlags != 2) && (pixelFlags != 0) && (pixelFlags != 2) && (cmdFlags != 0)) error("decompressImage() Unsupported flags: cmdFlags = %02X; maskFlags = %02X, pixelFlags = %02X", cmdFlags, maskFlags, pixelFlags); byte *destPtr = (byte *)surface.getPixels(); byte lineBuf[640 * 4]; byte bitBuf[40]; int bitBufLastOfs = (((lineSize + 1) >> 1) << 1) - 2; int bitBufLastCount = ((width + 3) >> 2) & 7; if (bitBufLastCount == 0) bitBufLastCount = 8; while (height > 0) { int drawDestOfs = 0; memset(lineBuf, 0, sizeof(lineBuf)); memcpy(bitBuf, cmdBuffer, lineSize); cmdBuffer += lineSize; for (uint16 bitBufOfs = 0; bitBufOfs < lineSize; bitBufOfs += 2) { uint16 bits = READ_LE_UINT16(&bitBuf[bitBufOfs]); int bitCount; if (bitBufOfs == bitBufLastOfs) bitCount = bitBufLastCount; else bitCount = 8; for (int curCmd = 0; curCmd < bitCount; curCmd++) { int cmd = bits & 3; bits >>= 2; byte pixels[4]; uint32 mask; switch (cmd) { case 0: pixels[0] = pixelReader.readPixel(); for (int i = 0; i < 16; i++) lineBuf[drawDestOfs + offsets[i]] = pixels[0]; break; case 1: pixels[0] = pixelReader.readPixel(); pixels[1] = pixelReader.readPixel(); mask = maskReader.readUint16(); for (int i = 0; i < 16; i++) { lineBuf[drawDestOfs + offsets[i]] = pixels[mask & 1]; mask >>= 1; } break; case 2: pixels[0] = pixelReader.readPixel(); pixels[1] = pixelReader.readPixel(); pixels[2] = pixelReader.readPixel(); pixels[3] = pixelReader.readPixel(); mask = maskReader.readUint32(); for (int i = 0; i < 16; i++) { lineBuf[drawDestOfs + offsets[i]] = pixels[mask & 3]; mask >>= 2; } break; case 3: if (!deltaFrame) { // For EGA pictures: Pixels are read starting from a new byte maskReader.resetNibbleSwitch(); // Yes, it reads from maskReader here for (int i = 0; i < 16; i++) lineBuf[drawDestOfs + offsets[i]] = maskReader.readPixel(); } break; } drawDestOfs += 4; } } if (deltaFrame) { for (int y = 0; y < 4 && height > 0; y++, height--) { for (int x = 0; x < width; x++) { if (lineBuf[x + y * 320] != 0) *destPtr = lineBuf[x + y * 320]; destPtr++; } } } else { for (int y = 0; y < 4 && height > 0; y++, height--) { memcpy(destPtr, &lineBuf[y * 320], width); destPtr += width; } } } } void decompressMovieImage(byte *source, Graphics::Surface &surface, uint16 cmdOffs, uint16 pixelOffs, uint16 maskOffs, uint16 lineSize) { uint16 width = surface.w; uint16 height = surface.h; uint16 bx = 0, by = 0, bw = ((width + 3) / 4) * 4; byte *cmdBuffer = source + cmdOffs; byte *maskBuffer = source + maskOffs; byte *pixelBuffer = source + pixelOffs; byte *destPtr = (byte *)surface.getPixels(); byte bitBuf[40]; int bitBufLastOfs = (((lineSize + 1) >> 1) << 1) - 2; int bitBufLastCount = ((width + 3) >> 2) & 7; if (bitBufLastCount == 0) bitBufLastCount = 8; debug(1, "width = %d; bw = %d", width, bw); while (height > 0) { memcpy(bitBuf, cmdBuffer, lineSize); cmdBuffer += lineSize; for (uint16 bitBufOfs = 0; bitBufOfs < lineSize; bitBufOfs += 2) { uint16 bits = READ_LE_UINT16(&bitBuf[bitBufOfs]); int bitCount; if (bitBufOfs == bitBufLastOfs) bitCount = bitBufLastCount; else bitCount = 8; for (int curCmd = 0; curCmd < bitCount; curCmd++) { uint cmd = bits & 3; bits >>= 2; byte pixels[4], block[16]; uint32 mask; switch (cmd) { case 0: pixels[0] = *pixelBuffer++; for (int i = 0; i < 16; i++) block[i] = pixels[0]; break; case 1: pixels[0] = *pixelBuffer++; pixels[1] = *pixelBuffer++; mask = READ_LE_UINT16(maskBuffer); maskBuffer += 2; for (int i = 0; i < 16; i++) { block[i] = pixels[mask & 1]; mask >>= 1; } break; case 2: pixels[0] = *pixelBuffer++; pixels[1] = *pixelBuffer++; pixels[2] = *pixelBuffer++; pixels[3] = *pixelBuffer++; mask = READ_LE_UINT32(maskBuffer); maskBuffer += 4; for (int i = 0; i < 16; i++) { block[i] = pixels[mask & 3]; mask >>= 2; } break; case 3: break; } if (cmd != 3) { uint16 blockPos = 0; uint32 maxW = MIN(4, surface.w - bx); uint32 maxH = (MIN(4, surface.h - by) + by) * width; for (uint32 yc = by * width; yc < maxH; yc += width) { for (uint32 xc = 0; xc < maxW; xc++) { destPtr[(bx + xc) + yc] = block[xc + blockPos]; } blockPos += 4; } } bx += 4; if (bx >= bw) { bx = 0; by += 4; } } } height -= 4; } } } // End of namespace Made