blob: 8720342d359d5b174da461ba3e52261a25262ae6 [file] [log] [blame]
// Copyright 2016 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/fpdfapi/page/cpdf_image.h"
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include "constants/stream_dict_common.h"
#include "core/fpdfapi/cpdf_modulemgr.h"
#include "core/fpdfapi/page/cpdf_page.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_boolean.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/cpdf_name.h"
#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fpdfapi/parser/cpdf_reference.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fpdfapi/render/cpdf_dibsource.h"
#include "core/fpdfapi/render/cpdf_pagerendercache.h"
#include "core/fxcodec/codec/ccodec_jpegmodule.h"
#include "core/fxcrt/fx_stream.h"
#include "core/fxge/dib/cfx_dibitmap.h"
#include "core/fxge/fx_dib.h"
#include "third_party/base/numerics/safe_conversions.h"
#include "third_party/base/ptr_util.h"
CPDF_Image::CPDF_Image(CPDF_Document* pDoc) : m_pDocument(pDoc) {}
CPDF_Image::CPDF_Image(CPDF_Document* pDoc,
std::unique_ptr<CPDF_Stream> pStream)
: m_bIsInline(true), m_pDocument(pDoc), m_pStream(std::move(pStream)) {
ASSERT(m_pStream.IsOwned());
FinishInitialization(m_pStream->GetDict());
}
CPDF_Image::CPDF_Image(CPDF_Document* pDoc, uint32_t dwStreamObjNum)
: m_pDocument(pDoc),
m_pStream(ToStream(pDoc->GetIndirectObject(dwStreamObjNum))) {
ASSERT(!m_pStream.IsOwned());
FinishInitialization(m_pStream->GetDict());
}
CPDF_Image::~CPDF_Image() {}
void CPDF_Image::FinishInitialization(CPDF_Dictionary* pStreamDict) {
m_pOC = pStreamDict->GetDictFor("OC");
m_bIsMask = !pStreamDict->KeyExist("ColorSpace") ||
pStreamDict->GetIntegerFor("ImageMask");
m_bInterpolate = !!pStreamDict->GetIntegerFor("Interpolate");
m_Height = pStreamDict->GetIntegerFor("Height");
m_Width = pStreamDict->GetIntegerFor("Width");
}
void CPDF_Image::ConvertStreamToIndirectObject() {
if (!m_pStream->IsInline())
return;
ASSERT(m_pStream.IsOwned());
m_pDocument->AddIndirectObject(m_pStream.Release());
}
CPDF_Dictionary* CPDF_Image::GetDict() const {
return m_pStream ? m_pStream->GetDict() : nullptr;
}
std::unique_ptr<CPDF_Dictionary> CPDF_Image::InitJPEG(uint8_t* pData,
uint32_t size) {
int32_t width;
int32_t height;
int32_t num_comps;
int32_t bits;
bool color_trans;
if (!CPDF_ModuleMgr::Get()->GetJpegModule()->LoadInfo(
pData, size, &width, &height, &num_comps, &bits, &color_trans)) {
return nullptr;
}
auto pDict =
pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
pDict->SetNewFor<CPDF_Name>("Type", "XObject");
pDict->SetNewFor<CPDF_Name>("Subtype", "Image");
pDict->SetNewFor<CPDF_Number>("Width", width);
pDict->SetNewFor<CPDF_Number>("Height", height);
const char* csname = nullptr;
if (num_comps == 1) {
csname = "DeviceGray";
} else if (num_comps == 3) {
csname = "DeviceRGB";
} else if (num_comps == 4) {
csname = "DeviceCMYK";
CPDF_Array* pDecode = pDict->SetNewFor<CPDF_Array>("Decode");
for (int n = 0; n < 4; n++) {
pDecode->AddNew<CPDF_Number>(1);
pDecode->AddNew<CPDF_Number>(0);
}
}
pDict->SetNewFor<CPDF_Name>("ColorSpace", csname);
pDict->SetNewFor<CPDF_Number>("BitsPerComponent", bits);
pDict->SetNewFor<CPDF_Name>("Filter", "DCTDecode");
if (!color_trans) {
CPDF_Dictionary* pParms =
pDict->SetNewFor<CPDF_Dictionary>(pdfium::stream::kDecodeParms);
pParms->SetNewFor<CPDF_Number>("ColorTransform", 0);
}
m_bIsMask = false;
m_Width = width;
m_Height = height;
if (!m_pStream)
m_pStream = pdfium::MakeUnique<CPDF_Stream>();
return pDict;
}
void CPDF_Image::SetJpegImage(const RetainPtr<IFX_SeekableReadStream>& pFile) {
uint32_t size = pdfium::base::checked_cast<uint32_t>(pFile->GetSize());
if (!size)
return;
uint32_t dwEstimateSize = std::min(size, 8192U);
std::vector<uint8_t> data(dwEstimateSize);
if (!pFile->ReadBlock(data.data(), 0, dwEstimateSize))
return;
std::unique_ptr<CPDF_Dictionary> pDict =
InitJPEG(data.data(), dwEstimateSize);
if (!pDict && size > dwEstimateSize) {
data.resize(size);
pFile->ReadBlock(data.data(), 0, size);
pDict = InitJPEG(data.data(), size);
}
if (!pDict)
return;
m_pStream->InitStreamFromFile(pFile, std::move(pDict));
}
void CPDF_Image::SetJpegImageInline(
const RetainPtr<IFX_SeekableReadStream>& pFile) {
uint32_t size = pdfium::base::checked_cast<uint32_t>(pFile->GetSize());
if (!size)
return;
std::vector<uint8_t> data(size);
if (!pFile->ReadBlock(data.data(), 0, size))
return;
std::unique_ptr<CPDF_Dictionary> pDict = InitJPEG(data.data(), size);
if (!pDict)
return;
m_pStream->InitStream(&(data[0]), size, std::move(pDict));
}
void CPDF_Image::SetImage(const RetainPtr<CFX_DIBitmap>& pBitmap) {
int32_t BitmapWidth = pBitmap->GetWidth();
int32_t BitmapHeight = pBitmap->GetHeight();
if (BitmapWidth < 1 || BitmapHeight < 1)
return;
auto pDict =
pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
pDict->SetNewFor<CPDF_Name>("Type", "XObject");
pDict->SetNewFor<CPDF_Name>("Subtype", "Image");
pDict->SetNewFor<CPDF_Number>("Width", BitmapWidth);
pDict->SetNewFor<CPDF_Number>("Height", BitmapHeight);
const int32_t bpp = pBitmap->GetBPP();
size_t dest_pitch = 0;
bool bCopyWithoutAlpha = true;
if (bpp == 1) {
int32_t reset_a = 0;
int32_t reset_r = 0;
int32_t reset_g = 0;
int32_t reset_b = 0;
int32_t set_a = 0;
int32_t set_r = 0;
int32_t set_g = 0;
int32_t set_b = 0;
if (!pBitmap->IsAlphaMask()) {
std::tie(reset_a, reset_r, reset_g, reset_b) =
ArgbDecode(pBitmap->GetPaletteArgb(0));
std::tie(set_a, set_r, set_g, set_b) =
ArgbDecode(pBitmap->GetPaletteArgb(1));
}
if (set_a == 0 || reset_a == 0) {
pDict->SetNewFor<CPDF_Boolean>("ImageMask", true);
if (reset_a == 0) {
CPDF_Array* pArray = pDict->SetNewFor<CPDF_Array>("Decode");
pArray->AddNew<CPDF_Number>(1);
pArray->AddNew<CPDF_Number>(0);
}
} else {
CPDF_Array* pCS = pDict->SetNewFor<CPDF_Array>("ColorSpace");
pCS->AddNew<CPDF_Name>("Indexed");
pCS->AddNew<CPDF_Name>("DeviceRGB");
pCS->AddNew<CPDF_Number>(1);
ByteString ct;
{
// Span's lifetime must end before ReleaseBuffer() below.
pdfium::span<char> pBuf = ct.GetBuffer(6);
pBuf[0] = (char)reset_r;
pBuf[1] = (char)reset_g;
pBuf[2] = (char)reset_b;
pBuf[3] = (char)set_r;
pBuf[4] = (char)set_g;
pBuf[5] = (char)set_b;
}
ct.ReleaseBuffer(6);
pCS->AddNew<CPDF_String>(ct, true);
}
pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 1);
dest_pitch = (BitmapWidth + 7) / 8;
} else if (bpp == 8) {
int32_t iPalette = pBitmap->GetPaletteSize();
if (iPalette > 0) {
CPDF_Array* pCS = m_pDocument->NewIndirect<CPDF_Array>();
pCS->AddNew<CPDF_Name>("Indexed");
pCS->AddNew<CPDF_Name>("DeviceRGB");
pCS->AddNew<CPDF_Number>(iPalette - 1);
std::unique_ptr<uint8_t, FxFreeDeleter> pColorTable(
FX_Alloc2D(uint8_t, iPalette, 3));
uint8_t* ptr = pColorTable.get();
for (int32_t i = 0; i < iPalette; i++) {
uint32_t argb = pBitmap->GetPaletteArgb(i);
ptr[0] = (uint8_t)(argb >> 16);
ptr[1] = (uint8_t)(argb >> 8);
ptr[2] = (uint8_t)argb;
ptr += 3;
}
auto pNewDict =
pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
CPDF_Stream* pCTS = m_pDocument->NewIndirect<CPDF_Stream>(
std::move(pColorTable), iPalette * 3, std::move(pNewDict));
pCS->AddNew<CPDF_Reference>(m_pDocument.Get(), pCTS->GetObjNum());
pDict->SetNewFor<CPDF_Reference>("ColorSpace", m_pDocument.Get(),
pCS->GetObjNum());
} else {
pDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
}
pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
dest_pitch = BitmapWidth;
} else {
pDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceRGB");
pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
dest_pitch = BitmapWidth * 3;
bCopyWithoutAlpha = false;
}
RetainPtr<CFX_DIBitmap> pMaskBitmap;
if (pBitmap->HasAlpha())
pMaskBitmap = pBitmap->CloneAlphaMask();
if (pMaskBitmap) {
int32_t maskWidth = pMaskBitmap->GetWidth();
int32_t maskHeight = pMaskBitmap->GetHeight();
std::unique_ptr<uint8_t, FxFreeDeleter> mask_buf;
int32_t mask_size = 0;
auto pMaskDict =
pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
pMaskDict->SetNewFor<CPDF_Name>("Type", "XObject");
pMaskDict->SetNewFor<CPDF_Name>("Subtype", "Image");
pMaskDict->SetNewFor<CPDF_Number>("Width", maskWidth);
pMaskDict->SetNewFor<CPDF_Number>("Height", maskHeight);
pMaskDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
pMaskDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
if (pMaskBitmap->GetFormat() != FXDIB_1bppMask) {
mask_buf.reset(FX_Alloc2D(uint8_t, maskHeight, maskWidth));
mask_size = maskHeight * maskWidth; // Safe since checked alloc returned.
for (int32_t a = 0; a < maskHeight; a++) {
memcpy(mask_buf.get() + a * maskWidth, pMaskBitmap->GetScanline(a),
maskWidth);
}
}
pMaskDict->SetNewFor<CPDF_Number>("Length", mask_size);
CPDF_Stream* pNewStream = m_pDocument->NewIndirect<CPDF_Stream>(
std::move(mask_buf), mask_size, std::move(pMaskDict));
pDict->SetNewFor<CPDF_Reference>("SMask", m_pDocument.Get(),
pNewStream->GetObjNum());
}
uint8_t* src_buf = pBitmap->GetBuffer();
int32_t src_pitch = pBitmap->GetPitch();
uint8_t* dest_buf = FX_Alloc2D(uint8_t, dest_pitch, BitmapHeight);
// Safe as checked alloc returned.
size_t dest_size = dest_pitch * BitmapHeight;
uint8_t* pDest = dest_buf;
if (bCopyWithoutAlpha) {
for (int32_t i = 0; i < BitmapHeight; i++) {
memcpy(pDest, src_buf, dest_pitch);
pDest += dest_pitch;
src_buf += src_pitch;
}
} else {
int32_t src_offset = 0;
int32_t dest_offset = 0;
for (int32_t row = 0; row < BitmapHeight; row++) {
src_offset = row * src_pitch;
for (int32_t column = 0; column < BitmapWidth; column++) {
float alpha = 1;
pDest[dest_offset] = (uint8_t)(src_buf[src_offset + 2] * alpha);
pDest[dest_offset + 1] = (uint8_t)(src_buf[src_offset + 1] * alpha);
pDest[dest_offset + 2] = (uint8_t)(src_buf[src_offset] * alpha);
dest_offset += 3;
src_offset += bpp == 24 ? 3 : 4;
}
pDest += dest_pitch;
dest_offset = 0;
}
}
if (!m_pStream)
m_pStream = pdfium::MakeUnique<CPDF_Stream>();
m_pStream->InitStream(dest_buf, dest_size, std::move(pDict));
m_bIsMask = pBitmap->IsAlphaMask();
m_Width = BitmapWidth;
m_Height = BitmapHeight;
FX_Free(dest_buf);
}
void CPDF_Image::ResetCache(CPDF_Page* pPage,
const RetainPtr<CFX_DIBitmap>& pBitmap) {
RetainPtr<CPDF_Image> pHolder(this);
pPage->GetRenderCache()->ResetBitmap(pHolder, pBitmap);
}
RetainPtr<CFX_DIBSource> CPDF_Image::LoadDIBSource() const {
auto source = pdfium::MakeRetain<CPDF_DIBSource>();
if (!source->Load(m_pDocument.Get(), m_pStream.Get()))
return nullptr;
if (!source->IsJBigImage())
return source;
CPDF_DIBSource::LoadState ret = CPDF_DIBSource::LoadState::kContinue;
while (ret == CPDF_DIBSource::LoadState::kContinue)
ret = source->ContinueLoadDIBSource(nullptr);
return ret == CPDF_DIBSource::LoadState::kSuccess ? source : nullptr;
}
RetainPtr<CFX_DIBSource> CPDF_Image::DetachBitmap() {
return std::move(m_pDIBSource);
}
RetainPtr<CFX_DIBSource> CPDF_Image::DetachMask() {
return std::move(m_pMask);
}
bool CPDF_Image::StartLoadDIBSource(const CPDF_Dictionary* pFormResource,
CPDF_Dictionary* pPageResource,
bool bStdCS,
uint32_t GroupFamily,
bool bLoadMask) {
auto source = pdfium::MakeRetain<CPDF_DIBSource>();
CPDF_DIBSource::LoadState ret = source->StartLoadDIBSource(
m_pDocument.Get(), m_pStream.Get(), true, pFormResource, pPageResource,
bStdCS, GroupFamily, bLoadMask);
if (ret == CPDF_DIBSource::LoadState::kFail) {
m_pDIBSource.Reset();
return false;
}
m_pDIBSource = source;
if (ret == CPDF_DIBSource::LoadState::kContinue)
return true;
m_pMask = source->DetachMask();
m_MatteColor = source->GetMatteColor();
return false;
}
bool CPDF_Image::Continue(PauseIndicatorIface* pPause) {
RetainPtr<CPDF_DIBSource> pSource = m_pDIBSource.As<CPDF_DIBSource>();
CPDF_DIBSource::LoadState ret = pSource->ContinueLoadDIBSource(pPause);
if (ret == CPDF_DIBSource::LoadState::kContinue)
return true;
if (ret == CPDF_DIBSource::LoadState::kSuccess) {
m_pMask = pSource->DetachMask();
m_MatteColor = pSource->GetMatteColor();
} else {
m_pDIBSource.Reset();
}
return false;
}