Bug 948265 - Rename intermediate space to filter space and change filter space origin to user space origin. r=mstange, r=longsonr

This commit is contained in:
Max Vujovic 2014-03-26 16:21:48 -07:00
Родитель 6b12580552
Коммит b79c737e9a
8 изменённых файлов: 151 добавлений и 123 удалений

Просмотреть файл

@ -0,0 +1,17 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<!--
Place the filtered rect and the filter region at (0, 0) in user space.
Remove the filter output before (50, 50) using a clipPath.
-->
<filter id="f" x="0%" y="0%" width="100%" height="100%">
<feTurbulence type="turbulence" baseFrequency="0.01" numOctaves="1"></feTurbulence>
</filter>
<clipPath id="clip-top-left">
<rect x="50" y="50" width="100" height="100"/>
</clipPath>
<rect x="0" y="0" width="150" height="150" filter="url(#f)" clip-path="url(#clip-top-left)"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 669 B

Просмотреть файл

@ -0,0 +1,20 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!--
This test verifies that feTurbulence creates a noise image that remains
anchored at the user space origin, not the filter region origin. The filter
region should act as a viewport into the anchored noise image.
-->
<svg xmlns="http://www.w3.org/2000/svg">
<!--
Place the filtered rect at (75, 75) in user space.
Make the filter region start at (-25, -25) from the top left corner of the rect.
Thus, the filter output should start at (50, 50).
-->
<filter id="f" x="-25%" y="-25%" width="100%" height="100%">
<feTurbulence type="turbulence" baseFrequency="0.01" numOctaves="1"></feTurbulence>
</filter>
<rect x="75" y="75" width="100" height="100" filter="url(#f)"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 837 B

Просмотреть файл

@ -101,6 +101,8 @@ fuzzy(2,2659) skip-if(d2d) == feSpecularLighting-1.svg feSpecularLighting-1-ref.
== fePointLight-zoomed-page.svg fePointLight-zoomed-page-ref.svg
== feTurbulence-offset.svg feTurbulence-offset-ref.svg
pref(layout.css.filters.enabled,true) == multiple-svg-filters.svg multiple-svg-filters-ref.svg
pref(layout.css.filters.enabled,true) == multiple-svg-filters-long-chain.svg multiple-svg-filters-ref.svg
pref(layout.css.filters.enabled,true) == multiple-svg-filters-second-uses-SourceGraphic.svg multiple-svg-filters-ref.svg

Просмотреть файл

@ -95,7 +95,7 @@ skip-if(B2G) == dynamic-pattern-02.svg pass.svg
skip-if(B2G) == dynamic-pattern-contents-01.svg pass.svg
skip-if(B2G) == dynamic-pattern-contents-02.svg pass.svg
== dynamic-rect-01.svg dynamic-rect-01-ref.svg
== dynamic-rect-02.svg dynamic-rect-02-ref.svg
fuzzy-if(d2d&&layersGPUAccelerated,3,1200) == dynamic-rect-02.svg dynamic-rect-02-ref.svg # bug 776038 for Win7, Win8
== dynamic-rect-03.svg dynamic-rect-03-ref.svg
== dynamic-rect-04.xhtml pass.svg
== dynamic-rect-05.svg pass.svg
@ -107,7 +107,7 @@ skip-if(B2G) == dynamic-pattern-contents-02.svg pass.svg
== dynamic-stroke-width-01.svg pass.svg
== dynamic-switch-01.svg pass.svg
== dynamic-text-01.svg dynamic-text-01-ref.svg
fuzzy-if(d2d&&layersGPUAccelerated,2,12739) == dynamic-text-02.svg dynamic-text-02-ref.svg # bug 776038 for Win7
fuzzy-if(d2d&&layersGPUAccelerated,3,12739) == dynamic-text-02.svg dynamic-text-02-ref.svg # bug 776038 for Win7, Win8
fuzzy-if(d2d&&layersGPUAccelerated,2,10539) == dynamic-text-03.svg dynamic-text-03-ref.svg # bug 776038 for Win7
random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),47,89) == dynamic-text-04.svg dynamic-text-04-ref.svg # bug 421587 for WinXP, bug 776038 for Win7
skip-if(B2G) == dynamic-text-05.svg pass.svg

Просмотреть файл

