Bug 1724433 - Remove special CG text drawing code from DrawTargetSkia. r=lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D122399
This commit is contained in:
Markus Stange 2021-08-13 23:08:02 +00:00
parent 2177eb9bb5
commit 193a8de121
2 changed files with 1 additions and 261 deletions

View File

@ -1,135 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _MOZILLA_GFX_SKIACGPOPUPDRAWER_H
#define _MOZILLA_GFX_SKIACGPOPUPDRAWER_H
#include <ApplicationServices/ApplicationServices.h>
#include "nsDebug.h"
#include "mozilla/Vector.h"
#include "ScaledFontMac.h"
#include <dlfcn.h>
// This is used when we explicitly need CG to draw text to support things such
// as vibrancy and subpixel AA on transparent backgrounds. The current use cases
// are really only to enable Skia to support drawing text in those situations.
namespace mozilla {
namespace gfx {
typedef void (*CGContextSetFontSmoothingBackgroundColorFunc)(CGContextRef cgContext,
CGColorRef color);
static CGContextSetFontSmoothingBackgroundColorFunc
GetCGContextSetFontSmoothingBackgroundColorFunc() {
static CGContextSetFontSmoothingBackgroundColorFunc func = nullptr;
static bool lookedUpFunc = false;
if (!lookedUpFunc) {
func = (CGContextSetFontSmoothingBackgroundColorFunc)dlsym(
RTLD_DEFAULT, "CGContextSetFontSmoothingBackgroundColor");
lookedUpFunc = true;
}
return func;
}
static CGColorRef ColorToCGColor(CGColorSpaceRef aColorSpace, const DeviceColor& aColor) {
CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
return CGColorCreate(aColorSpace, components);
}
static bool SetFontSmoothingBackgroundColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace,
const DeviceColor& aFontSmoothingBackgroundColor) {
if (aFontSmoothingBackgroundColor.a > 0) {
CGContextSetFontSmoothingBackgroundColorFunc setFontSmoothingBGColorFunc =
GetCGContextSetFontSmoothingBackgroundColorFunc();
if (setFontSmoothingBGColorFunc) {
CGColorRef color = ColorToCGColor(aColorSpace, aFontSmoothingBackgroundColor);
setFontSmoothingBGColorFunc(aCGContext, color);
CGColorRelease(color);
return true;
}
}
return false;
}
// Font rendering with a non-transparent font smoothing background color
// can leave pixels in our buffer where the rgb components exceed the alpha
// component. When this happens we need to clean up the data afterwards.
// The purpose of this is probably the following: Correct compositing of
// subpixel anti-aliased fonts on transparent backgrounds requires
// different alpha values per RGB component. Usually, premultiplied color
// values are derived by multiplying all components with the same per-pixel
// alpha value. However, if you multiply each component with a *different*
// alpha, and set the alpha component of the pixel to, say, the average
// of the alpha values that you used during the premultiplication of the
// RGB components, you can trick OVER compositing into doing a simplified
// form of component alpha compositing. (You just need to make sure to
// clamp the components of the result pixel to [0,255] afterwards.)
static void EnsureValidPremultipliedData(CGContextRef aContext,
CGRect aTextBounds = CGRectInfinite) {
if (CGBitmapContextGetBitsPerPixel(aContext) != 32 ||
CGBitmapContextGetAlphaInfo(aContext) != kCGImageAlphaPremultipliedFirst) {
return;
}
uint8_t* bitmapData = (uint8_t*)CGBitmapContextGetData(aContext);
CGRect bitmapBounds =
CGRectMake(0, 0, CGBitmapContextGetWidth(aContext), CGBitmapContextGetHeight(aContext));
int stride = CGBitmapContextGetBytesPerRow(aContext);
CGRect bounds = CGRectIntersection(bitmapBounds, aTextBounds);
if (CGRectIsEmpty(bounds)) {
return;
}
int startX = bounds.origin.x;
int endX = startX + bounds.size.width;
MOZ_ASSERT(endX <= bitmapBounds.size.width);
// CGRect assume that our origin is the bottom left.
// The data assumes that the origin is the top left.
// Have to switch the Y axis so that our coordinates are correct
int startY = bitmapBounds.size.height - (bounds.origin.y + bounds.size.height);
int endY = startY + bounds.size.height;
MOZ_ASSERT(endY <= (int)CGBitmapContextGetHeight(aContext));
for (int y = startY; y < endY; y++) {
for (int x = startX; x < endX; x++) {
int i = y * stride + x * 4;
uint8_t a = bitmapData[i + 3];
bitmapData[i + 0] = std::min(a, bitmapData[i + 0]);
bitmapData[i + 1] = std::min(a, bitmapData[i + 1]);
bitmapData[i + 2] = std::min(a, bitmapData[i + 2]);
}
}
}
static CGRect ComputeGlyphsExtents(CGRect* bboxes, CGPoint* positions, CFIndex count, float scale) {
CGFloat x1, x2, y1, y2;
if (count < 1) return CGRectZero;
x1 = bboxes[0].origin.x + positions[0].x;
x2 = bboxes[0].origin.x + positions[0].x + scale * bboxes[0].size.width;
y1 = bboxes[0].origin.y + positions[0].y;
y2 = bboxes[0].origin.y + positions[0].y + scale * bboxes[0].size.height;
// accumulate max and minimum coordinates
for (int i = 1; i < count; i++) {
x1 = std::min(x1, bboxes[i].origin.x + positions[i].x);
y1 = std::min(y1, bboxes[i].origin.y + positions[i].y);
x2 = std::max(x2, bboxes[i].origin.x + positions[i].x + scale * bboxes[i].size.width);
y2 = std::max(y2, bboxes[i].origin.y + positions[i].y + scale * bboxes[i].size.height);
}
CGRect extents = {{x1, y1}, {x2 - x1, y2 - y1}};
return extents;
}
} // namespace gfx
} // namespace mozilla
#endif

