// Copyright 2014 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/fxcodec/codec/ccodec_progressivedecoder.h"

#include <algorithm>
#include <memory>

#include "core/fxcodec/fx_codec.h"
#include "core/fxge/dib/cfx_dibitmap.h"
#include "core/fxge/fx_dib.h"
#include "third_party/base/numerics/safe_math.h"
#include "third_party/base/ptr_util.h"

#define FXCODEC_BLOCK_SIZE 4096

namespace {

#if _FX_OS_ == _FX_MACOSX_ || _FX_OS_ == _FX_IOS_
const double kPngGamma = 1.7;
#else  // _FX_OS_ == _FX_MACOSX_ || _FX_OS_ == _FX_IOS_
const double kPngGamma = 2.2;
#endif  // _FX_OS_ == _FX_MACOSX_ || _FX_OS_ == _FX_IOS_

void RGB2BGR(uint8_t* buffer, int width = 1) {
  if (buffer && width > 0) {
    uint8_t temp;
    int i = 0;
    int j = 0;
    for (; i < width; i++, j += 3) {
      temp = buffer[j];
      buffer[j] = buffer[j + 2];
      buffer[j + 2] = temp;
    }
  }
}

}  // namespace

CCodec_ProgressiveDecoder::CFXCODEC_WeightTable::CFXCODEC_WeightTable() {}

CCodec_ProgressiveDecoder::CFXCODEC_WeightTable::~CFXCODEC_WeightTable() {}

void CCodec_ProgressiveDecoder::CFXCODEC_WeightTable::Calc(int dest_len,
                                                           int dest_min,
                                                           int dest_max,
                                                           int src_len,
                                                           int src_min,
                                                           int src_max,
                                                           bool bInterpol) {
  double scale, base;
  scale = (float)src_len / (float)dest_len;
  if (dest_len < 0) {
    base = (float)(src_len);
  } else {
    base = 0.0f;
  }
  m_ItemSize =
      (int)(sizeof(int) * 2 + sizeof(int) * (ceil(fabs((float)scale)) + 1));
  m_DestMin = dest_min;
  m_pWeightTables.resize((dest_max - dest_min) * m_ItemSize + 4);
  if (fabs((float)scale) < 1.0f) {
    for (int dest_pixel = dest_min; dest_pixel < dest_max; dest_pixel++) {
      PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
      double src_pos = dest_pixel * scale + scale / 2 + base;
      if (bInterpol) {
        pixel_weights.m_SrcStart = (int)floor((float)src_pos - 1.0f / 2);
        pixel_weights.m_SrcEnd = (int)floor((float)src_pos + 1.0f / 2);
        if (pixel_weights.m_SrcStart < src_min) {
          pixel_weights.m_SrcStart = src_min;
        }
        if (pixel_weights.m_SrcEnd >= src_max) {
          pixel_weights.m_SrcEnd = src_max - 1;
        }
        if (pixel_weights.m_SrcStart == pixel_weights.m_SrcEnd) {
          pixel_weights.m_Weights[0] = 65536;
        } else {
          pixel_weights.m_Weights[1] = FXSYS_round(
              (float)(src_pos - pixel_weights.m_SrcStart - 1.0f / 2) * 65536);
          pixel_weights.m_Weights[0] = 65536 - pixel_weights.m_Weights[1];
        }
      } else {
        pixel_weights.m_SrcStart = pixel_weights.m_SrcEnd =
            (int)floor((float)src_pos);
        pixel_weights.m_Weights[0] = 65536;
      }
    }
    return;
  }
  for (int dest_pixel = dest_min; dest_pixel < dest_max; dest_pixel++) {
    PixelWeight& pixel_weights = *GetPixelWeight(dest_pixel);
    double src_start = dest_pixel * scale + base;
    double src_end = src_start + scale;
    int start_i, end_i;
    if (src_start < src_end) {
      start_i = (int)floor((float)src_start);
      end_i = (int)ceil((float)src_end);
    } else {
      start_i = (int)floor((float)src_end);
      end_i = (int)ceil((float)src_start);
    }
    if (start_i < src_min) {
      start_i = src_min;
    }
    if (end_i >= src_max) {
      end_i = src_max - 1;
    }
    if (start_i > end_i) {
      pixel_weights.m_SrcStart = start_i;
      pixel_weights.m_SrcEnd = start_i;
      continue;
    }
    pixel_weights.m_SrcStart = start_i;
    pixel_weights.m_SrcEnd = end_i;
    for (int j = start_i; j <= end_i; j++) {
      double dest_start = ((float)j - base) / scale;
      double dest_end = ((float)(j + 1) - base) / scale;
      if (dest_start > dest_end) {
        double temp = dest_start;
        dest_start = dest_end;
        dest_end = temp;
      }
      double area_start =
          dest_start > (float)(dest_pixel) ? dest_start : (float)(dest_pixel);
      double area_end = dest_end > (float)(dest_pixel + 1)
                            ? (float)(dest_pixel + 1)
                            : dest_end;
      double weight = area_start >= area_end ? 0.0f : area_end - area_start;
      if (weight == 0 && j == end_i) {
        pixel_weights.m_SrcEnd--;
        break;
      }
      pixel_weights.m_Weights[j - start_i] =
          FXSYS_round((float)(weight * 65536));
    }
  }
}

CCodec_ProgressiveDecoder::CFXCODEC_HorzTable::CFXCODEC_HorzTable() {}

CCodec_ProgressiveDecoder::CFXCODEC_HorzTable::~CFXCODEC_HorzTable() {}

void CCodec_ProgressiveDecoder::CFXCODEC_HorzTable::Calc(int dest_len,
                                                         int src_len,
                                                         bool bInterpol) {
  double scale = (double)dest_len / (double)src_len;
  m_ItemSize = sizeof(int) * 4;
  int size = dest_len * m_ItemSize + 4;
  m_pWeightTables.resize(size, 0);
  if (scale > 1) {
    int pre_des_col = 0;
    for (int src_col = 0; src_col < src_len; src_col++) {
      double des_col_f = src_col * scale;
      int des_col = FXSYS_round((float)des_col_f);
      PixelWeight* pWeight = GetPixelWeight(des_col);
      pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
      pWeight->m_Weights[0] = 65536;
      pWeight->m_Weights[1] = 0;
      if (src_col == src_len - 1 && des_col < dest_len - 1) {
        for (int des_col_index = pre_des_col + 1; des_col_index < dest_len;
             des_col_index++) {
          pWeight = GetPixelWeight(des_col_index);
          pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
          pWeight->m_Weights[0] = 65536;
          pWeight->m_Weights[1] = 0;
        }
        return;
      }
      int des_col_len = des_col - pre_des_col;
      for (int des_col_index = pre_des_col + 1; des_col_index < des_col;
           des_col_index++) {
        pWeight = GetPixelWeight(des_col_index);
        pWeight->m_SrcStart = src_col - 1;
        pWeight->m_SrcEnd = src_col;
        pWeight->m_Weights[0] =
            bInterpol
                ? FXSYS_round((float)(((float)des_col - (float)des_col_index) /
                                      (float)des_col_len * 65536))
                : 65536;
        pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0];
      }
      pre_des_col = des_col;
    }
    return;
  }
  for (int des_col = 0; des_col < dest_len; des_col++) {
    double src_col_f = des_col / scale;
    int src_col = FXSYS_round((float)src_col_f);
    PixelWeight* pWeight = GetPixelWeight(des_col);
    pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col;
    pWeight->m_Weights[0] = 65536;
    pWeight->m_Weights[1] = 0;
  }
}

CCodec_ProgressiveDecoder::CFXCODEC_VertTable::CFXCODEC_VertTable() {}

CCodec_ProgressiveDecoder::CFXCODEC_VertTable::~CFXCODEC_VertTable() {}

void CCodec_ProgressiveDecoder::CFXCODEC_VertTable::Calc(int dest_len,
                                                         int src_len) {
  double scale = (double)dest_len / (double)src_len;
  m_ItemSize = sizeof(int) * 4;
  int size = dest_len * m_ItemSize + 4;
  m_pWeightTables.resize(size, 0);
  if (scale <= 1) {
    for (int des_row = 0; des_row < dest_len; des_row++) {
      PixelWeight* pWeight = GetPixelWeight(des_row);
      pWeight->m_SrcStart = des_row;
      pWeight->m_SrcEnd = des_row;
      pWeight->m_Weights[0] = 65536;
      pWeight->m_Weights[1] = 0;
    }
    return;
  }

  double step = 0.0;
  int src_row = 0;
  while (step < (double)dest_len) {
    int start_step = (int)step;
    step = scale * (++src_row);
    int end_step = (int)step;
    if (end_step >= dest_len) {
      end_step = dest_len;
      for (int des_row = start_step; des_row < end_step; des_row++) {
        PixelWeight* pWeight = GetPixelWeight(des_row);
        pWeight->m_SrcStart = start_step;
        pWeight->m_SrcEnd = start_step;
        pWeight->m_Weights[0] = 65536;
        pWeight->m_Weights[1] = 0;
      }
      return;
    }
    int length = end_step - start_step;
    {
      PixelWeight* pWeight = GetPixelWeight(start_step);
      pWeight->m_SrcStart = start_step;
      pWeight->m_SrcEnd = start_step;
      pWeight->m_Weights[0] = 65536;
      pWeight->m_Weights[1] = 0;
    }
    for (int des_row = start_step + 1; des_row < end_step; des_row++) {
      PixelWeight* pWeight = GetPixelWeight(des_row);
      pWeight->m_SrcStart = start_step;
      pWeight->m_SrcEnd = end_step;
      pWeight->m_Weights[0] =
          FXSYS_round((float)(end_step - des_row) / (float)length * 65536);
      pWeight->m_Weights[1] = 65536 - pWeight->m_Weights[0];
    }
  }
}

CCodec_ProgressiveDecoder::CCodec_ProgressiveDecoder(
    CCodec_ModuleMgr* pCodecMgr) {
  m_pFile = nullptr;
  m_pJpegContext = nullptr;
  m_pPngContext = nullptr;
  m_pGifContext = nullptr;
  m_pBmpContext = nullptr;
  m_pTiffContext = nullptr;
  m_pCodecMgr = nullptr;
  m_pSrcBuf = nullptr;
  m_pDecodeBuf = nullptr;
  m_pDeviceBitmap = nullptr;
  m_pSrcPalette = nullptr;
  m_pCodecMgr = pCodecMgr;
  m_offSet = 0;
  m_SrcSize = 0;
  m_ScanlineSize = 0;
  m_SrcWidth = 0;
  m_SrcHeight = 0;
  m_SrcComponents = 0;
  m_SrcBPC = 0;
  m_SrcPassNumber = 0;
  m_clipBox = FX_RECT(0, 0, 0, 0);
  m_imagType = FXCODEC_IMAGE_UNKNOWN;
  m_status = FXCODEC_STATUS_DECODE_FINISH;
  m_TransMethod = -1;
  m_SrcRow = 0;
  m_SrcFormat = FXCodec_Invalid;
  m_bInterpol = true;
  m_FrameNumber = 0;
  m_FrameCur = 0;
  m_SrcPaletteNumber = 0;
  m_GifPltNumber = 0;
  m_GifBgIndex = 0;
  m_pGifPalette = nullptr;
  m_GifTransIndex = -1;
  m_GifFrameRect = FX_RECT(0, 0, 0, 0);
  m_BmpIsTopBottom = false;
}

