1 // Copyright 2016 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fpdfapi/page/cpdf_image.h"
8
9 #include <stdint.h>
10
11 #include <algorithm>
12 #include <array>
13 #include <memory>
14 #include <utility>
15
16 #include "constants/stream_dict_common.h"
17 #include "core/fpdfapi/page/cpdf_dib.h"
18 #include "core/fpdfapi/page/cpdf_page.h"
19 #include "core/fpdfapi/page/cpdf_pageimagecache.h"
20 #include "core/fpdfapi/parser/cpdf_array.h"
21 #include "core/fpdfapi/parser/cpdf_boolean.h"
22 #include "core/fpdfapi/parser/cpdf_dictionary.h"
23 #include "core/fpdfapi/parser/cpdf_document.h"
24 #include "core/fpdfapi/parser/cpdf_name.h"
25 #include "core/fpdfapi/parser/cpdf_number.h"
26 #include "core/fpdfapi/parser/cpdf_reference.h"
27 #include "core/fpdfapi/parser/cpdf_stream.h"
28 #include "core/fpdfapi/parser/cpdf_string.h"
29 #include "core/fxcodec/jpeg/jpegmodule.h"
30 #include "core/fxcrt/check.h"
31 #include "core/fxcrt/compiler_specific.h"
32 #include "core/fxcrt/data_vector.h"
33 #include "core/fxcrt/fx_2d_size.h"
34 #include "core/fxcrt/fx_stream.h"
35 #include "core/fxcrt/numerics/safe_conversions.h"
36 #include "core/fxcrt/span_util.h"
37 #include "core/fxcrt/stl_util.h"
38 #include "core/fxge/dib/cfx_dibitmap.h"
39 #include "core/fxge/dib/fx_dib.h"
40
41 // static
IsValidJpegComponent(int32_t comps)42 bool CPDF_Image::IsValidJpegComponent(int32_t comps) {
43 return comps == 1 || comps == 3 || comps == 4;
44 }
45
46 // static
IsValidJpegBitsPerComponent(int32_t bpc)47 bool CPDF_Image::IsValidJpegBitsPerComponent(int32_t bpc) {
48 return bpc == 1 || bpc == 2 || bpc == 4 || bpc == 8 || bpc == 16;
49 }
50
CPDF_Image(CPDF_Document * pDoc)51 CPDF_Image::CPDF_Image(CPDF_Document* pDoc) : m_pDocument(pDoc) {
52 DCHECK(m_pDocument);
53 }
54
CPDF_Image(CPDF_Document * pDoc,RetainPtr<CPDF_Stream> pStream)55 CPDF_Image::CPDF_Image(CPDF_Document* pDoc, RetainPtr<CPDF_Stream> pStream)
56 : m_bIsInline(true), m_pDocument(pDoc), m_pStream(std::move(pStream)) {
57 DCHECK(m_pDocument);
58 FinishInitialization();
59 }
60
CPDF_Image(CPDF_Document * pDoc,uint32_t dwStreamObjNum)61 CPDF_Image::CPDF_Image(CPDF_Document* pDoc, uint32_t dwStreamObjNum)
62 : m_pDocument(pDoc),
63 m_pStream(ToStream(pDoc->GetMutableIndirectObject(dwStreamObjNum))) {
64 DCHECK(m_pDocument);
65 FinishInitialization();
66 }
67
68 CPDF_Image::~CPDF_Image() = default;
69
FinishInitialization()70 void CPDF_Image::FinishInitialization() {
71 RetainPtr<const CPDF_Dictionary> pStreamDict = m_pStream->GetDict();
72 m_pOC = pStreamDict->GetDictFor("OC");
73 m_bIsMask = !pStreamDict->KeyExist("ColorSpace") ||
74 pStreamDict->GetBooleanFor("ImageMask", /*bDefault=*/false);
75 m_bInterpolate = !!pStreamDict->GetIntegerFor("Interpolate");
76 m_Height = pStreamDict->GetIntegerFor("Height");
77 m_Width = pStreamDict->GetIntegerFor("Width");
78 }
79
ConvertStreamToIndirectObject()80 void CPDF_Image::ConvertStreamToIndirectObject() {
81 CHECK(m_pStream->IsInline());
82 m_pDocument->AddIndirectObject(m_pStream);
83 }
84
GetDict() const85 RetainPtr<const CPDF_Dictionary> CPDF_Image::GetDict() const {
86 return m_pStream ? m_pStream->GetDict() : nullptr;
87 }
88
GetStream() const89 RetainPtr<const CPDF_Stream> CPDF_Image::GetStream() const {
90 return m_pStream;
91 }
92
GetOC() const93 RetainPtr<const CPDF_Dictionary> CPDF_Image::GetOC() const {
94 return m_pOC;
95 }
96
InitJPEG(pdfium::span<uint8_t> src_span)97 RetainPtr<CPDF_Dictionary> CPDF_Image::InitJPEG(
98 pdfium::span<uint8_t> src_span) {
99 std::optional<JpegModule::ImageInfo> info_opt =
100 JpegModule::LoadInfo(src_span);
101 if (!info_opt.has_value())
102 return nullptr;
103
104 const JpegModule::ImageInfo& info = info_opt.value();
105 if (!IsValidJpegComponent(info.num_components) ||
106 !IsValidJpegBitsPerComponent(info.bits_per_components)) {
107 return nullptr;
108 }
109
110 RetainPtr<CPDF_Dictionary> pDict =
111 CreateXObjectImageDict(info.width, info.height);
112 const char* csname = nullptr;
113 if (info.num_components == 1) {
114 csname = "DeviceGray";
115 } else if (info.num_components == 3) {
116 csname = "DeviceRGB";
117 } else if (info.num_components == 4) {
118 csname = "DeviceCMYK";
119 auto pDecode = pDict->SetNewFor<CPDF_Array>("Decode");
120 for (int n = 0; n < 4; n++) {
121 pDecode->AppendNew<CPDF_Number>(1);
122 pDecode->AppendNew<CPDF_Number>(0);
123 }
124 }
125 pDict->SetNewFor<CPDF_Name>("ColorSpace", csname);
126 pDict->SetNewFor<CPDF_Number>("BitsPerComponent", info.bits_per_components);
127 pDict->SetNewFor<CPDF_Name>("Filter", "DCTDecode");
128 if (!info.color_transform) {
129 auto pParms =
130 pDict->SetNewFor<CPDF_Dictionary>(pdfium::stream::kDecodeParms);
131 pParms->SetNewFor<CPDF_Number>("ColorTransform", 0);
132 }
133 m_bIsMask = false;
134 m_Width = info.width;
135 m_Height = info.height;
136 return pDict;
137 }
138
SetJpegImage(RetainPtr<IFX_SeekableReadStream> pFile)139 void CPDF_Image::SetJpegImage(RetainPtr<IFX_SeekableReadStream> pFile) {
140 uint32_t size = pdfium::checked_cast<uint32_t>(pFile->GetSize());
141 if (!size) {
142 return;
143 }
144
145 uint32_t dwEstimateSize = std::min(size, 8192U);
146 DataVector<uint8_t> data(dwEstimateSize);
147 if (!pFile->ReadBlockAtOffset(data, 0)) {
148 return;
149 }
150
151 RetainPtr<CPDF_Dictionary> dict = InitJPEG(data);
152 if (!dict && size > dwEstimateSize) {
153 data.resize(size);
154 if (pFile->ReadBlockAtOffset(data, 0)) {
155 dict = InitJPEG(data);
156 }
157 }
158 if (!dict) {
159 return;
160 }
161
162 m_pStream =
163 pdfium::MakeRetain<CPDF_Stream>(std::move(pFile), std::move(dict));
164 }
165
SetJpegImageInline(RetainPtr<IFX_SeekableReadStream> pFile)166 void CPDF_Image::SetJpegImageInline(RetainPtr<IFX_SeekableReadStream> pFile) {
167 uint32_t size = pdfium::checked_cast<uint32_t>(pFile->GetSize());
168 if (!size) {
169 return;
170 }
171
172 DataVector<uint8_t> data(size);
173 if (!pFile->ReadBlockAtOffset(data, 0)) {
174 return;
175 }
176
177 RetainPtr<CPDF_Dictionary> dict = InitJPEG(data);
178 if (!dict) {
179 return;
180 }
181
182 m_pStream = pdfium::MakeRetain<CPDF_Stream>(std::move(data), std::move(dict));
183 }
184
SetImage(const RetainPtr<CFX_DIBitmap> & pBitmap)185 void CPDF_Image::SetImage(const RetainPtr<CFX_DIBitmap>& pBitmap) {
186 int32_t BitmapWidth = pBitmap->GetWidth();
187 int32_t BitmapHeight = pBitmap->GetHeight();
188 if (BitmapWidth < 1 || BitmapHeight < 1)
189 return;
190
191 RetainPtr<CPDF_Dictionary> pDict =
192 CreateXObjectImageDict(BitmapWidth, BitmapHeight);
193 const int32_t bpp = pBitmap->GetBPP();
194 size_t dest_pitch = 0;
195 bool bCopyWithoutAlpha = true;
196 if (bpp == 1) {
197 FX_BGRA_STRUCT<uint8_t> reset_bgra;
198 FX_BGRA_STRUCT<uint8_t> set_bgra;
199 if (!pBitmap->IsMaskFormat()) {
200 reset_bgra = ArgbToBGRAStruct(pBitmap->GetPaletteArgb(0));
201 set_bgra = ArgbToBGRAStruct(pBitmap->GetPaletteArgb(1));
202 }
203 if (set_bgra.alpha == 0 || reset_bgra.alpha == 0) {
204 pDict->SetNewFor<CPDF_Boolean>("ImageMask", true);
205 if (reset_bgra.alpha == 0) {
206 auto pArray = pDict->SetNewFor<CPDF_Array>("Decode");
207 pArray->AppendNew<CPDF_Number>(1);
208 pArray->AppendNew<CPDF_Number>(0);
209 }
210 } else {
211 auto pCS = pDict->SetNewFor<CPDF_Array>("ColorSpace");
212 pCS->AppendNew<CPDF_Name>("Indexed");
213 pCS->AppendNew<CPDF_Name>("DeviceRGB");
214 pCS->AppendNew<CPDF_Number>(1);
215 const uint8_t ct[6] = {reset_bgra.red, reset_bgra.green, reset_bgra.blue,
216 set_bgra.red, set_bgra.green, set_bgra.blue};
217 pCS->AppendNew<CPDF_String>(ct, CPDF_String::DataType::kIsHex);
218 }
219 pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 1);
220 dest_pitch = (BitmapWidth + 7) / 8;
221 } else if (bpp == 8) {
222 size_t palette_size = pBitmap->GetRequiredPaletteSize();
223 if (palette_size > 0) {
224 DCHECK(palette_size <= 256);
225 auto pCS = m_pDocument->NewIndirect<CPDF_Array>();
226 pCS->AppendNew<CPDF_Name>("Indexed");
227 pCS->AppendNew<CPDF_Name>("DeviceRGB");
228 pCS->AppendNew<CPDF_Number>(static_cast<int>(palette_size - 1));
229 DataVector<uint8_t> color_table(Fx2DSizeOrDie(palette_size, 3));
230 auto color_table_span = pdfium::make_span(color_table);
231 for (size_t i = 0; i < palette_size; i++) {
232 uint32_t argb = pBitmap->GetPaletteArgb(i);
233 color_table_span[0] = FXARGB_R(argb);
234 color_table_span[1] = FXARGB_G(argb);
235 color_table_span[2] = FXARGB_B(argb);
236 color_table_span = color_table_span.subspan(3);
237 }
238 auto pNewDict = m_pDocument->New<CPDF_Dictionary>();
239 auto pCTS = m_pDocument->NewIndirect<CPDF_Stream>(std::move(color_table),
240 std::move(pNewDict));
241 pCS->AppendNew<CPDF_Reference>(m_pDocument, pCTS->GetObjNum());
242 pDict->SetNewFor<CPDF_Reference>("ColorSpace", m_pDocument,
243 pCS->GetObjNum());
244 } else {
245 pDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
246 }
247 pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
248 dest_pitch = BitmapWidth;
249 } else {
250 pDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceRGB");
251 pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
252 dest_pitch = BitmapWidth * 3;
253 bCopyWithoutAlpha = false;
254 }
255
256 RetainPtr<CFX_DIBitmap> pMaskBitmap;
257 if (pBitmap->IsAlphaFormat())
258 pMaskBitmap = pBitmap->CloneAlphaMask();
259
260 if (pMaskBitmap) {
261 const int32_t mask_width = pMaskBitmap->GetWidth();
262 const int32_t mask_height = pMaskBitmap->GetHeight();
263 DataVector<uint8_t> mask_buf;
264 RetainPtr<CPDF_Dictionary> pMaskDict =
265 CreateXObjectImageDict(mask_width, mask_height);
266 pMaskDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
267 pMaskDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
268 if (pMaskBitmap->GetFormat() != FXDIB_Format::k1bppMask) {
269 mask_buf.resize(Fx2DSizeOrDie(mask_width, mask_height));
270 for (int32_t a = 0; a < mask_height; a++) {
271 fxcrt::Copy(pMaskBitmap->GetScanline(a).first(mask_width),
272 pdfium::make_span(mask_buf).subspan(a * mask_width));
273 }
274 }
275 pMaskDict->SetNewFor<CPDF_Number>(
276 "Length", pdfium::checked_cast<int>(mask_buf.size()));
277 auto pNewStream = m_pDocument->NewIndirect<CPDF_Stream>(
278 std::move(mask_buf), std::move(pMaskDict));
279 pDict->SetNewFor<CPDF_Reference>("SMask", m_pDocument,
280 pNewStream->GetObjNum());
281 }
282
283 DataVector<uint8_t> dest_buf(Fx2DSizeOrDie(dest_pitch, BitmapHeight));
284 pdfium::span<uint8_t> dest_span = pdfium::make_span(dest_buf);
285 pdfium::span<const uint8_t> src_span = pBitmap->GetBuffer();
286 const int32_t src_pitch = pBitmap->GetPitch();
287 if (bCopyWithoutAlpha) {
288 for (int32_t i = 0; i < BitmapHeight; i++) {
289 dest_span = fxcrt::spancpy(dest_span, src_span.first(dest_pitch));
290 src_span = src_span.subspan(src_pitch);
291 }
292 } else {
293 const size_t src_step = bpp == 24 ? 3 : 4;
294 for (int32_t row = 0; row < BitmapHeight; row++) {
295 uint8_t* dest_ptr = dest_span.data();
296 const uint8_t* src_ptr = src_span.data();
297 for (int32_t column = 0; column < BitmapWidth; column++) {
298 UNSAFE_TODO({
299 dest_ptr[0] = src_ptr[2];
300 dest_ptr[1] = src_ptr[1];
301 dest_ptr[2] = src_ptr[0];
302 dest_ptr += 3;
303 src_ptr += src_step;
304 });
305 }
306 dest_span = dest_span.subspan(dest_pitch);
307 src_span = src_span.subspan(src_pitch);
308 }
309 }
310
311 m_pStream =
312 pdfium::MakeRetain<CPDF_Stream>(std::move(dest_buf), std::move(pDict));
313 m_bIsMask = pBitmap->IsMaskFormat();
314 m_Width = BitmapWidth;
315 m_Height = BitmapHeight;
316 }
317
ResetCache(CPDF_Page * pPage)318 void CPDF_Image::ResetCache(CPDF_Page* pPage) {
319 RetainPtr<CPDF_Image> pHolder(this);
320 pPage->GetPageImageCache()->ResetBitmapForImage(std::move(pHolder));
321 }
322
WillBeDestroyed()323 void CPDF_Image::WillBeDestroyed() {
324 m_bWillBeDestroyed = true;
325 }
326
CreateNewDIB() const327 RetainPtr<CPDF_DIB> CPDF_Image::CreateNewDIB() const {
328 return pdfium::MakeRetain<CPDF_DIB>(GetDocument(), GetStream());
329 }
330
LoadDIBBase() const331 RetainPtr<CFX_DIBBase> CPDF_Image::LoadDIBBase() const {
332 RetainPtr<CPDF_DIB> source = CreateNewDIB();
333 if (!source->Load())
334 return nullptr;
335
336 if (!source->IsJBigImage())
337 return source;
338
339 CPDF_DIB::LoadState ret = CPDF_DIB::LoadState::kContinue;
340 while (ret == CPDF_DIB::LoadState::kContinue)
341 ret = source->ContinueLoadDIBBase(nullptr);
342 return ret == CPDF_DIB::LoadState::kSuccess ? source : nullptr;
343 }
344
DetachBitmap()345 RetainPtr<CFX_DIBBase> CPDF_Image::DetachBitmap() {
346 return std::move(m_pDIBBase);
347 }
348
DetachMask()349 RetainPtr<CFX_DIBBase> CPDF_Image::DetachMask() {
350 return std::move(m_pMask);
351 }
352
StartLoadDIBBase(const CPDF_Dictionary * pFormResource,const CPDF_Dictionary * pPageResource,bool bStdCS,CPDF_ColorSpace::Family GroupFamily,bool bLoadMask,const CFX_Size & max_size_required)353 bool CPDF_Image::StartLoadDIBBase(const CPDF_Dictionary* pFormResource,
354 const CPDF_Dictionary* pPageResource,
355 bool bStdCS,
356 CPDF_ColorSpace::Family GroupFamily,
357 bool bLoadMask,
358 const CFX_Size& max_size_required) {
359 RetainPtr<CPDF_DIB> source = CreateNewDIB();
360 CPDF_DIB::LoadState ret =
361 source->StartLoadDIBBase(true, pFormResource, pPageResource, bStdCS,
362 GroupFamily, bLoadMask, max_size_required);
363 if (ret == CPDF_DIB::LoadState::kFail) {
364 m_pDIBBase.Reset();
365 return false;
366 }
367 m_pDIBBase = source;
368 if (ret == CPDF_DIB::LoadState::kContinue)
369 return true;
370
371 m_pMask = source->DetachMask();
372 m_MatteColor = source->GetMatteColor();
373 return false;
374 }
375
Continue(PauseIndicatorIface * pPause)376 bool CPDF_Image::Continue(PauseIndicatorIface* pPause) {
377 RetainPtr<CPDF_DIB> pSource = m_pDIBBase.As<CPDF_DIB>();
378 CPDF_DIB::LoadState ret = pSource->ContinueLoadDIBBase(pPause);
379 if (ret == CPDF_DIB::LoadState::kContinue)
380 return true;
381
382 if (ret == CPDF_DIB::LoadState::kSuccess) {
383 m_pMask = pSource->DetachMask();
384 m_MatteColor = pSource->GetMatteColor();
385 } else {
386 m_pDIBBase.Reset();
387 }
388 return false;
389 }
390
CreateXObjectImageDict(int width,int height)391 RetainPtr<CPDF_Dictionary> CPDF_Image::CreateXObjectImageDict(int width,
392 int height) {
393 auto dict = m_pDocument->New<CPDF_Dictionary>();
394 dict->SetNewFor<CPDF_Name>("Type", "XObject");
395 dict->SetNewFor<CPDF_Name>("Subtype", "Image");
396 dict->SetNewFor<CPDF_Number>("Width", width);
397 dict->SetNewFor<CPDF_Number>("Height", height);
398 return dict;
399 }
400