Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[ios, macos] Get glyph metrics from font
Browse files Browse the repository at this point in the history
Get glyph metrics from the font in the process of drawing each glyph into a bitmap context. These metrics result in more accurate kerning and better aligned baselines than the previous hard-coded values.

Align iOS/macOS local glyph rasterization test fixture to Qt.
  • Loading branch information
1ec5 committed Apr 23, 2020
1 parent 7e32964 commit 5d823dd
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 33 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

By default, CJK characters are now set in the font specified by the `text-font` layout property. If the named font is not installed on the device or bundled with the application, the characters are set in one of the fallback fonts passed into the `localFontFamily` parameter of `mbgl::Renderer::Renderer()` and `mbgl::MapSnapshotter::MapSnapshotter()`. This parameter can now contain a list of font family names, font display names, and font PostScript names, each name separated by a newline.

CJK characters are now laid out according to the font, so fonts with nonsquare glyphs have the correct kerning. This also fixes an issue where the baseline for CJK characters was too low compared to non-CJK characters.

## maps-v1.6.0-rc.1

### ✨ New features
Expand Down
89 changes: 56 additions & 33 deletions platform/darwin/src/local_glyph_rasterizer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,39 @@ CTFontRef createFont(const FontStack& fontStack) {
return util::i18n::allowsFixedWidthGlyphGeneration(glyphID) && impl->isEnabled();
}

PremultipliedImage drawGlyphBitmap(GlyphID glyphID, CTFontRef font, Size size) {
PremultipliedImage rgbaBitmap(size);

PremultipliedImage drawGlyphBitmap(GlyphID glyphID, CTFontRef font, GlyphMetrics& metrics) {
CFStringRefHandle string(CFStringCreateWithCharacters(NULL, reinterpret_cast<UniChar*>(&glyphID), 1));
if (!string) {
throw std::runtime_error("Unable to create string from codepoint");
}

CFStringRef keys[] = { kCTFontAttributeName };
CFTypeRef values[] = { font };

CFDictionaryRefHandle attributes(
CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
(const void**)&values, sizeof(keys) / sizeof(keys[0]),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
if (!attributes) {
throw std::runtime_error("Unable to create attributed string attributes dictionary");
}

CFAttributedStringRefHandle attrString(CFAttributedStringCreate(kCFAllocatorDefault, *string, *attributes));
if (!attrString) {
throw std::runtime_error("Unable to create attributed string");
}
CTLineRefHandle line(CTLineCreateWithAttributedString(*attrString));
if (!line) {
throw std::runtime_error("Unable to create line from attributed string");
}

Size size(35, 35);
metrics.width = size.width;
metrics.height = size.height;

PremultipliedImage rgbaBitmap(size);

CGColorSpaceHandle colorSpace(CGColorSpaceCreateDeviceRGB());
if (!colorSpace) {
throw std::runtime_error("CGColorSpaceCreateDeviceRGB failed");
Expand All @@ -173,52 +201,47 @@ CGContextHandle context(CGBitmapContextCreate(
if (!context) {
throw std::runtime_error("CGBitmapContextCreate failed");
}

CFStringRef keys[] = { kCTFontAttributeName };
CFTypeRef values[] = { font };

CFDictionaryRefHandle attributes(
CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
(const void**)&values, sizeof(keys) / sizeof(keys[0]),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));

CFAttributedStringRefHandle attrString(CFAttributedStringCreate(kCFAllocatorDefault, *string, *attributes));
CTLineRefHandle line(CTLineCreateWithAttributedString(*attrString));

// Start drawing a little bit below the top of the bitmap
CGContextSetTextPosition(*context, 0.0, 5.0);
CFArrayRef glyphRuns = CTLineGetGlyphRuns(*line);
CTRunRef glyphRun = (CTRunRef)CFArrayGetValueAtIndex(glyphRuns, 0);
CFRange wholeRunRange = CFRangeMake(0, CTRunGetGlyphCount(glyphRun));
CGSize advances[wholeRunRange.length];
CTRunGetAdvances(glyphRun, wholeRunRange, advances);
metrics.advance = std::round(advances[0].width);

// Mimic glyph PBF metrics.
metrics.left = Glyph::borderSize;
metrics.top = -1;

// Move the text upward to avoid clipping off descenders.
CGFloat descent;
CTRunGetTypographicBounds(glyphRun, wholeRunRange, NULL, &descent, NULL);
CGContextSetTextPosition(*context, 0.0, descent);

CTLineDraw(*line, *context);

return rgbaBitmap;
}

Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack& fontStack, GlyphID glyphID) {
Glyph fixedMetrics;
CTFontRef font = impl->createFont(fontStack);
Glyph manufacturedGlyph;
CTFontRefHandle font(impl->createFont(fontStack));
if (!font) {
return fixedMetrics;
return manufacturedGlyph;
}

fixedMetrics.id = glyphID;
manufacturedGlyph.id = glyphID;

Size size(35, 35);
PremultipliedImage rgbaBitmap = drawGlyphBitmap(glyphID, *font, manufacturedGlyph.metrics);

fixedMetrics.metrics.width = size.width;
fixedMetrics.metrics.height = size.height;
fixedMetrics.metrics.left = 3;
fixedMetrics.metrics.top = -1;
fixedMetrics.metrics.advance = 24;

PremultipliedImage rgbaBitmap = drawGlyphBitmap(glyphID, font, size);

Size size(manufacturedGlyph.metrics.width, manufacturedGlyph.metrics.height);
// Copy alpha values from RGBA bitmap into the AlphaImage output
fixedMetrics.bitmap = AlphaImage(size);
manufacturedGlyph.bitmap = AlphaImage(size);
for (uint32_t i = 0; i < size.width * size.height; i++) {
fixedMetrics.bitmap.data[i] = rgbaBitmap.data[4 * i + 3];
manufacturedGlyph.bitmap.data[i] = rgbaBitmap.data[4 * i + 3];
}

return fixedMetrics;
return manufacturedGlyph;
}

} // namespace mbgl
Binary file modified test/fixtures/local_glyphs/ping_fang/expected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 5d823dd

Please sign in to comment.