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 34 CPDF_Image::CPDF_Image(CPDF_Document* pDoc) : m_pDocument(pDoc) {} 35 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 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 56 CPDF_Image::~CPDF_Image() {} 57 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 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 75 CPDF_Dictionary* CPDF_Image::GetDict() const { 76 return m_pStream ? m_pStream->GetDict() : nullptr; 77 } 78 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 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 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 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 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 335 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 343 RetainPtr<CFX_DIBSource> CPDF_Image::DetachBitmap() { 344 return std::move(m_pDIBSource); 345 } 346 347 RetainPtr<CFX_DIBSource> CPDF_Image::DetachMask() { 348 return std::move(m_pMask); 349 } 350 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 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