@ -118,7 +118,12 @@ nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
mTargetBBox = aOverrideBBox ?
*aOverrideBBox : nsSVGUtils::GetBBox(mTargetFrame);
nsresult rv = BuildPrimitives();
nsresult rv = ComputeUserSpaceToFilterSpaceScale();
if (NS_FAILED(rv)) {
return;
}
rv = BuildPrimitives();
if (NS_FAILED(rv)) {
return;
}
@ -130,9 +135,9 @@ nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
// Get various transforms:
gfxMatrix filterToUserSpace(mFilterRegion.Width() / mFilterSpaceBounds.width, 0.0f,
0.0f, mFilterRegion.Height() / mFilterSpaceBounds.height,
mFilterRegion.X(), mFilterRegion.Y());
gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.width, 0.0f,
0.0f, mFilterSpaceToUserSpaceScale.height,
0.0f, 0.0f);
// Only used (so only set) when we paint:
if (mPaintCallback) {
@ -164,23 +169,44 @@ nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
mInitialized = true;
}
gfxRect
nsFilterInstance::UserSpaceToFilterSpace(const gfxRect& aRect) const
nsresult
nsFilterInstance::ComputeUserSpaceToFilterSpaceScale()
{
gfxRect r = aRect - mFilterRegion.TopLeft();
r.Scale(mFilterSpaceBounds.width / mFilterRegion.Width(),
mFilterSpaceBounds.height / mFilterRegion.Height());
return r;
gfxMatrix canvasTransform =
nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_OUTERSVG_TM);
if (canvasTransform.IsSingular()) {
// Nothing should be rendered.
return NS_ERROR_FAILURE;
}
mUserSpaceToFilterSpaceScale = canvasTransform.ScaleFactors(true);
if (mUserSpaceToFilterSpaceScale.width <= 0.0f ||
mUserSpaceToFilterSpaceScale.height <= 0.0f) {
// Nothing should be rendered.
return NS_ERROR_FAILURE;
}
mFilterSpaceToUserSpaceScale = gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width,
1.0f / mUserSpaceToFilterSpaceScale.height);
return NS_OK;
}
gfxMatrix
nsFilterInstance::GetUserSpaceToFilterSpaceTransform() const
gfxRect
nsFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
{
gfxFloat widthScale = mFilterSpaceBounds.width / mFilterRegion.Width();
gfxFloat heightScale = mFilterSpaceBounds.height / mFilterRegion.Height();
return gfxMatrix(widthScale, 0.0f,
0.0f, heightScale,
-mFilterRegion.X() * widthScale, -mFilterRegion.Y() * heightScale);
gfxRect filterSpaceRect = aUserSpaceRect;
filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width,
mUserSpaceToFilterSpaceScale.height);
return filterSpaceRect;
}
gfxRect
nsFilterInstance::FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const
{
gfxRect userSpaceRect = aFilterSpaceRect;
userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width,
mFilterSpaceToUserSpaceScale.height);
return userSpaceRect;
}
nsresult
@ -202,9 +228,15 @@ nsFilterInstance::BuildPrimitives()
nsresult
nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter)
{
NS_ASSERTION(mUserSpaceToFilterSpaceScale.width > 0.0f &&
mFilterSpaceToUserSpaceScale.height > 0.0f,
"scale factors between spaces should be positive values");
if (aFilter.GetType() == NS_STYLE_FILTER_URL) {
// Build primitives for an SVG filter.
nsSVGFilterInstance svgFilterInstance(aFilter, mTargetFrame, mTargetBBox);
nsSVGFilterInstance svgFilterInstance(aFilter, mTargetFrame, mTargetBBox,
mUserSpaceToFilterSpaceScale,
mFilterSpaceToUserSpaceScale);
if (!svgFilterInstance.IsInitialized()) {
return NS_ERROR_FAILURE;
}
@ -212,7 +244,7 @@ nsFilterInstance::BuildPrimitivesForFilter(const nsStyleFilter& aFilter)
// For now, we use the last SVG filter region as the overall filter region
// for the filter chain. Eventually, we will compute the overall filter
// using all of the generated FilterPrimitiveDescriptions.
mFilterRegion = svgFilterInstance.GetFilterRegion();
mUserSpaceBounds = svgFilterInstance.GetFilterRegion();
mFilterSpaceBounds = svgFilterInstance.GetFilterSpaceBounds();
// If this overflows, we can at least paint the maximum surface size.
@ -289,10 +321,6 @@ nsFilterInstance::BuildSourcePaint(SourceInfo *aSource,
nsRenderingContext tmpCtx;
tmpCtx.Init(mTargetFrame->PresContext()->DeviceContext(), ctx);
gfxMatrix m = GetUserSpaceToFilterSpaceTransform();
m.Invert();
gfxRect r = m.TransformBounds(mFilterSpaceBounds);
gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert();
gfxContext *gfx = tmpCtx.ThebesContext();
gfx->Multiply(deviceToFilterSpace);
@ -304,7 +332,7 @@ nsFilterInstance::BuildSourcePaint(SourceInfo *aSource,
mTransformRoot);
if (!matrix.IsSingular()) {
gfx->Multiply(matrix);
gfx->Rectangle(r);
gfx->Rectangle(mUserSpaceBounds);
if ((aSource == &mFillPaint &&
nsSVGUtils::SetupCairoFillPaint(mTargetFrame, gfx)) ||
(aSource == &mStrokePaint &&
@ -376,9 +404,7 @@ nsFilterInstance::BuildSourceImage(gfxASurface* aTargetSurface,
nsRenderingContext tmpCtx;
tmpCtx.Init(mTargetFrame->PresContext()->DeviceContext(), ctx);
gfxMatrix m = GetUserSpaceToFilterSpaceTransform();
m.Invert();
gfxRect r = m.TransformBounds(neededRect);
gfxRect r = FilterSpaceToUserSpace(neededRect);
r.RoundOut();
nsIntRect dirty;
if (!gfxUtils::GfxRectToIntRect(r, &dirty))

Просмотреть файл

@ -155,11 +155,6 @@ public:
*/
nsresult ComputeSourceNeededRect(nsRect* aDirty);
/**
* Returns the transform from the filtered element's user space to filter
* space. This will be a simple translation and/or scale.
*/
gfxMatrix GetUserSpaceToFilterSpaceTransform() const;
/**
* Returns the transform from filter space to outer-<svg> device space.
@ -227,7 +222,16 @@ private:
*/
void ComputeNeededBoxes();
/**
* Compute the scale factors between user space and filter space.
*/
nsresult ComputeUserSpaceToFilterSpaceScale();
/**
* Transform a rect between user space and filter space.
*/
gfxRect UserSpaceToFilterSpace(const gfxRect& aUserSpace) const;
gfxRect FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const;
/**
* Converts an nsRect that is relative to a filtered frame's origin (i.e. the
@ -271,9 +275,15 @@ private:
/**
* The "filter region", in the filtered element's user space.
*/
gfxRect mFilterRegion;
gfxRect mUserSpaceBounds;
nsIntRect mFilterSpaceBounds;
/**
* The scale factors between user space and filter space.
*/
gfxSize mUserSpaceToFilterSpaceScale;
gfxSize mFilterSpaceToUserSpaceScale;
/**
* Pre-filter paint bounds of the element that is being filtered, in filter
* space.

Просмотреть файл

@ -26,10 +26,14 @@ using namespace mozilla::gfx;
nsSVGFilterInstance::nsSVGFilterInstance(const nsStyleFilter& aFilter,
nsIFrame *aTargetFrame,
const gfxRect& aTargetBBox) :
const gfxRect& aTargetBBox,
const gfxSize& aUserSpaceToFilterSpaceScale,
const gfxSize& aFilterSpaceToUserSpaceScale) :
mFilter(aFilter),
mTargetFrame(aTargetFrame),
mTargetBBox(aTargetBBox),
mUserSpaceToFilterSpaceScale(aUserSpaceToFilterSpaceScale),
mFilterSpaceToUserSpaceScale(aFilterSpaceToUserSpaceScale),
mInitialized(false) {
// Get the filter frame.
@ -48,12 +52,7 @@ nsSVGFilterInstance::nsSVGFilterInstance(const nsStyleFilter& aFilter,
mPrimitiveUnits =
mFilterFrame->GetEnumValue(SVGFilterElement::PRIMITIVEUNITS);
nsresult rv = ComputeUserSpaceToIntermediateSpaceScale();
if (NS_FAILED(rv)) {
return;
}
rv = ComputeBounds();
nsresult rv = ComputeBounds();
if (NS_FAILED(rv)) {
return;
}
@ -61,21 +60,6 @@ nsSVGFilterInstance::nsSVGFilterInstance(const nsStyleFilter& aFilter,
mInitialized = true;
}
nsresult
nsSVGFilterInstance::ComputeUserSpaceToIntermediateSpaceScale()
{
gfxMatrix canvasTransform =
nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_OUTERSVG_TM);
if (canvasTransform.IsSingular()) {
// Nothing should be rendered.
return NS_ERROR_FAILURE;
}
mUserSpaceToIntermediateSpaceScale = canvasTransform.ScaleFactors(true);
mIntermediateSpaceToUserSpaceScale = gfxSize(1.0 / mUserSpaceToIntermediateSpaceScale.width,
1.0 / mUserSpaceToIntermediateSpaceScale.height);
return NS_OK;
}
nsresult
nsSVGFilterInstance::ComputeBounds()
{
@ -104,10 +88,10 @@ nsSVGFilterInstance::ComputeBounds()
mUserSpaceBounds = nsSVGUtils::GetRelativeRect(filterUnits,
XYWH, mTargetBBox, mTargetFrame);
// Temporarily transform the user space bounds to intermediate space, so we
// Temporarily transform the user space bounds to filter space, so we
// can align them with the pixel boundries of the offscreen surface.
// The offscreen surface has the same scale as intermediate space.
mUserSpaceBounds = UserSpaceToIntermediateSpace(mUserSpaceBounds);
// The offscreen surface has the same scale as filter space.
mUserSpaceBounds = UserSpaceToFilterSpace(mUserSpaceBounds);
mUserSpaceBounds.RoundOut();
if (mUserSpaceBounds.Width() <= 0 || mUserSpaceBounds.Height() <= 0) {
// 0 disables rendering, < 0 is error. dispatch error console warning
@ -115,18 +99,14 @@ nsSVGFilterInstance::ComputeBounds()
return NS_ERROR_FAILURE;
}
// Set the intermediate space bounds.
if (!gfxUtils::GfxRectToIntRect(mUserSpaceBounds, &mIntermediateSpaceBounds)) {
// Set the filter space bounds.
if (!gfxUtils::GfxRectToIntRect(mUserSpaceBounds, &mFilterSpaceBounds)) {
// The filter region is way too big if there is float -> int overflow.
return NS_ERROR_FAILURE;
}
// Set the filter space bounds.
mFilterSpaceBounds = mIntermediateSpaceBounds;
mFilterSpaceBounds.MoveTo(0, 0);
// Undo the temporary transformation of the user space bounds.
mUserSpaceBounds = IntermediateSpaceToUserSpace(mUserSpaceBounds);
mUserSpaceBounds = FilterSpaceToUserSpace(mUserSpaceBounds);
return NS_OK;
}
@ -189,14 +169,14 @@ nsSVGFilterInstance::GetPrimitiveNumber(uint8_t aCtxType, float aValue) const
switch (aCtxType) {
case SVGContentUtils::X:
return value * mUserSpaceToIntermediateSpaceScale.width;
return value * mUserSpaceToFilterSpaceScale.width;
case SVGContentUtils::Y:
return value * mUserSpaceToIntermediateSpaceScale.height;
return value * mUserSpaceToFilterSpaceScale.height;
case SVGContentUtils::XY:
default:
return value * SVGContentUtils::ComputeNormalizedHypotenuse(
mUserSpaceToIntermediateSpaceScale.width,
mUserSpaceToIntermediateSpaceScale.height);
mUserSpaceToFilterSpaceScale.width,
mUserSpaceToFilterSpaceScale.height);
}
}
@ -223,24 +203,18 @@ nsSVGFilterInstance::ConvertLocation(const Point3D& aPoint) const
gfxRect
nsSVGFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
{
return UserSpaceToIntermediateSpace(aUserSpaceRect - mUserSpaceBounds.TopLeft());
gfxRect filterSpaceRect = aUserSpaceRect;
filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width,
mUserSpaceToFilterSpaceScale.height);
return filterSpaceRect;
}
gfxRect
nsSVGFilterInstance::UserSpaceToIntermediateSpace(const gfxRect& aUserSpaceRect) const
nsSVGFilterInstance::FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const
{
gfxRect intermediateSpaceRect = aUserSpaceRect;
intermediateSpaceRect.Scale(mUserSpaceToIntermediateSpaceScale.width,
mUserSpaceToIntermediateSpaceScale.height);
return intermediateSpaceRect;
}
gfxRect
nsSVGFilterInstance::IntermediateSpaceToUserSpace(const gfxRect& aIntermediateSpaceRect) const
{
gfxRect userSpaceRect = aIntermediateSpaceRect;
userSpaceRect.Scale(mIntermediateSpaceToUserSpaceScale.width,
mIntermediateSpaceToUserSpaceScale.height);
gfxRect userSpaceRect = aFilterSpaceRect;
userSpaceRect.Scale(mFilterSpaceToUserSpaceScale.width,
mFilterSpaceToUserSpaceScale.height);
return userSpaceRect;
}

Просмотреть файл

@ -39,20 +39,15 @@ class SVGFilterElement;
* CSS pixel space. The origin for an HTML element is the top left corner of
* its border box.
*
* "intermediate space"
* "filter space"
* User space scaled to device pixels. Shares the same origin as user space.
* This space is the same across chained SVG and CSS filters. To compute the
* overall filter space for a chain, we first need to build each filter's
* FilterPrimitiveDescriptions in some common space. That space is
* intermediate space.
* filter space.
*
* "filter space"
* Intermediate space translated to the origin of this SVG filter's
* filter region. This space may be different for each filter in a chain.
*
* To understand the spaces better, let's take an example filter that defines a
* filter region:
* <filter id="f" x="-15" y="-15" width="130" height="130">...</filter>
* To understand the spaces better, let's take an example filter:
* <filter id="f">...</filter>
*
* And apply the filter to a div element:
* <div style="filter: url(#f); ...">...</div>
@ -65,21 +60,10 @@ class SVGFilterElement;
* The point will be inset 10 CSS pixels from both the top and left edges of the
* div element's border box.
*
* Now, let's transform the point from user space to intermediate space:
* "intermediate space point" = "user space point" * "device pixels per CSS pixel"
* "intermediate space point" = (10, 10) * 2
* "intermediate space point" = (20, 20)
*
* Next, let's transform the point from user space to filter space:
* "filter space point" = ("user space point" - "filter region position in user space") * "device pixels per CSS pixel"
* "filter space point" = ((10, 10) - (-15, -15)) * 2
* "filter space point" = (50, 50)
*
* Similarly, we can convert the point from intermediate space to filter space:
* "filter space point" = "intermediate space point" - "filter region position in intermediate space"
* "filter space point" = "intermediate space point" - ("filter region position in user space" * "device pixels per CSS pixel")
* "filter space point" = (20, 20) - ((-15, -15) * 2)
* "filter space point" = (50, 50)
* Now, let's transform the point from user space to filter space:
* "filter space point" = "user space point" * "device pixels per CSS pixel"
* "filter space point" = (10, 10) * 2
* "filter space point" = (20, 20)
*/
class nsSVGFilterInstance
{
@ -97,7 +81,9 @@ public:
*/
nsSVGFilterInstance(const nsStyleFilter& aFilter,
nsIFrame *aTargetFrame,
const gfxRect& aTargetBBox);
const gfxRect& aTargetBBox,
const gfxSize& aUserSpaceToFilterSpaceScale,
const gfxSize& aFilterSpaceToUserSpaceScale);
/**
* Returns true if the filter instance was created successfully.
@ -176,10 +162,9 @@ private:
float GetPrimitiveNumber(uint8_t aCtxType, float aValue) const;
/**
* Transform a rect between user space and intermediate space.
* Transform a rect between user space and filter space.
*/
gfxRect UserSpaceToIntermediateSpace(const gfxRect& aUserSpaceRect) const;
gfxRect IntermediateSpaceToUserSpace(const gfxRect& aIntermediateSpaceRect) const;
gfxRect FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const;
/**
* Returns the transform from frame space to the coordinate space that
@ -201,12 +186,7 @@ private:
nsTArray<int32_t>& aSourceIndices);
/**
* Compute the scale factors between user space and intermediate space.
*/
nsresult ComputeUserSpaceToIntermediateSpaceScale();
/**
* Compute the filter region in user space, intermediate space, and filter
* Compute the filter region in user space, filter space, and filter
* space.
*/
nsresult ComputeBounds();
@ -240,14 +220,13 @@ private:
* The "filter region" in various spaces.
*/
gfxRect mUserSpaceBounds;
nsIntRect mIntermediateSpaceBounds;
nsIntRect mFilterSpaceBounds;
/**
* The scale factors between user space and intermediate space.
* The scale factors between user space and filter space.
*/
gfxSize mUserSpaceToIntermediateSpaceScale;
gfxSize mIntermediateSpaceToUserSpaceScale;
gfxSize mUserSpaceToFilterSpaceScale;
gfxSize mFilterSpaceToUserSpaceScale;
/**
* The 'primitiveUnits' attribute value (objectBoundingBox or userSpaceOnUse).