/* 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. * */ #ifndef SCI_GRAPHICS_TEXT32_H #define SCI_GRAPHICS_TEXT32_H #include "sci/graphics/celobj32.h" #include "sci/graphics/frameout.h" namespace Sci { enum TextAlign { kTextAlignLeft = 0, kTextAlignCenter = 1, kTextAlignRight = 2 }; enum BitmapFlags { kBitmapRemap = 2 }; #define BITMAP_PROPERTY(size, property, offset)\ inline uint##size get##property() const {\ return READ_SCI11ENDIAN_UINT##size(_bitmap + (offset));\ }\ inline void set##property(uint##size value) {\ WRITE_SCI11ENDIAN_UINT##size(_bitmap + (offset), (value));\ } /** * A convenience class for creating and modifying in-memory * bitmaps. */ class BitmapResource { byte *_bitmap; reg_t _object; /** * Gets the size of the bitmap header for the current * engine version. */ static inline uint16 getBitmapHeaderSize() { // TODO: These values are accurate for each engine, but there may be no reason // to not simply just always use size 40, since SCI2.1mid does not seem to // actually store any data above byte 40, and SCI2 did not allow bitmaps with // scaling resolutions other than the default (320x200). Perhaps SCI3 used // the extra bytes, or there is some reason why they tried to align the header // size with other headers like pic headers? // uint32 bitmapHeaderSize; // if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { // bitmapHeaderSize = 46; // } else if (getSciVersion() == SCI_VERSION_2_1_EARLY) { // bitmapHeaderSize = 40; // } else { // bitmapHeaderSize = 36; // } // return bitmapHeaderSize; return 46; } /** * Gets the byte size of a bitmap with the given width * and height. */ static inline uint32 getBitmapSize(const uint16 width, const uint16 height) { return width * height + getBitmapHeaderSize(); } public: /** * Create a bitmap resource for an existing bitmap. * Ownership of the bitmap is retained by the caller. */ inline BitmapResource(reg_t bitmap) : _bitmap(g_sci->getEngineState()->_segMan->getHunkPointer(bitmap)), _object(bitmap) { if (_bitmap == nullptr || getUncompressedDataOffset() != getBitmapHeaderSize()) { error("Invalid Text bitmap %04x:%04x", PRINT_REG(bitmap)); } } /** * Allocates and initialises a new bitmap in the given * segment manager. */ inline BitmapResource(SegManager *segMan, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool remap) { _object = segMan->allocateHunkEntry("Bitmap()", getBitmapSize(width, height)); _bitmap = segMan->getHunkPointer(_object); const uint16 bitmapHeaderSize = getBitmapHeaderSize(); setWidth(width); setHeight(height); setDisplace(Common::Point(displaceX, displaceY)); setSkipColor(skipColor); _bitmap[9] = 0; WRITE_SCI11ENDIAN_UINT16(_bitmap + 10, 0); setRemap(remap); setDataSize(width * height); WRITE_SCI11ENDIAN_UINT32(_bitmap + 16, 0); setHunkPaletteOffset(hunkPaletteOffset); setDataOffset(bitmapHeaderSize); setUncompressedDataOffset(bitmapHeaderSize); setControlOffset(0); setScaledWidth(scaledWidth); setScaledHeight(scaledHeight); } reg_t getObject() const { return _object; } BITMAP_PROPERTY(16, Width, 0); BITMAP_PROPERTY(16, Height, 2); inline Common::Point getDisplace() const { return Common::Point( (int16)READ_SCI11ENDIAN_UINT16(_bitmap + 4), (int16)READ_SCI11ENDIAN_UINT16(_bitmap + 6) ); } inline void setDisplace(const Common::Point &displace) { WRITE_SCI11ENDIAN_UINT16(_bitmap + 4, (uint16)displace.x); WRITE_SCI11ENDIAN_UINT16(_bitmap + 6, (uint16)displace.y); } inline uint8 getSkipColor() const { return _bitmap[8]; } inline void setSkipColor(const uint8 skipColor) { _bitmap[8] = skipColor; } inline bool getRemap() const { return READ_SCI11ENDIAN_UINT16(_bitmap + 10) & kBitmapRemap; } inline void setRemap(const bool remap) { uint16 flags = READ_SCI11ENDIAN_UINT16(_bitmap + 10); if (remap) { flags |= kBitmapRemap; } else { flags &= ~kBitmapRemap; } WRITE_SCI11ENDIAN_UINT16(_bitmap + 10, flags); } BITMAP_PROPERTY(32, DataSize, 12); inline uint32 getHunkPaletteOffset() const { return READ_SCI11ENDIAN_UINT32(_bitmap + 20); } void setHunkPaletteOffset(uint32 hunkPaletteOffset) { if (hunkPaletteOffset) { hunkPaletteOffset += getBitmapHeaderSize(); } WRITE_SCI11ENDIAN_UINT32(_bitmap + 20, hunkPaletteOffset); } BITMAP_PROPERTY(32, DataOffset, 24); // NOTE: This property is used as a "magic number" for // validating that a block of memory is a valid bitmap, // and so is always set to the size of the header. BITMAP_PROPERTY(32, UncompressedDataOffset, 28); // NOTE: This property always seems to be zero BITMAP_PROPERTY(32, ControlOffset, 32); inline uint16 getScaledWidth() const { if (getDataOffset() >= 40) { return READ_SCI11ENDIAN_UINT16(_bitmap + 36); } // SCI2 bitmaps did not have scaling ability return 320; } inline void setScaledWidth(uint16 scaledWidth) { if (getDataOffset() >= 40) { WRITE_SCI11ENDIAN_UINT16(_bitmap + 36, scaledWidth); } } inline uint16 getScaledHeight() const { if (getDataOffset() >= 40) { return READ_SCI11ENDIAN_UINT16(_bitmap + 38); } // SCI2 bitmaps did not have scaling ability return 200; } inline void setScaledHeight(uint16 scaledHeight) { if (getDataOffset() >= 40) { WRITE_SCI11ENDIAN_UINT16(_bitmap + 38, scaledHeight); } } inline byte *getPixels() { return _bitmap + getUncompressedDataOffset(); } }; class GfxFont; /** * This class handles text calculation and rendering for * SCI32 games. The text calculation system in SCI32 is * nearly the same as SCI16, which means this class behaves * similarly. Notably, GfxText32 maintains drawing * parameters across multiple calls. */ class GfxText32 { private: SegManager *_segMan; GfxCache *_cache; /** * The resource ID of the default font used by the game. * * @todo Check all SCI32 games to learn what their * default font is. */ static int16 _defaultFontId; /** * The width and height of the currently active text * bitmap, in text-system coordinates. * * @note These are unsigned in the actual engine. */ int16 _width, _height; /** * The color used to draw text. */ uint8 _foreColor; /** * The background color of the text box. */ uint8 _backColor; /** * The transparent color of the text box. Used when * compositing the bitmap onto the screen. */ uint8 _skipColor; /** * The rect where the text is drawn within the bitmap. * This rect is clipped to the dimensions of the bitmap. */ Common::Rect _textRect; /** * The text being drawn to the currently active text * bitmap. */ Common::String _text; /** * The font being used to draw the text. */ GuiResourceId _fontId; /** * The color of the text box border. */ int16 _borderColor; /** * TODO: Document */ bool _dimmed; /** * The text alignment for the drawn text. */ TextAlign _alignment; /** * The position of the text draw cursor. */ Common::Point _drawPosition; void drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling); void drawChar(const char charIndex); void drawText(const uint index, uint length); /** * Gets the length of the longest run of text available * within the currently loaded text, starting from the * given `charIndex` and running for up to `maxWidth` * pixels. Returns the number of characters that can be * written, and mutates the value pointed to by * `charIndex` to point to the index of the next * character to render. */ uint getLongest(uint *charIndex, const int16 maxWidth); /** * Gets the pixel width of a substring of the currently * loaded text, without scaling. */ int16 getTextWidth(const uint index, uint length) const; inline Common::Rect scaleRect(const Common::Rect &rect) { Common::Rect scaledRect(rect); int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; Ratio scaleX(_scaledWidth, scriptWidth); Ratio scaleY(_scaledHeight, scriptHeight); mulinc(scaledRect, scaleX, scaleY); return scaledRect; } public: GfxText32(SegManager *segMan, GfxCache *fonts); /** * The memory handle of the currently active bitmap. */ reg_t _bitmap; /** * The size of the x-dimension of the coordinate system * used by the text renderer. */ int16 _scaledWidth; /** * The size of the y-dimension of the coordinate system * used by the text renderer. */ int16 _scaledHeight; /** * The currently active font resource used to write text * into the bitmap. * * @note SCI engine builds the font table directly * inside of FontMgr; we use GfxFont instead. */ GfxFont *_font; /** * Creates a plain font bitmap with a flat color * background. */ reg_t createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, TextAlign alignment, const int16 borderColor, bool dimmed, const bool doScaling); /** * Creates a font bitmap with a view background. */ reg_t createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed); inline int scaleUpWidth(int value) const { const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth; } inline int scaleUpHeight(int value) const { const int scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; return (value * scriptHeight + _scaledHeight - 1) / _scaledHeight; } /** * Draws the text to the bitmap. */ void drawTextBox(); /** * Draws the given text to the bitmap. * * @note The original engine holds a reference to a * shared string which lets the text be updated from * outside of the font manager. Instead, we give this * extra signature to send the text to draw. * * TODO: Use shared string instead? */ void drawTextBox(const Common::String &text); /** * Erases the given rect by filling with the background * color. */ void erase(const Common::Rect &rect, const bool doScaling); void invertRect(const reg_t bitmap, const int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling); /** * Sets the font to be used for rendering and * calculation of text dimensions. */ void setFont(const GuiResourceId fontId); /** * Gets the width of a character. */ uint16 getCharWidth(const char charIndex, const bool doScaling) const; /** * Retrieves the width and height of a block of text. */ Common::Rect getTextSize(const Common::String &text, const int16 maxWidth, bool doScaling); /** * Gets the pixel width of a substring of the currently * loaded text, with scaling. */ int16 getTextWidth(const Common::String &text, const uint index, const uint length); /** * Retrieves the width of a line of text. */ int16 getStringWidth(const Common::String &text); /** * Gets the number of characters of `text`, starting * from `index`, that can be safely rendered into * `textRect`. */ int16 getTextCount(const Common::String &text, const uint index, const Common::Rect &textRect, const bool doScaling); /** * Gets the number of characters of `text`, starting * from `index`, that can be safely rendered into * `textRect` using the given font. */ int16 getTextCount(const Common::String &text, const uint index, const GuiResourceId fontId, const Common::Rect &textRect, const bool doScaling); }; } // End of namespace Sci #endif