nsCSSFilterInstance.cpp 11.8 KB
Newer Older
1
2
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
4
5
6
7
8
9
10
11
12
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Main header first:
#include "nsCSSFilterInstance.h"

// Keep others in (case-insensitive) order:
#include "gfx2DGlue.h"
#include "gfxUtils.h"
13
#include "nsIFrame.h"
14
15
16
17
18
19
#include "nsStyleStruct.h"
#include "nsTArray.h"

using namespace mozilla;
using namespace mozilla::gfx;

20
static float ClampFactor(float aFactor) {
21
22
  if (aFactor > 1) {
    return 1;
23
24
  }
  if (aFactor < 0) {
25
    MOZ_ASSERT_UNREACHABLE("A negative value should not have been parsed.");
26
27
28
29
30
31
    return 0;
  }

  return aFactor;
}

32
nsCSSFilterInstance::nsCSSFilterInstance(
33
    const StyleFilter& aFilter, nscolor aShadowFallbackColor,
34
35
36
37
38
39
40
41
42
43
44
45
46
    const nsIntRect& aTargetBoundsInFilterSpace,
    const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform)
    : mFilter(aFilter),
      mShadowFallbackColor(aShadowFallbackColor),
      mTargetBoundsInFilterSpace(aTargetBoundsInFilterSpace),
      mFrameSpaceInCSSPxToFilterSpaceTransform(
          aFrameSpaceInCSSPxToFilterSpaceTransform) {}

nsresult nsCSSFilterInstance::BuildPrimitives(
    nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
    bool aInputIsTainted) {
  FilterPrimitiveDescription descr =
      CreatePrimitiveDescription(aPrimitiveDescrs, aInputIsTainted);
47
  nsresult result;
48
49
  switch (mFilter.tag) {
    case StyleFilter::Tag::Blur:
50
51
      result = SetAttributesForBlur(descr);
      break;
52
    case StyleFilter::Tag::Brightness:
53
54
      result = SetAttributesForBrightness(descr);
      break;
55
    case StyleFilter::Tag::Contrast:
56
57
      result = SetAttributesForContrast(descr);
      break;
58
    case StyleFilter::Tag::DropShadow:
59
60
      result = SetAttributesForDropShadow(descr);
      break;
61
    case StyleFilter::Tag::Grayscale:
62
63
      result = SetAttributesForGrayscale(descr);
      break;
64
    case StyleFilter::Tag::HueRotate:
65
66
      result = SetAttributesForHueRotate(descr);
      break;
67
    case StyleFilter::Tag::Invert:
68
69
      result = SetAttributesForInvert(descr);
      break;
70
    case StyleFilter::Tag::Opacity:
71
72
      result = SetAttributesForOpacity(descr);
      break;
73
    case StyleFilter::Tag::Saturate:
74
75
      result = SetAttributesForSaturate(descr);
      break;
76
    case StyleFilter::Tag::Sepia:
77
78
      result = SetAttributesForSepia(descr);
      break;
79
    default:
80
      MOZ_ASSERT_UNREACHABLE("not a valid CSS filter type");
81
82
83
84
85
86
87
88
89
90
91
92
      return NS_ERROR_FAILURE;
  }

  if (NS_FAILED(result)) {
    return result;
  }

  // Compute the primitive's bounds now that we've determined its attributes.
  // Some attributes like blur radius can influence the bounds.
  SetBounds(descr, aPrimitiveDescrs);

  // Add this primitive to the filter chain.
93
  aPrimitiveDescrs.AppendElement(std::move(descr));
94
95
96
  return NS_OK;
}

97
98
99
FilterPrimitiveDescription nsCSSFilterInstance::CreatePrimitiveDescription(
    const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
    bool aInputIsTainted) {
100
  FilterPrimitiveDescription descr;
101
102
  int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs);
  descr.SetInputPrimitive(0, inputIndex);
103
104
  descr.SetIsTainted(inputIndex < 0 ? aInputIsTainted
                                    : aPrimitiveDescrs[inputIndex].IsTainted());
105
106
107
108
109
  descr.SetInputColorSpace(0, ColorSpace::SRGB);
  descr.SetOutputColorSpace(ColorSpace::SRGB);
  return descr;
}

110
111
nsresult nsCSSFilterInstance::SetAttributesForBlur(
    FilterPrimitiveDescription& aDescr) {
112
  const Length& radiusInFrameSpace = mFilter.AsBlur();
113
  Size radiusInFilterSpace =
114
      BlurRadiusToFilterSpace(radiusInFrameSpace.ToAppUnits());
115
116
117
  GaussianBlurAttributes atts;
  atts.mStdDeviation = radiusInFilterSpace;
  aDescr.Attributes() = AsVariant(atts);
118
119
120
  return NS_OK;
}

