no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / gfx / 2d / ConicGradientEffectD2D1.cpp
blobdf03d9458063f76f2ccec112d737ccd20b0e3eb9
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"
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='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.'/>
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='Center' type='vector2'>
36 <Property name='DisplayName' type='string' value='Gradient center'/>
37 </Property>
38 <Property name='Angle' type='vector2'>
39 <Property name='DisplayName' type='string' value='Gradient angle'/>
40 </Property>
41 <Property name='StartOffset' type='float'>
42 <Property name='DisplayName' type='string' value='Start stop offset'/>
43 </Property>
44 <Property name='EndOffset' type='float'>
45 <Property name='DisplayName' type='string' value='End stop offset'/>
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 // {091fda1d-857e-4b1e-828f-1c839d9b7897}
55 static const GUID GUID_SampleConicGradientPS = {
56 0x091fda1d,
57 0x857e,
58 0x4b1e,
59 {0x82, 0x8f, 0x1c, 0x83, 0x9d, 0x9b, 0x78, 0x97}};
61 namespace mozilla {
62 namespace gfx {
64 ConicGradientEffectD2D1::ConicGradientEffectD2D1()
65 : mRefCount(0),
66 mCenter(D2D1::Vector2F(0, 0)),
67 mAngle(0),
68 mStartOffset(0),
69 mEndOffset(0),
70 mTransform(D2D1::IdentityMatrix())
74 IFACEMETHODIMP
75 ConicGradientEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal,
76 ID2D1TransformGraph* pTransformGraph) {
77 HRESULT hr;
79 hr = pContextInternal->LoadPixelShader(GUID_SampleConicGradientPS,
80 SampleConicGradientPS,
81 sizeof(SampleConicGradientPS));
83 if (FAILED(hr)) {
84 return hr;
87 hr = pTransformGraph->SetSingleTransformNode(this);
89 if (FAILED(hr)) {
90 return hr;
93 mEffectContext = pContextInternal;
95 return S_OK;
98 IFACEMETHODIMP
99 ConicGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType) {
100 if (changeType == D2D1_CHANGE_TYPE_NONE) {
101 return S_OK;
104 // We'll need to inverse transform our pixel, precompute inverse here.
105 Matrix mat = ToMatrix(mTransform);
106 if (!mat.Invert()) {
107 // Singular
108 return S_OK;
111 if (!mStopCollection) {
112 return S_OK;
115 HRESULT hr = mDrawInfo->SetPixelShader(GUID_SampleConicGradientPS);
117 if (FAILED(hr)) {
118 return hr;
121 RefPtr<ID2D1ResourceTexture> tex = CreateGradientTexture();
122 hr = mDrawInfo->SetResourceTexture(1, tex);
124 if (FAILED(hr)) {
125 return hr;
128 struct PSConstantBuffer {
129 float center[2];
130 float angle;
131 float start_offset;
132 float end_offset;
133 float repeat_correct;
134 float allow_odd;
135 float padding[1];
136 float transform[8];
139 PSConstantBuffer buffer = {
140 {mCenter.x, mCenter.y},
141 mAngle,
142 mStartOffset,
143 mEndOffset,
144 mStopCollection->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP ? 1.0f : 0.0f,
145 mStopCollection->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR ? 1.0f : 0.0f,
146 {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));
151 if (FAILED(hr)) {
152 return hr;
155 return S_OK;
158 IFACEMETHODIMP
159 ConicGradientEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph) {
160 return pGraph->SetSingleTransformNode(this);
163 IFACEMETHODIMP_(ULONG)
164 ConicGradientEffectD2D1::AddRef() { return ++mRefCount; }
166 IFACEMETHODIMP_(ULONG)
167 ConicGradientEffectD2D1::Release() {
168 if (!--mRefCount) {
169 delete this;
170 return 0;
172 return mRefCount;
175 IFACEMETHODIMP
176 ConicGradientEffectD2D1::QueryInterface(const IID& aIID, void** aPtr) {
177 if (!aPtr) {
178 return E_POINTER;
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);
191 } else {
192 return E_NOINTERFACE;
195 static_cast<IUnknown*>(*aPtr)->AddRef();
196 return S_OK;
199 IFACEMETHODIMP
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) {
205 return E_INVALIDARG;
208 *pOutputRect = *pInputRects;
209 *pOutputOpaqueSubRect = *pInputOpaqueSubRects;
210 return S_OK;
213 IFACEMETHODIMP
214 ConicGradientEffectD2D1::MapOutputRectToInputRects(
215 const D2D1_RECT_L* pOutputRect, D2D1_RECT_L* pInputRects,
216 UINT32 inputRectCount) const {
217 if (inputRectCount != 1) {
218 return E_INVALIDARG;
221 *pInputRects = *pOutputRect;
222 return S_OK;
225 IFACEMETHODIMP
226 ConicGradientEffectD2D1::MapInvalidRect(UINT32 inputIndex,
227 D2D1_RECT_L invalidInputRect,
228 D2D1_RECT_L* pInvalidOutputRect) const {
229 MOZ_ASSERT(inputIndex == 0);
231 *pInvalidOutputRect = invalidInputRect;
232 return S_OK;
235 IFACEMETHODIMP
236 ConicGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo* pDrawInfo) {
237 mDrawInfo = pDrawInfo;
238 return S_OK;
241 HRESULT
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),
262 CreateEffect);
264 if (FAILED(hr)) {
265 gfxWarning() << "Failed to register radial gradient effect.";
267 return hr;
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();
279 return S_OK;
282 HRESULT
283 ConicGradientEffectD2D1::SetStopCollection(IUnknown* aStopCollection) {
284 if (SUCCEEDED(aStopCollection->QueryInterface(
285 (ID2D1GradientStopCollection**)getter_AddRefs(mStopCollection)))) {
286 return S_OK;
289 return E_INVALIDARG;
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
312 // pixel.
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;
321 } else {
322 nextColorPos = 1.0f;
326 float interp;
328 if (nextColorPos != prevColorPos) {
329 interp = (pos - prevColorPos) / (nextColorPos - prevColorPos);
330 } else {
331 interp = 0;
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] = (unsigned char)(255.0f * newColor.r);
341 texData[i * 4 + 1] = (unsigned char)(255.0f * newColor.g);
342 texData[i * 4 + 2] = (unsigned char)(255.0f * newColor.b);
343 texData[i * 4 + 3] = (unsigned char)(255.0f * newColor.a);
346 RefPtr<ID2D1ResourceTexture> tex;
348 UINT32 width = 4096;
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
352 // texture.
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));
367 if (FAILED(hr)) {
368 gfxWarning() << "Failed to create resource texture: " << hexa(hr);
371 return tex.forget();
374 } // namespace gfx
375 } // namespace mozilla