From f797e1291f3386cc785ecd64bb7c142f4c0d5674 Mon Sep 17 00:00:00 2001 From: Max Vujovic Date: Thu, 14 Aug 2014 11:29:56 -0700 Subject: [PATCH] Bug 948265 - Add CSS drop-shadow filter to nsCSSFilterInstance. r=mstange --- .../drop-shadow-default-color-ref.html | 28 +++++++ .../drop-shadow-default-color.html | 31 +++++++ .../drop-shadow-negative-offset-ref.html | 31 +++++++ .../drop-shadow-negative-offset.html | 32 +++++++ .../filters/css-filters/drop-shadow-ref.html | 28 +++++++ .../svg/filters/css-filters/drop-shadow.html | 28 +++++++ .../svg/filters/css-filters/reftest.list | 3 + layout/svg/nsCSSFilterInstance.cpp | 83 ++++++++++++++++--- layout/svg/nsCSSFilterInstance.h | 32 +++++++ layout/svg/nsFilterInstance.cpp | 3 +- 10 files changed, 288 insertions(+), 11 deletions(-) create mode 100644 layout/reftests/svg/filters/css-filters/drop-shadow-default-color-ref.html create mode 100644 layout/reftests/svg/filters/css-filters/drop-shadow-default-color.html create mode 100644 layout/reftests/svg/filters/css-filters/drop-shadow-negative-offset-ref.html create mode 100644 layout/reftests/svg/filters/css-filters/drop-shadow-negative-offset.html create mode 100644 layout/reftests/svg/filters/css-filters/drop-shadow-ref.html create mode 100644 layout/reftests/svg/filters/css-filters/drop-shadow.html diff --git a/layout/reftests/svg/filters/css-filters/drop-shadow-default-color-ref.html b/layout/reftests/svg/filters/css-filters/drop-shadow-default-color-ref.html new file mode 100644 index 000000000000..2fe89507c504 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow-default-color-ref.html @@ -0,0 +1,28 @@ + + + + + CSS Filters: Drop Shadow Default Color + + + + +

You should see a blue square with a green drop shadow.

+
+ + + + + + + diff --git a/layout/reftests/svg/filters/css-filters/drop-shadow-default-color.html b/layout/reftests/svg/filters/css-filters/drop-shadow-default-color.html new file mode 100644 index 000000000000..ac9f8ecb122d --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow-default-color.html @@ -0,0 +1,31 @@ + + + + + CSS Filters: Drop Shadow Default Color + + + + + + + + +

You should see a blue square with a green drop shadow.

+
+ + diff --git a/layout/reftests/svg/filters/css-filters/drop-shadow-negative-offset-ref.html b/layout/reftests/svg/filters/css-filters/drop-shadow-negative-offset-ref.html new file mode 100644 index 000000000000..bf04c1d511fa --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow-negative-offset-ref.html @@ -0,0 +1,31 @@ + + + + +CSS Filters: Negative Drop Shadow Offset + + + + +

You should see a blue square with a green drop shadow in its top left corner.

+
+ + + + + + + diff --git a/layout/reftests/svg/filters/css-filters/drop-shadow-negative-offset.html b/layout/reftests/svg/filters/css-filters/drop-shadow-negative-offset.html new file mode 100644 index 000000000000..79b301daba38 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow-negative-offset.html @@ -0,0 +1,32 @@ + + + + + CSS Filters: Negative Drop Shadow Offset + + + + + + + +

You should see a blue square with a green drop shadow in its top left corner.

+
+ + diff --git a/layout/reftests/svg/filters/css-filters/drop-shadow-ref.html b/layout/reftests/svg/filters/css-filters/drop-shadow-ref.html new file mode 100644 index 000000000000..211fbfbc4339 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow-ref.html @@ -0,0 +1,28 @@ + + + + + CSS Filters: Drop Shadow on HTML Element + + + + +

You should see a blue square with a green drop shadow.

+
+ + + + + + + diff --git a/layout/reftests/svg/filters/css-filters/drop-shadow.html b/layout/reftests/svg/filters/css-filters/drop-shadow.html new file mode 100644 index 000000000000..1e4673465193 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow.html @@ -0,0 +1,28 @@ + + + + + CSS Filters: Drop Shadow on HTML Element + + + + + + + +

You should see a blue square with a green drop shadow.

