forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			353 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			353 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* 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 "CSSFilterInstance.h"
 | |
| 
 | |
| // Keep others in (case-insensitive) order:
 | |
| #include "FilterDescription.h"
 | |
| #include "gfx2DGlue.h"
 | |
| #include "gfxUtils.h"
 | |
| #include "nsIFrame.h"
 | |
| #include "nsStyleStruct.h"
 | |
| #include "nsTArray.h"
 | |
| 
 | |
| using namespace mozilla::gfx;
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| static float ClampFactor(float aFactor) {
 | |
|   if (aFactor > 1) {
 | |
|     return 1;
 | |
|   }
 | |
|   if (aFactor < 0) {
 | |
|     MOZ_ASSERT_UNREACHABLE("A negative value should not have been parsed.");
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   return aFactor;
 | |
| }
 | |
| 
 | |
| CSSFilterInstance::CSSFilterInstance(
 | |
|     const StyleFilter& aFilter, nscolor aShadowFallbackColor,
 | |
|     const nsIntRect& aTargetBoundsInFilterSpace,
 | |
|     const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform)
 | |
|     : mFilter(aFilter),
 | |
|       mShadowFallbackColor(aShadowFallbackColor),
 | |
|       mTargetBoundsInFilterSpace(aTargetBoundsInFilterSpace),
 | |
|       mFrameSpaceInCSSPxToFilterSpaceTransform(
 | |
|           aFrameSpaceInCSSPxToFilterSpaceTransform) {}
 | |
| 
 | |
| nsresult CSSFilterInstance::BuildPrimitives(
 | |
|     nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
 | |
|     bool aInputIsTainted) {
 | |
|   FilterPrimitiveDescription descr =
 | |
|       CreatePrimitiveDescription(aPrimitiveDescrs, aInputIsTainted);
 | |
|   nsresult result;
 | |
|   switch (mFilter.tag) {
 | |
|     case StyleFilter::Tag::Blur:
 | |
|       result = SetAttributesForBlur(descr);
 | |
|       break;
 | |
|     case StyleFilter::Tag::Brightness:
 | |
|       result = SetAttributesForBrightness(descr);
 | |
|       break;
 | |
|     case StyleFilter::Tag::Contrast:
 | |
|       result = SetAttributesForContrast(descr);
 | |
|       break;
 | |
|     case StyleFilter::Tag::DropShadow:
 | |
|       result = SetAttributesForDropShadow(descr);
 | |
|       break;
 | |
|     case StyleFilter::Tag::Grayscale:
 | |
|       result = SetAttributesForGrayscale(descr);
 | |
|       break;
 | |
|     case StyleFilter::Tag::HueRotate:
 | |
|       result = SetAttributesForHueRotate(descr);
 | |
|       break;
 | |
|     case StyleFilter::Tag::Invert:
 | |
|       result = SetAttributesForInvert(descr);
 | |
|       break;
 | |
|     case StyleFilter::Tag::Opacity:
 | |
|       result = SetAttributesForOpacity(descr);
 | |
|       break;
 | |
|     case StyleFilter::Tag::Saturate:
 | |
|       result = SetAttributesForSaturate(descr);
 | |
|       break;
 | |
|     case StyleFilter::Tag::Sepia:
 | |
|       result = SetAttributesForSepia(descr);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT_UNREACHABLE("not a valid CSS filter type");
 | |
|       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.
 | |
|   aPrimitiveDescrs.AppendElement(std::move(descr));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| FilterPrimitiveDescription CSSFilterInstance::CreatePrimitiveDescription(
 | |
|     const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
 | |
|     bool aInputIsTainted) {
 | |
|   FilterPrimitiveDescription descr;
 | |
|   int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs);
 | |
|   descr.SetInputPrimitive(0, inputIndex);
 | |
|   descr.SetIsTainted(inputIndex < 0 ? aInputIsTainted
 | |
|                                     : aPrimitiveDescrs[inputIndex].IsTainted());
 | |
|   descr.SetInputColorSpace(0, ColorSpace::SRGB);
 | |
|   descr.SetOutputColorSpace(ColorSpace::SRGB);
 | |
|   return descr;
 | |
| }
 | |
| 
 | |
| nsresult CSSFilterInstance::SetAttributesForBlur(
 | |
|     FilterPrimitiveDescription& aDescr) {
 | |
|   const Length& radiusInFrameSpace = mFilter.AsBlur();
 | |
|   Size radiusInFilterSpace =
 | |
|       BlurRadiusToFilterSpace(radiusInFrameSpace.ToAppUnits());
 | |
|   GaussianBlurAttributes atts;
 | |
|   atts.mStdDeviation = radiusInFilterSpace;
 | |
|   aDescr.Attributes() = AsVariant(atts);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult CSSFilterInstance::SetAttributesForBrightness(
 | |
|     FilterPrimitiveDescription& aDescr) {
 | |
|   float value = mFilter.AsBrightness();
 | |
|   float intercept = 0.0f;
 | |
|   ComponentTransferAttributes atts;
 | |
| 
 | |
|   // Set transfer functions for RGB.
 | |
|   atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
 | |
|   atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
 | |
|   atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
 | |
|   float slopeIntercept[2];
 | |
|   slopeIntercept[kComponentTransferSlopeIndex] = value;
 | |
|   slopeIntercept[kComponentTransferInterceptIndex] = intercept;
 | |
|   atts.mValues[kChannelROrRGB].AppendElements(slopeIntercept, 2);
 | |
| 
 | |
|   atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
 | |
| 
 | |
|   aDescr.Attributes() = AsVariant(std::move(atts));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult CSSFilterInstance::SetAttributesForContrast(
 | |
|     FilterPrimitiveDescription& aDescr) {
 | |
|   float value = mFilter.AsContrast();
 | |
|   float intercept = -(0.5 * value) + 0.5;
 | |
|   ComponentTransferAttributes atts;
 | |
| 
 | |
|   // Set transfer functions for RGB.
 | |
|   atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_LINEAR;
 | |
|   atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
 | |
|   atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
 | |
|   float slopeIntercept[2];
 | |
|   slopeIntercept[kComponentTransferSlopeIndex] = value;
 | |
|   slopeIntercept[kComponentTransferInterceptIndex] = intercept;
 | |
|   atts.mValues[kChannelROrRGB].AppendElements(slopeIntercept, 2);
 | |
| 
 | |
|   atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
 | |
| 
 | |
|   aDescr.Attributes() = AsVariant(std::move(atts));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult CSSFilterInstance::SetAttributesForDropShadow(
 | |
|     FilterPrimitiveDescription& aDescr) {
 | |
|   const auto& shadow = mFilter.AsDropShadow();
 | |
| 
 | |
|   DropShadowAttributes atts;
 | |
| 
 | |
|   // Set drop shadow blur radius.
 | |
|   Size radiusInFilterSpace = BlurRadiusToFilterSpace(shadow.blur.ToAppUnits());
 | |
|   atts.mStdDeviation = radiusInFilterSpace;
 | |
| 
 | |
|   // Set offset.
 | |
|   IntPoint offsetInFilterSpace = OffsetToFilterSpace(
 | |
|       shadow.horizontal.ToAppUnits(), shadow.vertical.ToAppUnits());
 | |
|   atts.mOffset = offsetInFilterSpace;
 | |
| 
 | |
|   // Set color. If unspecified, use the CSS color property.
 | |
|   nscolor shadowColor = shadow.color.CalcColor(mShadowFallbackColor);
 | |
|   atts.mColor = ToAttributeColor(shadowColor);
 | |
| 
 | |
|   aDescr.Attributes() = AsVariant(std::move(atts));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult CSSFilterInstance::SetAttributesForGrayscale(
 | |
|     FilterPrimitiveDescription& aDescr) {
 | |
|   ColorMatrixAttributes atts;
 | |
|   // Set color matrix type.
 | |
|   atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE;
 | |
| 
 | |
|   // Set color matrix values.
 | |
|   float value = 1 - ClampFactor(mFilter.AsGrayscale());
 | |
|   atts.mValues.AppendElements(&value, 1);
 | |
| 
 | |
|   aDescr.Attributes() = AsVariant(std::move(atts));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult CSSFilterInstance::SetAttributesForHueRotate(
 | |
|     FilterPrimitiveDescription& aDescr) {
 | |
|   ColorMatrixAttributes atts;
 | |
|   // Set color matrix type.
 | |
|   atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_HUE_ROTATE;
 | |
| 
 | |
|   // Set color matrix values.
 | |
|   float value = mFilter.AsHueRotate().ToDegrees();
 | |
|   atts.mValues.AppendElements(&value, 1);
 | |
| 
 | |
|   aDescr.Attributes() = AsVariant(std::move(atts));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult CSSFilterInstance::SetAttributesForInvert(
 | |
|     FilterPrimitiveDescription& aDescr) {
 | |
|   ComponentTransferAttributes atts;
 | |
|   float value = ClampFactor(mFilter.AsInvert());
 | |
| 
 | |
|   // Set transfer functions for RGB.
 | |
|   float invertTableValues[2];
 | |
|   invertTableValues[0] = value;
 | |
|   invertTableValues[1] = 1 - value;
 | |
| 
 | |
|   // Set transfer functions for RGB.
 | |
|   atts.mTypes[kChannelROrRGB] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_TABLE;
 | |
|   atts.mTypes[kChannelG] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
 | |
|   atts.mTypes[kChannelB] = (uint8_t)SVG_FECOMPONENTTRANSFER_SAME_AS_R;
 | |
|   atts.mValues[kChannelROrRGB].AppendElements(invertTableValues, 2);
 | |
| 
 | |
|   atts.mTypes[kChannelA] = (uint8_t)SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY;
 | |
| 
 | |
|   aDescr.Attributes() = AsVariant(std::move(atts));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult CSSFilterInstance::SetAttributesForOpacity(
 | |
|     FilterPrimitiveDescription& aDescr) {
 | |
|   OpacityAttributes atts;
 | |
|   float value = ClampFactor(mFilter.AsOpacity());
 | |
| 
 | |
|   atts.mOpacity = value;
 | |
|   aDescr.Attributes() = AsVariant(std::move(atts));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult CSSFilterInstance::SetAttributesForSaturate(
 | |
|     FilterPrimitiveDescription& aDescr) {
 | |
|   ColorMatrixAttributes atts;
 | |
|   // Set color matrix type.
 | |
|   atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SATURATE;
 | |
| 
 | |
|   // Set color matrix values.
 | |
|   float value = mFilter.AsSaturate();
 | |
|   atts.mValues.AppendElements(&value, 1);
 | |
| 
 | |
|   aDescr.Attributes() = AsVariant(std::move(atts));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult CSSFilterInstance::SetAttributesForSepia(
 | |
|     FilterPrimitiveDescription& aDescr) {
 | |
|   ColorMatrixAttributes atts;
 | |
|   // Set color matrix type.
 | |
|   atts.mType = (uint32_t)SVG_FECOLORMATRIX_TYPE_SEPIA;
 | |
| 
 | |
|   // Set color matrix values.
 | |
|   float value = ClampFactor(mFilter.AsSepia());
 | |
|   atts.mValues.AppendElements(&value, 1);
 | |
| 
 | |
|   aDescr.Attributes() = AsVariant(std::move(atts));
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| Size CSSFilterInstance::BlurRadiusToFilterSpace(nscoord aRadiusInFrameSpace) {
 | |
|   float radiusInFrameSpaceInCSSPx =
 | |
|       nsPresContext::AppUnitsToFloatCSSPixels(aRadiusInFrameSpace);
 | |
| 
 | |
|   // Convert the radius to filter space.
 | |
|   Size radiusInFilterSpace(radiusInFrameSpaceInCSSPx,
 | |
|                            radiusInFrameSpaceInCSSPx);
 | |
|   gfxSize frameSpaceInCSSPxToFilterSpaceScale =
 | |
|       mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors();
 | |
|   radiusInFilterSpace.Scale(frameSpaceInCSSPxToFilterSpaceScale.width,
 | |
|                             frameSpaceInCSSPxToFilterSpaceScale.height);
 | |
| 
 | |
|   // Check the radius limits.
 | |
|   if (radiusInFilterSpace.width < 0 || radiusInFilterSpace.height < 0) {
 | |
|     MOZ_ASSERT_UNREACHABLE(
 | |
|         "we shouldn't have parsed a negative radius in the "
 | |
|         "style");
 | |
|     return Size();
 | |
|   }
 | |
| 
 | |
|   Float maxStdDeviation = (Float)kMaxStdDeviation;
 | |
|   radiusInFilterSpace.width =
 | |
|       std::min(radiusInFilterSpace.width, maxStdDeviation);
 | |
|   radiusInFilterSpace.height =
 | |
|       std::min(radiusInFilterSpace.height, maxStdDeviation);
 | |
| 
 | |
|   return radiusInFilterSpace;
 | |
| }
 | |
| 
 | |
| IntPoint CSSFilterInstance::OffsetToFilterSpace(nscoord aXOffsetInFrameSpace,
 | |
|                                                 nscoord aYOffsetInFrameSpace) {
 | |
|   gfxPoint offsetInFilterSpace(
 | |
|       nsPresContext::AppUnitsToFloatCSSPixels(aXOffsetInFrameSpace),
 | |
|       nsPresContext::AppUnitsToFloatCSSPixels(aYOffsetInFrameSpace));
 | |
| 
 | |
|   // Convert the radius to filter space.
 | |
|   gfxSize frameSpaceInCSSPxToFilterSpaceScale =
 | |
|       mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors();
 | |
|   offsetInFilterSpace.x *= frameSpaceInCSSPxToFilterSpaceScale.width;
 | |
|   offsetInFilterSpace.y *= frameSpaceInCSSPxToFilterSpaceScale.height;
 | |
| 
 | |
|   return IntPoint(int32_t(offsetInFilterSpace.x),
 | |
|                   int32_t(offsetInFilterSpace.y));
 | |
| }
 | |
| 
 | |
| sRGBColor CSSFilterInstance::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);
 | |
| }
 | |
| 
 | |
| int32_t CSSFilterInstance::GetLastResultIndex(
 | |
|     const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) {
 | |
|   uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length();
 | |
|   return !numPrimitiveDescrs
 | |
|              ? FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic
 | |
|              : numPrimitiveDescrs - 1;
 | |
| }
 | |
| 
 | |
| void CSSFilterInstance::SetBounds(
 | |
|     FilterPrimitiveDescription& aDescr,
 | |
|     const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) {
 | |
|   int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs);
 | |
|   nsIntRect inputBounds =
 | |
|       (inputIndex < 0) ? mTargetBoundsInFilterSpace
 | |
|                        : aPrimitiveDescrs[inputIndex].PrimitiveSubregion();
 | |
| 
 | |
|   nsTArray<nsIntRegion> inputExtents;
 | |
|   inputExtents.AppendElement(inputBounds);
 | |
| 
 | |
|   nsIntRegion outputExtents =
 | |
|       FilterSupport::PostFilterExtentsForPrimitive(aDescr, inputExtents);
 | |
|   IntRect outputBounds = outputExtents.GetBounds();
 | |
| 
 | |
|   aDescr.SetPrimitiveSubregion(outputBounds);
 | |
|   aDescr.SetFilterSpaceBounds(outputBounds);
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla
 | 
