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