summaryrefslogtreecommitdiffstats
path: root/gfx/2d/ExtendInputEffectD2D1.cpp
blob: 1203df9a4ffe807f5870fe37f172492fdad20d58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/* -*- 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 "ExtendInputEffectD2D1.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='ExtendInputEffect'/>
            <Property name='Author' type='string' value='Mozilla'/>
            <Property name='Category' type='string' value='Utility Effects'/>
            <Property name='Description' type='string' value='This effect is used to extend the output rect of any input effect to a specified rect.'/>
            <Inputs>
                <Input name='InputEffect'/>
            </Inputs>
            <Property name='OutputRect' type='vector4'>
              <Property name='DisplayName' type='string' value='Output Rect'/>
            </Property>
        </Effect>
        );

namespace mozilla {
namespace gfx {

ExtendInputEffectD2D1::ExtendInputEffectD2D1()
    : mRefCount(0),
      mOutputRect(D2D1::Vector4F(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX)) {}

IFACEMETHODIMP
ExtendInputEffectD2D1::Initialize(ID2D1EffectContext* pContextInternal,
                                  ID2D1TransformGraph* pTransformGraph) {
  HRESULT hr;
  hr = pTransformGraph->SetSingleTransformNode(this);

  if (FAILED(hr)) {
    return hr;
  }

  return S_OK;
}

IFACEMETHODIMP
ExtendInputEffectD2D1::PrepareForRender(D2D1_CHANGE_TYPE changeType) {
  return S_OK;
}

IFACEMETHODIMP
ExtendInputEffectD2D1::SetGraph(ID2D1TransformGraph* pGraph) {
  return E_NOTIMPL;
}

IFACEMETHODIMP_(ULONG)
ExtendInputEffectD2D1::AddRef() { return ++mRefCount; }

IFACEMETHODIMP_(ULONG)
ExtendInputEffectD2D1::Release() {
  if (!--mRefCount) {
    delete this;
    return 0;
  }
  return mRefCount;
}

IFACEMETHODIMP
ExtendInputEffectD2D1::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;
}

static D2D1_RECT_L ConvertFloatToLongRect(const D2D1_VECTOR_4F& aRect) {
  // Clamp values to LONG range. We can't use std::min/max here because we want
  // the comparison to operate on a type that's different from the type of the
  // result.
  return D2D1::RectL(aRect.x <= float(LONG_MIN) ? LONG_MIN : LONG(aRect.x),
                     aRect.y <= float(LONG_MIN) ? LONG_MIN : LONG(aRect.y),
                     aRect.z >= float(LONG_MAX) ? LONG_MAX : LONG(aRect.z),
                     aRect.w >= float(LONG_MAX) ? LONG_MAX : LONG(aRect.w));
}

static D2D1_RECT_L IntersectRect(const D2D1_RECT_L& aRect1,
                                 const D2D1_RECT_L& aRect2) {
  return D2D1::RectL(std::max(aRect1.left, aRect2.left),
                     std::max(aRect1.top, aRect2.top),
                     std::min(aRect1.right, aRect2.right),
                     std::min(aRect1.bottom, aRect2.bottom));
}

IFACEMETHODIMP
ExtendInputEffectD2D1::MapInputRectsToOutputRect(
    const D2D1_RECT_L* pInputRects, const D2D1_RECT_L* pInputOpaqueSubRects,
    UINT32 inputRectCount, D2D1_RECT_L* pOutputRect,
    D2D1_RECT_L* pOutputOpaqueSubRect) {
  // This transform only accepts one input, so there will only be one input
  // rect.
  if (inputRectCount != 1) {
    return E_INVALIDARG;
  }

  // Set the output rect to the specified rect. This is the whole purpose of
  // this effect.
  *pOutputRect = ConvertFloatToLongRect(mOutputRect);
  *pOutputOpaqueSubRect = IntersectRect(*pOutputRect, pInputOpaqueSubRects[0]);
  return S_OK;
}

IFACEMETHODIMP
ExtendInputEffectD2D1::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
ExtendInputEffectD2D1::MapInvalidRect(UINT32 inputIndex,
                                      D2D1_RECT_L invalidInputRect,
                                      D2D1_RECT_L* pInvalidOutputRect) const {
  MOZ_ASSERT(inputIndex == 0);

  *pInvalidOutputRect = invalidInputRect;
  return S_OK;
}

HRESULT
ExtendInputEffectD2D1::Register(ID2D1Factory1* aFactory) {
  D2D1_PROPERTY_BINDING bindings[] = {
      D2D1_VALUE_TYPE_BINDING(L"OutputRect",
                              &ExtendInputEffectD2D1::SetOutputRect,
                              &ExtendInputEffectD2D1::GetOutputRect),
  };
  HRESULT hr = aFactory->RegisterEffectFromString(
      CLSID_ExtendInputEffect, kXmlDescription, bindings, 1, CreateEffect);

  if (FAILED(hr)) {
    gfxWarning() << "Failed to register extend input effect.";
  }
  return hr;
}

void ExtendInputEffectD2D1::Unregister(ID2D1Factory1* aFactory) {
  aFactory->UnregisterEffect(CLSID_ExtendInputEffect);
}

HRESULT __stdcall ExtendInputEffectD2D1::CreateEffect(IUnknown** aEffectImpl) {
  *aEffectImpl = static_cast<ID2D1EffectImpl*>(new ExtendInputEffectD2D1());
  (*aEffectImpl)->AddRef();

  return S_OK;
}

}  // namespace gfx
}  // namespace mozilla