From e8fe128740579a470953a6ae9ee337ab6ef4cebc Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Thu, 29 Sep 2022 15:07:41 +0000 Subject: [PATCH] Bug 1791686 - For COLRv1 glyphs that use PaintComposite, render the composited sub-graph to a temporary Skia surface. r=gfx-reviewers,lsalzman This avoids calling PushLayerWithBlend on the eventual DrawTarget, because not all DrawTarget implementations support it; instead we perform the blend operation to a Skia surface and then copy the result to the real target. Checked that the results are as expected with Dominik's test page at https://roettsch.es/var_colrv1.html, where many of the glyphs (not just the explicit composite-mode tests, but also a bunch of the transform tests etc) involve PaintComposite layers. Differential Revision: https://phabricator.services.mozilla.com/D158197 --- gfx/thebes/COLRFonts.cpp | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/gfx/thebes/COLRFonts.cpp b/gfx/thebes/COLRFonts.cpp index cb62cfb0c604..9ec3632ced1c 100644 --- a/gfx/thebes/COLRFonts.cpp +++ b/gfx/thebes/COLRFonts.cpp @@ -1615,16 +1615,37 @@ struct PaintComposite { if (compositeMode == COMPOSITE_DEST) { return DispatchPaint(aState, aOffset + backdropPaintOffset); } - aState.mDrawTarget->PushLayer(true, 1.0, nullptr, Matrix()); - bool ok = DispatchPaint(aState, aOffset + backdropPaintOffset); - if (ok) { - aState.mDrawTarget->PushLayerWithBlend(true, 1.0, nullptr, Matrix(), - IntRect(), false, - mapCompositionMode(compositeMode)); - ok = DispatchPaint(aState, aOffset + sourcePaintOffset); - aState.mDrawTarget->PopLayer(); + Rect r = GetBoundingRect(aState, aOffset); + if (r.IsEmpty()) { + return true; + } + r.RoundOut(); + // Because not all backends support PushLayerWithBlend (looking at you, + // DrawTargetD2D1), we paint this sub-graph of the glyph to a temporary + // Skia surface where we know we *can* use PushLayerWithBlend, and then + // copy it to the final destination. + RefPtr dt = Factory::CreateDrawTarget(BackendType::SKIA, + IntSize(int(r.width), int(r.height)), + SurfaceFormat::B8G8R8A8); + if (!dt) { + // If this failed, we'll just bail out, leaving this glyph (partially) + // unpainted, but allow other rendering to continue. + return true; + } + dt->SetTransform(Matrix::Translation(-r.TopLeft())); + PaintState state = aState; + state.mDrawTarget = dt; + bool ok = DispatchPaint(state, aOffset + backdropPaintOffset); + if (ok) { + dt->PushLayerWithBlend(true, 1.0, nullptr, Matrix(), IntRect(), false, + mapCompositionMode(compositeMode)); + ok = DispatchPaint(state, aOffset + sourcePaintOffset); + dt->PopLayer(); + } + if (ok) { + RefPtr snapshot = dt->Snapshot(); + aState.mDrawTarget->DrawSurface(snapshot, r, Rect(Point(), r.Size())); } - aState.mDrawTarget->PopLayer(); return ok; }