From fb43174297ae02ad043aeaf96f66b269ac72520d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Tue, 21 Apr 2020 12:53:08 -0700 Subject: [ios, macos] Attempts in vain to replace codepoints with glyphs --- platform/darwin/src/local_glyph_rasterizer.mm | 72 ++++++++++++++-- platform/darwin/src/shaping.mm | 117 +++++++++++++++----------- src/mbgl/layout/symbol_layout.cpp | 20 +++-- src/mbgl/text/local_glyph_rasterizer.hpp | 1 + src/mbgl/text/shaping.hpp | 4 + 5 files changed, 152 insertions(+), 62 deletions(-) diff --git a/platform/darwin/src/local_glyph_rasterizer.mm b/platform/darwin/src/local_glyph_rasterizer.mm index fe5a5218a4..b28d67d267 100644 --- a/platform/darwin/src/local_glyph_rasterizer.mm +++ b/platform/darwin/src/local_glyph_rasterizer.mm @@ -10,6 +10,12 @@ #import #import +#if TARGET_OS_IPHONE + #import +#else + #import +#endif + #import "CFHandle.hpp" /// Enables local glyph rasterization for all writing systems, not just CJK. @@ -160,10 +166,18 @@ PremultipliedImage drawGlyphBitmap(GlyphID glyphID, CTFontRef font, Size size) { positions[0] = CGPointMake(0.0, 0.0); CTFontDrawGlyphs(font, glyphs, positions, 1, *context); - const CGFloat *black = CGColorGetComponents(CGColorGetConstantColor(kCGColorBlack)); - CGContextSetFillColor(*context, black); - CGContextFillRect(*context, CGRectMake(0, 0, size.width, size.height)); +// const CGFloat *black = CGColorGetComponents(CGColorGetConstantColor(kCGColorBlack)); +// CGContextSetFillColor(*context, black); +// CGContextFillRect(*context, CGRectMake(0, 0, size.width, size.height)); + constexpr const size_t bitsPerPixel = bitsPerComponent * bytesPerPixel; + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, rgbaBitmap.data.get(), bytesPerPixel * size.width * size.height, [](void*, const void*, size_t) {}); + CGImageRef image = CGImageCreate(size.width, size.height, bitsPerComponent, bitsPerPixel, + bytesPerRow, *colorSpace, + kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast, provider, + NULL, false, kCGRenderingIntentDefault); + CGDataProviderRelease(provider); + CGImageRelease(image); return rgbaBitmap; } @@ -195,17 +209,22 @@ Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack& fontStack, GlyphID g } manufacturedGlyph.metrics.left = std::round(CGRectGetMinX(boundingRects[0])); manufacturedGlyph.metrics.top = std::round(CGRectGetMinY(boundingRects[0])); - // Mimic glyph PBF metrics. - manufacturedGlyph.metrics.width = 35; - manufacturedGlyph.metrics.height = 35; + manufacturedGlyph.metrics.width = std::ceil(CGRectGetWidth(boundingRects[0])); + manufacturedGlyph.metrics.height = std::ceil(CGRectGetHeight(boundingRects[0])); CGSize advances[1]; CTFontGetAdvancesForGlyphs(*font, orientation, glyphs, advances, 1); manufacturedGlyph.metrics.advance = std::ceil(advances[0].width); - Size size(MAX(manufacturedGlyph.metrics.width, 1), MAX(manufacturedGlyph.metrics.height, 1)); +#if TARGET_OS_IPHONE + CGFloat scaleFactor = UIScreen.mainScreen.scale; +#else + CGFloat scaleFactor = NSScreen.mainScreen.backingScaleFactor; +#endif + Size size(MAX(manufacturedGlyph.metrics.width, 1) * scaleFactor, + MAX(manufacturedGlyph.metrics.height, 1) * scaleFactor); PremultipliedImage rgbaBitmap = drawGlyphBitmap(glyphID, *font, size); - + // Copy alpha values from RGBA bitmap into the AlphaImage output manufacturedGlyph.bitmap = AlphaImage(size); for (uint32_t i = 0; i < size.width * size.height; i++) { @@ -338,4 +357,41 @@ GlyphDependencies getGlyphDependencies(const FontStack& fontStack, const std::st return dependencies; } +std::vector getGlyphIDs(const FontStack& fontStack, const std::string& text, bool isVertical) { + // TODO: Implement global fallback fonts. + CTFontDescriptorRefHandle descriptor(createFontDescriptor(fontStack, @[], isVertical)); + CTFontRefHandle font(CTFontCreateWithFontDescriptor(*descriptor, 0.0, NULL)); + + CFStringRef keys[] = { kCTFontAttributeName }; + CFTypeRef values[] = { *font }; + + CFDictionaryRefHandle attributes( + CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, + (const void**)&values, sizeof(keys) / sizeof(keys[0]), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + + CFStringRef string = (__bridge CFStringRef)@(text.c_str()); + CFAttributedStringRefHandle attrString(CFAttributedStringCreate(kCFAllocatorDefault, string, *attributes)); + CTLineRefHandle line(CTLineCreateWithAttributedString(*attrString)); + + CFArrayRef glyphRuns = CTLineGetGlyphRuns(*line); + std::vector glyphIDs; + for (CFIndex i = 0; i < CFArrayGetCount(glyphRuns); i++) { + CTRunRef glyphRun = (CTRunRef)CFArrayGetValueAtIndex(glyphRuns, 0); + CFRange wholeRunRange = CFRangeMake(0, CTRunGetGlyphCount(glyphRun)); + + CTFontRef glyphFont = (CTFontRef)CFDictionaryGetValue(CTRunGetAttributes(glyphRun), kCTFontAttributeName); + CFStringRefHandle glyphFontName(CTFontCopyName(glyphFont, kCTFontPostScriptNameKey)); + FontStack glyphFontStack = {{ [(__bridge NSString *)*glyphFontName UTF8String] }}; + + // Use CTRunGetGlyphsPtr() if available. + CGGlyph glyphs[wholeRunRange.length]; + CTRunGetGlyphs(glyphRun, wholeRunRange, glyphs); + + glyphIDs.insert(glyphIDs.end(), glyphs, glyphs + wholeRunRange.length); + } + return glyphIDs; +} + } // namespace mbgl diff --git a/platform/darwin/src/shaping.mm b/platform/darwin/src/shaping.mm index 753bb2b386..cc8b95ace5 100644 --- a/platform/darwin/src/shaping.mm +++ b/platform/darwin/src/shaping.mm @@ -145,58 +145,19 @@ void PositionedIcon::fitIconToText(const Shaping& shapedText, // Defined in local_glyph_rasterizer.mm CTFontDescriptorRef createFontDescriptor(const FontStack& fontStack, NSArray* fallbackFontNames, bool isVertical); -Shaping getShaping(const TaggedString& formattedString, - const float maxWidth, - const float lineHeight, - const style::SymbolAnchorType textAnchor, - const style::TextJustifyType textJustify, - const float spacing, - const std::array& translate, - const WritingModeType writingMode, - BiDi& bidi, - const GlyphMap& glyphMap, - const GlyphPositions& glyphPositions, - const ImagePositions& imagePositions, - float layoutTextSize, - float layoutTextSizeAtBucketZoomLevel, - bool allowVerticalPlacement) { +void shapeLines(const TaggedString& formattedString, + CFDictionaryRef attributes, + const float maxWidth, + bool allowVerticalPlacement, + Shaping& shaping, + std::vector& glyphIDs) { // TODO: Vertical text. // const bool vertical = writingMode != WritingModeType::Horizontal && allowVerticalPlacement; const bool vertical = false; CTFontOrientation orientation = vertical ? kCTFontOrientationVertical : kCTFontOrientationHorizontal; - CTTextAlignment textAlignment; - switch (textJustify) { - case style::TextJustifyType::Auto: - textAlignment = kCTTextAlignmentNatural; - break; - case style::TextJustifyType::Center: - textAlignment = kCTTextAlignmentCenter; - break; - case style::TextJustifyType::Left: - textAlignment = kCTTextAlignmentLeft; - break; - case style::TextJustifyType::Right: - textAlignment = kCTTextAlignmentRight; - break; - } - CGFloat lineHeightInEms = lineHeight / util::ONE_EM; - CTParagraphStyleSetting paragraphSettings[] = { - { kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &textAlignment }, - { kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(float), &lineHeightInEms }, - }; - CTParagraphStyleRefHandle paragraphStyle(CTParagraphStyleCreate(paragraphSettings, sizeof(paragraphSettings) / sizeof(paragraphSettings[0]))); - - CFStringRef keys[] = { kCTParagraphStyleAttributeName }; - CFTypeRef values[] = { *paragraphStyle }; - - CFDictionaryRefHandle attributes( - CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, - (const void**)&values, sizeof(keys) / sizeof(keys[0]), - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); const auto& string = util::convertUTF16ToUTF8(formattedString.rawText()); - CFAttributedStringRefHandle attrString(CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)@(string.c_str()), *attributes)); + CFAttributedStringRefHandle attrString(CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)@(string.c_str()), attributes)); CFMutableAttributedStringRefHandle mutableAttrString(CFAttributedStringCreateMutableCopy(kCFAllocatorDefault, 0, *attrString)); for (std::size_t i = 0; i < formattedString.length(); i++) { const auto& section = formattedString.getSection(i); @@ -208,9 +169,6 @@ Shaping getShaping(const TaggedString& formattedString, CFAttributedStringSetAttribute(*mutableAttrString, CFRangeMake(i, 1), kCTFontAttributeName, *font); } - Shaping shaping(translate[0], translate[1], writingMode); - shaping.verticalizable = true; - CTFramesetterRefHandle framesetter(CTFramesetterCreateWithAttributedString(*mutableAttrString)); CFRange fittingRange; CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(*framesetter, CFRangeMake(0, 0), NULL, CGSizeMake(maxWidth, CGFLOAT_MAX), &fittingRange); @@ -237,6 +195,7 @@ Shaping getShaping(const TaggedString& formattedString, CGGlyph glyphs[wholeRunRange.length]; CTRunGetGlyphs(run, wholeRunRange, glyphs); + glyphIDs.insert(glyphIDs.end(), glyphs, glyphs + wholeRunRange.length); CGPoint positions[wholeRunRange.length]; CTRunGetPositions(run, wholeRunRange, positions); @@ -273,8 +232,68 @@ Shaping getShaping(const TaggedString& formattedString, } } } +} +Shaping getShaping(const TaggedString& formattedString, + const float maxWidth, + const float lineHeight, + const style::SymbolAnchorType textAnchor, + const style::TextJustifyType textJustify, + const float spacing, + const std::array& translate, + const WritingModeType writingMode, + BiDi& bidi, + const GlyphMap& glyphMap, + const GlyphPositions& glyphPositions, + const ImagePositions& imagePositions, + float layoutTextSize, + float layoutTextSizeAtBucketZoomLevel, + bool allowVerticalPlacement) { + CTTextAlignment textAlignment; + switch (textJustify) { + case style::TextJustifyType::Auto: + textAlignment = kCTTextAlignmentNatural; + break; + case style::TextJustifyType::Center: + textAlignment = kCTTextAlignmentCenter; + break; + case style::TextJustifyType::Left: + textAlignment = kCTTextAlignmentLeft; + break; + case style::TextJustifyType::Right: + textAlignment = kCTTextAlignmentRight; + break; + } + CGFloat lineHeightInEms = lineHeight / util::ONE_EM; + CTParagraphStyleSetting paragraphSettings[] = { + { kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &textAlignment }, + { kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(float), &lineHeightInEms }, + }; + CTParagraphStyleRefHandle paragraphStyle(CTParagraphStyleCreate(paragraphSettings, sizeof(paragraphSettings) / sizeof(paragraphSettings[0]))); + + CFStringRef keys[] = { kCTParagraphStyleAttributeName }; + CFTypeRef values[] = { *paragraphStyle }; + + CFDictionaryRefHandle attributes( + CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys, + (const void**)&values, sizeof(keys) / sizeof(keys[0]), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + + Shaping shaping(translate[0], translate[1], writingMode); + shaping.verticalizable = true; + std::vector glyphIDs; + shapeLines(formattedString, *attributes, maxWidth, allowVerticalPlacement, shaping, glyphIDs); return shaping; } +std::vector getShapedGlyphs(const TaggedString& formattedString, const float maxWidth, const WritingModeType writingMode, bool allowVerticalPlacement) { + CFDictionaryRefHandle attributes(CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + + std::vector glyphIDs; + Shaping shaping(0, 0, writingMode); + shapeLines(formattedString, *attributes, maxWidth, allowVerticalPlacement, shaping, glyphIDs); + return glyphIDs; +} + } // namespace mbgl diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 61eae08523..2d7bc6ee74 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -158,13 +158,15 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, } const auto& sectionFontStack = section.fontStack ? *section.fontStack : baseFontStack; + const auto& dependencies = mbgl::getGlyphDependencies(sectionFontStack, u8string, allowVerticalPlacement); + layoutParameters.glyphDependencies.insert(dependencies.begin(), dependencies.end()); + +// auto glyphIDs = mbgl::getGlyphIDs(sectionFontStack, u8string, allowVerticalPlacement); +// auto glyphString = std::u16string(glyphIDs.begin(), glyphIDs.end()); ft.formattedText->addTextSection(applyArabicShaping(util::convertUTF8ToUTF16(u8string)), section.fontScale ? *section.fontScale : 1.0, sectionFontStack, section.textColor); - - const auto& dependencies = mbgl::getGlyphDependencies(sectionFontStack, u8string, allowVerticalPlacement); - layoutParameters.glyphDependencies.insert(dependencies.begin(), dependencies.end()); } else { layoutParameters.imageDependencies.emplace(section.image->id(), ImageType::Icon); ft.formattedText->addImageSection(section.image->id()); @@ -372,14 +374,15 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, ? layout->evaluate(zoom, feature, canonicalID) * util::ONE_EM : 0.0f; + /// Maximum line width in ems. + auto maxWidth = isPointPlacement ? layout->evaluate(zoom, feature, canonicalID) * util::ONE_EM : 0.0f; auto applyShaping = [&](const TaggedString& formattedText, WritingModeType writingMode, SymbolAnchorType textAnchor, TextJustifyType textJustify) { Shaping result = getShaping( /* string */ formattedText, - /* maxWidth: ems */ - isPointPlacement ? layout->evaluate(zoom, feature, canonicalID) * util::ONE_EM : 0.0f, + maxWidth, /* ems */ lineHeight, textAnchor, textJustify, @@ -475,6 +478,13 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, shapedTextOrientations.vertical = applyShaping(*feature.formattedText, WritingModeType::Vertical, textAnchor, textJustify); } } + + auto glyphIDs = getShapedGlyphs(*feature.formattedText, maxWidth, WritingModeType::None, allowVerticalPlacement); + auto styledText = feature.formattedText->getStyledText(); + std::vector sections = feature.formattedText->getSections(); + auto glyphString = std::u16string(glyphIDs.begin(), glyphIDs.end()); + StyledText styledGlyphIDs(glyphString, styledText.second); + feature.formattedText = TaggedString(styledGlyphIDs, sections); } // if feature has icon, get sprite atlas position diff --git a/src/mbgl/text/local_glyph_rasterizer.hpp b/src/mbgl/text/local_glyph_rasterizer.hpp index 47d077d197..db63aa1d73 100644 --- a/src/mbgl/text/local_glyph_rasterizer.hpp +++ b/src/mbgl/text/local_glyph_rasterizer.hpp @@ -45,5 +45,6 @@ private: }; GlyphDependencies getGlyphDependencies(const FontStack& fontStack, const std::string& text, bool isVertical); +std::vector getGlyphIDs(const FontStack& fontStack, const std::string& text, bool isVertical); } // namespace mbgl diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index 1043aad6bf..83a9dfd6e2 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -96,5 +96,9 @@ Shaping getShaping(const TaggedString& string, float layoutTextSize, float layoutTextSizeAtBucketZoomLevel, bool allowVerticalPlacement); +std::vector getShapedGlyphs(const TaggedString& formattedString, + const float maxWidth, + const WritingModeType writingMode, + bool allowVerticalPlacement); } // namespace mbgl -- cgit v1.2.1