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 ()__anona9a3e6c70111::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