1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ConicGradientEffectD2D1.h"
11 #include "ShadersD2D1.h"
12 #include "HelpersD2D.h"
18 TEXTW(#X) // This macro creates a single string from multiple lines of text.
20 static const PCWSTR kXmlDescription
=
24 <!-- System Properties
-->
25 <Property name
='DisplayName' type
='string' value
='ConicGradientEffect'/>
26 <Property name
='Author' type
='string' value
='Mozilla'/>
27 <Property name
='Category' type
='string' value
='Pattern effects'/>
28 <Property name
='Description' type
='string' value
='This effect is used to render CSS conic gradients.'/>
30 <Input name
='Geometry'/>
32 <Property name
='StopCollection' type
='iunknown'>
33 <Property name
='DisplayName' type
='string' value
='Gradient stop collection'/>
35 <Property name
='Center' type
='vector2'>
36 <Property name
='DisplayName' type
='string' value
='Gradient center'/>
38 <Property name
='Angle' type
='vector2'>
39 <Property name
='DisplayName' type
='string' value
='Gradient angle'/>
41 <Property name
='StartOffset' type
='float'>
42 <Property name
='DisplayName' type
='string' value
='Start stop offset'/>
44 <Property name
='EndOffset' type
='float'>
45 <Property name
='DisplayName' type
='string' value
='End stop offset'/>
47 <Property name
='Transform' type
='matrix3x2'>
48 <Property name
='DisplayName' type
='string' value
='Transform applied to the pattern'/>
54 // {091fda1d-857e-4b1e-828f-1c839d9b7897}
55 static const GUID GUID_SampleConicGradientPS
= {
59 {0x82, 0x8f, 0x1c, 0x83, 0x9d, 0x9b, 0x78, 0x97}};
64 ConicGradientEffectD2D1::ConicGradientEffectD2D1()
66 mCenter(D2D1::Vector2F(0, 0)),
70 mTransform(D2D1::IdentityMatrix())
75 ConicGradientEffectD2D1::Initialize(ID2D1EffectContext
* pContextInternal
,
76 ID2D1TransformGraph
* pTransformGraph
) {
79 hr
= pContextInternal
->LoadPixelShader(GUID_SampleConicGradientPS
,
80 SampleConicGradientPS
,
81 sizeof(SampleConicGradientPS
));
87 hr
= pTransformGraph
->SetSingleTransformNode(this);
93 mEffectContext
= pContextInternal
;
99 ConicGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType
) {
100 if (changeType
== D2D1_CHANGE_TYPE_NONE
) {
104 // We'll need to inverse transform our pixel, precompute inverse here.
105 Matrix mat
= ToMatrix(mTransform
);
111 if (!mStopCollection
) {
115 HRESULT hr
= mDrawInfo
->SetPixelShader(GUID_SampleConicGradientPS
);
121 RefPtr
<ID2D1ResourceTexture
> tex
= CreateGradientTexture();
122 hr
= mDrawInfo
->SetResourceTexture(1, tex
);
128 struct PSConstantBuffer
{
133 float repeat_correct
;
139 PSConstantBuffer buffer
= {
140 {mCenter
.x
, mCenter
.y
},
144 mStopCollection
->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP
? 1.0f
: 0.0f
,
145 mStopCollection
->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR
? 1.0f
: 0.0f
,
147 {mat
._11
, mat
._21
, mat
._31
, 0.0f
, mat
._12
, mat
._22
, mat
._32
, 0.0f
}};
149 hr
= mDrawInfo
->SetPixelShaderConstantBuffer((BYTE
*)&buffer
, sizeof(buffer
));
159 ConicGradientEffectD2D1::SetGraph(ID2D1TransformGraph
* pGraph
) {
160 return pGraph
->SetSingleTransformNode(this);
163 IFACEMETHODIMP_(ULONG
)
164 ConicGradientEffectD2D1::AddRef() { return ++mRefCount
; }
166 IFACEMETHODIMP_(ULONG
)
167 ConicGradientEffectD2D1::Release() {
176 ConicGradientEffectD2D1::QueryInterface(const IID
& aIID
, void** aPtr
) {
181 if (aIID
== IID_IUnknown
) {
182 *aPtr
= static_cast<IUnknown
*>(static_cast<ID2D1EffectImpl
*>(this));
183 } else if (aIID
== IID_ID2D1EffectImpl
) {
184 *aPtr
= static_cast<ID2D1EffectImpl
*>(this);
185 } else if (aIID
== IID_ID2D1DrawTransform
) {
186 *aPtr
= static_cast<ID2D1DrawTransform
*>(this);
187 } else if (aIID
== IID_ID2D1Transform
) {
188 *aPtr
= static_cast<ID2D1Transform
*>(this);
189 } else if (aIID
== IID_ID2D1TransformNode
) {
190 *aPtr
= static_cast<ID2D1TransformNode
*>(this);
192 return E_NOINTERFACE
;
195 static_cast<IUnknown
*>(*aPtr
)->AddRef();
200 ConicGradientEffectD2D1::MapInputRectsToOutputRect(
201 const D2D1_RECT_L
* pInputRects
, const D2D1_RECT_L
* pInputOpaqueSubRects
,
202 UINT32 inputRectCount
, D2D1_RECT_L
* pOutputRect
,
203 D2D1_RECT_L
* pOutputOpaqueSubRect
) {
204 if (inputRectCount
!= 1) {
208 *pOutputRect
= *pInputRects
;
209 *pOutputOpaqueSubRect
= *pInputOpaqueSubRects
;
214 ConicGradientEffectD2D1::MapOutputRectToInputRects(
215 const D2D1_RECT_L
* pOutputRect
, D2D1_RECT_L
* pInputRects
,
216 UINT32 inputRectCount
) const {
217 if (inputRectCount
!= 1) {
221 *pInputRects
= *pOutputRect
;
226 ConicGradientEffectD2D1::MapInvalidRect(UINT32 inputIndex
,
227 D2D1_RECT_L invalidInputRect
,
228 D2D1_RECT_L
* pInvalidOutputRect
) const {
229 MOZ_ASSERT(inputIndex
== 0);
231 *pInvalidOutputRect
= invalidInputRect
;
236 ConicGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo
* pDrawInfo
) {
237 mDrawInfo
= pDrawInfo
;
242 ConicGradientEffectD2D1::Register(ID2D1Factory1
* aFactory
) {
243 D2D1_PROPERTY_BINDING bindings
[] = {
244 D2D1_VALUE_TYPE_BINDING(L
"StopCollection",
245 &ConicGradientEffectD2D1::SetStopCollection
,
246 &ConicGradientEffectD2D1::GetStopCollection
),
247 D2D1_VALUE_TYPE_BINDING(L
"Center", &ConicGradientEffectD2D1::SetCenter
,
248 &ConicGradientEffectD2D1::GetCenter
),
249 D2D1_VALUE_TYPE_BINDING(L
"Angle", &ConicGradientEffectD2D1::SetAngle
,
250 &ConicGradientEffectD2D1::GetAngle
),
251 D2D1_VALUE_TYPE_BINDING(L
"StartOffset",
252 &ConicGradientEffectD2D1::SetStartOffset
,
253 &ConicGradientEffectD2D1::GetStartOffset
),
254 D2D1_VALUE_TYPE_BINDING(L
"EndOffset",
255 &ConicGradientEffectD2D1::SetEndOffset
,
256 &ConicGradientEffectD2D1::GetEndOffset
),
257 D2D1_VALUE_TYPE_BINDING(L
"Transform",
258 &ConicGradientEffectD2D1::SetTransform
,
259 &ConicGradientEffectD2D1::GetTransform
)};
260 HRESULT hr
= aFactory
->RegisterEffectFromString(
261 CLSID_ConicGradientEffect
, kXmlDescription
, bindings
, ARRAYSIZE(bindings
),
265 gfxWarning() << "Failed to register radial gradient effect.";
270 void ConicGradientEffectD2D1::Unregister(ID2D1Factory1
* aFactory
) {
271 aFactory
->UnregisterEffect(CLSID_ConicGradientEffect
);
274 HRESULT __stdcall
ConicGradientEffectD2D1::CreateEffect(
275 IUnknown
** aEffectImpl
) {
276 *aEffectImpl
= static_cast<ID2D1EffectImpl
*>(new ConicGradientEffectD2D1());
277 (*aEffectImpl
)->AddRef();
283 ConicGradientEffectD2D1::SetStopCollection(IUnknown
* aStopCollection
) {
284 if (SUCCEEDED(aStopCollection
->QueryInterface(
285 (ID2D1GradientStopCollection
**)getter_AddRefs(mStopCollection
)))) {
292 already_AddRefed
<ID2D1ResourceTexture
>
293 ConicGradientEffectD2D1::CreateGradientTexture() {
294 std::vector
<D2D1_GRADIENT_STOP
> rawStops
;
295 rawStops
.resize(mStopCollection
->GetGradientStopCount());
296 mStopCollection
->GetGradientStops(&rawStops
.front(), rawStops
.size());
298 std::vector
<unsigned char> textureData
;
299 textureData
.resize(4096 * 4);
300 unsigned char* texData
= &textureData
.front();
302 float prevColorPos
= 0;
303 float nextColorPos
= rawStops
[0].position
;
304 D2D1_COLOR_F prevColor
= rawStops
[0].color
;
305 D2D1_COLOR_F nextColor
= prevColor
;
306 uint32_t stopPosition
= 1;
308 // Not the most optimized way but this will do for now.
309 for (int i
= 0; i
< 4096; i
++) {
310 // The 4095 seems a little counter intuitive, but we want the gradient
311 // color at offset 0 at the first pixel, and at offset 1.0f at the last
313 float pos
= float(i
) / 4095;
315 while (pos
> nextColorPos
) {
316 prevColor
= nextColor
;
317 prevColorPos
= nextColorPos
;
318 if (rawStops
.size() > stopPosition
) {
319 nextColor
= rawStops
[stopPosition
].color
;
320 nextColorPos
= rawStops
[stopPosition
++].position
;
328 if (nextColorPos
!= prevColorPos
) {
329 interp
= (pos
- prevColorPos
) / (nextColorPos
- prevColorPos
);
334 DeviceColor
newColor(prevColor
.r
+ (nextColor
.r
- prevColor
.r
) * interp
,
335 prevColor
.g
+ (nextColor
.g
- prevColor
.g
) * interp
,
336 prevColor
.b
+ (nextColor
.b
- prevColor
.b
) * interp
,
337 prevColor
.a
+ (nextColor
.a
- prevColor
.a
) * interp
);
339 // Note D2D expects RGBA here!!
340 texData
[i
* 4] = (char)(255.0f
* newColor
.r
);
341 texData
[i
* 4 + 1] = (char)(255.0f
* newColor
.g
);
342 texData
[i
* 4 + 2] = (char)(255.0f
* newColor
.b
);
343 texData
[i
* 4 + 3] = (char)(255.0f
* newColor
.a
);
346 RefPtr
<ID2D1ResourceTexture
> tex
;
349 UINT32 stride
= 4096 * 4;
350 D2D1_RESOURCE_TEXTURE_PROPERTIES props
;
351 // Older shader models do not support 1D textures. So just use a width x 1
353 props
.dimensions
= 2;
354 UINT32 dims
[] = {width
, 1};
355 props
.extents
= dims
;
356 props
.channelDepth
= D2D1_CHANNEL_DEPTH_4
;
357 props
.bufferPrecision
= D2D1_BUFFER_PRECISION_8BPC_UNORM
;
358 props
.filter
= D2D1_FILTER_MIN_MAG_MIP_LINEAR
;
359 D2D1_EXTEND_MODE extendMode
[] = {mStopCollection
->GetExtendMode(),
360 mStopCollection
->GetExtendMode()};
361 props
.extendModes
= extendMode
;
363 HRESULT hr
= mEffectContext
->CreateResourceTexture(
364 nullptr, &props
, &textureData
.front(), &stride
, 4096 * 4,
365 getter_AddRefs(tex
));
368 gfxWarning() << "Failed to create resource texture: " << hexa(hr
);
375 } // namespace mozilla