This repository has been archived by the owner on Jan 6, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: overly thin font rendering on mojave
- Loading branch information
Showing
2 changed files
with
185 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
+ } | ||
} | ||
} | ||
|