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/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