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

ttf.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM 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 // Since FreeType2 includes files, which contain forbidden symbols, we need to
00024 // allow all symbols here.
00025 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00026 
00027 #include "common/scummsys.h"
00028 #ifdef USE_FREETYPE2
00029 
00030 #include "graphics/fonts/ttf.h"
00031 #include "graphics/font.h"
00032 #include "graphics/surface.h"
00033 
00034 #include "common/encoding.h"
00035 #include "common/file.h"
00036 #include "common/config-manager.h"
00037 #include "common/singleton.h"
00038 #include "common/stream.h"
00039 #include "common/memstream.h"
00040 #include "common/hashmap.h"
00041 #include "common/ptr.h"
00042 #include "common/unzip.h"
00043 
00044 #include <ft2build.h>
00045 #include FT_FREETYPE_H
00046 #include FT_BITMAP_H
00047 #include FT_GLYPH_H
00048 #include FT_SFNT_NAMES_H
00049 #include FT_TRUETYPE_IDS_H
00050 #include FT_TRUETYPE_TABLES_H
00051 #include FT_TRUETYPE_TAGS_H
00052 
00053 #if (FREETYPE_MAJOR > 2 ||                                                          \
00054         (FREETYPE_MAJOR == 2 && (FREETYPE_MINOR > 3 ||                              \
00055                                  (FREETYPE_MINOR == 3 && FREETYPE_PATCH >= 8))))
00056 // FT2.3.8+, nothing to do, FT_GlyphSlot_Own_Bitmap is in FT_BITMAP_H
00057 #define FAKE_BOLD 2
00058 #elif (FREETYPE_MAJOR > 2 ||                                                        \
00059         (FREETYPE_MAJOR == 2 && (FREETYPE_MINOR > 2 ||                              \
00060                                  (FREETYPE_MINOR == 2 && FREETYPE_PATCH >= 0))))
00061 // FT2.2.0+ have FT_GlyphSlot_Own_Bitmap in FT_SYNTHESIS_H
00062 #include FT_SYNTHESIS_H
00063 #define FAKE_BOLD 2
00064 #elif (FREETYPE_MAJOR > 2 ||                                                        \
00065         (FREETYPE_MAJOR == 2 && (FREETYPE_MINOR > 1 ||                              \
00066                                  (FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 10))))
00067 // FT2.1.10+ don't have FT_GlyphSlot_Own_Bitmap but they have FT_Bitmap_Embolden, do workaround
00068 #define FAKE_BOLD 1
00069 #else
00070 // Older versions don't have FT_Bitmap_Embolden
00071 #define FAKE_BOLD 0
00072 #endif
00073 
00074 // ResidualVM specific
00075 #if FREETYPE_MAJOR > 2 || ( FREETYPE_MAJOR == 2 &&  FREETYPE_MINOR >= 9)
00076 #include FT_TRUETYPE_DRIVER_H
00077 #endif
00078 
00079 namespace Graphics {
00080 
00081 namespace {
00082 
00083 inline int ftCeil26_6(FT_Pos x) {
00084     return (x + 63) / 64;
00085 }
00086 
00087 inline int divRoundToNearest(int dividend, int divisor) {
00088     return (dividend + (divisor / 2)) / divisor;
00089 }
00090 
00091 } // End of anonymous namespace
00092 
00093 class TTFLibrary : public Common::Singleton<TTFLibrary> {
00094 public:
00095     TTFLibrary();
00096     ~TTFLibrary();
00097 
00101     bool isInitialized() const { return _initialized; }
00102 
00103     bool loadFont(const uint8 *file, const int32 face_index, const uint32 size, FT_Face &face);
00104     void closeFont(FT_Face &face);
00105 private:
00106     FT_Library _library;
00107     bool _initialized;
00108 };
00109 
00110 void shutdownTTF() {
00111     TTFLibrary::destroy();
00112 }
00113 
00114 #define g_ttf ::Graphics::TTFLibrary::instance()
00115 
00116 TTFLibrary::TTFLibrary() : _library(), _initialized(false) {
00117     if (!FT_Init_FreeType(&_library))
00118         _initialized = true;
00119 }
00120 
00121 TTFLibrary::~TTFLibrary() {
00122     if (_initialized) {
00123         FT_Done_FreeType(_library);
00124         _initialized = false;
00125     }
00126 }
00127 
00128 bool TTFLibrary::loadFont(const uint8 *file, const int32 face_index, const uint32 size, FT_Face &face) {
00129     assert(_initialized);
00130 
00131     return (FT_New_Memory_Face(_library, file, size, face_index, &face) == 0);
00132 }
00133 
00134 void TTFLibrary::closeFont(FT_Face &face) {
00135     assert(_initialized);
00136 
00137     FT_Done_Face(face);
00138 }
00139 
00140 class TTFFont : public Font {
00141 public:
00142     TTFFont();
00143     virtual ~TTFFont();
00144 
00145 // ResidualVM specific argument: stemDarkening
00146     bool load(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode,
00147               uint dpi, TTFRenderMode renderMode, const uint32 *mapping, bool stemDarkening);
00148 // ResidualVM specific argument: stemDarkening
00149     bool load(uint8 *ttfFile, uint32 sizeFile, int32 faceIndex, bool fakeBold, bool fakeItalic,
00150               int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping, bool stemDarkening);
00151 
00152     virtual int getFontHeight() const;
00153 
00154     virtual int getMaxCharWidth() const;
00155 
00156     virtual int getCharWidth(uint32 chr) const;
00157 
00158     virtual int getKerningOffset(uint32 left, uint32 right) const;
00159 
00160     virtual Common::Rect getBoundingBox(uint32 chr) const;
00161 
00162     virtual void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const;
00163 private:
00164     bool _initialized;
00165     FT_Face _face;
00166 
00167     uint8 *_ttfFile;
00168     uint32 _size;
00169 
00170     int _width, _height;
00171     int _ascent, _descent;
00172 
00173     struct Glyph {
00174         Surface image;
00175         int xOffset, yOffset;
00176         int advance;
00177         FT_UInt slot;
00178     };
00179 
00180     bool cacheGlyph(Glyph &glyph, uint32 chr) const;
00181     typedef Common::HashMap<uint32, Glyph> GlyphCache;
00182     mutable GlyphCache _glyphs;
00183     bool _allowLateCaching;
00184     void assureCached(uint32 chr) const;
00185 
00186     Common::SeekableReadStream *readTTFTable(FT_ULong tag) const;
00187 
00188     int computePointSize(int size, TTFSizeMode sizeMode) const;
00189     int readPointSizeFromVDMXTable(int height) const;
00190     int computePointSizeFromHeaders(int height) const;
00191 
00192     FT_Int32 _loadFlags;
00193     FT_Render_Mode _renderMode;
00194     bool _hasKerning;
00195 
00196     bool _fakeBold;
00197     bool _fakeItalic;
00198 };
00199 
00200 TTFFont::TTFFont()
00201     : _initialized(false), _face(), _ttfFile(0), _size(0), _width(0), _height(0), _ascent(0),
00202       _descent(0), _glyphs(), _loadFlags(FT_LOAD_TARGET_NORMAL), _renderMode(FT_RENDER_MODE_NORMAL),
00203       _hasKerning(false), _allowLateCaching(false), _fakeBold(false), _fakeItalic(false) {
00204 }
00205 
00206 TTFFont::~TTFFont() {
00207     if (_initialized) {
00208         g_ttf.closeFont(_face);
00209 
00210         delete[] _ttfFile;
00211         _ttfFile = 0;
00212 
00213         for (GlyphCache::iterator i = _glyphs.begin(), end = _glyphs.end(); i != end; ++i)
00214             i->_value.image.free();
00215 
00216         _initialized = false;
00217     }
00218 }
00219 
00220 // ResidualVM specific argument: stemDarkening
00221 bool TTFFont::load(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode,
00222                    uint dpi, TTFRenderMode renderMode, const uint32 *mapping, bool stemDarkening) {
00223     if (!g_ttf.isInitialized())
00224         return false;
00225 
00226     uint32 sizeFile = stream.size();
00227     if (!sizeFile)
00228         return false;
00229 
00230     uint8 *ttfFile = new uint8[sizeFile];
00231     assert(ttfFile);
00232 
00233     if (stream.read(ttfFile, sizeFile) != sizeFile) {
00234         delete[] ttfFile;
00235         return false;
00236     }
00237 
00238 // ResidualVM specific argument: stemDarkening
00239     if (!load(ttfFile, sizeFile, 0, false, false, size, sizeMode, dpi, renderMode, mapping, stemDarkening)) {
00240         delete[] ttfFile;
00241         return false;
00242     }
00243 
00244     // Don't delete ttfFile as it's now owned by the class
00245     return true;
00246 }
00247 
00248 // ResidualVM specific argument: stemDarkening
00249 bool TTFFont::load(uint8 *ttfFile, uint32 sizeFile, int32 faceIndex, bool bold, bool italic,
00250                    int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping, bool stemDarkening) {
00251     _initialized = false;
00252 
00253     if (!g_ttf.isInitialized())
00254         return false;
00255 
00256     _size = sizeFile;
00257     if (!_size)
00258         return false;
00259 
00260     _ttfFile = ttfFile;
00261     assert(_ttfFile);
00262 
00263     if (!g_ttf.loadFont(_ttfFile, faceIndex, _size, _face)) {
00264         // Don't delete ttfFile as we return fail
00265         _ttfFile = 0;
00266 
00267         return false;
00268     }
00269 
00270     // We only support scalable fonts.
00271     if (!FT_IS_SCALABLE(_face)) {
00272         g_ttf.closeFont(_face);
00273 
00274         // Don't delete ttfFile as we return fail
00275         _ttfFile = 0;
00276 
00277         return false;
00278     }
00279 
00280     // RedisualVM specific
00281     if (stemDarkening) {
00282 #if FREETYPE_MAJOR > 2 || ( FREETYPE_MAJOR == 2 &&  FREETYPE_MINOR >= 9)
00283         FT_Parameter param;
00284         param.tag = FT_PARAM_TAG_STEM_DARKENING;
00285         param.data = &stemDarkening;
00286         FT_Face_Properties(_face, 1, &param);
00287 #else
00288         warning("Stem darkening is not available with this version of FreeType");
00289 #endif
00290     }
00291 
00292     // Check whether we have kerning support
00293     _hasKerning = (FT_HAS_KERNING(_face) != 0);
00294 
00295     if (FT_Set_Char_Size(_face, 0, computePointSize(size, sizeMode) * 64, dpi, dpi)) {
00296         g_ttf.closeFont(_face);
00297 
00298         // Don't delete ttfFile as we return fail
00299         _ttfFile = 0;
00300 
00301         return false;
00302     }
00303 
00304     bool fontBold = ((_face->style_flags & FT_STYLE_FLAG_BOLD) != 0);
00305     _fakeBold = bold && !fontBold;
00306     bool fontItalic = ((_face->style_flags & FT_STYLE_FLAG_ITALIC) != 0);
00307     _fakeItalic = italic && !fontItalic;
00308 
00309     switch (renderMode) {
00310     case kTTFRenderModeNormal:
00311         _loadFlags = FT_LOAD_TARGET_NORMAL;
00312         _renderMode = FT_RENDER_MODE_NORMAL;
00313         break;
00314 
00315     case kTTFRenderModeLight:
00316         _loadFlags = FT_LOAD_TARGET_LIGHT;
00317         _renderMode = FT_RENDER_MODE_LIGHT;
00318         break;
00319 
00320     case kTTFRenderModeMonochrome:
00321         _loadFlags = FT_LOAD_TARGET_MONO;
00322         _renderMode = FT_RENDER_MODE_MONO;
00323         break;
00324 
00325     default:
00326         break;
00327     }
00328 
00329     FT_Fixed yScale = _face->size->metrics.y_scale;
00330     _ascent = ftCeil26_6(FT_MulFix(_face->ascender, yScale));
00331     _descent = ftCeil26_6(FT_MulFix(_face->descender, yScale));
00332 
00333     _width = ftCeil26_6(FT_MulFix(_face->max_advance_width, _face->size->metrics.x_scale));
00334     _height = _ascent - _descent + 1;
00335 
00336 #if FAKE_BOLD > 0
00337     // Width isn't modified when we can't fake bold
00338     if (_fakeBold) {
00339         // Embolden by 1 pixel width
00340         _width += 1;
00341     }
00342 #endif
00343 
00344     // Apply a matrix transform for all loaded glyphs
00345     if (_fakeItalic) {
00346         // This matrix is taken from Wine source code
00347         // 16.16 fixed-point
00348         FT_Matrix slantMat;
00349         slantMat.xx = (1 << 16);      // 1.
00350         slantMat.xy = (1 << 16) >> 2; // .25
00351         slantMat.yx = 0;              // 0.
00352         slantMat.yy = (1 << 16);      // 1.
00353         FT_Set_Transform(_face, &slantMat, nullptr);
00354 
00355         // Don't try to load bitmap version of font as we have to transform the strokes
00356         _loadFlags |= FT_LOAD_NO_BITMAP;
00357     }
00358 
00359     if (!mapping) {
00360         // Allow loading of all unicode characters.
00361         _allowLateCaching = true;
00362 
00363         // Load all ISO-8859-1 characters.
00364         for (uint i = 0; i < 256; ++i) {
00365             if (!cacheGlyph(_glyphs[i], i)) {
00366                 _glyphs.erase(i);
00367             }
00368         }
00369     } else {
00370         // We have a fixed map of characters do not load more later.
00371         _allowLateCaching = false;
00372 
00373         for (uint i = 0; i < 256; ++i) {
00374             const uint32 unicode = mapping[i] & 0x7FFFFFFF;
00375             const bool isRequired = (mapping[i] & 0x80000000) != 0;
00376             // Check whether loading an important glyph fails and error out if
00377             // that is the case.
00378             if (!cacheGlyph(_glyphs[i], unicode)) {
00379                 _glyphs.erase(i);
00380                 if (isRequired) {
00381                     g_ttf.closeFont(_face);
00382 
00383                     // Don't delete ttfFile as we return fail
00384                     _ttfFile = 0;
00385 
00386                     return false;
00387                 }
00388             }
00389         }
00390     }
00391 
00392     if (_glyphs.size() == 0) {
00393         g_ttf.closeFont(_face);
00394 
00395         // Don't delete ttfFile as we return fail
00396         _ttfFile = 0;
00397 
00398         return false;
00399     } else {
00400         _initialized = true;
00401         // At this point we get ownership of _ttfFile
00402         return true;
00403     }
00404 }
00405 
00406 int TTFFont::computePointSize(int size, TTFSizeMode sizeMode) const {
00407     int ptSize = 0;
00408     switch (sizeMode) {
00409     case kTTFSizeModeCell: {
00410         ptSize = readPointSizeFromVDMXTable(size);
00411 
00412         if (ptSize == 0) {
00413             ptSize = computePointSizeFromHeaders(size);
00414         }
00415 
00416         if (ptSize == 0) {
00417             warning("Unable to compute point size for font '%s'", _face->family_name);
00418             ptSize = 1;
00419         }
00420         break;
00421     }
00422     case kTTFSizeModeCharacter:
00423         ptSize = size;
00424         break;
00425     default:
00426         break;
00427     }
00428 
00429     return ptSize;
00430 }
00431 
00432 Common::SeekableReadStream *TTFFont::readTTFTable(FT_ULong tag) const {
00433     // Find the required buffer size by calling the load function with nullptr
00434     FT_ULong size = 0;
00435     FT_Error err = FT_Load_Sfnt_Table(_face, tag, 0, nullptr, &size);
00436     if (err) {
00437         return nullptr;
00438     }
00439 
00440     byte *buf = (byte *)malloc(size);
00441     if (!buf) {
00442         return nullptr;
00443     }
00444 
00445     err = FT_Load_Sfnt_Table(_face, tag, 0, buf, &size);
00446     if (err) {
00447         free(buf);
00448         return nullptr;
00449     }
00450 
00451     return new Common::MemoryReadStream(buf, size, DisposeAfterUse::YES);
00452 }
00453 
00454 int TTFFont::readPointSizeFromVDMXTable(int height) const {
00455     // The Vertical Device Metrics table matches font heights with point sizes.
00456     // FreeType does not expose it, we have to parse it ourselves.
00457     // See https://www.microsoft.com/typography/otspec/vdmx.htm
00458 
00459     Common::ScopedPtr<Common::SeekableReadStream> vdmxBuf(readTTFTable(TTAG_VDMX));
00460     if (!vdmxBuf) {
00461         return 0;
00462     }
00463 
00464     // Read the main header
00465     vdmxBuf->skip(4); // Skip the version
00466     uint16 numRatios = vdmxBuf->readUint16BE();
00467 
00468     // Compute the starting position for the group table positions table
00469     int32 offsetTableStart = vdmxBuf->pos() + 4 * numRatios;
00470 
00471     // Search the ratio table for the 1:1 ratio, or the default record (0, 0, 0)
00472     int32 selectedRatio = -1;
00473     for (uint16 i = 0; i < numRatios; i++) {
00474         vdmxBuf->skip(1); // Skip the charset subset
00475         uint8 xRatio = vdmxBuf->readByte();
00476         uint8 yRatio1 = vdmxBuf->readByte();
00477         uint8 yRatio2 = vdmxBuf->readByte();
00478 
00479         if ((xRatio == 1 && yRatio1 <= 1 && yRatio2 >= 1)
00480             || (xRatio == 0 && yRatio1 == 0 && yRatio2 == 0)) {
00481             selectedRatio = i;
00482             break;
00483         }
00484     }
00485     if (selectedRatio < 0) {
00486         return 0;
00487     }
00488 
00489     // Read from group table positions table to get the group table offset
00490     vdmxBuf->seek(offsetTableStart + sizeof(uint16) * selectedRatio);
00491     uint16 groupOffset = vdmxBuf->readUint16BE();
00492 
00493     // Read the group table header
00494     vdmxBuf->seek(groupOffset);
00495     uint16 numRecords = vdmxBuf->readUint16BE();
00496     vdmxBuf->skip(2); // Skip the table bounds
00497 
00498     // Search a record matching the required height
00499     for (uint16 i = 0; i < numRecords; i++) {
00500         uint16 pointSize = vdmxBuf->readUint16BE();
00501         int16 yMax = vdmxBuf->readSint16BE();
00502         int16 yMin = vdmxBuf->readSint16BE();
00503 
00504         if (yMax + -yMin > height) {
00505             return 0;
00506         }
00507         if (yMax + -yMin == height) {
00508             return pointSize;
00509         }
00510     }
00511 
00512     return 0;
00513 }
00514 
00515 int TTFFont::computePointSizeFromHeaders(int height) const {
00516     TT_OS2 *os2Header = (TT_OS2 *)FT_Get_Sfnt_Table(_face, ft_sfnt_os2);
00517     TT_HoriHeader *horiHeader = (TT_HoriHeader *)FT_Get_Sfnt_Table(_face, ft_sfnt_hhea);
00518 
00519     if (os2Header && (os2Header->usWinAscent + os2Header->usWinDescent != 0)) {
00520         return divRoundToNearest(_face->units_per_EM * height, os2Header->usWinAscent + os2Header->usWinDescent);
00521     } else if (horiHeader && (horiHeader->Ascender + horiHeader->Descender != 0)) {
00522         return divRoundToNearest(_face->units_per_EM * height, horiHeader->Ascender + horiHeader->Descender);
00523     }
00524 
00525     return 0;
00526 }
00527 
00528 int TTFFont::getFontHeight() const {
00529     return _height;
00530 }
00531 
00532 int TTFFont::getMaxCharWidth() const {
00533     return _width;
00534 }
00535 
00536 int TTFFont::getCharWidth(uint32 chr) const {
00537     assureCached(chr);
00538     GlyphCache::const_iterator glyphEntry = _glyphs.find(chr);
00539     if (glyphEntry == _glyphs.end())
00540         return 0;
00541     else
00542         return glyphEntry->_value.advance;
00543 }
00544 
00545 int TTFFont::getKerningOffset(uint32 left, uint32 right) const {
00546     if (!_hasKerning)
00547         return 0;
00548 
00549     assureCached(left);
00550     assureCached(right);
00551 
00552     FT_UInt leftGlyph, rightGlyph;
00553     GlyphCache::const_iterator glyphEntry;
00554 
00555     glyphEntry = _glyphs.find(left);
00556     if (glyphEntry != _glyphs.end()) {
00557         leftGlyph = glyphEntry->_value.slot;
00558     } else {
00559         return 0;
00560     }
00561 
00562     glyphEntry = _glyphs.find(right);
00563     if (glyphEntry != _glyphs.end()) {
00564         rightGlyph = glyphEntry->_value.slot;
00565     } else {
00566         return 0;
00567     }
00568 
00569     if (!leftGlyph || !rightGlyph)
00570         return 0;
00571 
00572     FT_Vector kerningVector;
00573     FT_Get_Kerning(_face, leftGlyph, rightGlyph, FT_KERNING_DEFAULT, &kerningVector);
00574     return (kerningVector.x / 64);
00575 }
00576 
00577 Common::Rect TTFFont::getBoundingBox(uint32 chr) const {
00578     assureCached(chr);
00579     GlyphCache::const_iterator glyphEntry = _glyphs.find(chr);
00580     if (glyphEntry == _glyphs.end()) {
00581         return Common::Rect();
00582     } else {
00583         const int xOffset = glyphEntry->_value.xOffset;
00584         const int yOffset = glyphEntry->_value.yOffset;
00585         const Graphics::Surface &image = glyphEntry->_value.image;
00586         return Common::Rect(xOffset, yOffset, xOffset + image.w, yOffset + image.h);
00587     }
00588 }
00589 
00590 namespace {
00591 
00592 template<typename ColorType>
00593 static void renderGlyph(uint8 *dstPos, const int dstPitch, const uint8 *srcPos, const int srcPitch, const int w, const int h, ColorType color, const PixelFormat &dstFormat) {
00594     uint8 sR, sG, sB;
00595     dstFormat.colorToRGB(color, sR, sG, sB);
00596 
00597     for (int y = 0; y < h; ++y) {
00598         ColorType *rDst = (ColorType *)dstPos;
00599         const uint8 *src = srcPos;
00600 
00601         for (int x = 0; x < w; ++x) {
00602             if (*src == 255) {
00603                 *rDst = color;
00604             } else if (*src) {
00605                 const uint8 a = *src;
00606 
00607                 uint8 dR, dG, dB;
00608                 dstFormat.colorToRGB(*rDst, dR, dG, dB);
00609 
00610                 dR = ((255 - a) * dR + a * sR) / 255;
00611                 dG = ((255 - a) * dG + a * sG) / 255;
00612                 dB = ((255 - a) * dB + a * sB) / 255;
00613 
00614                 *rDst = dstFormat.RGBToColor(dR, dG, dB);
00615             }
00616 
00617             ++rDst;
00618             ++src;
00619         }
00620 
00621         dstPos += dstPitch;
00622         srcPos += srcPitch;
00623     }
00624 }
00625 
00626 } // End of anonymous namespace
00627 
00628 void TTFFont::drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const {
00629     assureCached(chr);
00630     GlyphCache::const_iterator glyphEntry = _glyphs.find(chr);
00631     if (glyphEntry == _glyphs.end())
00632         return;
00633 
00634     const Glyph &glyph = glyphEntry->_value;
00635 
00636     x += glyph.xOffset;
00637     y += glyph.yOffset;
00638 
00639     if (x > dst->w)
00640         return;
00641     if (y > dst->h)
00642         return;
00643 
00644     int w = glyph.image.w;
00645     int h = glyph.image.h;
00646 
00647     const uint8 *srcPos = (const uint8 *)glyph.image.getPixels();
00648 
00649     // Make sure we are not drawing outside the screen bounds
00650     if (x < 0) {
00651         srcPos -= x;
00652         w += x;
00653         x = 0;
00654     }
00655 
00656     if (x + w > dst->w)
00657         w = dst->w - x;
00658 
00659     if (w <= 0)
00660         return;
00661 
00662     if (y < 0) {
00663         srcPos -= y * glyph.image.pitch;
00664         h += y;
00665         y = 0;
00666     }
00667 
00668     if (y + h > dst->h)
00669         h = dst->h - y;
00670 
00671     if (h <= 0)
00672         return;
00673 
00674     uint8 *dstPos = (uint8 *)dst->getBasePtr(x, y);
00675 
00676     if (dst->format.bytesPerPixel == 1) {
00677         for (int cy = 0; cy < h; ++cy) {
00678             uint8 *rDst = dstPos;
00679             const uint8 *src = srcPos;
00680 
00681             for (int cx = 0; cx < w; ++cx) {
00682                 // We assume a 1Bpp mode is a color indexed mode, thus we can
00683                 // not take advantage of anti-aliasing here.
00684                 if (*src >= 0x80)
00685                     *rDst = color;
00686 
00687                 ++rDst;
00688                 ++src;
00689             }
00690 
00691             dstPos += dst->pitch;
00692             srcPos += glyph.image.pitch;
00693         }
00694     } else if (dst->format.bytesPerPixel == 2) {
00695         renderGlyph<uint16>(dstPos, dst->pitch, srcPos, glyph.image.pitch, w, h, color, dst->format);
00696     } else if (dst->format.bytesPerPixel == 4) {
00697         renderGlyph<uint32>(dstPos, dst->pitch, srcPos, glyph.image.pitch, w, h, color, dst->format);
00698     }
00699 }
00700 
00701 bool TTFFont::cacheGlyph(Glyph &glyph, uint32 chr) const {
00702     FT_UInt slot = FT_Get_Char_Index(_face, chr);
00703     if (!slot)
00704         return false;
00705 
00706     glyph.slot = slot;
00707 
00708     // We use the light target and render mode to improve the looks of the
00709     // glyphs. It is most noticable in FreeSansBold.ttf, where otherwise the
00710     // 't' glyph looks like it is cut off on the right side.
00711     if (FT_Load_Glyph(_face, slot, _loadFlags))
00712         return false;
00713 
00714     if (FT_Render_Glyph(_face->glyph, _renderMode))
00715         return false;
00716 
00717     if (_face->glyph->format != FT_GLYPH_FORMAT_BITMAP)
00718         return false;
00719 
00720     glyph.xOffset = _face->glyph->bitmap_left;
00721     glyph.yOffset = _ascent - _face->glyph->bitmap_top;
00722 
00723     glyph.advance = ftCeil26_6(_face->glyph->advance.x);
00724 
00725     const FT_Bitmap *bitmap;
00726 #if FAKE_BOLD == 1
00727     FT_Bitmap ownBitmap;
00728 #endif
00729 
00730     if (_fakeBold) {
00731 #if FAKE_BOLD >= 2
00732         // Embolden by 1 pixel in x and 0 in y
00733         glyph.advance += 1;
00734 
00735         if (FT_GlyphSlot_Own_Bitmap(_face->glyph))
00736             return false;
00737 
00738         // That's 26.6 fixed-point units
00739         if (FT_Bitmap_Embolden(_face->glyph->library, &_face->glyph->bitmap, 1 << 6, 0))
00740             return false;
00741         
00742         bitmap = &_face->glyph->bitmap;
00743 #elif FAKE_BOLD >= 1
00744         FT_Bitmap_New(&ownBitmap);
00745 
00746         if (FT_Bitmap_Copy(_face->glyph->library, &_face->glyph->bitmap, &ownBitmap))
00747             return false;
00748 
00749         // Embolden by 1 pixel in x and 0 in y
00750         glyph.advance += 1;
00751 
00752         // That's 26.6 fixed-point units
00753         if (FT_Bitmap_Embolden(_face->glyph->library, &ownBitmap, 1 << 6, 0))
00754             return false;
00755 
00756         bitmap = &ownBitmap;
00757 #else
00758         // Can't do anything, just don't fake bold
00759         bitmap = &_face->glyph->bitmap;
00760 #endif
00761     } else {
00762         bitmap = &_face->glyph->bitmap;
00763     }
00764 
00765 
00766     glyph.image.create(bitmap->width, bitmap->rows, PixelFormat::createFormatCLUT8());
00767 
00768     const uint8 *src = bitmap->buffer;
00769     int srcPitch = bitmap->pitch;
00770     if (srcPitch < 0) {
00771         src += (bitmap->rows - 1) * srcPitch;
00772         srcPitch = -srcPitch;
00773     }
00774 
00775     uint8 *dst = (uint8 *)glyph.image.getPixels();
00776     memset(dst, 0, glyph.image.h * glyph.image.pitch);
00777 
00778     switch (bitmap->pixel_mode) {
00779     case FT_PIXEL_MODE_MONO:
00780         for (int y = 0; y < (int)bitmap->rows; ++y) {
00781             const uint8 *curSrc = src;
00782             uint8 mask = 0;
00783 
00784             for (int x = 0; x < (int)bitmap->width; ++x) {
00785                 if ((x % 8) == 0)
00786                     mask = *curSrc++;
00787 
00788                 if (mask & 0x80)
00789                     *dst = 255;
00790 
00791                 mask <<= 1;
00792                 ++dst;
00793             }
00794 
00795             src += srcPitch;
00796         }
00797         break;
00798 
00799     case FT_PIXEL_MODE_GRAY:
00800         for (int y = 0; y < (int)bitmap->rows; ++y) {
00801             memcpy(dst, src, bitmap->width);
00802             dst += glyph.image.pitch;
00803             src += srcPitch;
00804         }
00805         break;
00806 
00807     default:
00808         warning("TTFFont::cacheGlyph: Unsupported pixel mode %d", bitmap->pixel_mode);
00809         glyph.image.free();
00810         return false;
00811     }
00812 
00813 #if FAKE_BOLD == 1
00814     if (_fakeBold) {
00815         FT_Bitmap_Done(_face->glyph->library, &ownBitmap);
00816     }
00817 #endif
00818 
00819     return true;
00820 }
00821 
00822 void TTFFont::assureCached(uint32 chr) const {
00823     if (!chr || !_allowLateCaching || _glyphs.contains(chr)) {
00824         return;
00825     }
00826 
00827     Glyph newGlyph;
00828     if (cacheGlyph(newGlyph, chr)) {
00829         _glyphs[chr] = newGlyph;
00830     }
00831 }
00832 
00833 // ResidualVM specific argument: stemDarkening
00834 Font *loadTTFFont(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping, bool stemDarkening) {
00835     TTFFont *font = new TTFFont();
00836 
00837 // ResidualVM specific argument: stemDarkening
00838     if (!font->load(stream, size, sizeMode, dpi, renderMode, mapping, stemDarkening)) {
00839         delete font;
00840         return 0;
00841     }
00842 
00843     return font;
00844 }
00845 
00846 Font *loadTTFFontFromArchive(const Common::String &filename, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
00847     Common::SeekableReadStream *archiveStream = nullptr;
00848     if (ConfMan.hasKey("extrapath")) {
00849         Common::FSDirectory extrapath(ConfMan.get("extrapath"));
00850         archiveStream = extrapath.createReadStreamForMember("fonts.dat");
00851     }
00852 
00853     if (!archiveStream) {
00854         archiveStream = SearchMan.createReadStreamForMember("fonts.dat");
00855     }
00856 
00857     Common::Archive *archive = Common::makeZipArchive(archiveStream);
00858     if (!archive) {
00859         return nullptr;
00860     }
00861 
00862     Common::File f;
00863     if (!f.open(filename, *archive)) {
00864         return nullptr;
00865     }
00866 
00867     Font *font = loadTTFFont(f, size, sizeMode, dpi, renderMode, mapping);
00868 
00869     delete archive;
00870     return font;
00871 }
00872 
00873 static bool matchFaceName(const Common::U32String &faceName, const FT_Face &face) {
00874     if (faceName == Common::U32String(face->family_name)) {
00875         // International name in ASCII match
00876         return true;
00877     }
00878 
00879     // Try to match with localized name
00880     // Loosely copied from freetype2-demos
00881     FT_SfntName aname;
00882     FT_UInt num_strings = FT_Get_Sfnt_Name_Count(face);
00883     for (FT_UInt j = 0; j < num_strings; j++) {
00884         if (FT_Get_Sfnt_Name(face, j, &aname)) {
00885             continue;
00886         }
00887         if (aname.name_id != TT_NAME_ID_FONT_FAMILY) {
00888             continue;
00889         }
00890 
00891         if (aname.platform_id == TT_PLATFORM_MICROSOFT &&
00892                 aname.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES) {
00893             if (aname.encoding_id == TT_MS_ID_SYMBOL_CS ||
00894                     aname.encoding_id == TT_MS_ID_UNICODE_CS) {
00895                 // MS local name in UTF-16
00896                 char *u32 = Common::Encoding::convert("utf-32", "utf-16be", (char *)aname.string, aname.string_len);
00897                 Common::U32String localName((uint32 *)u32);
00898                 free(u32);
00899 
00900                 if (faceName == localName) {
00901                     return true;
00902                 }
00903             } else {
00904                 // No conversion
00905                 if (faceName == Common::U32String((char *)aname.string, aname.string_len)) {
00906                     return true;
00907                 }
00908             }
00909         } else if (aname.platform_id == TT_PLATFORM_MACINTOSH &&
00910                    aname.language_id != TT_MAC_LANGID_ENGLISH) {
00911             // No conversion
00912             if (faceName == Common::U32String((char *)aname.string, aname.string_len)) {
00913                 return true;
00914             }
00915         }
00916     }
00917     return false;
00918 }
00919 
00920 Font *findTTFace(const Common::Array<Common::String> &files, const Common::U32String &faceName,
00921                  bool bold, bool italic, int size, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
00922     if (!g_ttf.isInitialized())
00923         return nullptr;
00924 
00925     uint8 *bestTTFFile = nullptr;
00926     uint32 bestSize = 0;
00927     uint32 bestFaceId = (uint32) -1;
00928     uint32 bestPenalty = (uint32) -1;
00929 
00930     for (Common::Array<Common::String>::const_iterator it = files.begin(); it != files.end(); it++) {
00931         Common::File ttf;
00932         if (!ttf.open(*it)) {
00933             continue;
00934         }
00935         uint32 sizeFile = ttf.size();
00936         if (!sizeFile) {
00937             continue;
00938         }
00939         uint8 *ttfFile = new uint8[sizeFile];
00940         assert(ttfFile);
00941 
00942         if (ttf.read(ttfFile, sizeFile) != sizeFile) {
00943             delete[] ttfFile;
00944             ttfFile = 0;
00945 
00946             continue;
00947         }
00948 
00949         ttf.close();
00950 
00951         FT_Face face;
00952 
00953         // Load face index -1 to get the count
00954         if (!g_ttf.loadFont(ttfFile, -1, sizeFile, face)) {
00955             delete[] ttfFile;
00956             ttfFile = 0;
00957 
00958             continue;
00959         }
00960 
00961         FT_Long num_faces = face->num_faces;
00962 
00963         g_ttf.closeFont(face);
00964 
00965         for (FT_Long i = 0; i < num_faces; i++) {
00966             if (!g_ttf.loadFont(ttfFile, i, sizeFile, face)) {
00967                 continue;
00968             }
00969 
00970             if (!matchFaceName(faceName, face)) {
00971                 // No match on names: we don't do like Windows, we don't take a random font
00972                 g_ttf.closeFont(face);
00973                 continue;
00974             }
00975 
00976             bool fontBold = ((face->style_flags & FT_STYLE_FLAG_BOLD) != 0);
00977             bool fontItalic = ((face->style_flags & FT_STYLE_FLAG_ITALIC) != 0);
00978 
00979             g_ttf.closeFont(face);
00980 
00981             // These scores are taken from Microsoft docs (table 1):
00982             // https://docs.microsoft.com/en-us/previous-versions/ms969909(v=msdn.10)
00983             uint32 penalty = 0;
00984             if (italic != fontItalic) {
00985                 penalty += 4;
00986             }
00987             if (bold != fontBold) {
00988                 penalty += 120;
00989             }
00990             if (penalty < bestPenalty) {
00991                 // Better font
00992                 // Cleanup old best font if it's not the same file as the current one
00993                 if (bestTTFFile != ttfFile) {
00994                     delete [] bestTTFFile;
00995                 }
00996 
00997                 bestPenalty = penalty;
00998                 bestTTFFile = ttfFile;
00999                 bestFaceId = i;
01000                 bestSize = sizeFile;
01001             }
01002         }
01003 
01004         // Don't free the file if it has been elected the best
01005         if (bestTTFFile != ttfFile) {
01006             delete [] ttfFile;
01007         }
01008         ttfFile = nullptr;
01009     }
01010 
01011     if (!bestTTFFile) {
01012         return nullptr;
01013     }
01014 
01015     TTFFont *font = new TTFFont();
01016 
01017     TTFSizeMode sizeMode = kTTFSizeModeCell;
01018     if (size < 0) {
01019         size = -size;
01020         sizeMode = kTTFSizeModeCharacter;
01021     }
01022     if (dpi == 0) {
01023         dpi = 96;
01024     }
01025 
01026     // ResidualVM last argument: false
01027     if (!font->load(bestTTFFile, bestSize, bestFaceId, bold, italic, size, sizeMode,
01028                     dpi, renderMode, mapping, false)) {
01029         delete font;
01030         delete [] bestTTFFile;
01031         return nullptr;
01032     }
01033 
01034     return font;
01035 }
01036 
01037 } // End of namespace Graphics
01038 
01039 namespace Common {
01040 DECLARE_SINGLETON(Graphics::TTFLibrary);
01041 } // End of namespace Common
01042 
01043 #endif
01044 


Generated on Sat May 30 2020 05:01:01 for ResidualVM by doxygen 1.7.1
curved edge   curved edge