mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
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:
parent
2177eb9bb5
commit
193a8de121
@ -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
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user