Bug 1092634 - Let feColorMatrix and feComponentTransfer generate output outside their input's bounds. r=Bas

This makes the color matrix and component transfer Moz2D filters generate
an infinite output, which is then cropped to the primitive's filter
primitive subregion by a subsequent crop filter node. This still gives us
different behavior than other browser when the primitive subregion is
overridden using the x/y/width/height attributes - other browsers either
ignore those completely (IE) or only let them crop the default subregion
(which is defined to be the same as the input subregion) and not enlargen
it - but I'll fix that in a separate bug.
This commit is contained in:
Markus Stange 2015-02-09 14:04:11 -05:00
parent bce83391f5
commit 6e4e723923
13 changed files with 515 additions and 64 deletions

View File

@ -20,6 +20,7 @@
#include "FilterNodeSoftware.h"
#include "FilterNodeD2D1.h"
#include "ExtendInputEffectD2D1.h"
#include <dwrite.h>
@ -2697,6 +2698,12 @@ DrawTargetD2D::factory()
gfxWarning() << "Failed to create Direct2D factory.";
}
RefPtr<ID2D1Factory1> factoryD2D1;
hr = mFactory->QueryInterface((ID2D1Factory1**)byRef(factoryD2D1));
if (SUCCEEDED(hr)) {
ExtendInputEffectD2D1::Register(factoryD2D1);
}
return mFactory;
}
@ -2704,6 +2711,12 @@ void
DrawTargetD2D::CleanupD2D()
{
if (mFactory) {
RefPtr<ID2D1Factory1> factoryD2D1;
HRESULT hr = mFactory->QueryInterface((ID2D1Factory1**)byRef(factoryD2D1));
if (SUCCEEDED(hr)) {
ExtendInputEffectD2D1::Unregister(factoryD2D1);
}
mFactory->Release();
mFactory = nullptr;
}

View File

@ -0,0 +1,211 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#include "ExtendInputEffectD2D1.h"
#include "Logging.h"
#include "ShadersD2D1.h"
#include "HelpersD2D.h"
#include <vector>
#define TEXTW(x) L##x
#define XML(X) TEXTW(#X) // This macro creates a single string from multiple lines of text.
static const PCWSTR kXmlDescription =
XML(
<?xml version='1.0'?>
<Effect>
<!-- System Properties -->
<Property name='DisplayName' type='string' value='ExtendInputEffect'/>
<Property name='Author' type='string' value='Mozilla'/>
<Property name='Category' type='string' value='Utility Effects'/>
<Property name='Description' type='string' value='This effect is used to extend the output rect of any input effect to a specified rect.'/>
<Inputs>
<Input name='InputEffect'/>
</Inputs>
<Property name='OutputRect' type='vector4'>
<Property name='DisplayName' type='string' value='Output Rect'/>
</Property>
</Effect>
);
// {FB947CDA-718E-40CC-AE7B-D255830D7D14}
static const GUID GUID_SampleExtendInputPS =
{0xfb947cda, 0x718e, 0x40cc, {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}};
// {2C468128-6546-453C-8E25-F2DF0DE10A0F}
static const GUID GUID_SampleExtendInputA0PS =
{0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}};
namespace mozilla {
namespace gfx {
ExtendInputEffectD2D1::ExtendInputEffectD2D1()
: mRefCount(0)
, mOutputRect(D2D1::Vector4F(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX))
{
}
IFACEMETHODIMP
ExtendInputEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph)
{
HRESULT hr;
hr = pTransformGraph->SetSingleTransformNode(this);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
IFACEMETHODIMP
ExtendInputEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
{
return S_OK;
}
IFACEMETHODIMP
ExtendInputEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph)
{
return E_NOTIMPL;
}
IFACEMETHODIMP_(ULONG)
ExtendInputEffectD2D1::AddRef()
{
return ++mRefCount;
}
IFACEMETHODIMP_(ULONG)
ExtendInputEffectD2D1::Release()
{
if (!--mRefCount) {
delete this;
return 0;
}
return mRefCount;
}
IFACEMETHODIMP
ExtendInputEffectD2D1::QueryInterface(const IID &aIID, void **aPtr)
{
if (!aPtr) {
return E_POINTER;
}
if (aIID == IID_IUnknown) {
*aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this));
} else if (aIID == IID_ID2D1EffectImpl) {
*aPtr = static_cast<ID2D1EffectImpl*>(this);
} else if (aIID == IID_ID2D1DrawTransform) {
*aPtr = static_cast<ID2D1DrawTransform*>(this);
} else if (aIID == IID_ID2D1Transform) {
*aPtr = static_cast<ID2D1Transform*>(this);
} else if (aIID == IID_ID2D1TransformNode) {
*aPtr = static_cast<ID2D1TransformNode*>(this);
} else {
return E_NOINTERFACE;
}
static_cast<IUnknown*>(*aPtr)->AddRef();
return S_OK;
}
static D2D1_RECT_L
ConvertFloatToLongRect(const D2D1_VECTOR_4F& aRect)
{
// Clamp values to LONG range. We can't use std::min/max here because we want
// the comparison to operate on a type that's different from the type of the
// result.
return D2D1::RectL(aRect.x <= LONG_MIN ? LONG_MIN : LONG(aRect.x),
aRect.y <= LONG_MIN ? LONG_MIN : LONG(aRect.y),
aRect.z >= LONG_MAX ? LONG_MAX : LONG(aRect.z),
aRect.w >= LONG_MAX ? LONG_MAX : LONG(aRect.w));
}
static D2D1_RECT_L
IntersectRect(const D2D1_RECT_L& aRect1, const D2D1_RECT_L& aRect2)
{
return D2D1::RectL(std::max(aRect1.left, aRect2.left),
std::max(aRect1.top, aRect2.top),
std::min(aRect1.right, aRect2.right),
std::min(aRect1.bottom, aRect2.bottom));
}
IFACEMETHODIMP
ExtendInputEffectD2D1::MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
const D2D1_RECT_L* pInputOpaqueSubRects,
UINT32 inputRectCount,
D2D1_RECT_L* pOutputRect,
D2D1_RECT_L* pOutputOpaqueSubRect)
{
// This transform only accepts one input, so there will only be one input rect.
if (inputRectCount != 1) {
return E_INVALIDARG;
}
// Set the output rect to the specified rect. This is the whole purpose of this effect.
*pOutputRect = ConvertFloatToLongRect(mOutputRect);
*pOutputOpaqueSubRect = IntersectRect(*pOutputRect, pInputOpaqueSubRects[0]);
return S_OK;
}
IFACEMETHODIMP
ExtendInputEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
D2D1_RECT_L* pInputRects,
UINT32 inputRectCount) const
{
if (inputRectCount != 1) {
return E_INVALIDARG;
}
*pInputRects = *pOutputRect;
return S_OK;
}
IFACEMETHODIMP
ExtendInputEffectD2D1::MapInvalidRect(UINT32 inputIndex,
D2D1_RECT_L invalidInputRect,
D2D1_RECT_L* pInvalidOutputRect) const
{
MOZ_ASSERT(inputIndex = 0);
*pInvalidOutputRect = invalidInputRect;
return S_OK;
}
HRESULT
ExtendInputEffectD2D1::Register(ID2D1Factory1 *aFactory)
{
D2D1_PROPERTY_BINDING bindings[] = {
D2D1_VALUE_TYPE_BINDING(L"OutputRect", &ExtendInputEffectD2D1::SetOutputRect, &ExtendInputEffectD2D1::GetOutputRect),
};
HRESULT hr = aFactory->RegisterEffectFromString(CLSID_ExtendInputEffect, kXmlDescription, bindings, 1, CreateEffect);
if (FAILED(hr)) {
gfxWarning() << "Failed to register extend input effect.";
}
return hr;
}
void
ExtendInputEffectD2D1::Unregister(ID2D1Factory1 *aFactory)
{
aFactory->UnregisterEffect(CLSID_ExtendInputEffect);
}
HRESULT __stdcall
ExtendInputEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
{
*aEffectImpl = static_cast<ID2D1EffectImpl*>(new ExtendInputEffectD2D1());
(*aEffectImpl)->AddRef();
return S_OK;
}
}
}