121
122
nsresult nsCSSFilterInstance::SetAttributesForBrightness(
    FilterPrimitiveDescription& aDescr) {
123
  float value = mFilter.AsBrightness();
124
125
  float intercept = 0.0f;
  ComponentTransferAttributes atts;
126
127

  // Set transfer functions for RGB.
128
  atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
129
130
  atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
  atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
131
132
133
134
135
  float slopeIntercept[2];
  slopeIntercept[kComponentTransferSlopeIndex] = value;
  slopeIntercept[kComponentTransferInterceptIndex] = intercept;
  atts.mValues[kChannelROrRGB].AppendElements(slopeIntercept, 2);

136
  atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
137
138

  aDescr.Attributes() = AsVariant(std::move(atts));
139
140
141
  return NS_OK;
}

142
143
nsresult nsCSSFilterInstance::SetAttributesForContrast(
    FilterPrimitiveDescription& aDescr) {
144
  float value = mFilter.AsContrast();
145
  float intercept = -(0.5 * value) + 0.5;
146
  ComponentTransferAttributes atts;
147
148

  // Set transfer functions for RGB.
149
  atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
150
151
  atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
  atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
152
153
154
155
156
  float slopeIntercept[2];
  slopeIntercept[kComponentTransferSlopeIndex] = value;
  slopeIntercept[kComponentTransferInterceptIndex] = intercept;
  atts.mValues[kChannelROrRGB].AppendElements(slopeIntercept, 2);

157
  atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
158
159

  aDescr.Attributes() = AsVariant(std::move(atts));
160
161
162
  return NS_OK;
}

163
164
nsresult nsCSSFilterInstance::SetAttributesForDropShadow(
    FilterPrimitiveDescription& aDescr) {
165
  const auto& shadow = mFilter.AsDropShadow();
166

167
  DropShadowAttributes atts;
168
169

  // Set drop shadow blur radius.
170
  Size radiusInFilterSpace = BlurRadiusToFilterSpace(shadow.blur.ToAppUnits());
171
  atts.mStdDeviation = radiusInFilterSpace;
172
173

  // Set offset.
174
175
  IntPoint offsetInFilterSpace = OffsetToFilterSpace(
      shadow.horizontal.ToAppUnits(), shadow.vertical.ToAppUnits());
176
  atts.mOffset = offsetInFilterSpace;
177
178

  // Set color. If unspecified, use the CSS color property.
179
  nscolor shadowColor = shadow.color.CalcColor(mShadowFallbackColor);
180
  atts.mColor = ToAttributeColor(shadowColor);
181

182
  aDescr.Attributes() = AsVariant(std::move(atts));
183
184
185
  return NS_OK;
}

186
187
nsresult nsCSSFilterInstance::SetAttributesForGrayscale(
    FilterPrimitiveDescription& aDescr) {
188
  ColorMatrixAttributes atts;
189
  // Set color matrix type.
190
  atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE;
191
192

  // Set color matrix values.
193
  float value = 1 - ClampFactor(mFilter.AsGrayscale());
194
  atts.mValues.AppendElements(&value, 1);
195

196
  aDescr.Attributes() = AsVariant(std::move(atts));
197
198
199
  return NS_OK;
}

200
201
nsresult nsCSSFilterInstance::SetAttributesForHueRotate(
    FilterPrimitiveDescription& aDescr) {
202
  ColorMatrixAttributes atts;
203
  // Set color matrix type.
204
  atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_HUE_ROTATE;
205
206

  // Set color matrix values.
207
  float value = mFilter.AsHueRotate().ToDegrees();
208
  atts.mValues.AppendElements(&value, 1);
209

210
  aDescr.Attributes() = AsVariant(std::move(atts));
211
212
213
  return NS_OK;
}

214
215
nsresult nsCSSFilterInstance::SetAttributesForInvert(
    FilterPrimitiveDescription& aDescr) {
216
  ComponentTransferAttributes atts;
217
  float value = ClampFactor(mFilter.AsInvert());
218
219
220
221
222
223

  // Set transfer functions for RGB.
  float invertTableValues[2];
  invertTableValues[0] = value;
  invertTableValues[1] = 1 - value;

224
225
  // Set transfer functions for RGB.
  atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_TABLE;
226
227
  atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
  atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
228
229
230
  atts.mValues[kChannelROrRGB].AppendElements(invertTableValues, 2);

  atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
231

232
  aDescr.Attributes() = AsVariant(std::move(atts));
233
234
235
  return NS_OK;
}

236
237
nsresult nsCSSFilterInstance::SetAttributesForOpacity(
    FilterPrimitiveDescription& aDescr) {
238
  OpacityAttributes atts;
239
  float value = ClampFactor(mFilter.AsOpacity());
240

241
242
  atts.mOpacity = value;
  aDescr.Attributes() = AsVariant(std::move(atts));
243
244
245
  return NS_OK;
}

