Bug 1902253 - When the compositing mode is not OVER, we must first render the color glyph to a temporary surface, then composite it with the appropriate operator. r=gfx-reviewers,lsalzman
Differential Revision: https://phabricator.services.mozilla.com/D215408
This commit is contained in:
@@ -2392,8 +2392,7 @@ bool COLRFonts::ValidateColorGlyphs(hb_blob_t* aCOLR, hb_blob_t* aCPAL) {
|
||||
}
|
||||
|
||||
if (uint16_t(colr->version) == 1) {
|
||||
return StaticPrefs::gfx_font_rendering_colr_v1_enabled() &&
|
||||
colrLength >= sizeof(COLRv1Header) &&
|
||||
return colrLength >= sizeof(COLRv1Header) &&
|
||||
reinterpret_cast<const COLRv1Header*>(colr)->Validate(colrLength);
|
||||
}
|
||||
|
||||
|
||||
@@ -2664,82 +2664,126 @@ bool gfxFont::RenderColorGlyph(DrawTarget* aDrawTarget, gfxContext* aContext,
|
||||
}
|
||||
|
||||
auto* colr = GetFontEntry()->GetCOLR();
|
||||
if (const auto* paintGraph = COLRFonts::GetGlyphPaintGraph(colr, aGlyphId)) {
|
||||
const auto* hbShaper = GetHarfBuzzShaper();
|
||||
if (hbShaper && hbShaper->IsInitialized()) {
|
||||
const auto* paintGraph = COLRFonts::GetGlyphPaintGraph(colr, aGlyphId);
|
||||
const gfxHarfBuzzShaper* hbShaper = nullptr;
|
||||
if (paintGraph) {
|
||||
// We need the hbShaper to get color glyph bounds, so check that it's
|
||||
// usable.
|
||||
hbShaper = GetHarfBuzzShaper();
|
||||
if (!hbShaper && !hbShaper->IsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
if (aTextDrawer) {
|
||||
aTextDrawer->FoundUnsupportedFeature();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const auto* layers =
|
||||
paintGraph ? nullptr : COLRFonts::GetGlyphLayers(colr, aGlyphId);
|
||||
|
||||
if (!paintGraph && !layers) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For reasonable font sizes, use a cache of rasterized glyphs.
|
||||
if (GetAdjustedSize() <= 256.0) {
|
||||
bool useCache = GetAdjustedSize() <= 256.0;
|
||||
|
||||
// If the composition op is not OVER, rasterize to a temporary surface
|
||||
// and then composite to the destination, even if we're not caching.
|
||||
// But we can't do this if the target is a TextDrawTarget, as it doesn't
|
||||
// support DrawSurface.
|
||||
RefPtr<SourceSurface> snapshot;
|
||||
if ((useCache ||
|
||||
aFontParams.drawOptions.mCompositionOp != CompositionOp::OP_OVER) &&
|
||||
aDrawTarget->GetBackendType() != BackendType::WEBRENDER_TEXT) {
|
||||
AutoWriteLock lock(mLock);
|
||||
if (!mColorGlyphCache) {
|
||||
if (!mColorGlyphCache && useCache) {
|
||||
mColorGlyphCache = MakeUnique<ColorGlyphCache>();
|
||||
}
|
||||
|
||||
// Tell the cache what colors we're using; if they have changed, it will
|
||||
// discard any currently-cached entries.
|
||||
mColorGlyphCache->SetColors(aFontParams.currentColor,
|
||||
aFontParams.palette);
|
||||
|
||||
bool ok = false;
|
||||
auto cached = mColorGlyphCache->mCache.lookupForAdd(aGlyphId);
|
||||
Rect bounds = COLRFonts::GetColorGlyphBounds(
|
||||
Rect bounds;
|
||||
if (paintGraph) {
|
||||
bounds = COLRFonts::GetColorGlyphBounds(
|
||||
colr, hbShaper->GetHBFont(), aGlyphId, aDrawTarget,
|
||||
aFontParams.scaledFont, mFUnitsConvFactor);
|
||||
} else {
|
||||
bounds = GetFontEntry()->GetFontExtents(mFUnitsConvFactor);
|
||||
}
|
||||
bounds.RoundOut();
|
||||
|
||||
// Tell the cache what colors we're using; if they have changed, it
|
||||
// will discard any currently-cached entries.
|
||||
HashMap<uint32_t, RefPtr<SourceSurface>>::AddPtr cached;
|
||||
if (useCache) {
|
||||
mColorGlyphCache->SetColors(aFontParams.currentColor,
|
||||
aFontParams.palette);
|
||||
cached = mColorGlyphCache->mCache.lookupForAdd(aGlyphId);
|
||||
if (cached) {
|
||||
ok = true;
|
||||
} else {
|
||||
// Create a temporary DrawTarget, render the glyph, and save a
|
||||
// snapshot of the rendering in the cache.
|
||||
snapshot = cached->value();
|
||||
}
|
||||
}
|
||||
|
||||
if (!snapshot) {
|
||||
// Create a temporary DrawTarget and render the glyph to it.
|
||||
IntSize size(int(bounds.width), int(bounds.height));
|
||||
SurfaceFormat format = SurfaceFormat::B8G8R8A8;
|
||||
RefPtr target =
|
||||
Factory::CreateDrawTarget(BackendType::SKIA, size, format);
|
||||
if (target) {
|
||||
// Use OP_OVER to create the glyph snapshot.
|
||||
DrawOptions drawOptions(aFontParams.drawOptions);
|
||||
drawOptions.mCompositionOp = CompositionOp::OP_OVER;
|
||||
bool ok = false;
|
||||
if (paintGraph) {
|
||||
ok = COLRFonts::PaintGlyphGraph(
|
||||
GetFontEntry()->GetCOLR(), hbShaper->GetHBFont(), paintGraph,
|
||||
target, nullptr, aFontParams.scaledFont,
|
||||
aFontParams.drawOptions, -bounds.TopLeft(),
|
||||
aFontParams.currentColor, aFontParams.palette->Colors(),
|
||||
aGlyphId, mFUnitsConvFactor);
|
||||
if (ok) {
|
||||
RefPtr snapshot = target->Snapshot();
|
||||
ok = mColorGlyphCache->mCache.add(cached, aGlyphId, snapshot);
|
||||
}
|
||||
}
|
||||
colr, hbShaper->GetHBFont(), paintGraph, target, nullptr,
|
||||
aFontParams.scaledFont, drawOptions, -bounds.TopLeft(),
|
||||
aFontParams.currentColor, aFontParams.palette->Colors(), aGlyphId,
|
||||
mFUnitsConvFactor);
|
||||
} else {
|
||||
auto face(GetFontEntry()->GetHBFace());
|
||||
ok = COLRFonts::PaintGlyphLayers(
|
||||
colr, face, layers, target, nullptr, aFontParams.scaledFont,
|
||||
drawOptions, -bounds.TopLeft(), aFontParams.currentColor,
|
||||
aFontParams.palette->Colors());
|
||||
}
|
||||
if (ok) {
|
||||
// Paint the snapshot from cached->value(), and return.
|
||||
aDrawTarget->DrawSurface(
|
||||
cached->value(), Rect(aPoint + bounds.TopLeft(), bounds.Size()),
|
||||
Rect(Point(), bounds.Size()));
|
||||
snapshot = target->Snapshot();
|
||||
if (useCache) {
|
||||
// Save a snapshot of the rendering in the cache.
|
||||
// (We ignore potential failure here, and just paint the snapshot
|
||||
// without caching it.)
|
||||
Unused << mColorGlyphCache->mCache.add(cached, aGlyphId, snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (snapshot) {
|
||||
// Paint the snapshot using the appropriate composition op.
|
||||
aDrawTarget->DrawSurface(snapshot,
|
||||
Rect(aPoint + bounds.TopLeft(), bounds.Size()),
|
||||
Rect(Point(), bounds.Size()),
|
||||
DrawSurfaceOptions(), aFontParams.drawOptions);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we failed to cache the glyph, or it was too large to even try,
|
||||
// just paint directly to the target.
|
||||
// If we didn't paint from a cached or temporary snapshot, just render
|
||||
// directly to the destination drawTarget.
|
||||
if (paintGraph) {
|
||||
return COLRFonts::PaintGlyphGraph(
|
||||
colr, hbShaper->GetHBFont(), paintGraph, aDrawTarget, aTextDrawer,
|
||||
aFontParams.scaledFont, aFontParams.drawOptions, aPoint,
|
||||
aFontParams.currentColor, aFontParams.palette->Colors(), aGlyphId,
|
||||
mFUnitsConvFactor);
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto* layers =
|
||||
COLRFonts::GetGlyphLayers(GetFontEntry()->GetCOLR(), aGlyphId)) {
|
||||
if (layers) {
|
||||
auto face(GetFontEntry()->GetHBFace());
|
||||
bool ok = COLRFonts::PaintGlyphLayers(
|
||||
return COLRFonts::PaintGlyphLayers(
|
||||
colr, face, layers, aDrawTarget, aTextDrawer, aFontParams.scaledFont,
|
||||
aFontParams.drawOptions, aPoint, aFontParams.currentColor,
|
||||
aFontParams.palette->Colors());
|
||||
return ok;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -328,7 +328,7 @@ pref(layout.css.moz-control-character-visibility.enabled,true) pref(layout.css.c
|
||||
== segment-break-transformation-1.html segment-break-transformation-1-ref.html
|
||||
|
||||
# color glyphs, opacity, and RTL runs - bug 1318539
|
||||
== color-opacity-rtl-1.html color-opacity-rtl-1-ref.html
|
||||
fuzzy-if(winWidget,0-1,0-4) == color-opacity-rtl-1.html color-opacity-rtl-1-ref.html
|
||||
fuzzy-if(cocoaWidget,0-1,0-1421) == color-opacity-rtl-2.html color-opacity-rtl-2-ref.html
|
||||
|
||||
== letter-spacing-nolig-1.html letter-spacing-nolig-1-ref.html
|
||||
|
||||
Reference in New Issue
Block a user