View File

@ -0,0 +1,88 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#ifndef MOZILLA_GFX_EXTENDINPUTEFFECTD2D1_H_
#define MOZILLA_GFX_EXTENDINPUTEFFECTD2D1_H_
#include <d2d1_1.h>
#include <d2d1effectauthor.h>
#include <d2d1effecthelpers.h>
#include "2D.h"
#include "mozilla/Attributes.h"
// {97143DC6-CBC4-4DD4-A8BA-13342B0BA46D}
DEFINE_GUID(CLSID_ExtendInputEffect,
0x5fb55c7c, 0xd795, 0x4ba3, 0xa9, 0x5c, 0x22, 0x82, 0x5d, 0x0c, 0x4d, 0xf7);
namespace mozilla {
namespace gfx {
enum {
EXTENDINPUT_PROP_OUTPUT_RECT = 0
};
// An effect type that passes through its input unchanged but sets the effect's
// output rect to a specified rect. Unlike the built-in Crop effect, the
// ExtendInput effect can extend the input rect, and not just make it smaller.
// The added margins are filled with transparent black.
// Some effects have different output depending on their input effect's output
// rect, for example the Border effect (which repeats the edges of its input
// effect's output rect) or the component transfer and color matrix effects
// (which can transform transparent pixels into non-transparent ones, but only
// inside their input effect's output rect).
class ExtendInputEffectD2D1 MOZ_FINAL : public ID2D1EffectImpl
, public ID2D1DrawTransform
{
public:
// ID2D1EffectImpl
IFACEMETHODIMP Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph);
IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE changeType);
IFACEMETHODIMP SetGraph(ID2D1TransformGraph* pGraph);
// IUnknown
IFACEMETHODIMP_(ULONG) AddRef();
IFACEMETHODIMP_(ULONG) Release();
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppOutput);
// ID2D1Transform
IFACEMETHODIMP MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
const D2D1_RECT_L* pInputOpaqueSubRects,
UINT32 inputRectCount,
D2D1_RECT_L* pOutputRect,
D2D1_RECT_L* pOutputOpaqueSubRect);
IFACEMETHODIMP MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
D2D1_RECT_L* pInputRects,
UINT32 inputRectCount) const;
IFACEMETHODIMP MapInvalidRect(UINT32 inputIndex,
D2D1_RECT_L invalidInputRect,
D2D1_RECT_L* pInvalidOutputRect) const;
// ID2D1TransformNode
IFACEMETHODIMP_(UINT32) GetInputCount() const { return 1; }
// ID2D1DrawTransform
IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo) { return S_OK; }
static HRESULT Register(ID2D1Factory1* aFactory);
static void Unregister(ID2D1Factory1* aFactory);
static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl);
HRESULT SetOutputRect(D2D1_VECTOR_4F aOutputRect)
{ mOutputRect = aOutputRect; return S_OK; }
D2D1_VECTOR_4F GetOutputRect() const { return mOutputRect; }
private:
ExtendInputEffectD2D1();
uint32_t mRefCount;
D2D1_VECTOR_4F mOutputRect;
};
}
}
#undef SIMPLE_PROP
#endif