CCodec_ProgressiveDecoder::~CCodec_ProgressiveDecoder() {
  m_pFile = nullptr;
  if (m_pJpegContext)
    m_pCodecMgr->GetJpegModule()->Finish(m_pJpegContext);
  if (m_pBmpContext)
    m_pCodecMgr->GetBmpModule()->Finish(m_pBmpContext);
  if (m_pGifContext)
    m_pCodecMgr->GetGifModule()->Finish(m_pGifContext);
  if (m_pPngContext)
    m_pCodecMgr->GetPngModule()->Finish(m_pPngContext);
  if (m_pTiffContext)
    m_pCodecMgr->GetTiffModule()->DestroyDecoder(m_pTiffContext);
  FX_Free(m_pSrcBuf);
  FX_Free(m_pDecodeBuf);
  FX_Free(m_pSrcPalette);
}

bool CCodec_ProgressiveDecoder::JpegReadMoreData(CCodec_JpegModule* pJpegModule,
                                                 FXCODEC_STATUS& err_status) {
  uint32_t dwSize = (uint32_t)m_pFile->GetSize();
  if (dwSize <= m_offSet) {
    return false;
  }
  dwSize = dwSize - m_offSet;
  uint32_t dwAvail = pJpegModule->GetAvailInput(m_pJpegContext, nullptr);
  if (dwAvail == m_SrcSize) {
    if (dwSize > FXCODEC_BLOCK_SIZE) {
      dwSize = FXCODEC_BLOCK_SIZE;
    }
    m_SrcSize = (dwSize + dwAvail + FXCODEC_BLOCK_SIZE - 1) /
                FXCODEC_BLOCK_SIZE * FXCODEC_BLOCK_SIZE;
    m_pSrcBuf = FX_Realloc(uint8_t, m_pSrcBuf, m_SrcSize);
    if (!m_pSrcBuf) {
      err_status = FXCODEC_STATUS_ERR_MEMORY;
      return false;
    }
  } else {
    uint32_t dwConsume = m_SrcSize - dwAvail;
    if (dwAvail) {
      memmove(m_pSrcBuf, m_pSrcBuf + dwConsume, dwAvail);
    }
    if (dwSize > dwConsume) {
      dwSize = dwConsume;
    }
  }
  if (!m_pFile->ReadBlock(m_pSrcBuf + dwAvail, m_offSet, dwSize)) {
    err_status = FXCODEC_STATUS_ERR_READ;
    return false;
  }
  m_offSet += dwSize;
  pJpegModule->Input(m_pJpegContext, m_pSrcBuf, dwSize + dwAvail);
  return true;
}

bool CCodec_ProgressiveDecoder::PngReadHeader(int width,
                                              int height,
                                              int bpc,
                                              int pass,
                                              int* color_type,
                                              double* gamma) {
  if (!m_pDeviceBitmap) {
    m_SrcWidth = width;
    m_SrcHeight = height;
    m_SrcBPC = bpc;
    m_SrcPassNumber = pass;
    switch (*color_type) {
      case 0:
        m_SrcComponents = 1;
        break;
      case 4:
        m_SrcComponents = 2;
        break;
      case 2:
        m_SrcComponents = 3;
        break;
      case 3:
      case 6:
        m_SrcComponents = 4;
        break;
      default:
        m_SrcComponents = 0;
        break;
    }
    m_clipBox = FX_RECT(0, 0, width, height);
    return false;
  }
  FXDIB_Format format = m_pDeviceBitmap->GetFormat();
  switch (format) {
    case FXDIB_1bppMask:
    case FXDIB_1bppRgb:
      ASSERT(false);
      return false;
    case FXDIB_8bppMask:
    case FXDIB_8bppRgb:
      *color_type = 0;
      break;
    case FXDIB_Rgb:
      *color_type = 2;
      break;
    case FXDIB_Rgb32:
    case FXDIB_Argb:
      *color_type = 6;
      break;
    default:
      ASSERT(false);
      return false;
  }
  *gamma = kPngGamma;
  return true;
}

bool CCodec_ProgressiveDecoder::PngAskScanlineBuf(int line, uint8_t*& src_buf) {
  CFX_RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
  if (!pDIBitmap) {
    ASSERT(false);
    return false;
  }
  if (line >= m_clipBox.top && line < m_clipBox.bottom) {
    double scale_y = (double)m_sizeY / (double)m_clipBox.Height();
    int32_t row = (int32_t)((line - m_clipBox.top) * scale_y) + m_startY;
    uint8_t* src_scan = (uint8_t*)pDIBitmap->GetScanline(row);
    uint8_t* des_scan = m_pDecodeBuf;
    src_buf = m_pDecodeBuf;
    int32_t src_Bpp = pDIBitmap->GetBPP() >> 3;
    int32_t des_Bpp = (m_SrcFormat & 0xff) >> 3;
    int32_t src_left = m_startX;
    int32_t des_left = m_clipBox.left;
    src_scan += src_left * src_Bpp;
    des_scan += des_left * des_Bpp;
    for (int32_t src_col = 0; src_col < m_sizeX; src_col++) {
      PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(src_col);
      if (pPixelWeights->m_SrcStart != pPixelWeights->m_SrcEnd) {
        continue;
      }
      switch (pDIBitmap->GetFormat()) {
        case FXDIB_1bppMask:
        case FXDIB_1bppRgb:
          ASSERT(false);
          return false;
        case FXDIB_8bppMask:
        case FXDIB_8bppRgb: {
          if (pDIBitmap->GetPalette()) {
            return false;
          }
          uint32_t des_g = 0;
          des_g += pPixelWeights->m_Weights[0] * src_scan[src_col];
          des_scan[pPixelWeights->m_SrcStart] = (uint8_t)(des_g >> 16);
        } break;
        case FXDIB_Rgb:
        case FXDIB_Rgb32: {
          uint32_t des_b = 0, des_g = 0, des_r = 0;
          const uint8_t* p = src_scan + src_col * src_Bpp;
          des_b += pPixelWeights->m_Weights[0] * (*p++);
          des_g += pPixelWeights->m_Weights[0] * (*p++);
          des_r += pPixelWeights->m_Weights[0] * (*p);
          uint8_t* pDes = &des_scan[pPixelWeights->m_SrcStart * des_Bpp];
          *pDes++ = (uint8_t)((des_b) >> 16);
          *pDes++ = (uint8_t)((des_g) >> 16);
          *pDes = (uint8_t)((des_r) >> 16);
        } break;
        case FXDIB_Argb: {
          uint32_t des_r = 0, des_g = 0, des_b = 0;
          const uint8_t* p = src_scan + src_col * src_Bpp;
          des_b += pPixelWeights->m_Weights[0] * (*p++);
          des_g += pPixelWeights->m_Weights[0] * (*p++);
          des_r += pPixelWeights->m_Weights[0] * (*p++);
          uint8_t* pDes = &des_scan[pPixelWeights->m_SrcStart * des_Bpp];
          *pDes++ = (uint8_t)((des_b) >> 16);
          *pDes++ = (uint8_t)((des_g) >> 16);
          *pDes++ = (uint8_t)((des_r) >> 16);
          *pDes = *p;
        } break;
        default:
          return false;
      }
    }
  }
  return true;
}

void CCodec_ProgressiveDecoder::PngOneOneMapResampleHorz(
    const CFX_RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
    int32_t des_line,
    uint8_t* src_scan,
    FXCodec_Format src_format) {
  uint8_t* des_scan = (uint8_t*)pDeviceBitmap->GetScanline(des_line);
  int32_t src_Bpp = (m_SrcFormat & 0xff) >> 3;
  int32_t des_Bpp = pDeviceBitmap->GetBPP() >> 3;
  int32_t src_left = m_clipBox.left;
  int32_t des_left = m_startX;
  src_scan += src_left * src_Bpp;
  des_scan += des_left * des_Bpp;
  for (int32_t des_col = 0; des_col < m_sizeX; des_col++) {
    PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(des_col);
    switch (pDeviceBitmap->GetFormat()) {
      case FXDIB_1bppMask:
      case FXDIB_1bppRgb:
        ASSERT(false);
        return;
      case FXDIB_8bppMask:
      case FXDIB_8bppRgb: {
        if (pDeviceBitmap->GetPalette()) {
          return;
        }
        uint32_t des_g = 0;
        des_g +=
            pPixelWeights->m_Weights[0] * src_scan[pPixelWeights->m_SrcStart];
        des_g +=
            pPixelWeights->m_Weights[1] * src_scan[pPixelWeights->m_SrcEnd];
        *des_scan++ = (uint8_t)(des_g >> 16);
      } break;
      case FXDIB_Rgb:
      case FXDIB_Rgb32: {
        uint32_t des_b = 0, des_g = 0, des_r = 0;
        const uint8_t* p = src_scan;
        p = src_scan + pPixelWeights->m_SrcStart * src_Bpp;
        des_b += pPixelWeights->m_Weights[0] * (*p++);
        des_g += pPixelWeights->m_Weights[0] * (*p++);
        des_r += pPixelWeights->m_Weights[0] * (*p);
        p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp;
        des_b += pPixelWeights->m_Weights[1] * (*p++);
        des_g += pPixelWeights->m_Weights[1] * (*p++);
        des_r += pPixelWeights->m_Weights[1] * (*p);
        *des_scan++ = (uint8_t)((des_b) >> 16);
        *des_scan++ = (uint8_t)((des_g) >> 16);
        *des_scan++ = (uint8_t)((des_r) >> 16);
        des_scan += des_Bpp - 3;
      } break;
      case FXDIB_Argb: {
        uint32_t des_a = 0, des_b = 0, des_g = 0, des_r = 0;
        const uint8_t* p = src_scan;
        p = src_scan + pPixelWeights->m_SrcStart * src_Bpp;
        des_b += pPixelWeights->m_Weights[0] * (*p++);
        des_g += pPixelWeights->m_Weights[0] * (*p++);
        des_r += pPixelWeights->m_Weights[0] * (*p++);
        des_a += pPixelWeights->m_Weights[0] * (*p);
        p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp;
        des_b += pPixelWeights->m_Weights[1] * (*p++);
        des_g += pPixelWeights->m_Weights[1] * (*p++);
        des_r += pPixelWeights->m_Weights[1] * (*p++);
        des_a += pPixelWeights->m_Weights[1] * (*p);
        *des_scan++ = (uint8_t)((des_b) >> 16);
        *des_scan++ = (uint8_t)((des_g) >> 16);
        *des_scan++ = (uint8_t)((des_r) >> 16);
        *des_scan++ = (uint8_t)((des_a) >> 16);
      } break;
      default:
        return;
    }
  }
}

