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