1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/2d/RadialGradientEffectD2D1.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,386 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "RadialGradientEffectD2D1.h" 1.10 + 1.11 +#include "Logging.h" 1.12 + 1.13 +#include "ShadersD2D1.h" 1.14 +#include "HelpersD2D.h" 1.15 + 1.16 +#include <vector> 1.17 + 1.18 +#define TEXTW(x) L##x 1.19 +#define XML(X) TEXTW(#X) // This macro creates a single string from multiple lines of text. 1.20 + 1.21 +static const PCWSTR kXmlDescription = 1.22 + XML( 1.23 + <?xml version='1.0'?> 1.24 + <Effect> 1.25 + <!-- System Properties --> 1.26 + <Property name='DisplayName' type='string' value='RadialGradientEffect'/> 1.27 + <Property name='Author' type='string' value='Mozilla'/> 1.28 + <Property name='Category' type='string' value='Pattern effects'/> 1.29 + <Property name='Description' type='string' value='This effect is used to render radial gradients in a manner compliant with the 2D Canvas specification.'/> 1.30 + <Inputs> 1.31 + <Input name='Geometry'/> 1.32 + </Inputs> 1.33 + <Property name='StopCollection' type='iunknown'> 1.34 + <Property name='DisplayName' type='string' value='Gradient stop collection'/> 1.35 + </Property> 1.36 + <Property name='Center1' type='vector2'> 1.37 + <Property name='DisplayName' type='string' value='Inner circle center'/> 1.38 + </Property> 1.39 + <Property name='Center2' type='vector2'> 1.40 + <Property name='DisplayName' type='string' value='Outer circle center'/> 1.41 + </Property> 1.42 + <Property name='Radius1' type='float'> 1.43 + <Property name='DisplayName' type='string' value='Inner circle radius'/> 1.44 + </Property> 1.45 + <Property name='Radius2' type='float'> 1.46 + <Property name='DisplayName' type='string' value='Outer circle radius'/> 1.47 + </Property> 1.48 + <Property name='Transform' type='matrix3x2'> 1.49 + <Property name='DisplayName' type='string' value='Transform applied to the pattern'/> 1.50 + </Property> 1.51 + 1.52 + </Effect> 1.53 + ); 1.54 + 1.55 +// {FB947CDA-718E-40CC-AE7B-D255830D7D14} 1.56 +static const GUID GUID_SampleRadialGradientPS = 1.57 + {0xfb947cda, 0x718e, 0x40cc, {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}}; 1.58 +// {2C468128-6546-453C-8E25-F2DF0DE10A0F} 1.59 +static const GUID GUID_SampleRadialGradientA0PS = 1.60 + {0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}}; 1.61 + 1.62 +namespace mozilla { 1.63 +namespace gfx { 1.64 + 1.65 +RadialGradientEffectD2D1::RadialGradientEffectD2D1() 1.66 + : mRefCount(0) 1.67 + , mCenter1(D2D1::Vector2F(0, 0)) 1.68 + , mCenter2(D2D1::Vector2F(0, 0)) 1.69 + , mRadius1(0) 1.70 + , mRadius2(0) 1.71 + , mTransform(D2D1::IdentityMatrix()) 1.72 + 1.73 +{ 1.74 +} 1.75 + 1.76 +IFACEMETHODIMP 1.77 +RadialGradientEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph) 1.78 +{ 1.79 + HRESULT hr; 1.80 + 1.81 + hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientPS, SampleRadialGradientPS, sizeof(SampleRadialGradientPS)); 1.82 + 1.83 + if (FAILED(hr)) { 1.84 + return hr; 1.85 + } 1.86 + 1.87 + hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientA0PS, SampleRadialGradientA0PS, sizeof(SampleRadialGradientA0PS)); 1.88 + 1.89 + if (FAILED(hr)) { 1.90 + return hr; 1.91 + } 1.92 + 1.93 + hr = pTransformGraph->SetSingleTransformNode(this); 1.94 + 1.95 + if (FAILED(hr)) { 1.96 + return hr; 1.97 + } 1.98 + 1.99 + mEffectContext = pContextInternal; 1.100 + 1.101 + return S_OK; 1.102 +} 1.103 + 1.104 +IFACEMETHODIMP 1.105 +RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType) 1.106 +{ 1.107 + if (changeType == D2D1_CHANGE_TYPE_NONE) { 1.108 + return S_OK; 1.109 + } 1.110 + 1.111 + // We'll need to inverse transform our pixel, precompute inverse here. 1.112 + Matrix mat = ToMatrix(mTransform); 1.113 + if (!mat.Invert()) { 1.114 + // Singular 1.115 + return S_OK; 1.116 + } 1.117 + 1.118 + if (!mStopCollection) { 1.119 + return S_OK; 1.120 + } 1.121 + 1.122 + D2D1_POINT_2F dc = D2D1::Point2F(mCenter2.x - mCenter1.x, mCenter2.y - mCenter2.y); 1.123 + float dr = mRadius2 - mRadius1; 1.124 + float A = dc.x * dc.x + dc.y * dc.y - dr * dr; 1.125 + 1.126 + HRESULT hr; 1.127 + 1.128 + if (A == 0) { 1.129 + hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientA0PS); 1.130 + } else { 1.131 + hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientPS); 1.132 + } 1.133 + 1.134 + if (FAILED(hr)) { 1.135 + return hr; 1.136 + } 1.137 + 1.138 + RefPtr<ID2D1ResourceTexture> tex = CreateGradientTexture(); 1.139 + hr = mDrawInfo->SetResourceTexture(1, tex); 1.140 + 1.141 + if (FAILED(hr)) { 1.142 + return hr; 1.143 + } 1.144 + 1.145 + struct PSConstantBuffer 1.146 + { 1.147 + float diff[3]; 1.148 + float padding; 1.149 + float center1[2]; 1.150 + float A; 1.151 + float radius1; 1.152 + float sq_radius1; 1.153 + float padding2[3]; 1.154 + float transform[8]; 1.155 + }; 1.156 + 1.157 + PSConstantBuffer buffer = { { dc.x, dc.y, dr }, 0, 1.158 + { mCenter1.x, mCenter1.y }, 1.159 + A, mRadius1, mRadius1 * mRadius1, 1.160 + { 0, 0, 0 }, { mat._11, mat._21, mat._31, 0, 1.161 + mat._12, mat._22, mat._32, 0 } }; 1.162 + 1.163 + hr = mDrawInfo->SetPixelShaderConstantBuffer((BYTE*)&buffer, sizeof(buffer)); 1.164 + 1.165 + if (FAILED(hr)) { 1.166 + return hr; 1.167 + } 1.168 + 1.169 + return S_OK; 1.170 +} 1.171 + 1.172 +IFACEMETHODIMP 1.173 +RadialGradientEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph) 1.174 +{ 1.175 + return pGraph->SetSingleTransformNode(this); 1.176 +} 1.177 + 1.178 +IFACEMETHODIMP_(ULONG) 1.179 +RadialGradientEffectD2D1::AddRef() 1.180 +{ 1.181 + return ++mRefCount; 1.182 +} 1.183 + 1.184 +IFACEMETHODIMP_(ULONG) 1.185 +RadialGradientEffectD2D1::Release() 1.186 +{ 1.187 + if (!--mRefCount) { 1.188 + delete this; 1.189 + return 0; 1.190 + } 1.191 + return mRefCount; 1.192 +} 1.193 + 1.194 +IFACEMETHODIMP 1.195 +RadialGradientEffectD2D1::QueryInterface(const IID &aIID, void **aPtr) 1.196 +{ 1.197 + if (!aPtr) { 1.198 + return E_POINTER; 1.199 + } 1.200 + 1.201 + if (aIID == IID_IUnknown) { 1.202 + *aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this)); 1.203 + } else if (aIID == IID_ID2D1EffectImpl) { 1.204 + *aPtr = static_cast<ID2D1EffectImpl*>(this); 1.205 + } else if (aIID == IID_ID2D1DrawTransform) { 1.206 + *aPtr = static_cast<ID2D1DrawTransform*>(this); 1.207 + } else if (aIID == IID_ID2D1Transform) { 1.208 + *aPtr = static_cast<ID2D1Transform*>(this); 1.209 + } else if (aIID == IID_ID2D1TransformNode) { 1.210 + *aPtr = static_cast<ID2D1TransformNode*>(this); 1.211 + } else { 1.212 + return E_NOINTERFACE; 1.213 + } 1.214 + 1.215 + static_cast<IUnknown*>(*aPtr)->AddRef(); 1.216 + return S_OK; 1.217 +} 1.218 + 1.219 +IFACEMETHODIMP 1.220 +RadialGradientEffectD2D1::MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects, 1.221 + const D2D1_RECT_L* pInputOpaqueSubRects, 1.222 + UINT32 inputRectCount, 1.223 + D2D1_RECT_L* pOutputRect, 1.224 + D2D1_RECT_L* pOutputOpaqueSubRect) 1.225 +{ 1.226 + if (inputRectCount != 1) { 1.227 + return E_INVALIDARG; 1.228 + } 1.229 + 1.230 + *pOutputRect = *pInputRects; 1.231 + *pOutputOpaqueSubRect = *pInputOpaqueSubRects; 1.232 + return S_OK; 1.233 +} 1.234 + 1.235 +IFACEMETHODIMP 1.236 +RadialGradientEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect, 1.237 + D2D1_RECT_L* pInputRects, 1.238 + UINT32 inputRectCount) const 1.239 +{ 1.240 + if (inputRectCount != 1) { 1.241 + return E_INVALIDARG; 1.242 + } 1.243 + 1.244 + *pInputRects = *pOutputRect; 1.245 + return S_OK; 1.246 +} 1.247 + 1.248 +IFACEMETHODIMP 1.249 +RadialGradientEffectD2D1::MapInvalidRect(UINT32 inputIndex, 1.250 + D2D1_RECT_L invalidInputRect, 1.251 + D2D1_RECT_L* pInvalidOutputRect) const 1.252 +{ 1.253 + MOZ_ASSERT(inputIndex = 0); 1.254 + 1.255 + *pInvalidOutputRect = invalidInputRect; 1.256 + return S_OK; 1.257 +} 1.258 + 1.259 +IFACEMETHODIMP 1.260 +RadialGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo *pDrawInfo) 1.261 +{ 1.262 + mDrawInfo = pDrawInfo; 1.263 + return S_OK; 1.264 +} 1.265 + 1.266 +HRESULT 1.267 +RadialGradientEffectD2D1::Register(ID2D1Factory1 *aFactory) 1.268 +{ 1.269 + D2D1_PROPERTY_BINDING bindings[] = { 1.270 + D2D1_VALUE_TYPE_BINDING(L"StopCollection", &RadialGradientEffectD2D1::SetStopCollection, 1.271 + &RadialGradientEffectD2D1::GetStopCollection), 1.272 + D2D1_VALUE_TYPE_BINDING(L"Center1", &RadialGradientEffectD2D1::SetCenter1, &RadialGradientEffectD2D1::GetCenter1), 1.273 + D2D1_VALUE_TYPE_BINDING(L"Center2", &RadialGradientEffectD2D1::SetCenter2, &RadialGradientEffectD2D1::GetCenter2), 1.274 + D2D1_VALUE_TYPE_BINDING(L"Radius1", &RadialGradientEffectD2D1::SetRadius1, &RadialGradientEffectD2D1::GetRadius1), 1.275 + D2D1_VALUE_TYPE_BINDING(L"Radius2", &RadialGradientEffectD2D1::SetRadius2, &RadialGradientEffectD2D1::GetRadius2), 1.276 + D2D1_VALUE_TYPE_BINDING(L"Transform", &RadialGradientEffectD2D1::SetTransform, &RadialGradientEffectD2D1::GetTransform) 1.277 + }; 1.278 + HRESULT hr = aFactory->RegisterEffectFromString(CLSID_RadialGradientEffect, kXmlDescription, bindings, ARRAYSIZE(bindings), CreateEffect); 1.279 + 1.280 + if (FAILED(hr)) { 1.281 + gfxWarning() << "Failed to register radial gradient effect."; 1.282 + } 1.283 + return hr; 1.284 +} 1.285 + 1.286 +HRESULT __stdcall 1.287 +RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl) 1.288 +{ 1.289 + *aEffectImpl = static_cast<ID2D1EffectImpl*>(new RadialGradientEffectD2D1()); 1.290 + (*aEffectImpl)->AddRef(); 1.291 + 1.292 + return S_OK; 1.293 +} 1.294 + 1.295 +HRESULT 1.296 +RadialGradientEffectD2D1::SetStopCollection(IUnknown *aStopCollection) 1.297 +{ 1.298 + if (SUCCEEDED(aStopCollection->QueryInterface((ID2D1GradientStopCollection**)byRef(mStopCollection)))) { 1.299 + return S_OK; 1.300 + } 1.301 + 1.302 + return E_INVALIDARG; 1.303 +} 1.304 + 1.305 +TemporaryRef<ID2D1ResourceTexture> 1.306 +RadialGradientEffectD2D1::CreateGradientTexture() 1.307 +{ 1.308 + std::vector<D2D1_GRADIENT_STOP> rawStops; 1.309 + rawStops.resize(mStopCollection->GetGradientStopCount()); 1.310 + mStopCollection->GetGradientStops(&rawStops.front(), rawStops.size()); 1.311 + 1.312 + std::vector<unsigned char> textureData; 1.313 + textureData.resize(4096 * 4); 1.314 + unsigned char *texData = &textureData.front(); 1.315 + 1.316 + float prevColorPos = 0; 1.317 + float nextColorPos = 1.0f; 1.318 + D2D1_COLOR_F prevColor = rawStops[0].color; 1.319 + D2D1_COLOR_F nextColor = prevColor; 1.320 + 1.321 + if (rawStops.size() >= 2) { 1.322 + nextColor = rawStops[1].color; 1.323 + nextColorPos = rawStops[1].position; 1.324 + } 1.325 + 1.326 + uint32_t stopPosition = 2; 1.327 + 1.328 + // Not the most optimized way but this will do for now. 1.329 + for (int i = 0; i < 4096; i++) { 1.330 + // The 4095 seems a little counter intuitive, but we want the gradient 1.331 + // color at offset 0 at the first pixel, and at offset 1.0f at the last 1.332 + // pixel. 1.333 + float pos = float(i) / 4095; 1.334 + 1.335 + while (pos > nextColorPos) { 1.336 + prevColor = nextColor; 1.337 + prevColorPos = nextColorPos; 1.338 + if (rawStops.size() > stopPosition) { 1.339 + nextColor = rawStops[stopPosition].color; 1.340 + nextColorPos = rawStops[stopPosition++].position; 1.341 + } else { 1.342 + nextColorPos = 1.0f; 1.343 + } 1.344 + } 1.345 + 1.346 + float interp; 1.347 + 1.348 + if (nextColorPos != prevColorPos) { 1.349 + interp = (pos - prevColorPos) / (nextColorPos - prevColorPos); 1.350 + } else { 1.351 + interp = 0; 1.352 + } 1.353 + 1.354 + Color newColor(prevColor.r + (nextColor.r - prevColor.r) * interp, 1.355 + prevColor.g + (nextColor.g - prevColor.g) * interp, 1.356 + prevColor.b + (nextColor.b - prevColor.b) * interp, 1.357 + prevColor.a + (nextColor.a - prevColor.a) * interp); 1.358 + 1.359 + // Note D2D expects RGBA here!! 1.360 + texData[i * 4] = (char)(255.0f * newColor.r); 1.361 + texData[i * 4 + 1] = (char)(255.0f * newColor.g); 1.362 + texData[i * 4 + 2] = (char)(255.0f * newColor.b); 1.363 + texData[i * 4 + 3] = (char)(255.0f * newColor.a); 1.364 + } 1.365 + 1.366 + RefPtr<ID2D1ResourceTexture> tex; 1.367 + 1.368 + UINT32 width = 4096; 1.369 + UINT32 stride = 4096 * 4; 1.370 + D2D1_RESOURCE_TEXTURE_PROPERTIES props; 1.371 + props.dimensions = 1; 1.372 + props.extents = &width; 1.373 + props.channelDepth = D2D1_CHANNEL_DEPTH_4; 1.374 + props.bufferPrecision = D2D1_BUFFER_PRECISION_8BPC_UNORM; 1.375 + props.filter = D2D1_FILTER_MIN_MAG_MIP_LINEAR; 1.376 + D2D1_EXTEND_MODE extendMode = mStopCollection->GetExtendMode(); 1.377 + props.extendModes = &extendMode; 1.378 + 1.379 + HRESULT hr = mEffectContext->CreateResourceTexture(nullptr, &props, &textureData.front(), &stride, 4096 * 4, byRef(tex)); 1.380 + 1.381 + if (FAILED(hr)) { 1.382 + gfxWarning() << "Failed to create resource texture: " << hr; 1.383 + } 1.384 + 1.385 + return tex; 1.386 +} 1.387 + 1.388 +} 1.389 +}