| // 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/progressive_decoder.h" | 
 |  | 
 | #include <algorithm> | 
 | #include <memory> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "build/build_config.h" | 
 | #include "core/fxcodec/cfx_codec_memory.h" | 
 | #include "core/fxcodec/jpeg/jpeg_progressive_decoder.h" | 
 | #include "core/fxcrt/fx_safe_types.h" | 
 | #include "core/fxcrt/fx_stream.h" | 
 | #include "core/fxcrt/fx_system.h" | 
 | #include "core/fxcrt/span_util.h" | 
 | #include "core/fxge/dib/cfx_cmyk_to_srgb.h" | 
 | #include "core/fxge/dib/cfx_dibitmap.h" | 
 | #include "core/fxge/dib/fx_dib.h" | 
 | #include "third_party/base/check.h" | 
 | #include "third_party/base/check_op.h" | 
 | #include "third_party/base/notreached.h" | 
 | #include "third_party/base/numerics/safe_conversions.h" | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_BMP | 
 | #include "core/fxcodec/bmp/bmp_progressive_decoder.h" | 
 | #endif  // PDF_ENABLE_XFA_BMP | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_GIF | 
 | #include "core/fxcodec/gif/gif_progressive_decoder.h" | 
 | #endif  // PDF_ENABLE_XFA_GIF | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_TIFF | 
 | #include "core/fxcodec/tiff/tiff_decoder.h" | 
 | #endif  // PDF_ENABLE_XFA_TIFF | 
 |  | 
 | namespace fxcodec { | 
 |  | 
 | namespace { | 
 |  | 
 | constexpr size_t kBlockSize = 4096; | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_PNG | 
 | #if BUILDFLAG(IS_APPLE) | 
 | const double kPngGamma = 1.7; | 
 | #else | 
 | const double kPngGamma = 2.2; | 
 | #endif  // BUILDFLAG(IS_APPLE) | 
 | #endif  // PDF_ENABLE_XFA_PNG | 
 |  | 
 | 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 | 
 |  | 
 | ProgressiveDecoder::HorzTable::HorzTable() = default; | 
 |  | 
 | ProgressiveDecoder::HorzTable::~HorzTable() = default; | 
 |  | 
 | void ProgressiveDecoder::HorzTable::CalculateWeights(int dest_len, | 
 |                                                      int src_len) { | 
 |   CHECK_GE(dest_len, 0); | 
 |   m_ItemSize = | 
 |       pdfium::base::checked_cast<int>(PixelWeight::TotalBytesForWeightCount(2)); | 
 |   FX_SAFE_SIZE_T safe_size = m_ItemSize; | 
 |   safe_size *= dest_len; | 
 |   m_pWeightTables.resize(safe_size.ValueOrDie(), 0); | 
 |   double scale = (double)dest_len / (double)src_len; | 
 |   if (scale > 1) { | 
 |     int pre_dest_col = 0; | 
 |     for (int src_col = 0; src_col < src_len; src_col++) { | 
 |       double dest_col_f = src_col * scale; | 
 |       int dest_col = FXSYS_roundf((float)dest_col_f); | 
 |       PixelWeight* pWeight = GetPixelWeight(dest_col); | 
 |       pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col; | 
 |       pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; | 
 |       pWeight->m_Weights[1] = 0; | 
 |       if (src_col == src_len - 1 && dest_col < dest_len - 1) { | 
 |         for (int dest_col_index = pre_dest_col + 1; dest_col_index < dest_len; | 
 |              dest_col_index++) { | 
 |           pWeight = GetPixelWeight(dest_col_index); | 
 |           pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col; | 
 |           pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; | 
 |           pWeight->m_Weights[1] = 0; | 
 |         } | 
 |         return; | 
 |       } | 
 |       int dest_col_len = dest_col - pre_dest_col; | 
 |       for (int dest_col_index = pre_dest_col + 1; dest_col_index < dest_col; | 
 |            dest_col_index++) { | 
 |         pWeight = GetPixelWeight(dest_col_index); | 
 |         pWeight->m_SrcStart = src_col - 1; | 
 |         pWeight->m_SrcEnd = src_col; | 
 |         pWeight->m_Weights[0] = CStretchEngine::FixedFromFloat( | 
 |             ((float)dest_col - (float)dest_col_index) / (float)dest_col_len); | 
 |         pWeight->m_Weights[1] = | 
 |             CStretchEngine::kFixedPointOne - pWeight->m_Weights[0]; | 
 |       } | 
 |       pre_dest_col = dest_col; | 
 |     } | 
 |     return; | 
 |   } | 
 |   for (int dest_col = 0; dest_col < dest_len; dest_col++) { | 
 |     double src_col_f = dest_col / scale; | 
 |     int src_col = FXSYS_roundf((float)src_col_f); | 
 |     PixelWeight* pWeight = GetPixelWeight(dest_col); | 
 |     pWeight->m_SrcStart = pWeight->m_SrcEnd = src_col; | 
 |     pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; | 
 |     pWeight->m_Weights[1] = 0; | 
 |   } | 
 | } | 
 |  | 
 | ProgressiveDecoder::VertTable::VertTable() = default; | 
 |  | 
 | ProgressiveDecoder::VertTable::~VertTable() = default; | 
 |  | 
 | void ProgressiveDecoder::VertTable::CalculateWeights(int dest_len, | 
 |                                                      int src_len) { | 
 |   CHECK_GE(dest_len, 0); | 
 |   m_ItemSize = | 
 |       pdfium::base::checked_cast<int>(PixelWeight::TotalBytesForWeightCount(2)); | 
 |   FX_SAFE_SIZE_T safe_size = m_ItemSize; | 
 |   safe_size *= dest_len; | 
 |   m_pWeightTables.resize(safe_size.ValueOrDie(), 0); | 
 |   double scale = (double)dest_len / (double)src_len; | 
 |   if (scale <= 1) { | 
 |     for (int dest_row = 0; dest_row < dest_len; dest_row++) { | 
 |       PixelWeight* pWeight = GetPixelWeight(dest_row); | 
 |       pWeight->m_SrcStart = dest_row; | 
 |       pWeight->m_SrcEnd = dest_row; | 
 |       pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; | 
 |       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 dest_row = start_step; dest_row < end_step; dest_row++) { | 
 |         PixelWeight* pWeight = GetPixelWeight(dest_row); | 
 |         pWeight->m_SrcStart = start_step; | 
 |         pWeight->m_SrcEnd = start_step; | 
 |         pWeight->m_Weights[0] = CStretchEngine::kFixedPointOne; | 
 |         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] = CStretchEngine::kFixedPointOne; | 
 |       pWeight->m_Weights[1] = 0; | 
 |     } | 
 |     for (int dest_row = start_step + 1; dest_row < end_step; dest_row++) { | 
 |       PixelWeight* pWeight = GetPixelWeight(dest_row); | 
 |       pWeight->m_SrcStart = start_step; | 
 |       pWeight->m_SrcEnd = end_step; | 
 |       pWeight->m_Weights[0] = CStretchEngine::FixedFromFloat( | 
 |           (float)(end_step - dest_row) / (float)length); | 
 |       pWeight->m_Weights[1] = | 
 |           CStretchEngine::kFixedPointOne - pWeight->m_Weights[0]; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | ProgressiveDecoder::ProgressiveDecoder() = default; | 
 |  | 
 | ProgressiveDecoder::~ProgressiveDecoder() = default; | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_PNG | 
 | bool 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_Format::k1bppMask: | 
 |     case FXDIB_Format::k1bppRgb: | 
 |       NOTREACHED(); | 
 |       return false; | 
 |     case FXDIB_Format::k8bppMask: | 
 |     case FXDIB_Format::k8bppRgb: | 
 |       *color_type = 0; | 
 |       break; | 
 |     case FXDIB_Format::kRgb: | 
 |       *color_type = 2; | 
 |       break; | 
 |     case FXDIB_Format::kRgb32: | 
 |     case FXDIB_Format::kArgb: | 
 |       *color_type = 6; | 
 |       break; | 
 |     default: | 
 |       NOTREACHED(); | 
 |       return false; | 
 |   } | 
 |   *gamma = kPngGamma; | 
 |   return true; | 
 | } | 
 |  | 
 | bool ProgressiveDecoder::PngAskScanlineBuf(int line, uint8_t** pSrcBuf) { | 
 |   RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap; | 
 |   if (!pDIBitmap) { | 
 |     NOTREACHED(); | 
 |     return false; | 
 |   } | 
 |   if (line < m_clipBox.top || line >= m_clipBox.bottom) | 
 |     return true; | 
 |  | 
 |   double scale_y = static_cast<double>(m_sizeY) / m_clipBox.Height(); | 
 |   int32_t row = | 
 |       static_cast<int32_t>((line - m_clipBox.top) * scale_y) + m_startY; | 
 |   *pSrcBuf = m_DecodeBuf.data(); | 
 |   int32_t src_Bpp = pDIBitmap->GetBPP() >> 3; | 
 |   int32_t dest_Bpp = (m_SrcFormat & 0xff) >> 3; | 
 |   int32_t src_left = m_startX; | 
 |   int32_t dest_left = m_clipBox.left; | 
 |   const uint8_t* src_scan = | 
 |       pDIBitmap->GetScanline(row).subspan(src_left * src_Bpp).data(); | 
 |   uint8_t* dest_scan = m_DecodeBuf.data() + dest_left * dest_Bpp; | 
 |   switch (pDIBitmap->GetFormat()) { | 
 |     case FXDIB_Format::k1bppMask: | 
 |     case FXDIB_Format::k1bppRgb: | 
 |       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; | 
 |         NOTREACHED(); | 
 |         return false; | 
 |       } | 
 |       return true; | 
 |     case FXDIB_Format::k8bppMask: | 
 |     case FXDIB_Format::k8bppRgb: | 
 |       if (pDIBitmap->HasPalette()) | 
 |         return false; | 
 |       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; | 
 |         uint32_t dest_g = pPixelWeights->m_Weights[0] * src_scan[src_col]; | 
 |         dest_scan[pPixelWeights->m_SrcStart] = | 
 |             CStretchEngine::PixelFromFixed(dest_g); | 
 |       } | 
 |       return true; | 
 |     case FXDIB_Format::kRgb: | 
 |     case FXDIB_Format::kRgb32: | 
 |       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; | 
 |         const uint8_t* p = src_scan + src_col * src_Bpp; | 
 |         uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++); | 
 |         uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++); | 
 |         uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p); | 
 |         uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp]; | 
 |         *pDes++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |         *pDes++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         *pDes = CStretchEngine::PixelFromFixed(dest_r); | 
 |       } | 
 |       return true; | 
 |     case FXDIB_Format::kArgb: | 
 |       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; | 
 |         const uint8_t* p = src_scan + src_col * src_Bpp; | 
 |         uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++); | 
 |         uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++); | 
 |         uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p++); | 
 |         uint8_t dest_a = *p; | 
 |         uint8_t* pDes = &dest_scan[pPixelWeights->m_SrcStart * dest_Bpp]; | 
 |         *pDes++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |         *pDes++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         *pDes++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |         *pDes = dest_a; | 
 |       } | 
 |       return true; | 
 |     default: | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | void ProgressiveDecoder::PngFillScanlineBufCompleted(int pass, int line) { | 
 |   RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap; | 
 |   DCHECK(pDIBitmap); | 
 |   int src_top = m_clipBox.top; | 
 |   int src_bottom = m_clipBox.bottom; | 
 |   int dest_top = m_startY; | 
 |   int src_height = m_clipBox.Height(); | 
 |   int dest_height = m_sizeY; | 
 |   if (line >= src_top && line < src_bottom) { | 
 |     double scale_y = static_cast<double>(dest_height) / src_height; | 
 |     int src_row = line - src_top; | 
 |     int dest_row = (int)(src_row * scale_y) + dest_top; | 
 |     if (dest_row >= dest_top + dest_height) { | 
 |       return; | 
 |     } | 
 |     PngOneOneMapResampleHorz(pDIBitmap, dest_row, m_DecodeBuf.data(), | 
 |                              m_SrcFormat); | 
 |     if (m_SrcPassNumber == 1 && scale_y > 1.0) { | 
 |       ResampleVert(pDIBitmap, scale_y, dest_row); | 
 |       return; | 
 |     } | 
 |     if (pass == 6 && scale_y > 1.0) { | 
 |       ResampleVert(pDIBitmap, scale_y, dest_row); | 
 |     } | 
 |   } | 
 | } | 
 | #endif  // PDF_ENABLE_XFA_PNG | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_GIF | 
 | uint32_t ProgressiveDecoder::GifCurrentPosition() const { | 
 |   uint32_t remain_size = pdfium::base::checked_cast<uint32_t>( | 
 |       GifDecoder::GetAvailInput(m_pGifContext.get())); | 
 |   return m_offSet - remain_size; | 
 | } | 
 |  | 
 | bool ProgressiveDecoder::GifInputRecordPositionBuf(uint32_t rcd_pos, | 
 |                                                    const FX_RECT& img_rc, | 
 |                                                    int32_t pal_num, | 
 |                                                    CFX_GifPalette* pal_ptr, | 
 |                                                    int32_t trans_index, | 
 |                                                    bool interlace) { | 
 |   m_offSet = rcd_pos; | 
 |   m_InvalidateGifBuffer = true; | 
 |  | 
 |   FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; | 
 |   if (!GifReadMoreData(&error_status)) | 
 |     return false; | 
 |  | 
 |   CFX_GifPalette* pPalette = nullptr; | 
 |   if (pal_num != 0 && pal_ptr) { | 
 |     pPalette = pal_ptr; | 
 |   } else { | 
 |     if (!m_pGifPalette) | 
 |       return false; | 
 |     pal_num = m_GifPltNumber; | 
 |     pPalette = m_pGifPalette; | 
 |   } | 
 |   m_SrcPalette.resize(pal_num); | 
 |   m_SrcPaletteNumber = pal_num; | 
 |   for (int i = 0; i < pal_num; i++) { | 
 |     m_SrcPalette[i] = | 
 |         ArgbEncode(0xff, pPalette[i].r, pPalette[i].g, pPalette[i].b); | 
 |   } | 
 |   m_GifTransIndex = trans_index; | 
 |   m_GifFrameRect = img_rc; | 
 |   m_SrcPassNumber = interlace ? 4 : 1; | 
 |   int32_t pal_index = m_GifBgIndex; | 
 |   RetainPtr<CFX_DIBitmap> pDevice = m_pDeviceBitmap; | 
 |   if (trans_index >= pal_num) | 
 |     trans_index = -1; | 
 |   if (trans_index != -1) { | 
 |     m_SrcPalette[trans_index] &= 0x00ffffff; | 
 |     if (pDevice->IsAlphaFormat()) | 
 |       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_SrcPalette[pal_index]; | 
 |   for (int row = 0; row < sizeY; row++) { | 
 |     uint8_t* pScanline = | 
 |         pDevice->GetWritableScanline(row + startY).subspan(startX * Bpp).data(); | 
 |     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 ProgressiveDecoder::GifReadScanline(int32_t row_num, uint8_t* row_buf) { | 
 |   RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap; | 
 |   DCHECK(pDIBitmap); | 
 |   int32_t img_width = m_GifFrameRect.Width(); | 
 |   if (!pDIBitmap->IsAlphaFormat()) { | 
 |     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->IsAlphaFormat()) { | 
 |     pal_index = m_GifTransIndex; | 
 |   } | 
 |   memset(m_DecodeBuf.data(), 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_DecodeBuf.data() + left, row_buf, img_width); | 
 |   int src_top = m_clipBox.top; | 
 |   int src_bottom = m_clipBox.bottom; | 
 |   int dest_top = m_startY; | 
 |   int src_height = m_clipBox.Height(); | 
 |   int dest_height = m_sizeY; | 
 |   if (line < src_top || line >= src_bottom) | 
 |     return; | 
 |  | 
 |   double scale_y = static_cast<double>(dest_height) / src_height; | 
 |   int src_row = line - src_top; | 
 |   int dest_row = (int)(src_row * scale_y) + dest_top; | 
 |   if (dest_row >= dest_top + dest_height) | 
 |     return; | 
 |  | 
 |   ResampleScanline(pDIBitmap, dest_row, m_DecodeBuf.data(), m_SrcFormat); | 
 |   if (scale_y > 1.0 && m_SrcPassNumber == 1) { | 
 |     ResampleVert(pDIBitmap, scale_y, dest_row); | 
 |     return; | 
 |   } | 
 |   if (scale_y <= 1.0) | 
 |     return; | 
 |  | 
 |   int dest_bottom = dest_top + m_sizeY; | 
 |   int dest_Bpp = pDIBitmap->GetBPP() >> 3; | 
 |   uint32_t dest_ScanOffset = m_startX * dest_Bpp; | 
 |   if (dest_row + (int)scale_y >= dest_bottom - 1) { | 
 |     const uint8_t* scan_src = | 
 |         pDIBitmap->GetScanline(dest_row).subspan(dest_ScanOffset).data(); | 
 |     int cur_row = dest_row; | 
 |     while (++cur_row < dest_bottom) { | 
 |       uint8_t* scan_des = pDIBitmap->GetWritableScanline(cur_row) | 
 |                               .subspan(dest_ScanOffset) | 
 |                               .data(); | 
 |       uint32_t size = m_sizeX * dest_Bpp; | 
 |       memmove(scan_des, scan_src, size); | 
 |     } | 
 |   } | 
 |   if (bLastPass) | 
 |     GifDoubleLineResampleVert(pDIBitmap, scale_y, dest_row); | 
 | } | 
 | #endif  // PDF_ENABLE_XFA_GIF | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_BMP | 
 | bool ProgressiveDecoder::BmpInputImagePositionBuf(uint32_t rcd_pos) { | 
 |   m_offSet = rcd_pos; | 
 |   FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; | 
 |   return BmpReadMoreData(m_pBmpContext.get(), &error_status); | 
 | } | 
 |  | 
 | void ProgressiveDecoder::BmpReadScanline(uint32_t row_num, | 
 |                                          pdfium::span<const uint8_t> row_buf) { | 
 |   RetainPtr<CFX_DIBitmap> pDIBitmap = m_pDeviceBitmap; | 
 |   DCHECK(pDIBitmap); | 
 |  | 
 |   fxcrt::spancpy(pdfium::make_span(m_DecodeBuf), row_buf.first(m_ScanlineSize)); | 
 |  | 
 |   int src_top = m_clipBox.top; | 
 |   int src_bottom = m_clipBox.bottom; | 
 |   int dest_top = m_startY; | 
 |   int src_height = m_clipBox.Height(); | 
 |   int dest_height = m_sizeY; | 
 |   if ((src_top >= 0 && row_num < static_cast<uint32_t>(src_top)) || | 
 |       src_bottom < 0 || row_num >= static_cast<uint32_t>(src_bottom)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   double scale_y = static_cast<double>(dest_height) / src_height; | 
 |   int src_row = row_num - src_top; | 
 |   int dest_row = (int)(src_row * scale_y) + dest_top; | 
 |   if (dest_row >= dest_top + dest_height) | 
 |     return; | 
 |  | 
 |   ResampleScanline(pDIBitmap, dest_row, m_DecodeBuf.data(), m_SrcFormat); | 
 |   if (scale_y <= 1.0) | 
 |     return; | 
 |  | 
 |   if (m_BmpIsTopBottom) { | 
 |     ResampleVert(pDIBitmap, scale_y, dest_row); | 
 |     return; | 
 |   } | 
 |   ResampleVertBT(pDIBitmap, scale_y, dest_row); | 
 | } | 
 |  | 
 | void ProgressiveDecoder::ResampleVertBT( | 
 |     const RetainPtr<CFX_DIBitmap>& pDeviceBitmap, | 
 |     double scale_y, | 
 |     int dest_row) { | 
 |   int dest_Bpp = pDeviceBitmap->GetBPP() >> 3; | 
 |   uint32_t dest_ScanOffset = m_startX * dest_Bpp; | 
 |   int dest_top = m_startY; | 
 |   int dest_bottom = m_startY + m_sizeY; | 
 |   FX_SAFE_INT32 check_dest_row_1 = dest_row; | 
 |   check_dest_row_1 += pdfium::base::checked_cast<int>(scale_y); | 
 |   int dest_row_1 = check_dest_row_1.ValueOrDie(); | 
 |   if (dest_row_1 >= dest_bottom - 1) { | 
 |     const uint8_t* scan_src = | 
 |         pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset).data(); | 
 |     while (++dest_row < dest_bottom) { | 
 |       uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row) | 
 |                               .subspan(dest_ScanOffset) | 
 |                               .data(); | 
 |       uint32_t size = m_sizeX * dest_Bpp; | 
 |       memmove(scan_des, scan_src, size); | 
 |     } | 
 |     return; | 
 |   } | 
 |   for (; dest_row_1 > dest_row; dest_row_1--) { | 
 |     uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row_1) | 
 |                             .subspan(dest_ScanOffset) | 
 |                             .data(); | 
 |     PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top); | 
 |     const uint8_t* scan_src1 = | 
 |         pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) | 
 |             .subspan(dest_ScanOffset) | 
 |             .data(); | 
 |     const uint8_t* scan_src2 = | 
 |         pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) | 
 |             .subspan(dest_ScanOffset) | 
 |             .data(); | 
 |     switch (pDeviceBitmap->GetFormat()) { | 
 |       case FXDIB_Format::kInvalid: | 
 |       case FXDIB_Format::k1bppMask: | 
 |       case FXDIB_Format::k1bppRgb: | 
 |         return; | 
 |       case FXDIB_Format::k8bppMask: | 
 |       case FXDIB_Format::k8bppRgb: | 
 |         if (pDeviceBitmap->HasPalette()) | 
 |           return; | 
 |         for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |           uint32_t dest_g = 0; | 
 |           dest_g += pWeight->m_Weights[0] * (*scan_src1++); | 
 |           dest_g += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         } | 
 |         break; | 
 |       case FXDIB_Format::kRgb: | 
 |       case FXDIB_Format::kRgb32: | 
 |         for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |           uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           scan_src1 += dest_Bpp - 3; | 
 |           dest_b += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_g += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_r += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           scan_src2 += dest_Bpp - 3; | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |           scan_des += dest_Bpp - 3; | 
 |         } | 
 |         break; | 
 |       case FXDIB_Format::kArgb: | 
 |         for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |           uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_a = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           dest_b += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_g += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_r += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_a += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_a); | 
 |         } | 
 |         break; | 
 |       default: | 
 |         return; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | bool ProgressiveDecoder::BmpDetectImageTypeInBuffer( | 
 |     CFX_DIBAttribute* pAttribute) { | 
 |   std::unique_ptr<ProgressiveDecoderIface::Context> pBmpContext = | 
 |       BmpDecoder::StartDecode(this); | 
 |   BmpDecoder::Input(pBmpContext.get(), m_pCodecMemory); | 
 |  | 
 |   const std::vector<uint32_t>* palette; | 
 |   BmpDecoder::Status read_result = BmpDecoder::ReadHeader( | 
 |       pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom, | 
 |       &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute); | 
 |   while (read_result == BmpDecoder::Status::kContinue) { | 
 |     FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; | 
 |     if (!BmpReadMoreData(pBmpContext.get(), &error_status)) { | 
 |       m_status = error_status; | 
 |       return false; | 
 |     } | 
 |     read_result = BmpDecoder::ReadHeader( | 
 |         pBmpContext.get(), &m_SrcWidth, &m_SrcHeight, &m_BmpIsTopBottom, | 
 |         &m_SrcComponents, &m_SrcPaletteNumber, &palette, pAttribute); | 
 |   } | 
 |  | 
 |   if (read_result != BmpDecoder::Status::kSuccess) { | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |  | 
 |   FXDIB_Format format = FXDIB_Format::kInvalid; | 
 |   switch (m_SrcComponents) { | 
 |     case 1: | 
 |       m_SrcFormat = FXCodec_8bppRgb; | 
 |       format = FXDIB_Format::k8bppRgb; | 
 |       break; | 
 |     case 3: | 
 |       m_SrcFormat = FXCodec_Rgb; | 
 |       format = FXDIB_Format::kRgb; | 
 |       break; | 
 |     case 4: | 
 |       m_SrcFormat = FXCodec_Rgb32; | 
 |       format = FXDIB_Format::kRgb32; | 
 |       break; | 
 |     default: | 
 |       m_status = FXCODEC_STATUS::kError; | 
 |       return false; | 
 |   } | 
 |  | 
 |   // Set to 0 to make CalculatePitchAndSize() calculate it. | 
 |   constexpr uint32_t kNoPitch = 0; | 
 |   absl::optional<CFX_DIBitmap::PitchAndSize> needed_data = | 
 |       CFX_DIBitmap::CalculatePitchAndSize(m_SrcWidth, m_SrcHeight, format, | 
 |                                           kNoPitch); | 
 |   if (!needed_data.has_value()) { | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |  | 
 |   uint32_t available_data = pdfium::base::checked_cast<uint32_t>( | 
 |       m_pFile->GetSize() - m_offSet + | 
 |       BmpDecoder::GetAvailInput(pBmpContext.get())); | 
 |   if (needed_data.value().size > available_data) { | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |  | 
 |   m_SrcBPC = 8; | 
 |   m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight); | 
 |   m_pBmpContext = std::move(pBmpContext); | 
 |   if (m_SrcPaletteNumber) { | 
 |     m_SrcPalette.resize(m_SrcPaletteNumber); | 
 |     memcpy(m_SrcPalette.data(), palette->data(), | 
 |            m_SrcPaletteNumber * sizeof(FX_ARGB)); | 
 |   } else { | 
 |     m_SrcPalette.clear(); | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool ProgressiveDecoder::BmpReadMoreData( | 
 |     ProgressiveDecoderIface::Context* pContext, | 
 |     FXCODEC_STATUS* err_status) { | 
 |   return ReadMoreData(BmpProgressiveDecoder::GetInstance(), pContext, false, | 
 |                       err_status); | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::BmpStartDecode( | 
 |     const RetainPtr<CFX_DIBitmap>& pDIBitmap) { | 
 |   GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat); | 
 |   m_ScanlineSize = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents); | 
 |   m_DecodeBuf.resize(m_ScanlineSize); | 
 |   FXDIB_ResampleOptions options; | 
 |   options.bInterpolateBilinear = true; | 
 |   m_WeightHorz.CalculateWeights(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0, | 
 |                                 m_clipBox.Width(), options); | 
 |   m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height()); | 
 |   m_status = FXCODEC_STATUS::kDecodeToBeContinued; | 
 |   return m_status; | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::BmpContinueDecode() { | 
 |   BmpDecoder::Status read_res = BmpDecoder::LoadImage(m_pBmpContext.get()); | 
 |   while (read_res == BmpDecoder::Status::kContinue) { | 
 |     FXCODEC_STATUS error_status = FXCODEC_STATUS::kDecodeFinished; | 
 |     if (!BmpReadMoreData(m_pBmpContext.get(), &error_status)) { | 
 |       m_pDeviceBitmap = nullptr; | 
 |       m_pFile = nullptr; | 
 |       m_status = error_status; | 
 |       return m_status; | 
 |     } | 
 |     read_res = BmpDecoder::LoadImage(m_pBmpContext.get()); | 
 |   } | 
 |  | 
 |   m_pDeviceBitmap = nullptr; | 
 |   m_pFile = nullptr; | 
 |   m_status = read_res == BmpDecoder::Status::kSuccess | 
 |                  ? FXCODEC_STATUS::kDecodeFinished | 
 |                  : FXCODEC_STATUS::kError; | 
 |   return m_status; | 
 | } | 
 | #endif  // PDF_ENABLE_XFA_BMP | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_GIF | 
 | bool ProgressiveDecoder::GifReadMoreData(FXCODEC_STATUS* err_status) { | 
 |   if (!ReadMoreData(GifProgressiveDecoder::GetInstance(), m_pGifContext.get(), | 
 |                     m_InvalidateGifBuffer, err_status)) { | 
 |     return false; | 
 |   } | 
 |   m_InvalidateGifBuffer = false; | 
 |   return true; | 
 | } | 
 |  | 
 | bool ProgressiveDecoder::GifDetectImageTypeInBuffer() { | 
 |   m_pGifContext = GifDecoder::StartDecode(this); | 
 |   GifDecoder::Input(m_pGifContext.get(), m_pCodecMemory); | 
 |   m_SrcComponents = 1; | 
 |   GifDecoder::Status readResult = | 
 |       GifDecoder::ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight, | 
 |                              &m_GifPltNumber, &m_pGifPalette, &m_GifBgIndex); | 
 |   while (readResult == GifDecoder::Status::kUnfinished) { | 
 |     FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; | 
 |     if (!GifReadMoreData(&error_status)) { | 
 |       m_pGifContext = nullptr; | 
 |       m_status = error_status; | 
 |       return false; | 
 |     } | 
 |     readResult = | 
 |         GifDecoder::ReadHeader(m_pGifContext.get(), &m_SrcWidth, &m_SrcHeight, | 
 |                                &m_GifPltNumber, &m_pGifPalette, &m_GifBgIndex); | 
 |   } | 
 |   if (readResult == GifDecoder::Status::kSuccess) { | 
 |     m_SrcBPC = 8; | 
 |     m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight); | 
 |     return true; | 
 |   } | 
 |   m_pGifContext = nullptr; | 
 |   m_status = FXCODEC_STATUS::kError; | 
 |   return false; | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::GifStartDecode( | 
 |     const RetainPtr<CFX_DIBitmap>& pDIBitmap) { | 
 |   m_SrcFormat = FXCodec_8bppRgb; | 
 |   GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat); | 
 |   int scanline_size = FxAlignToBoundary<4>(m_SrcWidth); | 
 |   m_DecodeBuf.resize(scanline_size); | 
 |   FXDIB_ResampleOptions options; | 
 |   options.bInterpolateBilinear = true; | 
 |   m_WeightHorz.CalculateWeights(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0, | 
 |                                 m_clipBox.Width(), options); | 
 |   m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height()); | 
 |   m_FrameCur = 0; | 
 |   m_status = FXCODEC_STATUS::kDecodeToBeContinued; | 
 |   return m_status; | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::GifContinueDecode() { | 
 |   GifDecoder::Status readRes = | 
 |       GifDecoder::LoadFrame(m_pGifContext.get(), m_FrameCur); | 
 |   while (readRes == GifDecoder::Status::kUnfinished) { | 
 |     FXCODEC_STATUS error_status = FXCODEC_STATUS::kDecodeFinished; | 
 |     if (!GifReadMoreData(&error_status)) { | 
 |       m_pDeviceBitmap = nullptr; | 
 |       m_pFile = nullptr; | 
 |       m_status = error_status; | 
 |       return m_status; | 
 |     } | 
 |     readRes = GifDecoder::LoadFrame(m_pGifContext.get(), m_FrameCur); | 
 |   } | 
 |  | 
 |   if (readRes == GifDecoder::Status::kSuccess) { | 
 |     m_pDeviceBitmap = nullptr; | 
 |     m_pFile = nullptr; | 
 |     m_status = FXCODEC_STATUS::kDecodeFinished; | 
 |     return m_status; | 
 |   } | 
 |  | 
 |   m_pDeviceBitmap = nullptr; | 
 |   m_pFile = nullptr; | 
 |   m_status = FXCODEC_STATUS::kError; | 
 |   return m_status; | 
 | } | 
 |  | 
 | void ProgressiveDecoder::GifDoubleLineResampleVert( | 
 |     const RetainPtr<CFX_DIBitmap>& pDeviceBitmap, | 
 |     double scale_y, | 
 |     int dest_row) { | 
 |   int dest_Bpp = pDeviceBitmap->GetBPP() >> 3; | 
 |   uint32_t dest_ScanOffset = m_startX * dest_Bpp; | 
 |   int dest_top = m_startY; | 
 |   pdfium::base::CheckedNumeric<double> scale_y2 = scale_y; | 
 |   scale_y2 *= 2; | 
 |   FX_SAFE_INT32 check_dest_row_1 = dest_row; | 
 |   check_dest_row_1 -= scale_y2.ValueOrDie(); | 
 |   int dest_row_1 = check_dest_row_1.ValueOrDie(); | 
 |   dest_row_1 = std::max(dest_row_1, dest_top); | 
 |   for (; dest_row_1 < dest_row; dest_row_1++) { | 
 |     uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row_1) | 
 |                             .subspan(dest_ScanOffset) | 
 |                             .data(); | 
 |     PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top); | 
 |     const uint8_t* scan_src1 = | 
 |         pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) | 
 |             .subspan(dest_ScanOffset) | 
 |             .data(); | 
 |     const uint8_t* scan_src2 = | 
 |         pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) | 
 |             .subspan(dest_ScanOffset) | 
 |             .data(); | 
 |     switch (pDeviceBitmap->GetFormat()) { | 
 |       case FXDIB_Format::kInvalid: | 
 |       case FXDIB_Format::k1bppMask: | 
 |       case FXDIB_Format::k1bppRgb: | 
 |         return; | 
 |       case FXDIB_Format::k8bppMask: | 
 |       case FXDIB_Format::k8bppRgb: | 
 |         if (pDeviceBitmap->HasPalette()) | 
 |           return; | 
 |         for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |           uint32_t dest_g = 0; | 
 |           dest_g += pWeight->m_Weights[0] * (*scan_src1++); | 
 |           dest_g += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         } | 
 |         break; | 
 |       case FXDIB_Format::kRgb: | 
 |       case FXDIB_Format::kRgb32: | 
 |         for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |           uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           scan_src1 += dest_Bpp - 3; | 
 |           dest_b += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_g += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_r += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           scan_src2 += dest_Bpp - 3; | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |           scan_des += dest_Bpp - 3; | 
 |         } | 
 |         break; | 
 |       case FXDIB_Format::kArgb: | 
 |         for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |           uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_a = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           dest_b += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_g += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_r += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_a += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_a); | 
 |         } | 
 |         break; | 
 |       default: | 
 |         return; | 
 |     } | 
 |   } | 
 |   int dest_bottom = dest_top + m_sizeY - 1; | 
 |   if (dest_row + (int)(2 * scale_y) >= dest_bottom && | 
 |       dest_row + (int)scale_y < dest_bottom) { | 
 |     GifDoubleLineResampleVert(pDeviceBitmap, scale_y, dest_row + (int)scale_y); | 
 |   } | 
 | } | 
 | #endif  // PDF_ENABLE_XFA_GIF | 
 |  | 
 | bool ProgressiveDecoder::JpegReadMoreData(FXCODEC_STATUS* err_status) { | 
 |   return ReadMoreData(JpegProgressiveDecoder::GetInstance(), | 
 |                       m_pJpegContext.get(), false, err_status); | 
 | } | 
 |  | 
 | bool ProgressiveDecoder::JpegDetectImageTypeInBuffer( | 
 |     CFX_DIBAttribute* pAttribute) { | 
 |   m_pJpegContext = JpegProgressiveDecoder::Start(); | 
 |   if (!m_pJpegContext) { | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |   JpegProgressiveDecoder::GetInstance()->Input(m_pJpegContext.get(), | 
 |                                                m_pCodecMemory); | 
 |  | 
 |   // Setting jump marker before calling ReadHeader, since a longjmp to | 
 |   // the marker indicates a fatal error. | 
 |   if (setjmp(JpegProgressiveDecoder::GetJumpMark(m_pJpegContext.get())) == -1) { | 
 |     m_pJpegContext.reset(); | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |  | 
 |   int32_t readResult = JpegProgressiveDecoder::ReadHeader( | 
 |       m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight, &m_SrcComponents, | 
 |       pAttribute); | 
 |   while (readResult == 2) { | 
 |     FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; | 
 |     if (!JpegReadMoreData(&error_status)) { | 
 |       m_status = error_status; | 
 |       return false; | 
 |     } | 
 |     readResult = JpegProgressiveDecoder::ReadHeader( | 
 |         m_pJpegContext.get(), &m_SrcWidth, &m_SrcHeight, &m_SrcComponents, | 
 |         pAttribute); | 
 |   } | 
 |   if (!readResult) { | 
 |     m_SrcBPC = 8; | 
 |     m_clipBox = FX_RECT(0, 0, m_SrcWidth, m_SrcHeight); | 
 |     return true; | 
 |   } | 
 |   m_pJpegContext.reset(); | 
 |   m_status = FXCODEC_STATUS::kError; | 
 |   return false; | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::JpegStartDecode( | 
 |     const RetainPtr<CFX_DIBitmap>& pDIBitmap) { | 
 |   int down_scale = GetDownScale(); | 
 |   // Setting jump marker before calling StartScanLine, since a longjmp to | 
 |   // the marker indicates a fatal error. | 
 |   if (setjmp(JpegProgressiveDecoder::GetJumpMark(m_pJpegContext.get())) == -1) { | 
 |     m_pJpegContext.reset(); | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return FXCODEC_STATUS::kError; | 
 |   } | 
 |  | 
 |   bool startStatus = | 
 |       JpegProgressiveDecoder::StartScanline(m_pJpegContext.get(), down_scale); | 
 |   while (!startStatus) { | 
 |     FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; | 
 |     if (!JpegReadMoreData(&error_status)) { | 
 |       m_pDeviceBitmap = nullptr; | 
 |       m_pFile = nullptr; | 
 |       m_status = error_status; | 
 |       return m_status; | 
 |     } | 
 |  | 
 |     startStatus = | 
 |         JpegProgressiveDecoder::StartScanline(m_pJpegContext.get(), down_scale); | 
 |   } | 
 |   int scanline_size = (m_SrcWidth + down_scale - 1) / down_scale; | 
 |   scanline_size = FxAlignToBoundary<4>(scanline_size * m_SrcComponents); | 
 |   m_DecodeBuf.resize(scanline_size); | 
 |   FXDIB_ResampleOptions options; | 
 |   options.bInterpolateBilinear = true; | 
 |   m_WeightHorz.CalculateWeights(m_sizeX, 0, m_sizeX, m_clipBox.Width(), 0, | 
 |                                 m_clipBox.Width(), options); | 
 |   m_WeightVert.CalculateWeights(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::kDecodeToBeContinued; | 
 |   return m_status; | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::JpegContinueDecode() { | 
 |   // JpegModule* pJpegModule = m_pCodecMgr->GetJpegModule(); | 
 |   // Setting jump marker before calling ReadScanLine, since a longjmp to | 
 |   // the marker indicates a fatal error. | 
 |   if (setjmp(JpegProgressiveDecoder::GetJumpMark(m_pJpegContext.get())) == -1) { | 
 |     m_pJpegContext.reset(); | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return FXCODEC_STATUS::kError; | 
 |   } | 
 |  | 
 |   while (true) { | 
 |     bool readRes = JpegProgressiveDecoder::ReadScanline(m_pJpegContext.get(), | 
 |                                                         m_DecodeBuf.data()); | 
 |     while (!readRes) { | 
 |       FXCODEC_STATUS error_status = FXCODEC_STATUS::kDecodeFinished; | 
 |       if (!JpegReadMoreData(&error_status)) { | 
 |         m_pDeviceBitmap = nullptr; | 
 |         m_pFile = nullptr; | 
 |         m_status = error_status; | 
 |         return m_status; | 
 |       } | 
 |       readRes = JpegProgressiveDecoder::ReadScanline(m_pJpegContext.get(), | 
 |                                                      m_DecodeBuf.data()); | 
 |     } | 
 |     if (m_SrcFormat == FXCodec_Rgb) { | 
 |       int src_Bpp = (m_SrcFormat & 0xff) >> 3; | 
 |       RGB2BGR(m_DecodeBuf.data() + m_clipBox.left * src_Bpp, m_clipBox.Width()); | 
 |     } | 
 |     if (m_SrcRow >= m_clipBox.bottom) { | 
 |       m_pDeviceBitmap = nullptr; | 
 |       m_pFile = nullptr; | 
 |       m_status = FXCODEC_STATUS::kDecodeFinished; | 
 |       return m_status; | 
 |     } | 
 |     Resample(m_pDeviceBitmap, m_SrcRow, m_DecodeBuf.data(), m_SrcFormat); | 
 |     m_SrcRow++; | 
 |   } | 
 | } | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_PNG | 
 | void ProgressiveDecoder::PngOneOneMapResampleHorz( | 
 |     const RetainPtr<CFX_DIBitmap>& pDeviceBitmap, | 
 |     int32_t dest_line, | 
 |     uint8_t* src_scan, | 
 |     FXCodec_Format src_format) { | 
 |   int32_t src_Bpp = (m_SrcFormat & 0xff) >> 3; | 
 |   int32_t dest_Bpp = pDeviceBitmap->GetBPP() >> 3; | 
 |   int32_t src_left = m_clipBox.left; | 
 |   int32_t dest_left = m_startX; | 
 |   src_scan += src_left * src_Bpp; | 
 |   uint8_t* dest_scan = pDeviceBitmap->GetWritableScanline(dest_line) | 
 |                            .subspan(dest_left * dest_Bpp) | 
 |                            .data(); | 
 |   switch (pDeviceBitmap->GetFormat()) { | 
 |     case FXDIB_Format::k1bppMask: | 
 |     case FXDIB_Format::k1bppRgb: | 
 |       NOTREACHED(); | 
 |       return; | 
 |     case FXDIB_Format::k8bppMask: | 
 |     case FXDIB_Format::k8bppRgb: | 
 |       if (pDeviceBitmap->HasPalette()) | 
 |         return; | 
 |       for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |         PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col); | 
 |         uint32_t dest_g = | 
 |             pPixelWeights->m_Weights[0] * src_scan[pPixelWeights->m_SrcStart]; | 
 |         dest_g += | 
 |             pPixelWeights->m_Weights[1] * src_scan[pPixelWeights->m_SrcEnd]; | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |       } | 
 |       break; | 
 |     case FXDIB_Format::kRgb: | 
 |     case FXDIB_Format::kRgb32: | 
 |       for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |         PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col); | 
 |         const uint8_t* p = src_scan + pPixelWeights->m_SrcStart * src_Bpp; | 
 |         uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++); | 
 |         uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++); | 
 |         uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p); | 
 |         p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp; | 
 |         dest_b += pPixelWeights->m_Weights[1] * (*p++); | 
 |         dest_g += pPixelWeights->m_Weights[1] * (*p++); | 
 |         dest_r += pPixelWeights->m_Weights[1] * (*p); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |         dest_scan += dest_Bpp - 3; | 
 |       } | 
 |       break; | 
 |     case FXDIB_Format::kArgb: | 
 |       for (int32_t dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |         PixelWeight* pPixelWeights = m_WeightHorzOO.GetPixelWeight(dest_col); | 
 |         const uint8_t* p = src_scan + pPixelWeights->m_SrcStart * src_Bpp; | 
 |         uint32_t dest_b = pPixelWeights->m_Weights[0] * (*p++); | 
 |         uint32_t dest_g = pPixelWeights->m_Weights[0] * (*p++); | 
 |         uint32_t dest_r = pPixelWeights->m_Weights[0] * (*p++); | 
 |         uint32_t dest_a = pPixelWeights->m_Weights[0] * (*p); | 
 |         p = src_scan + pPixelWeights->m_SrcEnd * src_Bpp; | 
 |         dest_b += pPixelWeights->m_Weights[1] * (*p++); | 
 |         dest_g += pPixelWeights->m_Weights[1] * (*p++); | 
 |         dest_r += pPixelWeights->m_Weights[1] * (*p++); | 
 |         dest_a += pPixelWeights->m_Weights[1] * (*p); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_a); | 
 |       } | 
 |       break; | 
 |     default: | 
 |       return; | 
 |   } | 
 | } | 
 |  | 
 | bool ProgressiveDecoder::PngDetectImageTypeInBuffer( | 
 |     CFX_DIBAttribute* pAttribute) { | 
 |   m_pPngContext = PngDecoder::StartDecode(this); | 
 |   if (!m_pPngContext) { | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |   while (PngDecoder::ContinueDecode(m_pPngContext.get(), m_pCodecMemory, | 
 |                                     pAttribute)) { | 
 |     uint32_t remain_size = static_cast<uint32_t>(m_pFile->GetSize()) - m_offSet; | 
 |     uint32_t input_size = std::min<uint32_t>(remain_size, kBlockSize); | 
 |     if (input_size == 0) { | 
 |       m_pPngContext.reset(); | 
 |       m_status = FXCODEC_STATUS::kError; | 
 |       return false; | 
 |     } | 
 |     if (m_pCodecMemory && input_size > m_pCodecMemory->GetSize()) | 
 |       m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(input_size); | 
 |  | 
 |     if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(), m_offSet, | 
 |                                     input_size)) { | 
 |       m_status = FXCODEC_STATUS::kError; | 
 |       return false; | 
 |     } | 
 |     m_offSet += input_size; | 
 |   } | 
 |   m_pPngContext.reset(); | 
 |   if (m_SrcPassNumber == 0) { | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::PngStartDecode( | 
 |     const RetainPtr<CFX_DIBitmap>& pDIBitmap) { | 
 |   m_pPngContext = PngDecoder::StartDecode(this); | 
 |   if (!m_pPngContext) { | 
 |     m_pDeviceBitmap = nullptr; | 
 |     m_pFile = nullptr; | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return m_status; | 
 |   } | 
 |   m_offSet = 0; | 
 |   switch (m_pDeviceBitmap->GetFormat()) { | 
 |     case FXDIB_Format::k8bppMask: | 
 |     case FXDIB_Format::k8bppRgb: | 
 |       m_SrcComponents = 1; | 
 |       m_SrcFormat = FXCodec_8bppGray; | 
 |       break; | 
 |     case FXDIB_Format::kRgb: | 
 |       m_SrcComponents = 3; | 
 |       m_SrcFormat = FXCodec_Rgb; | 
 |       break; | 
 |     case FXDIB_Format::kRgb32: | 
 |     case FXDIB_Format::kArgb: | 
 |       m_SrcComponents = 4; | 
 |       m_SrcFormat = FXCodec_Argb; | 
 |       break; | 
 |     default: { | 
 |       m_pDeviceBitmap = nullptr; | 
 |       m_pFile = nullptr; | 
 |       m_status = FXCODEC_STATUS::kError; | 
 |       return m_status; | 
 |     } | 
 |   } | 
 |   GetTransMethod(m_pDeviceBitmap->GetFormat(), m_SrcFormat); | 
 |   int scanline_size = FxAlignToBoundary<4>(m_SrcWidth * m_SrcComponents); | 
 |   m_DecodeBuf.resize(scanline_size); | 
 |   m_WeightHorzOO.CalculateWeights(m_sizeX, m_clipBox.Width()); | 
 |   m_WeightVert.CalculateWeights(m_sizeY, m_clipBox.Height()); | 
 |   m_status = FXCODEC_STATUS::kDecodeToBeContinued; | 
 |   return m_status; | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::PngContinueDecode() { | 
 |   while (true) { | 
 |     uint32_t remain_size = (uint32_t)m_pFile->GetSize() - m_offSet; | 
 |     uint32_t input_size = std::min<uint32_t>(remain_size, kBlockSize); | 
 |     if (input_size == 0) { | 
 |       m_pPngContext.reset(); | 
 |       m_pDeviceBitmap = nullptr; | 
 |       m_pFile = nullptr; | 
 |       m_status = FXCODEC_STATUS::kDecodeFinished; | 
 |       return m_status; | 
 |     } | 
 |     if (m_pCodecMemory && input_size > m_pCodecMemory->GetSize()) | 
 |       m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(input_size); | 
 |  | 
 |     bool bResult = m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(), | 
 |                                               m_offSet, input_size); | 
 |     if (!bResult) { | 
 |       m_pDeviceBitmap = nullptr; | 
 |       m_pFile = nullptr; | 
 |       m_status = FXCODEC_STATUS::kError; | 
 |       return m_status; | 
 |     } | 
 |     m_offSet += input_size; | 
 |     bResult = PngDecoder::ContinueDecode(m_pPngContext.get(), m_pCodecMemory, | 
 |                                          nullptr); | 
 |     if (!bResult) { | 
 |       m_pDeviceBitmap = nullptr; | 
 |       m_pFile = nullptr; | 
 |       m_status = FXCODEC_STATUS::kError; | 
 |       return m_status; | 
 |     } | 
 |   } | 
 | } | 
 | #endif  // PDF_ENABLE_XFA_PNG | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_TIFF | 
 | bool ProgressiveDecoder::TiffDetectImageTypeFromFile( | 
 |     CFX_DIBAttribute* pAttribute) { | 
 |   m_pTiffContext = TiffDecoder::CreateDecoder(m_pFile); | 
 |   if (!m_pTiffContext) { | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |   int32_t dummy_bpc; | 
 |   bool ret = TiffDecoder::LoadFrameInfo(m_pTiffContext.get(), 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) { | 
 |     m_pTiffContext.reset(); | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::TiffContinueDecode() { | 
 |   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 = TiffDecoder::Decode(m_pTiffContext.get(), m_pDeviceBitmap); | 
 |     m_pDeviceBitmap = nullptr; | 
 |     m_pFile = nullptr; | 
 |     if (!ret) { | 
 |       m_status = FXCODEC_STATUS::kError; | 
 |       return m_status; | 
 |     } | 
 |     m_status = FXCODEC_STATUS::kDecodeFinished; | 
 |     return m_status; | 
 |   } | 
 |  | 
 |   auto pDIBitmap = pdfium::MakeRetain<CFX_DIBitmap>(); | 
 |   pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Format::kArgb); | 
 |   if (!pDIBitmap->GetBuffer()) { | 
 |     m_pDeviceBitmap = nullptr; | 
 |     m_pFile = nullptr; | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return m_status; | 
 |   } | 
 |   ret = TiffDecoder::Decode(m_pTiffContext.get(), pDIBitmap); | 
 |   if (!ret) { | 
 |     m_pDeviceBitmap = nullptr; | 
 |     m_pFile = nullptr; | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return m_status; | 
 |   } | 
 |   RetainPtr<CFX_DIBitmap> pClipBitmap = | 
 |       (m_clipBox.left == 0 && m_clipBox.top == 0 && | 
 |        m_clipBox.right == m_SrcWidth && m_clipBox.bottom == m_SrcHeight) | 
 |           ? pDIBitmap | 
 |           : pDIBitmap->ClipTo(m_clipBox); | 
 |   if (!pClipBitmap) { | 
 |     m_pDeviceBitmap = nullptr; | 
 |     m_pFile = nullptr; | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return m_status; | 
 |   } | 
 |   RetainPtr<CFX_DIBitmap> pFormatBitmap; | 
 |   switch (m_pDeviceBitmap->GetFormat()) { | 
 |     case FXDIB_Format::k8bppRgb: | 
 |       pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>(); | 
 |       pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), | 
 |                             FXDIB_Format::k8bppRgb); | 
 |       break; | 
 |     case FXDIB_Format::k8bppMask: | 
 |       pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>(); | 
 |       pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), | 
 |                             FXDIB_Format::k8bppMask); | 
 |       break; | 
 |     case FXDIB_Format::kRgb: | 
 |       pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>(); | 
 |       pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), | 
 |                             FXDIB_Format::kRgb); | 
 |       break; | 
 |     case FXDIB_Format::kRgb32: | 
 |       pFormatBitmap = pdfium::MakeRetain<CFX_DIBitmap>(); | 
 |       pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), | 
 |                             FXDIB_Format::kRgb32); | 
 |       break; | 
 |     case FXDIB_Format::kArgb: | 
 |       pFormatBitmap = pClipBitmap; | 
 |       break; | 
 |     default: | 
 |       break; | 
 |   } | 
 |   switch (m_pDeviceBitmap->GetFormat()) { | 
 |     case FXDIB_Format::k8bppRgb: | 
 |     case FXDIB_Format::k8bppMask: { | 
 |       for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) { | 
 |         const uint8_t* src_line = pClipBitmap->GetScanline(row).data(); | 
 |         uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row).data(); | 
 |         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; | 
 |           *dest_line++ = FXRGB2GRAY(r, g, b); | 
 |           src_line += 4; | 
 |         } | 
 |       } | 
 |     } break; | 
 |     case FXDIB_Format::kRgb: | 
 |     case FXDIB_Format::kRgb32: { | 
 |       int32_t desBpp = | 
 |           (m_pDeviceBitmap->GetFormat() == FXDIB_Format::kRgb) ? 3 : 4; | 
 |       for (int32_t row = 0; row < pClipBitmap->GetHeight(); row++) { | 
 |         const uint8_t* src_line = pClipBitmap->GetScanline(row).data(); | 
 |         uint8_t* dest_line = pFormatBitmap->GetWritableScanline(row).data(); | 
 |         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; | 
 |           *dest_line++ = b; | 
 |           *dest_line++ = g; | 
 |           *dest_line++ = r; | 
 |           dest_line += desBpp - 3; | 
 |           src_line += 4; | 
 |         } | 
 |       } | 
 |     } break; | 
 |     default: | 
 |       break; | 
 |   } | 
 |   if (!pFormatBitmap) { | 
 |     m_pDeviceBitmap = nullptr; | 
 |     m_pFile = nullptr; | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return m_status; | 
 |   } | 
 |  | 
 |   FXDIB_ResampleOptions options; | 
 |   options.bInterpolateBilinear = true; | 
 |   RetainPtr<CFX_DIBitmap> pStrechBitmap = | 
 |       pFormatBitmap->StretchTo(m_sizeX, m_sizeY, options, nullptr); | 
 |   pFormatBitmap = nullptr; | 
 |   if (!pStrechBitmap) { | 
 |     m_pDeviceBitmap = nullptr; | 
 |     m_pFile = nullptr; | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     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::kDecodeFinished; | 
 |   return m_status; | 
 | } | 
 | #endif  // PDF_ENABLE_XFA_TIFF | 
 |  | 
 | bool ProgressiveDecoder::DetectImageType(FXCODEC_IMAGE_TYPE imageType, | 
 |                                          CFX_DIBAttribute* pAttribute) { | 
 | #ifdef PDF_ENABLE_XFA_TIFF | 
 |   if (imageType == FXCODEC_IMAGE_TIFF) | 
 |     return TiffDetectImageTypeFromFile(pAttribute); | 
 | #endif  // PDF_ENABLE_XFA_TIFF | 
 |  | 
 |   size_t size = pdfium::base::checked_cast<size_t>( | 
 |       std::min<FX_FILESIZE>(m_pFile->GetSize(), kBlockSize)); | 
 |   m_pCodecMemory = pdfium::MakeRetain<CFX_CodecMemory>(size); | 
 |   m_offSet = 0; | 
 |   if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer(), m_offSet, | 
 |                                   size)) { | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |   m_offSet += size; | 
 |  | 
 |   if (imageType == FXCODEC_IMAGE_JPG) | 
 |     return JpegDetectImageTypeInBuffer(pAttribute); | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_BMP | 
 |   if (imageType == FXCODEC_IMAGE_BMP) | 
 |     return BmpDetectImageTypeInBuffer(pAttribute); | 
 | #endif  // PDF_ENABLE_XFA_BMP | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_GIF | 
 |   if (imageType == FXCODEC_IMAGE_GIF) | 
 |     return GifDetectImageTypeInBuffer(); | 
 | #endif  // PDF_ENABLE_XFA_GIF | 
 |  | 
 | #ifdef PDF_ENABLE_XFA_PNG | 
 |   if (imageType == FXCODEC_IMAGE_PNG) | 
 |     return PngDetectImageTypeInBuffer(pAttribute); | 
 | #endif  // PDF_ENABLE_XFA_PNG | 
 |  | 
 |   m_status = FXCODEC_STATUS::kError; | 
 |   return false; | 
 | } | 
 |  | 
 | bool ProgressiveDecoder::ReadMoreData( | 
 |     ProgressiveDecoderIface* pModule, | 
 |     ProgressiveDecoderIface::Context* pContext, | 
 |     bool invalidate_buffer, | 
 |     FXCODEC_STATUS* err_status) { | 
 |   // Check for EOF. | 
 |   if (m_offSet >= static_cast<uint32_t>(m_pFile->GetSize())) | 
 |     return false; | 
 |  | 
 |   // Try to get whatever remains. | 
 |   uint32_t dwBytesToFetchFromFile = | 
 |       pdfium::base::checked_cast<uint32_t>(m_pFile->GetSize() - m_offSet); | 
 |  | 
 |   // Figure out if the codec stopped processing midway through the buffer. | 
 |   size_t dwUnconsumed = 0; | 
 |   if (!invalidate_buffer) { | 
 |     FX_SAFE_SIZE_T avail_input = pModule->GetAvailInput(pContext); | 
 |     if (!avail_input.IsValid()) | 
 |       return false; | 
 |     dwUnconsumed = avail_input.ValueOrDie(); | 
 |   } | 
 |  | 
 |   if (dwUnconsumed == m_pCodecMemory->GetSize()) { | 
 |     // Codec couldn't make any progress against the bytes in the buffer. | 
 |     // Increase the buffer size so that there might be enough contiguous | 
 |     // bytes to allow whatever operation is having difficulty to succeed. | 
 |     dwBytesToFetchFromFile = | 
 |         std::min<uint32_t>(dwBytesToFetchFromFile, kBlockSize); | 
 |     size_t dwNewSize = m_pCodecMemory->GetSize() + dwBytesToFetchFromFile; | 
 |     if (!m_pCodecMemory->TryResize(dwNewSize)) { | 
 |       *err_status = FXCODEC_STATUS::kError; | 
 |       return false; | 
 |     } | 
 |   } else { | 
 |     size_t dwConsumed = m_pCodecMemory->GetSize() - dwUnconsumed; | 
 |     m_pCodecMemory->Consume(dwConsumed); | 
 |     dwBytesToFetchFromFile = pdfium::base::checked_cast<uint32_t>( | 
 |         std::min<size_t>(dwBytesToFetchFromFile, dwConsumed)); | 
 |   } | 
 |  | 
 |   // Append new data past the bytes not yet processed by the codec. | 
 |   if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBuffer() + dwUnconsumed, | 
 |                                   m_offSet, dwBytesToFetchFromFile)) { | 
 |     *err_status = FXCODEC_STATUS::kError; | 
 |     return false; | 
 |   } | 
 |   m_offSet += dwBytesToFetchFromFile; | 
 |   return pModule->Input(pContext, m_pCodecMemory); | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::LoadImageInfo( | 
 |     const RetainPtr<IFX_SeekableReadStream>& pFile, | 
 |     FXCODEC_IMAGE_TYPE imageType, | 
 |     CFX_DIBAttribute* pAttribute, | 
 |     bool bSkipImageTypeCheck) { | 
 |   DCHECK(pAttribute); | 
 |  | 
 |   switch (m_status) { | 
 |     case FXCODEC_STATUS::kFrameReady: | 
 |     case FXCODEC_STATUS::kFrameToBeContinued: | 
 |     case FXCODEC_STATUS::kDecodeReady: | 
 |     case FXCODEC_STATUS::kDecodeToBeContinued: | 
 |       return FXCODEC_STATUS::kError; | 
 |     default: | 
 |       break; | 
 |   } | 
 |   if (!pFile) { | 
 |     m_status = FXCODEC_STATUS::kError; | 
 |     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(); | 
 |   m_startX = m_startY = 0; | 
 |   m_sizeX = m_sizeY = 0; | 
 |   m_SrcPassNumber = 0; | 
 |   if (imageType != FXCODEC_IMAGE_UNKNOWN && | 
 |       DetectImageType(imageType, pAttribute)) { | 
 |     m_imageType = imageType; | 
 |     m_status = FXCODEC_STATUS::kFrameReady; | 
 |     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_UNKNOWN + 1; type < FXCODEC_IMAGE_MAX; type++) { | 
 |     if (DetectImageType(static_cast<FXCODEC_IMAGE_TYPE>(type), pAttribute)) { | 
 |       m_imageType = static_cast<FXCODEC_IMAGE_TYPE>(type); | 
 |       m_status = FXCODEC_STATUS::kFrameReady; | 
 |       return m_status; | 
 |     } | 
 |   } | 
 |   m_status = FXCODEC_STATUS::kError; | 
 |   m_pFile = nullptr; | 
 |   return m_status; | 
 | } | 
 |  | 
 | void ProgressiveDecoder::SetClipBox(FX_RECT* clip) { | 
 |   if (m_status != FXCODEC_STATUS::kFrameReady) | 
 |     return; | 
 |  | 
 |   if (clip->IsEmpty()) { | 
 |     m_clipBox = FX_RECT(); | 
 |     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(); | 
 |     return; | 
 |   } | 
 |   m_clipBox = *clip; | 
 | } | 
 |  | 
 | int ProgressiveDecoder::GetDownScale() { | 
 |   int down_scale = 1; | 
 |   int ratio_w = m_clipBox.Width() / m_sizeX; | 
 |   int ratio_h = m_clipBox.Height() / m_sizeY; | 
 |   int ratio = std::min(ratio_w, ratio_h); | 
 |   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; | 
 |   return down_scale; | 
 | } | 
 |  | 
 | void ProgressiveDecoder::GetTransMethod(FXDIB_Format dest_format, | 
 |                                         FXCodec_Format src_format) { | 
 |   switch (dest_format) { | 
 |     case FXDIB_Format::k1bppMask: | 
 |     case FXDIB_Format::k1bppRgb: { | 
 |       switch (src_format) { | 
 |         case FXCodec_1bppGray: | 
 |           m_TransMethod = 0; | 
 |           break; | 
 |         default: | 
 |           m_TransMethod = -1; | 
 |       } | 
 |     } break; | 
 |     case FXDIB_Format::k8bppMask: | 
 |     case FXDIB_Format::k8bppRgb: { | 
 |       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_Format::kRgb: { | 
 |       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_Format::kRgb32: | 
 |     case FXDIB_Format::kArgb: { | 
 |       switch (src_format) { | 
 |         case FXCodec_1bppGray: | 
 |           m_TransMethod = 6; | 
 |           break; | 
 |         case FXCodec_8bppGray: | 
 |           m_TransMethod = 7; | 
 |           break; | 
 |         case FXCodec_1bppRgb: | 
 |         case FXCodec_8bppRgb: | 
 |           if (dest_format == FXDIB_Format::kArgb) { | 
 |             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 ProgressiveDecoder::ResampleScanline( | 
 |     const RetainPtr<CFX_DIBitmap>& pDeviceBitmap, | 
 |     int dest_line, | 
 |     uint8_t* src_scan, | 
 |     FXCodec_Format src_format) { | 
 |   int src_left = m_clipBox.left; | 
 |   int dest_left = m_startX; | 
 |   uint8_t* dest_scan = pDeviceBitmap->GetWritableScanline(dest_line).data(); | 
 |   int src_bytes_per_pixel = (src_format & 0xff) / 8; | 
 |   int dest_bytes_per_pixel = pDeviceBitmap->GetBPP() / 8; | 
 |   src_scan += src_left * src_bytes_per_pixel; | 
 |   dest_scan += dest_left * dest_bytes_per_pixel; | 
 |   for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |     PixelWeight* pPixelWeights = m_WeightHorz.GetPixelWeight(dest_col); | 
 |     switch (m_TransMethod) { | 
 |       case -1: | 
 |         return; | 
 |       case 0: | 
 |         return; | 
 |       case 1: | 
 |         return; | 
 |       case 2: { | 
 |         uint32_t dest_g = 0; | 
 |         for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |              j++) { | 
 |           uint32_t pixel_weight = | 
 |               pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |           dest_g += pixel_weight * src_scan[j]; | 
 |         } | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |       } break; | 
 |       case 3: { | 
 |         uint32_t dest_r = 0; | 
 |         uint32_t dest_g = 0; | 
 |         uint32_t dest_b = 0; | 
 |         for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |              j++) { | 
 |           uint32_t pixel_weight = | 
 |               pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |           uint32_t argb = m_SrcPalette[src_scan[j]]; | 
 |           dest_r += pixel_weight * FXARGB_R(argb); | 
 |           dest_g += pixel_weight * FXARGB_G(argb); | 
 |           dest_b += pixel_weight * FXARGB_B(argb); | 
 |         } | 
 |         *dest_scan++ = static_cast<uint8_t>( | 
 |             FXRGB2GRAY(CStretchEngine::PixelFromFixed(dest_r), | 
 |                        CStretchEngine::PixelFromFixed(dest_g), | 
 |                        CStretchEngine::PixelFromFixed(dest_b))); | 
 |       } break; | 
 |       case 4: { | 
 |         uint32_t dest_b = 0; | 
 |         uint32_t dest_g = 0; | 
 |         uint32_t dest_r = 0; | 
 |         for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |              j++) { | 
 |           uint32_t pixel_weight = | 
 |               pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |           const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel; | 
 |           dest_b += pixel_weight * (*src_pixel++); | 
 |           dest_g += pixel_weight * (*src_pixel++); | 
 |           dest_r += pixel_weight * (*src_pixel); | 
 |         } | 
 |         *dest_scan++ = static_cast<uint8_t>( | 
 |             FXRGB2GRAY(CStretchEngine::PixelFromFixed(dest_r), | 
 |                        CStretchEngine::PixelFromFixed(dest_g), | 
 |                        CStretchEngine::PixelFromFixed(dest_b))); | 
 |       } break; | 
 |       case 5: { | 
 |         uint32_t dest_b = 0; | 
 |         uint32_t dest_g = 0; | 
 |         uint32_t dest_r = 0; | 
 |         for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |              j++) { | 
 |           uint32_t pixel_weight = | 
 |               pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |           const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel; | 
 |           uint8_t src_b = 0; | 
 |           uint8_t src_g = 0; | 
 |           uint8_t src_r = 0; | 
 |           std::tie(src_r, src_g, src_b) = | 
 |               AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1], | 
 |                                  255 - src_pixel[2], 255 - src_pixel[3]); | 
 |           dest_b += pixel_weight * src_b; | 
 |           dest_g += pixel_weight * src_g; | 
 |           dest_r += pixel_weight * src_r; | 
 |         } | 
 |         *dest_scan++ = static_cast<uint8_t>( | 
 |             FXRGB2GRAY(CStretchEngine::PixelFromFixed(dest_r), | 
 |                        CStretchEngine::PixelFromFixed(dest_g), | 
 |                        CStretchEngine::PixelFromFixed(dest_b))); | 
 |       } break; | 
 |       case 6: | 
 |         return; | 
 |       case 7: { | 
 |         uint32_t dest_g = 0; | 
 |         for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |              j++) { | 
 |           uint32_t pixel_weight = | 
 |               pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |           dest_g += pixel_weight * src_scan[j]; | 
 |         } | 
 |         memset(dest_scan, CStretchEngine::PixelFromFixed(dest_g), 3); | 
 |         dest_scan += dest_bytes_per_pixel; | 
 |       } break; | 
 |       case 8: { | 
 |         uint32_t dest_r = 0; | 
 |         uint32_t dest_g = 0; | 
 |         uint32_t dest_b = 0; | 
 |         for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |              j++) { | 
 |           uint32_t pixel_weight = | 
 |               pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |           uint32_t argb = m_SrcPalette[src_scan[j]]; | 
 |           dest_r += pixel_weight * FXARGB_R(argb); | 
 |           dest_g += pixel_weight * FXARGB_G(argb); | 
 |           dest_b += pixel_weight * FXARGB_B(argb); | 
 |         } | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |         dest_scan += dest_bytes_per_pixel - 3; | 
 |       } break; | 
 |       case 12: { | 
 | #ifdef PDF_ENABLE_XFA_BMP | 
 |         if (m_pBmpContext) { | 
 |           uint32_t dest_r = 0; | 
 |           uint32_t dest_g = 0; | 
 |           uint32_t dest_b = 0; | 
 |           for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |                j++) { | 
 |             uint32_t pixel_weight = | 
 |                 pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |             uint32_t argb = m_SrcPalette[src_scan[j]]; | 
 |             dest_r += pixel_weight * FXARGB_R(argb); | 
 |             dest_g += pixel_weight * FXARGB_G(argb); | 
 |             dest_b += pixel_weight * FXARGB_B(argb); | 
 |           } | 
 |           *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |           *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |           *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |           *dest_scan++ = 0xFF; | 
 |           break; | 
 |         } | 
 | #endif  // PDF_ENABLE_XFA_BMP | 
 |         uint32_t dest_a = 0; | 
 |         uint32_t dest_r = 0; | 
 |         uint32_t dest_g = 0; | 
 |         uint32_t dest_b = 0; | 
 |         for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |              j++) { | 
 |           uint32_t pixel_weight = | 
 |               pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |           unsigned long argb = m_SrcPalette[src_scan[j]]; | 
 |           dest_a += pixel_weight * FXARGB_A(argb); | 
 |           dest_r += pixel_weight * FXARGB_R(argb); | 
 |           dest_g += pixel_weight * FXARGB_G(argb); | 
 |           dest_b += pixel_weight * FXARGB_B(argb); | 
 |         } | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_a); | 
 |       } break; | 
 |       case 9: { | 
 |         uint32_t dest_b = 0; | 
 |         uint32_t dest_g = 0; | 
 |         uint32_t dest_r = 0; | 
 |         for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |              j++) { | 
 |           uint32_t pixel_weight = | 
 |               pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |           const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel; | 
 |           dest_b += pixel_weight * (*src_pixel++); | 
 |           dest_g += pixel_weight * (*src_pixel++); | 
 |           dest_r += pixel_weight * (*src_pixel); | 
 |         } | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |         dest_scan += dest_bytes_per_pixel - 3; | 
 |       } break; | 
 |       case 10: { | 
 |         uint32_t dest_b = 0; | 
 |         uint32_t dest_g = 0; | 
 |         uint32_t dest_r = 0; | 
 |         for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |              j++) { | 
 |           uint32_t pixel_weight = | 
 |               pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |           const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel; | 
 |           uint8_t src_b = 0; | 
 |           uint8_t src_g = 0; | 
 |           uint8_t src_r = 0; | 
 |           std::tie(src_r, src_g, src_b) = | 
 |               AdobeCMYK_to_sRGB1(255 - src_pixel[0], 255 - src_pixel[1], | 
 |                                  255 - src_pixel[2], 255 - src_pixel[3]); | 
 |           dest_b += pixel_weight * src_b; | 
 |           dest_g += pixel_weight * src_g; | 
 |           dest_r += pixel_weight * src_r; | 
 |         } | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |         dest_scan += dest_bytes_per_pixel - 3; | 
 |       } break; | 
 |       case 11: { | 
 |         uint32_t dest_alpha = 0; | 
 |         uint32_t dest_r = 0; | 
 |         uint32_t dest_g = 0; | 
 |         uint32_t dest_b = 0; | 
 |         for (int j = pPixelWeights->m_SrcStart; j <= pPixelWeights->m_SrcEnd; | 
 |              j++) { | 
 |           uint32_t pixel_weight = | 
 |               pPixelWeights->m_Weights[j - pPixelWeights->m_SrcStart]; | 
 |           const uint8_t* src_pixel = src_scan + j * src_bytes_per_pixel; | 
 |           pixel_weight = pixel_weight * src_pixel[3] / 255; | 
 |           dest_b += pixel_weight * (*src_pixel++); | 
 |           dest_g += pixel_weight * (*src_pixel++); | 
 |           dest_r += pixel_weight * (*src_pixel); | 
 |           dest_alpha += pixel_weight; | 
 |         } | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |         *dest_scan++ = CStretchEngine::PixelFromFixed(dest_alpha * 255); | 
 |       } break; | 
 |       default: | 
 |         return; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void ProgressiveDecoder::ResampleVert( | 
 |     const RetainPtr<CFX_DIBitmap>& pDeviceBitmap, | 
 |     double scale_y, | 
 |     int dest_row) { | 
 |   int dest_Bpp = pDeviceBitmap->GetBPP() >> 3; | 
 |   uint32_t dest_ScanOffset = m_startX * dest_Bpp; | 
 |   int dest_top = m_startY; | 
 |   FX_SAFE_INT32 check_dest_row_1 = dest_row; | 
 |   check_dest_row_1 -= pdfium::base::checked_cast<int>(scale_y); | 
 |   int dest_row_1 = check_dest_row_1.ValueOrDie(); | 
 |   if (dest_row_1 < dest_top) { | 
 |     int dest_bottom = dest_top + m_sizeY; | 
 |     if (dest_row + (int)scale_y >= dest_bottom - 1) { | 
 |       const uint8_t* scan_src = | 
 |           pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset).data(); | 
 |       while (++dest_row < dest_bottom) { | 
 |         uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row) | 
 |                                 .subspan(dest_ScanOffset) | 
 |                                 .data(); | 
 |         uint32_t size = m_sizeX * dest_Bpp; | 
 |         memmove(scan_des, scan_src, size); | 
 |       } | 
 |     } | 
 |     return; | 
 |   } | 
 |   for (; dest_row_1 < dest_row; dest_row_1++) { | 
 |     uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row_1) | 
 |                             .subspan(dest_ScanOffset) | 
 |                             .data(); | 
 |     PixelWeight* pWeight = m_WeightVert.GetPixelWeight(dest_row_1 - dest_top); | 
 |     const uint8_t* scan_src1 = | 
 |         pDeviceBitmap->GetScanline(pWeight->m_SrcStart + dest_top) | 
 |             .subspan(dest_ScanOffset) | 
 |             .data(); | 
 |     const uint8_t* scan_src2 = | 
 |         pDeviceBitmap->GetScanline(pWeight->m_SrcEnd + dest_top) | 
 |             .subspan(dest_ScanOffset) | 
 |             .data(); | 
 |     switch (pDeviceBitmap->GetFormat()) { | 
 |       case FXDIB_Format::kInvalid: | 
 |       case FXDIB_Format::k1bppMask: | 
 |       case FXDIB_Format::k1bppRgb: | 
 |         return; | 
 |       case FXDIB_Format::k8bppMask: | 
 |       case FXDIB_Format::k8bppRgb: | 
 |         if (pDeviceBitmap->HasPalette()) | 
 |           return; | 
 |         for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |           uint32_t dest_g = 0; | 
 |           dest_g += pWeight->m_Weights[0] * (*scan_src1++); | 
 |           dest_g += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |         } | 
 |         break; | 
 |       case FXDIB_Format::kRgb: | 
 |       case FXDIB_Format::kRgb32: | 
 |         for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |           uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           scan_src1 += dest_Bpp - 3; | 
 |           dest_b += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_g += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_r += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           scan_src2 += dest_Bpp - 3; | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |           scan_des += dest_Bpp - 3; | 
 |         } | 
 |         break; | 
 |       case FXDIB_Format::kArgb: | 
 |         for (int dest_col = 0; dest_col < m_sizeX; dest_col++) { | 
 |           uint32_t dest_b = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_g = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_r = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           uint32_t dest_a = pWeight->m_Weights[0] * (*scan_src1++); | 
 |           dest_b += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_g += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_r += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           dest_a += pWeight->m_Weights[1] * (*scan_src2++); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_b); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_g); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_r); | 
 |           *scan_des++ = CStretchEngine::PixelFromFixed(dest_a); | 
 |         } | 
 |         break; | 
 |       default: | 
 |         return; | 
 |     } | 
 |   } | 
 |   int dest_bottom = dest_top + m_sizeY; | 
 |   if (dest_row + (int)scale_y >= dest_bottom - 1) { | 
 |     const uint8_t* scan_src = | 
 |         pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset).data(); | 
 |     while (++dest_row < dest_bottom) { | 
 |       uint8_t* scan_des = pDeviceBitmap->GetWritableScanline(dest_row) | 
 |                               .subspan(dest_ScanOffset) | 
 |                               .data(); | 
 |       uint32_t size = m_sizeX * dest_Bpp; | 
 |       memmove(scan_des, scan_src, size); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void ProgressiveDecoder::Resample(const RetainPtr<CFX_DIBitmap>& pDeviceBitmap, | 
 |                                   int32_t src_line, | 
 |                                   uint8_t* src_scan, | 
 |                                   FXCodec_Format src_format) { | 
 |   int src_top = m_clipBox.top; | 
 |   int dest_top = m_startY; | 
 |   int src_height = m_clipBox.Height(); | 
 |   int dest_height = m_sizeY; | 
 |   if (src_line >= src_top) { | 
 |     double scale_y = static_cast<double>(dest_height) / src_height; | 
 |     int src_row = src_line - src_top; | 
 |     int dest_row = (int)(src_row * scale_y) + dest_top; | 
 |     if (dest_row >= dest_top + dest_height) | 
 |       return; | 
 |  | 
 |     ResampleScanline(pDeviceBitmap, dest_row, m_DecodeBuf.data(), src_format); | 
 |     if (scale_y > 1.0) | 
 |       ResampleVert(pDeviceBitmap, scale_y, dest_row); | 
 |   } | 
 | } | 
 |  | 
 | std::pair<FXCODEC_STATUS, size_t> ProgressiveDecoder::GetFrames() { | 
 |   if (!(m_status == FXCODEC_STATUS::kFrameReady || | 
 |         m_status == FXCODEC_STATUS::kFrameToBeContinued)) { | 
 |     return {FXCODEC_STATUS::kError, 0}; | 
 |   } | 
 |  | 
 |   switch (m_imageType) { | 
 | #ifdef PDF_ENABLE_XFA_BMP | 
 |     case FXCODEC_IMAGE_BMP: | 
 | #endif  // PDF_ENABLE_XFA_BMP | 
 |     case FXCODEC_IMAGE_JPG: | 
 | #ifdef PDF_ENABLE_XFA_PNG | 
 |     case FXCODEC_IMAGE_PNG: | 
 | #endif  // PDF_ENABLE_XFA_PNG | 
 | #ifdef PDF_ENABLE_XFA_TIFF | 
 |     case FXCODEC_IMAGE_TIFF: | 
 | #endif  // PDF_ENABLE_XFA_TIFF | 
 |       m_FrameNumber = 1; | 
 |       m_status = FXCODEC_STATUS::kDecodeReady; | 
 |       return {m_status, 1}; | 
 | #ifdef PDF_ENABLE_XFA_GIF | 
 |     case FXCODEC_IMAGE_GIF: { | 
 |       while (true) { | 
 |         GifDecoder::Status readResult; | 
 |         std::tie(readResult, m_FrameNumber) = | 
 |             GifDecoder::LoadFrameInfo(m_pGifContext.get()); | 
 |         while (readResult == GifDecoder::Status::kUnfinished) { | 
 |           FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; | 
 |           if (!GifReadMoreData(&error_status)) | 
 |             return {error_status, 0}; | 
 |  | 
 |           std::tie(readResult, m_FrameNumber) = | 
 |               GifDecoder::LoadFrameInfo(m_pGifContext.get()); | 
 |         } | 
 |         if (readResult == GifDecoder::Status::kSuccess) { | 
 |           m_status = FXCODEC_STATUS::kDecodeReady; | 
 |           return {m_status, m_FrameNumber}; | 
 |         } | 
 |         m_pGifContext = nullptr; | 
 |         m_status = FXCODEC_STATUS::kError; | 
 |         return {m_status, 0}; | 
 |       } | 
 |     } | 
 | #endif  // PDF_ENABLE_XFA_GIF | 
 |     default: | 
 |       return {FXCODEC_STATUS::kError, 0}; | 
 |   } | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::StartDecode( | 
 |     const RetainPtr<CFX_DIBitmap>& pDIBitmap, | 
 |     int start_x, | 
 |     int start_y, | 
 |     int size_x, | 
 |     int size_y) { | 
 |   if (m_status != FXCODEC_STATUS::kDecodeReady) | 
 |     return FXCODEC_STATUS::kError; | 
 |  | 
 |   if (!pDIBitmap || pDIBitmap->GetBPP() < 8 || m_FrameNumber == 0) | 
 |     return FXCODEC_STATUS::kError; | 
 |  | 
 |   m_pDeviceBitmap = pDIBitmap; | 
 |   if (m_clipBox.IsEmpty()) | 
 |     return FXCODEC_STATUS::kError; | 
 |   if (size_x <= 0 || size_x > 65535 || size_y <= 0 || size_y > 65535) | 
 |     return FXCODEC_STATUS::kError; | 
 |  | 
 |   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::kError; | 
 |  | 
 |   m_startX = device_rc.left; | 
 |   m_startY = device_rc.top; | 
 |   m_sizeX = device_rc.Width(); | 
 |   m_sizeY = device_rc.Height(); | 
 |   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 -= static_cast<int32_t>(ceil((float)start_x * scaleX)); | 
 |     } | 
 |     if (out_range_x > 0) { | 
 |       m_clipBox.right -= | 
 |           static_cast<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 -= static_cast<int32_t>(ceil((float)start_y * scaleY)); | 
 |     } | 
 |     if (out_range_y > 0) { | 
 |       m_clipBox.bottom -= | 
 |           static_cast<int32_t>(floor((float)out_range_y * scaleY)); | 
 |     } | 
 |   } | 
 |   if (m_clipBox.IsEmpty()) { | 
 |     return FXCODEC_STATUS::kError; | 
 |   } | 
 |   switch (m_imageType) { | 
 | #ifdef PDF_ENABLE_XFA_BMP | 
 |     case FXCODEC_IMAGE_BMP: | 
 |       return BmpStartDecode(pDIBitmap); | 
 | #endif  // PDF_ENABLE_XFA_BMP | 
 | #ifdef PDF_ENABLE_XFA_GIF | 
 |     case FXCODEC_IMAGE_GIF: | 
 |       return GifStartDecode(pDIBitmap); | 
 | #endif  // PDF_ENABLE_XFA_GIF | 
 |     case FXCODEC_IMAGE_JPG: | 
 |       return JpegStartDecode(pDIBitmap); | 
 | #ifdef PDF_ENABLE_XFA_PNG | 
 |     case FXCODEC_IMAGE_PNG: | 
 |       return PngStartDecode(pDIBitmap); | 
 | #endif  // PDF_ENABLE_XFA_PNG | 
 | #ifdef PDF_ENABLE_XFA_TIFF | 
 |     case FXCODEC_IMAGE_TIFF: | 
 |       m_status = FXCODEC_STATUS::kDecodeToBeContinued; | 
 |       return m_status; | 
 | #endif  // PDF_ENABLE_XFA_TIFF | 
 |     default: | 
 |       return FXCODEC_STATUS::kError; | 
 |   } | 
 | } | 
 |  | 
 | FXCODEC_STATUS ProgressiveDecoder::ContinueDecode() { | 
 |   if (m_status != FXCODEC_STATUS::kDecodeToBeContinued) | 
 |     return FXCODEC_STATUS::kError; | 
 |  | 
 |   switch (m_imageType) { | 
 |     case FXCODEC_IMAGE_JPG: | 
 |       return JpegContinueDecode(); | 
 | #ifdef PDF_ENABLE_XFA_BMP | 
 |     case FXCODEC_IMAGE_BMP: | 
 |       return BmpContinueDecode(); | 
 | #endif  // PDF_ENABLE_XFA_BMP | 
 | #ifdef PDF_ENABLE_XFA_GIF | 
 |     case FXCODEC_IMAGE_GIF: | 
 |       return GifContinueDecode(); | 
 | #endif  // PDF_ENABLE_XFA_GIF | 
 | #ifdef PDF_ENABLE_XFA_PNG | 
 |     case FXCODEC_IMAGE_PNG: | 
 |       return PngContinueDecode(); | 
 | #endif  // PDF_ENABLE_XFA_PNG | 
 | #ifdef PDF_ENABLE_XFA_TIFF | 
 |     case FXCODEC_IMAGE_TIFF: | 
 |       return TiffContinueDecode(); | 
 | #endif  // PDF_ENABLE_XFA_TIFF | 
 |     default: | 
 |       return FXCODEC_STATUS::kError; | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace fxcodec |