scummvm/engines/grim/bitmap.cpp

335 lines
8.5 KiB
C++
Raw Normal View History

2009-05-26 14:13:08 +00:00
/* Residual - A 3D game interpreter
*
* Residual is the legal property of its developers, whose names
2011-04-16 14:12:44 +02:00
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
2006-04-02 14:20:45 +00:00
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library 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
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* $URL$
* $Id$
*
*/
2003-08-15 18:00:22 +00:00
2011-05-08 15:38:26 +02:00
#define FORBIDDEN_SYMBOL_EXCEPTION_printf
#include "common/endian.h"
2011-05-09 04:37:51 +08:00
#include "common/zlib.h"
#include "common/memstream.h"
2009-06-18 11:52:26 +00:00
#include "engines/grim/grim.h"
#include "engines/grim/bitmap.h"
#include "engines/grim/gfx_base.h"
#include "engines/grim/savegame.h"
2011-04-10 11:24:03 +02:00
#include "engines/grim/colormap.h"
2009-05-25 06:49:57 +00:00
namespace Grim {
2003-08-15 18:00:22 +00:00
static void decompress_codec3(const char *compressed, char *result);
Bitmap::Bitmap(const char *fname, const char *data, int len) :
Object() {
2009-10-17 12:48:23 +00:00
_fname = fname;
2011-05-09 04:37:51 +08:00
printf("Trying to load %s\n", fname);
if (len < 8 || memcmp(data, "BM F\0\0\0", 8) != 0) {
2011-05-09 04:37:51 +08:00
// Should probably just check the filename
2011-05-09 01:16:14 +02:00
if (loadTile(fname, data, len)){
2011-05-09 04:37:51 +08:00
return;
// Everything is fine
}
else if (gDebugLevel == DEBUG_BITMAPS || gDebugLevel == DEBUG_ERROR || gDebugLevel == DEBUG_ALL)
error("Invalid magic loading bitmap");
}
int codec = READ_LE_UINT32(data + 8);
2011-05-09 01:16:14 +02:00
// _paletteIncluded = READ_LE_UINT32(data + 12);
2004-12-10 07:26:03 +00:00
_numImages = READ_LE_UINT32(data + 16);
2004-12-09 23:55:43 +00:00
_x = READ_LE_UINT32(data + 20);
_y = READ_LE_UINT32(data + 24);
2011-05-09 01:16:14 +02:00
// _transparentColor = READ_LE_UINT32(data + 28);
2004-12-09 23:55:43 +00:00
_format = READ_LE_UINT32(data + 32);
2011-05-01 03:50:18 +08:00
_bpp = READ_LE_UINT32(data + 36);
2011-05-09 01:16:14 +02:00
// _blueBits = READ_LE_UINT32(data + 40);
// _greenBits = READ_LE_UINT32(data + 44);
// _redBits = READ_LE_UINT32(data + 48);
// _blueShift = READ_LE_UINT32(data + 52);
// _greenShift = READ_LE_UINT32(data + 56);
// _redShift = READ_LE_UINT32(data + 60);
2004-12-09 23:55:43 +00:00
_width = READ_LE_UINT32(data + 128);
_height = READ_LE_UINT32(data + 132);
2004-12-10 07:26:03 +00:00
_currImage = 1;
2004-12-09 23:55:43 +00:00
2004-12-10 07:26:03 +00:00
_data = new char *[_numImages];
int pos = 0x88;
2004-12-10 07:26:03 +00:00
for (int i = 0; i < _numImages; i++) {
2011-04-30 21:59:49 +02:00
_data[i] = new char[_bpp / 8 * _width * _height];
if (codec == 0) {
2011-04-30 21:59:49 +02:00
memcpy(_data[i], data + pos, _bpp / 8 * _width * _height);
pos += _bpp / 8 * _width * _height + 8;
} else if (codec == 3) {
2004-12-09 23:55:43 +00:00
int compressed_len = READ_LE_UINT32(data + pos);
decompress_codec3(data + pos + 4, _data[i]);
pos += compressed_len + 12;
}
#ifdef SCUMM_BIG_ENDIAN
if (_format == 1)
2004-12-09 23:55:43 +00:00
for (int j = 0; j < _width * _height; ++j) {
((uint16 *)_data[i])[j] = SWAP_BYTES_16(((uint16 *)_data[i])[j]);
}
2004-12-09 23:55:43 +00:00
#endif
}
2004-04-19 09:56:34 +00:00
g_driver->createBitmap(this);
2003-08-15 18:00:22 +00:00
}
2011-05-01 03:50:18 +08:00
Bitmap::Bitmap(const char *data, int w, int h, int bpp, const char *fname) : Object() {
2009-10-17 12:48:23 +00:00
_fname = fname;
if (gDebugLevel == DEBUG_BITMAPS || gDebugLevel == DEBUG_NORMAL || gDebugLevel == DEBUG_ALL)
2009-10-17 12:48:23 +00:00
printf("New bitmap loaded: %s\n", fname);
_currImage = 1;
_numImages = 1;
_x = 0;
_y = 0;
2009-10-17 12:48:23 +00:00
_width = w;
_height = h;
_format = 1;
_numTex = 0;
_texIds = NULL;
2011-05-01 03:50:18 +08:00
_bpp = bpp;
_hasTransparency = false;
_data = new char *[_numImages];
2011-04-30 21:59:49 +02:00
_data[0] = new char[_bpp / 8 * _width * _height];
memcpy(_data[0], data, _bpp / 8 * _width * _height);
g_driver->createBitmap(this);
}
2011-03-21 17:18:04 +01:00
Bitmap::Bitmap() :
Object() {
_data = NULL;
}
2011-05-09 04:37:51 +08:00
// Helper function for makeBitmapFromTile
2011-05-09 01:16:14 +02:00
char *getLine(int lineNum, char* data, unsigned int width, int bpp){
return data + (lineNum *(width * bpp));
2011-05-09 04:37:51 +08:00
}
2011-05-09 01:16:14 +02:00
char *makeBitmapFromTile(char **bits, int width, int height, int bpp) {
bpp = bpp / 8;
char *fullImage = new char[width * height * bpp];
2011-05-09 04:37:51 +08:00
2011-05-09 01:16:14 +02:00
const int tWidth = 256 * bpp; // All tiles so far are 256 wide
2011-05-09 04:37:51 +08:00
const int tWidth2 = 256;
2011-05-09 01:16:14 +02:00
char *target = fullImage;
for (int i = 0; i < 256; i++) {
2011-05-09 04:37:51 +08:00
/* This can be modified to actually use the last 32 lines.
* We simply put the lower half on line 223 and down to line 32,
* then skip the last 32.
* While the upper half is put on line 479 and down to line 224.
*/
2011-05-09 01:16:14 +02:00
if (i < 224) { // Skip blank space
target=getLine(223 - i, fullImage, width, bpp);
2011-05-09 04:37:51 +08:00
2011-05-09 01:16:14 +02:00
memcpy(target, getLine(i, bits[3], tWidth2, bpp), tWidth);
2011-05-09 04:37:51 +08:00
target += tWidth;
2011-05-09 01:16:14 +02:00
memcpy(target, getLine(i, bits[4], tWidth2, bpp), tWidth);
2011-05-09 04:37:51 +08:00
target += tWidth;
2011-05-09 01:16:14 +02:00
memcpy(target, getLine(i, bits[2], tWidth2, bpp) + 128 * bpp, 128 * bpp);
target += tWidth / 2;
2011-05-09 04:37:51 +08:00
}
// Top half of course
2011-05-09 01:16:14 +02:00
target = getLine(479 - i, fullImage, width, bpp);
2011-05-09 04:37:51 +08:00
2011-05-09 01:16:14 +02:00
memcpy(target, getLine(i, bits[0], tWidth2, bpp), tWidth);
2011-05-09 04:37:51 +08:00
target += tWidth;
2011-05-09 01:16:14 +02:00
memcpy(target, getLine(i, bits[1], tWidth2, bpp), tWidth);
2011-05-09 04:37:51 +08:00
target += tWidth;
2011-05-09 01:16:14 +02:00
memcpy(target, getLine(i, bits[2], tWidth2, bpp), 128 * bpp);
target += tWidth / 2;
2011-05-09 04:37:51 +08:00
}
return fullImage;
}
2011-05-09 01:16:14 +02:00
char *upconvertto32(char *data, unsigned int width, unsigned int height, int bpp) {
if (bpp==16) {
bpp = 2;
char *newData = new char[width * height * 4];
char *to = newData;
char red = 0, green = 0, blue = 0;
for (unsigned int i = 0; i< height; i++) {
for(unsigned int j = 0; j < width; j++) {
char byte2 = data[i * width * bpp + j *2];
char byte1 = data[i * width * bpp + j * 2 + 1];
2011-05-09 04:37:51 +08:00
// Probably Alpha, then 555.
// Red
2011-05-09 01:16:14 +02:00
red = (byte1 >> 2) & 31;
2011-05-09 04:37:51 +08:00
red = red << 3 | red >> 2;
// Green
2011-05-09 01:16:14 +02:00
char green1 = (byte1 & 3);
char green2 = (((byte2) >> 5) & 7);
2011-05-09 04:37:51 +08:00
char green3 = green1 << 3 | green2;
green = green3 << 3 | green3 >> 2 ;
// Blue
2011-05-09 01:16:14 +02:00
blue = (byte2) & 31;
2011-05-09 04:37:51 +08:00
blue = blue << 3 | blue >> 2;
// Some more magic to stretch the values
*to = red;
to++;
*to = green;
to++;
*to = blue;
to++;
*to = 0;
to++;
}
}
delete data;
return newData;
}
return data;
}
2011-05-09 01:16:14 +02:00
bool Bitmap::loadTile(const char *filename, const char *data, int len) {
2011-05-09 04:37:51 +08:00
_x = 0;
_y = 0;
//warning("Loading TILE: %s",filename);
Common::MemoryReadStream stream((const byte *)data, len);
Common::SeekableReadStream *o = Common::wrapCompressedReadStream(&stream);
uint32 id, bmoffset;
id = o->readUint32LE();
// Should check that we actually HAVE a TIL
bmoffset = o->readUint32LE();
o->seek(bmoffset + 16);
_numImages = o->readUint32LE();
if (_numImages < 5)
2011-05-09 01:16:14 +02:00
error("Can not handle a tile with less than 5 images");
2011-05-09 04:37:51 +08:00
_data = new char *[_numImages];
o->seek(16, SEEK_CUR);
_bpp = o->readUint32LE();
o->seek(bmoffset + 128);
_width = o->readUint32LE();
_height = o->readUint32LE();
o->seek(-8, SEEK_CUR);
int size = _bpp / 8 * _width * _height;
for (int i = 0; i < _numImages; ++i) {
_data[i] = new char[size];
o->seek(8, SEEK_CUR);
o->read(_data[i], size);
}
2011-05-09 01:16:14 +02:00
char* bMap = makeBitmapFromTile(_data, 640, 480, _bpp);
2011-05-09 04:37:51 +08:00
for (int i = 0; i < _numImages; ++i) {
2011-05-09 01:16:14 +02:00
delete[] _data[i];
2011-05-09 04:37:51 +08:00
}
_data[0] = bMap;
_numImages = 1;
2011-05-09 01:16:14 +02:00
if (_bpp == 16) {
_data[0] = upconvertto32(_data[0], 640, 480, _bpp);
_bpp = 32;
2011-05-09 04:37:51 +08:00
}
_width = 640;
_height = 480;
2011-05-09 01:16:14 +02:00
_currImage = 1;
2011-05-09 04:37:51 +08:00
g_driver->createBitmap(this);
return true;
}
2004-03-22 11:23:37 +00:00
void Bitmap::draw() const {
2004-12-10 07:26:03 +00:00
if (_currImage == 0)
return;
2004-04-19 09:56:34 +00:00
g_driver->drawBitmap(this);
2003-08-15 18:00:22 +00:00
}
Bitmap::~Bitmap() {
2008-09-10 11:16:57 +00:00
if (_data) {
for (int i = 0; i < _numImages; i++)
2008-09-10 11:16:57 +00:00
if (_data[i])
delete[] _data[i];
2004-12-09 23:55:43 +00:00
delete[] _data;
_data = NULL;
g_driver->destroyBitmap(this);
}
g_grim->killBitmap(this);
2003-08-15 18:00:22 +00:00
}
2004-02-21 15:36:31 +00:00
#define GET_BIT do { bit = bitstr_value & 1; \
bitstr_len--; \
bitstr_value >>= 1; \
if (bitstr_len == 0) { \
bitstr_value = READ_LE_UINT16(compressed); \
bitstr_len = 16; \
compressed += 2; \
} \
} while (0)
2003-08-15 18:00:22 +00:00
static void decompress_codec3(const char *compressed, char *result) {
int bitstr_value = READ_LE_UINT16(compressed);
int bitstr_len = 16;
2003-08-15 18:00:22 +00:00
compressed += 2;
bool bit;
for (;;) {
GET_BIT;
if (bit == 1)
*result++ = *compressed++;
else {
GET_BIT;
int copy_len, copy_offset;
if (bit == 0) {
GET_BIT;
copy_len = 2 * bit;
GET_BIT;
copy_len += bit + 3;
copy_offset = *(uint8 *)(compressed++) - 0x100;
} else {
2004-12-09 23:55:43 +00:00
copy_offset = (*(uint8 *)(compressed) | (*(uint8 *)(compressed + 1) & 0xf0) << 4) - 0x1000;
copy_len = (*(uint8 *)(compressed + 1) & 0xf) + 3;
compressed += 2;
if (copy_len == 3) {
copy_len = *(uint8 *)(compressed++) + 1;
if (copy_len == 1)
return;
}
}
2004-12-09 23:55:43 +00:00
while (copy_len > 0) {
*result = result[copy_offset];
result++;
copy_len--;
}
}
2003-08-15 18:00:22 +00:00
}
}
2009-05-25 06:49:57 +00:00
} // end of namespace Grim