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.

     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>
    50         );
    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())
    70 {
    71 }
    73 IFACEMETHODIMP
    74 RadialGradientEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal, ID2D1TransformGraph* pTransformGraph)
    75 {
    76   HRESULT hr;
    78   hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientPS, SampleRadialGradientPS, sizeof(SampleRadialGradientPS));
    80   if (FAILED(hr)) {
    81     return hr;
    82   }
    84   hr = pContextInternal->LoadPixelShader(GUID_SampleRadialGradientA0PS, SampleRadialGradientA0PS, sizeof(SampleRadialGradientA0PS));
    86   if (FAILED(hr)) {
    87     return hr;
    88   }
    90   hr = pTransformGraph->SetSingleTransformNode(this);
    92   if (FAILED(hr)) {
    93     return hr;
    94   }
    96   mEffectContext = pContextInternal;
    98   return S_OK;
    99 }
   101 IFACEMETHODIMP
   102 RadialGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType)
   103 {
   104   if (changeType == D2D1_CHANGE_TYPE_NONE) {
   105     return S_OK;
   106   }
   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;
   113   }
   115   if (!mStopCollection) {
   116     return S_OK;
   117   }
   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);
   129   }
   131   if (FAILED(hr)) {
   132     return hr;
   133   }
   135   RefPtr<ID2D1ResourceTexture> tex = CreateGradientTexture();
   136   hr = mDrawInfo->SetResourceTexture(1, tex);
   138   if (FAILED(hr)) {
   139     return hr;
   140   }
   142   struct PSConstantBuffer
   143   {
   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];
   152   };
   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;
   164   }
   166   return S_OK;
   167 }
   169 IFACEMETHODIMP
   170 RadialGradientEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph)
   171 {
   172   return pGraph->SetSingleTransformNode(this);
   173 }
   175 IFACEMETHODIMP_(ULONG)
   176 RadialGradientEffectD2D1::AddRef()
   177 {
   178   return ++mRefCount;
   179 }
   181 IFACEMETHODIMP_(ULONG)
   182 RadialGradientEffectD2D1::Release()
   183 {
   184   if (!--mRefCount) {
   185     delete this;
   186     return 0;
   187   }
   188   return mRefCount;
   189 }
   191 IFACEMETHODIMP
   192 RadialGradientEffectD2D1::QueryInterface(const IID &aIID, void **aPtr)
   193 {
   194   if (!aPtr) {
   195     return E_POINTER;
   196   }
   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;
   210   }
   212   static_cast<IUnknown*>(*aPtr)->AddRef();
   213   return S_OK;
   214 }
   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)
   222 {
   223   if (inputRectCount != 1) {
   224     return E_INVALIDARG;
   225   }
   227   *pOutputRect = *pInputRects;
   228   *pOutputOpaqueSubRect = *pInputOpaqueSubRects;
   229   return S_OK;
   230 }
   232 IFACEMETHODIMP
   233 RadialGradientEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
   234                                                     D2D1_RECT_L* pInputRects,
   235                                                     UINT32 inputRectCount) const
   236 {
   237   if (inputRectCount != 1) {
   238       return E_INVALIDARG;
   239   }
   241   *pInputRects = *pOutputRect;
   242   return S_OK;
   243 }
   245 IFACEMETHODIMP
   246 RadialGradientEffectD2D1::MapInvalidRect(UINT32 inputIndex,
   247                                          D2D1_RECT_L invalidInputRect,
   248                                          D2D1_RECT_L* pInvalidOutputRect) const
   249 {
   250   MOZ_ASSERT(inputIndex = 0);
   252   *pInvalidOutputRect = invalidInputRect;
   253   return S_OK;
   254 }
   256 IFACEMETHODIMP
   257 RadialGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo *pDrawInfo)
   258 {
   259   mDrawInfo = pDrawInfo;
   260   return S_OK;
   261 }
   263 HRESULT
   264 RadialGradientEffectD2D1::Register(ID2D1Factory1 *aFactory)
   265 {
   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)
   274   };
   275   HRESULT hr = aFactory->RegisterEffectFromString(CLSID_RadialGradientEffect, kXmlDescription, bindings, ARRAYSIZE(bindings), CreateEffect);
   277   if (FAILED(hr)) {
   278     gfxWarning() << "Failed to register radial gradient effect.";
   279   }
   280   return hr;
   281 }
   283 HRESULT __stdcall
   284 RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
   285 {
   286   *aEffectImpl = static_cast<ID2D1EffectImpl*>(new RadialGradientEffectD2D1());
   287   (*aEffectImpl)->AddRef();
   289   return S_OK;
   290 }
   292 HRESULT
   293 RadialGradientEffectD2D1::SetStopCollection(IUnknown *aStopCollection)
   294 {
   295   if (SUCCEEDED(aStopCollection->QueryInterface((ID2D1GradientStopCollection**)byRef(mStopCollection)))) {
   296     return S_OK;
   297   }
   299   return E_INVALIDARG;
   300 }
   302 TemporaryRef<ID2D1ResourceTexture>
   303 RadialGradientEffectD2D1::CreateGradientTexture()
   304 {
   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;
   321   }
   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;
   340       }
   341     }
   343     float interp;
   345     if (nextColorPos != prevColorPos) {
   346       interp = (pos - prevColorPos) / (nextColorPos - prevColorPos);
   347     } else {
   348       interp = 0;
   349     }
   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);
   361   }
   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;
   380   }
   382   return tex;
   383 }
   385 }
   386 }

mercurial