ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

bitmap.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/endian.h"
00024 
00025 #include "graphics/colormasks.h"
00026 #include "graphics/pixelbuffer.h"
00027 #include "image/tga.h"
00028 
00029 #include "engines/grim/savegame.h"
00030 #include "engines/grim/debug.h"
00031 #include "engines/grim/bitmap.h"
00032 #include "engines/grim/resource.h"
00033 #include "engines/grim/gfx_base.h"
00034 
00035 namespace Grim {
00036 
00037 static bool decompress_codec3(const char *compressed, char *result, int maxBytes);
00038 
00039 Common::HashMap<Common::String, BitmapData *> *BitmapData::_bitmaps = nullptr;
00040 
00041 BitmapData *BitmapData::getBitmapData(const Common::String &fname) {
00042     Common::String str(fname);
00043     if (_bitmaps && _bitmaps->contains(str)) {
00044         BitmapData *b = (*_bitmaps)[str];
00045         ++b->_refCount;
00046         return b;
00047     }
00048 
00049     BitmapData *b = new BitmapData(fname);
00050     if (!_bitmaps) {
00051         _bitmaps = new Common::HashMap<Common::String, BitmapData *>();
00052     }
00053     (*_bitmaps)[str] = b;
00054     return b;
00055 }
00056 
00057 BitmapData::BitmapData(const Common::String &fname) {
00058     _fname = fname;
00059     _refCount = 1;
00060     _data = nullptr;
00061     _loaded = false;
00062     _keepData = true;
00063 
00064     // Initialize members to avoid warnings:
00065     _numImages = 0;
00066     _width = 0;
00067     _height = 0;
00068     _x = 0;
00069     _y = 0;
00070     _format = 0;
00071     _numTex = 0;
00072     _bpp = 0;
00073     _colorFormat = 0;
00074     _texIds = nullptr;
00075     _hasTransparency = 0;
00076 
00077     _texc = nullptr;
00078 
00079     _verts = nullptr;
00080     _layers = nullptr;
00081 
00082     _numCoords = 0;
00083     _numVerts = 0;
00084     _numLayers = 0;
00085 
00086     _userData = nullptr;
00087 }
00088 
00089 void BitmapData::load() {
00090     if (_loaded) {
00091         return;
00092     }
00093     Common::SeekableReadStream *data = g_resourceloader->openNewStreamFile(_fname.c_str());
00094 
00095     uint32 tag = data->readUint32BE();
00096     switch(tag) {
00097         case(MKTAG('B','M',' ',' ')):               //Grim bitmap
00098             loadGrimBm(data);
00099             break;
00100         case(MKTAG('T','I','L','0')):               // MI4 bitmap
00101             loadTile(data);
00102             break;
00103         default:
00104             if (!loadTGA(data)) // Try to load as TGA.
00105                 Debug::error(Debug::Bitmaps, "Invalid magic loading bitmap");
00106             break;
00107     }
00108     delete data;
00109     _loaded = true;
00110 }
00111 
00112 bool BitmapData::loadGrimBm(Common::SeekableReadStream *data) {
00113     uint32 tag2 = data->readUint32BE();
00114     if (tag2 != (MKTAG('F','\0','\0','\0')))
00115         return false;
00116 
00117     int codec = data->readUint32LE();
00118     data->readUint32LE();               //_paletteIncluded
00119     _numImages = data->readUint32LE();
00120     _x = data->readUint32LE();
00121     _y = data->readUint32LE();
00122     data->readUint32LE();               //_transparentColor
00123     _format = data->readUint32LE();
00124     _bpp = data->readUint32LE();
00125 //  uint32 redBits = data->readUint32LE();
00126 //  uint32 greenBits = data->readUint32LE();
00127 //  uint32 blueBits = data->readUint32LE();
00128 //  uint32 redShift = data->readUint32LE();
00129 //  uint32 greenShift = data->readUint32LE();
00130 //  uint32 blueShift = data->readUint32LE();
00131 
00132     // Hardcode the format, since the values saved in the files are garbage for some, like "ha_0_elvos.zbm".
00133     Graphics::PixelFormat pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
00134 
00135     data->seek(128, SEEK_SET);
00136     _width = data->readUint32LE();
00137     _height = data->readUint32LE();
00138     _colorFormat = BM_RGB565;
00139     _hasTransparency = false;
00140 
00141     _data = new Graphics::PixelBuffer[_numImages];
00142     data->seek(0x80, SEEK_SET);
00143     for (int i = 0; i < _numImages; i++) {
00144         data->seek(8, SEEK_CUR);
00145         _data[i].create(pixelFormat, _width * _height, DisposeAfterUse::YES);
00146         if (codec == 0) {
00147             uint32 dsize = _bpp / 8 * _width * _height;
00148             data->read(_data[i].getRawBuffer(), dsize);
00149         } else if (codec == 3) {
00150             int compressed_len = data->readUint32LE();
00151             char *compressed = new char[compressed_len];
00152             data->read(compressed, compressed_len);
00153             bool success = decompress_codec3(compressed, (char *)_data[i].getRawBuffer(), _bpp / 8 * _width * _height);
00154             delete[] compressed;
00155             if (!success)
00156                 warning(".. when loading image %s.\n", _fname.c_str());
00157         } else
00158             Debug::error(Debug::Bitmaps, "Unknown image codec in BitmapData ctor!");
00159 
00160 #ifdef SCUMM_BIG_ENDIAN
00161         if (_format == 1) {
00162             uint16 *d = (uint16 *)_data[i].getRawBuffer();
00163             for (int j = 0; j < _width * _height; ++j) {
00164                 d[j] = SWAP_BYTES_16(d[j]);
00165             }
00166         }
00167 #endif
00168     }
00169 
00170     // Initially, no GPU-side textures created. the createBitmap
00171     // function will allocate some if necessary (and successful)
00172     _numTex = 0;
00173     _texIds = nullptr;
00174 
00175     g_driver->createBitmap(this);
00176     return true;
00177 }
00178 
00179 BitmapData::BitmapData(const Graphics::PixelBuffer &buf, int w, int h, const char *fname) : _fname(fname) {
00180     _refCount = 1;
00181     Debug::debug(Debug::Bitmaps, "New bitmap loaded: %s\n", fname);
00182     _numImages = 1;
00183     _x = 0;
00184     _y = 0;
00185     _width = w;
00186     _height = h;
00187     _format = 1;
00188     _numTex = 0;
00189     _texIds = nullptr;
00190     _bpp = buf.getFormat().bytesPerPixel * 8;
00191     _hasTransparency = false;
00192     _colorFormat = BM_RGB565;
00193     _data = new Graphics::PixelBuffer[_numImages];
00194     _data[0].create(buf.getFormat(), w * h, DisposeAfterUse::YES);
00195     _data[0].copyBuffer(0, w * h, buf);
00196     _loaded = true;
00197     _keepData = true;
00198 
00199     _userData = nullptr;
00200     _texc = nullptr;
00201     _verts = nullptr;
00202     _layers = nullptr;
00203 
00204     g_driver->createBitmap(this);
00205 }
00206 
00207 BitmapData::BitmapData() :
00208         _numImages(0), _width(0), _height(0), _x(0), _y(0), _format(0), _numTex(0),
00209         _bpp(0), _colorFormat(0), _texIds(nullptr), _hasTransparency(false), _data(nullptr),
00210         _refCount(1), _loaded(false), _keepData(false), _texc(nullptr), _verts(nullptr),
00211         _layers(nullptr), _numCoords(0), _numVerts(0), _numLayers(0), _userData(nullptr) {
00212 }
00213 
00214 BitmapData::~BitmapData() {
00215     _keepData = false;
00216     if (_loaded) {
00217         g_driver->destroyBitmap(this);
00218     }
00219     freeData();
00220     if (_bitmaps) {
00221         if (_bitmaps->contains(_fname)) {
00222             _bitmaps->erase(_fname);
00223         }
00224         if (_bitmaps->empty()) {
00225             delete _bitmaps;
00226             _bitmaps = nullptr;
00227         }
00228     }
00229     delete[] _texc;
00230     delete[] _layers;
00231     delete[] _verts;
00232 }
00233 
00234 void BitmapData::freeData() {
00235     if (!_keepData && _data) {
00236         for (int i = 0; i < _numImages; ++i) {
00237             _data[i].free();
00238         }
00239         delete[] _data;
00240         _data = nullptr;
00241     }
00242 }
00243 
00244 bool BitmapData::loadTGA(Common::SeekableReadStream *data) {
00245     Image::TGADecoder dec;
00246     bool success = dec.loadStream(*data);
00247 
00248     if (!success)
00249         return false;
00250 
00251     const Graphics::Surface *origSurf = dec.getSurface();
00252     Graphics::PixelFormat pixelFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
00253     Graphics::Surface *surf = origSurf->convertTo(pixelFormat);
00254 
00255     _width = surf->w;
00256     _height = surf->h;
00257     _format = 1;
00258     _x = _y = 0;
00259     _bpp = 4;
00260     _colorFormat = BM_RGBA;
00261     _numImages = 1;
00262     _data = new Graphics::PixelBuffer[1];
00263     _data[0].set(pixelFormat, (unsigned char *)surf->getPixels());
00264 
00265     g_driver->createBitmap(this);
00266 
00267     freeData();
00268     delete surf;
00269 
00270     return true;
00271 }
00272 
00273 bool BitmapData::loadTile(Common::SeekableReadStream *o) {
00274 #ifdef ENABLE_MONKEY4
00275     _x = 0;
00276     _y = 0;
00277     _format = 1;
00278     o->seek(0, SEEK_SET);
00279 
00280     /*uint32 id = */o->readUint32LE();
00281     // Should check that we actually HAVE a TIL
00282     uint32 bmoffset = o->readUint32LE();
00283     _numCoords = o->readUint32LE();
00284     _numLayers = o->readUint32LE();
00285     _numVerts = o->readUint32LE();
00286 
00287     // skip some 0
00288     o->seek(16, SEEK_CUR);
00289     _texc = new float[_numCoords * 4];
00290 
00291     for (uint32 i = 0; i < _numCoords * 4; ++i) {
00292         _texc[i] = o->readFloatLE();
00293     }
00294 
00295     _layers = new Layer[_numLayers];
00296     for (uint32 i = 0; i < _numLayers; ++i) {
00297         _layers[i]._offset = o->readUint32LE();
00298         _layers[i]._numImages = o->readUint32LE();
00299     }
00300 
00301     _verts = new Vert[_numVerts];
00302     for (uint32 i = 0; i < _numVerts; ++i) {
00303         _verts[i]._texid = o->readUint32LE();
00304         _verts[i]._pos = o->readUint32LE();
00305         _verts[i]._verts = o->readUint32LE();
00306     }
00307 
00308     o->seek(16, SEEK_CUR);
00309     int numSubImages = o->readUint32LE();
00310 
00311     char **data = new char *[numSubImages];
00312 
00313     o->seek(16, SEEK_CUR);
00314     _bpp = o->readUint32LE();
00315 
00316     o->seek(bmoffset + 128);
00317 
00318     _width = o->readUint32LE();
00319     _height = o->readUint32LE();
00320     o->seek(-8, SEEK_CUR);
00321 
00322     int size = 4 * _width * _height;
00323     for (int i = 0; i < numSubImages; ++i) {
00324         data[i] = new char[size];
00325         o->seek(8, SEEK_CUR);
00326         if (_bpp == 16) {
00327             uint32 *d = (uint32 *)data[i];
00328             for (int j = 0; j < _width * _height; ++j) {
00329                 uint16 p = o->readUint16LE();
00330                 // These values are shifted left by 3 so that they saturate the color channel
00331                 uint8 b = (p & 0x7C00) >> 7;
00332                 uint8 g = (p & 0x03E0) >> 2;
00333                 uint8 r = (p & 0x001F) << 3;
00334                 uint8 a = (p & 0x8000) ? 0xFF : 0x00;
00335                 // Recombine the color components into a 32 bit RGB value
00336                 uint32 tmp = (r << 24) | (g << 16) | (b << 8) | a;
00337                 WRITE_BE_UINT32(&d[j], tmp);
00338             }
00339         } else if (_bpp == 32) {
00340             uint32 *d = (uint32 *)data[i];
00341             for (int j = 0; j < _width * _height; ++j) {
00342                 o->read(&(d[j]), 4);
00343             }
00344         }
00345     }
00346     _bpp = 32;
00347 
00348     Graphics::PixelFormat pixelFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
00349     _colorFormat = BM_RGBA;
00350 
00351     _width = 256;
00352     _height = 256;
00353     _numImages = numSubImages;
00354     _data = new Graphics::PixelBuffer[_numImages];
00355     for (int i = 0; i < _numImages; ++i) {
00356         _data[i].create(pixelFormat, _width * _height, DisposeAfterUse::YES);
00357         _data[i].set(pixelFormat, (byte *)data[i]);
00358     }
00359 
00360     delete[] data;
00361 
00362     g_driver->createBitmap(this);
00363 #endif // ENABLE_MONKEY4
00364     return true;
00365 }
00366 
00367 const Graphics::PixelBuffer &BitmapData::getImageData(int num) const {
00368     assert(num >= 0);
00369     assert(num < _numImages);
00370     return _data[num];
00371 }
00372 
00373 // Bitmap
00374 
00375 Bitmap::Bitmap(const Common::String &fname) {
00376     _data = BitmapData::getBitmapData(fname);
00377     _currImage = 1;
00378 }
00379 
00380 Bitmap::Bitmap(const Graphics::PixelBuffer &buf, int w, int h, const char *fname) {
00381     _data = new BitmapData(buf, w, h, fname);
00382     _currImage = 1;
00383 }
00384 
00385 Bitmap::Bitmap() {
00386     _data = new BitmapData();
00387     _currImage = 0;
00388 }
00389 
00390 Bitmap *Bitmap::create(const Common::String &filename) {
00391     if (!SearchMan.hasFile(filename)) {
00392         warning("Could not find bitmap %s", filename.c_str());
00393         return nullptr;
00394     }
00395     Bitmap *b = new Bitmap(filename);
00396     return b;
00397 }
00398 
00399 void Bitmap::saveState(SaveGame *state) const {
00400     state->writeString(getFilename());
00401 
00402     state->writeLESint32(getActiveImage());
00403 }
00404 
00405 void Bitmap::restoreState(SaveGame *state) {
00406     freeData();
00407 
00408     Common::String fname = state->readString();
00409     _data = BitmapData::getBitmapData(fname);
00410 
00411     _currImage = state->readLESint32();
00412 }
00413 
00414 void Bitmap::draw() {
00415     _data->load();
00416     if (_currImage == 0)
00417         return;
00418 
00419     g_driver->drawBitmap(this, _data->_x, _data->_y);
00420 }
00421 
00422 void Bitmap::draw(int x, int y) {
00423     _data->load();
00424     if (_currImage == 0)
00425         return;
00426 
00427     g_driver->drawBitmap(this, x, y, _data->_numLayers - 1);
00428 }
00429 
00430 void Bitmap::drawLayer(uint32 layer) {
00431     _data->load();
00432     if (_currImage == 0)
00433         return;
00434 
00435     g_driver->drawBitmap(this, _data->_x, _data->_y, layer);
00436 }
00437 
00438 void Bitmap::setActiveImage(int n) {
00439     assert(n >= 0);
00440     _data->load();
00441     if ((n - 1) >= _data->_numImages) {
00442         warning("Bitmap::setActiveImage: no anim image: %d. (%s)", n, _data->_fname.c_str());
00443     } else {
00444         _currImage = n;
00445     }
00446 }
00447 
00448 int Bitmap::getNumImages() const {
00449     _data->load();
00450     return _data->_numImages;
00451 }
00452 
00453 int Bitmap::getNumLayers() const {
00454     _data->load();
00455     return _data->_numLayers;
00456 }
00457 
00458 void Bitmap::freeData() {
00459     --_data->_refCount;
00460     if (_data->_refCount < 1) {
00461         delete _data;
00462         _data = nullptr;
00463     }
00464 }
00465 
00466 Bitmap::~Bitmap() {
00467     freeData();
00468 }
00469 
00470 const Graphics::PixelFormat &Bitmap::getPixelFormat(int num) const {
00471     return getData(num).getFormat();
00472 }
00473 
00474 void BitmapData::convertToColorFormat(int num, const Graphics::PixelFormat &format) {
00475     if (_data[num].getFormat() == format) {
00476         return;
00477     }
00478 
00479     Graphics::PixelBuffer dst(format, _width * _height, DisposeAfterUse::NO);
00480     dst.copyBuffer(0, _width * _height, _data[num]);
00481     _data[num].free();
00482     _data[num] = dst;
00483 }
00484 
00485 void BitmapData::convertToColorFormat(const Graphics::PixelFormat &format) {
00486     for (int i = 0; i < _numImages; ++i) {
00487         convertToColorFormat(i, format);
00488     }
00489 }
00490 
00491 #define GET_BIT do { bit = bitstr_value & 1; \
00492     bitstr_len--; \
00493     bitstr_value >>= 1; \
00494     if (bitstr_len == 0) { \
00495         bitstr_value = READ_LE_UINT16(compressed); \
00496         bitstr_len = 16; \
00497         compressed += 2; \
00498     } \
00499 } while (0)
00500 
00501 static bool decompress_codec3(const char *compressed, char *result, int maxBytes) {
00502     int bitstr_value = READ_LE_UINT16(compressed);
00503     int bitstr_len = 16;
00504     compressed += 2;
00505     bool bit;
00506 
00507     int byteIndex = 0;
00508     for (;;) {
00509         GET_BIT;
00510         if (bit == 1) {
00511             if (byteIndex >= maxBytes) {
00512                 warning("Buffer overflow when decoding image: decompress_codec3 walked past the input buffer!");
00513                 return false;
00514             } else {
00515                 *result++ = *compressed++;
00516             }
00517             ++byteIndex;
00518         } else {
00519             GET_BIT;
00520             int copy_len, copy_offset;
00521             if (bit == 0) {
00522                 GET_BIT;
00523                 copy_len = 2 * bit;
00524                 GET_BIT;
00525                 copy_len += bit + 3;
00526                 copy_offset = *(const uint8 *)(compressed++) - 0x100;
00527             } else {
00528                 copy_offset = (*(const uint8 *)(compressed) | (*(const uint8 *)(compressed + 1) & 0xf0) << 4) - 0x1000;
00529                 copy_len = (*(const uint8 *)(compressed + 1) & 0xf) + 3;
00530                 compressed += 2;
00531                 if (copy_len == 3) {
00532                     copy_len = *(const uint8 *)(compressed++) + 1;
00533                     if (copy_len == 1)
00534                         return true;
00535                 }
00536             }
00537             while (copy_len > 0) {
00538                 if (byteIndex >= maxBytes) {
00539                     warning("Buffer overflow when decoding image: decompress_codec3 walked past the input buffer!");
00540                     return false;
00541                 } else {
00542                     assert(byteIndex + copy_offset >= 0);
00543                     assert(byteIndex + copy_offset < maxBytes);
00544                     *result = result[copy_offset];
00545                     result++;
00546                 }
00547                 ++byteIndex;
00548                 copy_len--;
00549             }
00550         }
00551     }
00552     return true;
00553 }
00554 
00555 } // end of namespace Grim


Generated on Sat Feb 23 2019 05:00:57 for ResidualVM by doxygen 1.7.1
curved edge   curved edge