feat(core): render local CJK glyphs at 2x texture resolution (#4304)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bart Louwers <bart@emeel.net>
This commit is contained in:
@@ -71,7 +71,8 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD
|
||||
|
||||
Glyph GlyphManager::generateLocalSDF(const FontStack& fontStack, GlyphID glyphID) {
|
||||
Glyph local = localGlyphRasterizer->rasterizeGlyph(fontStack, glyphID);
|
||||
local.bitmap = util::transformRasterToSDF(local.bitmap, 8, .25);
|
||||
const double radius = local.metrics.isDoubleResolution ? 16.0 : 8.0;
|
||||
local.bitmap = util::transformRasterToSDF(local.bitmap, radius, .25);
|
||||
return local;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,21 +11,13 @@ namespace mbgl {
|
||||
the glyph and appropriate metrics. GlyphManager will then use TinySDF to
|
||||
transform the rasterized bitmap into an SDF.
|
||||
|
||||
The JS equivalent of this functionality will only generate glyphs in the
|
||||
'CJK Unified Ideographs' and 'Hangul Syllables' ranges, for which it can
|
||||
get away with rendering a fixed 30px square image and GlyphMetrics of:
|
||||
|
||||
width: 24,
|
||||
height: 24,
|
||||
left: 0,
|
||||
top: -8,
|
||||
advance: 24
|
||||
|
||||
The JS equivalent also uses heuristic evaluation of the font stack name
|
||||
to control the font-weight it uses during rasterization.
|
||||
The shared default codepoint filter is
|
||||
util::i18n::allowsFixedWidthGlyphGeneration.
|
||||
Following maplibre-gl-js, platform implementations use 2x bitmaps with
|
||||
1x metrics via GlyphMetrics::isDoubleResolution.
|
||||
|
||||
It is left to platform-specific implementation to decide how best to
|
||||
map a FontStack to a particular rasterization.
|
||||
map a FontStack to a particular rasterization (font, weight, style).
|
||||
|
||||
The default implementation simply refuses to rasterize any glyphs.
|
||||
*/
|
||||
|
||||
@@ -267,11 +267,15 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
|
||||
builtInOffset = {0.0f, 0.0f};
|
||||
}
|
||||
|
||||
// rectBuffer is in 1x logical units; only rect.w/h carry the texture
|
||||
// scale. GL JS keeps 2x glyphs centered by storing fractional
|
||||
// left/top metrics, so native local glyphs do the same.
|
||||
const float textureScale = positionedGlyph.metrics.isDoubleResolution ? 2.0f : 1.0f;
|
||||
const float x1 = (positionedGlyph.metrics.left - rectBuffer) * positionedGlyph.scale - halfAdvance +
|
||||
builtInOffset.x;
|
||||
const float y1 = (-positionedGlyph.metrics.top - rectBuffer) * positionedGlyph.scale + builtInOffset.y;
|
||||
const float x2 = x1 + rect.w * positionedGlyph.scale / pixelRatio;
|
||||
const float y2 = y1 + rect.h * positionedGlyph.scale / pixelRatio;
|
||||
const float x2 = x1 + rect.w / textureScale * positionedGlyph.scale / pixelRatio;
|
||||
const float y2 = y1 + rect.h / textureScale * positionedGlyph.scale / pixelRatio;
|
||||
|
||||
Point<float> tl{x1, y1};
|
||||
Point<float> tr{x2, y1};
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace {
|
||||
return codepoint >= (first) && codepoint <= (last); \
|
||||
}
|
||||
|
||||
inline bool isInRange(char16_t codepoint, char16_t first, char16_t last) {
|
||||
return codepoint >= first && codepoint <= last;
|
||||
}
|
||||
|
||||
// The following table comes from <http://www.unicode.org/Public/12.0.0/ucd/Blocks.txt>.
|
||||
// Keep it synchronized with <http://www.unicode.org/Public/UCD/latest/ucd/Blocks.txt>.
|
||||
|
||||
@@ -411,8 +415,59 @@ bool allowsIdeographicBreaking(char16_t chr) {
|
||||
}
|
||||
|
||||
bool allowsFixedWidthGlyphGeneration(char16_t chr) {
|
||||
// Mirrors conservative set of characters used in glyph_manager.js/_tinySDF
|
||||
return isInCJKUnifiedIdeographs(chr) || isInHangulSyllables(chr) || isInKatakana(chr) || isInHiragana(chr);
|
||||
// Mirrors the BMP ranges in GL JS's codePointUsesLocalIdeographFontFamily.
|
||||
// Astral-plane ranges are not included because GlyphID only carries BMP
|
||||
// codepoints for local glyph generation.
|
||||
return
|
||||
// Bopomofo tone marks.
|
||||
isInRange(chr, 0x02EA, 0x02EB) ||
|
||||
// Hangul Jamo.
|
||||
isInHangulJamo(chr) ||
|
||||
// CJK Radicals Supplement, Kangxi Radicals.
|
||||
isInCJKRadicalsSupplement(chr) || isInKangxiRadicals(chr) ||
|
||||
// CJK Symbols and Punctuation, Hiragana, Katakana.
|
||||
isInCJKSymbolsandPunctuation(chr) || isInHiragana(chr) || isInKatakana(chr) ||
|
||||
// Bopomofo.
|
||||
isInRange(chr, 0x3105, 0x312F) ||
|
||||
// Hangul Compatibility Jamo.
|
||||
isInRange(chr, 0x3131, 0x318E) ||
|
||||
// Bopomofo Extended through CJK Unified Ideographs Extension A.
|
||||
isInBopomofoExtended(chr) || isInCJKStrokes(chr) || isInKatakanaPhoneticExtensions(chr) ||
|
||||
isInEnclosedCJKLettersandMonths(chr) || isInCJKCompatibility(chr) || isInCJKUnifiedIdeographsExtensionA(chr) ||
|
||||
// CJK Unified Ideographs and Yi Syllables.
|
||||
isInCJKUnifiedIdeographs(chr) || isInRange(chr, 0xA000, 0xA48C) ||
|
||||
// Yi Radicals.
|
||||
isInRange(chr, 0xA490, 0xA4C6) ||
|
||||
// Hangul Jamo Extended-A.
|
||||
isInRange(chr, 0xA960, 0xA97C) ||
|
||||
// Hangul Syllables and Hangul Jamo Extended-B.
|
||||
isInHangulSyllables(chr) || isInRange(chr, 0xD7B0, 0xD7C6) ||
|
||||
// Hangul Jamo Extended-B.
|
||||
isInRange(chr, 0xD7CB, 0xD7FB) ||
|
||||
// CJK Compatibility Ideographs.
|
||||
isInRange(chr, 0xF900, 0xFA6D) ||
|
||||
// CJK Compatibility Ideographs.
|
||||
isInRange(chr, 0xFA70, 0xFAD9) ||
|
||||
// Vertical Forms.
|
||||
isInVerticalForms(chr) ||
|
||||
// CJK Compatibility Forms.
|
||||
isInCJKCompatibilityForms(chr) ||
|
||||
// Halfwidth and Fullwidth Forms.
|
||||
isInHalfwidthandFullwidthForms(chr);
|
||||
}
|
||||
|
||||
bool glyphRangeIsEntirelyLocallyGenerated(char16_t rangeStart) {
|
||||
// Some ranges (notably 0xFF00, where Halfwidth/Fullwidth Forms ends at
|
||||
// 0xFFEF and the Specials block continues to 0xFFFD) mix locally-rendered
|
||||
// and non-local codepoints, so it is not safe to infer the whole range
|
||||
// from the first codepoint alone.
|
||||
constexpr uint32_t glyphsPerRange = 256;
|
||||
for (uint32_t offset = 0; offset < glyphsPerRange; ++offset) {
|
||||
if (!allowsFixedWidthGlyphGeneration(static_cast<char16_t>(rangeStart + offset))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool allowsVerticalWritingMode(const std::u16string& string) {
|
||||
|
||||
@@ -27,6 +27,10 @@ bool allowsIdeographicBreaking(char16_t chr);
|
||||
advances */
|
||||
bool allowsFixedWidthGlyphGeneration(char16_t chr);
|
||||
|
||||
/** Returns whether the whole 256-codepoint glyph range can be generated
|
||||
locally. Use this for range-level decisions because some ranges are mixed. */
|
||||
bool glyphRangeIsEntirelyLocallyGenerated(char16_t rangeStart);
|
||||
|
||||
/** Returns whether any substring of the given string can be drawn as vertical
|
||||
text with upright glyphs. */
|
||||
bool allowsVerticalWritingMode(const std::u16string& string);
|
||||
|
||||
Reference in New Issue
Block a user