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