void CCodec_ProgressiveDecoder::PngFillScanlineBufCompleted(int pass,
                                                            int line) {
  CFX_RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
  ASSERT(pDIBitmap);
  int src_top = m_clipBox.top;
  int src_bottom = m_clipBox.bottom;
  int des_top = m_startY;
  int src_hei = m_clipBox.Height();
  int des_hei = m_sizeY;
  if (line >= src_top && line < src_bottom) {
    double scale_y = (double)des_hei / (double)src_hei;
    int src_row = line - src_top;
    int des_row = (int)(src_row * scale_y) + des_top;
    if (des_row >= des_top + des_hei) {
      return;
    }
    PngOneOneMapResampleHorz(pDIBitmap, des_row, m_pDecodeBuf, m_SrcFormat);
    if (m_SrcPassNumber == 1 && scale_y > 1.0) {
      ResampleVert(pDIBitmap, scale_y, des_row);
      return;
    }
    if (pass == 6 && scale_y > 1.0) {
      ResampleVert(pDIBitmap, scale_y, des_row);
    }
  }
}

bool CCodec_ProgressiveDecoder::GifReadMoreData(CCodec_GifModule* pGifModule,
                                                FXCODEC_STATUS& err_status) {
  uint32_t dwSize = (uint32_t)m_pFile->GetSize();
  if (dwSize <= m_offSet) {
    return false;
  }
  dwSize = dwSize - m_offSet;
  uint32_t dwAvail = pGifModule->GetAvailInput(m_pGifContext, nullptr);
  if (dwAvail == m_SrcSize) {
    if (dwSize > FXCODEC_BLOCK_SIZE) {
      dwSize = FXCODEC_BLOCK_SIZE;
    }
    m_SrcSize = (dwSize + dwAvail + FXCODEC_BLOCK_SIZE - 1) /
                FXCODEC_BLOCK_SIZE * FXCODEC_BLOCK_SIZE;
    m_pSrcBuf = FX_Realloc(uint8_t, m_pSrcBuf, m_SrcSize);
    if (!m_pSrcBuf) {
      err_status = FXCODEC_STATUS_ERR_MEMORY;
      return false;
    }
  } else {
    uint32_t dwConsume = m_SrcSize - dwAvail;
    if (dwAvail) {
      memmove(m_pSrcBuf, m_pSrcBuf + dwConsume, dwAvail);
    }
    if (dwSize > dwConsume) {
      dwSize = dwConsume;
    }
  }
  if (!m_pFile->ReadBlock(m_pSrcBuf + dwAvail, m_offSet, dwSize)) {
    err_status = FXCODEC_STATUS_ERR_READ;
    return false;
  }
  m_offSet += dwSize;
  pGifModule->Input(m_pGifContext, m_pSrcBuf, dwSize + dwAvail);
  return true;
}

void CCodec_ProgressiveDecoder::GifRecordCurrentPosition(uint32_t& cur_pos) {
  uint32_t remain_size =
      m_pCodecMgr->GetGifModule()->GetAvailInput(m_pGifContext);
  cur_pos = m_offSet - remain_size;
}

bool CCodec_ProgressiveDecoder::GifInputRecordPositionBuf(
    uint32_t rcd_pos,
    const FX_RECT& img_rc,
    int32_t pal_num,
    void* pal_ptr,
    int32_t delay_time,
    bool user_input,
    int32_t trans_index,
    int32_t disposal_method,
    bool interlace) {
  m_offSet = rcd_pos;
  FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
  if (!GifReadMoreData(m_pCodecMgr->GetGifModule(), error_status)) {
    return false;
  }
  uint8_t* pPalette = nullptr;
  if (pal_num != 0 && pal_ptr) {
    pPalette = (uint8_t*)pal_ptr;
  } else {
    pal_num = m_GifPltNumber;
    pPalette = m_pGifPalette;
  }
  if (!m_pSrcPalette)
    m_pSrcPalette = FX_Alloc(FX_ARGB, pal_num);
  else if (pal_num > m_SrcPaletteNumber)
    m_pSrcPalette = FX_Realloc(FX_ARGB, m_pSrcPalette, pal_num);
  if (!m_pSrcPalette)
    return false;

  m_SrcPaletteNumber = pal_num;
  for (int i = 0; i < pal_num; i++) {
    uint32_t j = i * 3;
    m_pSrcPalette[i] =
        ArgbEncode(0xff, pPalette[j], pPalette[j + 1], pPalette[j + 2]);
  }
  m_GifTransIndex = trans_index;
  m_GifFrameRect = img_rc;
  m_SrcPassNumber = interlace ? 4 : 1;
  int32_t pal_index = m_GifBgIndex;
  CFX_RetainPtr<CFX_DIBitmap> pDevice = m_pDeviceBitmap;
  if (trans_index >= pal_num)
    trans_index = -1;
  if (trans_index != -1) {
    m_pSrcPalette[trans_index] &= 0x00ffffff;
    if (pDevice->HasAlpha())
      pal_index = trans_index;
  }
  if (pal_index >= pal_num)
    return false;

  int startX = m_startX;
  int startY = m_startY;
  int sizeX = m_sizeX;
  int sizeY = m_sizeY;
  int Bpp = pDevice->GetBPP() / 8;
  FX_ARGB argb = m_pSrcPalette[pal_index];
  for (int row = 0; row < sizeY; row++) {
    uint8_t* pScanline =
        (uint8_t*)pDevice->GetScanline(row + startY) + startX * Bpp;
    switch (m_TransMethod) {
      case 3: {
        uint8_t gray =
            FXRGB2GRAY(FXARGB_R(argb), FXARGB_G(argb), FXARGB_B(argb));
        memset(pScanline, gray, sizeX);
        break;
      }
      case 8: {
        for (int col = 0; col < sizeX; col++) {
          *pScanline++ = FXARGB_B(argb);
          *pScanline++ = FXARGB_G(argb);
          *pScanline++ = FXARGB_R(argb);
          pScanline += Bpp - 3;
        }
        break;
      }
      case 12: {
        for (int col = 0; col < sizeX; col++) {
          FXARGB_SETDIB(pScanline, argb);
          pScanline += 4;
        }
        break;
      }
    }
  }
  return true;
}

void CCodec_ProgressiveDecoder::GifReadScanline(int32_t row_num,
                                                uint8_t* row_buf) {
  CFX_RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
  ASSERT(pDIBitmap);
  int32_t img_width = m_GifFrameRect.Width();
  if (!pDIBitmap->HasAlpha()) {
    uint8_t* byte_ptr = row_buf;
    for (int i = 0; i < img_width; i++) {
      if (*byte_ptr == m_GifTransIndex) {
        *byte_ptr = m_GifBgIndex;
      }
      byte_ptr++;
    }
  }
  int32_t pal_index = m_GifBgIndex;
  if (m_GifTransIndex != -1 && m_pDeviceBitmap->HasAlpha()) {
    pal_index = m_GifTransIndex;
  }
  memset(m_pDecodeBuf, pal_index, m_SrcWidth);
  bool bLastPass = (row_num % 2) == 1;
  int32_t line = row_num + m_GifFrameRect.top;
  int32_t left = m_GifFrameRect.left;
  memcpy(m_pDecodeBuf + left, row_buf, img_width);
  int src_top = m_clipBox.top;
  int src_bottom = m_clipBox.bottom;
  int des_top = m_startY;
  int src_hei = m_clipBox.Height();
  int des_hei = m_sizeY;
  if (line < src_top || line >= src_bottom)
    return;

  double scale_y = (double)des_hei / (double)src_hei;
  int src_row = line - src_top;
  int des_row = (int)(src_row * scale_y) + des_top;
  if (des_row >= des_top + des_hei)
    return;

  ReSampleScanline(pDIBitmap, des_row, m_pDecodeBuf, m_SrcFormat);
  if (scale_y > 1.0 && (!m_bInterpol || m_SrcPassNumber == 1)) {
    ResampleVert(pDIBitmap, scale_y, des_row);
    return;
  }
  if (scale_y <= 1.0)
    return;

  int des_bottom = des_top + m_sizeY;
  int des_Bpp = pDIBitmap->GetBPP() >> 3;
  uint32_t des_ScanOffet = m_startX * des_Bpp;
  if (des_row + (int)scale_y >= des_bottom - 1) {
    uint8_t* scan_src =
        (uint8_t*)pDIBitmap->GetScanline(des_row) + des_ScanOffet;
    int cur_row = des_row;
    while (++cur_row < des_bottom) {
      uint8_t* scan_des =
          (uint8_t*)pDIBitmap->GetScanline(cur_row) + des_ScanOffet;
      uint32_t size = m_sizeX * des_Bpp;
      memmove(scan_des, scan_src, size);
    }
  }
  if (bLastPass)
    GifDoubleLineResampleVert(pDIBitmap, scale_y, des_row);
}

void CCodec_ProgressiveDecoder::GifDoubleLineResampleVert(
    const CFX_RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
    double scale_y,
    int des_row) {
  int des_Bpp = pDeviceBitmap->GetBPP() >> 3;
  uint32_t des_ScanOffet = m_startX * des_Bpp;
  int des_top = m_startY;
  pdfium::base::CheckedNumeric<double> scale_y2 = scale_y;
  scale_y2 *= 2;
  pdfium::base::CheckedNumeric<int> check_des_row_1 = des_row;
  check_des_row_1 -= scale_y2.ValueOrDie();
  int des_row_1 = check_des_row_1.ValueOrDie();
  des_row_1 = std::max(des_row_1, des_top);
  for (; des_row_1 < des_row; des_row_1++) {
    uint8_t* scan_des =
        (uint8_t*)pDeviceBitmap->GetScanline(des_row_1) + des_ScanOffet;
    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(des_row_1 - des_top);
    const uint8_t* scan_src1 =
        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + des_top) +
        des_ScanOffet;
    const uint8_t* scan_src2 =
        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + des_top) + des_ScanOffet;
    for (int des_col = 0; des_col < m_sizeX; des_col++) {
      switch (pDeviceBitmap->GetFormat()) {
        case FXDIB_Invalid:
        case FXDIB_1bppMask:
        case FXDIB_1bppRgb:
          return;
        case FXDIB_8bppMask:
        case FXDIB_8bppRgb: {
          if (pDeviceBitmap->GetPalette()) {
            return;
          }
          int des_g = 0;
          des_g += pWeight->m_Weights[0] * (*scan_src1++);
          des_g += pWeight->m_Weights[1] * (*scan_src2++);
          *scan_des++ = (uint8_t)(des_g >> 16);
        } break;
        case FXDIB_Rgb:
        case FXDIB_Rgb32: {
          uint32_t des_b = 0, des_g = 0, des_r = 0;
          des_b += pWeight->m_Weights[0] * (*scan_src1++);
          des_g += pWeight->m_Weights[0] * (*scan_src1++);
          des_r += pWeight->m_Weights[0] * (*scan_src1++);
          scan_src1 += des_Bpp - 3;
          des_b += pWeight->m_Weights[1] * (*scan_src2++);
          des_g += pWeight->m_Weights[1] * (*scan_src2++);
          des_r += pWeight->m_Weights[1] * (*scan_src2++);
          scan_src2 += des_Bpp - 3;
          *scan_des++ = (uint8_t)((des_b) >> 16);
          *scan_des++ = (uint8_t)((des_g) >> 16);
          *scan_des++ = (uint8_t)((des_r) >> 16);
          scan_des += des_Bpp - 3;
        } break;
        case FXDIB_Argb: {
          uint32_t des_a = 0, des_b = 0, des_g = 0, des_r = 0;
          des_b += pWeight->m_Weights[0] * (*scan_src1++);
          des_g += pWeight->m_Weights[0] * (*scan_src1++);
          des_r += pWeight->m_Weights[0] * (*scan_src1++);
          des_a += pWeight->m_Weights[0] * (*scan_src1++);
          des_b += pWeight->m_Weights[1] * (*scan_src2++);
          des_g += pWeight->m_Weights[1] * (*scan_src2++);
          des_r += pWeight->m_Weights[1] * (*scan_src2++);
          des_a += pWeight->m_Weights[1] * (*scan_src2++);
          *scan_des++ = (uint8_t)((des_b) >> 16);
          *scan_des++ = (uint8_t)((des_g) >> 16);
          *scan_des++ = (uint8_t)((des_r) >> 16);
          *scan_des++ = (uint8_t)((des_a) >> 16);
        } break;
        default:
          return;
      }
    }
  }
  int des_bottom = des_top + m_sizeY - 1;
  if (des_row + (int)(2 * scale_y) >= des_bottom &&
      des_row + (int)scale_y < des_bottom) {
    GifDoubleLineResampleVert(pDeviceBitmap, scale_y, des_row + (int)scale_y);
  }
}

