gfx/2d/RadialGradientEffectD2D1.cpp

changeset 0
6474c204b198
     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 +}

mercurial