Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
scummvm/engines/darkseed2/sprite.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1100 lines (806 sloc)
26.7 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* 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. | |
| * | |
| * $URL$ | |
| * $Id$ | |
| * | |
| */ | |
| #include "common/serializer.h" | |
| #include "common/system.h" | |
| #include "common/textconsole.h" | |
| #include "graphics/font.h" | |
| #include "graphics/fontman.h" | |
| #include "graphics/pixelformat.h" | |
| #include "graphics/surface.h" | |
| #include "image/pict.h" | |
| #include "darkseed2/sprite.h" | |
| #include "darkseed2/imageconverter.h" | |
| #include "darkseed2/resources.h" | |
| #include "darkseed2/cursors.h" | |
| #include "darkseed2/saveload.h" | |
| namespace DarkSeed2 { | |
| Sprite::Sprite() { | |
| clearData(); | |
| } | |
| Sprite::Sprite(const Sprite &sprite) : Saveable(sprite) { | |
| _transparencyMap = 0; | |
| copyFrom(sprite); | |
| } | |
| Sprite::~Sprite() { | |
| discard(); | |
| } | |
| Sprite &Sprite::operator=(const Sprite &sprite) { | |
| copyFrom(sprite); | |
| return *this; | |
| } | |
| void Sprite::copyFrom(const Sprite &sprite) { | |
| discard(); | |
| if (sprite._surfacePaletted.getPixels()) { | |
| _surfacePaletted.copyFrom(sprite._surfacePaletted); | |
| _transparencyMap = new uint8[_surfacePaletted.w * _surfacePaletted.h]; | |
| memcpy(_transparencyMap, sprite._transparencyMap, _surfacePaletted.w * _surfacePaletted.h); | |
| } | |
| if (sprite._surfaceTrueColor.getPixels()) | |
| _surfaceTrueColor.copyFrom(sprite._surfaceTrueColor); | |
| _palette = sprite._palette; | |
| _fileName = sprite._fileName; | |
| _fromCursor = sprite._fromCursor; | |
| _defaultX = sprite._defaultX; | |
| _defaultY = sprite._defaultY; | |
| _feetX = sprite._feetX; | |
| _feetY = sprite._feetY; | |
| _flippedHorizontally = sprite._flippedHorizontally; | |
| _flippedVertically = sprite._flippedVertically; | |
| _scale = sprite._scale; | |
| _scaleInverse = sprite._scaleInverse; | |
| } | |
| void Sprite::copyFrom(const byte *sprite, uint8 bpp, bool system) { | |
| if (bpp == 1) { | |
| memcpy(_surfacePaletted.getPixels(), sprite, _surfacePaletted.w * _surfacePaletted.h); | |
| memset(_transparencyMap, 0, _surfacePaletted.w * _surfacePaletted.h); | |
| convertToTrueColor(system); | |
| } else { | |
| memcpy(_surfaceTrueColor.getPixels(), sprite, _surfacePaletted.w * _surfacePaletted.h * 2); | |
| memset(_transparencyMap, 0, _surfacePaletted.w * _surfacePaletted.h); | |
| } | |
| } | |
| bool Sprite::exists() const { | |
| return _surfacePaletted.getPixels() != 0; | |
| } | |
| int32 Sprite::getWidth(bool unscaled) const { | |
| if (unscaled || (_scale == FRAC_ONE)) | |
| return _surfacePaletted.w; | |
| return fracToInt(_surfacePaletted.w * _scale); | |
| } | |
| int32 Sprite::getHeight(bool unscaled) const { | |
| if (unscaled || (_scale == FRAC_ONE)) | |
| return _surfacePaletted.h; | |
| return fracToInt(_surfacePaletted.h * _scale); | |
| } | |
| int32 Sprite::getDefaultX(bool unscaled) const { | |
| if (unscaled || (_scale == FRAC_ONE)) | |
| return _defaultX; | |
| return fracToInt(_defaultX * _scale); | |
| } | |
| int32 Sprite::getDefaultY(bool unscaled) const { | |
| if (unscaled || (_scale == FRAC_ONE)) | |
| return _defaultY; | |
| return fracToInt(_defaultY * _scale); | |
| } | |
| int32 Sprite::getFeetX(bool unscaled) const { | |
| if (unscaled || (_scale == FRAC_ONE)) | |
| return _feetX; | |
| return fracToInt(_feetX * _scale); | |
| } | |
| int32 Sprite::getFeetY(bool unscaled) const { | |
| if (unscaled || (_scale == FRAC_ONE)) | |
| return _feetY; | |
| return fracToInt(_feetY * _scale); | |
| } | |
| Common::Rect Sprite::getArea(bool unscaled) const { | |
| if (unscaled || (_scale == FRAC_ONE)) | |
| return Common::Rect(_surfacePaletted.w, _surfacePaletted.h); | |
| return Common::Rect(getWidth(), getHeight()); | |
| } | |
| const ::Graphics::Surface &Sprite::getPaletted() const { | |
| return _surfacePaletted; | |
| } | |
| const ::Graphics::Surface &Sprite::getTrueColor() const { | |
| return _surfaceTrueColor; | |
| } | |
| void Sprite::setPalette(const Palette &palette) { | |
| _palette = palette; | |
| } | |
| const Palette &Sprite::getPalette() const { | |
| return _palette; | |
| } | |
| void Sprite::create(int32 width, int32 height) { | |
| // Sanity checks | |
| assert((width > 0) && (height > 0) && (width <= 0x7FFF) && (height <= 0x7FFF)); | |
| discard(); | |
| _surfacePaletted.create(width, height, ::Graphics::PixelFormat::createFormatCLUT8()); | |
| _surfaceTrueColor.create(width, height, g_system->getScreenFormat()); | |
| _transparencyMap = new uint8[width * height]; | |
| clear(); | |
| } | |
| void Sprite::discard() { | |
| _surfacePaletted.free(); | |
| _surfaceTrueColor.free(); | |
| delete[] _transparencyMap; | |
| clearData(); | |
| } | |
| void Sprite::clearData() { | |
| _fileName.clear(); | |
| _fromCursor = false; | |
| _transparencyMap = 0; | |
| _defaultX = 0; | |
| _defaultY = 0; | |
| _feetX = 0; | |
| _feetY = 0; | |
| _flippedHorizontally = false; | |
| _flippedVertically = false; | |
| _scale = FRAC_ONE; | |
| _scaleInverse = FRAC_ONE; | |
| _palette.clear(); | |
| } | |
| void Sprite::convertToTrueColor(bool system) { | |
| if (!exists()) | |
| return; | |
| if (system) | |
| ImgConv.convert8bitSystem(_surfaceTrueColor, _surfacePaletted); | |
| else | |
| ImgConv.convert8bit(_surfaceTrueColor, _surfacePaletted, _palette); | |
| } | |
| void Sprite::createTransparencyMap() { | |
| if (!exists()) | |
| return; | |
| const byte *img = (const byte *)_surfacePaletted.getPixels(); | |
| uint8 *map = _transparencyMap; | |
| for (int32 y = 0; y < _surfacePaletted.h; y++) | |
| for (int32 x = 0; x < _surfacePaletted.w; x++) | |
| *map++ = (*img++ == 0) ? 1 : 0; | |
| } | |
| void Sprite::updateTransparencyMap() { | |
| if (!exists()) | |
| return; | |
| const byte *img = (const byte *)_surfaceTrueColor.getPixels(); | |
| uint8 *map = _transparencyMap; | |
| uint32 colorTransp = ImgConv.convertColor(0, _palette); | |
| for (int32 y = 0; y < _surfaceTrueColor.h; y++) { | |
| for (int32 x = 0; x < _surfaceTrueColor.w; x++, map++, img += _surfaceTrueColor.format.bytesPerPixel) { | |
| const uint32 p = ImgConv.readColor(img); | |
| if ((*map == 1) && (p != colorTransp)) | |
| *map = 0; | |
| } | |
| } | |
| } | |
| bool Sprite::loadFromImage(Resources &resources, const Common::String &image) { | |
| switch (resources.getPlatform()) { | |
| case Common::kPlatformWindows: | |
| return loadFromBMP(resources, image); | |
| case Common::kPlatformSaturn: | |
| return loadFromRGB(resources, image); | |
| case Common::kPlatformMacintosh: | |
| // TODO: Unknown format | |
| return false; | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool Sprite::loadFromRoomImage(Resources &resources, const Common::String &image) { | |
| switch (resources.getPlatform()) { | |
| case Common::kPlatformWindows: | |
| return loadFromBMP(resources, image); | |
| case Common::kPlatformSaturn: | |
| return loadFromBDP(resources, image); | |
| case Common::kPlatformMacintosh: | |
| return loadFromMacRoomImage(resources, image); | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool Sprite::loadFromInvItemImage(Resources &resources, const Common::String &image) { | |
| switch (resources.getPlatform()) { | |
| case Common::kPlatformWindows: | |
| return loadFromBMP(resources, image); | |
| case Common::kPlatformSaturn: | |
| return loadFromRGB(resources, image); | |
| case Common::kPlatformMacintosh: | |
| return loadFromPICT(resources, image); | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool Sprite::loadFromBoxImage(Resources &resources, const Common::String &image, | |
| int32 width, int32 height) { | |
| switch (resources.getPlatform()) { | |
| case Common::kPlatformWindows: | |
| return loadFromBMP(resources, image); | |
| case Common::kPlatformSaturn: | |
| return loadFrom256(resources, image, width, height); | |
| case Common::kPlatformMacintosh: | |
| return loadFromPICT(resources, image); | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool Sprite::loadFromBMP(Common::SeekableReadStream &bmp) { | |
| discard(); | |
| if (!bmp.seek(0)) | |
| return false; | |
| uint32 fSize = bmp.size(); | |
| // 'BM' | |
| if (bmp.readUint16BE() != 0x424D) | |
| return false; | |
| // Size of image + reserved + reserved | |
| bmp.skip(8); | |
| uint32 bmpDataOffset = bmp.readUint32LE(); | |
| if (bmpDataOffset >= fSize) | |
| return false; | |
| // Header size | |
| if (bmp.readUint32LE() != 40) | |
| return false; | |
| int32 width = (int32)bmp.readUint32LE(); | |
| int32 height = (int32)bmp.readUint32LE(); | |
| // Sanity checks | |
| assert((width > 0) && (height > 0) && (width <= 0x7FFF) && (height <= 0x7FFF)); | |
| // Create surfaces | |
| create(width, height); | |
| // Number of color planes | |
| if (bmp.readUint16LE() != 1) | |
| return false; | |
| // Bits per pixel | |
| if (bmp.readUint16LE() != 8) | |
| return false; | |
| uint32 compression = bmp.readUint32LE(); | |
| if ((compression != 0) && (compression != 2)) | |
| return false; | |
| uint32 bmpDataSize = bmp.readUint32LE(); | |
| // Sprite's feet position | |
| _feetX = (int32)MIN<uint16>(ABS(((int16)bmp.readUint16LE())), width - 1); | |
| _feetY = (int32)MIN<uint16>(ABS(((int16)bmp.readUint16LE())), height - 1); | |
| // Default coordinates | |
| _defaultX = (int32)bmp.readUint16LE(); | |
| _defaultY = (int32)bmp.readUint16LE(); | |
| uint32 numPalColors = bmp.readUint32LE(); | |
| if (numPalColors == 0) | |
| numPalColors = 256; | |
| if (numPalColors > 256) | |
| numPalColors = 256; | |
| if (bmpDataOffset == 54) { | |
| // Image data begins right after the header => no palette | |
| numPalColors = 0; | |
| } | |
| // Important colors | |
| bmp.skip(4); | |
| loadPalette(bmp, numPalColors); | |
| if (!bmp.seek(bmpDataOffset)) | |
| return false; | |
| if (compression == 0) { | |
| if (!readBMPDataComp0(bmp, bmpDataSize)) | |
| return false; | |
| } else if (compression == 2) { | |
| if (!readBMPDataComp2(bmp, bmpDataSize)) | |
| return false; | |
| } | |
| createTransparencyMap(); | |
| convertToTrueColor(); | |
| return true; | |
| } | |
| bool Sprite::loadFromRGB(Common::SeekableReadStream &rgb) { | |
| rgb.seek(0); | |
| int32 size = rgb.size(); | |
| uint16 width = rgb.readUint16BE(); | |
| uint16 height = rgb.readUint16BE(); | |
| if (size < (12 + width * height * 2)) | |
| return false; | |
| // Each line might be padded. I don't quite understand to which limits (some files pad to | |
| // a power of two, some to 80 bytes, some not at all), so we just calculate the pad from | |
| // the file size. | |
| uint16 linePad = (size - 4 - 8 - (width * height * 2)) / height; | |
| create(width, height); | |
| // TODO: Are those correct for RGB files? | |
| // Sprite's feet position | |
| _feetX = (int32)MIN<uint16>(ABS(((int16)rgb.readUint16BE())), width - 1); | |
| _feetY = (int32)MIN<uint16>(ABS(((int16)rgb.readUint16BE())), height - 1); | |
| // Default coordinates | |
| _defaultX = (int32)rgb.readUint16BE(); | |
| _defaultY = (int32)rgb.readUint16BE(); | |
| byte *img = (byte *)_surfaceTrueColor.getPixels(); | |
| uint8 *transp = _transparencyMap; | |
| for (int32 y = 0; y < height; y++) { | |
| for (int32 x = 0; x < width; x++) { | |
| ImgConv.writeColor(img, readColor555(rgb, transp)); | |
| img += _surfaceTrueColor.format.bytesPerPixel; | |
| transp++; | |
| } | |
| rgb.skip(linePad); | |
| } | |
| return true; | |
| } | |
| bool Sprite::loadFromBDP(Common::SeekableReadStream &bdp) { | |
| bdp.seek(0); | |
| if (bdp.size() != (320 * 240 * 2)) | |
| return false; | |
| create(320, 240); | |
| byte *img = (byte *)_surfaceTrueColor.getPixels(); | |
| for (int32 y = 0; y < 320; y++) { | |
| for (int32 x = 0; x < 240; x++) { | |
| ImgConv.writeColor(img, readColor555(bdp)); | |
| img += _surfaceTrueColor.format.bytesPerPixel; | |
| } | |
| } | |
| // Completely non-transparent | |
| memset(_transparencyMap, 0, _surfacePaletted.w * _surfacePaletted.h); | |
| return true; | |
| } | |
| bool Sprite::loadFrom256(Common::SeekableReadStream &f256, int32 width, int32 height) { | |
| if (f256.size() < (width * height)) | |
| return false; | |
| create(width, height); | |
| byte *img = (byte *)_surfacePaletted.getPixels(); | |
| for (int32 y = 0; y < width; y++) { | |
| for (int32 x = 0; x < height; x++) { | |
| *img++ = f256.readByte(); | |
| } | |
| } | |
| createTransparencyMap(); | |
| convertToTrueColor(); | |
| return true; | |
| } | |
| bool Sprite::loadFromSaturnCursor(Common::SeekableReadStream &cursor) { | |
| if (cursor.size() != 260) | |
| return false; | |
| _fromCursor = true; | |
| create(16, 16); | |
| cursor.seek(0); | |
| _feetX = cursor.readUint16BE(); | |
| _feetY = cursor.readUint16BE(); | |
| byte *img = (byte *)_surfaceTrueColor.getPixels(); | |
| for (int32 y = 0; y < 16; y++) { | |
| for (int32 x = 0; x < 16; x++) { | |
| const uint8 p = cursor.readByte(); | |
| const uint32 c = (p == 0) ? ImgConv.getColor(0, 0, 255) : ImgConv.getColor(255 - p, 255 - p, 255 - p); | |
| ImgConv.writeColor(img, c); | |
| img += _surfaceTrueColor.format.bytesPerPixel; | |
| } | |
| } | |
| return true; | |
| } | |
| uint32 Sprite::readColor555(Common::SeekableReadStream &stream, uint8 *transp) const { | |
| const uint16 p = stream.readUint16BE(); | |
| const uint8 r = ((p & 0x001F) ) << 3; | |
| const uint8 g = ((p & 0x03E0) >> 5) << 3; | |
| const uint8 b = ((p & 0x7C00) >> 10) << 3; | |
| if (transp) | |
| *transp = (p == 0) ? 1 : 0; | |
| return ImgConv.getColor(r, g, b); | |
| } | |
| bool Sprite::loadFromBMP(Resources &resources, const Common::String &bmp) { | |
| Common::String bmpFile = Resources::addExtension(bmp, "BMP"); | |
| if (!resources.hasResource(bmpFile)) | |
| return false; | |
| Common::SeekableReadStream *resBMP = resources.getResource(bmpFile); | |
| bool result = loadFromBMP(*resBMP); | |
| delete resBMP; | |
| _fileName = bmp; | |
| return result; | |
| } | |
| bool Sprite::loadFromRGB(Resources &resources, const Common::String &rgb) { | |
| Common::String rgbFile = Resources::addExtension(rgb, "RGB"); | |
| if (!resources.hasResource(rgbFile)) | |
| return false; | |
| Common::SeekableReadStream *resRGB = resources.getResource(rgbFile); | |
| bool result = loadFromRGB(*resRGB); | |
| delete resRGB; | |
| _fileName = rgb; | |
| return result; | |
| } | |
| bool Sprite::loadFromBDP(Resources &resources, const Common::String &bdp) { | |
| Common::String bdpFile = Resources::addExtension(bdp, "BDP"); | |
| if (!resources.hasResource(bdpFile)) | |
| return false; | |
| Common::SeekableReadStream *resBDP = resources.getResource(bdpFile); | |
| bool result = loadFromBDP(*resBDP); | |
| delete resBDP; | |
| _fileName = bdp; | |
| return result; | |
| } | |
| bool Sprite::loadFrom256(Resources &resources, const Common::String &f256, int32 width, int32 height) { | |
| Common::String f256File = Resources::addExtension(f256, "256"); | |
| if (!resources.hasResource(f256File)) | |
| return false; | |
| Common::SeekableReadStream *res256 = resources.getResource(f256File); | |
| bool result = loadFrom256(*res256, width, height); | |
| delete res256; | |
| _fileName = f256; | |
| return result; | |
| } | |
| bool Sprite::loadFromMacWalkMap(Resources &resources, const Common::String &image) { | |
| if (!resources.hasResource(image)) | |
| return false; | |
| Common::SeekableReadStream *stream = resources.getResource(image); | |
| create(64, 48); | |
| // TODO: Maybe add some sort of dummy palette? | |
| for (int32 y = 0; y < 48; y++) | |
| stream->read((byte *)_surfacePaletted.getBasePtr(0, y), 64); | |
| // Completely non-transparent | |
| memset(_transparencyMap, 0, _surfacePaletted.w * _surfacePaletted.h); | |
| convertToTrueColor(); | |
| delete stream; | |
| return true; | |
| } | |
| bool Sprite::loadFromMacRoomImage(Resources &resources, const Common::String &image) { | |
| if (!resources.hasResource(image)) | |
| return false; | |
| Common::SeekableReadStream *stream = resources.getResource(image); | |
| // First, read in the QuickTime palette | |
| // We can't read directly to _palette because create() hasn't been | |
| // called yet. | |
| stream->readUint32BE(); | |
| stream->readUint16BE(); | |
| uint16 colorCount = stream->readUint16BE() + 1; | |
| Palette pal; | |
| pal.resize(colorCount); | |
| for (uint16 i = 0; i < colorCount; i++) { | |
| stream->readUint16BE(); | |
| pal.get()[i * 3 + 0] = stream->readUint16BE() >> 8; | |
| pal.get()[i * 3 + 1] = stream->readUint16BE() >> 8; | |
| pal.get()[i * 3 + 2] = stream->readUint16BE() >> 8; | |
| } | |
| stream->readUint32BE(); // unknown | |
| uint16 height = stream->readUint16BE(); | |
| uint16 width = stream->readUint16BE(); | |
| create(width, height); | |
| _palette = pal; | |
| for (uint16 y = 0; y < height; y++) { | |
| // TODO: Figure out what these are exactly | |
| // If it's compression, it's the worst compression ever | |
| // Might be like the bmp comp 2 crap | |
| uint16 unk1 = stream->readUint16BE(); | |
| uint16 unk2 = stream->readUint16BE(); | |
| uint16 unk3 = stream->readUint16BE(); | |
| uint16 unk4 = stream->readUint16BE(); | |
| if (unk1 != 0x100) | |
| error("Mac room image unk1 = %d", unk1); | |
| if (unk2 != width + 4) | |
| error("Mac room image unk2 = %d", unk2); | |
| if (unk3 != 0x200) | |
| error("Mac room image unk3 = %d", unk3); | |
| if (unk4 != width) | |
| error("Mac room image unk4 = %d", unk4); | |
| stream->read((byte *)_surfacePaletted.getBasePtr(0, y), width); | |
| } | |
| // Completely non-transparent | |
| memset(_transparencyMap, 0, _surfacePaletted.w * _surfacePaletted.h); | |
| convertToTrueColor(); | |
| delete stream; | |
| return true; | |
| } | |
| bool Sprite::loadFromPICT(Resources &resources, const Common::String &image) { | |
| if (!resources.hasResource(image)) | |
| return false; | |
| Common::SeekableReadStream *stream = resources.getResource(image); | |
| Image::PICTDecoder pict; | |
| if (!pict.loadStream(*stream)) { | |
| warning("Failed to decode PICT image"); | |
| return false; | |
| } | |
| const ::Graphics::Surface *output = pict.getSurface(); | |
| const byte *palette = pict.getPalette(); | |
| delete stream; | |
| if (output->format.bytesPerPixel != 1) { | |
| warning("Only 8bpp PICT images supported"); | |
| return false; | |
| } | |
| create(output->w, output->h); | |
| _surfacePaletted.copyFrom(*output); | |
| _palette.copyFrom(palette, 256); | |
| createTransparencyMap(); | |
| convertToTrueColor(); | |
| return true; | |
| } | |
| bool Sprite::loadFromSaturnCursor(Resources &resources, const Common::String &cursor) { | |
| Common::String cursorFile = Resources::addExtension(cursor, "CUR"); | |
| if (!resources.hasResource(cursorFile)) | |
| return false; | |
| Common::SeekableReadStream *resCursor = resources.getResource(cursorFile); | |
| bool result = loadFromSaturnCursor(*resCursor); | |
| delete resCursor; | |
| _fileName = cursor; | |
| return result; | |
| } | |
| void Sprite::loadPalette(Common::SeekableReadStream &stream, uint32 count) { | |
| if (count == 0) | |
| return; | |
| byte *palette = new byte[count * 3]; | |
| for (uint32 i = 0; i < count ; i++) { | |
| palette[i * 3 + 2] = stream.readByte(); | |
| palette[i * 3 + 1] = stream.readByte(); | |
| palette[i * 3 + 0] = stream.readByte(); | |
| stream.skip(1); | |
| } | |
| _palette.copyFrom(palette, count); | |
| delete[] palette; | |
| } | |
| void Sprite::flipHorizontally() { | |
| if (!exists()) | |
| return; | |
| int32 width = _surfacePaletted.w; | |
| int32 height = _surfacePaletted.h; | |
| int32 halfWidth = width / 2; | |
| byte *dataPal = (byte *)_surfacePaletted.getPixels(); | |
| byte *dataTrue = (byte *)_surfaceTrueColor.getPixels(); | |
| uint8 *dataTransp = _transparencyMap; | |
| for (int32 i = 0; i < height; i++) { | |
| byte *dataPalStart = dataPal; | |
| byte *dataPalEnd = dataPal + width - 1; | |
| byte *dataTrueStart = dataTrue; | |
| byte *dataTrueEnd = dataTrue + _surfaceTrueColor.pitch - _surfaceTrueColor.format.bytesPerPixel; | |
| uint8 *dataTranspStart = dataTransp; | |
| uint8 *dataTranspEnd = dataTransp + width - 1; | |
| for (int32 j = 0; j < halfWidth; j++) { | |
| SWAP(*dataPalStart, *dataPalEnd); | |
| dataPalStart++; | |
| dataPalEnd--; | |
| ImgConv.swapColor(dataTrueStart, dataTrueEnd); | |
| dataTrueStart += _surfaceTrueColor.format.bytesPerPixel; | |
| dataTrueEnd -= _surfaceTrueColor.format.bytesPerPixel; | |
| SWAP(*dataTranspStart, *dataTranspEnd); | |
| dataTranspStart++; | |
| dataTranspEnd--; | |
| } | |
| dataPal += width; | |
| dataTrue += _surfaceTrueColor.pitch; | |
| dataTransp += width; | |
| } | |
| _feetX = width - _feetX; | |
| _flippedHorizontally = !_flippedHorizontally; | |
| } | |
| void Sprite::flipVertically() { | |
| if (!exists()) | |
| return; | |
| int32 width = _surfacePaletted.w; | |
| int32 height = _surfacePaletted.h; | |
| int32 halfHeight = height / 2; | |
| byte *dataPal = (byte *)_surfacePaletted.getPixels(); | |
| byte *dataTrue = (byte *)_surfaceTrueColor.getPixels(); | |
| uint8 *dataTransp = _transparencyMap; | |
| byte *dataPalStart = dataPal; | |
| byte *dataPalEnd = dataPal + (width * height) - width; | |
| byte *dataTrueStart = dataTrue; | |
| byte *dataTrueEnd = dataTrue + (_surfaceTrueColor.pitch * height) - _surfaceTrueColor.pitch; | |
| uint8 *dataTranspStart = dataTransp; | |
| uint8 *dataTranspEnd = dataTransp + (width * height) - width; | |
| byte *bufferPal = new byte [width]; | |
| byte *bufferTrue = new byte [_surfaceTrueColor.pitch]; | |
| uint8 *bufferTransp = new uint8 [width]; | |
| for (int32 i = 0; i < halfHeight; i++) { | |
| memcpy(bufferPal , dataPalStart, width); | |
| memcpy(dataPalStart, dataPalEnd , width); | |
| memcpy(dataPalEnd , bufferPal , width); | |
| dataPalStart += width; | |
| dataPalEnd -= width; | |
| memcpy(bufferTrue , dataTrueStart, _surfaceTrueColor.pitch); | |
| memcpy(dataTrueStart, dataTrueEnd , _surfaceTrueColor.pitch); | |
| memcpy(dataTrueEnd , bufferTrue , _surfaceTrueColor.pitch); | |
| dataTrueStart += _surfaceTrueColor.pitch; | |
| dataTrueEnd -= _surfaceTrueColor.pitch; | |
| memcpy(bufferTransp , dataTranspStart, width); | |
| memcpy(dataTranspStart, dataTranspEnd , width); | |
| memcpy(dataTranspEnd , bufferTransp , width); | |
| dataTranspStart += width; | |
| dataTranspEnd -= width; | |
| } | |
| delete[] bufferPal; | |
| delete[] bufferTrue; | |
| delete[] bufferTransp; | |
| _feetY = height - _feetY; | |
| _flippedVertically = !_flippedVertically; | |
| } | |
| void Sprite::blit(const Sprite &from, const Common::Rect &area, int32 x, int32 y, bool transp) { | |
| // Sanity checks | |
| assert((x >= 0) && (y >= 0) && (x <= 0x7FFF) && (y <= 0x7FFF)); | |
| if (!exists() || !from.exists()) | |
| return; | |
| Common::Rect toArea = getArea(true); | |
| toArea.left = x; | |
| toArea.top = y; | |
| if (toArea.isEmpty()) | |
| return; | |
| Common::Rect fromArea = from.getArea(); | |
| fromArea.clip(area); | |
| fromArea.setWidth (MIN(fromArea.width() , toArea.width())); | |
| fromArea.setHeight(MIN(fromArea.height(), toArea.height())); | |
| if (fromArea.isEmpty() || !fromArea.isValidRect()) | |
| return; | |
| int32 w = fromArea.width(); | |
| int32 h = fromArea.height(); | |
| const int32 fromTop = fracToInt(fromArea.top * from._scaleInverse); | |
| const int32 fromLeft = fracToInt(fromArea.left * from._scaleInverse); | |
| const byte *src = (const byte *)from._surfaceTrueColor.getBasePtr(fromLeft, fromTop); | |
| byte *dst = ( byte *) _surfaceTrueColor.getBasePtr(x, y); | |
| const uint8 *srcT = from._transparencyMap + fromTop * from._surfaceTrueColor.w + fromLeft; | |
| uint8 *dstT = _transparencyMap + y * _surfaceTrueColor.w + x; | |
| frac_t posW = 0, posH = 0; | |
| while (h-- > 0) { | |
| posW = 0; | |
| const byte *srcRow = src; | |
| byte *dstRow = dst; | |
| const uint8 *srcRowT = srcT; | |
| uint8 *dstRowT = dstT; | |
| for (int32 j = 0; j < w; j++, dstRow += _surfaceTrueColor.format.bytesPerPixel, dstRowT++) { | |
| if (!transp || (*srcRowT == 0)) { | |
| // Ignore transparency or source is solid => copy | |
| memcpy(dstRow, srcRow, _surfaceTrueColor.format.bytesPerPixel); | |
| *dstRowT = *srcRowT; | |
| } else if (*srcRowT == 2) { | |
| // Half-transparent | |
| if (*dstRowT == 1) | |
| // But destination is transparent => propagate | |
| memcpy(dstRow, srcRow, _surfaceTrueColor.format.bytesPerPixel); | |
| else | |
| // Destination is solid => mix | |
| ImgConv.mixTrueColor(dstRow, srcRow); | |
| *dstRowT = *srcRowT; | |
| } | |
| // Advance source data | |
| posW += from._scaleInverse; | |
| while (posW >= ((frac_t) FRAC_ONE)) { | |
| srcRow += from._surfaceTrueColor.format.bytesPerPixel; | |
| srcRowT++; | |
| posW -= FRAC_ONE; | |
| } | |
| } | |
| dst += _surfaceTrueColor.pitch; | |
| dstT += _surfaceTrueColor.w; | |
| // Advance source data | |
| posH += from._scaleInverse; | |
| while (posH >= ((frac_t) FRAC_ONE)) { | |
| src += from._surfaceTrueColor.pitch; | |
| srcT += from._surfaceTrueColor.w; | |
| posH -= FRAC_ONE; | |
| } | |
| } | |
| } | |
| void Sprite::blit(const Sprite &from, int32 x, int32 y, bool transp) { | |
| blit(from, from.getArea(), x, y, transp); | |
| } | |
| void Sprite::fillImage(byte cP, uint32 cT) { | |
| memset(_surfacePaletted.getPixels(), cP, | |
| _surfacePaletted.w * _surfacePaletted.h * _surfacePaletted.format.bytesPerPixel); | |
| byte *trueColor = (byte *)_surfaceTrueColor.getPixels(); | |
| for (int32 y = 0; y < _surfaceTrueColor.h; y++) { | |
| for (int32 x = 0; x < _surfaceTrueColor.w; x++) { | |
| if (_surfaceTrueColor.format.bytesPerPixel == 2) | |
| ImgConv.writeColor(trueColor, cT); | |
| trueColor += _surfaceTrueColor.format.bytesPerPixel; | |
| } | |
| } | |
| } | |
| void Sprite::fill(byte c) { | |
| if (!exists()) | |
| return; | |
| fillImage(c, ImgConv.convertColor(c, _palette)); | |
| memset(_transparencyMap, 0, _surfacePaletted.w * _surfacePaletted.h); | |
| } | |
| void Sprite::fill(uint32 c) { | |
| if (!exists()) | |
| return; | |
| fillImage(0, c); | |
| memset(_transparencyMap, 0, _surfacePaletted.w * _surfacePaletted.h); | |
| } | |
| void Sprite::clear() { | |
| if (!exists()) | |
| return; | |
| fillImage(0, ImgConv.convertColor(0, _palette)); | |
| memset(_transparencyMap, 1, _surfacePaletted.w * _surfacePaletted.h); | |
| } | |
| void Sprite::darken() { | |
| if (!exists()) | |
| return; | |
| fillImage(0, ImgConv.getColor(0, 0, 0)); | |
| memset(_transparencyMap, 0, _surfacePaletted.w * _surfacePaletted.h); | |
| } | |
| void Sprite::shade(uint32 c) { | |
| if (!exists()) | |
| return; | |
| fillImage(0, c); | |
| memset(_transparencyMap, 2, _surfacePaletted.w * _surfacePaletted.h); | |
| } | |
| void Sprite::drawStrings(const FontManager::TextList &strings, const FontManager &fontManager, | |
| int x, int y, uint32 color) { | |
| for (FontManager::TextList::const_iterator it = strings.begin(); it != strings.end(); ++it) { | |
| fontManager.drawText(_surfaceTrueColor, *it, x, y, color); | |
| y += fontManager.getFontHeight(); | |
| } | |
| updateTransparencyMap(); | |
| } | |
| bool Sprite::readBMPDataComp0(Common::SeekableReadStream &bmp, uint32 dataSize) { | |
| int32 width = _surfacePaletted.w; | |
| int32 height = _surfacePaletted.h; | |
| byte *data = (byte *)_surfacePaletted.getBasePtr(0, height - 1); | |
| int extraDataLength = (width % 4) ? 4 - (width % 4) : 0; | |
| for (int32 i = 0; i < height; i++) { | |
| byte *rowData = data; | |
| for (int32 j = 0; j < width; j++) | |
| *rowData++ = bmp.readByte(); | |
| bmp.skip(extraDataLength); | |
| data -= width; | |
| } | |
| return true; | |
| } | |
| bool Sprite::readBMPDataComp2(Common::SeekableReadStream &bmp, uint32 dataSize) { | |
| int32 width = _surfacePaletted.w; | |
| int32 height = _surfacePaletted.h; | |
| byte *data = (byte *)_surfacePaletted.getBasePtr(0, height - 1); | |
| for (int32 i = 0; i < height; i++) { | |
| byte *rowData = data; | |
| // Skip this many pixels (they'll stay transparent) | |
| int32 sizeSkip = bmp.readUint16LE(); | |
| // Read this many pixels of data | |
| int32 sizeData = bmp.readUint16LE(); | |
| if ((sizeSkip + sizeData) > width) { | |
| warning("Sprite::readBMPDataComp2(): Broken image compression: size %d (%d + %d), width %d", | |
| sizeSkip + sizeData, sizeSkip, sizeData, width); | |
| return false; | |
| } | |
| rowData += sizeSkip; | |
| bmp.read(rowData, sizeData); | |
| data -= width; | |
| } | |
| return true; | |
| } | |
| frac_t Sprite::getScale() const { | |
| return _scale; | |
| } | |
| void Sprite::setScale(frac_t scale) { | |
| assert(scale != 0); | |
| _scale = scale; | |
| // Is there a better way to do that? :/ | |
| _scaleInverse = doubleToFrac(1.0 / fracToDouble(scale)); | |
| } | |
| bool Sprite::saveLoad(Common::Serializer &serializer, Resources &resources) { | |
| assert(!_fromCursor); | |
| uint32 scale = (uint32)_scale; | |
| SaveLoad::sync(serializer, _fileName); | |
| SaveLoad::sync(serializer, _flippedHorizontally); | |
| SaveLoad::sync(serializer, _flippedVertically); | |
| SaveLoad::sync(serializer, scale); | |
| _scale = (frac_t) scale; | |
| return true; | |
| } | |
| bool Sprite::loading(Resources &resources) { | |
| if (_fileName.empty()) | |
| return true; | |
| byte flippedHorizontally = _flippedHorizontally; | |
| byte flippedVertically = _flippedVertically; | |
| uint32 scale = _scale; | |
| loadFromImage(resources, _fileName); | |
| if (flippedHorizontally) | |
| flipHorizontally(); | |
| if (flippedVertically) | |
| flipVertically(); | |
| setScale(scale); | |
| return true; | |
| } | |
| } // End of namespace DarkSeed2 |