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/file.h"
00035 #include "common/singleton.h"
00036 #include "common/stream.h"
00037 #include "common/memstream.h"
00038 #include "common/hashmap.h"
00039 #include "common/ptr.h"
00040 #include "common/unzip.h"
00041 
00042 #include <ft2build.h>
00043 #include FT_FREETYPE_H
00044 #include FT_GLYPH_H
00045 #include FT_TRUETYPE_TABLES_H
00046 #include FT_TRUETYPE_TAGS_H
00047 
00048 namespace Graphics {
00049 
00050 namespace {
00051 
00052 inline int ftCeil26_6(FT_Pos x) {
00053     return (x + 63) / 64;
00054 }
00055 
00056 inline int divRoundToNearest(int dividend, int divisor) {
00057     return (dividend + (divisor / 2)) / divisor;
00058 }
00059 
00060 } // End of anonymous namespace
00061 
00062 class TTFLibrary : public Common::Singleton<TTFLibrary> {
00063 public:
00064     TTFLibrary();
00065     ~TTFLibrary();
00066 
00070     bool isInitialized() const { return _initialized; }
00071 
00072     bool loadFont(const uint8 *file, const uint32 size, FT_Face &face);
00073     void closeFont(FT_Face &face);
00074 private:
00075     FT_Library _library;
00076     bool _initialized;
00077 };
00078 
00079 void shutdownTTF() {
00080     TTFLibrary::destroy();
00081 }
00082 
00083 #define g_ttf ::Graphics::TTFLibrary::instance()
00084 
00085 TTFLibrary::TTFLibrary() : _library(), _initialized(false) {
00086     if (!FT_Init_FreeType(&_library))
00087         _initialized = true;
00088 }
00089 
00090 TTFLibrary::~TTFLibrary() {
00091     if (_initialized) {
00092         FT_Done_FreeType(_library);
00093         _initialized = false;
00094     }
00095 }
00096 
00097 bool TTFLibrary::loadFont(const uint8 *file, const uint32 size, FT_Face &face) {
00098     assert(_initialized);
00099 
00100     return (FT_New_Memory_Face(_library, file, size, 0, &face) == 0);
00101 }
00102 
00103 void TTFLibrary::closeFont(FT_Face &face) {
00104     assert(_initialized);
00105 
00106     FT_Done_Face(face);
00107 }
00108 
00109 class TTFFont : public Font {
00110 public:
00111     TTFFont();
00112     virtual ~TTFFont();
00113 
00114     bool load(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping);
00115 
00116     virtual int getFontHeight() const;
00117 
00118     virtual int getMaxCharWidth() const;
00119 
00120     virtual int getCharWidth(uint32 chr) const;
00121 
00122     virtual int getKerningOffset(uint32 left, uint32 right) const;
00123 
00124     virtual Common::Rect getBoundingBox(uint32 chr) const;
00125 
00126     virtual void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const;
00127 private:
00128     bool _initialized;
00129     FT_Face _face;
00130 
00131     uint8 *_ttfFile;
00132     uint32 _size;
00133 
00134     int _width, _height;
00135     int _ascent, _descent;
00136 
00137     struct Glyph {
00138         Surface image;
00139         int xOffset, yOffset;
00140         int advance;
00141         FT_UInt slot;
00142     };
00143 
00144     bool cacheGlyph(Glyph &glyph, uint32 chr) const;
00145     typedef Common::HashMap<uint32, Glyph> GlyphCache;
00146     mutable GlyphCache _glyphs;
00147     bool _allowLateCaching;
00148     void assureCached(uint32 chr) const;
00149 
00150     Common::SeekableReadStream *readTTFTable(FT_ULong tag) const;
00151 
00152     int computePointSize(int size, TTFSizeMode sizeMode) const;
00153     int readPointSizeFromVDMXTable(int height) const;
00154     int computePointSizeFromHeaders(int height) const;
00155 
00156     FT_Int32 _loadFlags;
00157     FT_Render_Mode _renderMode;
00158     bool _hasKerning;
00159 };
00160 
00161 TTFFont::TTFFont()
00162     : _initialized(false), _face(), _ttfFile(0), _size(0), _width(0), _height(0), _ascent(0),
00163       _descent(0), _glyphs(), _loadFlags(FT_LOAD_TARGET_NORMAL), _renderMode(FT_RENDER_MODE_NORMAL),
00164       _hasKerning(false), _allowLateCaching(false) {
00165 }
00166 
00167 TTFFont::~TTFFont() {
00168     if (_initialized) {
00169         g_ttf.closeFont(_face);
00170 
00171         delete[] _ttfFile;
00172         _ttfFile = 0;
00173 
00174         for (GlyphCache::iterator i = _glyphs.begin(), end = _glyphs.end(); i != end; ++i)
00175             i->_value.image.free();
00176 
00177         _initialized = false;
00178     }
00179 }
00180 
00181 bool TTFFont::load(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
00182     if (!g_ttf.isInitialized())
00183         return false;
00184 
00185     _size = stream.size();
00186     if (!_size)
00187         return false;
00188 
00189     _ttfFile = new uint8[_size];
00190     assert(_ttfFile);
00191 
00192     if (stream.read(_ttfFile, _size) != _size) {
00193         delete[] _ttfFile;
00194         _ttfFile = 0;
00195 
00196         return false;
00197     }
00198 
00199     if (!g_ttf.loadFont(_ttfFile, _size, _face)) {
00200         delete[] _ttfFile;
00201         _ttfFile = 0;
00202 
00203         return false;
00204     }
00205 
00206     // We only support scalable fonts.
00207     if (!FT_IS_SCALABLE(_face)) {
00208         delete[] _ttfFile;
00209         _ttfFile = 0;
00210 
00211         g_ttf.closeFont(_face);
00212 
00213         return false;
00214     }
00215 
00216     // Check whether we have kerning support
00217     _hasKerning = (FT_HAS_KERNING(_face) != 0);
00218 
00219     if (FT_Set_Char_Size(_face, 0, computePointSize(size, sizeMode) * 64, dpi, dpi)) {
00220         delete[] _ttfFile;
00221         _ttfFile = 0;
00222 
00223         return false;
00224     }
00225 
00226     switch (renderMode) {
00227     case kTTFRenderModeNormal:
00228         _loadFlags = FT_LOAD_TARGET_NORMAL;
00229         _renderMode = FT_RENDER_MODE_NORMAL;
00230         break;
00231 
00232     case kTTFRenderModeLight:
00233         _loadFlags = FT_LOAD_TARGET_LIGHT;
00234         _renderMode = FT_RENDER_MODE_LIGHT;
00235         break;
00236 
00237     case kTTFRenderModeMonochrome:
00238         _loadFlags = FT_LOAD_TARGET_MONO;
00239         _renderMode = FT_RENDER_MODE_MONO;
00240         break;
00241     }
00242 
00243     FT_Fixed yScale = _face->size->metrics.y_scale;
00244     _ascent = ftCeil26_6(FT_MulFix(_face->ascender, yScale));
00245     _descent = ftCeil26_6(FT_MulFix(_face->descender, yScale));
00246 
00247     _width = ftCeil26_6(FT_MulFix(_face->max_advance_width, _face->size->metrics.x_scale));
00248     _height = _ascent - _descent + 1;
00249 
00250     if (!mapping) {
00251         // Allow loading of all unicode characters.
00252         _allowLateCaching = true;
00253 
00254         // Load all ISO-8859-1 characters.
00255         for (uint i = 0; i < 256; ++i) {
00256             if (!cacheGlyph(_glyphs[i], i)) {
00257                 _glyphs.erase(i);
00258             }
00259         }
00260     } else {
00261         // We have a fixed map of characters do not load more later.
00262         _allowLateCaching = false;
00263 
00264         for (uint i = 0; i < 256; ++i) {
00265             const uint32 unicode = mapping[i] & 0x7FFFFFFF;
00266             const bool isRequired = (mapping[i] & 0x80000000) != 0;
00267             // Check whether loading an important glyph fails and error out if
00268             // that is the case.
00269             if (!cacheGlyph(_glyphs[i], unicode)) {
00270                 _glyphs.erase(i);
00271                 if (isRequired)
00272                     return false;
00273             }
00274         }
00275     }
00276 
00277     _initialized = (_glyphs.size() != 0);
00278     return _initialized;
00279 }
00280 
00281 int TTFFont::computePointSize(int size, TTFSizeMode sizeMode) const {
00282     int ptSize = 0;
00283     switch (sizeMode) {
00284     case kTTFSizeModeCell: {
00285         ptSize = readPointSizeFromVDMXTable(size);
00286 
00287         if (ptSize == 0) {
00288             ptSize = computePointSizeFromHeaders(size);
00289         }
00290 
00291         if (ptSize == 0) {
00292             warning("Unable to compute point size for font '%s'", _face->family_name);
00293             ptSize = 1;
00294         }
00295         break;
00296     }
00297     case kTTFSizeModeCharacter:
00298         ptSize = size;
00299         break;
00300     }
00301 
00302     return ptSize;
00303 }
00304 
00305 Common::SeekableReadStream *TTFFont::readTTFTable(FT_ULong tag) const {
00306     // Find the required buffer size by calling the load function with nullptr
00307     FT_ULong size = 0;
00308     FT_Error err = FT_Load_Sfnt_Table(_face, tag, 0, nullptr, &size);
00309     if (err) {
00310         return nullptr;
00311     }
00312 
00313     byte *buf = (byte *)malloc(size);
00314     if (!buf) {
00315         return nullptr;
00316     }
00317 
00318     err = FT_Load_Sfnt_Table(_face, tag, 0, buf, &size);
00319     if (err) {
00320         free(buf);
00321         return nullptr;
00322     }
00323 
00324     return new Common::MemoryReadStream(buf, size, DisposeAfterUse::YES);
00325 }
00326 
00327 int TTFFont::readPointSizeFromVDMXTable(int height) const {
00328     // The Vertical Device Metrics table matches font heights with point sizes.
00329     // FreeType does not expose it, we have to parse it ourselves.
00330     // See https://www.microsoft.com/typography/otspec/vdmx.htm
00331 
00332     Common::ScopedPtr<Common::SeekableReadStream> vdmxBuf(readTTFTable(TTAG_VDMX));
00333     if (!vdmxBuf) {
00334         return 0;
00335     }
00336 
00337     // Read the main header
00338     vdmxBuf->skip(4); // Skip the version
00339     uint16 numRatios = vdmxBuf->readUint16BE();
00340 
00341     // Compute the starting position for the group table positions table
00342     int32 offsetTableStart = vdmxBuf->pos() + 4 * numRatios;
00343 
00344     // Search the ratio table for the 1:1 ratio, or the default record (0, 0, 0)
00345     int32 selectedRatio = -1;
00346     for (uint16 i = 0; i < numRatios; i++) {
00347         vdmxBuf->skip(1); // Skip the charset subset
00348         uint8 xRatio = vdmxBuf->readByte();
00349         uint8 yRatio1 = vdmxBuf->readByte();
00350         uint8 yRatio2 = vdmxBuf->readByte();
00351 
00352         if ((xRatio == 1 && yRatio1 <= 1 && yRatio2 >= 1)
00353             || (xRatio == 0 && yRatio1 == 0 && yRatio2 == 0)) {
00354             selectedRatio = i;
00355             break;
00356         }
00357     }
00358     if (selectedRatio < 0) {
00359         return 0;
00360     }
00361 
00362     // Read from group table positions table to get the group table offset
00363     vdmxBuf->seek(offsetTableStart + sizeof(uint16) * selectedRatio);
00364     uint16 groupOffset = vdmxBuf->readUint16BE();
00365 
00366     // Read the group table header
00367     vdmxBuf->seek(groupOffset);
00368     uint16 numRecords = vdmxBuf->readUint16BE();
00369     vdmxBuf->skip(2); // Skip the table bounds
00370 
00371     // Search a record matching the required height
00372     for (uint16 i = 0; i < numRecords; i++) {
00373         uint16 pointSize = vdmxBuf->readUint16BE();
00374         int16 yMax = vdmxBuf->readSint16BE();
00375         int16 yMin = vdmxBuf->readSint16BE();
00376 
00377         if (yMax + -yMin > height) {
00378             return 0;
00379         }
00380         if (yMax + -yMin == height) {
00381             return pointSize;
00382         }
00383     }
00384 
00385     return 0;
00386 }
00387 
00388 int TTFFont::computePointSizeFromHeaders(int height) const {
00389     TT_OS2 *os2Header = (TT_OS2 *)FT_Get_Sfnt_Table(_face, ft_sfnt_os2);
00390     TT_HoriHeader *horiHeader = (TT_HoriHeader *)FT_Get_Sfnt_Table(_face, ft_sfnt_hhea);
00391 
00392     if (os2Header && (os2Header->usWinAscent + os2Header->usWinDescent != 0)) {
00393         return divRoundToNearest(_face->units_per_EM * height, os2Header->usWinAscent + os2Header->usWinDescent);
00394     } else if (horiHeader && (horiHeader->Ascender + horiHeader->Descender != 0)) {
00395         return divRoundToNearest(_face->units_per_EM * height, horiHeader->Ascender + horiHeader->Descender);
00396     }
00397 
00398     return 0;
00399 }
00400 
00401 int TTFFont::getFontHeight() const {
00402     return _height;
00403 }
00404 
00405 int TTFFont::getMaxCharWidth() const {
00406     return _width;
00407 }
00408 
00409 int TTFFont::getCharWidth(uint32 chr) const {
00410     assureCached(chr);
00411     GlyphCache::const_iterator glyphEntry = _glyphs.find(chr);
00412     if (glyphEntry == _glyphs.end())
00413         return 0;
00414     else
00415         return glyphEntry->_value.advance;
00416 }
00417 
00418 int TTFFont::getKerningOffset(uint32 left, uint32 right) const {
00419     if (!_hasKerning)
00420         return 0;
00421 
00422     assureCached(left);
00423     assureCached(right);
00424 
00425     FT_UInt leftGlyph, rightGlyph;
00426     GlyphCache::const_iterator glyphEntry;
00427 
00428     glyphEntry = _glyphs.find(left);
00429     if (glyphEntry != _glyphs.end()) {
00430         leftGlyph = glyphEntry->_value.slot;
00431     } else {
00432         return 0;
00433     }
00434 
00435     glyphEntry = _glyphs.find(right);
00436     if (glyphEntry != _glyphs.end()) {
00437         rightGlyph = glyphEntry->_value.slot;
00438     } else {
00439         return 0;
00440     }
00441 
00442     if (!leftGlyph || !rightGlyph)
00443         return 0;
00444 
00445     FT_Vector kerningVector;
00446     FT_Get_Kerning(_face, leftGlyph, rightGlyph, FT_KERNING_DEFAULT, &kerningVector);
00447     return (kerningVector.x / 64);
00448 }
00449 
00450 Common::Rect TTFFont::getBoundingBox(uint32 chr) const {
00451     assureCached(chr);
00452     GlyphCache::const_iterator glyphEntry = _glyphs.find(chr);
00453     if (glyphEntry == _glyphs.end()) {
00454         return Common::Rect();
00455     } else {
00456         const int xOffset = glyphEntry->_value.xOffset;
00457         const int yOffset = glyphEntry->_value.yOffset;
00458         const Graphics::Surface &image = glyphEntry->_value.image;
00459         return Common::Rect(xOffset, yOffset, xOffset + image.w, yOffset + image.h);
00460     }
00461 }
00462 
00463 namespace {
00464 
00465 template<typename ColorType>
00466 void renderGlyph(uint8 *dstPos, const int dstPitch, const uint8 *srcPos, const int srcPitch, const int w, const int h, ColorType color, const PixelFormat &dstFormat) {
00467     uint8 sR, sG, sB;
00468     dstFormat.colorToRGB(color, sR, sG, sB);
00469 
00470     for (int y = 0; y < h; ++y) {
00471         ColorType *rDst = (ColorType *)dstPos;
00472         const uint8 *src = srcPos;
00473 
00474         for (int x = 0; x < w; ++x) {
00475             if (*src == 255) {
00476                 *rDst = color;
00477             } else if (*src) {
00478                 const uint8 a = *src;
00479 
00480                 uint8 dR, dG, dB;
00481                 dstFormat.colorToRGB(*rDst, dR, dG, dB);
00482 
00483                 dR = ((255 - a) * dR + a * sR) / 255;
00484                 dG = ((255 - a) * dG + a * sG) / 255;
00485                 dB = ((255 - a) * dB + a * sB) / 255;
00486 
00487                 *rDst = dstFormat.RGBToColor(dR, dG, dB);
00488             }
00489 
00490             ++rDst;
00491             ++src;
00492         }
00493 
00494         dstPos += dstPitch;
00495         srcPos += srcPitch;
00496     }
00497 }
00498 
00499 } // End of anonymous namespace
00500 
00501 void TTFFont::drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const {
00502     assureCached(chr);
00503     GlyphCache::const_iterator glyphEntry = _glyphs.find(chr);
00504     if (glyphEntry == _glyphs.end())
00505         return;
00506 
00507     const Glyph &glyph = glyphEntry->_value;
00508 
00509     x += glyph.xOffset;
00510     y += glyph.yOffset;
00511 
00512     if (x > dst->w)
00513         return;
00514     if (y > dst->h)
00515         return;
00516 
00517     int w = glyph.image.w;
00518     int h = glyph.image.h;
00519 
00520     const uint8 *srcPos = (const uint8 *)glyph.image.getPixels();
00521 
00522     // Make sure we are not drawing outside the screen bounds
00523     if (x < 0) {
00524         srcPos -= x;
00525         w += x;
00526         x = 0;
00527     }
00528 
00529     if (x + w > dst->w)
00530         w = dst->w - x;
00531 
00532     if (w <= 0)
00533         return;
00534 
00535     if (y < 0) {
00536         srcPos -= y * glyph.image.pitch;
00537         h += y;
00538         y = 0;
00539     }
00540 
00541     if (y + h > dst->h)
00542         h = dst->h - y;
00543 
00544     if (h <= 0)
00545         return;
00546 
00547     uint8 *dstPos = (uint8 *)dst->getBasePtr(x, y);
00548 
00549     if (dst->format.bytesPerPixel == 1) {
00550         for (int cy = 0; cy < h; ++cy) {
00551             uint8 *rDst = dstPos;
00552             const uint8 *src = srcPos;
00553 
00554             for (int cx = 0; cx < w; ++cx) {
00555                 // We assume a 1Bpp mode is a color indexed mode, thus we can
00556                 // not take advantage of anti-aliasing here.
00557                 if (*src >= 0x80)
00558                     *rDst = color;
00559 
00560                 ++rDst;
00561                 ++src;
00562             }
00563 
00564             dstPos += dst->pitch;
00565             srcPos += glyph.image.pitch;
00566         }
00567     } else if (dst->format.bytesPerPixel == 2) {
00568         renderGlyph<uint16>(dstPos, dst->pitch, srcPos, glyph.image.pitch, w, h, color, dst->format);
00569     } else if (dst->format.bytesPerPixel == 4) {
00570         renderGlyph<uint32>(dstPos, dst->pitch, srcPos, glyph.image.pitch, w, h, color, dst->format);
00571     }
00572 }
00573 
00574 bool TTFFont::cacheGlyph(Glyph &glyph, uint32 chr) const {
00575     FT_UInt slot = FT_Get_Char_Index(_face, chr);
00576     if (!slot)
00577         return false;
00578 
00579     glyph.slot = slot;
00580 
00581     // We use the light target and render mode to improve the looks of the
00582     // glyphs. It is most noticable in FreeSansBold.ttf, where otherwise the
00583     // 't' glyph looks like it is cut off on the right side.
00584     if (FT_Load_Glyph(_face, slot, _loadFlags))
00585         return false;
00586 
00587     if (FT_Render_Glyph(_face->glyph, _renderMode))
00588         return false;
00589 
00590     if (_face->glyph->format != FT_GLYPH_FORMAT_BITMAP)
00591         return false;
00592 
00593     glyph.xOffset = _face->glyph->bitmap_left;
00594     glyph.yOffset = _ascent - _face->glyph->bitmap_top;
00595 
00596     glyph.advance = ftCeil26_6(_face->glyph->advance.x);
00597 
00598     const FT_Bitmap &bitmap = _face->glyph->bitmap;
00599     glyph.image.create(bitmap.width, bitmap.rows, PixelFormat::createFormatCLUT8());
00600 
00601     const uint8 *src = bitmap.buffer;
00602     int srcPitch = bitmap.pitch;
00603     if (srcPitch < 0) {
00604         src += (bitmap.rows - 1) * srcPitch;
00605         srcPitch = -srcPitch;
00606     }
00607 
00608     uint8 *dst = (uint8 *)glyph.image.getPixels();
00609     memset(dst, 0, glyph.image.h * glyph.image.pitch);
00610 
00611     switch (bitmap.pixel_mode) {
00612     case FT_PIXEL_MODE_MONO:
00613         for (int y = 0; y < (int)bitmap.rows; ++y) {
00614             const uint8 *curSrc = src;
00615             uint8 mask = 0;
00616 
00617             for (int x = 0; x < (int)bitmap.width; ++x) {
00618                 if ((x % 8) == 0)
00619                     mask = *curSrc++;
00620 
00621                 if (mask & 0x80)
00622                     *dst = 255;
00623 
00624                 mask <<= 1;
00625                 ++dst;
00626             }
00627 
00628             src += srcPitch;
00629         }
00630         break;
00631 
00632     case FT_PIXEL_MODE_GRAY:
00633         for (int y = 0; y < (int)bitmap.rows; ++y) {
00634             memcpy(dst, src, bitmap.width);
00635             dst += glyph.image.pitch;
00636             src += srcPitch;
00637         }
00638         break;
00639 
00640     default:
00641         warning("TTFFont::cacheGlyph: Unsupported pixel mode %d", bitmap.pixel_mode);
00642         glyph.image.free();
00643         return false;
00644     }
00645 
00646     return true;
00647 }
00648 
00649 void TTFFont::assureCached(uint32 chr) const {
00650     if (!chr || !_allowLateCaching || _glyphs.contains(chr)) {
00651         return;
00652     }
00653 
00654     Glyph newGlyph;
00655     if (cacheGlyph(newGlyph, chr)) {
00656         _glyphs[chr] = newGlyph;
00657     }
00658 }
00659 
00660 Font *loadTTFFont(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
00661     TTFFont *font = new TTFFont();
00662 
00663     if (!font->load(stream, size, sizeMode, dpi, renderMode, mapping)) {
00664         delete font;
00665         return 0;
00666     }
00667 
00668     return font;
00669 }
00670 
00671 Font *loadTTFFontFromArchive(const Common::String &filename, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
00672     Common::Archive *archive;
00673     if (!Common::File::exists("fonts.dat") || (archive = Common::makeZipArchive("fonts.dat")) == nullptr) {
00674         return 0;
00675     }
00676 
00677     Common::File f;
00678     if (!f.open(filename, *archive)) {
00679         return 0;
00680     }
00681 
00682     Font *font = loadTTFFont(f, size, sizeMode, dpi, renderMode, mapping);
00683 
00684     delete archive;
00685     return font;
00686 }
00687 
00688 } // End of namespace Graphics
00689 
00690 namespace Common {
00691 DECLARE_SINGLETON(Graphics::TTFLibrary);
00692 } // End of namespace Common
00693 
00694 #endif
00695 


Generated on Sat May 18 2019 05:01:28 for ResidualVM by doxygen 1.7.1
curved edge   curved edge