View File

@ -12,6 +12,7 @@
#include "SourceSurfaceD2DTarget.h"
#include "DrawTargetD2D.h"
#include "DrawTargetD2D1.h"
#include "ExtendInputEffectD2D1.h"
namespace mozilla {
namespace gfx {
@ -521,6 +522,20 @@ static inline REFCLSID GetCLDIDForFilterType(FilterType aType)
return GUID_NULL;
}
static bool
IsTransferFilterType(FilterType aType)
{
switch (aType) {
case FilterType::LINEAR_TRANSFER:
case FilterType::GAMMA_TRANSFER:
case FilterType::TABLE_TRANSFER:
case FilterType::DISCRETE_TRANSFER:
return true;
default:
return false;
}
}
/* static */
TemporaryRef<FilterNode>
FilterNodeD2D1::Create(ID2D1DeviceContext *aDC, FilterType aType)
@ -539,15 +554,21 @@ FilterNodeD2D1::Create(ID2D1DeviceContext *aDC, FilterType aType)
return nullptr;
}
switch (aType) {
case FilterType::LINEAR_TRANSFER:
case FilterType::GAMMA_TRANSFER:
case FilterType::TABLE_TRANSFER:
case FilterType::DISCRETE_TRANSFER:
return new FilterNodeComponentTransferD2D1(aDC, effect, aType);
default:
return new FilterNodeD2D1(effect, aType);
RefPtr<FilterNodeD2D1> filter = new FilterNodeD2D1(effect, aType);
if (IsTransferFilterType(aType) || aType == FilterType::COLOR_MATRIX) {
// These filters can produce non-transparent output from transparent
// input pixels, and we want them to have an unbounded output region.
filter = new FilterNodeExtendInputAdapterD2D1(aDC, filter, aType);
}
if (IsTransferFilterType(aType)) {
// Component transfer filters should appear to apply on unpremultiplied
// colors, but the D2D1 effects apply on premultiplied colors.
filter = new FilterNodePremultiplyAdapterD2D1(aDC, filter, aType);
}
return filter.forget();
}
void
@ -828,16 +849,9 @@ FilterNodeConvolveD2D1::FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC)
// attribute). So if our input surface or filter is smaller than the source
// rect, we need to add transparency around it until we reach the edges of
// the source rect, and only then do any repeating or edge duplicating.
// Unfortunately, D2D1 does not have any "extend with transparency" effect.
// (The crop effect can only cut off parts, it can't make the output rect
// bigger.) And the border effect does not have a source rect attribute -
// it only looks at the output rect of its input filter or surface.
// So we use the following trick to extend the input size to the source rect:
// Instead of feeding the input directly into the border effect, we first
// composite it with a transparent flood effect (which is infinite-sized) and
// use a crop effect on the result in order to get the right size. Then we
// feed the cropped composition into the border effect, which then finally
// feeds into the convolve matrix effect.
// Unfortunately, the border effect does not have a source rect attribute -
// it only looks at the output rect of its input filter or surface. So we use
// our custom ExtendInput effect to adjust the output rect of our input.
// All of this is only necessary when our edge mode is not EDGE_MODE_NONE, so
// we update the filter chain dynamically in UpdateChain().
@ -852,33 +866,13 @@ FilterNodeConvolveD2D1::FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC)
mEffect->SetValue(D2D1_CONVOLVEMATRIX_PROP_BORDER_MODE, D2D1_BORDER_MODE_SOFT);
hr = aDC->CreateEffect(CLSID_D2D1Flood, byRef(mFloodEffect));
hr = aDC->CreateEffect(CLSID_ExtendInputEffect, byRef(mExtendInputEffect));
if (FAILED(hr)) {
gfxWarning() << "Failed to create ConvolveMatrix filter!";
return;
}
mFloodEffect->SetValue(D2D1_FLOOD_PROP_COLOR, D2D1::Vector4F(0.0f, 0.0f, 0.0f, 0.0f));
hr = aDC->CreateEffect(CLSID_D2D1Composite, byRef(mCompositeEffect));
if (FAILED(hr)) {
gfxWarning() << "Failed to create ConvolveMatrix filter!";
return;
}
mCompositeEffect->SetInputEffect(1, mFloodEffect.get());
hr = aDC->CreateEffect(CLSID_D2D1Crop, byRef(mCropEffect));
if (FAILED(hr)) {
gfxWarning() << "Failed to create ConvolveMatrix filter!";
return;
}
mCropEffect->SetInputEffect(0, mCompositeEffect.get());
hr = aDC->CreateEffect(CLSID_D2D1Border, byRef(mBorderEffect));
if (FAILED(hr)) {
@ -886,7 +880,7 @@ FilterNodeConvolveD2D1::FilterNodeConvolveD2D1(ID2D1DeviceContext *aDC)
return;
}
mBorderEffect->SetInputEffect(0, mCropEffect.get());
mBorderEffect->SetInputEffect(0, mExtendInputEffect.get());
UpdateChain();
UpdateSourceRect();
@ -915,7 +909,7 @@ FilterNodeConvolveD2D1::SetAttribute(uint32_t aIndex, uint32_t aValue)
ID2D1Effect*
FilterNodeConvolveD2D1::InputEffect()
{
return mEdgeMode == EDGE_MODE_NONE ? mEffect.get() : mCompositeEffect.get();
return mEdgeMode == EDGE_MODE_NONE ? mEffect.get() : mExtendInputEffect.get();
}
void
@ -927,8 +921,7 @@ FilterNodeConvolveD2D1::UpdateChain()
// input --> convolvematrix
//
// EDGE_MODE_DUPLICATE or EDGE_MODE_WRAP:
// input -------v
// flood --> composite --> crop --> border --> convolvematrix
// input --> extendinput --> border --> convolvematrix
//
// mEffect is convolvematrix.
@ -1006,14 +999,36 @@ FilterNodeConvolveD2D1::UpdateOffset()
void
FilterNodeConvolveD2D1::UpdateSourceRect()
{
mCropEffect->SetValue(D2D1_CROP_PROP_RECT,
D2D1::RectF(Float(mSourceRect.x), Float(mSourceRect.y),
Float(mSourceRect.XMost()), Float(mSourceRect.YMost())));
mExtendInputEffect->SetValue(EXTENDINPUT_PROP_OUTPUT_RECT,
D2D1::Vector4F(Float(mSourceRect.x), Float(mSourceRect.y),
Float(mSourceRect.XMost()), Float(mSourceRect.YMost())));
}
FilterNodeComponentTransferD2D1::FilterNodeComponentTransferD2D1(ID2D1DeviceContext *aDC,
ID2D1Effect *aEffect, FilterType aType)
: FilterNodeD2D1(aEffect, aType)
FilterNodeExtendInputAdapterD2D1::FilterNodeExtendInputAdapterD2D1(ID2D1DeviceContext *aDC,
FilterNodeD2D1 *aFilterNode, FilterType aType)
: FilterNodeD2D1(aFilterNode->MainEffect(), aType)
, mWrappedFilterNode(aFilterNode)
{
// We have an mEffect that looks at the bounds of the input effect, and we
// want mEffect to regard its input as unbounded. So we take the input,
// pipe it through an ExtendInput effect (which has an infinite output rect
// by default), and feed the resulting unbounded composition into mEffect.
HRESULT hr;
hr = aDC->CreateEffect(CLSID_ExtendInputEffect, byRef(mExtendInputEffect));
if (FAILED(hr)) {
gfxWarning() << "Failed to create extend input effect for filter: " << hexa(hr);
return;
}
aFilterNode->InputEffect()->SetInputEffect(0, mExtendInputEffect.get());
}
FilterNodePremultiplyAdapterD2D1::FilterNodePremultiplyAdapterD2D1(ID2D1DeviceContext *aDC,
FilterNodeD2D1 *aFilterNode, FilterType aType)
: FilterNodeD2D1(aFilterNode->MainEffect(), aType)
{
// D2D1 component transfer effects do strange things when it comes to
// premultiplication.
@ -1059,8 +1074,8 @@ FilterNodeComponentTransferD2D1::FilterNodeComponentTransferD2D1(ID2D1DeviceCont
return;
}
mEffect->SetInputEffect(0, mPrePremultiplyEffect.get());
mPostUnpremultiplyEffect->SetInputEffect(0, mEffect.get());
aFilterNode->InputEffect()->SetInputEffect(0, mPrePremultiplyEffect.get());
mPostUnpremultiplyEffect->SetInputEffect(0, aFilterNode->OutputEffect());
}
}

