Bug 1874684 - Part 17: Fix uninitialised variable warnings from clang-tidy. r=allstarschh
[gecko.git] / gfx / 2d / RadialGradientEffectD2D1.cpp
blob34a5d9dcdb6da50aabd5654135b2470e89b386f7
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 "RadialGradientEffectD2D1.h"
9 #include "Logging.h"
11 #include "ShadersD2D1.h"
12 #include "HelpersD2D.h"
14 #include <vector>
16 #define TEXTW(x) L##x
17 #define XML(X) \
18 TEXTW(#X) // This macro creates a single string from multiple lines of text.
20 static const PCWSTR kXmlDescription =
21 XML(
22 <?xml version='1.0'?>
23 <Effect>
24 <!-- System Properties -->
25 <Property name='DisplayName' type='string' value='RadialGradientEffect'/>
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 radial gradients in a manner compliant with the 2D Canvas specification.'/>
29 <Inputs>
30 <Input name='Geometry'/>
31 </Inputs>
32 <Property name='StopCollection' type='iunknown'>
33 <Property name='DisplayName' type='string' value='Gradient stop collection'/>
34 </Property>
35 <Property name='Center1' type='vector2'>
36 <Property name='DisplayName' type='string' value='Inner circle center'/>
37 </Property>
38 <Property name='Center2' type='vector2'>
39 <Property name='DisplayName' type='string' value='Outer circle center'/>
40 </Property>
41 <Property name='Radius1' type='float'>
42 <Property name='DisplayName' type='string' value='Inner circle radius'/>
43 </Property>
44 <Property name='Radius2' type='float'>
45 <Property name='DisplayName' type='string' value='Outer circle radius'/>
46 </Property>
47 <Property name='Transform' type='matrix3x2'>
48 <Property name='DisplayName' type='string' value='Transform applied to the pattern'/>
49 </Property>
51 </Effect>
54 // {FB947CDA-718E-40CC-AE7B-D255830D7D14}
55 static const GUID GUID_SampleRadialGradientPS = {
56 0xfb947cda,
57 0x718e,
58 0x40cc,
59 {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}};
60 // {2C468128-6546-453C-8E25-F2DF0DE10A0F}
61 static const GUID GUID_SampleRadialGradientA0PS = {
62 0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}};
64 namespace mozilla {
65 namespace gfx {
67 RadialGradientEffectD2D1::RadialGradientEffectD2D1()
68 : mRefCount(0),
69 mCenter1(D2D1::Vector2F(0, 0)),
70 mCenter2(D2D1::Vector2F(0, 0)),
71 mRadius1(0),
72 mRadius2(0),
73 mTransform(D2D1::IdentityMatrix())
77 IFACEMETHODIMP
78 RadialGradientEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal,
79 ID2D1TransformGraph* pTransformGraph) {
80 HRESULT hr;
82 hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientPS,
83 SampleRadialGradientPS,
84 sizeof(SampleRadialGradientPS));
86 if (FAILED(hr)) {
87 return hr;
90 hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientA0PS,
91 SampleRadialGradientA0PS,
92 sizeof(SampleRadialGradientA0PS));
94 if (FAILED(hr)) {
95 return hr;
98 hr = pTransformGraph->SetSingleTransformNode(this);
100 if (FAILED(hr)) {
101 return hr;
104 mEffectContext = pContextInternal;
106 return S_OK;
109 IFACEMETHODIMP
110 RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType) {
111 if (changeType == D2D1_CHANGE_TYPE_NONE) {
112 return S_OK;
115 // We'll need to inverse transform our pixel, precompute inverse here.
116 Matrix mat = ToMatrix(mTransform);
117 if (!mat.Invert()) {
118 // Singular
119 return S_OK;
122 if (!mStopCollection) {
123 return S_OK;
126 D2D1_POINT_2F dc =
127 D2D1::Point2F(mCenter2.x - mCenter1.x, mCenter2.y - mCenter1.y);
128 float dr = mRadius2 - mRadius1;
129 float A = dc.x * dc.x + dc.y * dc.y - dr * dr;
131 HRESULT hr;
133 if (A == 0) {
134 hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientA0PS);
135 } else {
136 hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientPS);
139 if (FAILED(hr)) {
140 return hr;
143 RefPtr<ID2D1ResourceTexture> tex = CreateGradientTexture();
144 hr = mDrawInfo->SetResourceTexture(1, tex);
146 if (FAILED(hr)) {
147 return hr;
150 struct PSConstantBuffer {
151 float diff[3];
152 float padding;
153 float center1[2];
154 float A;
155 float radius1;
156 float sq_radius1;
157 float repeat_correct;
158 float allow_odd;
159 float padding2[1];
160 float transform[8];
163 PSConstantBuffer buffer = {
164 {dc.x, dc.y, dr},
165 0.0f,
166 {mCenter1.x, mCenter1.y},
168 mRadius1,
169 mRadius1 * mRadius1,
170 mStopCollection->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP ? 1.0f : 0.0f,
171 mStopCollection->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR ? 1.0f : 0.0f,
172 {0.0f},
173 {mat._11, mat._21, mat._31, 0.0f, mat._12, mat._22, mat._32, 0.0f}};
175 hr = mDrawInfo->SetPixelShaderConstantBuffer((BYTE*)&buffer, sizeof(buffer));
177 if (FAILED(hr)) {
178 return hr;
181 return S_OK;
184 IFACEMETHODIMP
185 RadialGradientEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph) {
186 return pGraph->SetSingleTransformNode(this);
189 IFACEMETHODIMP_(ULONG)
190 RadialGradientEffectD2D1::AddRef() { return ++mRefCount; }
192 IFACEMETHODIMP_(ULONG)
193 RadialGradientEffectD2D1::Release() {
194 if (!--mRefCount) {
195 delete this;
196 return 0;
198 return mRefCount;
201 IFACEMETHODIMP
202 RadialGradientEffectD2D1::QueryInterface(const IID& aIID, void** aPtr) {
203 if (!aPtr) {
204 return E_POINTER;
207 if (aIID == IID_IUnknown) {
208 *aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this));
209 } else if (aIID == IID_ID2D1EffectImpl) {
210 *aPtr = static_cast<ID2D1EffectImpl*>(this);
211 } else if (aIID == IID_ID2D1DrawTransform) {
212 *aPtr = static_cast<ID2D1DrawTransform*>(this);
213 } else if (aIID == IID_ID2D1Transform) {
214 *aPtr = static_cast<ID2D1Transform*>(this);
215 } else if (aIID == IID_ID2D1TransformNode) {
216 *aPtr = static_cast<ID2D1TransformNode*>(this);
217 } else {
218 return E_NOINTERFACE;
221 static_cast<IUnknown*>(*aPtr)->AddRef();
222 return S_OK;
225 IFACEMETHODIMP
226 RadialGradientEffectD2D1::MapInputRectsToOutputRect(
227 const D2D1_RECT_L* pInputRects, const D2D1_RECT_L* pInputOpaqueSubRects,
228 UINT32 inputRectCount, D2D1_RECT_L* pOutputRect,
229 D2D1_RECT_L* pOutputOpaqueSubRect) {
230 if (inputRectCount != 1) {
231 return E_INVALIDARG;
234 *pOutputRect = *pInputRects;
235 *pOutputOpaqueSubRect = *pInputOpaqueSubRects;
236 return S_OK;
239 IFACEMETHODIMP
240 RadialGradientEffectD2D1::MapOutputRectToInputRects(
241 const D2D1_RECT_L* pOutputRect, D2D1_RECT_L* pInputRects,
242 UINT32 inputRectCount) const {
243 if (inputRectCount != 1) {
244 return E_INVALIDARG;
247 *pInputRects = *pOutputRect;
248 return S_OK;
251 IFACEMETHODIMP
252 RadialGradientEffectD2D1::MapInvalidRect(
253 UINT32 inputIndex, D2D1_RECT_L invalidInputRect,
254 D2D1_RECT_L* pInvalidOutputRect) const {
255 MOZ_ASSERT(inputIndex == 0);
257 *pInvalidOutputRect = invalidInputRect;
258 return S_OK;
261 IFACEMETHODIMP
262 RadialGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo* pDrawInfo) {
263 mDrawInfo = pDrawInfo;
264 return S_OK;
267 HRESULT
268 RadialGradientEffectD2D1::Register(ID2D1Factory1* aFactory) {
269 D2D1_PROPERTY_BINDING bindings[] = {
270 D2D1_VALUE_TYPE_BINDING(L"StopCollection",
271 &RadialGradientEffectD2D1::SetStopCollection,
272 &RadialGradientEffectD2D1::GetStopCollection),
273 D2D1_VALUE_TYPE_BINDING(L"Center1", &RadialGradientEffectD2D1::SetCenter1,
274 &RadialGradientEffectD2D1::GetCenter1),
275 D2D1_VALUE_TYPE_BINDING(L"Center2", &RadialGradientEffectD2D1::SetCenter2,
276 &RadialGradientEffectD2D1::GetCenter2),
277 D2D1_VALUE_TYPE_BINDING(L"Radius1", &RadialGradientEffectD2D1::SetRadius1,
278 &RadialGradientEffectD2D1::GetRadius1),
279 D2D1_VALUE_TYPE_BINDING(L"Radius2", &RadialGradientEffectD2D1::SetRadius2,
280 &RadialGradientEffectD2D1::GetRadius2),
281 D2D1_VALUE_TYPE_BINDING(L"Transform",
282 &RadialGradientEffectD2D1::SetTransform,
283 &RadialGradientEffectD2D1::GetTransform)};
284 HRESULT hr = aFactory->RegisterEffectFromString(
285 CLSID_RadialGradientEffect, kXmlDescription, bindings,
286 ARRAYSIZE(bindings), CreateEffect);
288 if (FAILED(hr)) {
289 gfxWarning() << "Failed to register radial gradient effect.";
291 return hr;
294 void RadialGradientEffectD2D1::Unregister(ID2D1Factory1* aFactory) {
295 aFactory->UnregisterEffect(CLSID_RadialGradientEffect);
298 HRESULT __stdcall RadialGradientEffectD2D1::CreateEffect(
299 IUnknown** aEffectImpl) {
300 *aEffectImpl = static_cast<ID2D1EffectImpl*>(new RadialGradientEffectD2D1());
301 (*aEffectImpl)->AddRef();
303 return S_OK;
306 HRESULT
307 RadialGradientEffectD2D1::SetStopCollection(IUnknown* aStopCollection) {
308 if (SUCCEEDED(aStopCollection->QueryInterface(
309 (ID2D1GradientStopCollection**)getter_AddRefs(mStopCollection)))) {
310 return S_OK;
313 return E_INVALIDARG;
316 already_AddRefed<ID2D1ResourceTexture>
317 RadialGradientEffectD2D1::CreateGradientTexture() {
318 std::vector<D2D1_GRADIENT_STOP> rawStops;
319 rawStops.resize(mStopCollection->GetGradientStopCount());
320 mStopCollection->GetGradientStops(&rawStops.front(), rawStops.size());
322 std::vector<unsigned char> textureData;
323 textureData.resize(4096 * 4);
324 unsigned char* texData = &textureData.front();
326 float prevColorPos = 0;
327 float nextColorPos = 1.0f;
328 D2D1_COLOR_F prevColor = rawStops[0].color;
329 D2D1_COLOR_F nextColor = prevColor;
331 if (rawStops.size() >= 2) {
332 nextColor = rawStops[1].color;
333 nextColorPos = rawStops[1].position;
336 uint32_t stopPosition = 2;
338 // Not the most optimized way but this will do for now.
339 for (int i = 0; i < 4096; i++) {
340 // The 4095 seems a little counter intuitive, but we want the gradient
341 // color at offset 0 at the first pixel, and at offset 1.0f at the last
342 // pixel.
343 float pos = float(i) / 4095;
345 while (pos > nextColorPos) {
346 prevColor = nextColor;
347 prevColorPos = nextColorPos;
348 if (rawStops.size() > stopPosition) {
349 nextColor = rawStops[stopPosition].color;
350 nextColorPos = rawStops[stopPosition++].position;
351 } else {
352 nextColorPos = 1.0f;
356 float interp;
358 if (nextColorPos != prevColorPos) {
359 interp = (pos - prevColorPos) / (nextColorPos - prevColorPos);
360 } else {
361 interp = 0;
364 DeviceColor newColor(prevColor.r + (nextColor.r - prevColor.r) * interp,
365 prevColor.g + (nextColor.g - prevColor.g) * interp,
366 prevColor.b + (nextColor.b - prevColor.b) * interp,
367 prevColor.a + (nextColor.a - prevColor.a) * interp);
369 // Note D2D expects RGBA here!!
370 texData[i * 4] = (unsigned char)(255.0f * newColor.r);
371 texData[i * 4 + 1] = (unsigned char)(255.0f * newColor.g);
372 texData[i * 4 + 2] = (unsigned char)(255.0f * newColor.b);
373 texData[i * 4 + 3] = (unsigned char)(255.0f * newColor.a);
376 RefPtr<ID2D1ResourceTexture> tex;
378 UINT32 width = 4096;
379 UINT32 stride = 4096 * 4;
380 D2D1_RESOURCE_TEXTURE_PROPERTIES props;
381 // Older shader models do not support 1D textures. So just use a width x 1
382 // texture.
383 props.dimensions = 2;
384 UINT32 dims[] = {width, 1};
385 props.extents = dims;
386 props.channelDepth = D2D1_CHANNEL_DEPTH_4;
387 props.bufferPrecision = D2D1_BUFFER_PRECISION_8BPC_UNORM;
388 props.filter = D2D1_FILTER_MIN_MAG_MIP_LINEAR;
389 D2D1_EXTEND_MODE extendMode[] = {mStopCollection->GetExtendMode(),
390 mStopCollection->GetExtendMode()};
391 props.extendModes = extendMode;
393 HRESULT hr = mEffectContext->CreateResourceTexture(
394 nullptr, &props, &textureData.front(), &stride, 4096 * 4,
395 getter_AddRefs(tex));
397 if (FAILED(hr)) {
398 gfxWarning() << "Failed to create resource texture: " << hexa(hr);
401 return tex.forget();
404 } // namespace gfx
405 } // namespace mozilla