bool CCodec_ProgressiveDecoder::BmpReadMoreData(CCodec_BmpModule* pBmpModule,
                                                FXCODEC_STATUS& err_status) {
  uint32_t dwSize = (uint32_t)m_pFile->GetSize();
  if (dwSize <= m_offSet)
    return false;

  dwSize = dwSize - m_offSet;
  uint32_t dwAvail = pBmpModule->GetAvailInput(m_pBmpContext, nullptr);
  if (dwAvail == m_SrcSize) {
    if (dwSize > FXCODEC_BLOCK_SIZE) {
      dwSize = FXCODEC_BLOCK_SIZE;
    }
    m_SrcSize = (dwSize + dwAvail + FXCODEC_BLOCK_SIZE - 1) /
                FXCODEC_BLOCK_SIZE * FXCODEC_BLOCK_SIZE;
    m_pSrcBuf = FX_Realloc(uint8_t, m_pSrcBuf, m_SrcSize);
    if (!m_pSrcBuf) {
      err_status = FXCODEC_STATUS_ERR_MEMORY;
      return false;
    }
  } else {
    uint32_t dwConsume = m_SrcSize - dwAvail;
    if (dwAvail) {
      memmove(m_pSrcBuf, m_pSrcBuf + dwConsume, dwAvail);
    }
    if (dwSize > dwConsume) {
      dwSize = dwConsume;
    }
  }
  if (!m_pFile->ReadBlock(m_pSrcBuf + dwAvail, m_offSet, dwSize)) {
    err_status = FXCODEC_STATUS_ERR_READ;
    return false;
  }
  m_offSet += dwSize;
  pBmpModule->Input(m_pBmpContext, m_pSrcBuf, dwSize + dwAvail);
  return true;
}

bool CCodec_ProgressiveDecoder::BmpInputImagePositionBuf(uint32_t rcd_pos) {
  m_offSet = rcd_pos;
  FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
  return BmpReadMoreData(m_pCodecMgr->GetBmpModule(), error_status);
}

void CCodec_ProgressiveDecoder::BmpReadScanline(int32_t row_num,
                                                uint8_t* row_buf) {
  CFX_RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap;
  ASSERT(pDIBitmap);
  memcpy(m_pDecodeBuf, row_buf, m_ScanlineSize);
  int src_top = m_clipBox.top;
  int src_bottom = m_clipBox.bottom;
  int des_top = m_startY;
  int src_hei = m_clipBox.Height();
  int des_hei = m_sizeY;
  if (row_num < src_top || row_num >= src_bottom)
    return;

  double scale_y = (double)des_hei / (double)src_hei;
  int src_row = row_num - src_top;
  int des_row = (int)(src_row * scale_y) + des_top;
  if (des_row >= des_top + des_hei)
    return;

  ReSampleScanline(pDIBitmap, des_row, m_pDecodeBuf, m_SrcFormat);
  if (scale_y <= 1.0)
    return;

  if (m_BmpIsTopBottom || !m_bInterpol) {
    ResampleVert(pDIBitmap, scale_y, des_row);
    return;
  }
  ResampleVertBT(pDIBitmap, scale_y, des_row);
}

void CCodec_ProgressiveDecoder::ResampleVertBT(
    const CFX_RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
    double scale_y,
    int des_row) {
  int des_Bpp = pDeviceBitmap->GetBPP() >> 3;
  uint32_t des_ScanOffet = m_startX * des_Bpp;
  int des_top = m_startY;
  int des_bottom = m_startY + m_sizeY;
  pdfium::base::CheckedNumeric<int> check_des_row_1 = des_row;
  check_des_row_1 += pdfium::base::checked_cast<int>(scale_y);
  int des_row_1 = check_des_row_1.ValueOrDie();
  if (des_row_1 >= des_bottom - 1) {
    uint8_t* scan_src =
        (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
    while (++des_row < des_bottom) {
      uint8_t* scan_des =
          (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
      uint32_t size = m_sizeX * des_Bpp;
      memmove(scan_des, scan_src, size);
    }
    return;
  }
  for (; des_row_1 > des_row; des_row_1--) {
    uint8_t* scan_des =
        (uint8_t*)pDeviceBitmap->GetScanline(des_row_1) + des_ScanOffet;
    PixelWeight* pWeight = m_WeightVert.GetPixelWeight(des_row_1 - des_top);
    const uint8_t* scan_src1 =
        pDeviceBitmap->GetScanline(pWeight->m_SrcStart + des_top) +
        des_ScanOffet;
    const uint8_t* scan_src2 =
        pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + des_top) + des_ScanOffet;
    for (int des_col = 0; des_col < m_sizeX; des_col++) {
      switch (pDeviceBitmap->GetFormat()) {
        case FXDIB_Invalid:
        case FXDIB_1bppMask:
        case FXDIB_1bppRgb:
          return;
        case FXDIB_8bppMask:
        case FXDIB_8bppRgb: {
          if (pDeviceBitmap->GetPalette()) {
            return;
          }
          int des_g = 0;
          des_g += pWeight->m_Weights[0] * (*scan_src1++);
          des_g += pWeight->m_Weights[1] * (*scan_src2++);
          *scan_des++ = (uint8_t)(des_g >> 16);
        } break;
        case FXDIB_Rgb:
        case FXDIB_Rgb32: {
          uint32_t des_b = 0, des_g = 0, des_r = 0;
          des_b += pWeight->m_Weights[0] * (*scan_src1++);
          des_g += pWeight->m_Weights[0] * (*scan_src1++);
          des_r += pWeight->m_Weights[0] * (*scan_src1++);
          scan_src1 += des_Bpp - 3;
          des_b += pWeight->m_Weights[1] * (*scan_src2++);
          des_g += pWeight->m_Weights[1] * (*scan_src2++);
          des_r += pWeight->m_Weights[1] * (*scan_src2++);
          scan_src2 += des_Bpp - 3;
          *scan_des++ = (uint8_t)((des_b) >> 16);
          *scan_des++ = (uint8_t)((des_g) >> 16);
          *scan_des++ = (uint8_t)((des_r) >> 16);
          scan_des += des_Bpp - 3;
        } break;
        case FXDIB_Argb: {
          uint32_t des_a = 0, des_b = 0, des_g = 0, des_r = 0;
          des_b += pWeight->m_Weights[0] * (*scan_src1++);
          des_g += pWeight->m_Weights[0] * (*scan_src1++);
          des_r += pWeight->m_Weights[0] * (*scan_src1++);
          des_a += pWeight->m_Weights[0] * (*scan_src1++);
          des_b += pWeight->m_Weights[1] * (*scan_src2++);
          des_g += pWeight->m_Weights[1] * (*scan_src2++);
          des_r += pWeight->m_Weights[1] * (*scan_src2++);
          des_a += pWeight->m_Weights[1] * (*scan_src2++);
          *scan_des++ = (uint8_t)((des_b) >> 16);
          *scan_des++ = (uint8_t)((des_g) >> 16);
          *scan_des++ = (uint8_t)((des_r) >> 16);
          *scan_des++ = (uint8_t)((des_a) >> 16);
        } break;
        default:
          return;
      }
    }
  }
}

bool CCodec_ProgressiveDecoder::DetectImageType(FXCODEC_IMAGE_TYPE imageType,
                                                CFX_DIBAttribute* pAttribute) {
  m_offSet = 0;
  uint32_t size = (uint32_t)m_pFile->GetSize();
  if (size > FXCODEC_BLOCK_SIZE) {
    size = FXCODEC_BLOCK_SIZE;
  }
  FX_Free(m_pSrcBuf);
  m_pSrcBuf = FX_Alloc(uint8_t, size);
  memset(m_pSrcBuf, 0, size);
  m_SrcSize = size;
  switch (imageType) {
    case FXCODEC_IMAGE_BMP: {
      CCodec_BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
      if (!pBmpModule) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return false;
      }
      pBmpModule->SetDelegate(this);
      m_pBmpContext = pBmpModule->Start();
      if (!m_pBmpContext) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return false;
      }
      bool bResult = m_pFile->ReadBlock(m_pSrcBuf, 0, size);
      if (!bResult) {
        m_status = FXCODEC_STATUS_ERR_READ;
        return false;
      }
      m_offSet += size;
      pBmpModule->Input(m_pBmpContext, m_pSrcBuf, size);
      uint32_t* pPalette = nullptr;
      int32_t readResult = pBmpModule->ReadHeader(
          m_pBmpContext, &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom,
          &m_SrcComponents, &m_SrcPaletteNumber, &pPalette, pAttribute);
      while (readResult == 2) {
        FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
        if (!BmpReadMoreData(pBmpModule, error_status)) {
          m_status = error_status;
          return false;
        }
        readResult = pBmpModule->ReadHeader(
            m_pBmpContext, &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom,
            &m_SrcComponents, &m_SrcPaletteNumber, &pPalette, pAttribute);
      }
      if (readResult == 1) {
        m_SrcBPC = 8;
        m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
        FX_Free(m_pSrcPalette);
        if (m_SrcPaletteNumber) {
          m_pSrcPalette = FX_Alloc(FX_ARGB, m_SrcPaletteNumber);
          memcpy(m_pSrcPalette, pPalette,
                 m_SrcPaletteNumber * sizeof(uint32_t));
        } else {
          m_pSrcPalette = nullptr;
        }
        return true;
      }
      if (m_pBmpContext) {
        pBmpModule->Finish(m_pBmpContext);
        m_pBmpContext = nullptr;
      }
      m_status = FXCODEC_STATUS_ERR_FORMAT;
      return false;
    }
    case FXCODEC_IMAGE_JPG: {
      CCodec_JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
      if (!pJpegModule) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return false;
      }
      m_pJpegContext = pJpegModule->Start();
      if (!m_pJpegContext) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return false;
      }
      bool bResult = m_pFile->ReadBlock(m_pSrcBuf, 0, size);
      if (!bResult) {
        m_status = FXCODEC_STATUS_ERR_READ;
        return false;
      }
      m_offSet += size;
      pJpegModule->Input(m_pJpegContext, m_pSrcBuf, size);
      int32_t readResult =
          pJpegModule->ReadHeader(m_pJpegContext, &m_SrcWidth, &m_SrcHeight,
                                  &m_SrcComponents, pAttribute);
      while (readResult == 2) {
        FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
        if (!JpegReadMoreData(pJpegModule, error_status)) {
          m_status = error_status;
          return false;
        }
        readResult =
            pJpegModule->ReadHeader(m_pJpegContext, &m_SrcWidth, &m_SrcHeight,
                                    &m_SrcComponents, pAttribute);
      }
      if (!readResult) {
        m_SrcBPC = 8;
        m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
        return true;
      }
      if (m_pJpegContext) {
        pJpegModule->Finish(m_pJpegContext);
        m_pJpegContext = nullptr;
      }
      m_status = FXCODEC_STATUS_ERR_FORMAT;
      return false;
    }
    case FXCODEC_IMAGE_PNG: {
      CCodec_PngModule* pPngModule = m_pCodecMgr->GetPngModule();
      if (!pPngModule) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return false;
      }
      pPngModule->SetDelegate(this);
      m_pPngContext = pPngModule->Start();
      if (!m_pPngContext) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return false;
      }
      bool bResult = m_pFile->ReadBlock(m_pSrcBuf, 0, size);
      if (!bResult) {
        m_status = FXCODEC_STATUS_ERR_READ;
        return false;
      }
      m_offSet += size;
      bResult = pPngModule->Input(m_pPngContext, m_pSrcBuf, size, pAttribute);
      while (bResult) {
        uint32_t remain_size = (uint32_t)m_pFile->GetSize() - m_offSet;
        uint32_t input_size =
            remain_size > FXCODEC_BLOCK_SIZE ? FXCODEC_BLOCK_SIZE : remain_size;
        if (input_size == 0) {
          if (m_pPngContext) {
            pPngModule->Finish(m_pPngContext);
          }
          m_pPngContext = nullptr;
          m_status = FXCODEC_STATUS_ERR_FORMAT;
          return false;
        }
        if (m_pSrcBuf && input_size > m_SrcSize) {
          FX_Free(m_pSrcBuf);
          m_pSrcBuf = FX_Alloc(uint8_t, input_size);
          memset(m_pSrcBuf, 0, input_size);
          m_SrcSize = input_size;
        }
        bResult = m_pFile->ReadBlock(m_pSrcBuf, m_offSet, input_size);
        if (!bResult) {
          m_status = FXCODEC_STATUS_ERR_READ;
          return false;
        }
        m_offSet += input_size;
        bResult =
            pPngModule->Input(m_pPngContext, m_pSrcBuf, input_size, pAttribute);
      }
      ASSERT(!bResult);
      if (m_pPngContext) {
        pPngModule->Finish(m_pPngContext);
        m_pPngContext = nullptr;
      }
      if (m_SrcPassNumber == 0) {
        m_status = FXCODEC_STATUS_ERR_FORMAT;
        return false;
      }
      return true;
    }
    case FXCODEC_IMAGE_GIF: {
      CCodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule();
      if (!pGifModule) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return false;
      }
      pGifModule->SetDelegate(this);
      m_pGifContext = pGifModule->Start();
      if (!m_pGifContext) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return false;
      }
      bool bResult = m_pFile->ReadBlock(m_pSrcBuf, 0, size);
      if (!bResult) {
        m_status = FXCODEC_STATUS_ERR_READ;
        return false;
      }
      m_offSet += size;
      pGifModule->Input(m_pGifContext, m_pSrcBuf, size);
      m_SrcComponents = 1;
      GifDecodeStatus readResult = pGifModule->ReadHeader(
          m_pGifContext, &m_SrcWidth, &m_SrcHeight, &m_GifPltNumber,
          (void**)&m_pGifPalette, &m_GifBgIndex, nullptr);
      while (readResult == GifDecodeStatus::Unfinished) {
        FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_FORMAT;
        if (!GifReadMoreData(pGifModule, error_status)) {
          m_status = error_status;
          return false;
        }
        readResult = pGifModule->ReadHeader(
            m_pGifContext, &m_SrcWidth, &m_SrcHeight, &m_GifPltNumber,
            (void**)&m_pGifPalette, &m_GifBgIndex, nullptr);
      }
      if (readResult == GifDecodeStatus::Success) {
        m_SrcBPC = 8;
        m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
        return true;
      }
      if (m_pGifContext) {
        pGifModule->Finish(m_pGifContext);
        m_pGifContext = nullptr;
      }
      m_status = FXCODEC_STATUS_ERR_FORMAT;
      return false;
    }
    case FXCODEC_IMAGE_TIF: {
      CCodec_TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule();
      if (!pTiffModule) {
        m_status = FXCODEC_STATUS_ERR_FORMAT;
        return false;
      }
      m_pTiffContext = pTiffModule->CreateDecoder(m_pFile);
      if (!m_pTiffContext) {
        m_status = FXCODEC_STATUS_ERR_FORMAT;
        return false;
      }
      int32_t dummy_bpc;
      bool ret = pTiffModule->LoadFrameInfo(m_pTiffContext, 0, &m_SrcWidth,
                                            &m_SrcHeight, &m_SrcComponents,
                                            &dummy_bpc, pAttribute);
      m_SrcComponents = 4;
      m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight);
      if (!ret) {
        pTiffModule->DestroyDecoder(m_pTiffContext);
        m_pTiffContext = nullptr;
        m_status = FXCODEC_STATUS_ERR_FORMAT;
        return false;
      }
      return true;
    }
    default:
      m_status = FXCODEC_STATUS_ERR_FORMAT;
      return false;
  }
}