+
+ + diff --git a/layout/reftests/svg/filters/css-filters/reftest.list b/layout/reftests/svg/filters/css-filters/reftest.list index 9226de0bed69..ca0e0385e437 100644 --- a/layout/reftests/svg/filters/css-filters/reftest.list +++ b/layout/reftests/svg/filters/css-filters/reftest.list @@ -7,3 +7,6 @@ default-preferences pref(layout.css.filters.enabled,true) == blur.svg blur-ref.svg == blur-zero-radius.html blur-zero-radius-ref.html == blur-zoomed-page.html blur-zoomed-page-ref.html +== drop-shadow.html drop-shadow-ref.html +== drop-shadow-default-color.html drop-shadow-default-color-ref.html +== drop-shadow-negative-offset.html drop-shadow-negative-offset-ref.html diff --git a/layout/svg/nsCSSFilterInstance.cpp b/layout/svg/nsCSSFilterInstance.cpp index d4591ed71afc..94a6bae95dce 100644 --- a/layout/svg/nsCSSFilterInstance.cpp +++ b/layout/svg/nsCSSFilterInstance.cpp @@ -9,6 +9,7 @@ // Keep others in (case-insensitive) order: #include "gfx2DGlue.h" #include "gfxUtils.h" +#include "nsIFrame.h" #include "nsStyleStruct.h" #include "nsTArray.h" @@ -16,9 +17,11 @@ using namespace mozilla; using namespace mozilla::gfx; nsCSSFilterInstance::nsCSSFilterInstance(const nsStyleFilter& aFilter, + nsIFrame *aTargetFrame, const nsIntRect& aTargetBBoxInFilterSpace, const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform) : mFilter(aFilter) + , mTargetFrame(aTargetFrame) , mTargetBBoxInFilterSpace(aTargetBBoxInFilterSpace) , mFrameSpaceInCSSPxToFilterSpaceTransform(aFrameSpaceInCSSPxToFilterSpaceTransform) { @@ -38,6 +41,9 @@ nsCSSFilterInstance::BuildPrimitives(nsTArray& aPrim case NS_STYLE_FILTER_BRIGHTNESS: case NS_STYLE_FILTER_CONTRAST: case NS_STYLE_FILTER_DROP_SHADOW: + descr = CreatePrimitiveDescription(PrimitiveType::DropShadow, aPrimitiveDescrs); + result = SetAttributesForDropShadow(descr); + break; case NS_STYLE_FILTER_GRAYSCALE: case NS_STYLE_FILTER_HUE_ROTATE: case NS_STYLE_FILTER_INVERT: @@ -78,17 +84,49 @@ nsCSSFilterInstance::CreatePrimitiveDescription(PrimitiveType aType, nsresult nsCSSFilterInstance::SetAttributesForBlur(FilterPrimitiveDescription& aDescr) { - // Get the radius from the style. - nsStyleCoord radiusStyleCoord = mFilter.GetFilterParameter(); - if (radiusStyleCoord.GetUnit() != eStyleUnit_Coord) { + const nsStyleCoord& radiusInFrameSpace = mFilter.GetFilterParameter(); + if (radiusInFrameSpace.GetUnit() != eStyleUnit_Coord) { NS_NOTREACHED("unexpected unit"); return NS_ERROR_FAILURE; } - // Get the radius in frame space. - nscoord radiusInFrameSpace = radiusStyleCoord.GetCoordValue(); + Size radiusInFilterSpace = BlurRadiusToFilterSpace(radiusInFrameSpace.GetCoordValue()); + aDescr.Attributes().Set(eGaussianBlurStdDeviation, radiusInFilterSpace); + return NS_OK; +} + +nsresult +nsCSSFilterInstance::SetAttributesForDropShadow(FilterPrimitiveDescription& aDescr) +{ + nsCSSShadowArray* shadows = mFilter.GetDropShadow(); + if (!shadows || shadows->Length() != 1) { + NS_NOTREACHED("Exactly one drop shadow should have been parsed."); + return NS_ERROR_FAILURE; + } + + nsCSSShadowItem* shadow = shadows->ShadowAt(0); + + // Set drop shadow blur radius. + Size radiusInFilterSpace = BlurRadiusToFilterSpace(shadow->mRadius); + aDescr.Attributes().Set(eDropShadowStdDeviation, radiusInFilterSpace); + + // Set offset. + IntPoint offsetInFilterSpace = OffsetToFilterSpace(shadow->mXOffset, shadow->mYOffset); + aDescr.Attributes().Set(eDropShadowOffset, offsetInFilterSpace); + + // Set color. If unspecified, use the CSS color property. + nscolor shadowColor = shadow->mHasColor ? + shadow->mColor : mTargetFrame->StyleColor()->mColor; + aDescr.Attributes().Set(eDropShadowColor, ToAttributeColor(shadowColor)); + + return NS_OK; +} + +Size +nsCSSFilterInstance::BlurRadiusToFilterSpace(nscoord aRadiusInFrameSpace) +{ float radiusInFrameSpaceInCSSPx = - nsPresContext::AppUnitsToFloatCSSPixels(radiusInFrameSpace); + nsPresContext::AppUnitsToFloatCSSPixels(aRadiusInFrameSpace); // Convert the radius to filter space. gfxSize radiusInFilterSpace(radiusInFrameSpaceInCSSPx, @@ -101,15 +139,40 @@ nsCSSFilterInstance::SetAttributesForBlur(FilterPrimitiveDescription& aDescr) // Check the radius limits. if (radiusInFilterSpace.width < 0 || radiusInFilterSpace.height < 0) { NS_NOTREACHED("we shouldn't have parsed a negative radius in the style"); - return NS_ERROR_FAILURE; + return Size(); } gfxFloat maxStdDeviation = (gfxFloat)kMaxStdDeviation; radiusInFilterSpace.width = std::min(radiusInFilterSpace.width, maxStdDeviation); radiusInFilterSpace.height = std::min(radiusInFilterSpace.height, maxStdDeviation); - // Set the radius parameter. - aDescr.Attributes().Set(eGaussianBlurStdDeviation, ToSize(radiusInFilterSpace)); - return NS_OK; + return ToSize(radiusInFilterSpace); +} + +IntPoint +nsCSSFilterInstance::OffsetToFilterSpace(nscoord aXOffsetInFrameSpace, + nscoord aYOffsetInFrameSpace) +{ + gfxPoint offsetInFilterSpace(nsPresContext::AppUnitsToFloatCSSPixels(aXOffsetInFrameSpace), + nsPresContext::AppUnitsToFloatCSSPixels(aYOffsetInFrameSpace)); + + // Convert the radius to filter space. + gfxSize frameSpaceInCSSPxToFilterSpaceScale = + mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors(true); + offsetInFilterSpace.x *= frameSpaceInCSSPxToFilterSpaceScale.width; + offsetInFilterSpace.y *= frameSpaceInCSSPxToFilterSpaceScale.height; + + return IntPoint(int32_t(offsetInFilterSpace.x), int32_t(offsetInFilterSpace.y)); +} + +Color +nsCSSFilterInstance::ToAttributeColor(nscolor aColor) +{ + return Color( + 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 diff --git a/layout/svg/nsCSSFilterInstance.h b/layout/svg/nsCSSFilterInstance.h index 0e1248b6b6bb..3b2250c968cd 100644 --- a/layout/svg/nsCSSFilterInstance.h +++ b/layout/svg/nsCSSFilterInstance.h @@ -9,7 +9,11 @@ #include "FilterSupport.h" #include "gfxMatrix.h" #include "gfxRect.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/gfx/Types.h" +#include "nsColor.h" +class nsIFrame; struct nsStyleFilter; template class nsTArray; @@ -20,8 +24,11 @@ template class nsTArray; */ class nsCSSFilterInstance { + typedef mozilla::gfx::Color Color; typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription; + typedef mozilla::gfx::IntPoint IntPoint; typedef mozilla::gfx::PrimitiveType PrimitiveType; + typedef mozilla::gfx::Size Size; public: /** @@ -34,6 +41,7 @@ public: * the filtered element's frame space in CSS pixels to filter space. */ nsCSSFilterInstance(const nsStyleFilter& aFilter, + nsIFrame *aTargetFrame, const nsIntRect& mTargetBBoxInFilterSpace, const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform); @@ -55,6 +63,7 @@ private: * Sets aDescr's attributes using the style info in mFilter. */ nsresult SetAttributesForBlur(FilterPrimitiveDescription& aDescr); + nsresult SetAttributesForDropShadow(FilterPrimitiveDescription& aDescr); /** * Returns the index of the last result in the aPrimitiveDescrs, which we'll @@ -71,11 +80,34 @@ private: void SetBounds(FilterPrimitiveDescription& aDescr, const nsTArray& aPrimitiveDescrs); + /** + * Converts an nscolor to a Color, suitable for use as a + * FilterPrimitiveDescription attribute. + */ + Color ToAttributeColor(nscolor aColor); + + /** + * Converts a blur radius in frame space to filter space. + */ + Size BlurRadiusToFilterSpace(nscoord aRadiusInFrameSpace); + + /** + * Converts a point defined by a pair of nscoord x, y coordinates from frame + * space to filter space. + */ + IntPoint OffsetToFilterSpace(nscoord aXOffsetInFrameSpace, + nscoord aYOffsetInFrameSpace); + /** * The CSS filter originally from the style system. */ const nsStyleFilter& mFilter; + /** + * The frame for the element that is currently being filtered. + */ + nsIFrame* mTargetFrame; + /** * The bounding box of the element being filtered, in filter space. Used for * input bounds if this CSS filter is the first in the filter chain. diff --git a/layout/svg/nsFilterInstance.cpp b/layout/svg/nsFilterInstance.cpp index 2c94993d3489..a8b696244bf8 100644 --- a/layout/svg/nsFilterInstance.cpp +++ b/layout/svg/nsFilterInstance.cpp @@ -260,7 +260,8 @@ nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter) } // Build primitives for a CSS filter. - nsCSSFilterInstance cssFilterInstance(aFilter, mTargetBBoxInFilterSpace, + nsCSSFilterInstance cssFilterInstance(aFilter, mTargetFrame, + mTargetBBoxInFilterSpace, mFrameSpaceInCSSPxToFilterSpaceTransform); return cssFilterInstance.BuildPrimitives(mPrimitiveDescriptions); }