246
247
nsresult nsCSSFilterInstance::SetAttributesForSaturate(
    FilterPrimitiveDescription& aDescr) {
248
  ColorMatrixAttributes atts;
249
  // Set color matrix type.
250
  atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE;
251
252

  // Set color matrix values.
253
  float value = mFilter.AsSaturate();
254
  atts.mValues.AppendElements(&value, 1);
255

256
  aDescr.Attributes() = AsVariant(std::move(atts));
257
258
259
  return NS_OK;
}

260
261
nsresult nsCSSFilterInstance::SetAttributesForSepia(
    FilterPrimitiveDescription& aDescr) {
262
  ColorMatrixAttributes atts;
263
  // Set color matrix type.
264
  atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SEPIA;
265
266

  // Set color matrix values.
267
  float value = ClampFactor(mFilter.AsSepia());
268
  atts.mValues.AppendElements(&value, 1);
269

270
  aDescr.Attributes() = AsVariant(std::move(atts));
271
272
273
  return NS_OK;
}

274
Size nsCSSFilterInstance::BlurRadiusToFilterSpace(nscoord aRadiusInFrameSpace) {
275
  float radiusInFrameSpaceInCSSPx =
276
      nsPresContext::AppUnitsToFloatCSSPixels(aRadiusInFrameSpace);
277
278

  // Convert the radius to filter space.
279
280
  Size radiusInFilterSpace(radiusInFrameSpaceInCSSPx,
                           radiusInFrameSpaceInCSSPx);
281
  gfxSize frameSpaceInCSSPxToFilterSpaceScale =
282
      mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors(true);
283
284
285
286
287
  radiusInFilterSpace.Scale(frameSpaceInCSSPxToFilterSpaceScale.width,
                            frameSpaceInCSSPxToFilterSpaceScale.height);

  // Check the radius limits.
  if (radiusInFilterSpace.width < 0 || radiusInFilterSpace.height < 0) {
288
289
290
    MOZ_ASSERT_UNREACHABLE(
        "we shouldn't have parsed a negative radius in the "
        "style");
291
    return Size();
292
  }
293

294
  Float maxStdDeviation = (Float)kMaxStdDeviation;
295
296
297
298
  radiusInFilterSpace.width =
      std::min(radiusInFilterSpace.width, maxStdDeviation);
  radiusInFilterSpace.height =
      std::min(radiusInFilterSpace.height, maxStdDeviation);
299

300
  return radiusInFilterSpace;
301
302
}

303
304
305
306
307
IntPoint nsCSSFilterInstance::OffsetToFilterSpace(
    nscoord aXOffsetInFrameSpace, nscoord aYOffsetInFrameSpace) {
  gfxPoint offsetInFilterSpace(
      nsPresContext::AppUnitsToFloatCSSPixels(aXOffsetInFrameSpace),
      nsPresContext::AppUnitsToFloatCSSPixels(aYOffsetInFrameSpace));
308
309
310

  // Convert the radius to filter space.
  gfxSize frameSpaceInCSSPxToFilterSpaceScale =
311
      mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors(true);
312
313
314
  offsetInFilterSpace.x *= frameSpaceInCSSPxToFilterSpaceScale.width;
  offsetInFilterSpace.y *= frameSpaceInCSSPxToFilterSpaceScale.height;

315
316
  return IntPoint(int32_t(offsetInFilterSpace.x),
                  int32_t(offsetInFilterSpace.y));
317
318
}

319
320
321
sRGBColor nsCSSFilterInstance::ToAttributeColor(nscolor aColor) {
  return sRGBColor(NS_GET_R(aColor) / 255.0, NS_GET_G(aColor) / 255.0,
                   NS_GET_B(aColor) / 255.0, NS_GET_A(aColor) / 255.0);
322
323
}

324
325
int32_t nsCSSFilterInstance::GetLastResultIndex(
    const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) {
326
  uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length();
327
328
329
  return !numPrimitiveDescrs
             ? FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic
             : numPrimitiveDescrs - 1;
330
331
}

332
333
334
void nsCSSFilterInstance::SetBounds(
    FilterPrimitiveDescription& aDescr,
    const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) {
335
  int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs);
336
337
338
  nsIntRect inputBounds =
      (inputIndex < 0) ? mTargetBoundsInFilterSpace
                       : aPrimitiveDescrs[inputIndex].PrimitiveSubregion();
339
340
341
342
343

  nsTArray<nsIntRegion> inputExtents;
  inputExtents.AppendElement(inputBounds);

  nsIntRegion outputExtents =
344
      FilterSupport::PostFilterExtentsForPrimitive(aDescr, inputExtents);
345
  IntRect outputBounds = outputExtents.GetBounds();
346
347
348
349

  aDescr.SetPrimitiveSubregion(outputBounds);
  aDescr.SetFilterSpaceBounds(outputBounds);
}