text: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
    * 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 <= LONG_MIN ? LONG_MIN : LONG(aRect.x),
                       aRect.y <= LONG_MIN ? LONG_MIN : LONG(aRect.y),
                       aRect.z >= LONG_MAX ? LONG_MAX : LONG(aRect.z),
                       aRect.w >= 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;
  }
  
  }
  }
  