// Copyright 2017 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "core/fxge/dib/cfx_imagerenderer.h"

#include <memory>

#include "core/fxge/dib/cfx_imagestretcher.h"
#include "core/fxge/dib/cfx_imagetransformer.h"
#include "core/fxge/ge/cfx_cliprgn.h"
#include "third_party/base/ptr_util.h"

CFX_ImageRenderer::CFX_ImageRenderer()
    : m_Status(0),
      m_pIccTransform(nullptr),
      m_bRgbByteOrder(false),
      m_BlendType(FXDIB_BLEND_NORMAL) {}

CFX_ImageRenderer::~CFX_ImageRenderer() {}

bool CFX_ImageRenderer::Start(const CFX_RetainPtr<CFX_DIBitmap>& pDevice,
                              const CFX_ClipRgn* pClipRgn,
                              const CFX_RetainPtr<CFX_DIBSource>& pSource,
                              int bitmap_alpha,
                              uint32_t mask_color,
                              const CFX_Matrix* pMatrix,
                              uint32_t dib_flags,
                              bool bRgbByteOrder,
                              int alpha_flag,
                              void* pIccTransform,
                              int blend_type) {
  m_Matrix = *pMatrix;
  CFX_FloatRect image_rect_f = m_Matrix.GetUnitRect();
  FX_RECT image_rect = image_rect_f.GetOuterRect();
  m_ClipBox = pClipRgn ? pClipRgn->GetBox() : FX_RECT(0, 0, pDevice->GetWidth(),
                                                      pDevice->GetHeight());
  m_ClipBox.Intersect(image_rect);
  if (m_ClipBox.IsEmpty())
    return false;

  m_pDevice = pDevice;
  m_pClipRgn = pClipRgn;
  m_MaskColor = mask_color;
  m_BitmapAlpha = bitmap_alpha;
  m_Matrix = *pMatrix;
  m_Flags = dib_flags;
  m_AlphaFlag = alpha_flag;
  m_pIccTransform = pIccTransform;
  m_bRgbByteOrder = bRgbByteOrder;
  m_BlendType = blend_type;

  if ((fabs(m_Matrix.b) >= 0.5f || m_Matrix.a == 0) ||
      (fabs(m_Matrix.c) >= 0.5f || m_Matrix.d == 0)) {
    if (fabs(m_Matrix.a) < fabs(m_Matrix.b) / 20 &&
        fabs(m_Matrix.d) < fabs(m_Matrix.c) / 20 && fabs(m_Matrix.a) < 0.5f &&
        fabs(m_Matrix.d) < 0.5f) {
      int dest_width = image_rect.Width();
      int dest_height = image_rect.Height();
      FX_RECT bitmap_clip = m_ClipBox;
      bitmap_clip.Offset(-image_rect.left, -image_rect.top);
      bitmap_clip = FXDIB_SwapClipBox(bitmap_clip, dest_width, dest_height,
                                      m_Matrix.c > 0, m_Matrix.b < 0);
      m_Composer.Compose(pDevice, pClipRgn, bitmap_alpha, mask_color, m_ClipBox,
                         true, m_Matrix.c > 0, m_Matrix.b < 0, m_bRgbByteOrder,
                         alpha_flag, pIccTransform, m_BlendType);
      m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
          &m_Composer, pSource, dest_height, dest_width, bitmap_clip,
          dib_flags);
      if (!m_Stretcher->Start())
        return false;

      m_Status = 1;
      return true;
    }
    m_Status = 2;
    m_pTransformer = pdfium::MakeUnique<CFX_ImageTransformer>(
        pSource, &m_Matrix, dib_flags, &m_ClipBox);
    m_pTransformer->Start();
    return true;
  }

  int dest_width = image_rect.Width();
  if (m_Matrix.a < 0)
    dest_width = -dest_width;

  int dest_height = image_rect.Height();
  if (m_Matrix.d > 0)
    dest_height = -dest_height;

  if (dest_width == 0 || dest_height == 0)
    return false;

  FX_RECT bitmap_clip = m_ClipBox;
  bitmap_clip.Offset(-image_rect.left, -image_rect.top);
  m_Composer.Compose(pDevice, pClipRgn, bitmap_alpha, mask_color, m_ClipBox,
                     false, false, false, m_bRgbByteOrder, alpha_flag,
                     pIccTransform, m_BlendType);
  m_Status = 1;
  m_Stretcher = pdfium::MakeUnique<CFX_ImageStretcher>(
      &m_Composer, pSource, dest_width, dest_height, bitmap_clip, dib_flags);
  return m_Stretcher->Start();
}

bool CFX_ImageRenderer::Continue(IFX_Pause* pPause) {
  if (m_Status == 1)
    return m_Stretcher->Continue(pPause);

  if (m_Status != 2)
    return false;

  if (m_pTransformer->Continue(pPause))
    return true;

  CFX_RetainPtr<CFX_DIBitmap> pBitmap = m_pTransformer->DetachBitmap();
  if (!pBitmap || !pBitmap->GetBuffer())
    return false;

  if (pBitmap->IsAlphaMask()) {
    if (m_BitmapAlpha != 255) {
      if (m_AlphaFlag >> 8) {
        m_AlphaFlag = (((uint8_t)((m_AlphaFlag & 0xff) * m_BitmapAlpha / 255)) |
                       ((m_AlphaFlag >> 8) << 8));
      } else {
        m_MaskColor = FXARGB_MUL_ALPHA(m_MaskColor, m_BitmapAlpha);
      }
    }
    m_pDevice->CompositeMask(
        m_pTransformer->result().left, m_pTransformer->result().top,
        pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, m_MaskColor, 0, 0,
        m_BlendType, m_pClipRgn, m_bRgbByteOrder, m_AlphaFlag, m_pIccTransform);
  } else {
    if (m_BitmapAlpha != 255)
      pBitmap->MultiplyAlpha(m_BitmapAlpha);
    m_pDevice->CompositeBitmap(
        m_pTransformer->result().left, m_pTransformer->result().top,
        pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, 0, 0, m_BlendType,
        m_pClipRgn, m_bRgbByteOrder, m_pIccTransform);
  }
  return false;
}
