aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Bouclet2015-11-22 14:18:14 +0100
committerBastien Bouclet2015-12-21 18:41:14 +0100
commit2d86f6da9c0ee2ffa80a2b570c148ea337b09cec (patch)
tree245b7c44d49e1e77f0b0b31732b3ce700285cf42
parent366e164705a920ccd5de9dc606399f9c5b54913c (diff)
downloadscummvm-rg350-2d86f6da9c0ee2ffa80a2b570c148ea337b09cec.tar.gz
scummvm-rg350-2d86f6da9c0ee2ffa80a2b570c148ea337b09cec.tar.bz2
scummvm-rg350-2d86f6da9c0ee2ffa80a2b570c148ea337b09cec.zip
GRAPHICS: Introduce a size mode for TrueType fonts
Allows to match Windows font size selection by converting font heights to point sizes using the TrueType tables.
-rw-r--r--engines/wintermute/base/font/base_font_truetype.cpp4
-rw-r--r--engines/zvision/text/truetype_font.cpp2
-rw-r--r--graphics/fonts/ttf.cpp144
-rw-r--r--graphics/fonts/ttf.h24
-rw-r--r--gui/ThemeEngine.cpp2
5 files changed, 166 insertions, 10 deletions
diff --git a/engines/wintermute/base/font/base_font_truetype.cpp b/engines/wintermute/base/font/base_font_truetype.cpp
index f27b565a7f..fa6973c58f 100644
--- a/engines/wintermute/base/font/base_font_truetype.cpp
+++ b/engines/wintermute/base/font/base_font_truetype.cpp
@@ -581,7 +581,7 @@ bool BaseFontTT::initFont() {
}
if (file) {
- _deletableFont = Graphics::loadTTFFont(*file, _fontHeight, 96); // Use the same dpi as WME (96 vs 72).
+ _deletableFont = Graphics::loadTTFFont(*file, _fontHeight, Graphics::kTTFSizeModeCharacter, 96); // Use the same dpi as WME (96 vs 72).
_font = _deletableFont;
BaseFileManager::getEngineInstance()->closeFile(file);
file = nullptr;
@@ -607,7 +607,7 @@ bool BaseFontTT::initFont() {
if (themeArchive->hasFile(fallbackFilename)) {
file = nullptr;
file = themeArchive->createReadStreamForMember(fallbackFilename);
- _deletableFont = Graphics::loadTTFFont(*file, _fontHeight, 96); // Use the same dpi as WME (96 vs 72).
+ _deletableFont = Graphics::loadTTFFont(*file, _fontHeight, Graphics::kTTFSizeModeCharacter, 96); // Use the same dpi as WME (96 vs 72).
_font = _deletableFont;
}
// We're not using BaseFileManager, so clean up after ourselves:
diff --git a/engines/zvision/text/truetype_font.cpp b/engines/zvision/text/truetype_font.cpp
index acb053ea8d..ed4a81a297 100644
--- a/engines/zvision/text/truetype_font.cpp
+++ b/engines/zvision/text/truetype_font.cpp
@@ -123,7 +123,7 @@ bool StyledTTFont::loadFont(const Common::String &fontName, int32 point, uint st
!file.open(freeFontName) && !_engine->getSearchManager()->openFile(file, freeFontName))
error("Unable to open font file %s (Liberation Font alternative: %s, FreeFont alternative: %s)", newFontName.c_str(), liberationFontName.c_str(), freeFontName.c_str());
- Graphics::Font *newFont = Graphics::loadTTFFont(file, point, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display
+ Graphics::Font *newFont = Graphics::loadTTFFont(file, point, Graphics::kTTFSizeModeCharacter, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display
if (newFont == nullptr) {
return false;
}
diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp
index dc7335f1c2..98420f6dfb 100644
--- a/graphics/fonts/ttf.cpp
+++ b/graphics/fonts/ttf.cpp
@@ -33,11 +33,15 @@
#include "common/singleton.h"
#include "common/stream.h"
+#include "common/memstream.h"
#include "common/hashmap.h"
+#include "common/ptr.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_TRUETYPE_TAGS_H
namespace Graphics {
@@ -47,6 +51,10 @@ inline int ftCeil26_6(FT_Pos x) {
return (x + 63) / 64;
}
+inline int divRoundToNearest(int dividend, int divisor) {
+ return (dividend + (divisor / 2)) / divisor;
+}
+
} // End of anonymous namespace
class TTFLibrary : public Common::Singleton<TTFLibrary> {
@@ -101,7 +109,7 @@ public:
TTFFont();
virtual ~TTFFont();
- bool load(Common::SeekableReadStream &stream, int size, uint dpi, TTFRenderMode renderMode, const uint32 *mapping);
+ bool load(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping);
virtual int getFontHeight() const;
@@ -137,6 +145,12 @@ private:
bool _allowLateCaching;
void assureCached(uint32 chr) const;
+ Common::SeekableReadStream *readTTFTable(FT_ULong tag) const;
+
+ int computePointSize(int size, TTFSizeMode sizeMode) const;
+ int readPointSizeFromVDMXTable(int height) const;
+ int computePointSizeFromHeaders(int height) const;
+
FT_Int32 _loadFlags;
FT_Render_Mode _renderMode;
bool _hasKerning;
@@ -162,7 +176,7 @@ TTFFont::~TTFFont() {
}
}
-bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
+bool TTFFont::load(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
if (!g_ttf.isInitialized())
return false;
@@ -200,7 +214,7 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, TTFRe
// Check whether we have kerning support
_hasKerning = (FT_HAS_KERNING(_face) != 0);
- if (FT_Set_Char_Size(_face, 0, size * 64, dpi, dpi)) {
+ if (FT_Set_Char_Size(_face, 0, computePointSize(size, sizeMode) * 64, dpi, dpi)) {
delete[] _ttfFile;
_ttfFile = 0;
@@ -262,6 +276,126 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, TTFRe
return _initialized;
}
+int TTFFont::computePointSize(int size, TTFSizeMode sizeMode) const {
+ int ptSize;
+ switch (sizeMode) {
+ case kTTFSizeModeCell: {
+ ptSize = readPointSizeFromVDMXTable(size);
+
+ if (ptSize == 0) {
+ ptSize = computePointSizeFromHeaders(size);
+ }
+
+ if (ptSize == 0) {
+ warning("Unable to compute point size for font '%s'", _face->family_name);
+ ptSize = 1;
+ }
+ break;
+ }
+ case kTTFSizeModeCharacter:
+ ptSize = size;
+ break;
+ }
+
+ return ptSize;
+}
+
+Common::SeekableReadStream *TTFFont::readTTFTable(FT_ULong tag) const {
+ // Find the required buffer size by calling the load function with nullptr
+ FT_ULong size = 0;
+ FT_Error err = FT_Load_Sfnt_Table(_face, tag, 0, nullptr, &size);
+ if (err) {
+ return nullptr;
+ }
+
+ byte *buf = (byte *)malloc(size);
+ if (!buf) {
+ return nullptr;
+ }
+
+ err = FT_Load_Sfnt_Table(_face, tag, 0, buf, &size);
+ if (err) {
+ free(buf);
+ return nullptr;
+ }
+
+ return new Common::MemoryReadStream(buf, size, DisposeAfterUse::YES);
+}
+
+int TTFFont::readPointSizeFromVDMXTable(int height) const {
+ // The Vertical Device Metrics table matches font heights with point sizes.
+ // FreeType does not expose it, we have to parse it ourselves.
+ // See https://www.microsoft.com/typography/otspec/vdmx.htm
+
+ Common::ScopedPtr<Common::SeekableReadStream> vdmxBuf(readTTFTable(TTAG_VDMX));
+ if (!vdmxBuf) {
+ return 0;
+ }
+
+ // Read the main header
+ vdmxBuf->skip(4); // Skip the version
+ uint16 numRatios = vdmxBuf->readUint16BE();
+
+ // Compute the starting position for the group table positions table
+ int32 offsetTableStart = vdmxBuf->pos() + 4 * numRatios;
+
+ // Search the ratio table for the 1:1 ratio, or the default record (0, 0, 0)
+ int32 selectedRatio = -1;
+ for (uint16 i = 0; i < numRatios; i++) {
+ vdmxBuf->skip(1); // Skip the charset subset
+ uint8 xRatio = vdmxBuf->readByte();
+ uint8 yRatio1 = vdmxBuf->readByte();
+ uint8 yRatio2 = vdmxBuf->readByte();
+
+ if ((xRatio == 1 && yRatio1 <= 1 && yRatio2 >= 1)
+ || (xRatio == 0 && yRatio1 == 0 && yRatio2 == 0)) {
+ selectedRatio = i;
+ break;
+ }
+ }
+ if (selectedRatio < 0) {
+ return 0;
+ }
+
+ // Read from group table positions table to get the group table offset
+ vdmxBuf->seek(offsetTableStart + sizeof(uint16) * selectedRatio);
+ uint16 groupOffset = vdmxBuf->readUint16BE();
+
+ // Read the group table header
+ vdmxBuf->seek(groupOffset);
+ uint16 numRecords = vdmxBuf->readUint16BE();
+ vdmxBuf->skip(2); // Skip the table bounds
+
+ // Search a record matching the required height
+ for (uint16 i = 0; i < numRecords; i++) {
+ uint16 pointSize = vdmxBuf->readUint16BE();
+ int16 yMax = vdmxBuf->readSint16BE();
+ int16 yMin = vdmxBuf->readSint16BE();
+
+ if (yMax + -yMin > height) {
+ return 0;
+ }
+ if (yMax + -yMin == height) {
+ return pointSize;
+ }
+ }
+
+ return 0;
+}
+
+int TTFFont::computePointSizeFromHeaders(int height) const {
+ TT_OS2 *os2Header = (TT_OS2 *)FT_Get_Sfnt_Table(_face, ft_sfnt_os2);
+ TT_HoriHeader *horiHeader = (TT_HoriHeader *)FT_Get_Sfnt_Table(_face, ft_sfnt_hhea);
+
+ if (os2Header && (os2Header->usWinAscent + os2Header->usWinDescent != 0)) {
+ return divRoundToNearest(_face->units_per_EM * height, os2Header->usWinAscent + os2Header->usWinDescent);
+ } else if (horiHeader && (horiHeader->Ascender + horiHeader->Descender != 0)) {
+ return divRoundToNearest(_face->units_per_EM * height, horiHeader->Ascender + horiHeader->Descender);
+ }
+
+ return 0;
+}
+
int TTFFont::getFontHeight() const {
return _height;
}
@@ -521,10 +655,10 @@ void TTFFont::assureCached(uint32 chr) const {
}
}
-Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
+Font *loadTTFFont(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode, uint dpi, TTFRenderMode renderMode, const uint32 *mapping) {
TTFFont *font = new TTFFont();
- if (!font->load(stream, size, dpi, renderMode, mapping)) {
+ if (!font->load(stream, size, sizeMode, dpi, renderMode, mapping)) {
delete font;
return 0;
}
diff --git a/graphics/fonts/ttf.h b/graphics/fonts/ttf.h
index bd25b69f21..4110486357 100644
--- a/graphics/fonts/ttf.h
+++ b/graphics/fonts/ttf.h
@@ -56,10 +56,32 @@ enum TTFRenderMode {
};
/**
+ * This specifies how the font size is defined.
+ */
+enum TTFSizeMode {
+ /**
+ * Character height only.
+ *
+ * This matches rendering obtained when calling
+ * CreateFont in Windows with negative height values.
+ */
+ kTTFSizeModeCharacter,
+
+ /**
+ * Full cell height.
+ *
+ * This matches rendering obtained when calling
+ * CreateFont in Windows with positive height values.
+ */
+ kTTFSizeModeCell
+};
+
+/**
* Loads a TTF font file from a given data stream object.
*
* @param stream Stream object to load font data from.
* @param size The point size to load.
+ * @param sizeMode The point size definition used for the size parameter.
* @param dpi The dpi to use for size calculations, by default 72dpi
* are used.
* @param renderMode FreeType2 mode used to render glyphs. @see TTFRenderMode
@@ -71,7 +93,7 @@ enum TTFRenderMode {
* supported.
* @return 0 in case loading fails, otherwise a pointer to the Font object.
*/
-Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi = 0, TTFRenderMode renderMode = kTTFRenderModeLight, const uint32 *mapping = 0);
+Font *loadTTFFont(Common::SeekableReadStream &stream, int size, TTFSizeMode sizeMode = kTTFSizeModeCharacter, uint dpi = 0, TTFRenderMode renderMode = kTTFRenderModeLight, const uint32 *mapping = 0);
void shutdownTTF();
diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp
index 6562a1d922..536e5192e0 100644
--- a/gui/ThemeEngine.cpp
+++ b/gui/ThemeEngine.cpp
@@ -1459,7 +1459,7 @@ const Graphics::Font *ThemeEngine::loadScalableFont(const Common::String &filena
for (Common::ArchiveMemberList::const_iterator i = members.begin(), end = members.end(); i != end; ++i) {
Common::SeekableReadStream *stream = (*i)->createReadStream();
if (stream) {
- font = Graphics::loadTTFFont(*stream, pointsize, 0, Graphics::kTTFRenderModeLight,
+ font = Graphics::loadTTFFont(*stream, pointsize, Graphics::kTTFSizeModeCharacter, 0, Graphics::kTTFRenderModeLight,
#ifdef USE_TRANSLATION
TransMan.getCharsetMapping()
#else