From 6bcecfb0fddd11a91542046a6032b3e5113d26d4 Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Tue, 9 Apr 2019 18:28:09 +0200 Subject: [PATCH] fix: overly thin font rendering on mojave --- patches/common/skia/.patches.yaml | 6 + patches/common/skia/fix_font_thickness.patch | 179 +++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 patches/common/skia/fix_font_thickness.patch diff --git a/patches/common/skia/.patches.yaml b/patches/common/skia/.patches.yaml index cf0b365c1..f70eb80f1 100644 --- a/patches/common/skia/.patches.yaml +++ b/patches/common/skia/.patches.yaml @@ -4,3 +4,9 @@ patches: owners: alespergl file: dcheck.patch description: null +- + owners: miniak + file: fix_font_thickness.patch + description: | + Detect macOS font smoothing behavior. + Backports https://skia-review.googlesource.com/c/skia/+/157566 diff --git a/patches/common/skia/fix_font_thickness.patch b/patches/common/skia/fix_font_thickness.patch new file mode 100644 index 000000000..6c6ebed14 --- /dev/null +++ b/patches/common/skia/fix_font_thickness.patch @@ -0,0 +1,179 @@ +diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp +index 5d8723b95f..c1e64e2351 100644 +--- a/src/ports/SkFontHost_mac.cpp ++++ b/src/ports/SkFontHost_mac.cpp +@@ -200,34 +200,76 @@ static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) { + + #define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host) + ++enum class SmoothBehavior { ++ none, // SmoothFonts produces no effect. ++ some, // SmoothFonts produces some effect, but not subpixel coverage. ++ subpixel, // SmoothFonts produces some effect and provides subpixel coverage. ++}; ++ + /** + * There does not appear to be a publicly accessable API for determining if lcd + * font smoothing will be applied if we request it. The main issue is that if + * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0. + */ +-static bool supports_LCD() { +- static bool gSupportsLCD = []{ +- uint32_t rgb = 0; +- UniqueCFRef colorspace(CGColorSpaceCreateDeviceRGB()); +- UniqueCFRef cgContext( +- CGBitmapContextCreate(&rgb, 1, 1, 8, 4, colorspace.get(), BITMAP_INFO_RGB)); +- UniqueCFRef ctFont(CTFontCreateWithName(CFSTR("Helvetica"), 16, nullptr)); +- CGContextSetShouldSmoothFonts(cgContext.get(), true); +- CGContextSetShouldAntialias(cgContext.get(), true); +- CGContextSetTextDrawingMode(cgContext.get(), kCGTextFill); +- CGContextSetGrayFillColor(cgContext.get(), 1, 1); +- CGPoint point = CGPointMake(-1, 0); +- static const UniChar pipeChar = '|'; +- CGGlyph pipeGlyph; +- CTFontGetGlyphsForCharacters(ctFont.get(), &pipeChar, &pipeGlyph, 1); +- CTFontDrawGlyphs(ctFont.get(), &pipeGlyph, &point, 1, cgContext.get()); +- +- uint32_t r = (rgb >> 16) & 0xFF; +- uint32_t g = (rgb >> 8) & 0xFF; +- uint32_t b = (rgb >> 0) & 0xFF; +- return (r != g || r != b); ++static SmoothBehavior smooth_behavior() { ++ static SmoothBehavior gSmoothBehavior = []{ ++ uint32_t noSmoothBitmap[16][16] = {}; ++ uint32_t smoothBitmap[16][16] = {}; ++ ++ SkUniqueCFRef colorspace(CGColorSpaceCreateDeviceRGB()); ++ SkUniqueCFRef noSmoothContext( ++ CGBitmapContextCreate(&noSmoothBitmap, 16, 16, 8, 16*4, ++ colorspace.get(), BITMAP_INFO_RGB)); ++ SkUniqueCFRef smoothContext( ++ CGBitmapContextCreate(&smoothBitmap, 16, 16, 8, 16*4, ++ colorspace.get(), BITMAP_INFO_RGB)); ++ ++ SkUniqueCFRef data( ++ CGDataProviderCreateWithData(nullptr, kSpiderSymbol_ttf, ++ SK_ARRAY_COUNT(kSpiderSymbol_ttf), nullptr)); ++ SkUniqueCFRef cgFont(CGFontCreateWithDataProvider(data.get())); ++ SkASSERT(cgFont); ++ SkUniqueCFRef ctFont( ++ CTFontCreateWithGraphicsFont(cgFont.get(), 16, nullptr, nullptr)); ++ SkASSERT(ctFont); ++ ++ CGContextSetShouldSmoothFonts(noSmoothContext.get(), false); ++ CGContextSetShouldAntialias(noSmoothContext.get(), true); ++ CGContextSetTextDrawingMode(noSmoothContext.get(), kCGTextFill); ++ CGContextSetGrayFillColor(noSmoothContext.get(), 1, 1); ++ ++ CGContextSetShouldSmoothFonts(smoothContext.get(), true); ++ CGContextSetShouldAntialias(smoothContext.get(), true); ++ CGContextSetTextDrawingMode(smoothContext.get(), kCGTextFill); ++ CGContextSetGrayFillColor(smoothContext.get(), 1, 1); ++ ++ CGPoint point = CGPointMake(0, 3); ++ CGGlyph spiderGlyph = 3; ++ CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, noSmoothContext.get()); ++ CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, smoothContext.get()); ++ ++ // For debugging. ++ //SkUniqueCFRef image(CGBitmapContextCreateImage(noSmoothContext())); ++ //SkUniqueCFRef image(CGBitmapContextCreateImage(smoothContext())); ++ ++ SmoothBehavior smoothBehavior = SmoothBehavior::none; ++ for (int x = 0; x < 16; ++x) { ++ for (int y = 0; y < 16; ++y) { ++ uint32_t smoothPixel = smoothBitmap[x][y]; ++ uint32_t r = (smoothPixel >> 16) & 0xFF; ++ uint32_t g = (smoothPixel >> 8) & 0xFF; ++ uint32_t b = (smoothPixel >> 0) & 0xFF; ++ if (r != g || r != b) { ++ return SmoothBehavior::subpixel; ++ } ++ if (noSmoothBitmap[x][y] != smoothPixel) { ++ smoothBehavior = SmoothBehavior::some; ++ } ++ } ++ } ++ return smoothBehavior; + }(); +- return gSupportsLCD; ++ return gSmoothBehavior; + } + + class Offscreen { +@@ -809,7 +851,7 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& + if (!fRGBSpace) { + //It doesn't appear to matter what color space is specified. + //Regular blends and antialiased text are always (s*a + d*(1-a)) +- //and smoothed text is always g=2.0. ++ //and subpixel antialiased text is always g=2.0. + fRGBSpace.reset(CGColorSpaceCreateDeviceRGB()); + } + +@@ -1064,7 +1106,7 @@ static constexpr uint8_t sk_pow2_table(size_t i) { + * This will invert the gamma applied by CoreGraphics, so we can get linear + * values. + * +- * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value. ++ * CoreGraphics obscurely defaults to 2.0 as the subpixel coverage gamma value. + * The color space used does not appear to affect this choice. + */ + static constexpr auto gLinearCoverageFromCGLCDValue = SkMakeArray<256>(sk_pow2_table); +@@ -1154,18 +1196,20 @@ void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) { + CGGlyph cgGlyph = SkTo(glyph.getGlyphID()); + + // FIXME: lcd smoothed un-hinted rasterization unsupported. +- bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting; ++ bool requestSmooth = fRec.getHinting() != SkPaint::kNo_Hinting; + + // Draw the glyph + size_t cgRowBytes; +- CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD); ++ CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, requestSmooth); + if (cgPixels == nullptr) { + return; + } + + // Fix the glyph + if ((glyph.fMaskFormat == SkMask::kLCD16_Format) || +- (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) ++ (glyph.fMaskFormat == SkMask::kA8_Format ++ && requestSmooth ++ && smooth_behavior() == SmoothBehavior::subpixel)) + { + const uint8_t* linear = gLinearCoverageFromCGLCDValue.data(); + +@@ -2042,14 +2086,14 @@ void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const { + + rec->fFlags &= ~flagsWeDontSupport; + +- bool lcdSupport = supports_LCD(); ++ SmoothBehavior smoothBehavior = smooth_behavior(); + + // Only two levels of hinting are supported. + // kNo_Hinting means avoid CoreGraphics outline dilation. + // kNormal_Hinting means CoreGraphics outline dilation is allowed. + // If there is no lcd support, hinting (dilation) cannot be supported. + SkPaint::Hinting hinting = rec->getHinting(); +- if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) { ++ if (SkPaint::kSlight_Hinting == hinting || smoothBehavior == SmoothBehavior::none) { + hinting = SkPaint::kNo_Hinting; + } else if (SkPaint::kFull_Hinting == hinting) { + hinting = SkPaint::kNormal_Hinting; +@@ -2076,12 +2120,15 @@ void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const { + // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output. + + if (rec->fMaskFormat == SkMask::kLCD16_Format) { +- if (lcdSupport) { ++ if (smoothBehavior == SmoothBehavior::subpixel) { + //CoreGraphics creates 555 masks for smoothed text anyway. + rec->fMaskFormat = SkMask::kLCD16_Format; + rec->setHinting(SkPaint::kNormal_Hinting); + } else { + rec->fMaskFormat = SkMask::kA8_Format; ++ if (smoothBehavior == SmoothBehavior::some) { ++ rec->setHinting(SkPaint::kNormal_Hinting); ++ } + } + } +