View File

@ -34,8 +34,6 @@
#ifdef MOZ_WIDGET_COCOA
# include "BorrowedContext.h"
# include <ApplicationServices/ApplicationServices.h>
# include "ScaledFontMac.h"
# include "CGTextDrawing.h"
#endif
#ifdef XP_WIN
@ -1089,33 +1087,7 @@ static bool SetupCGContext(DrawTargetSkia* aDT, CGContextRef aCGContext,
GfxMatrixToCGAffineTransform(aDT->GetTransform()));
return true;
}
static bool SetupCGGlyphs(CGContextRef aCGContext, const GlyphBuffer& aBuffer,
Vector<CGGlyph, 32>& aGlyphs,
Vector<CGPoint, 32>& aPositions) {
// Flip again so we draw text in right side up. Transform (3) from the top
CGContextScaleCTM(aCGContext, 1, -1);
if (!aGlyphs.resizeUninitialized(aBuffer.mNumGlyphs) ||
!aPositions.resizeUninitialized(aBuffer.mNumGlyphs)) {
gfxDevCrash(LogReason::GlyphAllocFailedCG)
<< "glyphs/positions allocation failed";
return false;
}
for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
aGlyphs[i] = aBuffer.mGlyphs[i].mIndex;
// Flip the y coordinates so that text ends up in the right spot after the
// (3) flip Inversion from (4) in the comments.
aPositions[i] = CGPointMake(aBuffer.mGlyphs[i].mPosition.x,
-aBuffer.mGlyphs[i].mPosition.y);
}
return true;
}
// End long comment about transforms. SetupCGContext and SetupCGGlyphs should
// stay next to each other.
// End long comment about transforms.
// The context returned from this method will have the origin
// in the top left and will have applied all the neccessary clips
@ -1222,96 +1194,7 @@ void BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget* aDT,
CGContextRef cg) {
DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
skiaDT->ReturnCGContext(cg);
return;
}
static void SetFontColor(CGContextRef aCGContext, CGColorSpaceRef aColorSpace,
const Pattern& aPattern) {
const DeviceColor& color = static_cast<const ColorPattern&>(aPattern).mColor;
CGColorRef textColor = ColorToCGColor(aColorSpace, color);
CGContextSetFillColorWithColor(aCGContext, textColor);
CGColorRelease(textColor);
}
/***
* We need this to support subpixel AA text on OS X in two cases:
* text in DrawTargets that are not opaque and text over vibrant backgrounds.
* Skia normally doesn't support subpixel AA text on transparent backgrounds.
* To get around this, we have to wrap the Skia bytes with a CGContext and ask
* CG to draw the text.
* In vibrancy cases, we have to use a private API,
* CGContextSetFontSmoothingBackgroundColor, which sets the expected
* background color the text will draw onto so that CG can render the text
* properly. After that, we have to go back and fixup the pixels
* such that their alpha values are correct.
*/
bool DrawTargetSkia::FillGlyphsWithCG(ScaledFont* aFont,
const GlyphBuffer& aBuffer,
const Pattern& aPattern,
const DrawOptions& aOptions) {
MOZ_ASSERT(aFont->GetType() == FontType::MAC);
MOZ_ASSERT(aPattern.GetType() == PatternType::COLOR);
CGContextRef cgContext = BorrowCGContext(aOptions);
if (!cgContext) {
return false;
}
Vector<CGGlyph, 32> glyphs;
Vector<CGPoint, 32> positions;
if (!SetupCGGlyphs(cgContext, aBuffer, glyphs, positions)) {
ReturnCGContext(cgContext);
return false;
}
ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont);
SetFontSmoothingBackgroundColor(cgContext, mColorSpace,
macFont->FontSmoothingBackgroundColor());
SetFontColor(cgContext, mColorSpace, aPattern);
CTFontDrawGlyphs(macFont->mCTFont, glyphs.begin(), positions.begin(),
aBuffer.mNumGlyphs, cgContext);
// Calculate the area of the text we just drew
auto* bboxes = new CGRect[aBuffer.mNumGlyphs];
CTFontGetBoundingRectsForGlyphs(macFont->mCTFont, kCTFontOrientationDefault,
glyphs.begin(), bboxes, aBuffer.mNumGlyphs);
CGRect extents =
ComputeGlyphsExtents(bboxes, positions.begin(), aBuffer.mNumGlyphs, 1.0f);
delete[] bboxes;
CGAffineTransform cgTransform = CGContextGetCTM(cgContext);
extents = CGRectApplyAffineTransform(extents, cgTransform);
// Have to round it out to ensure we fully cover all pixels
Rect rect(extents.origin.x, extents.origin.y, extents.size.width,
extents.size.height);
rect.RoundOut();
extents = CGRectMake(rect.x, rect.y, rect.width, rect.height);
EnsureValidPremultipliedData(cgContext, extents);
ReturnCGContext(cgContext);
return true;
}
static bool HasFontSmoothingBackgroundColor(ScaledFont* aFont) {
// This should generally only be true if we have a popup context menu
if (aFont && aFont->GetType() == FontType::MAC) {
DeviceColor fontSmoothingBackgroundColor =
static_cast<ScaledFontMac*>(aFont)->FontSmoothingBackgroundColor();
return fontSmoothingBackgroundColor.a > 0;
}
return false;
}
static bool ShouldUseCGToFillGlyphs(ScaledFont* aFont,
const Pattern& aPattern) {
return HasFontSmoothingBackgroundColor(aFont) &&
aPattern.GetType() == PatternType::COLOR;
}
#endif
static bool CanDrawFont(ScaledFont* aFont) {
@ -1337,14 +1220,6 @@ void DrawTargetSkia::DrawGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
MarkChanged();
#ifdef MOZ_WIDGET_COCOA
if (!aStrokeOptions && ShouldUseCGToFillGlyphs(aFont, aPattern)) {
if (FillGlyphsWithCG(aFont, aBuffer, aPattern, aOptions)) {
return;
}
}
#endif
ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
SkTypeface* typeface = skiaFont->GetSkTypeface();
if (!typeface) {