1 /* GemRB - Infinity Engine Emulator 2 * Copyright (C) 2003 The GemRB Project 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 * 18 * 19 */ 20 21 /** 22 * @file Font.h 23 * Declares Font, class for manipulating images serving as fonts 24 * 25 * Font uses an "Atlas" instead of individual sprites for characters. 26 * The TTF plugin will dynamically add to its atlas as characters are needed 27 * to avoid building a ton of worthless pages. Additionally, fonts (especially 28 * BAM based ones) use "alias" entries to avoid making duplicate glyphs. 29 * @author The GemRB Project 30 */ 31 32 #ifndef FONT_H 33 #define FONT_H 34 35 #include "globals.h" 36 #include "exports.h" 37 38 #include "SpriteSheet.h" 39 40 #include <deque> 41 #include <map> 42 43 namespace GemRB { 44 45 enum FontStyle { 46 NORMAL = 0x00, 47 BOLD = 0x01, 48 ITALIC = 0x02, 49 UNDERLINE = 0x04 50 }; 51 52 class Palette; 53 using PaletteHolder = Holder<Palette>; 54 55 #define IE_FONT_ALIGN_LEFT 0x00 56 #define IE_FONT_ALIGN_CENTER 0x01 57 #define IE_FONT_ALIGN_RIGHT 0x02 58 #define IE_FONT_ALIGN_BOTTOM 0x04 59 #define IE_FONT_ALIGN_TOP 0x10 60 #define IE_FONT_ALIGN_MIDDLE 0x20 61 // optimization to make printing faster when text should be on a single line 62 #define IE_FONT_SINGLE_LINE 0x40 63 // work around to use the region passed to Font::Print as if it were the actual print size when the text cant actually fit the region 64 #define IE_FONT_NO_CALC 0x80 65 66 struct Glyph { 67 const Size size; 68 const Point pos; 69 70 const ieWord pitch; 71 const ieByte* pixels; 72 GlyphGlyph73 Glyph(const Size& size, Point pos, ieByte* pixels, ieWord pitch) 74 : size(size), pos(pos), pitch(pitch), pixels(pixels) {}; 75 }; 76 77 /** 78 * @class Font 79 * Class for using and manipulating images serving as fonts 80 */ 81 82 class GEM_EXPORT Font { 83 /* 84 we cannot assume direct pixel access to a Sprite2D (opengl, BAM, etc), but we need raw pixels for rendering text 85 However, some clients still want to blit text directly to the screen, in which case we *must* use a Sprite2D. 86 Using a single sprite for each glyph is inefficient for OpenGL so we want large sprites composed of many glyphs. 87 However, we would also like to avoid creating many unused pages (for TTF fonts, etc) so we cannot simply create a single large page. 88 Furthermore, a single page may grow too large to be used as a texture in OpenGL (esp for mobile devices). 89 Therefore we generate pages of glyphs (1024 x LineHeight + descent) as sprites suitable for segmented blitting to the screen. 90 The pixel data will be safely shared between the Sprite2D objects and the Glyph objects when the video driver is using software rendering 91 so this isn't horribly wasteful useage of memory. Hardware acceleration will force the font to keep its own copy of pixel data. 92 93 One odd quirk in this implementation is that, since TTF fonts cannot be pregenerated (efficiently), we must defer creation of a page sprite 94 until either the page is full, or until a call to a print method is made. 95 To avoid generating more than one partial page, subsequent calls to add new glyphs will destroy the partial Sprite (not the source pixels of course) 96 */ 97 public: 98 struct PrintColors { 99 Color fg; 100 Color bg; 101 }; 102 103 struct StringSizeMetrics { 104 // specifiy behavior of StringSize based on values of struct 105 // StringSize will modify the struct with the results 106 Size size; // maximum size allowed; updated with actual size <= initial value 107 size_t numChars; // maximum characters to size (0 implies no limit); updated with the count of characters that fit within size <= initial value 108 uint32_t numLines; // maximum number of lines to allow (use 0 for unlimited); updated with the actual number of lines that were used 109 bool forceBreak;// whether or not a break can occur without whitespace; updated to false if initially true and no force break occured 110 }; 111 112 private: 113 class GlyphAtlasPage : public SpriteSheet<ieWord> { 114 private: 115 using GlyphMap = std::map<ieWord, Glyph>; 116 GlyphMap glyphs; 117 ieByte* pageData; // current raw page being built 118 int pageXPos; // current position on building page 119 Font* font; 120 Holder<Sprite2D> invertedSheet; 121 public: 122 GlyphAtlasPage(Size pageSize, Font* font); 123 ~GlyphAtlasPage()124 ~GlyphAtlasPage() override { 125 if (Sheet == NULL) { 126 free(pageData); 127 } 128 } 129 bool AddGlyph(ieWord chr, const Glyph& g); 130 const Glyph& GlyphForChr(ieWord chr) const; 131 132 // we need a non-const version of Draw here that will call the base const version 133 using SpriteSheet<ieWord>::Draw; 134 void Draw(ieWord chr, const Region& dest, const PrintColors* colors); 135 void DumpToScreen(const Region&); 136 }; 137 138 struct GlyphIndexEntry { 139 ieWord chr; 140 ieWord pageIdx; 141 const Glyph* glyph; 142 GlyphIndexEntryGlyphIndexEntry143 GlyphIndexEntry() : chr(0), pageIdx(-1), glyph(NULL) {} GlyphIndexEntryGlyphIndexEntry144 GlyphIndexEntry(ieWord c, ieWord p, const Glyph* g) : chr(c), pageIdx(p), glyph(g) {} 145 }; 146 147 using GlyphIndex = std::vector<GlyphIndexEntry>; 148 // TODO: Unfortunately, we have no smart pointer suitable for an STL container... 149 // if we ever transition to C++11 we can use one here 150 using GlyphAtlas = std::deque<GlyphAtlasPage*>; 151 152 GlyphAtlasPage* CurrentAtlasPage; 153 GlyphIndex AtlasIndex; 154 GlyphAtlas Atlas; 155 156 protected: 157 PaletteHolder palette; 158 bool background = false; 159 160 public: 161 const int LineHeight; 162 const int Baseline; 163 164 private: 165 void CreateGlyphIndex(ieWord chr, ieWord pageIdx, const Glyph*); 166 // Blit to the sprite or screen if canvas is NULL 167 size_t RenderText(const String&, Region&, ieByte alignment, const PrintColors*, 168 Point* = NULL, ieByte** canvas = NULL, bool grow = false) const; 169 // render a single line of text. called by RenderText() 170 size_t RenderLine(const String& string, const Region& rgn, 171 Point& dp, const PrintColors*, ieByte** canvas = NULL) const; 172 173 size_t Print(Region rgn, const String& string, ieByte Alignment, const PrintColors* colors, Point* point = nullptr) const; 174 175 public: 176 Font(PaletteHolder, ieWord lineheight, ieWord baseline, bool background); 177 virtual ~Font(); 178 179 const Glyph& CreateGlyphForCharSprite(ieWord chr, Holder<Sprite2D>); 180 // BAM fonts use alisases a lot so this saves quite a bit of space 181 // Aliases are 2 glyphs that share identical frames such as 'ā' and 'a' 182 void CreateAliasForChar(ieWord chr, ieWord alias); 183 184 //allow reading but not setting glyphs 185 virtual const Glyph& GetGlyph(ieWord chr) const; 186 GetKerningOffset(ieWord,ieWord)187 virtual int GetKerningOffset(ieWord /*leftChr*/, ieWord /*rightChr*/) const {return 0;}; 188 189 Holder<Sprite2D> RenderTextAsSprite(const String& string, const Size& size, ieByte alignment, 190 size_t* numPrinted = NULL, Point* = NULL) const; 191 192 // return the number of glyphs printed 193 // the "point" parameter can be passed with a start point for rendering 194 // it will be filled with the point inside 'rgn' where the string ends upon return 195 196 size_t Print(const Region& rgn, const String& string, ieByte Alignment, Point* point = nullptr) const; 197 size_t Print(const Region& rgn, const String& string, ieByte Alignment, const PrintColors& colors, Point* point = nullptr) const; 198 199 size_t Print(Region rgn, const String& string, 200 PaletteHolder hicolor, ieByte Alignment, Point* point = nullptr) const; 201 202 /** Returns size of the string rendered in this font in pixels */ 203 Size StringSize(const String&, StringSizeMetrics* metrics = NULL) const; 204 205 // like StringSize, but single line and doens't take whitespace into consideration 206 size_t StringSizeWidth(const String&, size_t width, size_t* numChars = NULL) const; 207 }; 208 209 } 210 211 #endif 212