CSSFilterInstance.cpp (11845B)
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 "CSSFilterInstance.h" 9 10 // Keep others in (case-insensitive) order: 11 #include "FilterDescription.h" 12 #include "gfx2DGlue.h" 13 #include "gfxUtils.h" 14 #include "nsIFrame.h" 15 #include "nsStyleStruct.h" 16 #include "nsTArray.h" 17 18 using namespace mozilla::gfx; 19 20 namespace mozilla { 21 22 static float ClampFactor(float aFactor) { 23 if (aFactor > 1) { 24 return 1; 25 } 26 if (aFactor < 0) { 27 MOZ_ASSERT_UNREACHABLE("A negative value should not have been parsed."); 28 return 0; 29 } 30 31 return aFactor; 32 } 33 34 CSSFilterInstance::CSSFilterInstance( 35 const StyleFilter& aFilter, nscolor aShadowFallbackColor, 36 const nsIntRect& aTargetBoundsInFilterSpace, 37 const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform) 38 : mFilter(aFilter), 39 mShadowFallbackColor(aShadowFallbackColor), 40 mTargetBoundsInFilterSpace(aTargetBoundsInFilterSpace), 41 mFrameSpaceInCSSPxToFilterSpaceTransform( 42 aFrameSpaceInCSSPxToFilterSpaceTransform) {} 43 44 nsresult CSSFilterInstance::BuildPrimitives( 45 nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs, 46 bool aInputIsTainted) { 47 FilterPrimitiveDescription descr = 48 CreatePrimitiveDescription(aPrimitiveDescrs, aInputIsTainted); 49 nsresult result; 50 switch (mFilter.tag) { 51 case StyleFilter::Tag::Blur: 52 result = SetAttributesForBlur(descr); 53 break; 54 case StyleFilter::Tag::Brightness: 55 result = SetAttributesForBrightness(descr); 56 break; 57 case StyleFilter::Tag::Contrast: 58 result = SetAttributesForContrast(descr); 59 break; 60 case StyleFilter::Tag::DropShadow: 61 result = SetAttributesForDropShadow(descr); 62 break; 63 case StyleFilter::Tag::Grayscale: 64 result = SetAttributesForGrayscale(descr); 65 break; 66 case StyleFilter::Tag::HueRotate: 67 result = SetAttributesForHueRotate(descr); 68 break; 69 case StyleFilter::Tag::Invert: 70 result = SetAttributesForInvert(descr); 71 break; 72 case StyleFilter::Tag::Opacity: 73 result = SetAttributesForOpacity(descr); 74 break; 75 case StyleFilter::Tag::Saturate: 76 result = SetAttributesForSaturate(descr); 77 break; 78 case StyleFilter::Tag::Sepia: 79 result = SetAttributesForSepia(descr); 80 break; 81 default: 82 MOZ_ASSERT_UNREACHABLE("not a valid CSS filter type"); 83 return NS_ERROR_FAILURE; 84 } 85 86 if (NS_FAILED(result)) { 87 return result; 88 } 89 90 // Compute the primitive's bounds now that we've determined its attributes. 91 // Some attributes like blur radius can influence the bounds. 92 SetBounds(descr, aPrimitiveDescrs); 93 94 // Add this primitive to the filter chain. 95 aPrimitiveDescrs.AppendElement(std::move(descr)); 96 return NS_OK; 97 } 98 99 FilterPrimitiveDescription CSSFilterInstance::CreatePrimitiveDescription( 100 const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs, 101 bool aInputIsTainted) { 102 FilterPrimitiveDescription descr; 103 int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs); 104 descr.SetInputPrimitive(0, inputIndex); 105 descr.SetIsTainted(inputIndex < 0 ? aInputIsTainted 106 : aPrimitiveDescrs[inputIndex].IsTainted()); 107 descr.SetInputColorSpace(0, ColorSpace::SRGB); 108 descr.SetOutputColorSpace(ColorSpace::SRGB); 109 return descr; 110 } 111 112 nsresult CSSFilterInstance::SetAttributesForBlur( 113 FilterPrimitiveDescription& aDescr) { 114 const Length& radiusInFrameSpace = mFilter.AsBlur(); 115 Size radiusInFilterSpace = 116 BlurRadiusToFilterSpace(radiusInFrameSpace.ToAppUnits()); 117 GaussianBlurAttributes atts; 118 atts.mStdDeviation = radiusInFilterSpace; 119 aDescr.Attributes() = AsVariant(atts); 120 return NS_OK; 121 } 122 123 nsresult CSSFilterInstance::SetAttributesForBrightness( 124 FilterPrimitiveDescription& aDescr) { 125 float value = mFilter.AsBrightness(); 126 float intercept = 0.0f; 127 ComponentTransferAttributes atts; 128 129 // Set transfer functions for RGB. 130 atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR; 131 atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R; 132 atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R; 133 std::array<float, 2> slopeIntercept; 134 slopeIntercept[kComponentTransferSlopeIndex] = value; 135 slopeIntercept[kComponentTransferInterceptIndex] = intercept; 136 atts.mValues[kChannelROrRGB].AppendElements(Span(slopeIntercept)); 137 138 atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY; 139 140 aDescr.Attributes() = AsVariant(std::move(atts)); 141 return NS_OK; 142 } 143 144 nsresult CSSFilterInstance::SetAttributesForContrast( 145 FilterPrimitiveDescription& aDescr) { 146 float value = mFilter.AsContrast(); 147 float intercept = -(0.5 * value) + 0.5; 148 ComponentTransferAttributes atts; 149 150 // Set transfer functions for RGB. 151 atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR; 152 atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R; 153 atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R; 154 std::array<float, 2> slopeIntercept; 155 slopeIntercept[kComponentTransferSlopeIndex] = value; 156 slopeIntercept[kComponentTransferInterceptIndex] = intercept; 157 atts.mValues[kChannelROrRGB].AppendElements(Span(slopeIntercept)); 158 159 atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY; 160 161 aDescr.Attributes() = AsVariant(std::move(atts)); 162 return NS_OK; 163 } 164 165 nsresult CSSFilterInstance::SetAttributesForDropShadow( 166 FilterPrimitiveDescription& aDescr) { 167 const auto& shadow = mFilter.AsDropShadow(); 168 169 DropShadowAttributes atts; 170 171 // Set drop shadow blur radius. 172 Size radiusInFilterSpace = BlurRadiusToFilterSpace(shadow.blur.ToAppUnits()); 173 atts.mStdDeviation = radiusInFilterSpace; 174 175 // Set offset. 176 IntPoint offsetInFilterSpace = OffsetToFilterSpace( 177 shadow.horizontal.ToAppUnits(), shadow.vertical.ToAppUnits()); 178 atts.mOffset = offsetInFilterSpace; 179 180 // Set color. If unspecified, use the CSS color property. 181 nscolor shadowColor = shadow.color.CalcColor(mShadowFallbackColor); 182 atts.mColor = sRGBColor::FromABGR(shadowColor); 183 184 aDescr.Attributes() = AsVariant(std::move(atts)); 185 return NS_OK; 186 } 187 188 nsresult CSSFilterInstance::SetAttributesForGrayscale( 189 FilterPrimitiveDescription& aDescr) { 190 ColorMatrixAttributes atts; 191 // Set color matrix type. 192 atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE; 193 194 // Set color matrix value. 195 atts.mValues.AppendElement(1 - ClampFactor(mFilter.AsGrayscale())); 196 197 aDescr.Attributes() = AsVariant(std::move(atts)); 198 return NS_OK; 199 } 200 201 nsresult CSSFilterInstance::SetAttributesForHueRotate( 202 FilterPrimitiveDescription& aDescr) { 203 ColorMatrixAttributes atts; 204 // Set color matrix type. 205 atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_HUE_ROTATE; 206 207 // Set color matrix value. 208 atts.mValues.AppendElement(mFilter.AsHueRotate().ToDegrees()); 209 210 aDescr.Attributes() = AsVariant(std::move(atts)); 211 return NS_OK; 212 } 213 214 nsresult CSSFilterInstance::SetAttributesForInvert( 215 FilterPrimitiveDescription& aDescr) { 216 ComponentTransferAttributes atts; 217 float value = ClampFactor(mFilter.AsInvert()); 218 219 // Set transfer functions for RGB. 220 std::array<float, 2> invertTableValues = {value, 1 - value}; 221 222 // Set transfer functions for RGB. 223 atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_TABLE; 224 atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R; 225 atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R; 226 atts.mValues[kChannelROrRGB].AppendElements(Span(invertTableValues)); 227 228 atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY; 229 230 aDescr.Attributes() = AsVariant(std::move(atts)); 231 return NS_OK; 232 } 233 234 nsresult CSSFilterInstance::SetAttributesForOpacity( 235 FilterPrimitiveDescription& aDescr) { 236 OpacityAttributes atts; 237 float value = ClampFactor(mFilter.AsOpacity()); 238 239 atts.mOpacity = value; 240 aDescr.Attributes() = AsVariant(std::move(atts)); 241 return NS_OK; 242 } 243 244 nsresult CSSFilterInstance::SetAttributesForSaturate( 245 FilterPrimitiveDescription& aDescr) { 246 ColorMatrixAttributes atts; 247 // Set color matrix type. 248 atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE; 249 250 // Set color matrix value. 251 atts.mValues.AppendElement(mFilter.AsSaturate()); 252 253 aDescr.Attributes() = AsVariant(std::move(atts)); 254 return NS_OK; 255 } 256 257 nsresult CSSFilterInstance::SetAttributesForSepia( 258 FilterPrimitiveDescription& aDescr) { 259 ColorMatrixAttributes atts; 260 // Set color matrix type. 261 atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SEPIA; 262 263 // Set color matrix value. 264 atts.mValues.AppendElement(ClampFactor(mFilter.AsSepia())); 265 266 aDescr.Attributes() = AsVariant(std::move(atts)); 267 return NS_OK; 268 } 269 270 Size CSSFilterInstance::BlurRadiusToFilterSpace(nscoord aRadiusInFrameSpace) { 271 float radiusInFrameSpaceInCSSPx = 272 nsPresContext::AppUnitsToFloatCSSPixels(aRadiusInFrameSpace); 273 274 // Convert the radius to filter space. 275 Size radiusInFilterSpace(radiusInFrameSpaceInCSSPx, 276 radiusInFrameSpaceInCSSPx); 277 // Narrow the scale factors. They will only be used with types containing 278 // floating point types. 279 auto frameSpaceInCSSPxToFilterSpaceScale = 280 mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors() 281 .ConvertTo<float>(); 282 radiusInFilterSpace = 283 radiusInFilterSpace * frameSpaceInCSSPxToFilterSpaceScale; 284 285 // Check the radius limits. 286 if (radiusInFilterSpace.width < 0 || radiusInFilterSpace.height < 0) { 287 MOZ_ASSERT_UNREACHABLE( 288 "we shouldn't have parsed a negative radius in the " 289 "style"); 290 return Size(); 291 } 292 293 Float maxStdDeviation = (Float)kMaxStdDeviation; 294 radiusInFilterSpace.width = 295 std::min(radiusInFilterSpace.width, maxStdDeviation); 296 radiusInFilterSpace.height = 297 std::min(radiusInFilterSpace.height, maxStdDeviation); 298 299 return radiusInFilterSpace; 300 } 301 302 IntPoint CSSFilterInstance::OffsetToFilterSpace(nscoord aXOffsetInFrameSpace, 303 nscoord aYOffsetInFrameSpace) { 304 gfxPoint offsetInFilterSpace( 305 nsPresContext::AppUnitsToFloatCSSPixels(aXOffsetInFrameSpace), 306 nsPresContext::AppUnitsToFloatCSSPixels(aYOffsetInFrameSpace)); 307 308 // Convert the radius to filter space. 309 auto frameSpaceInCSSPxToFilterSpaceScale = 310 mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors(); 311 offsetInFilterSpace.x *= frameSpaceInCSSPxToFilterSpaceScale.xScale; 312 offsetInFilterSpace.y *= frameSpaceInCSSPxToFilterSpaceScale.yScale; 313 314 return IntPoint(int32_t(offsetInFilterSpace.x), 315 int32_t(offsetInFilterSpace.y)); 316 } 317 318 int32_t CSSFilterInstance::GetLastResultIndex( 319 const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) { 320 uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length(); 321 return !numPrimitiveDescrs 322 ? FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic 323 : numPrimitiveDescrs - 1; 324 } 325 326 void CSSFilterInstance::SetBounds( 327 FilterPrimitiveDescription& aDescr, 328 const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) { 329 int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs); 330 nsIntRect inputBounds = 331 (inputIndex < 0) ? mTargetBoundsInFilterSpace 332 : aPrimitiveDescrs[inputIndex].PrimitiveSubregion(); 333 334 AutoTArray<nsIntRegion, 8> inputExtents; 335 inputExtents.AppendElement(inputBounds); 336 337 nsIntRegion outputExtents = 338 FilterSupport::PostFilterExtentsForPrimitive(aDescr, inputExtents); 339 IntRect outputBounds = outputExtents.GetBounds(); 340 341 aDescr.SetPrimitiveSubregion(outputBounds); 342 aDescr.SetFilterSpaceBounds(outputBounds); 343 } 344 345 } // namespace mozilla