summaryrefslogtreecommitdiffstats
path: root/layout/svg/SVGMaskFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--layout/svg/SVGMaskFrame.cpp190
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