diff options
Diffstat (limited to '')
-rw-r--r-- | layout/svg/SVGMaskFrame.cpp | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/layout/svg/SVGMaskFrame.cpp b/layout/svg/SVGMaskFrame.cpp new file mode 100644 index 0000000000..de192823d5 --- /dev/null +++ b/layout/svg/SVGMaskFrame.cpp @@ -0,0 +1,190 @@ +/* -*- 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 "SVGMaskFrame.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/SVGObserverUtils.h" +#include "mozilla/SVGUtils.h" +#include "mozilla/dom/SVGMaskElement.h" +#include "mozilla/dom/SVGUnitTypesBinding.h" +#include "mozilla/gfx/2D.h" + +using namespace mozilla::dom; +using namespace mozilla::dom::SVGUnitTypes_Binding; +using namespace mozilla::gfx; +using namespace mozilla::image; + +nsIFrame* NS_NewSVGMaskFrame(mozilla::PresShell* aPresShell, + mozilla::ComputedStyle* aStyle) { + return new (aPresShell) + mozilla::SVGMaskFrame(aStyle, aPresShell->GetPresContext()); +} + +namespace mozilla { + +NS_IMPL_FRAMEARENA_HELPERS(SVGMaskFrame) + +already_AddRefed<SourceSurface> SVGMaskFrame::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; + } + // 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(); + + StyleMaskType maskType; + if (aParams.maskMode == StyleMaskMode::MatchSource) { + maskType = StyleSVGReset()->mMaskType; + } else { + maskType = aParams.maskMode == StyleMaskMode::Luminance + ? StyleMaskType::Luminance + : StyleMaskType::Alpha; + } + + RefPtr<DrawTarget> maskDT; + if (maskType == StyleMaskType::Luminance) { + maskDT = aParams.dt->CreateClippedDrawTarget(maskSurfaceRect, + SurfaceFormat::B8G8R8A8); + } else { + maskDT = + aParams.dt->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 + ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); + if (SVGFrame) { + SVGFrame->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED); + m = SVGUtils::GetTransformMatrixInUserSpace(kid) * m; + } + + SVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m, aParams.imgParams); + } + + RefPtr<SourceSurface> surface; + if (maskType == StyleMaskType::Luminance) { + auto luminanceType = LuminanceType::LUMINANCE; + if (StyleSVG()->mColorInterpolation == StyleColorInterpolation::Linearrgb) { + luminanceType = LuminanceType::LINEARRGB; + } + + RefPtr<SourceSurface> maskSnapshot = + maskDT->IntoLuminanceSource(luminanceType, aParams.opacity); + if (!maskSnapshot) { + return nullptr; + } + surface = std::move(maskSnapshot); + } else { + maskDT->FillRect(maskSurfaceRect, + ColorPattern(DeviceColor::MaskWhite(aParams.opacity)), + DrawOptions(1, CompositionOp::OP_IN)); + RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot(); + if (!maskSnapshot) { + return nullptr; + } + surface = std::move(maskSnapshot); + } + + return surface.forget(); +} + +gfxRect SVGMaskFrame::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 = + SVGUtils::GetBBox(aMaskedFrame, SVGUtils::eUseFrameBoundsForOuterSVG | + SVGUtils::eBBoxIncludeFillGeometry); + } + + // Bounds in the user space of aMaskedFrame + gfxRect maskArea = SVGUtils::GetRelativeRect( + units, &maskElem->mLengthAttributes[SVGMaskElement::ATTR_X], bbox, + aMaskedFrame); + + return maskArea; +} + +nsresult SVGMaskFrame::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 SVGContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, + aModType); +} + +#ifdef DEBUG +void SVGMaskFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) { + NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::mask), + "Content is not an SVG mask"); + + SVGContainerFrame::Init(aContent, aParent, aPrevInFlow); +} +#endif /* DEBUG */ + +gfxMatrix SVGMaskFrame::GetCanvasTM() { return mMatrixForChildren; } + +gfxMatrix SVGMaskFrame::GetMaskTransform(nsIFrame* aMaskedFrame) { + SVGMaskElement* content = static_cast<SVGMaskElement*>(GetContent()); + + SVGAnimatedEnumeration* maskContentUnits = + &content->mEnumAttributes[SVGMaskElement::MASKCONTENTUNITS]; + + uint32_t flags = SVGUtils::eBBoxIncludeFillGeometry | + (aMaskedFrame->StyleBorder()->mBoxDecorationBreak == + StyleBoxDecorationBreak::Clone + ? SVGUtils::eIncludeOnlyCurrentFrameForNonSVGElement + : 0); + + return SVGUtils::AdjustMatrixForUnits(gfxMatrix(), maskContentUnits, + aMaskedFrame, flags); +} + +} // namespace mozilla |