Bug 948265 - Add CSS drop-shadow filter to nsCSSFilterInstance. r=mstange

This commit is contained in:
Max Vujovic 2014-08-14 11:29:56 -07:00
parent 181e45f4b1
commit f797e1291f
10 changed files with 288 additions and 11 deletions

View File

@ -0,0 +1,28 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
<html>
<head>
<title>CSS Filters: Drop Shadow Default Color</title>
<link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com">
<style type="text/css">
#target {
filter: url(#drop-shadow);
background-color: #00f;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<p>You should see a blue square with a green drop shadow.</p>
<div id="target"></div>
<svg width="0" height="0">
<filter id="drop-shadow" x="-50" y="-50" width="200" height="200" filterUnits="userSpaceOnUse">
<feDropShadow dx="10" dy="10" stdDeviation="3" flood-color="#0f0" color-interpolation-filters="sRGB"/>
</filter>
</svg>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
<html>
<head>
<title>CSS Filters: Drop Shadow Default Color</title>
<link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com">
<link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-drop-shadow">
<link rel="help" href="http://www.w3.org/TR/css3-background/#the-box-shadow">
<link rel="match" href="drop-shadow-default-color-ref.html">
<meta name="assert"
content="If the color is unspecified in a CSS drop-shadow filter
function, it should default to the value of the CSS color
property.">
<style type="text/css">
#target {
filter: drop-shadow(10px 10px 3px);
color: #0f0;
background-color: #00f;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<p>You should see a blue square with a green drop shadow.</p>
<div id="target"></div>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
<html>
<head>
<title>CSS Filters: Negative Drop Shadow Offset</title>
<link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com">
<style type="text/css">
#target {
filter: url(#drop-shadow);
background-color: #00f;
position: relative;
top: 20px;
left: 20px;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<p>You should see a blue square with a green drop shadow in its top left corner.</p>
<div id="target"></div>
<svg width="0" height="0">
<filter id="drop-shadow" x="-50" y="-50" width="200" height="200" filterUnits="userSpaceOnUse">
<feDropShadow dx="-10" dy="-10" stdDeviation="3" flood-color="#0f0" color-interpolation-filters="sRGB"/>
</filter>
</svg>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
<html>
<head>
<title>CSS Filters: Negative Drop Shadow Offset</title>
<link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com">
<link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-drop-shadow">
<link rel="match" href="drop-shadow-negative-offset-ref.html">
<meta name="assert"
content="Given negative shadow offsets, the CSS drop-shadow filter
function should add a drop shadow extending from the top left
corner of an HTML element.">
<style type="text/css">
#target {
filter: drop-shadow(-10px -10px 3px #0f0);
background-color: #00f;
position: relative;
top: 20px;
left: 20px;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<p>You should see a blue square with a green drop shadow in its top left corner.</p>
<div id="target"></div>
</body>
</html>

View File

@ -0,0 +1,28 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
<html>
<head>
<title>CSS Filters: Drop Shadow on HTML Element</title>
<link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com">
<style type="text/css">
#target {
filter: url(#drop-shadow);
background-color: #00f;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<p>You should see a blue square with a green drop shadow.</p>
<div id="target"></div>
<svg width="0" height="0">
<filter id="drop-shadow" x="-50" y="-50" width="200" height="200" filterUnits="userSpaceOnUse">
<feDropShadow dx="10" dy="10" stdDeviation="3" flood-color="#0f0" color-interpolation-filters="sRGB"/>
</filter>
</svg>
</body>
</html>

View File

@ -0,0 +1,28 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
<html>
<head>
<title>CSS Filters: Drop Shadow on HTML Element</title>
<link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com">
<link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-drop-shadow">
<link rel="match" href="drop-shadow-ref.html">
<meta name="assert"
content="The CSS drop-shadow filter function should add a drop shadow to
an HTML element.">
<style type="text/css">
#target {
filter: drop-shadow(10px 10px 3px #0f0);
background-color: #00f;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<p>You should see a blue square with a green drop shadow.</p>
<div id="target"></div>
</body>
</html>

View File

@ -7,3 +7,6 @@ default-preferences pref(layout.css.filters.enabled,true)
== blur.svg blur-ref.svg == blur.svg blur-ref.svg
== blur-zero-radius.html blur-zero-radius-ref.html == blur-zero-radius.html blur-zero-radius-ref.html
== blur-zoomed-page.html blur-zoomed-page-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

View File

@ -9,6 +9,7 @@
// Keep others in (case-insensitive) order: // Keep others in (case-insensitive) order:
#include "gfx2DGlue.h" #include "gfx2DGlue.h"
#include "gfxUtils.h" #include "gfxUtils.h"
#include "nsIFrame.h"
#include "nsStyleStruct.h" #include "nsStyleStruct.h"
#include "nsTArray.h" #include "nsTArray.h"
@ -16,9 +17,11 @@ using namespace mozilla;
using namespace mozilla::gfx; using namespace mozilla::gfx;
nsCSSFilterInstance::nsCSSFilterInstance(const nsStyleFilter& aFilter, nsCSSFilterInstance::nsCSSFilterInstance(const nsStyleFilter& aFilter,
nsIFrame *aTargetFrame,
const nsIntRect& aTargetBBoxInFilterSpace, const nsIntRect& aTargetBBoxInFilterSpace,
const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform) const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform)
: mFilter(aFilter) : mFilter(aFilter)
, mTargetFrame(aTargetFrame)
, mTargetBBoxInFilterSpace(aTargetBBoxInFilterSpace) , mTargetBBoxInFilterSpace(aTargetBBoxInFilterSpace)
, mFrameSpaceInCSSPxToFilterSpaceTransform(aFrameSpaceInCSSPxToFilterSpaceTransform) , mFrameSpaceInCSSPxToFilterSpaceTransform(aFrameSpaceInCSSPxToFilterSpaceTransform)
{ {
@ -38,6 +41,9 @@ nsCSSFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrim
case NS_STYLE_FILTER_BRIGHTNESS: case NS_STYLE_FILTER_BRIGHTNESS:
case NS_STYLE_FILTER_CONTRAST: case NS_STYLE_FILTER_CONTRAST:
case NS_STYLE_FILTER_DROP_SHADOW: case NS_STYLE_FILTER_DROP_SHADOW:
descr = CreatePrimitiveDescription(PrimitiveType::DropShadow, aPrimitiveDescrs);
result = SetAttributesForDropShadow(descr);
break;
case NS_STYLE_FILTER_GRAYSCALE: case NS_STYLE_FILTER_GRAYSCALE:
case NS_STYLE_FILTER_HUE_ROTATE: case NS_STYLE_FILTER_HUE_ROTATE:
case NS_STYLE_FILTER_INVERT: case NS_STYLE_FILTER_INVERT:
@ -78,17 +84,49 @@ nsCSSFilterInstance::CreatePrimitiveDescription(PrimitiveType aType,
nsresult nsresult
nsCSSFilterInstance::SetAttributesForBlur(FilterPrimitiveDescription& aDescr) nsCSSFilterInstance::SetAttributesForBlur(FilterPrimitiveDescription& aDescr)
{ {
// Get the radius from the style. const nsStyleCoord& radiusInFrameSpace = mFilter.GetFilterParameter();
nsStyleCoord radiusStyleCoord = mFilter.GetFilterParameter(); if (radiusInFrameSpace.GetUnit() != eStyleUnit_Coord) {
if (radiusStyleCoord.GetUnit() != eStyleUnit_Coord) {
NS_NOTREACHED("unexpected unit"); NS_NOTREACHED("unexpected unit");
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
// Get the radius in frame space. Size radiusInFilterSpace = BlurRadiusToFilterSpace(radiusInFrameSpace.GetCoordValue());
nscoord radiusInFrameSpace = radiusStyleCoord.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 = float radiusInFrameSpaceInCSSPx =
nsPresContext::AppUnitsToFloatCSSPixels(radiusInFrameSpace); nsPresContext::AppUnitsToFloatCSSPixels(aRadiusInFrameSpace);
// Convert the radius to filter space. // Convert the radius to filter space.
gfxSize radiusInFilterSpace(radiusInFrameSpaceInCSSPx, gfxSize radiusInFilterSpace(radiusInFrameSpaceInCSSPx,
@ -101,15 +139,40 @@ nsCSSFilterInstance::SetAttributesForBlur(FilterPrimitiveDescription& aDescr)
// Check the radius limits. // Check the radius limits.
if (radiusInFilterSpace.width < 0 || radiusInFilterSpace.height < 0) { if (radiusInFilterSpace.width < 0 || radiusInFilterSpace.height < 0) {
NS_NOTREACHED("we shouldn't have parsed a negative radius in the style"); NS_NOTREACHED("we shouldn't have parsed a negative radius in the style");
return NS_ERROR_FAILURE; return Size();
} }
gfxFloat maxStdDeviation = (gfxFloat)kMaxStdDeviation; gfxFloat maxStdDeviation = (gfxFloat)kMaxStdDeviation;
radiusInFilterSpace.width = std::min(radiusInFilterSpace.width, maxStdDeviation); radiusInFilterSpace.width = std::min(radiusInFilterSpace.width, maxStdDeviation);
radiusInFilterSpace.height = std::min(radiusInFilterSpace.height, maxStdDeviation); radiusInFilterSpace.height = std::min(radiusInFilterSpace.height, maxStdDeviation);
// Set the radius parameter. return ToSize(radiusInFilterSpace);
aDescr.Attributes().Set(eGaussianBlurStdDeviation, ToSize(radiusInFilterSpace)); }
return NS_OK;
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 int32_t

View File

@ -9,7 +9,11 @@
#include "FilterSupport.h" #include "FilterSupport.h"
#include "gfxMatrix.h" #include "gfxMatrix.h"
#include "gfxRect.h" #include "gfxRect.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/gfx/Types.h"
#include "nsColor.h"
class nsIFrame;
struct nsStyleFilter; struct nsStyleFilter;
template<class T> class nsTArray; template<class T> class nsTArray;
@ -20,8 +24,11 @@ template<class T> class nsTArray;
*/ */
class nsCSSFilterInstance class nsCSSFilterInstance
{ {
typedef mozilla::gfx::Color Color;
typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription; typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
typedef mozilla::gfx::IntPoint IntPoint;
typedef mozilla::gfx::PrimitiveType PrimitiveType; typedef mozilla::gfx::PrimitiveType PrimitiveType;
typedef mozilla::gfx::Size Size;
public: public:
/** /**
@ -34,6 +41,7 @@ public:
* the filtered element's frame space in CSS pixels to filter space. * the filtered element's frame space in CSS pixels to filter space.
*/ */
nsCSSFilterInstance(const nsStyleFilter& aFilter, nsCSSFilterInstance(const nsStyleFilter& aFilter,
nsIFrame *aTargetFrame,
const nsIntRect& mTargetBBoxInFilterSpace, const nsIntRect& mTargetBBoxInFilterSpace,
const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform); const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform);
@ -55,6 +63,7 @@ private:
* Sets aDescr's attributes using the style info in mFilter. * Sets aDescr's attributes using the style info in mFilter.
*/ */
nsresult SetAttributesForBlur(FilterPrimitiveDescription& aDescr); nsresult SetAttributesForBlur(FilterPrimitiveDescription& aDescr);
nsresult SetAttributesForDropShadow(FilterPrimitiveDescription& aDescr);
/** /**
* Returns the index of the last result in the aPrimitiveDescrs, which we'll * Returns the index of the last result in the aPrimitiveDescrs, which we'll
@ -71,11 +80,34 @@ private:
void SetBounds(FilterPrimitiveDescription& aDescr, void SetBounds(FilterPrimitiveDescription& aDescr,
const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs); const nsTArray<FilterPrimitiveDescription>& 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. * The CSS filter originally from the style system.
*/ */
const nsStyleFilter& mFilter; 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 * 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. * input bounds if this CSS filter is the first in the filter chain.

View File

@ -260,7 +260,8 @@ nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter)
} }
// Build primitives for a CSS filter. // Build primitives for a CSS filter.
nsCSSFilterInstance cssFilterInstance(aFilter, mTargetBBoxInFilterSpace, nsCSSFilterInstance cssFilterInstance(aFilter, mTargetFrame,
mTargetBBoxInFilterSpace,
mFrameSpaceInCSSPxToFilterSpaceTransform); mFrameSpaceInCSSPxToFilterSpaceTransform);
return cssFilterInstance.BuildPrimitives(mPrimitiveDescriptions); return cssFilterInstance.BuildPrimitives(mPrimitiveDescriptions);
} }