xref: /aosp_15_r20/external/pdfium/core/fxcodec/tiff/tiff_decoder.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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