1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "ExtendInputEffectD2D1.h"
8 
9 #include "Logging.h"
10 
11 #include "ShadersD2D1.h"
12 #include "HelpersD2D.h"
13 
14 #include <vector>
15 
16 #define TEXTW(x) L##x
17 #define XML(X) \
18   TEXTW(#X)  // This macro creates a single string from multiple lines of text.
19 
20 static const PCWSTR kXmlDescription =
21     XML(
22         <?xml version='1.0'?>
23         <Effect>
24             <!-- System Properties -->
25             <Property name='DisplayName' type='string' value='ExtendInputEffect'/>
26             <Property name='Author' type='string' value='Mozilla'/>
27             <Property name='Category' type='string' value='Utility Effects'/>
28             <Property name='Description' type='string' value='This effect is used to extend the output rect of any input effect to a specified rect.'/>
29             <Inputs>
30                 <Input name='InputEffect'/>
31             </Inputs>
32             <Property name='OutputRect' type='vector4'>
33               <Property name='DisplayName' type='string' value='Output Rect'/>
34             </Property>
35         </Effect>
36         );
37 
38 namespace mozilla {
39 namespace gfx {
40 
ExtendInputEffectD2D1()41 ExtendInputEffectD2D1::ExtendInputEffectD2D1()
42     : mRefCount(0),
43       mOutputRect(D2D1::Vector4F(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX)) {}
44 
45 IFACEMETHODIMP
Initialize(ID2D1EffectContext * pContextInternal,ID2D1TransformGraph * pTransformGraph)46 ExtendInputEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal,
47                                   ID2D1TransformGraph* pTransformGraph) {
48   HRESULT hr;
49   hr = pTransformGraph->SetSingleTransformNode(this);
50 
51   if (FAILED(hr)) {
52     return hr;
53   }
54 
55   return S_OK;
56 }
57 
58 IFACEMETHODIMP
PrepareForRender(D2D1_CHANGE_TYPE changeType)59 ExtendInputEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType) {
60   return S_OK;
61 }
62 
63 IFACEMETHODIMP
SetGraph(ID2D1TransformGraph * pGraph)64 ExtendInputEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph) {
65   return E_NOTIMPL;
66 }
67 
IFACEMETHODIMP_(ULONG)68 IFACEMETHODIMP_(ULONG)
69 ExtendInputEffectD2D1::AddRef() { return ++mRefCount; }
70 
IFACEMETHODIMP_(ULONG)71 IFACEMETHODIMP_(ULONG)
72 ExtendInputEffectD2D1::Release() {
73   if (!--mRefCount) {
74     delete this;
75     return 0;
76   }
77   return mRefCount;
78 }
79 
80 IFACEMETHODIMP
QueryInterface(const IID & aIID,void ** aPtr)81 ExtendInputEffectD2D1::QueryInterface(const IID& aIID, void** aPtr) {
82   if (!aPtr) {
83     return E_POINTER;
84   }
85 
86   if (aIID == IID_IUnknown) {
87     *aPtr = static_cast<IUnknown*>(static_cast<ID2D1EffectImpl*>(this));
88   } else if (aIID == IID_ID2D1EffectImpl) {
89     *aPtr = static_cast<ID2D1EffectImpl*>(this);
90   } else if (aIID == IID_ID2D1DrawTransform) {
91     *aPtr = static_cast<ID2D1DrawTransform*>(this);
92   } else if (aIID == IID_ID2D1Transform) {
93     *aPtr = static_cast<ID2D1Transform*>(this);
94   } else if (aIID == IID_ID2D1TransformNode) {
95     *aPtr = static_cast<ID2D1TransformNode*>(this);
96   } else {
97     return E_NOINTERFACE;
98   }
99 
100   static_cast<IUnknown*>(*aPtr)->AddRef();
101   return S_OK;
102 }
103 
ConvertFloatToLongRect(const D2D1_VECTOR_4F & aRect)104 static D2D1_RECT_L ConvertFloatToLongRect(const D2D1_VECTOR_4F& aRect) {
105   // Clamp values to LONG range. We can't use std::min/max here because we want
106   // the comparison to operate on a type that's different from the type of the
107   // result.
108   return D2D1::RectL(aRect.x <= LONG_MIN ? LONG_MIN : LONG(aRect.x),
109                      aRect.y <= LONG_MIN ? LONG_MIN : LONG(aRect.y),
110                      aRect.z >= LONG_MAX ? LONG_MAX : LONG(aRect.z),
111                      aRect.w >= LONG_MAX ? LONG_MAX : LONG(aRect.w));
112 }
113 
IntersectRect(const D2D1_RECT_L & aRect1,const D2D1_RECT_L & aRect2)114 static D2D1_RECT_L IntersectRect(const D2D1_RECT_L& aRect1,
115                                  const D2D1_RECT_L& aRect2) {
116   return D2D1::RectL(std::max(aRect1.left, aRect2.left),
117                      std::max(aRect1.top, aRect2.top),
118                      std::min(aRect1.right, aRect2.right),
119                      std::min(aRect1.bottom, aRect2.bottom));
120 }
121 
122 IFACEMETHODIMP
MapInputRectsToOutputRect(const D2D1_RECT_L * pInputRects,const D2D1_RECT_L * pInputOpaqueSubRects,UINT32 inputRectCount,D2D1_RECT_L * pOutputRect,D2D1_RECT_L * pOutputOpaqueSubRect)123 ExtendInputEffectD2D1::MapInputRectsToOutputRect(
124     const D2D1_RECT_L* pInputRects, const D2D1_RECT_L* pInputOpaqueSubRects,
125     UINT32 inputRectCount, D2D1_RECT_L* pOutputRect,
126     D2D1_RECT_L* pOutputOpaqueSubRect) {
127   // This transform only accepts one input, so there will only be one input
128   // rect.
129   if (inputRectCount != 1) {
130     return E_INVALIDARG;
131   }
132 
133   // Set the output rect to the specified rect. This is the whole purpose of
134   // this effect.
135   *pOutputRect = ConvertFloatToLongRect(mOutputRect);
136   *pOutputOpaqueSubRect = IntersectRect(*pOutputRect, pInputOpaqueSubRects[0]);
137   return S_OK;
138 }
139 
140 IFACEMETHODIMP
MapOutputRectToInputRects(const D2D1_RECT_L * pOutputRect,D2D1_RECT_L * pInputRects,UINT32 inputRectCount) const141 ExtendInputEffectD2D1::MapOutputRectToInputRects(const D2D1_RECT_L* pOutputRect,
142                                                  D2D1_RECT_L* pInputRects,
143                                                  UINT32 inputRectCount) const {
144   if (inputRectCount != 1) {
145     return E_INVALIDARG;
146   }
147 
148   *pInputRects = *pOutputRect;
149   return S_OK;
150 }
151 
152 IFACEMETHODIMP
MapInvalidRect(UINT32 inputIndex,D2D1_RECT_L invalidInputRect,D2D1_RECT_L * pInvalidOutputRect) const153 ExtendInputEffectD2D1::MapInvalidRect(UINT32 inputIndex,
154                                       D2D1_RECT_L invalidInputRect,
155                                       D2D1_RECT_L* pInvalidOutputRect) const {
156   MOZ_ASSERT(inputIndex == 0);
157 
158   *pInvalidOutputRect = invalidInputRect;
159   return S_OK;
160 }
161 
162 HRESULT
Register(ID2D1Factory1 * aFactory)163 ExtendInputEffectD2D1::Register(ID2D1Factory1* aFactory) {
164   D2D1_PROPERTY_BINDING bindings[] = {
165       D2D1_VALUE_TYPE_BINDING(L"OutputRect",
166                               &ExtendInputEffectD2D1::SetOutputRect,
167                               &ExtendInputEffectD2D1::GetOutputRect),
168   };
169   HRESULT hr = aFactory->RegisterEffectFromString(
170       CLSID_ExtendInputEffect, kXmlDescription, bindings, 1, CreateEffect);
171 
172   if (FAILED(hr)) {
173     gfxWarning() << "Failed to register extend input effect.";
174   }
175   return hr;
176 }
177 
Unregister(ID2D1Factory1 * aFactory)178 void ExtendInputEffectD2D1::Unregister(ID2D1Factory1* aFactory) {
179   aFactory->UnregisterEffect(CLSID_ExtendInputEffect);
180 }
181 
CreateEffect(IUnknown ** aEffectImpl)182 HRESULT __stdcall ExtendInputEffectD2D1::CreateEffect(IUnknown** aEffectImpl) {
183   *aEffectImpl = static_cast<ID2D1EffectImpl*>(new ExtendInputEffectD2D1());
184   (*aEffectImpl)->AddRef();
185 
186   return S_OK;
187 }
188 
189 }  // namespace gfx
190 }  // namespace mozilla
191