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 gfxContext
* context
= aParams
.ctx
;
52 // Get the clip extents in device space:
53 // Minimizing the mask surface extents (using both the current clip extents
54 // and maskArea) is important for performance.
56 gfxRect maskSurfaceRectDouble
= aParams
.toUserSpace
.TransformBounds(maskArea
);
57 Rect maskSurfaceRect
= ToRect(maskSurfaceRectDouble
);
58 maskSurfaceRect
.RoundOut();
60 StyleMaskType maskType
;
61 if (aParams
.maskMode
== StyleMaskMode::MatchSource
) {
62 maskType
= StyleSVGReset()->mMaskType
;
64 maskType
= aParams
.maskMode
== StyleMaskMode::Luminance
65 ? StyleMaskType::Luminance
66 : StyleMaskType::Alpha
;
69 RefPtr
<DrawTarget
> maskDT
;
70 if (maskType
== StyleMaskType::Luminance
) {
71 maskDT
= context
->GetDrawTarget()->CreateClippedDrawTarget(
72 maskSurfaceRect
, SurfaceFormat::B8G8R8A8
);
74 maskDT
= context
->GetDrawTarget()->CreateClippedDrawTarget(
75 maskSurfaceRect
, SurfaceFormat::A8
);
78 if (!maskDT
|| !maskDT
->IsValid()) {
82 RefPtr
<gfxContext
> tmpCtx
=
83 gfxContext::CreatePreservingTransformOrNull(maskDT
);
84 MOZ_ASSERT(tmpCtx
); // already checked the draw target above
87 GetMaskTransform(aParams
.maskedFrame
) * aParams
.toUserSpace
;
89 for (nsIFrame
* kid
= mFrames
.FirstChild(); kid
; kid
= kid
->GetNextSibling()) {
90 gfxMatrix m
= mMatrixForChildren
;
92 // The CTM of each frame referencing us can be different
93 ISVGDisplayableFrame
* SVGFrame
= do_QueryFrame(kid
);
95 SVGFrame
->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED
);
96 m
= SVGUtils::GetTransformMatrixInUserSpace(kid
) * m
;
99 SVGUtils::PaintFrameWithEffects(kid
, *tmpCtx
, m
, aParams
.imgParams
);
102 RefPtr
<SourceSurface
> surface
;
103 if (maskType
== StyleMaskType::Luminance
) {
104 auto luminanceType
= LuminanceType::LUMINANCE
;
105 if (StyleSVG()->mColorInterpolation
== StyleColorInterpolation::Linearrgb
) {
106 luminanceType
= LuminanceType::LINEARRGB
;
109 RefPtr
<SourceSurface
> maskSnapshot
=
110 maskDT
->IntoLuminanceSource(luminanceType
, aParams
.opacity
);
114 surface
= std::move(maskSnapshot
);
116 maskDT
->FillRect(maskSurfaceRect
,
117 ColorPattern(DeviceColor::MaskWhite(aParams
.opacity
)),
118 DrawOptions(1, CompositionOp::OP_IN
));
119 RefPtr
<SourceSurface
> maskSnapshot
= maskDT
->Snapshot();
123 surface
= std::move(maskSnapshot
);
126 return surface
.forget();
129 gfxRect
SVGMaskFrame::GetMaskArea(nsIFrame
* aMaskedFrame
) {
130 SVGMaskElement
* maskElem
= static_cast<SVGMaskElement
*>(GetContent());
133 maskElem
->mEnumAttributes
[SVGMaskElement::MASKUNITS
].GetAnimValue();
135 if (units
== SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
137 SVGUtils::GetBBox(aMaskedFrame
, SVGUtils::eUseFrameBoundsForOuterSVG
|
138 SVGUtils::eBBoxIncludeFillGeometry
);
141 // Bounds in the user space of aMaskedFrame
142 gfxRect maskArea
= SVGUtils::GetRelativeRect(
143 units
, &maskElem
->mLengthAttributes
[SVGMaskElement::ATTR_X
], bbox
,
149 nsresult
SVGMaskFrame::AttributeChanged(int32_t aNameSpaceID
,
150 nsAtom
* aAttribute
, int32_t aModType
) {
151 if (aNameSpaceID
== kNameSpaceID_None
&&
152 (aAttribute
== nsGkAtoms::x
|| aAttribute
== nsGkAtoms::y
||
153 aAttribute
== nsGkAtoms::width
|| aAttribute
== nsGkAtoms::height
||
154 aAttribute
== nsGkAtoms::maskUnits
||
155 aAttribute
== nsGkAtoms::maskContentUnits
)) {
156 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
159 return SVGContainerFrame::AttributeChanged(aNameSpaceID
, aAttribute
,
164 void SVGMaskFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
165 nsIFrame
* aPrevInFlow
) {
166 NS_ASSERTION(aContent
->IsSVGElement(nsGkAtoms::mask
),
167 "Content is not an SVG mask");
169 SVGContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
173 gfxMatrix
SVGMaskFrame::GetCanvasTM() { return mMatrixForChildren
; }
175 gfxMatrix
SVGMaskFrame::GetMaskTransform(nsIFrame
* aMaskedFrame
) {
176 SVGMaskElement
* content
= static_cast<SVGMaskElement
*>(GetContent());
178 SVGAnimatedEnumeration
* maskContentUnits
=
179 &content
->mEnumAttributes
[SVGMaskElement::MASKCONTENTUNITS
];
181 uint32_t flags
= SVGUtils::eBBoxIncludeFillGeometry
|
182 (aMaskedFrame
->StyleBorder()->mBoxDecorationBreak
==
183 StyleBoxDecorationBreak::Clone
184 ? SVGUtils::eIncludeOnlyCurrentFrameForNonSVGElement
187 return SVGUtils::AdjustMatrixForUnits(gfxMatrix(), maskContentUnits
,
188 aMaskedFrame
, flags
);
191 } // namespace mozilla