gfx/2d/RadialGradientEffectD2D1.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "RadialGradientEffectD2D1.h"
michael@0 7
michael@0 8 #include "Logging.h"
michael@0 9
michael@0 10 #include "ShadersD2D1.h"
michael@0 11 #include "HelpersD2D.h"
michael@0 12
michael@0 13 #include <vector>
michael@0 14
michael@0 15 #define TEXTW(x) L##x
michael@0 16 #define XML(X) TEXTW(#X) // This macro creates a single string from multiple lines of text.
michael@0 17
michael@0 18 static const PCWSTR kXmlDescription =
michael@0 19 XML(
michael@0 20 <?xml version='1.0'?>
michael@0 21 <Effect>
michael@0 22 <!-- System Properties -->
michael@0 23 <Property name='DisplayName' type='string' value='RadialGradientEffect'/>
michael@0 24 <Property name='Author' type='string' value='Mozilla'/>
michael@0 25 <Property name='Category' type='string' value='Pattern effects'/>
michael@0 26 <Property name='Description' type='string' value='This effect is used to render radial gradients in a manner compliant with the 2D Canvas specification.'/>
michael@0 27 <Inputs>
michael@0 28 <Input name='Geometry'/>
michael@0 29 </Inputs>
michael@0 30 <Property name='StopCollection' type='iunknown'>
michael@0 31 <Property name='DisplayName' type='string' value='Gradient stop collection'/>
michael@0 32 </Property>
michael@0 33 <Property name='Center1' type='vector2'>
michael@0 34 <Property name='DisplayName' type='string' value='Inner circle center'/>
michael@0 35 </Property>
michael@0 36 <Property name='Center2' type='vector2'>
michael@0 37 <Property name='DisplayName' type='string' value='Outer circle center'/>
michael@0 38 </Property>
michael@0 39 <Property name='Radius1' type='float'>
michael@0 40 <Property name='DisplayName' type='string' value='Inner circle radius'/>
michael@0 41 </Property>
michael@0 42 <Property name='Radius2' type='float'>
michael@0 43 <Property name='DisplayName' type='string' value='Outer circle radius'/>
michael@0 44 </Property>
michael@0 45 <Property name='Transform' type='matrix3x2'>
michael@0 46 <Property name='DisplayName' type='string' value='Transform applied to the pattern'/>
michael@0 47 </Property>
michael@0 48
michael@0 49 </Effect>
michael@0 50 );
michael@0 51
michael@0 52 // {FB947CDA-718E-40CC-AE7B-D255830D7D14}
michael@0 53 static const GUID GUID_SampleRadialGradientPS =
michael@0 54 {0xfb947cda, 0x718e, 0x40cc, {0xae, 0x7b, 0xd2, 0x55, 0x83, 0xd, 0x7d, 0x14}};
michael@0 55 // {2C468128-6546-453C-8E25-F2DF0DE10A0F}
michael@0 56 static const GUID GUID_SampleRadialGradientA0PS =
michael@0 57 {0x2c468128, 0x6546, 0x453c, {0x8e, 0x25, 0xf2, 0xdf, 0xd, 0xe1, 0xa, 0xf}};
michael@0 58
michael@0 59 namespace mozilla {
michael@0 60 namespace gfx {
michael@0 61
michael@0 62 RadialGradientEffectD2D1::RadialGradientEffectD2D1()
michael@0 63 : mRefCount(0)
michael@0 64 , mCenter1(D2D1::Vector2F(0, 0))
michael@0 65 , mCenter2(D2D1::Vector2F(0, 0))
michael@0 66 , mRadius1(0)
michael@0 67 , mRadius2(0)
michael@0 68 , mTransform(D2D1::IdentityMatrix())
michael@0 69
michael@0 70 {
michael@0 71 }
michael@0 72
michael@0 73 IFACEMETHODIMP
michael@0 74 RadialGradientEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph)
michael@0 75 {
michael@0 76 HRESULT hr;
michael@0 77
michael@0 78 hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientPS, SampleRadialGradientPS, sizeof(SampleRadialGradientPS));
michael@0 79
michael@0 80 if (FAILED(hr)) {
michael@0 81 return hr;
michael@0 82 }
michael@0 83
michael@0 84 hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientA0PS, SampleRadialGradientA0PS, sizeof(SampleRadialGradientA0PS));
michael@0 85
michael@0 86 if (FAILED(hr)) {
michael@0 87 return hr;
michael@0 88 }
michael@0 89
michael@0 90 hr = pTransformGraph->SetSingleTransformNode(this);
michael@0 91
michael@0 92 if (FAILED(hr)) {
michael@0 93 return hr;
michael@0 94 }
michael@0 95
michael@0 96 mEffectContext = pContextInternal;
michael@0 97
michael@0 98 return S_OK;
michael@0 99 }
michael@0 100
michael@0 101 IFACEMETHODIMP
michael@0 102 RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
michael@0 103 {
michael@0 104 if (changeType == D2D1_CHANGE_TYPE_NONE) {
michael@0 105 return S_OK;
michael@0 106 }
michael@0 107
michael@0 108 // We'll need to inverse transform our pixel, precompute inverse here.
michael@0 109 Matrix mat = ToMatrix(mTransform);
michael@0 110 if (!mat.Invert()) {
michael@0 111 // Singular
michael@0 112 return S_OK;
michael@0 113 }
michael@0 114
michael@0 115 if (!mStopCollection) {
michael@0 116 return S_OK;
michael@0 117 }
michael@0 118
michael@0 119 D2D1_POINT_2F dc = D2D1::Point2F(mCenter2.x - mCenter1.x, mCenter2.y - mCenter2.y);
michael@0 120 float dr = mRadius2 - mRadius1;
michael@0 121 float A = dc.x * dc.x + dc.y * dc.y - dr * dr;
michael@0 122
michael@0 123 HRESULT hr;
michael@0 124
michael@0 125 if (A == 0) {
michael@0 126 hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientA0PS);
michael@0 127 } else {
michael@0 128 hr = mDrawInfo->SetPixelShader(GUID_SampleRadialGradientPS);
michael@0 129 }
michael@0 130
michael@0 131 if (FAILED(hr)) {
michael@0 132 return hr;
michael@0 133 }
michael@0 134
michael@0 135 RefPtr<ID2D1ResourceTexture> tex = CreateGradientTexture();
michael@0 136 hr = mDrawInfo->SetResourceTexture(1, tex);
michael@0 137
michael@0 138 if (FAILED(hr)) {
michael@0 139 return hr;
michael@0 140 }
michael@0 141
michael@0 142 struct PSConstantBuffer
michael@0 143 {
michael@0 144 float diff[3];
michael@0 145 float padding;
michael@0 146 float center1[2];
michael@0 147 float A;
michael@0 148 float radius1;
michael@0 149 float sq_radius1;
michael@0 150 float padding2[3];
michael@0 151 float transform[8];
michael@0 152 };
michael@0 153
michael@0 154 PSConstantBuffer buffer = { { dc.x, dc.y, dr }, 0,
michael@0 155 { mCenter1.x, mCenter1.y },
michael@0 156 A, mRadius1, mRadius1 * mRadius1,
michael@0 157 { 0, 0, 0 }, { mat._11, mat._21, mat._31, 0,
michael@0 158 mat._12, mat._22, mat._32, 0 } };
michael@0 159
michael@0 160 hr = mDrawInfo->SetPixelShaderConstantBuffer((BYTE*)&buffer, sizeof(buffer));
michael@0 161
michael@0 162 if (FAILED(hr)) {
michael@0 163 return hr;
michael@0 164 }
michael@0 165
michael@0 166 return S_OK;
michael@0 167 }
michael@0 168
michael@0 169 IFACEMETHODIMP
michael@0 170 RadialGradientEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph)
michael@0 171 {
michael@0 172 return pGraph->SetSingleTransformNode(this);
michael@0 173 }
michael@0 174
michael@0 175 IFACEMETHODIMP_(ULONG)
michael@0 176 RadialGradientEffectD2D1::AddRef()
michael@0 177 {
michael@0 178 return ++mRefCount;
michael@0 179 }
michael@0 180
michael@0 181 IFACEMETHODIMP_(ULONG)
michael@0 182 RadialGradientEffectD2D1::Release()
michael@0 183 {
michael@0 184 if (!--mRefCount) {
michael@0 185 delete this;
michael@0 186 return 0;
michael@0 187 }
michael@0 188 return mRefCount;
michael@0 189 }
michael@0 190
michael@0 191 IFACEMETHODIMP
michael@0 192 RadialGradientEffectD2D1::QueryInterface(const IID &aIID, void **aPtr)
michael@0 193 {
michael@0 194 if (!aPtr) {
michael@0 195 return E_POINTER;
michael@0 196 }
michael@0 197
michael@0 198 if (aIID == IID_IUnknown) {
michael@0 199 *aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this));
michael@0 200 } else if (aIID == IID_ID2D1EffectImpl) {
michael@0 201 *aPtr = static_cast<ID2D1EffectImpl*>(this);
michael@0 202 } else if (aIID == IID_ID2D1DrawTransform) {
michael@0 203 *aPtr = static_cast<ID2D1DrawTransform*>(this);
michael@0 204 } else if (aIID == IID_ID2D1Transform) {
michael@0 205 *aPtr = static_cast<ID2D1Transform*>(this);
michael@0 206 } else if (aIID == IID_ID2D1TransformNode) {
michael@0 207 *aPtr = static_cast<ID2D1TransformNode*>(this);
michael@0 208 } else {
michael@0 209 return E_NOINTERFACE;
michael@0 210 }
michael@0 211
michael@0 212 static_cast<IUnknown*>(*aPtr)->AddRef();
michael@0 213 return S_OK;
michael@0 214 }
michael@0 215
michael@0 216 IFACEMETHODIMP
michael@0 217 RadialGradientEffectD2D1::MapInputRectsToOutputRect(const D2D1_RECT_L* pInputRects,
michael@0 218 const D2D1_RECT_L* pInputOpaqueSubRects,
michael@0 219 UINT32 inputRectCount,
michael@0 220 D2D1_RECT_L* pOutputRect,
michael@0 221 D2D1_RECT_L* pOutputOpaqueSubRect)
michael@0 222 {
michael@0 223 if (inputRectCount != 1) {
michael@0 224 return E_INVALIDARG;
michael@0 225 }
michael@0 226
michael@0 227 *pOutputRect = *pInputRects;
michael@0 228 *pOutputOpaqueSubRect = *pInputOpaqueSubRects;
michael@0 229 return S_OK;
michael@0 230 }
michael@0 231
michael@0 232 IFACEMETHODIMP
michael@0 233 RadialGradientEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
michael@0 234 D2D1_RECT_L* pInputRects,
michael@0 235 UINT32 inputRectCount) const
michael@0 236 {
michael@0 237 if (inputRectCount != 1) {
michael@0 238 return E_INVALIDARG;
michael@0 239 }
michael@0 240
michael@0 241 *pInputRects = *pOutputRect;
michael@0 242 return S_OK;
michael@0 243 }
michael@0 244
michael@0 245 IFACEMETHODIMP
michael@0 246 RadialGradientEffectD2D1::MapInvalidRect(UINT32 inputIndex,
michael@0 247 D2D1_RECT_L invalidInputRect,
michael@0 248 D2D1_RECT_L* pInvalidOutputRect) const
michael@0 249 {
michael@0 250 MOZ_ASSERT(inputIndex = 0);
michael@0 251
michael@0 252 *pInvalidOutputRect = invalidInputRect;
michael@0 253 return S_OK;
michael@0 254 }
michael@0 255
michael@0 256 IFACEMETHODIMP
michael@0 257 RadialGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo *pDrawInfo)
michael@0 258 {
michael@0 259 mDrawInfo = pDrawInfo;
michael@0 260 return S_OK;
michael@0 261 }
michael@0 262
michael@0 263 HRESULT
michael@0 264 RadialGradientEffectD2D1::Register(ID2D1Factory1 *aFactory)
michael@0 265 {
michael@0 266 D2D1_PROPERTY_BINDING bindings[] = {
michael@0 267 D2D1_VALUE_TYPE_BINDING(L"StopCollection", &RadialGradientEffectD2D1::SetStopCollection,
michael@0 268 &RadialGradientEffectD2D1::GetStopCollection),
michael@0 269 D2D1_VALUE_TYPE_BINDING(L"Center1", &RadialGradientEffectD2D1::SetCenter1, &RadialGradientEffectD2D1::GetCenter1),
michael@0 270 D2D1_VALUE_TYPE_BINDING(L"Center2", &RadialGradientEffectD2D1::SetCenter2, &RadialGradientEffectD2D1::GetCenter2),
michael@0 271 D2D1_VALUE_TYPE_BINDING(L"Radius1", &RadialGradientEffectD2D1::SetRadius1, &RadialGradientEffectD2D1::GetRadius1),
michael@0 272 D2D1_VALUE_TYPE_BINDING(L"Radius2", &RadialGradientEffectD2D1::SetRadius2, &RadialGradientEffectD2D1::GetRadius2),
michael@0 273 D2D1_VALUE_TYPE_BINDING(L"Transform", &RadialGradientEffectD2D1::SetTransform, &RadialGradientEffectD2D1::GetTransform)
michael@0 274 };
michael@0 275 HRESULT hr = aFactory->RegisterEffectFromString(CLSID_RadialGradientEffect, kXmlDescription, bindings, ARRAYSIZE(bindings), CreateEffect);
michael@0 276
michael@0 277 if (FAILED(hr)) {
michael@0 278 gfxWarning() << "Failed to register radial gradient effect.";
michael@0 279 }
michael@0 280 return hr;
michael@0 281 }
michael@0 282
michael@0 283 HRESULT __stdcall
michael@0 284 RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
michael@0 285 {
michael@0 286 *aEffectImpl = static_cast<ID2D1EffectImpl*>(new RadialGradientEffectD2D1());
michael@0 287 (*aEffectImpl)->AddRef();
michael@0 288
michael@0 289 return S_OK;
michael@0 290 }
michael@0 291
michael@0 292 HRESULT
michael@0 293 RadialGradientEffectD2D1::SetStopCollection(IUnknown *aStopCollection)
michael@0 294 {
michael@0 295 if (SUCCEEDED(aStopCollection->QueryInterface((ID2D1GradientStopCollection**)byRef(mStopCollection)))) {
michael@0 296 return S_OK;
michael@0 297 }
michael@0 298
michael@0 299 return E_INVALIDARG;
michael@0 300 }
michael@0 301
michael@0 302 TemporaryRef<ID2D1ResourceTexture>
michael@0 303 RadialGradientEffectD2D1::CreateGradientTexture()
michael@0 304 {
michael@0 305 std::vector<D2D1_GRADIENT_STOP> rawStops;
michael@0 306 rawStops.resize(mStopCollection->GetGradientStopCount());
michael@0 307 mStopCollection->GetGradientStops(&rawStops.front(), rawStops.size());
michael@0 308
michael@0 309 std::vector<unsigned char> textureData;
michael@0 310 textureData.resize(4096 * 4);
michael@0 311 unsigned char *texData = &textureData.front();
michael@0 312
michael@0 313 float prevColorPos = 0;
michael@0 314 float nextColorPos = 1.0f;
michael@0 315 D2D1_COLOR_F prevColor = rawStops[0].color;
michael@0 316 D2D1_COLOR_F nextColor = prevColor;
michael@0 317
michael@0 318 if (rawStops.size() >= 2) {
michael@0 319 nextColor = rawStops[1].color;
michael@0 320 nextColorPos = rawStops[1].position;
michael@0 321 }
michael@0 322
michael@0 323 uint32_t stopPosition = 2;
michael@0 324
michael@0 325 // Not the most optimized way but this will do for now.
michael@0 326 for (int i = 0; i < 4096; i++) {
michael@0 327 // The 4095 seems a little counter intuitive, but we want the gradient
michael@0 328 // color at offset 0 at the first pixel, and at offset 1.0f at the last
michael@0 329 // pixel.
michael@0 330 float pos = float(i) / 4095;
michael@0 331
michael@0 332 while (pos > nextColorPos) {
michael@0 333 prevColor = nextColor;
michael@0 334 prevColorPos = nextColorPos;
michael@0 335 if (rawStops.size() > stopPosition) {
michael@0 336 nextColor = rawStops[stopPosition].color;
michael@0 337 nextColorPos = rawStops[stopPosition++].position;
michael@0 338 } else {
michael@0 339 nextColorPos = 1.0f;
michael@0 340 }
michael@0 341 }
michael@0 342
michael@0 343 float interp;
michael@0 344
michael@0 345 if (nextColorPos != prevColorPos) {
michael@0 346 interp = (pos - prevColorPos) / (nextColorPos - prevColorPos);
michael@0 347 } else {
michael@0 348 interp = 0;
michael@0 349 }
michael@0 350
michael@0 351 Color newColor(prevColor.r + (nextColor.r - prevColor.r) * interp,
michael@0 352 prevColor.g + (nextColor.g - prevColor.g) * interp,
michael@0 353 prevColor.b + (nextColor.b - prevColor.b) * interp,
michael@0 354 prevColor.a + (nextColor.a - prevColor.a) * interp);
michael@0 355
michael@0 356 // Note D2D expects RGBA here!!
michael@0 357 texData[i * 4] = (char)(255.0f * newColor.r);
michael@0 358 texData[i * 4 + 1] = (char)(255.0f * newColor.g);
michael@0 359 texData[i * 4 + 2] = (char)(255.0f * newColor.b);
michael@0 360 texData[i * 4 + 3] = (char)(255.0f * newColor.a);
michael@0 361 }
michael@0 362
michael@0 363 RefPtr<ID2D1ResourceTexture> tex;
michael@0 364
michael@0 365 UINT32 width = 4096;
michael@0 366 UINT32 stride = 4096 * 4;
michael@0 367 D2D1_RESOURCE_TEXTURE_PROPERTIES props;
michael@0 368 props.dimensions = 1;
michael@0 369 props.extents = &width;
michael@0 370 props.channelDepth = D2D1_CHANNEL_DEPTH_4;
michael@0 371 props.bufferPrecision = D2D1_BUFFER_PRECISION_8BPC_UNORM;
michael@0 372 props.filter = D2D1_FILTER_MIN_MAG_MIP_LINEAR;
michael@0 373 D2D1_EXTEND_MODE extendMode = mStopCollection->GetExtendMode();
michael@0 374 props.extendModes = &extendMode;
michael@0 375
michael@0 376 HRESULT hr = mEffectContext->CreateResourceTexture(nullptr, &props, &textureData.front(), &stride, 4096 * 4, byRef(tex));
michael@0 377
michael@0 378 if (FAILED(hr)) {
michael@0 379 gfxWarning() << "Failed to create resource texture: " << hr;
michael@0 380 }
michael@0 381
michael@0 382 return tex;
michael@0 383 }
michael@0 384
michael@0 385 }
michael@0 386 }

mercurial