1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "RadialGradientEffectD2D1.h"
10 #include "ShadersD2D1.h"
11 #include "HelpersD2D.h"
16 #define XML(X) TEXTW(#X) // This macro creates a single string from multiple lines of text.
18 static const PCWSTR kXmlDescription
=
22 <!-- System Properties
-->
23 <Property name
='DisplayName' type
='string' value
='RadialGradientEffect'/>
24 <Property name
='Author' type
='string' value
='Mozilla'/>
25 <Property name
='Category' type
='string' value
='Pattern effects'/>
26 <Property name
='Description' type
='string' value
='This effect is used to render radial gradients in a manner compliant with the 2D Canvas specification.'/>
28 <Input name
='Geometry'/>
30 <Property name
='StopCollection' type
='iunknown'>
31 <Property name
='DisplayName' type
='string' value
='Gradient stop collection'/>
33 <Property name
='Center1' type
='vector2'>
34 <Property name
='DisplayName' type
='string' value
='Inner circle center'/>
36 <Property name
='Center2' type
='vector2'>
37 <Property name
='DisplayName' type
='string' value
='Outer circle center'/>
39 <Property name
='Radius1' type
='float'>
40 <Property name
='DisplayName' type
='string' value
='Inner circle radius'/>
42 <Property name
='Radius2' type
='float'>
43 <Property name
='DisplayName' type
='string' value
='Outer circle radius'/>
45 <Property name
='Transform' type
='matrix3x2'>
46 <Property name
='DisplayName' type
='string' value
='Transform applied to the pattern'/>
52 // {FB947CDA-718E-40CC-AE7B-D255830D7D14}
53 static const GUID GUID_SampleRadialGradientPS
=
54 {0xfb947cda, 0x718e, 0x40cc, {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}};
55 // {2C468128-6546-453C-8E25-F2DF0DE10A0F}
56 static const GUID GUID_SampleRadialGradientA0PS
=
57 {0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}};
62 RadialGradientEffectD2D1::RadialGradientEffectD2D1()
64 , mCenter1(D2D1::Vector2F(0, 0))
65 , mCenter2(D2D1::Vector2F(0, 0))
68 , mTransform(D2D1::IdentityMatrix())
74 RadialGradientEffectD2D1::Initialize(ID2D1EffectContext
* pContextInternal
, ID2D1TransformGraph
* pTransformGraph
)
78 hr
= pContextInternal
->LoadPixelShader(GUID_SampleRadialGradientPS
, SampleRadialGradientPS
, sizeof(SampleRadialGradientPS
));
84 hr
= pContextInternal
->LoadPixelShader(GUID_SampleRadialGradientA0PS
, SampleRadialGradientA0PS
, sizeof(SampleRadialGradientA0PS
));
90 hr
= pTransformGraph
->SetSingleTransformNode(this);
96 mEffectContext
= pContextInternal
;
102 RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType
)
104 if (changeType
== D2D1_CHANGE_TYPE_NONE
) {
108 // We'll need to inverse transform our pixel, precompute inverse here.
109 Matrix mat
= ToMatrix(mTransform
);
115 if (!mStopCollection
) {
119 D2D1_POINT_2F dc
= D2D1::Point2F(mCenter2
.x
- mCenter1
.x
, mCenter2
.y
- mCenter2
.y
);
120 float dr
= mRadius2
- mRadius1
;
121 float A
= dc
.x
* dc
.x
+ dc
.y
* dc
.y
- dr
* dr
;
126 hr
= mDrawInfo
->SetPixelShader(GUID_SampleRadialGradientA0PS
);
128 hr
= mDrawInfo
->SetPixelShader(GUID_SampleRadialGradientPS
);
135 RefPtr
<ID2D1ResourceTexture
> tex
= CreateGradientTexture();
136 hr
= mDrawInfo
->SetResourceTexture(1, tex
);
142 struct PSConstantBuffer
154 PSConstantBuffer buffer
= { { dc
.x
, dc
.y
, dr
}, 0,
155 { mCenter1
.x
, mCenter1
.y
},
156 A
, mRadius1
, mRadius1
* mRadius1
,
157 { 0, 0, 0 }, { mat
._11
, mat
._21
, mat
._31
, 0,
158 mat
._12
, mat
._22
, mat
._32
, 0 } };
160 hr
= mDrawInfo
->SetPixelShaderConstantBuffer((BYTE
*)&buffer
, sizeof(buffer
));
170 RadialGradientEffectD2D1::SetGraph(ID2D1TransformGraph
* pGraph
)
172 return pGraph
->SetSingleTransformNode(this);
175 IFACEMETHODIMP_(ULONG
)
176 RadialGradientEffectD2D1::AddRef()
181 IFACEMETHODIMP_(ULONG
)
182 RadialGradientEffectD2D1::Release()
192 RadialGradientEffectD2D1::QueryInterface(const IID
&aIID
, void **aPtr
)
198 if (aIID
== IID_IUnknown
) {
199 *aPtr
= static_cast<IUnknown
*>(static_cast<ID2D1EffectImpl
*>(this));
200 } else if (aIID
== IID_ID2D1EffectImpl
) {
201 *aPtr
= static_cast<ID2D1EffectImpl
*>(this);
202 } else if (aIID
== IID_ID2D1DrawTransform
) {
203 *aPtr
= static_cast<ID2D1DrawTransform
*>(this);
204 } else if (aIID
== IID_ID2D1Transform
) {
205 *aPtr
= static_cast<ID2D1Transform
*>(this);
206 } else if (aIID
== IID_ID2D1TransformNode
) {
207 *aPtr
= static_cast<ID2D1TransformNode
*>(this);
209 return E_NOINTERFACE
;
212 static_cast<IUnknown
*>(*aPtr
)->AddRef();
217 RadialGradientEffectD2D1::MapInputRectsToOutputRect(const D2D1_RECT_L
* pInputRects
,
218 const D2D1_RECT_L
* pInputOpaqueSubRects
,
219 UINT32 inputRectCount
,
220 D2D1_RECT_L
* pOutputRect
,
221 D2D1_RECT_L
* pOutputOpaqueSubRect
)
223 if (inputRectCount
!= 1) {
227 *pOutputRect
= *pInputRects
;
228 *pOutputOpaqueSubRect
= *pInputOpaqueSubRects
;
233 RadialGradientEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L
* pOutputRect
,
234 D2D1_RECT_L
* pInputRects
,
235 UINT32 inputRectCount
) const
237 if (inputRectCount
!= 1) {
241 *pInputRects
= *pOutputRect
;
246 RadialGradientEffectD2D1::MapInvalidRect(UINT32 inputIndex
,
247 D2D1_RECT_L invalidInputRect
,
248 D2D1_RECT_L
* pInvalidOutputRect
) const
250 MOZ_ASSERT(inputIndex
= 0);
252 *pInvalidOutputRect
= invalidInputRect
;
257 RadialGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo
*pDrawInfo
)
259 mDrawInfo
= pDrawInfo
;
264 RadialGradientEffectD2D1::Register(ID2D1Factory1
*aFactory
)
266 D2D1_PROPERTY_BINDING bindings
[] = {
267 D2D1_VALUE_TYPE_BINDING(L
"StopCollection", &RadialGradientEffectD2D1::SetStopCollection
,
268 &RadialGradientEffectD2D1::GetStopCollection
),
269 D2D1_VALUE_TYPE_BINDING(L
"Center1", &RadialGradientEffectD2D1::SetCenter1
, &RadialGradientEffectD2D1::GetCenter1
),
270 D2D1_VALUE_TYPE_BINDING(L
"Center2", &RadialGradientEffectD2D1::SetCenter2
, &RadialGradientEffectD2D1::GetCenter2
),
271 D2D1_VALUE_TYPE_BINDING(L
"Radius1", &RadialGradientEffectD2D1::SetRadius1
, &RadialGradientEffectD2D1::GetRadius1
),
272 D2D1_VALUE_TYPE_BINDING(L
"Radius2", &RadialGradientEffectD2D1::SetRadius2
, &RadialGradientEffectD2D1::GetRadius2
),
273 D2D1_VALUE_TYPE_BINDING(L
"Transform", &RadialGradientEffectD2D1::SetTransform
, &RadialGradientEffectD2D1::GetTransform
)
275 HRESULT hr
= aFactory
->RegisterEffectFromString(CLSID_RadialGradientEffect
, kXmlDescription
, bindings
, ARRAYSIZE(bindings
), CreateEffect
);
278 gfxWarning() << "Failed to register radial gradient effect.";
284 RadialGradientEffectD2D1::CreateEffect(IUnknown
**aEffectImpl
)
286 *aEffectImpl
= static_cast<ID2D1EffectImpl
*>(new RadialGradientEffectD2D1());
287 (*aEffectImpl
)->AddRef();
293 RadialGradientEffectD2D1::SetStopCollection(IUnknown
*aStopCollection
)
295 if (SUCCEEDED(aStopCollection
->QueryInterface((ID2D1GradientStopCollection
**)byRef(mStopCollection
)))) {
302 TemporaryRef
<ID2D1ResourceTexture
>
303 RadialGradientEffectD2D1::CreateGradientTexture()
305 std::vector
<D2D1_GRADIENT_STOP
> rawStops
;
306 rawStops
.resize(mStopCollection
->GetGradientStopCount());
307 mStopCollection
->GetGradientStops(&rawStops
.front(), rawStops
.size());
309 std::vector
<unsigned char> textureData
;
310 textureData
.resize(4096 * 4);
311 unsigned char *texData
= &textureData
.front();
313 float prevColorPos
= 0;
314 float nextColorPos
= 1.0f
;
315 D2D1_COLOR_F prevColor
= rawStops
[0].color
;
316 D2D1_COLOR_F nextColor
= prevColor
;
318 if (rawStops
.size() >= 2) {
319 nextColor
= rawStops
[1].color
;
320 nextColorPos
= rawStops
[1].position
;
323 uint32_t stopPosition
= 2;
325 // Not the most optimized way but this will do for now.
326 for (int i
= 0; i
< 4096; i
++) {
327 // The 4095 seems a little counter intuitive, but we want the gradient
328 // color at offset 0 at the first pixel, and at offset 1.0f at the last
330 float pos
= float(i
) / 4095;
332 while (pos
> nextColorPos
) {
333 prevColor
= nextColor
;
334 prevColorPos
= nextColorPos
;
335 if (rawStops
.size() > stopPosition
) {
336 nextColor
= rawStops
[stopPosition
].color
;
337 nextColorPos
= rawStops
[stopPosition
++].position
;
345 if (nextColorPos
!= prevColorPos
) {
346 interp
= (pos
- prevColorPos
) / (nextColorPos
- prevColorPos
);
351 Color
newColor(prevColor
.r
+ (nextColor
.r
- prevColor
.r
) * interp
,
352 prevColor
.g
+ (nextColor
.g
- prevColor
.g
) * interp
,
353 prevColor
.b
+ (nextColor
.b
- prevColor
.b
) * interp
,
354 prevColor
.a
+ (nextColor
.a
- prevColor
.a
) * interp
);
356 // Note D2D expects RGBA here!!
357 texData
[i
* 4] = (char)(255.0f
* newColor
.r
);
358 texData
[i
* 4 + 1] = (char)(255.0f
* newColor
.g
);
359 texData
[i
* 4 + 2] = (char)(255.0f
* newColor
.b
);
360 texData
[i
* 4 + 3] = (char)(255.0f
* newColor
.a
);
363 RefPtr
<ID2D1ResourceTexture
> tex
;
366 UINT32 stride
= 4096 * 4;
367 D2D1_RESOURCE_TEXTURE_PROPERTIES props
;
368 props
.dimensions
= 1;
369 props
.extents
= &width
;
370 props
.channelDepth
= D2D1_CHANNEL_DEPTH_4
;
371 props
.bufferPrecision
= D2D1_BUFFER_PRECISION_8BPC_UNORM
;
372 props
.filter
= D2D1_FILTER_MIN_MAG_MIP_LINEAR
;
373 D2D1_EXTEND_MODE extendMode
= mStopCollection
->GetExtendMode();
374 props
.extendModes
= &extendMode
;
376 HRESULT hr
= mEffectContext
->CreateResourceTexture(nullptr, &props
, &textureData
.front(), &stride
, 4096 * 4, byRef(tex
));
379 gfxWarning() << "Failed to create resource texture: " << hr
;