gecko-dev/layout/svg/nsSVGMaskFrame.cpp
Jeff Muizelaar 0b68652b49 Bug 1539702. Improve CreateClippedDrawTarget API r=jwatt,rhunt
This changes CreateClippedDrawTarget so that instead of taking
a max size and a transform it just takes a user space rect of
the desired bounds.

This change allows the caller to not worry about the computing
a max size based on the current clip. Instead this responsibility
is lowered into the specific backends.

The main motivation for this work is to allow blob recoordination
to create recordings that don't depend on the current clip.

Some additional benefits are that the API is easier to use and
as can be seen simplifies the SVG masking code because it doesn't
need to track surface offsets manually.

It's also an important step towards removing all the uses of
gfxContext::GetClipExtents which will let us get rid of the separate
clipping stack in gfxContext and help us move off of gfxContext
completely.

Most backend implementations of CreateClippedDrawTarget are relatively
simple. DrawTargetCapture is modified to track the current clip rect
so that it can create a new DrawTargetCapture of the appropriate size
without needing to worry about lazy resolution.

Differential Revision: https://phabricator.services.mozilla.com/D33363

--HG--
extra : moz-landing-system : lando
2019-06-21 09:51:00 +00:00

200 lines
6.9 KiB
C++

/* -*- 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/. */
// Main header first:
#include "nsSVGMaskFrame.h"
// Keep others in (case-insensitive) order:
#include "AutoReferenceChainGuard.h"
#include "gfx2DGlue.h"
#include "gfxContext.h"
#include "mozilla/PresShell.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/SVGMaskElement.h"
#include "mozilla/dom/SVGUnitTypesBinding.h"
#include "mozilla/gfx/2D.h"
#include "SVGObserverUtils.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::dom::SVGUnitTypes_Binding;
using namespace mozilla::gfx;
using namespace mozilla::image;
static LuminanceType GetLuminanceType(uint8_t aNSMaskType) {
switch (aNSMaskType) {
case NS_STYLE_MASK_TYPE_LUMINANCE:
return LuminanceType::LUMINANCE;
case NS_STYLE_COLOR_INTERPOLATION_LINEARRGB:
return LuminanceType::LINEARRGB;
default: {
NS_WARNING("Unknown SVG mask type, defaulting to luminance");
return LuminanceType::LUMINANCE;
}
}
}
nsIFrame* NS_NewSVGMaskFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
return new (aPresShell) nsSVGMaskFrame(aStyle, aPresShell->GetPresContext());
}
NS_IMPL_FRAMEARENA_HELPERS(nsSVGMaskFrame)
already_AddRefed<SourceSurface> nsSVGMaskFrame::GetMaskForMaskedFrame(
MaskParams& aParams) {
// Make sure we break reference loops and over long reference chains:
static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
AutoReferenceChainGuard refChainGuard(this, &mInUse, &sRefChainLengthCounter);
if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
// Break reference chain
return nullptr;
}
gfxRect maskArea = GetMaskArea(aParams.maskedFrame);
if (maskArea.IsEmpty()) {
return nullptr;
}
gfxContext* context = aParams.ctx;
// Get the clip extents in device space:
// Minimizing the mask surface extents (using both the current clip extents
// and maskArea) is important for performance.
//
gfxRect maskSurfaceRectDouble = aParams.toUserSpace.TransformBounds(maskArea);
Rect maskSurfaceRect = ToRect(maskSurfaceRectDouble);
maskSurfaceRect.RoundOut();
uint8_t maskType;
if (aParams.maskMode == StyleMaskMode::MatchSource) {
maskType = StyleSVGReset()->mMaskType;
} else {
maskType = aParams.maskMode == StyleMaskMode::Luminance
? NS_STYLE_MASK_TYPE_LUMINANCE
: NS_STYLE_MASK_TYPE_ALPHA;
}
RefPtr<DrawTarget> maskDT;
if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
maskDT = context->GetDrawTarget()->CreateClippedDrawTarget(
maskSurfaceRect, SurfaceFormat::B8G8R8A8);
} else {
maskDT = context->GetDrawTarget()->CreateClippedDrawTarget(
maskSurfaceRect, SurfaceFormat::A8);
}
if (!maskDT || !maskDT->IsValid()) {
return nullptr;
}
RefPtr<gfxContext> tmpCtx =
gfxContext::CreatePreservingTransformOrNull(maskDT);
MOZ_ASSERT(tmpCtx); // already checked the draw target above
mMatrixForChildren =
GetMaskTransform(aParams.maskedFrame) * aParams.toUserSpace;
for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) {
gfxMatrix m = mMatrixForChildren;
// The CTM of each frame referencing us can be different
nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
if (SVGFrame) {
SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
m = nsSVGUtils::GetTransformMatrixInUserSpace(kid) * m;
}
nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m, aParams.imgParams);
}
RefPtr<SourceSurface> surface;
if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
if (StyleSVG()->mColorInterpolation ==
NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
maskType = NS_STYLE_COLOR_INTERPOLATION_LINEARRGB;
}
RefPtr<SourceSurface> maskSnapshot = maskDT->IntoLuminanceSource(
GetLuminanceType(maskType), aParams.opacity);
if (!maskSnapshot) {
return nullptr;
}
surface = maskSnapshot.forget();
} else {
maskDT->FillRect(maskSurfaceRect,
ColorPattern(Color(1.0f, 1.0f, 1.0f, aParams.opacity)),
DrawOptions(1, CompositionOp::OP_IN));
RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot();
if (!maskSnapshot) {
return nullptr;
}
surface = maskSnapshot.forget();
}
return surface.forget();
}
gfxRect nsSVGMaskFrame::GetMaskArea(nsIFrame* aMaskedFrame) {
SVGMaskElement* maskElem = static_cast<SVGMaskElement*>(GetContent());
uint16_t units =
maskElem->mEnumAttributes[SVGMaskElement::MASKUNITS].GetAnimValue();
gfxRect bbox;
if (units == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
bbox = nsSVGUtils::GetBBox(aMaskedFrame,
nsSVGUtils::eUseFrameBoundsForOuterSVG |
nsSVGUtils::eBBoxIncludeFillGeometry);
}
// Bounds in the user space of aMaskedFrame
gfxRect maskArea = nsSVGUtils::GetRelativeRect(
units, &maskElem->mLengthAttributes[SVGMaskElement::ATTR_X], bbox,
aMaskedFrame);
return maskArea;
}
nsresult nsSVGMaskFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType) {
if (aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
aAttribute == nsGkAtoms::maskUnits ||
aAttribute == nsGkAtoms::maskContentUnits)) {
SVGObserverUtils::InvalidateDirectRenderingObservers(this);
}
return nsSVGContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
}
#ifdef DEBUG
void nsSVGMaskFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) {
NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::mask),
"Content is not an SVG mask");
nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
}
#endif /* DEBUG */
gfxMatrix nsSVGMaskFrame::GetCanvasTM() { return mMatrixForChildren; }
gfxMatrix nsSVGMaskFrame::GetMaskTransform(nsIFrame* aMaskedFrame) {
SVGMaskElement* content = static_cast<SVGMaskElement*>(GetContent());
SVGAnimatedEnumeration* maskContentUnits =
&content->mEnumAttributes[SVGMaskElement::MASKCONTENTUNITS];
uint32_t flags = nsSVGUtils::eBBoxIncludeFillGeometry |
(aMaskedFrame->StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Clone
? nsSVGUtils::eIncludeOnlyCurrentFrameForNonSVGElement
: 0);
return nsSVGUtils::AdjustMatrixForUnits(gfxMatrix(), maskContentUnits,
aMaskedFrame, flags);
}