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:
Jonathan Kew
2024-07-02 11:02:31 +00:00
parent 3f7f6499c2
commit 288948382a
3 changed files with 107 additions and 64 deletions

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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