// Copyright 2017 The PDFium Authors
// 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/agg/cfx_agg_imagerenderer.h"

#include <math.h>

#include <memory>
#include <utility>

#include "core/fxcrt/fx_system.h"
#include "core/fxge/agg/cfx_agg_cliprgn.h"
#include "core/fxge/dib/cfx_dibitmap.h"
#include "core/fxge/dib/cfx_imagestretcher.h"
#include "core/fxge/dib/cfx_imagetransformer.h"

CFX_AggImageRenderer::CFX_AggImageRenderer(
    const RetainPtr<CFX_DIBitmap>& pDevice,
    const CFX_AggClipRgn* pClipRgn,
    RetainPtr<const CFX_DIBBase> source,
    float alpha,
    uint32_t mask_color,
    const CFX_Matrix& matrix,
    const FXDIB_ResampleOptions& options,
    bool bRgbByteOrder)
    : device_(pDevice),
      clip_rgn_(pClipRgn),
      matrix_(matrix),
      alpha_(alpha),
      mask_color_(mask_color),
      rgb_byte_order_(bRgbByteOrder) {
  FX_RECT image_rect = matrix_.GetUnitRect().GetOuterRect();
  clip_box_ = pClipRgn
                  ? pClipRgn->GetBox()
                  : FX_RECT(0, 0, pDevice->GetWidth(), pDevice->GetHeight());
  clip_box_.Intersect(image_rect);
  if (clip_box_.IsEmpty()) {
    return;
  }

  if ((fabs(matrix_.b) >= 0.5f || matrix_.a == 0) ||
      (fabs(matrix_.c) >= 0.5f || matrix_.d == 0)) {
    if (fabs(matrix_.a) < fabs(matrix_.b) / 20 &&
        fabs(matrix_.d) < fabs(matrix_.c) / 20 && fabs(matrix_.a) < 0.5f &&
        fabs(matrix_.d) < 0.5f) {
      int dest_width = image_rect.Width();
      int dest_height = image_rect.Height();
      FX_RECT bitmap_clip = clip_box_;
      bitmap_clip.Offset(-image_rect.left, -image_rect.top);
      bitmap_clip = bitmap_clip.SwappedClipBox(dest_width, dest_height,
                                               matrix_.c > 0, matrix_.b < 0);
      const bool flip_x = matrix_.c > 0;
      const bool flip_y = matrix_.b < 0;
      composer_.Compose(pDevice, pClipRgn, alpha, mask_color, clip_box_,
                        /*bVertical=*/true, flip_x, flip_y, rgb_byte_order_,
                        BlendMode::kNormal);
      stretcher_ = std::make_unique<CFX_ImageStretcher>(
          &composer_, std::move(source), dest_height, dest_width, bitmap_clip,
          options);
      if (stretcher_->Start()) {
        state_ = State::kStretching;
      }
      return;
    }
    state_ = State::kTransforming;
    transformer_ = std::make_unique<CFX_ImageTransformer>(
        std::move(source), matrix_, options, &clip_box_);
    return;
  }

  int dest_width = image_rect.Width();
  if (matrix_.a < 0) {
    dest_width = -dest_width;
  }

  int dest_height = image_rect.Height();
  if (matrix_.d > 0) {
    dest_height = -dest_height;
  }

  if (dest_width == 0 || dest_height == 0) {
    return;
  }

  FX_RECT bitmap_clip = clip_box_;
  bitmap_clip.Offset(-image_rect.left, -image_rect.top);
  composer_.Compose(pDevice, pClipRgn, alpha, mask_color, clip_box_,
                    /*bVertical=*/false, /*bFlipX=*/false, /*bFlipY=*/false,
                    rgb_byte_order_, BlendMode::kNormal);
  state_ = State::kStretching;
  stretcher_ = std::make_unique<CFX_ImageStretcher>(
      &composer_, std::move(source), dest_width, dest_height, bitmap_clip,
      options);
  stretcher_->Start();
}

CFX_AggImageRenderer::~CFX_AggImageRenderer() = default;

bool CFX_AggImageRenderer::Continue(PauseIndicatorIface* pPause) {
  if (state_ == State::kStretching) {
    return stretcher_->Continue(pPause);
  }
  if (state_ != State::kTransforming) {
    return false;
  }
  if (transformer_->Continue(pPause)) {
    return true;
  }

  RetainPtr<CFX_DIBitmap> pBitmap = transformer_->DetachBitmap();
  if (!pBitmap || pBitmap->GetBuffer().empty()) {
    return false;
  }

  const FX_RECT* clip_rect = nullptr;
  RetainPtr<CFX_DIBitmap> clip_mask;
  if (clip_rgn_) {
    clip_rect = &clip_rgn_->GetBox();
    clip_mask = clip_rgn_->GetMask();
  }

  if (pBitmap->IsMaskFormat()) {
    if (alpha_ != 1.0f) {
      mask_color_ = FXARGB_MUL_ALPHA(mask_color_, FXSYS_roundf(alpha_ * 255));
    }
    device_->CompositeMask(
        transformer_->result().left, transformer_->result().top,
        pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, mask_color_, 0, 0,
        BlendMode::kNormal, clip_rect, clip_mask, rgb_byte_order_);
  } else {
    pBitmap->MultiplyAlpha(alpha_);
    device_->CompositeBitmap(
        transformer_->result().left, transformer_->result().top,
        pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap, 0, 0,
        BlendMode::kNormal, clip_rect, clip_mask, rgb_byte_order_);
  }
  return false;
}
