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