• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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