View File

@ -54,14 +54,15 @@ public:
// the DrawTarget that we will draw to.
virtual void WillDraw(DrawTarget *aDT);
virtual ID2D1Effect* MainEffect() { return mEffect.get(); }
virtual ID2D1Effect* InputEffect() { return mEffect.get(); }
virtual ID2D1Effect* OutputEffect() { return mEffect.get(); }
protected:
friend class DrawTargetD2D1;
friend class DrawTargetD2D;
friend class FilterNodeConvolveD2D1;
virtual ID2D1Effect* InputEffect() { return mEffect.get(); }
virtual ID2D1Effect* OutputEffect() { return mEffect.get(); }
void InitUnmappedProperties();
RefPtr<ID2D1Effect> mEffect;
@ -83,17 +84,14 @@ public:
virtual void SetAttribute(uint32_t aIndex, const IntPoint &aValue);
virtual void SetAttribute(uint32_t aIndex, const IntRect &aValue);
protected:
virtual ID2D1Effect* InputEffect();
virtual ID2D1Effect* InputEffect() MOZ_OVERRIDE;
private:
void UpdateChain();
void UpdateOffset();
void UpdateSourceRect();
RefPtr<ID2D1Effect> mFloodEffect;
RefPtr<ID2D1Effect> mCompositeEffect;
RefPtr<ID2D1Effect> mCropEffect;
RefPtr<ID2D1Effect> mExtendInputEffect;
RefPtr<ID2D1Effect> mBorderEffect;
ConvolveMatrixEdgeMode mEdgeMode;
IntPoint mTarget;
@ -101,13 +99,26 @@ private:
IntRect mSourceRect;
};
class FilterNodeComponentTransferD2D1 : public FilterNodeD2D1
class FilterNodeExtendInputAdapterD2D1 : public FilterNodeD2D1
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeComponentTransferD2D1)
FilterNodeComponentTransferD2D1(ID2D1DeviceContext *aDC, ID2D1Effect *aEffect, FilterType aType);
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeExtendInputAdapterD2D1)
FilterNodeExtendInputAdapterD2D1(ID2D1DeviceContext *aDC, FilterNodeD2D1 *aFilterNode, FilterType aType);
virtual ID2D1Effect* InputEffect() MOZ_OVERRIDE { return mExtendInputEffect.get(); }
virtual ID2D1Effect* OutputEffect() MOZ_OVERRIDE { return mWrappedFilterNode->OutputEffect(); }
private:
RefPtr<FilterNodeD2D1> mWrappedFilterNode;
RefPtr<ID2D1Effect> mExtendInputEffect;
};
class FilterNodePremultiplyAdapterD2D1 : public FilterNodeD2D1
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplyAdapterD2D1)
FilterNodePremultiplyAdapterD2D1(ID2D1DeviceContext *aDC, FilterNodeD2D1 *aFilterNode, FilterType aType);
protected:
virtual ID2D1Effect* InputEffect() MOZ_OVERRIDE { return mPrePremultiplyEffect.get(); }
virtual ID2D1Effect* OutputEffect() MOZ_OVERRIDE { return mPostUnpremultiplyEffect.get(); }