FXCODEC_STATUS CCodec_ProgressiveDecoder::LoadImageInfo(
    const CFX_RetainPtr<IFX_SeekableReadStream>& pFile,
    FXCODEC_IMAGE_TYPE imageType,
    CFX_DIBAttribute* pAttribute,
    bool bSkipImageTypeCheck) {
  switch (m_status) {
    case FXCODEC_STATUS_FRAME_READY:
    case FXCODEC_STATUS_FRAME_TOBECONTINUE:
    case FXCODEC_STATUS_DECODE_READY:
    case FXCODEC_STATUS_DECODE_TOBECONTINUE:
      return FXCODEC_STATUS_ERROR;
    default:
      break;
  }
  if (!pFile) {
    m_status = FXCODEC_STATUS_ERR_PARAMS;
    m_pFile = nullptr;
    return m_status;
  }
  m_pFile = pFile;
  m_offSet = 0;
  m_SrcWidth = m_SrcHeight = 0;
  m_SrcComponents = m_SrcBPC = 0;
  m_clipBox = FX_RECT(0, 0, 0, 0);
  m_startX = m_startY = 0;
  m_sizeX = m_sizeY = 0;
  m_SrcPassNumber = 0;
  if (imageType != FXCODEC_IMAGE_UNKNOWN &&
      DetectImageType(imageType, pAttribute)) {
    m_imagType = imageType;
    m_status = FXCODEC_STATUS_FRAME_READY;
    return m_status;
  }
  // If we got here then the image data does not match the requested decoder.
  // If we're skipping the type check then bail out at this point and return
  // the failed status.
  if (bSkipImageTypeCheck)
    return m_status;

  for (int type = FXCODEC_IMAGE_BMP; type < FXCODEC_IMAGE_MAX; type++) {
    if (DetectImageType((FXCODEC_IMAGE_TYPE)type, pAttribute)) {
      m_imagType = (FXCODEC_IMAGE_TYPE)type;
      m_status = FXCODEC_STATUS_FRAME_READY;
      return m_status;
    }
  }
  m_status = FXCODEC_STATUS_ERR_FORMAT;
  m_pFile = nullptr;
  return m_status;
}

void CCodec_ProgressiveDecoder::SetClipBox(FX_RECT* clip) {
  if (m_status != FXCODEC_STATUS_FRAME_READY)
    return;

  if (clip->IsEmpty()) {
    m_clipBox = FX_RECT(0, 0, 0, 0);
    return;
  }
  clip->left = std::max(clip->left, 0);
  clip->right = std::min(clip->right, m_SrcWidth);
  clip->top = std::max(clip->top, 0);
  clip->bottom = std::min(clip->bottom, m_SrcHeight);
  if (clip->IsEmpty()) {
    m_clipBox = FX_RECT(0, 0, 0, 0);
    return;
  }
  m_clipBox = *clip;
}

void CCodec_ProgressiveDecoder::GetDownScale(int& down_scale) {
  down_scale = 1;
  int ratio_w = m_clipBox.Width() / m_sizeX;
  int ratio_h = m_clipBox.Height() / m_sizeY;
  int ratio = (ratio_w > ratio_h) ? ratio_h : ratio_w;
  if (ratio >= 8) {
    down_scale = 8;
  } else if (ratio >= 4) {
    down_scale = 4;
  } else if (ratio >= 2) {
    down_scale = 2;
  }
  m_clipBox.left /= down_scale;
  m_clipBox.right /= down_scale;
  m_clipBox.top /= down_scale;
  m_clipBox.bottom /= down_scale;
  if (m_clipBox.right == m_clipBox.left) {
    m_clipBox.right = m_clipBox.left + 1;
  }
  if (m_clipBox.bottom == m_clipBox.top) {
    m_clipBox.bottom = m_clipBox.top + 1;
  }
}

void CCodec_ProgressiveDecoder::GetTransMethod(FXDIB_Format des_format,
                                               FXCodec_Format src_format) {
  switch (des_format) {
    case FXDIB_1bppMask:
    case FXDIB_1bppRgb: {
      switch (src_format) {
        case FXCodec_1bppGray:
          m_TransMethod = 0;
          break;
        default:
          m_TransMethod = -1;
      }
    } break;
    case FXDIB_8bppMask:
    case FXDIB_8bppRgb: {
      switch (src_format) {
        case FXCodec_1bppGray:
          m_TransMethod = 1;
          break;
        case FXCodec_8bppGray:
          m_TransMethod = 2;
          break;
        case FXCodec_1bppRgb:
        case FXCodec_8bppRgb:
          m_TransMethod = 3;
          break;
        case FXCodec_Rgb:
        case FXCodec_Rgb32:
        case FXCodec_Argb:
          m_TransMethod = 4;
          break;
        case FXCodec_Cmyk:
          m_TransMethod = 5;
          break;
        default:
          m_TransMethod = -1;
      }
    } break;
    case FXDIB_Rgb: {
      switch (src_format) {
        case FXCodec_1bppGray:
          m_TransMethod = 6;
          break;
        case FXCodec_8bppGray:
          m_TransMethod = 7;
          break;
        case FXCodec_1bppRgb:
        case FXCodec_8bppRgb:
          m_TransMethod = 8;
          break;
        case FXCodec_Rgb:
        case FXCodec_Rgb32:
        case FXCodec_Argb:
          m_TransMethod = 9;
          break;
        case FXCodec_Cmyk:
          m_TransMethod = 10;
          break;
        default:
          m_TransMethod = -1;
      }
    } break;
    case FXDIB_Rgb32:
    case FXDIB_Argb: {
      switch (src_format) {
        case FXCodec_1bppGray:
          m_TransMethod = 6;
          break;
        case FXCodec_8bppGray:
          m_TransMethod = 7;
          break;
        case FXCodec_1bppRgb:
        case FXCodec_8bppRgb:
          if (des_format == FXDIB_Argb) {
            m_TransMethod = 12;
          } else {
            m_TransMethod = 8;
          }
          break;
        case FXCodec_Rgb:
        case FXCodec_Rgb32:
          m_TransMethod = 9;
          break;
        case FXCodec_Cmyk:
          m_TransMethod = 10;
          break;
        case FXCodec_Argb:
          m_TransMethod = 11;
          break;
        default:
          m_TransMethod = -1;
      }
    } break;
    default:
      m_TransMethod = -1;
  }
}

