1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "SVGMaskFrame.h"
10 // Keep others in (case-insensitive) order:
11 #include "AutoReferenceChainGuard.h"
12 #include "gfx2DGlue.h"
13 #include "gfxContext.h"
14 #include "mozilla/PresShell.h"
15 #include "mozilla/RefPtr.h"
16 #include "mozilla/SVGObserverUtils.h"
17 #include "mozilla/SVGUtils.h"
18 #include "mozilla/dom/SVGMaskElement.h"
19 #include "mozilla/dom/SVGUnitTypesBinding.h"
20 #include "mozilla/gfx/2D.h"
22 using namespace mozilla::dom
;
23 using namespace mozilla::dom::SVGUnitTypes_Binding
;
24 using namespace mozilla::gfx
;
25 using namespace mozilla::image
;
27 nsIFrame
* NS_NewSVGMaskFrame(mozilla::PresShell
* aPresShell
,
28 mozilla::ComputedStyle
* aStyle
) {
29 return new (aPresShell
)
30 mozilla::SVGMaskFrame(aStyle
, aPresShell
->GetPresContext());
35 NS_IMPL_FRAMEARENA_HELPERS(SVGMaskFrame
)
37 already_AddRefed
<SourceSurface
> SVGMaskFrame::GetMaskForMaskedFrame(
38 MaskParams
& aParams
) {
39 // Make sure we break reference loops and over long reference chains:
40 static int16_t sRefChainLengthCounter
= AutoReferenceChainGuard::noChain
;
41 AutoReferenceChainGuard
refChainGuard(this, &mInUse
, &sRefChainLengthCounter
);
42 if (MOZ_UNLIKELY(!refChainGuard
.Reference())) {
43 // Break reference chain
47 gfxRect maskArea
= GetMaskArea(aParams
.maskedFrame
);
48 if (maskArea
.IsEmpty()) {
51 // Get the clip extents in device space:
52 // Minimizing the mask surface extents (using both the current clip extents
53 // and maskArea) is important for performance.
55 gfxRect maskSurfaceRectDouble
= aParams
.toUserSpace
.TransformBounds(maskArea
);
56 Rect maskSurfaceRect
= ToRect(maskSurfaceRectDouble
);
57 maskSurfaceRect
.RoundOut();
59 StyleMaskType maskType
;
60 if (aParams
.maskMode
== StyleMaskMode::MatchSource
) {
61 maskType
= StyleSVGReset()->mMaskType
;
63 maskType
= aParams
.maskMode
== StyleMaskMode::Luminance
64 ? StyleMaskType::Luminance
65 : StyleMaskType::Alpha
;
68 RefPtr
<DrawTarget
> maskDT
;
69 if (maskType
== StyleMaskType::Luminance
) {
70 maskDT
= aParams
.dt
->CreateClippedDrawTarget(maskSurfaceRect
,
71 SurfaceFormat::B8G8R8A8
);
74 aParams
.dt
->CreateClippedDrawTarget(maskSurfaceRect
, SurfaceFormat::A8
);
77 if (!maskDT
|| !maskDT
->IsValid()) {
81 gfxContext
tmpCtx(maskDT
, /* aPreserveTransform */ true);
84 GetMaskTransform(aParams
.maskedFrame
) * aParams
.toUserSpace
;
86 for (nsIFrame
* kid
= mFrames
.FirstChild(); kid
; kid
= kid
->GetNextSibling()) {
87 gfxMatrix m
= mMatrixForChildren
;
89 // The CTM of each frame referencing us can be different
90 ISVGDisplayableFrame
* SVGFrame
= do_QueryFrame(kid
);
92 SVGFrame
->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED
);
93 m
= SVGUtils::GetTransformMatrixInUserSpace(kid
) * m
;
96 SVGUtils::PaintFrameWithEffects(kid
, tmpCtx
, m
, aParams
.imgParams
);
99 RefPtr
<SourceSurface
> surface
;
100 if (maskType
== StyleMaskType::Luminance
) {
101 auto luminanceType
= LuminanceType::LUMINANCE
;
102 if (StyleSVG()->mColorInterpolation
== StyleColorInterpolation::Linearrgb
) {
103 luminanceType
= LuminanceType::LINEARRGB
;
106 RefPtr
<SourceSurface
> maskSnapshot
=
107 maskDT
->IntoLuminanceSource(luminanceType
, aParams
.opacity
);
111 surface
= std::move(maskSnapshot
);
113 maskDT
->FillRect(maskSurfaceRect
,
114 ColorPattern(DeviceColor::MaskWhite(aParams
.opacity
)),
115 DrawOptions(1, CompositionOp::OP_IN
));
116 RefPtr
<SourceSurface
> maskSnapshot
= maskDT
->Snapshot();
120 surface
= std::move(maskSnapshot
);
123 return surface
.forget();
126 gfxRect
SVGMaskFrame::GetMaskArea(nsIFrame
* aMaskedFrame
) {
127 SVGMaskElement
* maskElem
= static_cast<SVGMaskElement
*>(GetContent());
130 maskElem
->mEnumAttributes
[SVGMaskElement::MASKUNITS
].GetAnimValue();
132 if (units
== SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
134 SVGUtils::GetBBox(aMaskedFrame
, SVGUtils::eUseFrameBoundsForOuterSVG
|
135 SVGUtils::eBBoxIncludeFillGeometry
);
138 // Bounds in the user space of aMaskedFrame
139 gfxRect maskArea
= SVGUtils::GetRelativeRect(
140 units
, &maskElem
->mLengthAttributes
[SVGMaskElement::ATTR_X
], bbox
,
146 nsresult
SVGMaskFrame::AttributeChanged(int32_t aNameSpaceID
,
147 nsAtom
* aAttribute
, int32_t aModType
) {
148 if (aNameSpaceID
== kNameSpaceID_None
&&
149 (aAttribute
== nsGkAtoms::x
|| aAttribute
== nsGkAtoms::y
||
150 aAttribute
== nsGkAtoms::width
|| aAttribute
== nsGkAtoms::height
||
151 aAttribute
== nsGkAtoms::maskUnits
||
152 aAttribute
== nsGkAtoms::maskContentUnits
)) {
153 SVGObserverUtils::InvalidateRenderingObservers(this);
156 return SVGContainerFrame::AttributeChanged(aNameSpaceID
, aAttribute
,
161 void SVGMaskFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
162 nsIFrame
* aPrevInFlow
) {
163 NS_ASSERTION(aContent
->IsSVGElement(nsGkAtoms::mask
),
164 "Content is not an SVG mask");
166 SVGContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
170 gfxMatrix
SVGMaskFrame::GetCanvasTM() { return mMatrixForChildren
; }
172 gfxMatrix
SVGMaskFrame::GetMaskTransform(nsIFrame
* aMaskedFrame
) {
173 SVGMaskElement
* content
= static_cast<SVGMaskElement
*>(GetContent());
175 SVGAnimatedEnumeration
* maskContentUnits
=
176 &content
->mEnumAttributes
[SVGMaskElement::MASKCONTENTUNITS
];
178 uint32_t flags
= SVGUtils::eBBoxIncludeFillGeometry
|
179 (aMaskedFrame
->StyleBorder()->mBoxDecorationBreak
==
180 StyleBoxDecorationBreak::Clone
181 ? SVGUtils::eIncludeOnlyCurrentFrameForNonSVGElement
184 return SVGUtils::AdjustMatrixForUnits(gfxMatrix(), maskContentUnits
,
185 aMaskedFrame
, flags
);
188 } // namespace mozilla