// Copyright 2014 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "core/fxcodec/progressive_decoder.h" #include #include #include #include #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(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(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_NORETURN(); 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_NORETURN(); } *gamma = kPngGamma; return true; } bool ProgressiveDecoder::PngAskScanlineBuf(int line, uint8_t** pSrcBuf) { RetainPtr pDIBitmap = m_pDeviceBitmap; CHECK(pDIBitmap); if (line < m_clipBox.top || line >= m_clipBox.bottom) return true; double scale_y = static_cast(m_sizeY) / m_clipBox.Height(); int32_t row = static_cast((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; pdfium::span src_span = pDIBitmap->GetScanline(row).subspan(src_left * src_Bpp); pdfium::span dest_span = pdfium::make_span(m_DecodeBuf).subspan(dest_left * dest_Bpp); const uint8_t* src_scan = src_span.data(); uint8_t* dest_scan = dest_span.data(); 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_NORETURN(); } 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 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(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, 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( 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; FXCODEC_STATUS error_status = FXCODEC_STATUS::kError; m_pCodecMemory->Seek(m_pCodecMemory->GetSize()); 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 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, pdfium::span row_buf) { RetainPtr pDIBitmap = m_pDeviceBitmap; DCHECK(pDIBitmap); int32_t img_width = m_GifFrameRect.Width(); if (!pDIBitmap->IsAlphaFormat()) { pdfium::span byte_span = row_buf; for (int i = 0; i < img_width; i++) { if (byte_span.front() == m_GifTransIndex) { byte_span.front() = m_GifBgIndex; } byte_span = byte_span.subspan(1); } } int32_t pal_index = m_GifBgIndex; if (m_GifTransIndex != -1 && m_pDeviceBitmap->IsAlphaFormat()) { pal_index = m_GifTransIndex; } const int32_t left = m_GifFrameRect.left; const pdfium::span decode_span = m_DecodeBuf; fxcrt::spanset(decode_span.first(m_SrcWidth), pal_index); fxcrt::spancpy(decode_span.subspan(left), row_buf.first(img_width)); bool bLastPass = (row_num % 2) == 1; int32_t line = row_num + m_GifFrameRect.top; 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(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, decode_span, 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 row_buf) { RetainPtr 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(src_top)) || src_bottom < 0 || row_num >= static_cast(src_bottom)) { return; } double scale_y = static_cast(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, 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& 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(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 pBmpContext = BmpDecoder::StartDecode(this); BmpDecoder::Input(pBmpContext.get(), m_pCodecMemory); const std::vector* 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 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( 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, err_status); } FXCODEC_STATUS ProgressiveDecoder::BmpStartDecode() { 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) { return ReadMoreData(GifProgressiveDecoder::GetInstance(), m_pGifContext.get(), err_status); } 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() { 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& 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 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(), 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(FXDIB_Format format) { 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(format, 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& pDeviceBitmap, int32_t dest_line, pdfium::span src_span, 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; uint8_t* src_scan = src_span.subspan(src_left * src_Bpp).data(); 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_NORETURN(); 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(m_pFile->GetSize()) - m_offSet; uint32_t input_size = std::min(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(input_size); if (!m_pFile->ReadBlockAtOffset( m_pCodecMemory->GetBufferSpan().first(input_size), m_offSet)) { 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() { 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(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(input_size); bool bResult = m_pFile->ReadBlockAtOffset( m_pCodecMemory->GetBufferSpan().first(input_size), m_offSet); 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(); pDIBitmap->Create(m_SrcWidth, m_SrcHeight, FXDIB_Format::kArgb); if (pDIBitmap->GetBuffer().empty()) { 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 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 pFormatBitmap; switch (m_pDeviceBitmap->GetFormat()) { case FXDIB_Format::k8bppRgb: pFormatBitmap = pdfium::MakeRetain(); pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), FXDIB_Format::k8bppRgb); break; case FXDIB_Format::k8bppMask: pFormatBitmap = pdfium::MakeRetain(); pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), FXDIB_Format::k8bppMask); break; case FXDIB_Format::kRgb: pFormatBitmap = pdfium::MakeRetain(); pFormatBitmap->Create(pClipBitmap->GetWidth(), pClipBitmap->GetHeight(), FXDIB_Format::kRgb); break; case FXDIB_Format::kRgb32: pFormatBitmap = pdfium::MakeRetain(); 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 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( std::min(m_pFile->GetSize(), kBlockSize)); m_pCodecMemory = pdfium::MakeRetain(size); m_offSet = 0; if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBufferSpan().first(size), m_offSet)) { 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, FXCODEC_STATUS* err_status) { // Check for EOF. if (m_offSet >= static_cast(m_pFile->GetSize())) return false; // Try to get whatever remains. uint32_t dwBytesToFetchFromFile = pdfium::base::checked_cast(m_pFile->GetSize() - m_offSet); // Figure out if the codec stopped processing midway through the buffer. size_t dwUnconsumed; FX_SAFE_SIZE_T avail_input = pModule->GetAvailInput(pContext); if (!avail_input.AssignIfValid(&dwUnconsumed)) return false; 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(dwBytesToFetchFromFile, kBlockSize); size_t dwNewSize = m_pCodecMemory->GetSize() + dwBytesToFetchFromFile; if (!m_pCodecMemory->TryResize(dwNewSize)) { *err_status = FXCODEC_STATUS::kError; return false; } } else { // TODO(crbug.com/pdfium/1904): Simplify the `CFX_CodecMemory` API so we // don't need to do this awkward dance to free up exactly enough buffer // space for the next read. size_t dwConsumable = m_pCodecMemory->GetSize() - dwUnconsumed; dwBytesToFetchFromFile = pdfium::base::checked_cast( std::min(dwBytesToFetchFromFile, dwConsumable)); m_pCodecMemory->Consume(dwBytesToFetchFromFile); m_pCodecMemory->Seek(dwConsumable - dwBytesToFetchFromFile); dwUnconsumed += m_pCodecMemory->GetPosition(); } // Append new data past the bytes not yet processed by the codec. if (!m_pFile->ReadBlockAtOffset(m_pCodecMemory->GetBufferSpan().subspan( dwUnconsumed, dwBytesToFetchFromFile), m_offSet)) { *err_status = FXCODEC_STATUS::kError; return false; } m_offSet += dwBytesToFetchFromFile; return pModule->Input(pContext, m_pCodecMemory); } FXCODEC_STATUS ProgressiveDecoder::LoadImageInfo( RetainPtr 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; } m_pFile = std::move(pFile); if (!m_pFile) { m_status = FXCODEC_STATUS::kError; return m_status; } 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(type), pAttribute)) { m_imageType = static_cast(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& pDeviceBitmap, int dest_line, pdfium::span src_span, FXCodec_Format src_format) { uint8_t* src_scan = src_span.data(); 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( 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( 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( 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& 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(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) { pdfium::span scan_src = pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset, m_sizeX * dest_Bpp); while (++dest_row < dest_bottom) { fxcrt::spanmove(pDeviceBitmap->GetWritableScanline(dest_row).subspan( dest_ScanOffset), scan_src); } } 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) { pdfium::span scan_src = pDeviceBitmap->GetScanline(dest_row).subspan(dest_ScanOffset, m_sizeX * dest_Bpp); while (++dest_row < dest_bottom) { fxcrt::spanmove( pDeviceBitmap->GetWritableScanline(dest_row).subspan(dest_ScanOffset), scan_src); } } } void ProgressiveDecoder::Resample(const RetainPtr& 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(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, src_format); if (scale_y > 1.0) ResampleVert(pDeviceBitmap, scale_y, dest_row); } } std::pair 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& 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(ceil((float)start_x * scaleX)); } if (out_range_x > 0) { m_clipBox.right -= static_cast(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(ceil((float)start_y * scaleY)); } if (out_range_y > 0) { m_clipBox.bottom -= static_cast(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(); #endif // PDF_ENABLE_XFA_BMP #ifdef PDF_ENABLE_XFA_GIF case FXCODEC_IMAGE_GIF: return GifStartDecode(); #endif // PDF_ENABLE_XFA_GIF case FXCODEC_IMAGE_JPG: return JpegStartDecode(pDIBitmap->GetFormat()); #ifdef PDF_ENABLE_XFA_PNG case FXCODEC_IMAGE_PNG: return PngStartDecode(); #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