void CCodec_ProgressiveDecoder::ReSampleScanline(
    const CFX_RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
    int des_line,
    uint8_t* src_scan,
    FXCodec_Format src_format) {
  int src_left = m_clipBox.left;
  int des_left = m_startX;
  uint8_t* des_scan =
      pDeviceBitmap->GetBuffer() + des_line * pDeviceBitmap->GetPitch();
  int src_bpp = src_format & 0xff;
  int des_bpp = pDeviceBitmap->GetBPP();
  int src_Bpp = src_bpp >> 3;
  int des_Bpp = des_bpp >> 3;
  src_scan += src_left * src_Bpp;
  des_scan += des_left * des_Bpp;
  for (int des_col = 0; des_col < m_sizeX; des_col++) {
    PixelWeight* pPixelWeights = m_WeightHorz.GetPixelWeight(des_col);
    switch (m_TransMethod) {
      case -1:
        return;
      case 0:
        return;
      case 1:
        return;
      case 2: {
        uint32_t des_g = 0;
        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
             j++) {
          int pixel_weight =
              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
          des_g += pixel_weight * src_scan[j];
        }
        *des_scan++ = (uint8_t)(des_g >> 16);
      } break;
      case 3: {
        int des_r = 0, des_g = 0, des_b = 0;
        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
             j++) {
          int pixel_weight =
              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
          unsigned long argb = m_pSrcPalette[src_scan[j]];
          des_r += pixel_weight * (uint8_t)(argb >> 16);
          des_g += pixel_weight * (uint8_t)(argb >> 8);
          des_b += pixel_weight * (uint8_t)argb;
        }
        *des_scan++ =
            (uint8_t)FXRGB2GRAY((des_r >> 16), (des_g >> 16), (des_b >> 16));
      } break;
      case 4: {
        uint32_t des_b = 0, des_g = 0, des_r = 0;
        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
             j++) {
          int pixel_weight =
              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
          const uint8_t* src_pixel = src_scan + j * src_Bpp;
          des_b += pixel_weight * (*src_pixel++);
          des_g += pixel_weight * (*src_pixel++);
          des_r += pixel_weight * (*src_pixel);
        }
        *des_scan++ =
            (uint8_t)FXRGB2GRAY((des_r >> 16), (des_g >> 16), (des_b >> 16));
      } break;
      case 5: {
        uint32_t des_b = 0, des_g = 0, des_r = 0;
        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
             j++) {
          int pixel_weight =
              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
          const uint8_t* src_pixel = src_scan + j * src_Bpp;
          uint8_t src_b = 0, src_g = 0, src_r = 0;
          AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1],
                             255 - src_pixel[2], 255 - src_pixel[3], src_r,
                             src_g, src_b);
          des_b += pixel_weight * src_b;
          des_g += pixel_weight * src_g;
          des_r += pixel_weight * src_r;
        }
        *des_scan++ =
            (uint8_t)FXRGB2GRAY((des_r >> 16), (des_g >> 16), (des_b >> 16));
      } break;
      case 6:
        return;
      case 7: {
        uint32_t des_g = 0;
        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
             j++) {
          int pixel_weight =
              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
          des_g += pixel_weight * src_scan[j];
        }
        memset(des_scan, (uint8_t)(des_g >> 16), 3);
        des_scan += des_Bpp;
      } break;
      case 8: {
        int des_r = 0, des_g = 0, des_b = 0;
        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
             j++) {
          int pixel_weight =
              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
          unsigned long argb = m_pSrcPalette[src_scan[j]];
          des_r += pixel_weight * (uint8_t)(argb >> 16);
          des_g += pixel_weight * (uint8_t)(argb >> 8);
          des_b += pixel_weight * (uint8_t)argb;
        }
        *des_scan++ = (uint8_t)((des_b) >> 16);
        *des_scan++ = (uint8_t)((des_g) >> 16);
        *des_scan++ = (uint8_t)((des_r) >> 16);
        des_scan += des_Bpp - 3;
      } break;
      case 12: {
        if (m_pBmpContext) {
          int des_r = 0, des_g = 0, des_b = 0;
          for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
               j++) {
            int pixel_weight =
                pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
            unsigned long argb = m_pSrcPalette[src_scan[j]];
            des_r += pixel_weight * (uint8_t)(argb >> 16);
            des_g += pixel_weight * (uint8_t)(argb >> 8);
            des_b += pixel_weight * (uint8_t)argb;
          }
          *des_scan++ = (uint8_t)((des_b) >> 16);
          *des_scan++ = (uint8_t)((des_g) >> 16);
          *des_scan++ = (uint8_t)((des_r) >> 16);
          *des_scan++ = 0xFF;
        } else {
          int des_a = 0, des_r = 0, des_g = 0, des_b = 0;
          for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
               j++) {
            int pixel_weight =
                pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
            unsigned long argb = m_pSrcPalette[src_scan[j]];
            des_a += pixel_weight * (uint8_t)(argb >> 24);
            des_r += pixel_weight * (uint8_t)(argb >> 16);
            des_g += pixel_weight * (uint8_t)(argb >> 8);
            des_b += pixel_weight * (uint8_t)argb;
          }
          *des_scan++ = (uint8_t)((des_b) >> 16);
          *des_scan++ = (uint8_t)((des_g) >> 16);
          *des_scan++ = (uint8_t)((des_r) >> 16);
          *des_scan++ = (uint8_t)((des_a) >> 16);
        }
      } break;
      case 9: {
        uint32_t des_b = 0, des_g = 0, des_r = 0;
        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
             j++) {
          int pixel_weight =
              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
          const uint8_t* src_pixel = src_scan + j * src_Bpp;
          des_b += pixel_weight * (*src_pixel++);
          des_g += pixel_weight * (*src_pixel++);
          des_r += pixel_weight * (*src_pixel);
        }
        *des_scan++ = (uint8_t)((des_b) >> 16);
        *des_scan++ = (uint8_t)((des_g) >> 16);
        *des_scan++ = (uint8_t)((des_r) >> 16);
        des_scan += des_Bpp - 3;
      } break;
      case 10: {
        uint32_t des_b = 0, des_g = 0, des_r = 0;
        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
             j++) {
          int pixel_weight =
              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
          const uint8_t* src_pixel = src_scan + j * src_Bpp;
          uint8_t src_b = 0, src_g = 0, src_r = 0;
          AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1],
                             255 - src_pixel[2], 255 - src_pixel[3], src_r,
                             src_g, src_b);
          des_b += pixel_weight * src_b;
          des_g += pixel_weight * src_g;
          des_r += pixel_weight * src_r;
        }
        *des_scan++ = (uint8_t)((des_b) >> 16);
        *des_scan++ = (uint8_t)((des_g) >> 16);
        *des_scan++ = (uint8_t)((des_r) >> 16);
        des_scan += des_Bpp - 3;
      } break;
      case 11: {
        uint32_t des_alpha = 0, des_r = 0, des_g = 0, des_b = 0;
        for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd;
             j++) {
          int pixel_weight =
              pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart];
          const uint8_t* src_pixel = src_scan + j * src_Bpp;
          pixel_weight = pixel_weight * src_pixel[3] / 255;
          des_b += pixel_weight * (*src_pixel++);
          des_g += pixel_weight * (*src_pixel++);
          des_r += pixel_weight * (*src_pixel);
          des_alpha += pixel_weight;
        }
        *des_scan++ = (uint8_t)((des_b) >> 16);
        *des_scan++ = (uint8_t)((des_g) >> 16);
        *des_scan++ = (uint8_t)((des_r) >> 16);
        *des_scan++ = (uint8_t)((des_alpha * 255) >> 16);
      } break;
      default:
        return;
    }
  }
}