View File

@ -1374,6 +1374,9 @@ FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
IntRect
FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
{
if (mMatrix._54 > 0.0f) {
return aRect;
}
return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
}
@ -1731,7 +1734,10 @@ FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRe
IntRect
FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect)
{
return GetInputRectInRect(IN_TRANSFER_IN, aRect);
if (mDisableA) {
return GetInputRectInRect(IN_TRANSFER_IN, aRect);
}
return aRect;
}
int32_t

View File

@ -53,6 +53,7 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
SOURCES += [
'DrawTargetD2D.cpp',
'DrawTargetD2D1.cpp',
'ExtendInputEffectD2D1.cpp',
'FilterNodeD2D1.cpp',
'PathD2D.cpp',
'RadialGradientEffectD2D1.cpp',

View File

@ -1389,6 +1389,42 @@ FilterSupport::ComputeResultChangeRegion(const FilterDescription& aFilter,
return resultChangeRegions[resultChangeRegions.Length() - 1];
}
static float
ResultOfZeroUnderTransferFunction(const AttributeMap& aFunctionAttributes)
{
switch (aFunctionAttributes.GetUint(eComponentTransferFunctionType)) {
case SVG_FECOMPONENTTRANSFER_TYPE_TABLE:
{
const nsTArray<float>& tableValues =
aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
if (tableValues.Length() < 2) {
return 0.0f;
}
return tableValues[0];
}
case SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE:
{
const nsTArray<float>& tableValues =
aFunctionAttributes.GetFloats(eComponentTransferFunctionTableValues);
if (tableValues.Length() < 1) {
return 0.0f;
}
return tableValues[0];
}
case SVG_FECOMPONENTTRANSFER_TYPE_LINEAR:
return aFunctionAttributes.GetFloat(eComponentTransferFunctionIntercept);
case SVG_FECOMPONENTTRANSFER_TYPE_GAMMA:
return aFunctionAttributes.GetFloat(eComponentTransferFunctionOffset);
case SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY:
default:
return 0.0f;
}
}
nsIntRegion
FilterSupport::PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& aDescription,
const nsTArray<nsIntRegion>& aInputExtents)
@ -1439,6 +1475,27 @@ FilterSupport::PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& a
return ThebesIntRect(aDescription.PrimitiveSubregion());
}
case PrimitiveType::ColorMatrix:
{
if (atts.GetUint(eColorMatrixType) == (uint32_t)SVG_FECOLORMATRIX_TYPE_MATRIX) {
const nsTArray<float>& values = atts.GetFloats(eColorMatrixValues);
if (values[19] > 0.0f) {
return ThebesIntRect(aDescription.PrimitiveSubregion());
}
}
return aInputExtents[0];
}
case PrimitiveType::ComponentTransfer:
{
AttributeMap functionAttributes =
atts.GetAttributeMap(eComponentTransferFunctionA);
if (ResultOfZeroUnderTransferFunction(functionAttributes) > 0.0f) {
return ThebesIntRect(aDescription.PrimitiveSubregion());
}
return aInputExtents[0];
}
case PrimitiveType::Turbulence:
case PrimitiveType::Image:
{

View File

@ -0,0 +1,10 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="f" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feFlood flood-color="lime"/>
</filter>
<rect width="100" height="100" filter="url(#f)"/>
</svg>

After

Width:  |  Height:  |  Size: 348 B

View File

@ -0,0 +1,13 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="f" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feColorMatrix type="matrix" values="0 0 0 0 0
0 0 0 0 1
0 0 0 0 0
0 0 0 0 1"/>
</filter>
<rect width="100" height="100" filter="url(#f)"/>
</svg>

After

Width:  |  Height:  |  Size: 525 B

View File

@ -0,0 +1,15 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="f" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feComponentTransfer>
<feFuncR type="table" tableValues="0 0" />
<feFuncG type="table" tableValues="1 1" />
<feFuncB type="table" tableValues="0 0" />
<feFuncA type="table" tableValues="1 1" />
</feComponentTransfer>
</filter>
<rect width="100" height="100" filter="url(#f)"/>
</svg>

After

Width:  |  Height:  |  Size: 563 B

View File

@ -0,0 +1,7 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<rect width="200" height="200" fill="lime"/>
</svg>

After

Width:  |  Height:  |  Size: 213 B

View File

@ -116,3 +116,7 @@ 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
== outside-sourcegraphic-1.svg outside-sourcegraphic-ref.svg
== outside-sourcegraphic-2.svg outside-sourcegraphic-ref.svg
== outside-sourcegraphic-3.svg outside-sourcegraphic-ref.svg