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

Commit

Permalink
fix: overly thin font rendering on mojave
Browse files Browse the repository at this point in the history
  • Loading branch information
miniak committed Apr 9, 2019
1 parent 3c35d8e commit 6bcecfb
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 0 deletions.
6 changes: 6 additions & 0 deletions patches/common/skia/.patches.yaml
Expand Up @@ -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
179 changes: 179 additions & 0 deletions 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<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
- UniqueCFRef<CGContextRef> cgContext(
- CGBitmapContextCreate(&rgb, 1, 1, 8, 4, colorspace.get(), BITMAP_INFO_RGB));
- UniqueCFRef<CTFontRef> 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<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
+ SkUniqueCFRef<CGContextRef> noSmoothContext(
+ CGBitmapContextCreate(&noSmoothBitmap, 16, 16, 8, 16*4,
+ colorspace.get(), BITMAP_INFO_RGB));
+ SkUniqueCFRef<CGContextRef> smoothContext(
+ CGBitmapContextCreate(&smoothBitmap, 16, 16, 8, 16*4,
+ colorspace.get(), BITMAP_INFO_RGB));
+
+ SkUniqueCFRef<CGDataProviderRef> data(
+ CGDataProviderCreateWithData(nullptr, kSpiderSymbol_ttf,
+ SK_ARRAY_COUNT(kSpiderSymbol_ttf), nullptr));
+ SkUniqueCFRef<CGFontRef> cgFont(CGFontCreateWithDataProvider(data.get()));
+ SkASSERT(cgFont);
+ SkUniqueCFRef<CTFontRef> 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<CGImageRef> image(CGBitmapContextCreateImage(noSmoothContext()));
+ //SkUniqueCFRef<CGImageRef> 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<CGGlyph>(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);
+ }
}
}

0 comments on commit 6bcecfb

Please sign in to comment.