Home | History | Annotate | Download | only in page
      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