Bumping manifests a=b2g-bump
[gecko.git] / gfx / 2d / RadialGradientEffectD2D1.cpp
blobbab2a6f70a376acf711d4f71833733dd3b45f08d
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"
8 #include "Logging.h"
10 #include "ShadersD2D1.h"
11 #include "HelpersD2D.h"
13 #include <vector>
15 #define TEXTW(x) L##x
16 #define XML(X) TEXTW(#X) // This macro creates a single string from multiple lines of text.
18 static const PCWSTR kXmlDescription =
19 XML(
20 <?xml version='1.0'?>
21 <Effect>
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.'/>
27 <Inputs>
28 <Input name='Geometry'/>
29 </Inputs>
30 <Property name='StopCollection' type='iunknown'>
31 <Property name='DisplayName' type='string' value='Gradient stop collection'/>
32 </Property>
33 <Property name='Center1' type='vector2'>
34 <Property name='DisplayName' type='string' value='Inner circle center'/>
35 </Property>
36 <Property name='Center2' type='vector2'>
37 <Property name='DisplayName' type='string' value='Outer circle center'/>
38 </Property>
39 <Property name='Radius1' type='float'>
40 <Property name='DisplayName' type='string' value='Inner circle radius'/>
41 </Property>
42 <Property name='Radius2' type='float'>
43 <Property name='DisplayName' type='string' value='Outer circle radius'/>
44 </Property>
45 <Property name='Transform' type='matrix3x2'>
46 <Property name='DisplayName' type='string' value='Transform applied to the pattern'/>
47 </Property>
49 </Effect>
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}};
59 namespace mozilla {
60 namespace gfx {
62 RadialGradientEffectD2D1::RadialGradientEffectD2D1()
63 : mRefCount(0)
64 , mCenter1(D2D1::Vector2F(0, 0))
65 , mCenter2(D2D1::Vector2F(0, 0))
66 , mRadius1(0)
67 , mRadius2(0)
68 , mTransform(D2D1::IdentityMatrix())
73 IFACEMETHODIMP
74 RadialGradientEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph)
76 HRESULT hr;
78 hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientPS, SampleRadialGradientPS, sizeof(SampleRadialGradientPS));
80 if (FAILED(hr)) {
81 return hr;
84 hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientA0PS, SampleRadialGradientA0PS, sizeof(SampleRadialGradientA0PS));
86 if (FAILED(hr)) {
87 return hr;
90 hr = pTransformGraph->SetSingleTransformNode(this);
92 if (FAILED(hr)) {
93 return hr;
96 mEffectContext = pContextInternal;
98 return S_OK;
101 IFACEMETHODIMP
102 RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
104 if (changeType == D2D1_CHANGE_TYPE_NONE) {
105 return S_OK;
108 // We'll need to inverse transform our pixel, precompute inverse here.
109 Matrix mat = ToMatrix(mTransform);
110 if (!mat.Invert()) {
111 // Singular
112 return S_OK;
115 if (!mStopCollection) {
116 return S_OK;
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;
123 HRESULT hr;
125 if (A == 0) {
126 hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientA0PS);
127 } else {
128 hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientPS);
131 if (FAILED(hr)) {
132 return hr;
135 RefPtr<ID2D1ResourceTexture> tex = CreateGradientTexture();
136 hr = mDrawInfo->SetResourceTexture(1, tex);
138 if (FAILED(hr)) {
139 return hr;
142 struct PSConstantBuffer
144 float diff[3];
145 float padding;
146 float center1[2];
147 float A;
148 float radius1;
149 float sq_radius1;
150 float padding2[3];
151 float transform[8];
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));
162 if (FAILED(hr)) {
163 return hr;
166 return S_OK;
169 IFACEMETHODIMP
170 RadialGradientEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph)
172 return pGraph->SetSingleTransformNode(this);
175 IFACEMETHODIMP_(ULONG)
176 RadialGradientEffectD2D1::AddRef()
178 return ++mRefCount;
181 IFACEMETHODIMP_(ULONG)
182 RadialGradientEffectD2D1::Release()
184 if (!--mRefCount) {
185 delete this;
186 return 0;
188 return mRefCount;
191 IFACEMETHODIMP
192 RadialGradientEffectD2D1::QueryInterface(const IID &aIID, void **aPtr)
194 if (!aPtr) {
195 return E_POINTER;
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);
208 } else {
209 return E_NOINTERFACE;
212 static_cast<IUnknown*>(*aPtr)->AddRef();
213 return S_OK;
216 IFACEMETHODIMP
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) {
224 return E_INVALIDARG;
227 *pOutputRect = *pInputRects;
228 *pOutputOpaqueSubRect = *pInputOpaqueSubRects;
229 return S_OK;
232 IFACEMETHODIMP
233 RadialGradientEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
234 D2D1_RECT_L* pInputRects,
235 UINT32 inputRectCount) const
237 if (inputRectCount != 1) {
238 return E_INVALIDARG;
241 *pInputRects = *pOutputRect;
242 return S_OK;
245 IFACEMETHODIMP
246 RadialGradientEffectD2D1::MapInvalidRect(UINT32 inputIndex,
247 D2D1_RECT_L invalidInputRect,
248 D2D1_RECT_L* pInvalidOutputRect) const
250 MOZ_ASSERT(inputIndex = 0);
252 *pInvalidOutputRect = invalidInputRect;
253 return S_OK;
256 IFACEMETHODIMP
257 RadialGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo *pDrawInfo)
259 mDrawInfo = pDrawInfo;
260 return S_OK;
263 HRESULT
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);
277 if (FAILED(hr)) {
278 gfxWarning() << "Failed to register radial gradient effect.";
280 return hr;
283 HRESULT __stdcall
284 RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
286 *aEffectImpl = static_cast<ID2D1EffectImpl*>(new RadialGradientEffectD2D1());
287 (*aEffectImpl)->AddRef();
289 return S_OK;
292 HRESULT
293 RadialGradientEffectD2D1::SetStopCollection(IUnknown *aStopCollection)
295 if (SUCCEEDED(aStopCollection->QueryInterface((ID2D1GradientStopCollection**)byRef(mStopCollection)))) {
296 return S_OK;
299 return E_INVALIDARG;
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
329 // pixel.
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;
338 } else {
339 nextColorPos = 1.0f;
343 float interp;
345 if (nextColorPos != prevColorPos) {
346 interp = (pos - prevColorPos) / (nextColorPos - prevColorPos);
347 } else {
348 interp = 0;
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;
365 UINT32 width = 4096;
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));
378 if (FAILED(hr)) {
379 gfxWarning() << "Failed to create resource texture: " << hr;
382 return tex.forget();