Home | History | Annotate | Download | only in codec
      1 // Copyright 2014 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/fxcodec/codec/ccodec_tiffmodule.h"
      8 
      9 #include <limits>
     10 
     11 #include "core/fxcodec/codec/codec_int.h"
     12 #include "core/fxcodec/fx_codec.h"
     13 #include "core/fxcrt/fx_safe_types.h"
     14 #include "core/fxcrt/fx_stream.h"
     15 #include "core/fxcrt/retain_ptr.h"
     16 #include "core/fxge/dib/cfx_dibitmap.h"
     17 #include "core/fxge/fx_dib.h"
     18 #include "third_party/base/logging.h"
     19 #include "third_party/base/ptr_util.h"
     20 
     21 extern "C" {
     22 #include "third_party/libtiff/tiffiop.h"
     23 }
     24 
     25 class CTiffContext : public CCodec_TiffModule::Context {
     26  public:
     27   CTiffContext();
     28   ~CTiffContext() override;
     29 
     30   bool InitDecoder(const RetainPtr<IFX_SeekableReadStream>& file_ptr);
     31   bool LoadFrameInfo(int32_t frame,
     32                      int32_t* width,
     33                      int32_t* height,
     34                      int32_t* comps,
     35                      int32_t* bpc,
     36                      CFX_DIBAttribute* pAttribute);
     37   bool Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap);
     38 
     39   RetainPtr<IFX_SeekableReadStream> io_in() const { return m_io_in; }
     40   uint32_t offset() const { return m_offset; }
     41   void set_offset(uint32_t offset) { m_offset = offset; }
     42 
     43  private:
     44   bool IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const;
     45   void SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap, uint16_t bps);
     46   bool Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
     47                      int32_t height,
     48                      int32_t width,
     49                      uint16_t bps,
     50                      uint16_t spp);
     51   bool Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
     52                      int32_t height,
     53                      int32_t width,
     54                      uint16_t bps,
     55                      uint16_t spp);
     56   bool Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
     57                       int32_t height,
     58                       int32_t width,
     59                       uint16_t bps,
     60                       uint16_t spp);
     61 
     62   RetainPtr<IFX_SeekableReadStream> m_io_in;
     63   uint32_t m_offset;
     64   TIFF* m_tif_ctx;
     65 };
     66 
     67 void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz) {
     68   return FXMEM_DefaultCalloc(nmemb, siz);
     69 }
     70 
     71 void* _TIFFmalloc(tmsize_t size) {
     72   return FXMEM_DefaultAlloc(size);
     73 }
     74 
     75 void _TIFFfree(void* ptr) {
     76   if (ptr)
     77     FXMEM_DefaultFree(ptr);
     78 }
     79 
     80 void* _TIFFrealloc(void* ptr, tmsize_t size) {
     81   return FXMEM_DefaultRealloc(ptr, size);
     82 }
     83 
     84 void _TIFFmemset(void* ptr, int val, tmsize_t size) {
     85   memset(ptr, val, static_cast<size_t>(size));
     86 }
     87 
     88 void _TIFFmemcpy(void* des, const void* src, tmsize_t size) {
     89   memcpy(des, src, static_cast<size_t>(size));
     90 }
     91 
     92 int _TIFFmemcmp(const void* ptr1, const void* ptr2, tmsize_t size) {
     93   return memcmp(ptr1, ptr2, static_cast<size_t>(size));
     94 }
     95 
     96 int _TIFFIfMultiplicationOverflow(tmsize_t op1, tmsize_t op2) {
     97   return op1 > std::numeric_limits<tmsize_t>::max() / op2;
     98 }
     99 
    100 TIFFErrorHandler _TIFFwarningHandler = nullptr;
    101 TIFFErrorHandler _TIFFerrorHandler = nullptr;
    102 
    103 namespace {
    104 
    105 tsize_t tiff_read(thandle_t context, tdata_t buf, tsize_t length) {
    106   CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
    107   FX_SAFE_UINT32 increment = pTiffContext->offset();
    108   increment += length;
    109   if (!increment.IsValid())
    110     return 0;
    111 
    112   FX_FILESIZE offset = pTiffContext->offset();
    113   if (!pTiffContext->io_in()->ReadBlock(buf, offset, length))
    114     return 0;
    115 
    116   pTiffContext->set_offset(increment.ValueOrDie());
    117   if (offset + length > pTiffContext->io_in()->GetSize())
    118     return pTiffContext->io_in()->GetSize() - offset;
    119 
    120   return length;
    121 }
    122 
    123 tsize_t tiff_write(thandle_t context, tdata_t buf, tsize_t length) {
    124   NOTREACHED();
    125   return 0;
    126 }
    127 
    128 toff_t tiff_seek(thandle_t context, toff_t offset, int whence) {
    129   CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
    130   FX_SAFE_FILESIZE safe_offset = offset;
    131   if (!safe_offset.IsValid())
    132     return static_cast<toff_t>(-1);
    133   FX_FILESIZE file_offset = safe_offset.ValueOrDie();
    134 
    135   switch (whence) {
    136     case 0: {
    137       if (file_offset > pTiffContext->io_in()->GetSize())
    138         return static_cast<toff_t>(-1);
    139       pTiffContext->set_offset(file_offset);
    140       return pTiffContext->offset();
    141     }
    142     case 1: {
    143       FX_SAFE_UINT32 new_increment = pTiffContext->offset();
    144       new_increment += file_offset;
    145       if (!new_increment.IsValid())
    146         return static_cast<toff_t>(-1);
    147       pTiffContext->set_offset(new_increment.ValueOrDie());
    148       return pTiffContext->offset();
    149     }
    150     case 2: {
    151       if (pTiffContext->io_in()->GetSize() < file_offset)
    152         return static_cast<toff_t>(-1);
    153       pTiffContext->set_offset(pTiffContext->io_in()->GetSize() - file_offset);
    154       return pTiffContext->offset();
    155     }
    156     default:
    157       return static_cast<toff_t>(-1);
    158   }
    159 }
    160 
    161 int tiff_close(thandle_t context) {
    162   return 0;
    163 }
    164 
    165 toff_t tiff_get_size(thandle_t context) {
    166   CTiffContext* pTiffContext = reinterpret_cast<CTiffContext*>(context);
    167   return static_cast<toff_t>(pTiffContext->io_in()->GetSize());
    168 }
    169 
    170 int tiff_map(thandle_t context, tdata_t*, toff_t*) {
    171   return 0;
    172 }
    173 
    174 void tiff_unmap(thandle_t context, tdata_t, toff_t) {}
    175 
    176 TIFF* tiff_open(void* context, const char* mode) {
    177   TIFF* tif = TIFFClientOpen("Tiff Image", mode, (thandle_t)context, tiff_read,
    178                              tiff_write, tiff_seek, tiff_close, tiff_get_size,
    179                              tiff_map, tiff_unmap);
    180   if (tif) {
    181     tif->tif_fd = (int)(intptr_t)context;
    182   }
    183   return tif;
    184 }
    185 
    186 template <class T>
    187 bool Tiff_Exif_GetInfo(TIFF* tif_ctx, ttag_t tag, CFX_DIBAttribute* pAttr) {
    188   T val = 0;
    189   TIFFGetField(tif_ctx, tag, &val);
    190   if (!val)
    191     return false;
    192   T* ptr = FX_Alloc(T, 1);
    193   *ptr = val;
    194   pAttr->m_Exif[tag] = ptr;
    195   return true;
    196 }
    197 
    198 void Tiff_Exif_GetStringInfo(TIFF* tif_ctx,
    199                              ttag_t tag,
    200                              CFX_DIBAttribute* pAttr) {
    201   char* buf = nullptr;
    202   TIFFGetField(tif_ctx, tag, &buf);
    203   if (!buf)
    204     return;
    205   size_t size = strlen(buf);
    206   uint8_t* ptr = FX_Alloc(uint8_t, size + 1);
    207   memcpy(ptr, buf, size);
    208   ptr[size] = 0;
    209   pAttr->m_Exif[tag] = ptr;
    210 }
    211 
    212 void TiffBGRA2RGBA(uint8_t* pBuf, int32_t pixel, int32_t spp) {
    213   for (int32_t n = 0; n < pixel; n++) {
    214     uint8_t tmp = pBuf[0];
    215     pBuf[0] = pBuf[2];
    216     pBuf[2] = tmp;
    217     pBuf += spp;
    218   }
    219 }
    220 
    221 }  // namespace
    222 
    223 CTiffContext::CTiffContext()
    224     : m_io_in(nullptr), m_offset(0), m_tif_ctx(nullptr) {}
    225 
    226 CTiffContext::~CTiffContext() {
    227   if (m_tif_ctx)
    228     TIFFClose(m_tif_ctx);
    229 }
    230 
    231 bool CTiffContext::InitDecoder(
    232     const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
    233   m_io_in = file_ptr;
    234   m_tif_ctx = tiff_open(this, "r");
    235   return !!m_tif_ctx;
    236 }
    237 
    238 bool CTiffContext::LoadFrameInfo(int32_t frame,
    239                                  int32_t* width,
    240                                  int32_t* height,
    241                                  int32_t* comps,
    242                                  int32_t* bpc,
    243                                  CFX_DIBAttribute* pAttribute) {
    244   if (!TIFFSetDirectory(m_tif_ctx, (uint16)frame))
    245     return false;
    246 
    247   uint32_t tif_width = 0;
    248   uint32_t tif_height = 0;
    249   uint16_t tif_comps = 0;
    250   uint16_t tif_bpc = 0;
    251   uint32_t tif_rps = 0;
    252   TIFFGetField(m_tif_ctx, TIFFTAG_IMAGEWIDTH, &tif_width);
    253   TIFFGetField(m_tif_ctx, TIFFTAG_IMAGELENGTH, &tif_height);
    254   TIFFGetField(m_tif_ctx, TIFFTAG_SAMPLESPERPIXEL, &tif_comps);
    255   TIFFGetField(m_tif_ctx, TIFFTAG_BITSPERSAMPLE, &tif_bpc);
    256   TIFFGetField(m_tif_ctx, TIFFTAG_ROWSPERSTRIP, &tif_rps);
    257 
    258   if (pAttribute) {
    259     pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_INCH;
    260     if (TIFFGetField(m_tif_ctx, TIFFTAG_RESOLUTIONUNIT,
    261                      &pAttribute->m_wDPIUnit)) {
    262       pAttribute->m_wDPIUnit--;
    263     }
    264     Tiff_Exif_GetInfo<uint16_t>(m_tif_ctx, TIFFTAG_ORIENTATION, pAttribute);
    265     if (Tiff_Exif_GetInfo<float>(m_tif_ctx, TIFFTAG_XRESOLUTION, pAttribute)) {
    266       void* val = pAttribute->m_Exif[TIFFTAG_XRESOLUTION];
    267       float fDpi = val ? *reinterpret_cast<float*>(val) : 0;
    268       pAttribute->m_nXDPI = (int32_t)(fDpi + 0.5f);
    269     }
    270     if (Tiff_Exif_GetInfo<float>(m_tif_ctx, TIFFTAG_YRESOLUTION, pAttribute)) {
    271       void* val = pAttribute->m_Exif[TIFFTAG_YRESOLUTION];
    272       float fDpi = val ? *reinterpret_cast<float*>(val) : 0;
    273       pAttribute->m_nYDPI = (int32_t)(fDpi + 0.5f);
    274     }
    275     Tiff_Exif_GetStringInfo(m_tif_ctx, TIFFTAG_IMAGEDESCRIPTION, pAttribute);
    276     Tiff_Exif_GetStringInfo(m_tif_ctx, TIFFTAG_MAKE, pAttribute);
    277     Tiff_Exif_GetStringInfo(m_tif_ctx, TIFFTAG_MODEL, pAttribute);
    278   }
    279   pdfium::base::CheckedNumeric<int32_t> checked_width = tif_width;
    280   pdfium::base::CheckedNumeric<int32_t> checked_height = tif_height;
    281   if (!checked_width.IsValid() || !checked_height.IsValid())
    282     return false;
    283 
    284   *width = checked_width.ValueOrDie();
    285   *height = checked_height.ValueOrDie();
    286   *comps = tif_comps;
    287   *bpc = tif_bpc;
    288   if (tif_rps > tif_height) {
    289     tif_rps = tif_height;
    290     TIFFSetField(m_tif_ctx, TIFFTAG_ROWSPERSTRIP, tif_rps);
    291   }
    292   return true;
    293 }
    294 
    295 bool CTiffContext::IsSupport(const RetainPtr<CFX_DIBitmap>& pDIBitmap) const {
    296   if (TIFFIsTiled(m_tif_ctx))
    297     return false;
    298 
    299   uint16_t photometric = 0;
    300   if (!TIFFGetField(m_tif_ctx, TIFFTAG_PHOTOMETRIC, &photometric))
    301     return false;
    302 
    303   switch (pDIBitmap->GetBPP()) {
    304     case 1:
    305     case 8:
    306       if (photometric != PHOTOMETRIC_PALETTE) {
    307         return false;
    308       }
    309       break;
    310     case 24:
    311       if (photometric != PHOTOMETRIC_RGB) {
    312         return false;
    313       }
    314       break;
    315     default:
    316       return false;
    317   }
    318   uint16_t planarconfig = 0;
    319   if (!TIFFGetFieldDefaulted(m_tif_ctx, TIFFTAG_PLANARCONFIG, &planarconfig))
    320     return false;
    321 
    322   return planarconfig != PLANARCONFIG_SEPARATE;
    323 }
    324 
    325 void CTiffContext::SetPalette(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
    326                               uint16_t bps) {
    327   uint16_t* red_orig = nullptr;
    328   uint16_t* green_orig = nullptr;
    329   uint16_t* blue_orig = nullptr;
    330   TIFFGetField(m_tif_ctx, TIFFTAG_COLORMAP, &red_orig, &green_orig, &blue_orig);
    331   for (int32_t i = (1L << bps) - 1; i >= 0; i--) {
    332 #define CVT(x) ((uint16_t)((x) >> 8))
    333     red_orig[i] = CVT(red_orig[i]);
    334     green_orig[i] = CVT(green_orig[i]);
    335     blue_orig[i] = CVT(blue_orig[i]);
    336 #undef CVT
    337   }
    338   int32_t len = 1 << bps;
    339   for (int32_t index = 0; index < len; index++) {
    340     uint32_t r = red_orig[index] & 0xFF;
    341     uint32_t g = green_orig[index] & 0xFF;
    342     uint32_t b = blue_orig[index] & 0xFF;
    343     uint32_t color = (uint32_t)b | ((uint32_t)g << 8) | ((uint32_t)r << 16) |
    344                      (((uint32)0xffL) << 24);
    345     pDIBitmap->SetPaletteArgb(index, color);
    346   }
    347 }
    348 
    349 bool CTiffContext::Decode1bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
    350                                  int32_t height,
    351                                  int32_t width,
    352                                  uint16_t bps,
    353                                  uint16_t spp) {
    354   if (pDIBitmap->GetBPP() != 1 || spp != 1 || bps != 1 ||
    355       !IsSupport(pDIBitmap)) {
    356     return false;
    357   }
    358   SetPalette(pDIBitmap, bps);
    359   int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx);
    360   uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
    361   if (!buf) {
    362     TIFFError(TIFFFileName(m_tif_ctx), "No space for scanline buffer");
    363     return false;
    364   }
    365   uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
    366   uint32_t pitch = pDIBitmap->GetPitch();
    367   for (int32_t row = 0; row < height; row++) {
    368     TIFFReadScanline(m_tif_ctx, buf, row, 0);
    369     for (int32_t j = 0; j < size; j++) {
    370       bitMapbuffer[row * pitch + j] = buf[j];
    371     }
    372   }
    373   _TIFFfree(buf);
    374   return true;
    375 }
    376 
    377 bool CTiffContext::Decode8bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
    378                                  int32_t height,
    379                                  int32_t width,
    380                                  uint16_t bps,
    381                                  uint16_t spp) {
    382   if (pDIBitmap->GetBPP() != 8 || spp != 1 || (bps != 4 && bps != 8) ||
    383       !IsSupport(pDIBitmap)) {
    384     return false;
    385   }
    386   SetPalette(pDIBitmap, bps);
    387   int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx);
    388   uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
    389   if (!buf) {
    390     TIFFError(TIFFFileName(m_tif_ctx), "No space for scanline buffer");
    391     return false;
    392   }
    393   uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
    394   uint32_t pitch = pDIBitmap->GetPitch();
    395   for (int32_t row = 0; row < height; row++) {
    396     TIFFReadScanline(m_tif_ctx, buf, row, 0);
    397     for (int32_t j = 0; j < size; j++) {
    398       switch (bps) {
    399         case 4:
    400           bitMapbuffer[row * pitch + 2 * j + 0] = (buf[j] & 0xF0) >> 4;
    401           bitMapbuffer[row * pitch + 2 * j + 1] = (buf[j] & 0x0F) >> 0;
    402           break;
    403         case 8:
    404           bitMapbuffer[row * pitch + j] = buf[j];
    405           break;
    406       }
    407     }
    408   }
    409   _TIFFfree(buf);
    410   return true;
    411 }
    412 
    413 bool CTiffContext::Decode24bppRGB(const RetainPtr<CFX_DIBitmap>& pDIBitmap,
    414                                   int32_t height,
    415                                   int32_t width,
    416                                   uint16_t bps,
    417                                   uint16_t spp) {
    418   if (pDIBitmap->GetBPP() != 24 || !IsSupport(pDIBitmap))
    419     return false;
    420 
    421   int32_t size = (int32_t)TIFFScanlineSize(m_tif_ctx);
    422   uint8_t* buf = (uint8_t*)_TIFFmalloc(size);
    423   if (!buf) {
    424     TIFFError(TIFFFileName(m_tif_ctx), "No space for scanline buffer");
    425     return false;
    426   }
    427   uint8_t* bitMapbuffer = (uint8_t*)pDIBitmap->GetBuffer();
    428   uint32_t pitch = pDIBitmap->GetPitch();
    429   for (int32_t row = 0; row < height; row++) {
    430     TIFFReadScanline(m_tif_ctx, buf, row, 0);
    431     for (int32_t j = 0; j < size - 2; j += 3) {
    432       bitMapbuffer[row * pitch + j + 0] = buf[j + 2];
    433       bitMapbuffer[row * pitch + j + 1] = buf[j + 1];
    434       bitMapbuffer[row * pitch + j + 2] = buf[j + 0];
    435     }
    436   }
    437   _TIFFfree(buf);
    438   return true;
    439 }
    440 
    441 bool CTiffContext::Decode(const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
    442   uint32_t img_wid = pDIBitmap->GetWidth();
    443   uint32_t img_hei = pDIBitmap->GetHeight();
    444   uint32_t width = 0;
    445   uint32_t height = 0;
    446   TIFFGetField(m_tif_ctx, TIFFTAG_IMAGEWIDTH, &width);
    447   TIFFGetField(m_tif_ctx, TIFFTAG_IMAGELENGTH, &height);
    448   if (img_wid != width || img_hei != height)
    449     return false;
    450 
    451   if (pDIBitmap->GetBPP() == 32) {
    452     uint16_t rotation = ORIENTATION_TOPLEFT;
    453     TIFFGetField(m_tif_ctx, TIFFTAG_ORIENTATION, &rotation);
    454     if (TIFFReadRGBAImageOriented(m_tif_ctx, img_wid, img_hei,
    455                                   (uint32*)pDIBitmap->GetBuffer(), rotation,
    456                                   1)) {
    457       for (uint32_t row = 0; row < img_hei; row++) {
    458         uint8_t* row_buf = (uint8_t*)pDIBitmap->GetScanline(row);
    459         TiffBGRA2RGBA(row_buf, img_wid, 4);
    460       }
    461       return true;
    462     }
    463   }
    464   uint16_t spp = 0;
    465   uint16_t bps = 0;
    466   TIFFGetField(m_tif_ctx, TIFFTAG_SAMPLESPERPIXEL, &spp);
    467   TIFFGetField(m_tif_ctx, TIFFTAG_BITSPERSAMPLE, &bps);
    468   FX_SAFE_UINT32 safe_bpp = bps;
    469   safe_bpp *= spp;
    470   if (!safe_bpp.IsValid())
    471     return false;
    472   uint32_t bpp = safe_bpp.ValueOrDie();
    473   if (bpp == 1)
    474     return Decode1bppRGB(pDIBitmap, height, width, bps, spp);
    475   if (bpp <= 8)
    476     return Decode8bppRGB(pDIBitmap, height, width, bps, spp);
    477   if (bpp <= 24)
    478     return Decode24bppRGB(pDIBitmap, height, width, bps, spp);
    479   return false;
    480 }
    481 
    482 std::unique_ptr<CCodec_TiffModule::Context> CCodec_TiffModule::CreateDecoder(
    483     const RetainPtr<IFX_SeekableReadStream>& file_ptr) {
    484   auto pDecoder = pdfium::MakeUnique<CTiffContext>();
    485   if (!pDecoder->InitDecoder(file_ptr))
    486     return nullptr;
    487 
    488   return pDecoder;
    489 }
    490 
    491 bool CCodec_TiffModule::LoadFrameInfo(Context* pContext,
    492                                       int32_t frame,
    493                                       int32_t* width,
    494                                       int32_t* height,
    495                                       int32_t* comps,
    496                                       int32_t* bpc,
    497                                       CFX_DIBAttribute* pAttribute) {
    498   auto* ctx = static_cast<CTiffContext*>(pContext);
    499   return ctx->LoadFrameInfo(frame, width, height, comps, bpc, pAttribute);
    500 }
    501 
    502 bool CCodec_TiffModule::Decode(Context* pContext,
    503                                const RetainPtr<CFX_DIBitmap>& pDIBitmap) {
    504   auto* ctx = static_cast<CTiffContext*>(pContext);
    505   return ctx->Decode(pDIBitmap);
    506 }
    507