void CCodec_ProgressiveDecoder::ResampleVert(
    const CFX_RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
    double scale_y,
    int des_row) {
  int des_Bpp = pDeviceBitmap->GetBPP() >> 3;
  uint32_t des_ScanOffet = m_startX * des_Bpp;
  if (m_bInterpol) {
    int des_top = m_startY;
    pdfium::base::CheckedNumeric<int> check_des_row_1 = des_row;
    check_des_row_1 -= pdfium::base::checked_cast<int>(scale_y);
    int des_row_1 = check_des_row_1.ValueOrDie();
    if (des_row_1 < des_top) {
      int des_bottom = des_top + m_sizeY;
      if (des_row + (int)scale_y >= des_bottom - 1) {
        uint8_t* scan_src =
            (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
        while (++des_row < des_bottom) {
          uint8_t* scan_des =
              (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
          uint32_t size = m_sizeX * des_Bpp;
          memmove(scan_des, scan_src, size);
        }
      }
      return;
    }
    for (; des_row_1 < des_row; des_row_1++) {
      uint8_t* scan_des =
          (uint8_t*)pDeviceBitmap->GetScanline(des_row_1) + des_ScanOffet;
      PixelWeight* pWeight = m_WeightVert.GetPixelWeight(des_row_1 - des_top);
      const uint8_t* scan_src1 =
          pDeviceBitmap->GetScanline(pWeight->m_SrcStart + des_top) +
          des_ScanOffet;
      const uint8_t* scan_src2 =
          pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + des_top) +
          des_ScanOffet;
      for (int des_col = 0; des_col < m_sizeX; des_col++) {
        switch (pDeviceBitmap->GetFormat()) {
          case FXDIB_Invalid:
          case FXDIB_1bppMask:
          case FXDIB_1bppRgb:
            return;
          case FXDIB_8bppMask:
          case FXDIB_8bppRgb: {
            if (pDeviceBitmap->GetPalette()) {
              return;
            }
            int des_g = 0;
            des_g += pWeight->m_Weights[0] * (*scan_src1++);
            des_g += pWeight->m_Weights[1] * (*scan_src2++);
            *scan_des++ = (uint8_t)(des_g >> 16);
          } break;
          case FXDIB_Rgb:
          case FXDIB_Rgb32: {
            uint32_t des_b = 0, des_g = 0, des_r = 0;
            des_b += pWeight->m_Weights[0] * (*scan_src1++);
            des_g += pWeight->m_Weights[0] * (*scan_src1++);
            des_r += pWeight->m_Weights[0] * (*scan_src1++);
            scan_src1 += des_Bpp - 3;
            des_b += pWeight->m_Weights[1] * (*scan_src2++);
            des_g += pWeight->m_Weights[1] * (*scan_src2++);
            des_r += pWeight->m_Weights[1] * (*scan_src2++);
            scan_src2 += des_Bpp - 3;
            *scan_des++ = (uint8_t)((des_b) >> 16);
            *scan_des++ = (uint8_t)((des_g) >> 16);
            *scan_des++ = (uint8_t)((des_r) >> 16);
            scan_des += des_Bpp - 3;
          } break;
          case FXDIB_Argb: {
            uint32_t des_a = 0, des_b = 0, des_g = 0, des_r = 0;
            des_b += pWeight->m_Weights[0] * (*scan_src1++);
            des_g += pWeight->m_Weights[0] * (*scan_src1++);
            des_r += pWeight->m_Weights[0] * (*scan_src1++);
            des_a += pWeight->m_Weights[0] * (*scan_src1++);
            des_b += pWeight->m_Weights[1] * (*scan_src2++);
            des_g += pWeight->m_Weights[1] * (*scan_src2++);
            des_r += pWeight->m_Weights[1] * (*scan_src2++);
            des_a += pWeight->m_Weights[1] * (*scan_src2++);
            *scan_des++ = (uint8_t)((des_b) >> 16);
            *scan_des++ = (uint8_t)((des_g) >> 16);
            *scan_des++ = (uint8_t)((des_r) >> 16);
            *scan_des++ = (uint8_t)((des_a) >> 16);
          } break;
          default:
            return;
        }
      }
    }
    int des_bottom = des_top + m_sizeY;
    if (des_row + (int)scale_y >= des_bottom - 1) {
      uint8_t* scan_src =
          (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
      while (++des_row < des_bottom) {
        uint8_t* scan_des =
            (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
        uint32_t size = m_sizeX * des_Bpp;
        memmove(scan_des, scan_src, size);
      }
    }
    return;
  }
  int multiple = (int)ceil((float)scale_y - 1);
  if (multiple > 0) {
    uint8_t* scan_src =
        (uint8_t*)pDeviceBitmap->GetScanline(des_row) + des_ScanOffet;
    for (int i = 1; i <= multiple; i++) {
      if (des_row + i >= m_startY + m_sizeY) {
        return;
      }
      uint8_t* scan_des =
          (uint8_t*)pDeviceBitmap->GetScanline(des_row + i) + des_ScanOffet;
      uint32_t size = m_sizeX * des_Bpp;
      memmove(scan_des, scan_src, size);
    }
  }
}

void CCodec_ProgressiveDecoder::Resample(
    const CFX_RetainPtr<CFX_DIBitmap>& pDeviceBitmap,
    int32_t src_line,
    uint8_t* src_scan,
    FXCodec_Format src_format) {
  int src_top = m_clipBox.top;
  int des_top = m_startY;
  int src_hei = m_clipBox.Height();
  int des_hei = m_sizeY;
  if (src_line >= src_top) {
    double scale_y = (double)des_hei / (double)src_hei;
    int src_row = src_line - src_top;
    int des_row = (int)(src_row * scale_y) + des_top;
    if (des_row >= des_top + des_hei) {
      return;
    }
    ReSampleScanline(pDeviceBitmap, des_row, m_pDecodeBuf, src_format);
    if (scale_y > 1.0) {
      ResampleVert(pDeviceBitmap, scale_y, des_row);
    }
  }
}

FXCODEC_STATUS CCodec_ProgressiveDecoder::GetFrames(int32_t& frames) {
  if (!(m_status == FXCODEC_STATUS_FRAME_READY ||
        m_status == FXCODEC_STATUS_FRAME_TOBECONTINUE)) {
    return FXCODEC_STATUS_ERROR;
  }
  switch (m_imagType) {
    case FXCODEC_IMAGE_JPG:
    case FXCODEC_IMAGE_BMP:
    case FXCODEC_IMAGE_PNG:
    case FXCODEC_IMAGE_TIF:
      frames = m_FrameNumber = 1;
      m_status = FXCODEC_STATUS_DECODE_READY;
      return m_status;
    case FXCODEC_IMAGE_GIF: {
      CCodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule();
      if (!pGifModule) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      while (true) {
        GifDecodeStatus readResult =
            pGifModule->LoadFrameInfo(m_pGifContext, &m_FrameNumber);
        while (readResult == GifDecodeStatus::Unfinished) {
          FXCODEC_STATUS error_status = FXCODEC_STATUS_ERR_READ;
          if (!GifReadMoreData(pGifModule, error_status))
            return error_status;

          readResult = pGifModule->LoadFrameInfo(m_pGifContext, &m_FrameNumber);
        }
        if (readResult == GifDecodeStatus::Success) {
          frames = m_FrameNumber;
          m_status = FXCODEC_STATUS_DECODE_READY;
          return m_status;
        }
        if (m_pGifContext) {
          pGifModule->Finish(m_pGifContext);
          m_pGifContext = nullptr;
        }
        m_status = FXCODEC_STATUS_ERROR;
        return m_status;
      }
    }
    default:
      return FXCODEC_STATUS_ERROR;
  }
}

FXCODEC_STATUS CCodec_ProgressiveDecoder::StartDecode(
    const CFX_RetainPtr<CFX_DIBitmap>& pDIBitmap,
    int start_x,
    int start_y,
    int size_x,
    int size_y,
    int32_t frames,
    bool bInterpol) {
  if (m_status != FXCODEC_STATUS_DECODE_READY)
    return FXCODEC_STATUS_ERROR;

  if (!pDIBitmap || pDIBitmap->GetBPP() < 8 || frames < 0 ||
      frames >= m_FrameNumber) {
    return FXCODEC_STATUS_ERR_PARAMS;
  }
  m_pDeviceBitmap = pDIBitmap;
  if (m_clipBox.IsEmpty())
    return FXCODEC_STATUS_ERR_PARAMS;
  if (size_x <= 0 || size_x > 65535 || size_y <= 0 || size_y > 65535)
    return FXCODEC_STATUS_ERR_PARAMS;

  FX_RECT device_rc =
      FX_RECT(start_x, start_y, start_x + size_x, start_y + size_y);
  int32_t out_range_x = device_rc.right - pDIBitmap->GetWidth();
  int32_t out_range_y = device_rc.bottom - pDIBitmap->GetHeight();
  device_rc.Intersect(
      FX_RECT(0, 0, pDIBitmap->GetWidth(), pDIBitmap->GetHeight()));
  if (device_rc.IsEmpty())
    return FXCODEC_STATUS_ERR_PARAMS;

  m_startX = device_rc.left;
  m_startY = device_rc.top;
  m_sizeX = device_rc.Width();
  m_sizeY = device_rc.Height();
  m_bInterpol = bInterpol;
  m_FrameCur = 0;
  if (start_x < 0 || out_range_x > 0) {
    float scaleX = (float)m_clipBox.Width() / (float)size_x;
    if (start_x < 0) {
      m_clipBox.left -= (int32_t)ceil((float)start_x * scaleX);
    }
    if (out_range_x > 0) {
      m_clipBox.right -= (int32_t)floor((float)out_range_x * scaleX);
    }
  }
  if (start_y < 0 || out_range_y > 0) {
    float scaleY = (float)m_clipBox.Height() / (float)size_y;
    if (start_y < 0) {
      m_clipBox.top -= (int32_t)ceil((float)start_y * scaleY);
    }
    if (out_range_y > 0) {
      m_clipBox.bottom -= (int32_t)floor((float)out_range_y * scaleY);
    }
  }
  if (m_clipBox.IsEmpty()) {
    return FXCODEC_STATUS_ERR_PARAMS;
  }
  switch (m_imagType) {
    case FXCODEC_IMAGE_JPG: {
      CCodec_JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
      int down_scale = 1;
      GetDownScale(down_scale);
      bool bStart = pJpegModule->StartScanline(m_pJpegContext, down_scale);
      while (!bStart) {
        FXCODEC_STATUS error_status = FXCODEC_STATUS_ERROR;
        if (!JpegReadMoreData(pJpegModule, error_status)) {
          m_pDeviceBitmap = nullptr;
          m_pFile = nullptr;
          m_status = error_status;
          return m_status;
        }
        bStart = pJpegModule->StartScanline(m_pJpegContext, down_scale);
      }
      int scanline_size = (m_SrcWidth + down_scale - 1) / down_scale;
      scanline_size = (scanline_size * m_SrcComponents + 3) / 4 * 4;
      FX_Free(m_pDecodeBuf);
      m_pDecodeBuf = FX_Alloc(uint8_t, scanline_size);
      memset(m_pDecodeBuf, 0, scanline_size);
      m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
                        m_clipBox.Width(), m_bInterpol);
      m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
      switch (m_SrcComponents) {
        case 1:
          m_SrcFormat = FXCodec_8bppGray;
          break;
        case 3:
          m_SrcFormat = FXCodec_Rgb;
          break;
        case 4:
          m_SrcFormat = FXCodec_Cmyk;
          break;
      }
      GetTransMethod(pDIBitmap->GetFormat(), m_SrcFormat);
      m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
      return m_status;
    }
    case FXCODEC_IMAGE_PNG: {
      CCodec_PngModule* pPngModule = m_pCodecMgr->GetPngModule();
      if (!pPngModule) {
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      if (m_pPngContext) {
        pPngModule->Finish(m_pPngContext);
        m_pPngContext = nullptr;
      }
      m_pPngContext = pPngModule->Start();
      if (!m_pPngContext) {
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      m_offSet = 0;
      switch (m_pDeviceBitmap->GetFormat()) {
        case FXDIB_8bppMask:
        case FXDIB_8bppRgb:
          m_SrcComponents = 1;
          m_SrcFormat = FXCodec_8bppGray;
          break;
        case FXDIB_Rgb:
          m_SrcComponents = 3;
          m_SrcFormat = FXCodec_Rgb;
          break;
        case FXDIB_Rgb32:
        case FXDIB_Argb:
          m_SrcComponents = 4;
          m_SrcFormat = FXCodec_Argb;
          break;
        default: {
          m_pDeviceBitmap = nullptr;
          m_pFile = nullptr;
          m_status = FXCODEC_STATUS_ERR_PARAMS;
          return m_status;
        }
      }
      GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
      int scanline_size = (m_SrcWidth * m_SrcComponents + 3) / 4 * 4;
      FX_Free(m_pDecodeBuf);
      m_pDecodeBuf = FX_Alloc(uint8_t, scanline_size);
      memset(m_pDecodeBuf, 0, scanline_size);
      m_WeightHorzOO.Calc(m_sizeX, m_clipBox.Width(), m_bInterpol);
      m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
      m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
      return m_status;
    }
    case FXCODEC_IMAGE_GIF: {
      CCodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule();
      if (!pGifModule) {
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      m_SrcFormat = FXCodec_8bppRgb;
      GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
      int scanline_size = (m_SrcWidth + 3) / 4 * 4;
      FX_Free(m_pDecodeBuf);
      m_pDecodeBuf = FX_Alloc(uint8_t, scanline_size);
      memset(m_pDecodeBuf, 0, scanline_size);
      m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
                        m_clipBox.Width(), m_bInterpol);
      m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
      m_FrameCur = frames;
      m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
      return m_status;
    }
    case FXCODEC_IMAGE_BMP: {
      CCodec_BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
      if (!pBmpModule) {
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      switch (m_SrcComponents) {
        case 1:
          m_SrcFormat = FXCodec_8bppRgb;
          break;
        case 3:
          m_SrcFormat = FXCodec_Rgb;
          break;
        case 4:
          m_SrcFormat = FXCodec_Rgb32;
          break;
      }
      GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat);
      m_ScanlineSize = (m_SrcWidth * m_SrcComponents + 3) / 4 * 4;
      FX_Free(m_pDecodeBuf);
      m_pDecodeBuf = FX_Alloc(uint8_t, m_ScanlineSize);
      memset(m_pDecodeBuf, 0, m_ScanlineSize);
      m_WeightHorz.Calc(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0,
                        m_clipBox.Width(), m_bInterpol);
      m_WeightVert.Calc(m_sizeY, m_clipBox.Height());
      m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
      return m_status;
    }
    case FXCODEC_IMAGE_TIF:
      m_status = FXCODEC_STATUS_DECODE_TOBECONTINUE;
      return m_status;
    default:
      return FXCODEC_STATUS_ERROR;
  }
}

FXCODEC_STATUS CCodec_ProgressiveDecoder::ContinueDecode() {
  if (m_status != FXCODEC_STATUS_DECODE_TOBECONTINUE)
    return FXCODEC_STATUS_ERROR;

  switch (m_imagType) {
    case FXCODEC_IMAGE_JPG: {
      CCodec_JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule();
      while (true) {
        bool readRes = pJpegModule->ReadScanline(m_pJpegContext, m_pDecodeBuf);
        while (!readRes) {
          FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
          if (!JpegReadMoreData(pJpegModule, error_status)) {
            m_pDeviceBitmap = nullptr;
            m_pFile = nullptr;
            m_status = error_status;
            return m_status;
          }
          readRes = pJpegModule->ReadScanline(m_pJpegContext, m_pDecodeBuf);
        }
        if (m_SrcFormat == FXCodec_Rgb) {
          int src_Bpp = (m_SrcFormat & 0xff) >> 3;
          RGB2BGR(m_pDecodeBuf + m_clipBox.left * src_Bpp, m_clipBox.Width());
        }
        if (m_SrcRow >= m_clipBox.bottom) {
          m_pDeviceBitmap = nullptr;
          m_pFile = nullptr;
          m_status = FXCODEC_STATUS_DECODE_FINISH;
          return m_status;
        }
        Resample(m_pDeviceBitmap, m_SrcRow, m_pDecodeBuf, m_SrcFormat);
        m_SrcRow++;
      }
    }
    case FXCODEC_IMAGE_PNG: {
      CCodec_PngModule* pPngModule = m_pCodecMgr->GetPngModule();
      if (!pPngModule) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      while (true) {
        uint32_t remain_size = (uint32_t)m_pFile->GetSize() - m_offSet;
        uint32_t input_size =
            remain_size > FXCODEC_BLOCK_SIZE ? FXCODEC_BLOCK_SIZE : remain_size;
        if (input_size == 0) {
          if (m_pPngContext) {
            pPngModule->Finish(m_pPngContext);
          }
          m_pPngContext = nullptr;
          m_pDeviceBitmap = nullptr;
          m_pFile = nullptr;
          m_status = FXCODEC_STATUS_DECODE_FINISH;
          return m_status;
        }
        if (m_pSrcBuf && input_size > m_SrcSize) {
          FX_Free(m_pSrcBuf);
          m_pSrcBuf = FX_Alloc(uint8_t, input_size);
          memset(m_pSrcBuf, 0, input_size);
          m_SrcSize = input_size;
        }
        bool bResult = m_pFile->ReadBlock(m_pSrcBuf, m_offSet, input_size);
        if (!bResult) {
          m_pDeviceBitmap = nullptr;
          m_pFile = nullptr;
          m_status = FXCODEC_STATUS_ERR_READ;
          return m_status;
        }
        m_offSet += input_size;
        bResult =
            pPngModule->Input(m_pPngContext, m_pSrcBuf, input_size, nullptr);
        if (!bResult) {
          m_pDeviceBitmap = nullptr;
          m_pFile = nullptr;
          m_status = FXCODEC_STATUS_ERROR;
          return m_status;
        }
      }
    }
    case FXCODEC_IMAGE_GIF: {
      CCodec_GifModule* pGifModule = m_pCodecMgr->GetGifModule();
      if (!pGifModule) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      while (true) {
        GifDecodeStatus readRes =
            pGifModule->LoadFrame(m_pGifContext, m_FrameCur, nullptr);
        while (readRes == GifDecodeStatus::Unfinished) {
          FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
          if (!GifReadMoreData(pGifModule, error_status)) {
            m_pDeviceBitmap = nullptr;
            m_pFile = nullptr;
            m_status = error_status;
            return m_status;
          }
          readRes = pGifModule->LoadFrame(m_pGifContext, m_FrameCur, nullptr);
        }
        if (readRes == GifDecodeStatus::Success) {
          m_pDeviceBitmap = nullptr;
          m_pFile = nullptr;
          m_status = FXCODEC_STATUS_DECODE_FINISH;
          return m_status;
        }
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERROR;
        return m_status;
      }
    }
    case FXCODEC_IMAGE_BMP: {
      CCodec_BmpModule* pBmpModule = m_pCodecMgr->GetBmpModule();
      if (!pBmpModule) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      while (true) {
        int32_t readRes = pBmpModule->LoadImage(m_pBmpContext);
        while (readRes == 2) {
          FXCODEC_STATUS error_status = FXCODEC_STATUS_DECODE_FINISH;
          if (!BmpReadMoreData(pBmpModule, error_status)) {
            m_pDeviceBitmap = nullptr;
            m_pFile = nullptr;
            m_status = error_status;
            return m_status;
          }
          readRes = pBmpModule->LoadImage(m_pBmpContext);
        }
        if (readRes == 1) {
          m_pDeviceBitmap = nullptr;
          m_pFile = nullptr;
          m_status = FXCODEC_STATUS_DECODE_FINISH;
          return m_status;
        }
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERROR;
        return m_status;
      }
    }
    case FXCODEC_IMAGE_TIF: {
      CCodec_TiffModule* pTiffModule = m_pCodecMgr->GetTiffModule();
      if (!pTiffModule) {
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      bool ret = false;
      if (m_pDeviceBitmap->GetBPP() == 32 &&
          m_pDeviceBitmap->GetWidth() == m_SrcWidth && m_SrcWidth == m_sizeX &&
          m_pDeviceBitmap->GetHeight() == m_SrcHeight &&
          m_SrcHeight == m_sizeY && m_startX == 0 && m_startY == 0 &&
          m_clipBox.left == 0 && m_clipBox.top == 0 &&
          m_clipBox.right == m_SrcWidth && m_clipBox.bottom == m_SrcHeight) {
        ret = pTiffModule->Decode(m_pTiffContext, m_pDeviceBitmap);
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        if (!ret) {
          m_status = FXCODEC_STATUS_ERROR;
          return m_status;
        }
        m_status = FXCODEC_STATUS_DECODE_FINISH;
        return m_status;
      }

      auto pDIBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
      pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Argb);
      if (!pDIBitmap->GetBuffer()) {
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      ret = pTiffModule->Decode(m_pTiffContext, pDIBitmap);
      if (!ret) {
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERROR;
        return m_status;
      }
      CFX_RetainPtr<CFX_DIBitmap> pClipBitmap =
          (m_clipBox.left == 0 && m_clipBox.top == 0 &&
           m_clipBox.right == m_SrcWidth && m_clipBox.bottom == m_SrcHeight)
              ? pDIBitmap
              : pDIBitmap->Clone(&m_clipBox);
      if (!pClipBitmap) {
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      CFX_RetainPtr<CFX_DIBitmap> pFormatBitmap;
      switch (m_pDeviceBitmap->GetFormat()) {
        case FXDIB_8bppRgb:
          pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
          pFormatBitmap->Create(pClipBitmap->GetWidth(),
                                pClipBitmap->GetHeight(), FXDIB_8bppRgb);
          break;
        case FXDIB_8bppMask:
          pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
          pFormatBitmap->Create(pClipBitmap->GetWidth(),
                                pClipBitmap->GetHeight(), FXDIB_8bppMask);
          break;
        case FXDIB_Rgb:
          pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
          pFormatBitmap->Create(pClipBitmap->GetWidth(),
                                pClipBitmap->GetHeight(), FXDIB_Rgb);
          break;
        case FXDIB_Rgb32:
          pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
          pFormatBitmap->Create(pClipBitmap->GetWidth(),
                                pClipBitmap->GetHeight(), FXDIB_Rgb32);
          break;
        case FXDIB_Argb:
          pFormatBitmap = pClipBitmap;
          break;
        default:
          break;
      }
      switch (m_pDeviceBitmap->GetFormat()) {
        case FXDIB_8bppRgb:
        case FXDIB_8bppMask: {
          for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) {
            uint8_t* src_line = (uint8_t*)pClipBitmap->GetScanline(row);
            uint8_t* des_line = (uint8_t*)pFormatBitmap->GetScanline(row);
            for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) {
              uint8_t _a = 255 - src_line[3];
              uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
              uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
              uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
              *des_line++ = FXRGB2GRAY(r, g, b);
              src_line += 4;
            }
          }
        } break;
        case FXDIB_Rgb:
        case FXDIB_Rgb32: {
          int32_t desBpp = (m_pDeviceBitmap->GetFormat() == FXDIB_Rgb) ? 3 : 4;
          for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) {
            uint8_t* src_line = (uint8_t*)pClipBitmap->GetScanline(row);
            uint8_t* des_line = (uint8_t*)pFormatBitmap->GetScanline(row);
            for (int32_t col = 0; col < pClipBitmap->GetWidth(); col++) {
              uint8_t _a = 255 - src_line[3];
              uint8_t b = (src_line[0] * src_line[3] + 0xFF * _a) / 255;
              uint8_t g = (src_line[1] * src_line[3] + 0xFF * _a) / 255;
              uint8_t r = (src_line[2] * src_line[3] + 0xFF * _a) / 255;
              *des_line++ = b;
              *des_line++ = g;
              *des_line++ = r;
              des_line += desBpp - 3;
              src_line += 4;
            }
          }
        } break;
        default:
          break;
      }
      if (!pFormatBitmap) {
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      CFX_RetainPtr<CFX_DIBitmap> pStrechBitmap = pFormatBitmap->StretchTo(
          m_sizeX, m_sizeY, m_bInterpol ? FXDIB_INTERPOL : FXDIB_DOWNSAMPLE,
          nullptr);
      pFormatBitmap = nullptr;
      if (!pStrechBitmap) {
        m_pDeviceBitmap = nullptr;
        m_pFile = nullptr;
        m_status = FXCODEC_STATUS_ERR_MEMORY;
        return m_status;
      }
      m_pDeviceBitmap->TransferBitmap(m_startX, m_startY, m_sizeX, m_sizeY,
                                      pStrechBitmap, 0, 0);
      m_pDeviceBitmap = nullptr;
      m_pFile = nullptr;
      m_status = FXCODEC_STATUS_DECODE_FINISH;
      return m_status;
    }
    default:
      return FXCODEC_STATUS_ERROR;
  }
}

std::unique_ptr<CCodec_ProgressiveDecoder>
CCodec_ModuleMgr::CreateProgressiveDecoder() {
  return pdfium::MakeUnique<CCodec_ProgressiveDecoder>(this);
}