forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			375 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			375 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "ConicGradientEffectD2D1.h"
 | |
| 
 | |
| #include "Logging.h"
 | |
| 
 | |
| #include "ShadersD2D1.h"
 | |
| #include "HelpersD2D.h"
 | |
| 
 | |
| #include <vector>
 | |
| 
 | |
| #define TEXTW(x) L##x
 | |
| #define XML(X) \
 | |
|   TEXTW(#X)  // This macro creates a single string from multiple lines of text.
 | |
| 
 | |
| static const PCWSTR kXmlDescription =
 | |
|     XML(
 | |
|         <?xml version='1.0'?>
 | |
|         <Effect>
 | |
|             <!-- System Properties -->
 | |
|             <Property name='DisplayName' type='string' value='ConicGradientEffect'/>
 | |
|             <Property name='Author' type='string' value='Mozilla'/>
 | |
|             <Property name='Category' type='string' value='Pattern effects'/>
 | |
|             <Property name='Description' type='string' value='This effect is used to render CSS conic gradients.'/>
 | |
|             <Inputs>
 | |
|                 <Input name='Geometry'/>
 | |
|             </Inputs>
 | |
|             <Property name='StopCollection' type='iunknown'>
 | |
|               <Property name='DisplayName' type='string' value='Gradient stop collection'/>
 | |
|             </Property>
 | |
|             <Property name='Center' type='vector2'>
 | |
|               <Property name='DisplayName' type='string' value='Gradient center'/>
 | |
|             </Property>
 | |
|             <Property name='Angle' type='vector2'>
 | |
|               <Property name='DisplayName' type='string' value='Gradient angle'/>
 | |
|             </Property>
 | |
|             <Property name='StartOffset' type='float'>
 | |
|               <Property name='DisplayName' type='string' value='Start stop offset'/>
 | |
|             </Property>
 | |
|             <Property name='EndOffset' type='float'>
 | |
|               <Property name='DisplayName' type='string' value='End stop offset'/>
 | |
|             </Property>
 | |
|             <Property name='Transform' type='matrix3x2'>
 | |
|               <Property name='DisplayName' type='string' value='Transform applied to the pattern'/>
 | |
|             </Property>
 | |
| 
 | |
|         </Effect>
 | |
|         );
 | |
| 
 | |
| // {091fda1d-857e-4b1e-828f-1c839d9b7897}
 | |
| static const GUID GUID_SampleConicGradientPS = {
 | |
|     0x091fda1d,
 | |
|     0x857e,
 | |
|     0x4b1e,
 | |
|     {0x82, 0x8f, 0x1c, 0x83, 0x9d, 0x9b, 0x78, 0x97}};
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace gfx {
 | |
| 
 | |
| ConicGradientEffectD2D1::ConicGradientEffectD2D1()
 | |
|     : mRefCount(0),
 | |
|       mCenter(D2D1::Vector2F(0, 0)),
 | |
|       mAngle(0),
 | |
|       mStartOffset(0),
 | |
|       mEndOffset(0),
 | |
|       mTransform(D2D1::IdentityMatrix())
 | |
| 
 | |
| {}
 | |
| 
 | |
| IFACEMETHODIMP
 | |
| ConicGradientEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal,
 | |
|                                     ID2D1TransformGraph* pTransformGraph) {
 | |
|   HRESULT hr;
 | |
| 
 | |
|   hr = pContextInternal->LoadPixelShader(GUID_SampleConicGradientPS,
 | |
|                                          SampleConicGradientPS,
 | |
|                                          sizeof(SampleConicGradientPS));
 | |
| 
 | |
|   if (FAILED(hr)) {
 | |
|     return hr;
 | |
|   }
 | |
| 
 | |
|   hr = pTransformGraph->SetSingleTransformNode(this);
 | |
| 
 | |
|   if (FAILED(hr)) {
 | |
|     return hr;
 | |
|   }
 | |
| 
 | |
|   mEffectContext = pContextInternal;
 | |
| 
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| IFACEMETHODIMP
 | |
| ConicGradientEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType) {
 | |
|   if (changeType == D2D1_CHANGE_TYPE_NONE) {
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
|   // We'll need to inverse transform our pixel, precompute inverse here.
 | |
|   Matrix mat = ToMatrix(mTransform);
 | |
|   if (!mat.Invert()) {
 | |
|     // Singular
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
|   if (!mStopCollection) {
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
|   HRESULT hr = mDrawInfo->SetPixelShader(GUID_SampleConicGradientPS);
 | |
| 
 | |
|   if (FAILED(hr)) {
 | |
|     return hr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<ID2D1ResourceTexture> tex = CreateGradientTexture();
 | |
|   hr = mDrawInfo->SetResourceTexture(1, tex);
 | |
| 
 | |
|   if (FAILED(hr)) {
 | |
|     return hr;
 | |
|   }
 | |
| 
 | |
|   struct PSConstantBuffer {
 | |
|     float center[2];
 | |
|     float angle;
 | |
|     float start_offset;
 | |
|     float end_offset;
 | |
|     float repeat_correct;
 | |
|     float allow_odd;
 | |
|     float padding[1];
 | |
|     float transform[8];
 | |
|   };
 | |
| 
 | |
|   PSConstantBuffer buffer = {
 | |
|       {mCenter.x, mCenter.y},
 | |
|       mAngle,
 | |
|       mStartOffset,
 | |
|       mEndOffset,
 | |
|       mStopCollection->GetExtendMode() != D2D1_EXTEND_MODE_CLAMP ? 1.0f : 0.0f,
 | |
|       mStopCollection->GetExtendMode() == D2D1_EXTEND_MODE_MIRROR ? 1.0f : 0.0f,
 | |
|       {0.0f},
 | |
|       {mat._11, mat._21, mat._31, 0.0f, mat._12, mat._22, mat._32, 0.0f}};
 | |
| 
 | |
|   hr = mDrawInfo->SetPixelShaderConstantBuffer((BYTE*)&buffer, sizeof(buffer));
 | |
| 
 | |
|   if (FAILED(hr)) {
 | |
|     return hr;
 | |
|   }
 | |
| 
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| IFACEMETHODIMP
 | |
| ConicGradientEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph) {
 | |
|   return pGraph->SetSingleTransformNode(this);
 | |
| }
 | |
| 
 | |
| IFACEMETHODIMP_(ULONG)
 | |
| ConicGradientEffectD2D1::AddRef() { return ++mRefCount; }
 | |
| 
 | |
| IFACEMETHODIMP_(ULONG)
 | |
| ConicGradientEffectD2D1::Release() {
 | |
|   if (!--mRefCount) {
 | |
|     delete this;
 | |
|     return 0;
 | |
|   }
 | |
|   return mRefCount;
 | |
| }
 | |
| 
 | |
| IFACEMETHODIMP
 | |
| ConicGradientEffectD2D1::QueryInterface(const IID& aIID, void** aPtr) {
 | |
|   if (!aPtr) {
 | |
|     return E_POINTER;
 | |
|   }
 | |
| 
 | |
|   if (aIID == IID_IUnknown) {
 | |
|     *aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this));
 | |
|   } else if (aIID == IID_ID2D1EffectImpl) {
 | |
|     *aPtr = static_cast<ID2D1EffectImpl*>(this);
 | |
|   } else if (aIID == IID_ID2D1DrawTransform) {
 | |
|     *aPtr = static_cast<ID2D1DrawTransform*>(this);
 | |
|   } else if (aIID == IID_ID2D1Transform) {
 | |
|     *aPtr = static_cast<ID2D1Transform*>(this);
 | |
|   } else if (aIID == IID_ID2D1TransformNode) {
 | |
|     *aPtr = static_cast<ID2D1TransformNode*>(this);
 | |
|   } else {
 | |
|     return E_NOINTERFACE;
 | |
|   }
 | |
| 
 | |
|   static_cast<IUnknown*>(*aPtr)->AddRef();
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| IFACEMETHODIMP
 | |
| ConicGradientEffectD2D1::MapInputRectsToOutputRect(
 | |
|     const D2D1_RECT_L* pInputRects, const D2D1_RECT_L* pInputOpaqueSubRects,
 | |
|     UINT32 inputRectCount, D2D1_RECT_L* pOutputRect,
 | |
|     D2D1_RECT_L* pOutputOpaqueSubRect) {
 | |
|   if (inputRectCount != 1) {
 | |
|     return E_INVALIDARG;
 | |
|   }
 | |
| 
 | |
|   *pOutputRect = *pInputRects;
 | |
|   *pOutputOpaqueSubRect = *pInputOpaqueSubRects;
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| IFACEMETHODIMP
 | |
| ConicGradientEffectD2D1::MapOutputRectToInputRects(
 | |
|     const D2D1_RECT_L* pOutputRect, D2D1_RECT_L* pInputRects,
 | |
|     UINT32 inputRectCount) const {
 | |
|   if (inputRectCount != 1) {
 | |
|     return E_INVALIDARG;
 | |
|   }
 | |
| 
 | |
|   *pInputRects = *pOutputRect;
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| IFACEMETHODIMP
 | |
| ConicGradientEffectD2D1::MapInvalidRect(UINT32 inputIndex,
 | |
|                                         D2D1_RECT_L invalidInputRect,
 | |
|                                         D2D1_RECT_L* pInvalidOutputRect) const {
 | |
|   MOZ_ASSERT(inputIndex == 0);
 | |
| 
 | |
|   *pInvalidOutputRect = invalidInputRect;
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| IFACEMETHODIMP
 | |
| ConicGradientEffectD2D1::SetDrawInfo(ID2D1DrawInfo* pDrawInfo) {
 | |
|   mDrawInfo = pDrawInfo;
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| HRESULT
 | |
| ConicGradientEffectD2D1::Register(ID2D1Factory1* aFactory) {
 | |
|   D2D1_PROPERTY_BINDING bindings[] = {
 | |
|       D2D1_VALUE_TYPE_BINDING(L"StopCollection",
 | |
|                               &ConicGradientEffectD2D1::SetStopCollection,
 | |
|                               &ConicGradientEffectD2D1::GetStopCollection),
 | |
|       D2D1_VALUE_TYPE_BINDING(L"Center", &ConicGradientEffectD2D1::SetCenter,
 | |
|                               &ConicGradientEffectD2D1::GetCenter),
 | |
|       D2D1_VALUE_TYPE_BINDING(L"Angle", &ConicGradientEffectD2D1::SetAngle,
 | |
|                               &ConicGradientEffectD2D1::GetAngle),
 | |
|       D2D1_VALUE_TYPE_BINDING(L"StartOffset",
 | |
|                               &ConicGradientEffectD2D1::SetStartOffset,
 | |
|                               &ConicGradientEffectD2D1::GetStartOffset),
 | |
|       D2D1_VALUE_TYPE_BINDING(L"EndOffset",
 | |
|                               &ConicGradientEffectD2D1::SetEndOffset,
 | |
|                               &ConicGradientEffectD2D1::GetEndOffset),
 | |
|       D2D1_VALUE_TYPE_BINDING(L"Transform",
 | |
|                               &ConicGradientEffectD2D1::SetTransform,
 | |
|                               &ConicGradientEffectD2D1::GetTransform)};
 | |
|   HRESULT hr = aFactory->RegisterEffectFromString(
 | |
|       CLSID_ConicGradientEffect, kXmlDescription, bindings, ARRAYSIZE(bindings),
 | |
|       CreateEffect);
 | |
| 
 | |
|   if (FAILED(hr)) {
 | |
|     gfxWarning() << "Failed to register radial gradient effect.";
 | |
|   }
 | |
|   return hr;
 | |
| }
 | |
| 
 | |
| void ConicGradientEffectD2D1::Unregister(ID2D1Factory1* aFactory) {
 | |
|   aFactory->UnregisterEffect(CLSID_ConicGradientEffect);
 | |
| }
 | |
| 
 | |
| HRESULT __stdcall ConicGradientEffectD2D1::CreateEffect(
 | |
|     IUnknown** aEffectImpl) {
 | |
|   *aEffectImpl = static_cast<ID2D1EffectImpl*>(new ConicGradientEffectD2D1());
 | |
|   (*aEffectImpl)->AddRef();
 | |
| 
 | |
|   return S_OK;
 | |
| }
 | |
| 
 | |
| HRESULT
 | |
| ConicGradientEffectD2D1::SetStopCollection(IUnknown* aStopCollection) {
 | |
|   if (SUCCEEDED(aStopCollection->QueryInterface(
 | |
|           (ID2D1GradientStopCollection**)getter_AddRefs(mStopCollection)))) {
 | |
|     return S_OK;
 | |
|   }
 | |
| 
 | |
|   return E_INVALIDARG;
 | |
| }
 | |
| 
 | |
| already_AddRefed<ID2D1ResourceTexture>
 | |
| ConicGradientEffectD2D1::CreateGradientTexture() {
 | |
|   std::vector<D2D1_GRADIENT_STOP> rawStops;
 | |
|   rawStops.resize(mStopCollection->GetGradientStopCount());
 | |
|   mStopCollection->GetGradientStops(&rawStops.front(), rawStops.size());
 | |
| 
 | |
|   std::vector<unsigned char> textureData;
 | |
|   textureData.resize(4096 * 4);
 | |
|   unsigned char* texData = &textureData.front();
 | |
| 
 | |
|   float prevColorPos = 0;
 | |
|   float nextColorPos = rawStops[0].position;
 | |
|   D2D1_COLOR_F prevColor = rawStops[0].color;
 | |
|   D2D1_COLOR_F nextColor = prevColor;
 | |
|   uint32_t stopPosition = 1;
 | |
| 
 | |
|   // Not the most optimized way but this will do for now.
 | |
|   for (int i = 0; i < 4096; i++) {
 | |
|     // The 4095 seems a little counter intuitive, but we want the gradient
 | |
|     // color at offset 0 at the first pixel, and at offset 1.0f at the last
 | |
|     // pixel.
 | |
|     float pos = float(i) / 4095;
 | |
| 
 | |
|     while (pos > nextColorPos) {
 | |
|       prevColor = nextColor;
 | |
|       prevColorPos = nextColorPos;
 | |
|       if (rawStops.size() > stopPosition) {
 | |
|         nextColor = rawStops[stopPosition].color;
 | |
|         nextColorPos = rawStops[stopPosition++].position;
 | |
|       } else {
 | |
|         nextColorPos = 1.0f;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     float interp;
 | |
| 
 | |
|     if (nextColorPos != prevColorPos) {
 | |
|       interp = (pos - prevColorPos) / (nextColorPos - prevColorPos);
 | |
|     } else {
 | |
|       interp = 0;
 | |
|     }
 | |
| 
 | |
|     DeviceColor newColor(prevColor.r + (nextColor.r - prevColor.r) * interp,
 | |
|                          prevColor.g + (nextColor.g - prevColor.g) * interp,
 | |
|                          prevColor.b + (nextColor.b - prevColor.b) * interp,
 | |
|                          prevColor.a + (nextColor.a - prevColor.a) * interp);
 | |
| 
 | |
|     // Note D2D expects RGBA here!!
 | |
|     texData[i * 4] = (char)(255.0f * newColor.r);
 | |
|     texData[i * 4 + 1] = (char)(255.0f * newColor.g);
 | |
|     texData[i * 4 + 2] = (char)(255.0f * newColor.b);
 | |
|     texData[i * 4 + 3] = (char)(255.0f * newColor.a);
 | |
|   }
 | |
| 
 | |
|   RefPtr<ID2D1ResourceTexture> tex;
 | |
| 
 | |
|   UINT32 width = 4096;
 | |
|   UINT32 stride = 4096 * 4;
 | |
|   D2D1_RESOURCE_TEXTURE_PROPERTIES props;
 | |
|   // Older shader models do not support 1D textures. So just use a width x 1
 | |
|   // texture.
 | |
|   props.dimensions = 2;
 | |
|   UINT32 dims[] = {width, 1};
 | |
|   props.extents = dims;
 | |
|   props.channelDepth = D2D1_CHANNEL_DEPTH_4;
 | |
|   props.bufferPrecision = D2D1_BUFFER_PRECISION_8BPC_UNORM;
 | |
|   props.filter = D2D1_FILTER_MIN_MAG_MIP_LINEAR;
 | |
|   D2D1_EXTEND_MODE extendMode[] = {mStopCollection->GetExtendMode(),
 | |
|                                    mStopCollection->GetExtendMode()};
 | |
|   props.extendModes = extendMode;
 | |
| 
 | |
|   HRESULT hr = mEffectContext->CreateResourceTexture(
 | |
|       nullptr, &props, &textureData.front(), &stride, 4096 * 4,
 | |
|       getter_AddRefs(tex));
 | |
| 
 | |
|   if (FAILED(hr)) {
 | |
|     gfxWarning() << "Failed to create resource texture: " << hexa(hr);
 | |
|   }
 | |
| 
 | |
|   return tex.forget();
 | |
| }
 | |
| 
 | |
| }  // namespace gfx
 | |
| }  // namespace mozilla
 | 
