• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 PDFium Authors. All rights reserved.
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 <algorithm>
10 #include <memory>
11 #include <utility>
12 #include <vector>
13 
14 #include "core/fpdfapi/cpdf_modulemgr.h"
15 #include "core/fpdfapi/page/cpdf_page.h"
16 #include "core/fpdfapi/parser/cpdf_array.h"
17 #include "core/fpdfapi/parser/cpdf_boolean.h"
18 #include "core/fpdfapi/parser/cpdf_dictionary.h"
19 #include "core/fpdfapi/parser/cpdf_document.h"
20 #include "core/fpdfapi/parser/cpdf_name.h"
21 #include "core/fpdfapi/parser/cpdf_number.h"
22 #include "core/fpdfapi/parser/cpdf_reference.h"
23 #include "core/fpdfapi/parser/cpdf_stream.h"
24 #include "core/fpdfapi/parser/cpdf_string.h"
25 #include "core/fpdfapi/render/cpdf_dibsource.h"
26 #include "core/fpdfapi/render/cpdf_pagerendercache.h"
27 #include "core/fxcodec/codec/ccodec_jpegmodule.h"
28 #include "core/fxcrt/fx_stream.h"
29 #include "core/fxge/dib/cfx_dibitmap.h"
30 #include "core/fxge/fx_dib.h"
31 #include "third_party/base/numerics/safe_conversions.h"
32 #include "third_party/base/ptr_util.h"
33 
CPDF_Image(CPDF_Document * pDoc)34 CPDF_Image::CPDF_Image(CPDF_Document* pDoc) : m_pDocument(pDoc) {}
35 
CPDF_Image(CPDF_Document * pDoc,std::unique_ptr<CPDF_Stream> pStream)36 CPDF_Image::CPDF_Image(CPDF_Document* pDoc,
37                        std::unique_ptr<CPDF_Stream> pStream)
38     : m_bIsInline(true),
39       m_pDocument(pDoc),
40       m_pStream(std::move(pStream)),
41       m_pDict(ToDictionary(m_pStream->GetDict()->Clone())) {
42   ASSERT(m_pStream.IsOwned());
43   ASSERT(m_pDict.IsOwned());
44   FinishInitialization();
45 }
46 
CPDF_Image(CPDF_Document * pDoc,uint32_t dwStreamObjNum)47 CPDF_Image::CPDF_Image(CPDF_Document* pDoc, uint32_t dwStreamObjNum)
48     : m_pDocument(pDoc),
49       m_pStream(ToStream(pDoc->GetIndirectObject(dwStreamObjNum))),
50       m_pDict(m_pStream->GetDict()) {
51   ASSERT(!m_pStream.IsOwned());
52   ASSERT(!m_pDict.IsOwned());
53   FinishInitialization();
54 }
55 
~CPDF_Image()56 CPDF_Image::~CPDF_Image() {}
57 
FinishInitialization()58 void CPDF_Image::FinishInitialization() {
59   m_pOC = m_pDict->GetDictFor("OC");
60   m_bIsMask =
61       !m_pDict->KeyExist("ColorSpace") || m_pDict->GetIntegerFor("ImageMask");
62   m_bInterpolate = !!m_pDict->GetIntegerFor("Interpolate");
63   m_Height = m_pDict->GetIntegerFor("Height");
64   m_Width = m_pDict->GetIntegerFor("Width");
65 }
66 
ConvertStreamToIndirectObject()67 void CPDF_Image::ConvertStreamToIndirectObject() {
68   if (!m_pStream->IsInline())
69     return;
70 
71   ASSERT(m_pStream.IsOwned());
72   m_pDocument->AddIndirectObject(m_pStream.Release());
73 }
74 
GetDict() const75 CPDF_Dictionary* CPDF_Image::GetDict() const {
76   return m_pStream ? m_pStream->GetDict() : nullptr;
77 }
78 
InitJPEG(uint8_t * pData,uint32_t size)79 std::unique_ptr<CPDF_Dictionary> CPDF_Image::InitJPEG(uint8_t* pData,
80                                                       uint32_t size) {
81   int32_t width;
82   int32_t height;
83   int32_t num_comps;
84   int32_t bits;
85   bool color_trans;
86   if (!CPDF_ModuleMgr::Get()->GetJpegModule()->LoadInfo(
87           pData, size, &width, &height, &num_comps, &bits, &color_trans)) {
88     return nullptr;
89   }
90 
91   auto pDict =
92       pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
93   pDict->SetNewFor<CPDF_Name>("Type", "XObject");
94   pDict->SetNewFor<CPDF_Name>("Subtype", "Image");
95   pDict->SetNewFor<CPDF_Number>("Width", width);
96   pDict->SetNewFor<CPDF_Number>("Height", height);
97   const char* csname = nullptr;
98   if (num_comps == 1) {
99     csname = "DeviceGray";
100   } else if (num_comps == 3) {
101     csname = "DeviceRGB";
102   } else if (num_comps == 4) {
103     csname = "DeviceCMYK";
104     CPDF_Array* pDecode = pDict->SetNewFor<CPDF_Array>("Decode");
105     for (int n = 0; n < 4; n++) {
106       pDecode->AddNew<CPDF_Number>(1);
107       pDecode->AddNew<CPDF_Number>(0);
108     }
109   }
110   pDict->SetNewFor<CPDF_Name>("ColorSpace", csname);
111   pDict->SetNewFor<CPDF_Number>("BitsPerComponent", bits);
112   pDict->SetNewFor<CPDF_Name>("Filter", "DCTDecode");
113   if (!color_trans) {
114     CPDF_Dictionary* pParms = pDict->SetNewFor<CPDF_Dictionary>("DecodeParms");
115     pParms->SetNewFor<CPDF_Number>("ColorTransform", 0);
116   }
117   m_bIsMask = false;
118   m_Width = width;
119   m_Height = height;
120   if (!m_pStream)
121     m_pStream = pdfium::MakeUnique<CPDF_Stream>();
122   return pDict;
123 }
124 
SetJpegImage(const RetainPtr<IFX_SeekableReadStream> & pFile)125 void CPDF_Image::SetJpegImage(const RetainPtr<IFX_SeekableReadStream>& pFile) {
126   uint32_t size = pdfium::base::checked_cast<uint32_t>(pFile->GetSize());
127   if (!size)
128     return;
129 
130   uint32_t dwEstimateSize = std::min(size, 8192U);
131   std::vector<uint8_t> data(dwEstimateSize);
132   if (!pFile->ReadBlock(data.data(), 0, dwEstimateSize))
133     return;
134 
135   std::unique_ptr<CPDF_Dictionary> pDict =
136       InitJPEG(data.data(), dwEstimateSize);
137   if (!pDict && size > dwEstimateSize) {
138     data.resize(size);
139     pFile->ReadBlock(data.data(), 0, size);
140     pDict = InitJPEG(data.data(), size);
141   }
142   if (!pDict)
143     return;
144 
145   m_pStream->InitStreamFromFile(pFile, std::move(pDict));
146 }
147 
SetJpegImageInline(const RetainPtr<IFX_SeekableReadStream> & pFile)148 void CPDF_Image::SetJpegImageInline(
149     const RetainPtr<IFX_SeekableReadStream>& pFile) {
150   uint32_t size = pdfium::base::checked_cast<uint32_t>(pFile->GetSize());
151   if (!size)
152     return;
153 
154   std::vector<uint8_t> data(size);
155   if (!pFile->ReadBlock(data.data(), 0, size))
156     return;
157 
158   std::unique_ptr<CPDF_Dictionary> pDict = InitJPEG(data.data(), size);
159   if (!pDict)
160     return;
161 
162   m_pStream->InitStream(&(data[0]), size, std::move(pDict));
163 }
164 
SetImage(const RetainPtr<CFX_DIBitmap> & pBitmap)165 void CPDF_Image::SetImage(const RetainPtr<CFX_DIBitmap>& pBitmap) {
166   int32_t BitmapWidth = pBitmap->GetWidth();
167   int32_t BitmapHeight = pBitmap->GetHeight();
168   if (BitmapWidth < 1 || BitmapHeight < 1)
169     return;
170 
171   auto pDict =
172       pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
173   pDict->SetNewFor<CPDF_Name>("Type", "XObject");
174   pDict->SetNewFor<CPDF_Name>("Subtype", "Image");
175   pDict->SetNewFor<CPDF_Number>("Width", BitmapWidth);
176   pDict->SetNewFor<CPDF_Number>("Height", BitmapHeight);
177 
178   const int32_t bpp = pBitmap->GetBPP();
179   size_t dest_pitch = 0;
180   bool bCopyWithoutAlpha = true;
181   if (bpp == 1) {
182     int32_t reset_a = 0;
183     int32_t reset_r = 0;
184     int32_t reset_g = 0;
185     int32_t reset_b = 0;
186     int32_t set_a = 0;
187     int32_t set_r = 0;
188     int32_t set_g = 0;
189     int32_t set_b = 0;
190     if (!pBitmap->IsAlphaMask()) {
191       std::tie(reset_a, reset_r, reset_g, reset_b) =
192           ArgbDecode(pBitmap->GetPaletteArgb(0));
193       std::tie(set_a, set_r, set_g, set_b) =
194           ArgbDecode(pBitmap->GetPaletteArgb(1));
195     }
196     if (set_a == 0 || reset_a == 0) {
197       pDict->SetNewFor<CPDF_Boolean>("ImageMask", true);
198       if (reset_a == 0) {
199         CPDF_Array* pArray = pDict->SetNewFor<CPDF_Array>("Decode");
200         pArray->AddNew<CPDF_Number>(1);
201         pArray->AddNew<CPDF_Number>(0);
202       }
203     } else {
204       CPDF_Array* pCS = pDict->SetNewFor<CPDF_Array>("ColorSpace");
205       pCS->AddNew<CPDF_Name>("Indexed");
206       pCS->AddNew<CPDF_Name>("DeviceRGB");
207       pCS->AddNew<CPDF_Number>(1);
208       ByteString ct;
209       char* pBuf = ct.GetBuffer(6);
210       pBuf[0] = (char)reset_r;
211       pBuf[1] = (char)reset_g;
212       pBuf[2] = (char)reset_b;
213       pBuf[3] = (char)set_r;
214       pBuf[4] = (char)set_g;
215       pBuf[5] = (char)set_b;
216       ct.ReleaseBuffer(6);
217       pCS->AddNew<CPDF_String>(ct, true);
218     }
219     pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 1);
220     dest_pitch = (BitmapWidth + 7) / 8;
221   } else if (bpp == 8) {
222     int32_t iPalette = pBitmap->GetPaletteSize();
223     if (iPalette > 0) {
224       CPDF_Array* pCS = m_pDocument->NewIndirect<CPDF_Array>();
225       pCS->AddNew<CPDF_Name>("Indexed");
226       pCS->AddNew<CPDF_Name>("DeviceRGB");
227       pCS->AddNew<CPDF_Number>(iPalette - 1);
228       std::unique_ptr<uint8_t, FxFreeDeleter> pColorTable(
229           FX_Alloc2D(uint8_t, iPalette, 3));
230       uint8_t* ptr = pColorTable.get();
231       for (int32_t i = 0; i < iPalette; i++) {
232         uint32_t argb = pBitmap->GetPaletteArgb(i);
233         ptr[0] = (uint8_t)(argb >> 16);
234         ptr[1] = (uint8_t)(argb >> 8);
235         ptr[2] = (uint8_t)argb;
236         ptr += 3;
237       }
238       auto pNewDict =
239           pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
240       CPDF_Stream* pCTS = m_pDocument->NewIndirect<CPDF_Stream>(
241           std::move(pColorTable), iPalette * 3, std::move(pNewDict));
242       pCS->AddNew<CPDF_Reference>(m_pDocument.Get(), pCTS->GetObjNum());
243       pDict->SetNewFor<CPDF_Reference>("ColorSpace", m_pDocument.Get(),
244                                        pCS->GetObjNum());
245     } else {
246       pDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
247     }
248     pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
249     dest_pitch = BitmapWidth;
250   } else {
251     pDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceRGB");
252     pDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
253     dest_pitch = BitmapWidth * 3;
254     bCopyWithoutAlpha = false;
255   }
256 
257   RetainPtr<CFX_DIBitmap> pMaskBitmap;
258   if (pBitmap->HasAlpha())
259     pMaskBitmap = pBitmap->CloneAlphaMask();
260 
261   if (pMaskBitmap) {
262     int32_t maskWidth = pMaskBitmap->GetWidth();
263     int32_t maskHeight = pMaskBitmap->GetHeight();
264     std::unique_ptr<uint8_t, FxFreeDeleter> mask_buf;
265     int32_t mask_size = 0;
266     auto pMaskDict =
267         pdfium::MakeUnique<CPDF_Dictionary>(m_pDocument->GetByteStringPool());
268     pMaskDict->SetNewFor<CPDF_Name>("Type", "XObject");
269     pMaskDict->SetNewFor<CPDF_Name>("Subtype", "Image");
270     pMaskDict->SetNewFor<CPDF_Number>("Width", maskWidth);
271     pMaskDict->SetNewFor<CPDF_Number>("Height", maskHeight);
272     pMaskDict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
273     pMaskDict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
274     if (pMaskBitmap->GetFormat() != FXDIB_1bppMask) {
275       mask_buf.reset(FX_Alloc2D(uint8_t, maskHeight, maskWidth));
276       mask_size = maskHeight * maskWidth;  // Safe since checked alloc returned.
277       for (int32_t a = 0; a < maskHeight; a++) {
278         memcpy(mask_buf.get() + a * maskWidth, pMaskBitmap->GetScanline(a),
279                maskWidth);
280       }
281     }
282     pMaskDict->SetNewFor<CPDF_Number>("Length", mask_size);
283     CPDF_Stream* pNewStream = m_pDocument->NewIndirect<CPDF_Stream>(
284         std::move(mask_buf), mask_size, std::move(pMaskDict));
285     pDict->SetNewFor<CPDF_Reference>("SMask", m_pDocument.Get(),
286                                      pNewStream->GetObjNum());
287   }
288 
289   uint8_t* src_buf = pBitmap->GetBuffer();
290   int32_t src_pitch = pBitmap->GetPitch();
291   uint8_t* dest_buf = FX_Alloc2D(uint8_t, dest_pitch, BitmapHeight);
292   // Safe as checked alloc returned.
293   size_t dest_size = dest_pitch * BitmapHeight;
294   uint8_t* pDest = dest_buf;
295   if (bCopyWithoutAlpha) {
296     for (int32_t i = 0; i < BitmapHeight; i++) {
297       memcpy(pDest, src_buf, dest_pitch);
298       pDest += dest_pitch;
299       src_buf += src_pitch;
300     }
301   } else {
302     int32_t src_offset = 0;
303     int32_t dest_offset = 0;
304     for (int32_t row = 0; row < BitmapHeight; row++) {
305       src_offset = row * src_pitch;
306       for (int32_t column = 0; column < BitmapWidth; column++) {
307         float alpha = 1;
308         pDest[dest_offset] = (uint8_t)(src_buf[src_offset + 2] * alpha);
309         pDest[dest_offset + 1] = (uint8_t)(src_buf[src_offset + 1] * alpha);
310         pDest[dest_offset + 2] = (uint8_t)(src_buf[src_offset] * alpha);
311         dest_offset += 3;
312         src_offset += bpp == 24 ? 3 : 4;
313       }
314 
315       pDest += dest_pitch;
316       dest_offset = 0;
317     }
318   }
319   if (!m_pStream)
320     m_pStream = pdfium::MakeUnique<CPDF_Stream>();
321 
322   m_pStream->InitStream(dest_buf, dest_size, std::move(pDict));
323   m_bIsMask = pBitmap->IsAlphaMask();
324   m_Width = BitmapWidth;
325   m_Height = BitmapHeight;
326   FX_Free(dest_buf);
327 }
328 
ResetCache(CPDF_Page * pPage,const RetainPtr<CFX_DIBitmap> & pBitmap)329 void CPDF_Image::ResetCache(CPDF_Page* pPage,
330                             const RetainPtr<CFX_DIBitmap>& pBitmap) {
331   RetainPtr<CPDF_Image> pHolder(this);
332   pPage->GetRenderCache()->ResetBitmap(pHolder, pBitmap);
333 }
334 
LoadDIBSource() const335 RetainPtr<CFX_DIBSource> CPDF_Image::LoadDIBSource() const {
336   auto source = pdfium::MakeRetain<CPDF_DIBSource>();
337   if (!source->Load(m_pDocument.Get(), m_pStream.Get()))
338     return nullptr;
339 
340   return source;
341 }
342 
DetachBitmap()343 RetainPtr<CFX_DIBSource> CPDF_Image::DetachBitmap() {
344   return std::move(m_pDIBSource);
345 }
346 
DetachMask()347 RetainPtr<CFX_DIBSource> CPDF_Image::DetachMask() {
348   return std::move(m_pMask);
349 }
350 
StartLoadDIBSource(CPDF_Dictionary * pFormResource,CPDF_Dictionary * pPageResource,bool bStdCS,uint32_t GroupFamily,bool bLoadMask)351 bool CPDF_Image::StartLoadDIBSource(CPDF_Dictionary* pFormResource,
352                                     CPDF_Dictionary* pPageResource,
353                                     bool bStdCS,
354                                     uint32_t GroupFamily,
355                                     bool bLoadMask) {
356   auto source = pdfium::MakeRetain<CPDF_DIBSource>();
357   int ret = source->StartLoadDIBSource(m_pDocument.Get(), m_pStream.Get(), true,
358                                        pFormResource, pPageResource, bStdCS,
359                                        GroupFamily, bLoadMask);
360   if (!ret) {
361     m_pDIBSource.Reset();
362     return false;
363   }
364   m_pDIBSource = source;
365   if (ret == 2)
366     return true;
367 
368   m_pMask = source->DetachMask();
369   m_MatteColor = source->GetMatteColor();
370   return false;
371 }
372 
Continue(IFX_PauseIndicator * pPause)373 bool CPDF_Image::Continue(IFX_PauseIndicator* pPause) {
374   RetainPtr<CPDF_DIBSource> pSource = m_pDIBSource.As<CPDF_DIBSource>();
375   int ret = pSource->ContinueLoadDIBSource(pPause);
376   if (!ret) {
377     m_pDIBSource.Reset();
378     return false;
379   }
380   if (ret == 2)
381     return true;
382 
383   m_pMask = pSource->DetachMask();
384   m_MatteColor = pSource->GetMatteColor();
385   return false;
386 }
387