mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 11:55:49 +00:00
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:
parent
bce83391f5
commit
6e4e723923
@ -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;
|
||||
}
|
||||
|
211
gfx/2d/ExtendInputEffectD2D1.cpp
Normal file
211
gfx/2d/ExtendInputEffectD2D1.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
88
gfx/2d/ExtendInputEffectD2D1.h
Normal file
88
gfx/2d/ExtendInputEffectD2D1.h
Normal 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
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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(); }
|
||||
|
||||
|
@ -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
|
||||
|
@ -53,6 +53,7 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
SOURCES += [
|
||||
'DrawTargetD2D.cpp',
|
||||
'DrawTargetD2D1.cpp',
|
||||
'ExtendInputEffectD2D1.cpp',
|
||||
'FilterNodeD2D1.cpp',
|
||||
'PathD2D.cpp',
|
||||
'RadialGradientEffectD2D1.cpp',
|
||||
|
@ -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:
|
||||
{
|
||||
|
10
layout/reftests/svg/filters/outside-sourcegraphic-1.svg
Normal file
10
layout/reftests/svg/filters/outside-sourcegraphic-1.svg
Normal 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 |
13
layout/reftests/svg/filters/outside-sourcegraphic-2.svg
Normal file
13
layout/reftests/svg/filters/outside-sourcegraphic-2.svg
Normal 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 |
15
layout/reftests/svg/filters/outside-sourcegraphic-3.svg
Normal file
15
layout/reftests/svg/filters/outside-sourcegraphic-3.svg
Normal 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 |
@ -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 |
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user