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 "nsCSSFilterInstance.h"
8 :
9 : // Keep others in (case-insensitive) order:
10 : #include "gfx2DGlue.h"
11 : #include "gfxUtils.h"
12 : #include "nsIFrame.h"
13 : #include "nsStyleStruct.h"
14 : #include "nsTArray.h"
15 :
16 : using namespace mozilla;
17 : using namespace mozilla::gfx;
18 :
19 0 : static float ClampFactor(float aFactor)
20 : {
21 0 : if (aFactor > 1) {
22 0 : return 1;
23 0 : } else if (aFactor < 0) {
24 0 : NS_NOTREACHED("A negative value should not have been parsed.");
25 0 : return 0;
26 : }
27 :
28 0 : return aFactor;
29 : }
30 :
31 0 : nsCSSFilterInstance::nsCSSFilterInstance(const nsStyleFilter& aFilter,
32 : nscolor aShadowFallbackColor,
33 : const nsIntRect& aTargetBoundsInFilterSpace,
34 0 : const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform)
35 : : mFilter(aFilter)
36 : , mShadowFallbackColor(aShadowFallbackColor)
37 : , mTargetBoundsInFilterSpace(aTargetBoundsInFilterSpace)
38 0 : , mFrameSpaceInCSSPxToFilterSpaceTransform(aFrameSpaceInCSSPxToFilterSpaceTransform)
39 : {
40 0 : }
41 :
42 : nsresult
43 0 : nsCSSFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
44 : bool aInputIsTainted)
45 : {
46 0 : FilterPrimitiveDescription descr;
47 : nsresult result;
48 :
49 0 : switch(mFilter.GetType()) {
50 : case NS_STYLE_FILTER_BLUR:
51 0 : descr = CreatePrimitiveDescription(PrimitiveType::GaussianBlur,
52 : aPrimitiveDescrs,
53 0 : aInputIsTainted);
54 0 : result = SetAttributesForBlur(descr);
55 0 : break;
56 : case NS_STYLE_FILTER_BRIGHTNESS:
57 0 : descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
58 : aPrimitiveDescrs,
59 0 : aInputIsTainted);
60 0 : result = SetAttributesForBrightness(descr);
61 0 : break;
62 : case NS_STYLE_FILTER_CONTRAST:
63 0 : descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
64 : aPrimitiveDescrs,
65 0 : aInputIsTainted);
66 0 : result = SetAttributesForContrast(descr);
67 0 : break;
68 : case NS_STYLE_FILTER_DROP_SHADOW:
69 0 : descr = CreatePrimitiveDescription(PrimitiveType::DropShadow,
70 : aPrimitiveDescrs,
71 0 : aInputIsTainted);
72 0 : result = SetAttributesForDropShadow(descr);
73 0 : break;
74 : case NS_STYLE_FILTER_GRAYSCALE:
75 0 : descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
76 : aPrimitiveDescrs,
77 0 : aInputIsTainted);
78 0 : result = SetAttributesForGrayscale(descr);
79 0 : break;
80 : case NS_STYLE_FILTER_HUE_ROTATE:
81 0 : descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
82 : aPrimitiveDescrs,
83 0 : aInputIsTainted);
84 0 : result = SetAttributesForHueRotate(descr);
85 0 : break;
86 : case NS_STYLE_FILTER_INVERT:
87 0 : descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
88 : aPrimitiveDescrs,
89 0 : aInputIsTainted);
90 0 : result = SetAttributesForInvert(descr);
91 0 : break;
92 : case NS_STYLE_FILTER_OPACITY:
93 0 : descr = CreatePrimitiveDescription(PrimitiveType::ComponentTransfer,
94 : aPrimitiveDescrs,
95 0 : aInputIsTainted);
96 0 : result = SetAttributesForOpacity(descr);
97 0 : break;
98 : case NS_STYLE_FILTER_SATURATE:
99 0 : descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
100 : aPrimitiveDescrs,
101 0 : aInputIsTainted);
102 0 : result = SetAttributesForSaturate(descr);
103 0 : break;
104 : case NS_STYLE_FILTER_SEPIA:
105 0 : descr = CreatePrimitiveDescription(PrimitiveType::ColorMatrix,
106 : aPrimitiveDescrs,
107 0 : aInputIsTainted);
108 0 : result = SetAttributesForSepia(descr);
109 0 : break;
110 : default:
111 0 : NS_NOTREACHED("not a valid CSS filter type");
112 0 : return NS_ERROR_FAILURE;
113 : }
114 :
115 0 : if (NS_FAILED(result)) {
116 0 : return result;
117 : }
118 :
119 : // Compute the primitive's bounds now that we've determined its attributes.
120 : // Some attributes like blur radius can influence the bounds.
121 0 : SetBounds(descr, aPrimitiveDescrs);
122 :
123 : // Add this primitive to the filter chain.
124 0 : aPrimitiveDescrs.AppendElement(descr);
125 0 : return NS_OK;
126 : }
127 :
128 : FilterPrimitiveDescription
129 0 : nsCSSFilterInstance::CreatePrimitiveDescription(PrimitiveType aType,
130 : const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
131 : bool aInputIsTainted) {
132 0 : FilterPrimitiveDescription descr(aType);
133 0 : int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs);
134 0 : descr.SetInputPrimitive(0, inputIndex);
135 0 : descr.SetIsTainted(inputIndex < 0 ? aInputIsTainted : aPrimitiveDescrs[inputIndex].IsTainted());
136 0 : descr.SetInputColorSpace(0, ColorSpace::SRGB);
137 0 : descr.SetOutputColorSpace(ColorSpace::SRGB);
138 0 : return descr;
139 : }
140 :
141 : nsresult
142 0 : nsCSSFilterInstance::SetAttributesForBlur(FilterPrimitiveDescription& aDescr)
143 : {
144 0 : const nsStyleCoord& radiusInFrameSpace = mFilter.GetFilterParameter();
145 0 : if (radiusInFrameSpace.GetUnit() != eStyleUnit_Coord) {
146 0 : NS_NOTREACHED("unexpected unit");
147 0 : return NS_ERROR_FAILURE;
148 : }
149 :
150 0 : Size radiusInFilterSpace = BlurRadiusToFilterSpace(radiusInFrameSpace.GetCoordValue());
151 0 : aDescr.Attributes().Set(eGaussianBlurStdDeviation, radiusInFilterSpace);
152 0 : return NS_OK;
153 : }
154 :
155 : nsresult
156 0 : nsCSSFilterInstance::SetAttributesForBrightness(FilterPrimitiveDescription& aDescr)
157 : {
158 0 : const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
159 0 : float value = styleValue.GetFactorOrPercentValue();
160 :
161 : // Set transfer functions for RGB.
162 0 : AttributeMap brightnessAttrs;
163 : brightnessAttrs.Set(eComponentTransferFunctionType,
164 0 : (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR);
165 0 : brightnessAttrs.Set(eComponentTransferFunctionSlope, value);
166 0 : brightnessAttrs.Set(eComponentTransferFunctionIntercept, 0.0f);
167 0 : aDescr.Attributes().Set(eComponentTransferFunctionR, brightnessAttrs);
168 0 : aDescr.Attributes().Set(eComponentTransferFunctionG, brightnessAttrs);
169 0 : aDescr.Attributes().Set(eComponentTransferFunctionB, brightnessAttrs);
170 :
171 : // Set identity transfer function for A.
172 0 : AttributeMap identityAttrs;
173 : identityAttrs.Set(eComponentTransferFunctionType,
174 0 : (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY);
175 0 : aDescr.Attributes().Set(eComponentTransferFunctionA, identityAttrs);
176 :
177 0 : return NS_OK;
178 : }
179 :
180 : nsresult
181 0 : nsCSSFilterInstance::SetAttributesForContrast(FilterPrimitiveDescription& aDescr)
182 : {
183 0 : const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
184 0 : float value = styleValue.GetFactorOrPercentValue();
185 0 : float intercept = -(0.5 * value) + 0.5;
186 :
187 : // Set transfer functions for RGB.
188 0 : AttributeMap contrastAttrs;
189 : contrastAttrs.Set(eComponentTransferFunctionType,
190 0 : (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR);
191 0 : contrastAttrs.Set(eComponentTransferFunctionSlope, value);
192 0 : contrastAttrs.Set(eComponentTransferFunctionIntercept, intercept);
193 0 : aDescr.Attributes().Set(eComponentTransferFunctionR, contrastAttrs);
194 0 : aDescr.Attributes().Set(eComponentTransferFunctionG, contrastAttrs);
195 0 : aDescr.Attributes().Set(eComponentTransferFunctionB, contrastAttrs);
196 :
197 : // Set identity transfer function for A.
198 0 : AttributeMap identityAttrs;
199 : identityAttrs.Set(eComponentTransferFunctionType,
200 0 : (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY);
201 0 : aDescr.Attributes().Set(eComponentTransferFunctionA, identityAttrs);
202 :
203 0 : return NS_OK;
204 : }
205 :
206 : nsresult
207 0 : nsCSSFilterInstance::SetAttributesForDropShadow(FilterPrimitiveDescription& aDescr)
208 : {
209 0 : nsCSSShadowArray* shadows = mFilter.GetDropShadow();
210 0 : if (!shadows || shadows->Length() != 1) {
211 0 : NS_NOTREACHED("Exactly one drop shadow should have been parsed.");
212 0 : return NS_ERROR_FAILURE;
213 : }
214 :
215 0 : nsCSSShadowItem* shadow = shadows->ShadowAt(0);
216 :
217 : // Set drop shadow blur radius.
218 0 : Size radiusInFilterSpace = BlurRadiusToFilterSpace(shadow->mRadius);
219 0 : aDescr.Attributes().Set(eDropShadowStdDeviation, radiusInFilterSpace);
220 :
221 : // Set offset.
222 0 : IntPoint offsetInFilterSpace = OffsetToFilterSpace(shadow->mXOffset, shadow->mYOffset);
223 0 : aDescr.Attributes().Set(eDropShadowOffset, offsetInFilterSpace);
224 :
225 : // Set color. If unspecified, use the CSS color property.
226 0 : nscolor shadowColor = shadow->mHasColor ? shadow->mColor : mShadowFallbackColor;
227 0 : aDescr.Attributes().Set(eDropShadowColor, ToAttributeColor(shadowColor));
228 :
229 0 : return NS_OK;
230 : }
231 :
232 : nsresult
233 0 : nsCSSFilterInstance::SetAttributesForGrayscale(FilterPrimitiveDescription& aDescr)
234 : {
235 : // Set color matrix type.
236 0 : aDescr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE);
237 :
238 : // Set color matrix values.
239 0 : const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
240 0 : float value = 1 - ClampFactor(styleValue.GetFactorOrPercentValue());
241 0 : aDescr.Attributes().Set(eColorMatrixValues, &value, 1);
242 :
243 0 : return NS_OK;
244 : }
245 :
246 : nsresult
247 0 : nsCSSFilterInstance::SetAttributesForHueRotate(FilterPrimitiveDescription& aDescr)
248 : {
249 : // Set color matrix type.
250 0 : aDescr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_HUE_ROTATE);
251 :
252 : // Set color matrix values.
253 0 : const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
254 0 : float value = styleValue.GetAngleValueInDegrees();
255 0 : aDescr.Attributes().Set(eColorMatrixValues, &value, 1);
256 :
257 0 : return NS_OK;
258 : }
259 :
260 : nsresult
261 0 : nsCSSFilterInstance::SetAttributesForInvert(FilterPrimitiveDescription& aDescr)
262 : {
263 0 : const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
264 0 : float value = ClampFactor(styleValue.GetFactorOrPercentValue());
265 :
266 : // Set transfer functions for RGB.
267 0 : AttributeMap invertAttrs;
268 : float invertTableValues[2];
269 0 : invertTableValues[0] = value;
270 0 : invertTableValues[1] = 1 - value;
271 : invertAttrs.Set(eComponentTransferFunctionType,
272 0 : (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_TABLE);
273 0 : invertAttrs.Set(eComponentTransferFunctionTableValues, invertTableValues, 2);
274 0 : aDescr.Attributes().Set(eComponentTransferFunctionR, invertAttrs);
275 0 : aDescr.Attributes().Set(eComponentTransferFunctionG, invertAttrs);
276 0 : aDescr.Attributes().Set(eComponentTransferFunctionB, invertAttrs);
277 :
278 : // Set identity transfer function for A.
279 0 : AttributeMap identityAttrs;
280 : identityAttrs.Set(eComponentTransferFunctionType,
281 0 : (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY);
282 0 : aDescr.Attributes().Set(eComponentTransferFunctionA, identityAttrs);
283 :
284 0 : return NS_OK;
285 : }
286 :
287 : nsresult
288 0 : nsCSSFilterInstance::SetAttributesForOpacity(FilterPrimitiveDescription& aDescr)
289 : {
290 0 : const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
291 0 : float value = ClampFactor(styleValue.GetFactorOrPercentValue());
292 :
293 : // Set identity transfer functions for RGB.
294 0 : AttributeMap identityAttrs;
295 : identityAttrs.Set(eComponentTransferFunctionType,
296 0 : (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY);
297 0 : aDescr.Attributes().Set(eComponentTransferFunctionR, identityAttrs);
298 0 : aDescr.Attributes().Set(eComponentTransferFunctionG, identityAttrs);
299 0 : aDescr.Attributes().Set(eComponentTransferFunctionB, identityAttrs);
300 :
301 : // Set transfer function for A.
302 0 : AttributeMap opacityAttrs;
303 : float opacityTableValues[2];
304 0 : opacityTableValues[0] = 0;
305 0 : opacityTableValues[1] = value;
306 : opacityAttrs.Set(eComponentTransferFunctionType,
307 0 : (uint32_t)SVG_FECOMPONENTTRANSFER_TYPE_TABLE);
308 0 : opacityAttrs.Set(eComponentTransferFunctionTableValues, opacityTableValues, 2);
309 0 : aDescr.Attributes().Set(eComponentTransferFunctionA, opacityAttrs);
310 :
311 0 : return NS_OK;
312 : }
313 :
314 : nsresult
315 0 : nsCSSFilterInstance::SetAttributesForSaturate(FilterPrimitiveDescription& aDescr)
316 : {
317 : // Set color matrix type.
318 0 : aDescr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE);
319 :
320 : // Set color matrix values.
321 0 : const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
322 0 : float value = styleValue.GetFactorOrPercentValue();
323 0 : aDescr.Attributes().Set(eColorMatrixValues, &value, 1);
324 :
325 0 : return NS_OK;
326 : }
327 :
328 : nsresult
329 0 : nsCSSFilterInstance::SetAttributesForSepia(FilterPrimitiveDescription& aDescr)
330 : {
331 : // Set color matrix type.
332 0 : aDescr.Attributes().Set(eColorMatrixType, (uint32_t)SVG_FECOLORMATRIX_TYPE_SEPIA);
333 :
334 : // Set color matrix values.
335 0 : const nsStyleCoord& styleValue = mFilter.GetFilterParameter();
336 0 : float value = ClampFactor(styleValue.GetFactorOrPercentValue());
337 0 : aDescr.Attributes().Set(eColorMatrixValues, &value, 1);
338 :
339 0 : return NS_OK;
340 : }
341 :
342 : Size
343 0 : nsCSSFilterInstance::BlurRadiusToFilterSpace(nscoord aRadiusInFrameSpace)
344 : {
345 : float radiusInFrameSpaceInCSSPx =
346 0 : nsPresContext::AppUnitsToFloatCSSPixels(aRadiusInFrameSpace);
347 :
348 : // Convert the radius to filter space.
349 : Size radiusInFilterSpace(radiusInFrameSpaceInCSSPx,
350 0 : radiusInFrameSpaceInCSSPx);
351 : gfxSize frameSpaceInCSSPxToFilterSpaceScale =
352 0 : mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors(true);
353 0 : radiusInFilterSpace.Scale(frameSpaceInCSSPxToFilterSpaceScale.width,
354 0 : frameSpaceInCSSPxToFilterSpaceScale.height);
355 :
356 : // Check the radius limits.
357 0 : if (radiusInFilterSpace.width < 0 || radiusInFilterSpace.height < 0) {
358 0 : NS_NOTREACHED("we shouldn't have parsed a negative radius in the style");
359 0 : return Size();
360 : }
361 0 : Float maxStdDeviation = (Float)kMaxStdDeviation;
362 0 : radiusInFilterSpace.width = std::min(radiusInFilterSpace.width, maxStdDeviation);
363 0 : radiusInFilterSpace.height = std::min(radiusInFilterSpace.height, maxStdDeviation);
364 :
365 0 : return radiusInFilterSpace;
366 : }
367 :
368 : IntPoint
369 0 : nsCSSFilterInstance::OffsetToFilterSpace(nscoord aXOffsetInFrameSpace,
370 : nscoord aYOffsetInFrameSpace)
371 : {
372 0 : gfxPoint offsetInFilterSpace(nsPresContext::AppUnitsToFloatCSSPixels(aXOffsetInFrameSpace),
373 0 : nsPresContext::AppUnitsToFloatCSSPixels(aYOffsetInFrameSpace));
374 :
375 : // Convert the radius to filter space.
376 : gfxSize frameSpaceInCSSPxToFilterSpaceScale =
377 0 : mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors(true);
378 0 : offsetInFilterSpace.x *= frameSpaceInCSSPxToFilterSpaceScale.width;
379 0 : offsetInFilterSpace.y *= frameSpaceInCSSPxToFilterSpaceScale.height;
380 :
381 0 : return IntPoint(int32_t(offsetInFilterSpace.x), int32_t(offsetInFilterSpace.y));
382 : }
383 :
384 : Color
385 0 : nsCSSFilterInstance::ToAttributeColor(nscolor aColor)
386 : {
387 0 : return Color(
388 0 : NS_GET_R(aColor) / 255.0,
389 0 : NS_GET_G(aColor) / 255.0,
390 0 : NS_GET_B(aColor) / 255.0,
391 0 : NS_GET_A(aColor) / 255.0
392 0 : );
393 : }
394 :
395 : int32_t
396 0 : nsCSSFilterInstance::GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
397 : {
398 0 : uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length();
399 0 : return !numPrimitiveDescrs ?
400 : FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic :
401 0 : numPrimitiveDescrs - 1;
402 : }
403 :
404 : void
405 0 : nsCSSFilterInstance::SetBounds(FilterPrimitiveDescription& aDescr,
406 : const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
407 : {
408 0 : int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs);
409 : nsIntRect inputBounds = (inputIndex < 0) ?
410 0 : mTargetBoundsInFilterSpace : aPrimitiveDescrs[inputIndex].PrimitiveSubregion();
411 :
412 0 : nsTArray<nsIntRegion> inputExtents;
413 0 : inputExtents.AppendElement(inputBounds);
414 :
415 : nsIntRegion outputExtents =
416 0 : FilterSupport::PostFilterExtentsForPrimitive(aDescr, inputExtents);
417 0 : IntRect outputBounds = outputExtents.GetBounds();
418 :
419 0 : aDescr.SetPrimitiveSubregion(outputBounds);
420 0 : aDescr.SetFilterSpaceBounds(outputBounds);
421 0 : }
|