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 32 CPDF_Image::CPDF_Image(CPDF_Document* pDoc) : m_pDocument(pDoc) {} 33 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 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 54 CPDF_Image::~CPDF_Image() {} 55 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 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 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 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 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 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 323 void CPDF_Image::ResetCache(CPDF_Page* pPage, const CFX_DIBitmap* pBitmap) { 324 pPage->GetRenderCache()->ResetBitmap(m_pStream.Get(), pBitmap); 325 } 326 327 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 335 CFX_DIBSource* CPDF_Image::DetachBitmap() { 336 CFX_DIBSource* pBitmap = m_pDIBSource; 337 m_pDIBSource = nullptr; 338 return pBitmap; 339 } 340 341 CFX_DIBSource* CPDF_Image::DetachMask() { 342 CFX_DIBSource* pBitmap = m_pMask; 343 m_pMask = nullptr; 344 return pBitmap; 345 } 346 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 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