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