Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : // Main header first:
7 : #include "nsSVGMaskFrame.h"
8 :
9 : // Keep others in (case-insensitive) order:
10 : #include "AutoReferenceChainGuard.h"
11 : #include "gfx2DGlue.h"
12 : #include "gfxContext.h"
13 : #include "mozilla/gfx/2D.h"
14 : #include "mozilla/RefPtr.h"
15 : #include "nsSVGEffects.h"
16 : #include "mozilla/dom/SVGMaskElement.h"
17 :
18 : using namespace mozilla;
19 : using namespace mozilla::dom;
20 : using namespace mozilla::gfx;
21 : using namespace mozilla::image;
22 :
23 : static LuminanceType
24 0 : GetLuminanceType(uint8_t aNSMaskType)
25 : {
26 0 : switch (aNSMaskType) {
27 : case NS_STYLE_MASK_TYPE_LUMINANCE:
28 0 : return LuminanceType::LUMINANCE;
29 : case NS_STYLE_COLOR_INTERPOLATION_LINEARRGB:
30 0 : return LuminanceType::LINEARRGB;
31 : default:
32 : {
33 0 : NS_WARNING("Unknown SVG mask type, defaulting to luminance");
34 0 : return LuminanceType::LUMINANCE;
35 : }
36 : }
37 : }
38 :
39 : nsIFrame*
40 2 : NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
41 : {
42 2 : return new (aPresShell) nsSVGMaskFrame(aContext);
43 : }
44 :
45 2 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGMaskFrame)
46 :
47 : already_AddRefed<SourceSurface>
48 0 : nsSVGMaskFrame::GetMaskForMaskedFrame(MaskParams& aParams)
49 : {
50 : // Make sure we break reference loops and over long reference chains:
51 : static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
52 : AutoReferenceChainGuard refChainGuard(this, &mInUse,
53 0 : &sRefChainLengthCounter);
54 0 : if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
55 : // Break reference chain
56 0 : return nullptr;
57 : }
58 :
59 0 : gfxRect maskArea = GetMaskArea(aParams.maskedFrame);
60 0 : gfxContext* context = aParams.ctx;
61 : // Get the clip extents in device space:
62 : // Minimizing the mask surface extents (using both the current clip extents
63 : // and maskArea) is important for performance.
64 0 : context->Save();
65 0 : nsSVGUtils::SetClipRect(context, aParams.toUserSpace, maskArea);
66 0 : context->SetMatrix(gfxMatrix());
67 0 : gfxRect maskSurfaceRect = context->GetClipExtents();
68 0 : maskSurfaceRect.RoundOut();
69 0 : context->Restore();
70 :
71 : bool resultOverflows;
72 : IntSize maskSurfaceSize =
73 0 : nsSVGUtils::ConvertToSurfaceSize(maskSurfaceRect.Size(), &resultOverflows);
74 :
75 0 : if (resultOverflows || maskSurfaceSize.IsEmpty()) {
76 : // Return value other then DrawResult::SUCCESS, so the caller can skip
77 : // painting the masked frame(aParams.maskedFrame).
78 0 : return nullptr;
79 : }
80 :
81 : uint8_t maskType;
82 0 : if (aParams.maskMode == NS_STYLE_MASK_MODE_MATCH_SOURCE) {
83 0 : maskType = StyleSVGReset()->mMaskType;
84 : } else {
85 0 : maskType = aParams.maskMode == NS_STYLE_MASK_MODE_LUMINANCE
86 0 : ? NS_STYLE_MASK_TYPE_LUMINANCE : NS_STYLE_MASK_TYPE_ALPHA;
87 : }
88 :
89 0 : RefPtr<DrawTarget> maskDT;
90 0 : if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
91 0 : maskDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
92 0 : maskSurfaceSize, SurfaceFormat::B8G8R8A8);
93 : } else {
94 0 : maskDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
95 0 : maskSurfaceSize, SurfaceFormat::A8);
96 : }
97 :
98 0 : if (!maskDT || !maskDT->IsValid()) {
99 0 : return nullptr;
100 : }
101 :
102 : gfxMatrix maskSurfaceMatrix =
103 0 : context->CurrentMatrix() * gfxMatrix::Translation(-maskSurfaceRect.TopLeft());
104 :
105 0 : RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(maskDT);
106 0 : MOZ_ASSERT(tmpCtx); // already checked the draw target above
107 0 : tmpCtx->SetMatrix(maskSurfaceMatrix);
108 :
109 0 : mMatrixForChildren = GetMaskTransform(aParams.maskedFrame) *
110 0 : aParams.toUserSpace;
111 :
112 0 : for (nsIFrame* kid = mFrames.FirstChild(); kid;
113 : kid = kid->GetNextSibling()) {
114 : // The CTM of each frame referencing us can be different
115 0 : nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
116 0 : if (SVGFrame) {
117 0 : SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
118 : }
119 0 : gfxMatrix m = mMatrixForChildren;
120 0 : if (kid->GetContent()->IsSVGElement()) {
121 0 : m = static_cast<nsSVGElement*>(kid->GetContent())->
122 0 : PrependLocalTransformsTo(m, eUserSpaceToParent);
123 : }
124 0 : nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m, aParams.imgParams);
125 : }
126 :
127 0 : RefPtr<SourceSurface> surface;
128 0 : if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
129 0 : if (StyleSVG()->mColorInterpolation ==
130 : NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
131 0 : maskType = NS_STYLE_COLOR_INTERPOLATION_LINEARRGB;
132 : }
133 :
134 : RefPtr<SourceSurface> maskSnapshot =
135 0 : maskDT->IntoLuminanceSource(GetLuminanceType(maskType),
136 0 : aParams.opacity);
137 0 : if (!maskSnapshot) {
138 0 : return nullptr;
139 : }
140 0 : surface = maskSnapshot.forget();
141 : } else {
142 0 : maskDT->SetTransform(Matrix());
143 0 : maskDT->FillRect(Rect(0, 0, maskSurfaceSize.width, maskSurfaceSize.height), ColorPattern(Color(1.0f, 1.0f, 1.0f, aParams.opacity)), DrawOptions(1, CompositionOp::OP_IN));
144 0 : RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot();
145 0 : if (!maskSnapshot) {
146 0 : return nullptr;
147 : }
148 0 : surface = maskSnapshot.forget();
149 : }
150 :
151 : // Moz2D transforms in the opposite direction to Thebes
152 0 : if (!maskSurfaceMatrix.Invert()) {
153 0 : return nullptr;
154 : }
155 :
156 0 : *aParams.maskTransform = ToMatrix(maskSurfaceMatrix);
157 0 : return surface.forget();
158 : }
159 :
160 : gfxRect
161 0 : nsSVGMaskFrame::GetMaskArea(nsIFrame* aMaskedFrame)
162 : {
163 0 : SVGMaskElement *maskElem = static_cast<SVGMaskElement*>(mContent);
164 :
165 : uint16_t units =
166 0 : maskElem->mEnumAttributes[SVGMaskElement::MASKUNITS].GetAnimValue();
167 0 : gfxRect bbox;
168 0 : if (units == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
169 : bbox =
170 : nsSVGUtils::GetBBox(aMaskedFrame,
171 : nsSVGUtils::eUseFrameBoundsForOuterSVG |
172 0 : nsSVGUtils::eBBoxIncludeFillGeometry);
173 : }
174 :
175 : // Bounds in the user space of aMaskedFrame
176 : gfxRect maskArea = nsSVGUtils::GetRelativeRect(units,
177 0 : &maskElem->mLengthAttributes[SVGMaskElement::ATTR_X],
178 0 : bbox, aMaskedFrame);
179 :
180 0 : return maskArea;
181 : }
182 :
183 : nsresult
184 0 : nsSVGMaskFrame::AttributeChanged(int32_t aNameSpaceID,
185 : nsIAtom* aAttribute,
186 : int32_t aModType)
187 : {
188 0 : if (aNameSpaceID == kNameSpaceID_None &&
189 0 : (aAttribute == nsGkAtoms::x ||
190 0 : aAttribute == nsGkAtoms::y ||
191 0 : aAttribute == nsGkAtoms::width ||
192 0 : aAttribute == nsGkAtoms::height||
193 0 : aAttribute == nsGkAtoms::maskUnits ||
194 0 : aAttribute == nsGkAtoms::maskContentUnits)) {
195 0 : nsSVGEffects::InvalidateDirectRenderingObservers(this);
196 : }
197 :
198 0 : return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
199 0 : aAttribute, aModType);
200 : }
201 :
202 : #ifdef DEBUG
203 : void
204 2 : nsSVGMaskFrame::Init(nsIContent* aContent,
205 : nsContainerFrame* aParent,
206 : nsIFrame* aPrevInFlow)
207 : {
208 2 : NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::mask),
209 : "Content is not an SVG mask");
210 :
211 2 : nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
212 2 : }
213 : #endif /* DEBUG */
214 :
215 : gfxMatrix
216 0 : nsSVGMaskFrame::GetCanvasTM()
217 : {
218 0 : return mMatrixForChildren;
219 : }
220 :
221 : gfxMatrix
222 0 : nsSVGMaskFrame::GetMaskTransform(nsIFrame* aMaskedFrame)
223 : {
224 0 : SVGMaskElement *content = static_cast<SVGMaskElement*>(mContent);
225 :
226 : nsSVGEnum* maskContentUnits =
227 0 : &content->mEnumAttributes[SVGMaskElement::MASKCONTENTUNITS];
228 :
229 0 : return nsSVGUtils::AdjustMatrixForUnits(gfxMatrix(), maskContentUnits,
230 0 : aMaskedFrame);
231 : }
|