aboutsummaryrefslogtreecommitdiff
path: root/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'graphics')
-rw-r--r--graphics/fonts/ttf.cpp144
-rw-r--r--graphics/fonts/ttf.h24
2 files changed, 162 insertions, 6 deletions
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();