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-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

View File

@ -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<FilterPrimitiveDescription>& 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

View File

@ -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 T> class nsTArray;
@ -20,8 +24,11 @@ template<class T> 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<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.
*/
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.

View File

@ -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);
}