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