/* 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 "common/ptr.h" #include "common/stream.h" #include "common/textconsole.h" #include "graphics/wincursor.h" namespace Graphics { /** A Windows cursor. */ class WinCursor : public Cursor { public: WinCursor(); ~WinCursor(); /** Return the cursor's width. */ uint16 getWidth() const; /** Return the cursor's height. */ uint16 getHeight() const; /** Return the cursor's hotspot's x coordinate. */ uint16 getHotspotX() const; /** Return the cursor's hotspot's y coordinate. */ uint16 getHotspotY() const; /** Return the cursor's transparent key. */ byte getKeyColor() const; const byte *getSurface() const { return _surface; } const byte *getPalette() const { return _palette; } byte getPaletteStartIndex() const { return 0; } uint16 getPaletteCount() const { return 256; } /** Read the cursor's data out of a stream. */ bool readFromStream(Common::SeekableReadStream &stream); private: byte *_surface; byte _palette[256 * 3]; uint16 _width; ///< The cursor's width. uint16 _height; ///< The cursor's height. uint16 _hotspotX; ///< The cursor's hotspot's x coordinate. uint16 _hotspotY; ///< The cursor's hotspot's y coordinate. byte _keyColor; ///< The cursor's transparent key /** Clear the cursor. */ void clear(); }; WinCursor::WinCursor() { _width = 0; _height = 0; _hotspotX = 0; _hotspotY = 0; _surface = 0; _keyColor = 0; memset(_palette, 0, 256 * 3); } WinCursor::~WinCursor() { clear(); } uint16 WinCursor::getWidth() const { return _width; } uint16 WinCursor::getHeight() const { return _height; } uint16 WinCursor::getHotspotX() const { return _hotspotX; } uint16 WinCursor::getHotspotY() const { return _hotspotY; } byte WinCursor::getKeyColor() const { return _keyColor; } bool WinCursor::readFromStream(Common::SeekableReadStream &stream) { clear(); _hotspotX = stream.readUint16LE(); _hotspotY = stream.readUint16LE(); // Check header size if (stream.readUint32LE() != 40) return false; // Check dimensions _width = stream.readUint32LE(); _height = stream.readUint32LE() / 2; if (_width & 3) { // Cursors should always be a power of 2 // Of course, it wouldn't be hard to handle but if we have no examples... warning("Non-divisible-by-4 width cursor found"); return false; } // Color planes if (stream.readUint16LE() != 1) return false; // Only 1bpp and 8bpp supported uint16 bitsPerPixel = stream.readUint16LE(); if (bitsPerPixel != 1 && bitsPerPixel != 8) return false; // Compression if (stream.readUint32LE() != 0) return false; // Image size + X resolution + Y resolution stream.skip(12); uint32 numColors = stream.readUint32LE(); // If the color count is 0, then it uses up the maximum amount if (numColors == 0) numColors = 1 << bitsPerPixel; // Reading the palette stream.seek(40 + 4); for (uint32 i = 0 ; i < numColors; i++) { _palette[i * 3 + 2] = stream.readByte(); _palette[i * 3 + 1] = stream.readByte(); _palette[i * 3 ] = stream.readByte(); stream.readByte(); } // Reading the bitmap data uint32 dataSize = stream.size() - stream.pos(); byte *initialSource = new byte[dataSize]; stream.read(initialSource, dataSize); // Parse the XOR map const byte *src = initialSource; _surface = new byte[_width * _height]; byte *dest = _surface + _width * (_height - 1); uint32 imagePitch = _width * bitsPerPixel / 8; for (uint32 i = 0; i < _height; i++) { byte *rowDest = dest; if (bitsPerPixel == 1) { // 1bpp for (uint16 j = 0; j < (_width / 8); j++) { byte p = src[j]; for (int k = 0; k < 8; k++, rowDest++, p <<= 1) { if ((p & 0x80) == 0x80) *rowDest = 1; else *rowDest = 0; } } } else { // 8bpp memcpy(rowDest, src, _width); } dest -= _width; src += imagePitch; } // Calculate our key color if (numColors < 256) { // If we're not using the maximum colors in a byte, we can fit it in _keyColor = numColors; } else { // HACK: Try to find a color that's not being used so it can become // our keycolor. It's quite impossible to fit 257 entries into 256... for (uint32 i = 0; i < 256; i++) { for (int j = 0; j < _width * _height; j++) { // TODO: Also check to see if the space is transparent if (_surface[j] == i) break; if (j == _width * _height - 1) { _keyColor = i; i = 256; break; } } } } // Now go through and apply the AND map to get the transparency uint32 andWidth = (_width + 7) / 8; src += andWidth * (_height - 1); for (uint32 y = 0; y < _height; y++) { for (uint32 x = 0; x < _width; x++) if (src[x / 8] & (1 << (7 - x % 8))) _surface[y * _width + x] = _keyColor; src -= andWidth; } delete[] initialSource; return true; } void WinCursor::clear() { delete[] _surface; _surface = 0; } WinCursorGroup::WinCursorGroup() { } WinCursorGroup::~WinCursorGroup() { for (uint32 i = 0; i < cursors.size(); i++) delete cursors[i].cursor; } WinCursorGroup *WinCursorGroup::createCursorGroup(Common::WinResources *exe, const Common::WinResourceID &id) { Common::ScopedPtr stream(exe->getResource(Common::kWinGroupCursor, id)); if (!stream || stream->size() <= 6) return 0; stream->skip(4); uint32 cursorCount = stream->readUint16LE(); if ((uint32)stream->size() < (6 + cursorCount * 14)) return 0; WinCursorGroup *group = new WinCursorGroup(); group->cursors.reserve(cursorCount); for (uint32 i = 0; i < cursorCount; i++) { stream->readUint16LE(); // width stream->readUint16LE(); // height // Plane count if (stream->readUint16LE() != 1) { warning("PlaneCount is not 1."); } stream->readUint16LE(); // bits per pixel stream->readUint32LE(); // data size uint32 cursorId = stream->readUint16LE(); Common::ScopedPtr cursorStream(exe->getResource(Common::kWinCursor, cursorId)); if (!cursorStream) { delete group; return 0; } WinCursor *cursor = new WinCursor(); if (!cursor->readFromStream(*cursorStream)) { delete cursor; delete group; return 0; } CursorItem item; item.id = cursorId; item.cursor = cursor; group->cursors.push_back(item); } return group; } /** * The default Windows cursor */ class DefaultWinCursor : public Cursor { public: DefaultWinCursor() {} ~DefaultWinCursor() {} uint16 getWidth() const { return 12; } uint16 getHeight() const { return 20; } uint16 getHotspotX() const { return 0; } uint16 getHotspotY() const { return 0; } byte getKeyColor() const { return 0; } const byte *getSurface() const { static const byte defaultCursor[] = { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 1, 0, 0, 0, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 0, 0, 0, 1, 2, 1, 0, 1, 1, 2, 2, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 }; return defaultCursor; } const byte *getPalette() const { static const byte bwPalette[] = { 0x00, 0x00, 0x00, // Black 0xFF, 0xFF, 0xFF // White }; return bwPalette; } byte getPaletteStartIndex() const { return 1; } uint16 getPaletteCount() const { return 2; } }; Cursor *makeDefaultWinCursor() { return new DefaultWinCursor(); } } // End of namespace Graphics