bb60300d7de7bb85c7aa26fde4a76a3bfb5c9c89
[pdfium.git] / core / src / fxcodec / codec / fx_codec_jpeg.cpp
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 <setjmp.h>
8
9 #include "../../../include/fxcodec/fx_codec.h"
10 #include "../../../include/fxcrt/fx_safe_types.h"
11 #include "../../../include/fxge/fx_dib.h"
12 #include "codec_int.h"
13
14 extern "C" {
15     static void _JpegScanSOI(const uint8_t*& src_buf, FX_DWORD& src_size)
16     {
17         if (src_size == 0) {
18             return;
19         }
20         FX_DWORD offset = 0;
21         while (offset < src_size - 1) {
22             if (src_buf[offset] == 0xff && src_buf[offset + 1] == 0xd8) {
23                 src_buf += offset;
24                 src_size -= offset;
25                 return;
26             }
27             offset ++;
28         }
29     }
30 };
31 extern "C" {
32 #undef FAR
33 #include "../../fx_jpeglib.h"
34 }
35 extern "C" {
36     static void _src_do_nothing(struct jpeg_decompress_struct* cinfo) {}
37 };
38 extern "C" {
39     static void _error_fatal(j_common_ptr cinfo)
40     {
41         longjmp(*(jmp_buf*)cinfo->client_data, -1);
42     }
43 };
44 extern "C" {
45     static void _src_skip_data(struct jpeg_decompress_struct* cinfo, long num)
46     {
47         if (num > (long)cinfo->src->bytes_in_buffer) {
48             _error_fatal((j_common_ptr)cinfo);
49         }
50         cinfo->src->next_input_byte += num;
51         cinfo->src->bytes_in_buffer -= num;
52     }
53 };
54 extern "C" {
55     static boolean _src_fill_buffer(j_decompress_ptr cinfo)
56     {
57         return 0;
58     }
59 };
60 extern "C" {
61     static boolean _src_resync(j_decompress_ptr cinfo, int desired)
62     {
63         return 0;
64     }
65 };
66 extern "C" {
67     static void _error_do_nothing(j_common_ptr cinfo) {}
68 };
69 extern "C" {
70     static void _error_do_nothing1(j_common_ptr cinfo, int) {}
71 };
72 extern "C" {
73     static void _error_do_nothing2(j_common_ptr cinfo, char*) {}
74 };
75 #define JPEG_MARKER_EXIF                (JPEG_APP0 + 1)
76 #define JPEG_MARKER_ICC                 (JPEG_APP0 + 2)
77 #define JPEG_MARKER_AUTHORTIME  (JPEG_APP0 + 3)
78 #define JPEG_MARKER_MAXSIZE     0xFFFF
79 #define JPEG_OVERHEAD_LEN       14
80 static  FX_BOOL _JpegEmbedIccProfile(j_compress_ptr cinfo, const uint8_t* icc_buf_ptr, FX_DWORD icc_length)
81 {
82     if(icc_buf_ptr == NULL || icc_length == 0) {
83         return FALSE;
84     }
85     FX_DWORD icc_segment_size = (JPEG_MARKER_MAXSIZE - 2 - JPEG_OVERHEAD_LEN);
86     FX_DWORD icc_segment_num = (icc_length / icc_segment_size) + 1;
87     if (icc_segment_num > 255)  {
88         return FALSE;
89     }
90     FX_DWORD icc_data_length = JPEG_OVERHEAD_LEN + (icc_segment_num > 1 ? icc_segment_size : icc_length);
91     uint8_t* icc_data = FX_Alloc(uint8_t, icc_data_length);
92     FXSYS_memcpy(icc_data, "\x49\x43\x43\x5f\x50\x52\x4f\x46\x49\x4c\x45\x00", 12);
93     icc_data[13] = (uint8_t)icc_segment_num;
94     for (uint8_t i = 0; i < (icc_segment_num - 1); i++) {
95         icc_data[12] = i + 1;
96         FXSYS_memcpy(icc_data + JPEG_OVERHEAD_LEN, icc_buf_ptr + i * icc_segment_size, icc_segment_size);
97         jpeg_write_marker(cinfo, JPEG_MARKER_ICC, icc_data, icc_data_length);
98     }
99     icc_data[12] = (uint8_t)icc_segment_num;
100     FX_DWORD icc_size = (icc_segment_num - 1) * icc_segment_size;
101     FXSYS_memcpy(icc_data + JPEG_OVERHEAD_LEN, icc_buf_ptr + icc_size, icc_length - icc_size);
102     jpeg_write_marker(cinfo, JPEG_MARKER_ICC, icc_data, JPEG_OVERHEAD_LEN + icc_length - icc_size);
103     FX_Free(icc_data);
104     return TRUE;
105 }
106 extern "C" {
107     static void _dest_do_nothing(j_compress_ptr cinfo) {}
108 };
109 extern "C" {
110     static boolean _dest_empty(j_compress_ptr cinfo)
111     {
112         return FALSE;
113     }
114 };
115 #define JPEG_BLOCK_SIZE 1048576
116 static void _JpegEncode(const CFX_DIBSource* pSource, uint8_t*& dest_buf, FX_STRSIZE& dest_size, int quality, const uint8_t* icc_buf, FX_DWORD icc_length)
117 {
118     struct jpeg_error_mgr jerr;
119     jerr.error_exit = _error_do_nothing;
120     jerr.emit_message = _error_do_nothing1;
121     jerr.output_message = _error_do_nothing;
122     jerr.format_message = _error_do_nothing2;
123     jerr.reset_error_mgr = _error_do_nothing;
124
125     struct jpeg_compress_struct cinfo;
126     memset(&cinfo, 0, sizeof(cinfo));
127     cinfo.err = &jerr;
128     jpeg_create_compress(&cinfo);
129     int Bpp = pSource->GetBPP() / 8;
130     FX_DWORD nComponents = Bpp >= 3 ? (pSource->IsCmykImage() ? 4 : 3) : 1;
131     FX_DWORD pitch = pSource->GetPitch();
132     FX_DWORD width = pdfium::base::checked_cast<FX_DWORD>(pSource->GetWidth());
133     FX_DWORD height = pdfium::base::checked_cast<FX_DWORD>(pSource->GetHeight());
134     FX_SAFE_DWORD safe_buf_len = width;
135     safe_buf_len *= height;
136     safe_buf_len *= nComponents;
137     safe_buf_len += 1024;
138     if (icc_length) {
139         safe_buf_len += 255 * 18;
140         safe_buf_len += icc_length;
141     }
142     FX_DWORD dest_buf_length = 0;
143     if (!safe_buf_len.IsValid()) {
144         dest_buf = nullptr;
145     } else {
146         dest_buf_length = safe_buf_len.ValueOrDie();
147         dest_buf = FX_TryAlloc(uint8_t, dest_buf_length);
148         const int MIN_TRY_BUF_LEN = 1024;
149         while (!dest_buf && dest_buf_length > MIN_TRY_BUF_LEN) {
150             dest_buf_length >>= 1;
151             dest_buf = FX_TryAlloc(uint8_t, dest_buf_length);
152         }
153     }
154     if (!dest_buf) {
155         FX_OutOfMemoryTerminate(); 
156     }
157     struct jpeg_destination_mgr dest;
158     dest.init_destination = _dest_do_nothing;
159     dest.term_destination = _dest_do_nothing;
160     dest.empty_output_buffer = _dest_empty;
161     dest.next_output_byte = dest_buf;
162     dest.free_in_buffer = dest_buf_length;
163     cinfo.dest = &dest;
164     cinfo.image_width = width;
165     cinfo.image_height = height;
166     cinfo.input_components = nComponents;
167     if (nComponents == 1) {
168         cinfo.in_color_space = JCS_GRAYSCALE;
169     } else if (nComponents == 3) {
170         cinfo.in_color_space = JCS_RGB;
171     } else {
172         cinfo.in_color_space = JCS_CMYK;
173     }
174     uint8_t* line_buf = NULL;
175     if (nComponents > 1) {
176         line_buf = FX_Alloc2D(uint8_t, width, nComponents);
177     }
178     jpeg_set_defaults(&cinfo);
179     if(quality != 75) {
180         jpeg_set_quality(&cinfo, quality, TRUE);
181     }
182     jpeg_start_compress(&cinfo, TRUE);
183     _JpegEmbedIccProfile(&cinfo, icc_buf, icc_length);
184     JSAMPROW row_pointer[1];
185     JDIMENSION row;
186     while (cinfo.next_scanline < cinfo.image_height) {
187         const uint8_t* src_scan = pSource->GetScanline(cinfo.next_scanline);
188         if (nComponents > 1) {
189             uint8_t* dest_scan = line_buf;
190             if (nComponents == 3) {
191                 for (int i = 0; i < width; i ++) {
192                     dest_scan[0] = src_scan[2];
193                     dest_scan[1] = src_scan[1];
194                     dest_scan[2] = src_scan[0];
195                     dest_scan += 3;
196                     src_scan += Bpp;
197                 }
198             } else {
199                 for (int i = 0; i < pitch; i ++) {
200                     *dest_scan++ = ~*src_scan++;
201                 }
202             }
203             row_pointer[0] = line_buf;
204         } else {
205             row_pointer[0] = (uint8_t*)src_scan;
206         }
207         row = cinfo.next_scanline;
208         jpeg_write_scanlines(&cinfo, row_pointer, 1);
209         if (cinfo.next_scanline == row) {
210             dest_buf = FX_Realloc(uint8_t, dest_buf, dest_buf_length + JPEG_BLOCK_SIZE);
211             dest.next_output_byte = dest_buf + dest_buf_length - dest.free_in_buffer;
212             dest_buf_length += JPEG_BLOCK_SIZE;
213             dest.free_in_buffer += JPEG_BLOCK_SIZE;
214         }
215     }
216     jpeg_finish_compress(&cinfo);
217     jpeg_destroy_compress(&cinfo);
218     if (line_buf) {
219         FX_Free(line_buf);
220     }
221     dest_size = dest_buf_length - (FX_STRSIZE)dest.free_in_buffer;
222 }
223 static FX_BOOL _JpegLoadInfo(const uint8_t* src_buf, FX_DWORD src_size, int& width, int& height,
224                              int& num_components, int& bits_per_components, FX_BOOL& color_transform,
225                              uint8_t** icc_buf_ptr, FX_DWORD* icc_length)
226 {
227     _JpegScanSOI(src_buf, src_size);
228     struct jpeg_decompress_struct cinfo;
229     struct jpeg_error_mgr jerr;
230     jerr.error_exit = _error_fatal;
231     jerr.emit_message = _error_do_nothing1;
232     jerr.output_message = _error_do_nothing;
233     jerr.format_message = _error_do_nothing2;
234     jerr.reset_error_mgr = _error_do_nothing;
235     jerr.trace_level = 0;
236     cinfo.err = &jerr;
237     jmp_buf mark;
238     cinfo.client_data = &mark;
239     if (setjmp(mark) == -1) {
240         return FALSE;
241     }
242     jpeg_create_decompress(&cinfo);
243     struct jpeg_source_mgr src;
244     src.init_source = _src_do_nothing;
245     src.term_source = _src_do_nothing;
246     src.skip_input_data = _src_skip_data;
247     src.fill_input_buffer = _src_fill_buffer;
248     src.resync_to_restart = _src_resync;
249     src.bytes_in_buffer = src_size;
250     src.next_input_byte = src_buf;
251     cinfo.src = &src;
252     if (setjmp(mark) == -1) {
253         jpeg_destroy_decompress(&cinfo);
254         return FALSE;
255     }
256     if(icc_buf_ptr && icc_length) {
257         jpeg_save_markers(&cinfo, JPEG_MARKER_ICC, JPEG_MARKER_MAXSIZE);
258     }
259     int ret = jpeg_read_header(&cinfo, TRUE);
260     if (ret != JPEG_HEADER_OK) {
261         jpeg_destroy_decompress(&cinfo);
262         return FALSE;
263     }
264     width = cinfo.image_width;
265     height = cinfo.image_height;
266     num_components = cinfo.num_components;
267     color_transform = cinfo.jpeg_color_space == JCS_YCbCr || cinfo.jpeg_color_space == JCS_YCCK;
268     bits_per_components = cinfo.data_precision;
269     if(icc_buf_ptr != NULL) {
270         *icc_buf_ptr = NULL;
271     }
272     if(icc_length != NULL) {
273         *icc_length = 0;
274     }
275     jpeg_destroy_decompress(&cinfo);
276     return TRUE;
277 }
278 class CCodec_JpegDecoder : public CCodec_ScanlineDecoder
279 {
280 public:
281     CCodec_JpegDecoder();
282     ~CCodec_JpegDecoder();
283     FX_BOOL                             Create(const uint8_t* src_buf, FX_DWORD src_size, int width, int height, int nComps,
284                                FX_BOOL ColorTransform, IFX_JpegProvider* pJP);
285     virtual void                Destroy()
286     {
287         delete this;
288     }
289     virtual void                v_DownScale(int dest_width, int dest_height);
290     virtual FX_BOOL             v_Rewind();
291     virtual uint8_t*    v_GetNextLine();
292     virtual FX_DWORD    GetSrcOffset();
293     jmp_buf             m_JmpBuf;
294     struct jpeg_decompress_struct cinfo;
295     struct jpeg_error_mgr jerr;
296     struct jpeg_source_mgr src;
297     const uint8_t*      m_SrcBuf;
298     FX_DWORD    m_SrcSize;
299     uint8_t*    m_pScanlineBuf;
300     FX_BOOL             InitDecode();
301     FX_BOOL             m_bInited, m_bStarted, m_bJpegTransform;
302 protected:
303     IFX_JpegProvider*   m_pExtProvider;
304     void*                               m_pExtContext;
305     FX_DWORD                    m_nDefaultScaleDenom;
306 };
307 CCodec_JpegDecoder::CCodec_JpegDecoder()
308 {
309     m_pScanlineBuf = NULL;
310     m_DownScale = 1;
311     m_bStarted = FALSE;
312     m_bInited = FALSE;
313     m_pExtProvider = NULL;
314     m_pExtContext = NULL;
315     FXSYS_memset(&cinfo, 0, sizeof(cinfo));
316     FXSYS_memset(&jerr, 0, sizeof(jerr));
317     FXSYS_memset(&src, 0, sizeof(src));
318     m_nDefaultScaleDenom = 1;
319 }
320 CCodec_JpegDecoder::~CCodec_JpegDecoder()
321 {
322     if (m_pExtProvider) {
323         m_pExtProvider->DestroyDecoder(m_pExtContext);
324         return;
325     }
326     if (m_pScanlineBuf) {
327         FX_Free(m_pScanlineBuf);
328     }
329     if (m_bInited) {
330         jpeg_destroy_decompress(&cinfo);
331     }
332 }
333 FX_BOOL CCodec_JpegDecoder::InitDecode()
334 {
335     cinfo.err = &jerr;
336     cinfo.client_data = &m_JmpBuf;
337     if (setjmp(m_JmpBuf) == -1) {
338         return FALSE;
339     }
340     jpeg_create_decompress(&cinfo);
341     m_bInited = TRUE;
342     cinfo.src = &src;
343     src.bytes_in_buffer = m_SrcSize;
344     src.next_input_byte = m_SrcBuf;
345     if (setjmp(m_JmpBuf) == -1) {
346         jpeg_destroy_decompress(&cinfo);
347         m_bInited = FALSE;
348         return FALSE;
349     }
350     cinfo.image_width = m_OrigWidth;
351     cinfo.image_height = m_OrigHeight;
352     int ret = jpeg_read_header(&cinfo, TRUE);
353     if (ret != JPEG_HEADER_OK) {
354         return FALSE;
355     }
356     if (cinfo.saw_Adobe_marker) {
357         m_bJpegTransform = TRUE;
358     }
359     if (cinfo.num_components == 3 && !m_bJpegTransform) {
360         cinfo.out_color_space = cinfo.jpeg_color_space;
361     }
362     m_OrigWidth = cinfo.image_width;
363     m_OrigHeight = cinfo.image_height;
364     m_OutputWidth = m_OrigWidth;
365     m_OutputHeight = m_OrigHeight;
366     m_nDefaultScaleDenom = cinfo.scale_denom;
367     return TRUE;
368 }
369 FX_BOOL CCodec_JpegDecoder::Create(const uint8_t* src_buf, FX_DWORD src_size, int width, int height,
370                                    int nComps, FX_BOOL ColorTransform, IFX_JpegProvider* pJP)
371 {
372     if (pJP) {
373         m_pExtProvider = pJP;
374         m_pExtContext = m_pExtProvider->CreateDecoder(src_buf, src_size, width, height, nComps, ColorTransform);
375         return m_pExtContext != NULL;
376     }
377     _JpegScanSOI(src_buf, src_size);
378     m_SrcBuf = src_buf;
379     m_SrcSize = src_size;
380     jerr.error_exit = _error_fatal;
381     jerr.emit_message = _error_do_nothing1;
382     jerr.output_message = _error_do_nothing;
383     jerr.format_message = _error_do_nothing2;
384     jerr.reset_error_mgr = _error_do_nothing;
385     src.init_source = _src_do_nothing;
386     src.term_source = _src_do_nothing;
387     src.skip_input_data = _src_skip_data;
388     src.fill_input_buffer = _src_fill_buffer;
389     src.resync_to_restart = _src_resync;
390     m_bJpegTransform = ColorTransform;
391     if(src_size > 1 && FXSYS_memcmp(src_buf + src_size - 2, "\xFF\xD9", 2) != 0) {
392         ((uint8_t*)src_buf)[src_size - 2] = 0xFF;
393         ((uint8_t*)src_buf)[src_size - 1] = 0xD9;
394     }
395     m_OutputWidth = m_OrigWidth = width;
396     m_OutputHeight = m_OrigHeight = height;
397     if (!InitDecode()) {
398         return FALSE;
399     }
400     if (cinfo.num_components < nComps) {
401         return FALSE;
402     }
403     if ((int)cinfo.image_width < width) {
404         return FALSE;
405     }
406     m_Pitch = (cinfo.image_width * cinfo.num_components + 3) / 4 * 4;
407     m_pScanlineBuf = FX_Alloc(uint8_t, m_Pitch);
408     m_nComps = cinfo.num_components;
409     m_bpc = 8;
410     m_bColorTransformed = FALSE;
411     m_bStarted = FALSE;
412     return TRUE;
413 }
414 extern "C" {
415     int32_t FX_GetDownsampleRatio(int32_t originWidth, int32_t originHeight, int32_t downsampleWidth, int32_t downsampleHeight)
416     {
417         int iratio_w = originWidth / downsampleWidth;
418         int iratio_h = originHeight / downsampleHeight;
419         int ratio = (iratio_w > iratio_h) ? iratio_h : iratio_w;
420         if (ratio >= 8) {
421             return 8;
422         } else if (ratio >= 4) {
423             return 4;
424         } else if (ratio >= 2) {
425             return 2;
426         }
427         return 1;
428     }
429 }
430 void CCodec_JpegDecoder::v_DownScale(int dest_width, int dest_height)
431 {
432     if (m_pExtProvider) {
433         m_pExtProvider->DownScale(m_pExtContext, dest_width, dest_height);
434         return;
435     }
436     int old_scale = m_DownScale;
437     m_DownScale = FX_GetDownsampleRatio(m_OrigWidth, m_OrigHeight, dest_width, dest_height);
438     m_OutputWidth = (m_OrigWidth + m_DownScale - 1) / m_DownScale;
439     m_OutputHeight = (m_OrigHeight + m_DownScale - 1) / m_DownScale;
440     m_Pitch = (m_OutputWidth * m_nComps + 3) / 4 * 4;
441     if (old_scale != m_DownScale) {
442         m_NextLine = -1;
443     }
444 }
445 FX_BOOL CCodec_JpegDecoder::v_Rewind()
446 {
447     if (m_pExtProvider) {
448         return m_pExtProvider->Rewind(m_pExtContext);
449     }
450     if (m_bStarted) {
451         jpeg_destroy_decompress(&cinfo);
452         if (!InitDecode()) {
453             return FALSE;
454         }
455     }
456     if (setjmp(m_JmpBuf) == -1) {
457         return FALSE;
458     }
459     cinfo.scale_denom = m_nDefaultScaleDenom * m_DownScale;
460     m_OutputWidth = (m_OrigWidth + m_DownScale - 1) / m_DownScale;
461     m_OutputHeight = (m_OrigHeight + m_DownScale - 1) / m_DownScale;
462     if (!jpeg_start_decompress(&cinfo)) {
463         jpeg_destroy_decompress(&cinfo);
464         return FALSE;
465     }
466     if ((int)cinfo.output_width > m_OrigWidth) {
467         FXSYS_assert(FALSE);
468         return FALSE;
469     }
470     m_bStarted = TRUE;
471     return TRUE;
472 }
473 uint8_t* CCodec_JpegDecoder::v_GetNextLine()
474 {
475     if (m_pExtProvider) {
476         return m_pExtProvider->GetNextLine(m_pExtContext);
477     }
478     int nlines = jpeg_read_scanlines(&cinfo, &m_pScanlineBuf, 1);
479     if (nlines < 1) {
480         return NULL;
481     }
482     return m_pScanlineBuf;
483 }
484 FX_DWORD CCodec_JpegDecoder::GetSrcOffset()
485 {
486     if (m_pExtProvider) {
487         return m_pExtProvider->GetSrcOffset(m_pExtContext);
488     }
489     return (FX_DWORD)(m_SrcSize - src.bytes_in_buffer);
490 }
491 ICodec_ScanlineDecoder* CCodec_JpegModule::CreateDecoder(const uint8_t* src_buf, FX_DWORD src_size,
492         int width, int height, int nComps, FX_BOOL ColorTransform)
493 {
494     if (src_buf == NULL || src_size == 0) {
495         return NULL;
496     }
497     CCodec_JpegDecoder* pDecoder = new CCodec_JpegDecoder;
498     if (!pDecoder->Create(src_buf, src_size, width, height, nComps, ColorTransform, m_pExtProvider)) {
499         delete pDecoder;
500         return NULL;
501     }
502     return pDecoder;
503 }
504 FX_BOOL CCodec_JpegModule::LoadInfo(const uint8_t* src_buf, FX_DWORD src_size, int& width, int& height,
505                                     int& num_components, int& bits_per_components, FX_BOOL& color_transform,
506                                     uint8_t** icc_buf_ptr, FX_DWORD* icc_length)
507 {
508     if (m_pExtProvider) {
509         return m_pExtProvider->LoadInfo(src_buf, src_size, width, height,
510                                         num_components, bits_per_components, color_transform,
511                                         icc_buf_ptr, icc_length);
512     }
513     return _JpegLoadInfo(src_buf, src_size, width, height, num_components, bits_per_components, color_transform, icc_buf_ptr, icc_length);
514 }
515 FX_BOOL CCodec_JpegModule::Encode(const CFX_DIBSource* pSource, uint8_t*& dest_buf, FX_STRSIZE& dest_size, int quality, const uint8_t* icc_buf, FX_DWORD icc_length)
516 {
517     if (m_pExtProvider) {
518         return m_pExtProvider->Encode(pSource, dest_buf, dest_size, quality, icc_buf, icc_length);
519     }
520     if(pSource->GetBPP() < 8 || pSource->GetPalette() != NULL) {
521         ASSERT(pSource->GetBPP() >= 8 && pSource->GetPalette() == NULL);
522         return FALSE;
523     }
524     _JpegEncode(pSource, dest_buf, dest_size, quality, icc_buf, icc_length);
525     return TRUE;
526 }
527 struct FXJPEG_Context {
528     jmp_buf                     m_JumpMark;
529     jpeg_decompress_struct m_Info;
530     jpeg_error_mgr      m_ErrMgr;
531     jpeg_source_mgr     m_SrcMgr;
532     unsigned int        m_SkipSize;
533     void*               (*m_AllocFunc)(unsigned int);
534     void                (*m_FreeFunc)(void*);
535 };
536 extern "C" {
537     static void _error_fatal1(j_common_ptr cinfo)
538     {
539         longjmp(((FXJPEG_Context*)cinfo->client_data)->m_JumpMark, -1);
540     }
541 };
542 extern "C" {
543     static void _src_skip_data1(struct jpeg_decompress_struct* cinfo, long num)
544     {
545         if (cinfo->src->bytes_in_buffer < (size_t)num) {
546             ((FXJPEG_Context*)cinfo->client_data)->m_SkipSize = (unsigned int)(num - cinfo->src->bytes_in_buffer);
547             cinfo->src->bytes_in_buffer = 0;
548         } else {
549             cinfo->src->next_input_byte += num;
550             cinfo->src->bytes_in_buffer -= num;
551         }
552     }
553 };
554 static void* jpeg_alloc_func(unsigned int size)
555 {
556     return FX_Alloc(char, size);
557 }
558 static void jpeg_free_func(void* p)
559 {
560     FX_Free(p);
561 }
562 void* CCodec_JpegModule::Start()
563 {
564     if (m_pExtProvider) {
565         return m_pExtProvider->Start();
566     }
567     FXJPEG_Context* p = (FXJPEG_Context*)FX_Alloc(uint8_t, sizeof(FXJPEG_Context));
568     p->m_AllocFunc = jpeg_alloc_func;
569     p->m_FreeFunc = jpeg_free_func;
570     p->m_ErrMgr.error_exit = _error_fatal1;
571     p->m_ErrMgr.emit_message = _error_do_nothing1;
572     p->m_ErrMgr.output_message = _error_do_nothing;
573     p->m_ErrMgr.format_message = _error_do_nothing2;
574     p->m_ErrMgr.reset_error_mgr = _error_do_nothing;
575     p->m_SrcMgr.init_source = _src_do_nothing;
576     p->m_SrcMgr.term_source = _src_do_nothing;
577     p->m_SrcMgr.skip_input_data = _src_skip_data1;
578     p->m_SrcMgr.fill_input_buffer = _src_fill_buffer;
579     p->m_SrcMgr.resync_to_restart = _src_resync;
580     p->m_Info.client_data = p;
581     p->m_Info.err = &p->m_ErrMgr;
582     if (setjmp(p->m_JumpMark) == -1) {
583         return 0;
584     }
585     jpeg_create_decompress(&p->m_Info);
586     p->m_Info.src = &p->m_SrcMgr;
587     p->m_SkipSize = 0;
588     return p;
589 }
590 void CCodec_JpegModule::Finish(void* pContext)
591 {
592     if (m_pExtProvider) {
593         m_pExtProvider->Finish(pContext);
594         return;
595     }
596     FXJPEG_Context* p = (FXJPEG_Context*)pContext;
597     jpeg_destroy_decompress(&p->m_Info);
598     p->m_FreeFunc(p);
599 }
600 void CCodec_JpegModule::Input(void* pContext, const unsigned char* src_buf, FX_DWORD src_size)
601 {
602     if (m_pExtProvider) {
603         m_pExtProvider->Input(pContext, src_buf, src_size);
604         return;
605     }
606     FXJPEG_Context* p = (FXJPEG_Context*)pContext;
607     if (p->m_SkipSize) {
608         if (p->m_SkipSize > src_size) {
609             p->m_SrcMgr.bytes_in_buffer = 0;
610             p->m_SkipSize -= src_size;
611             return;
612         }
613         src_size -= p->m_SkipSize;
614         src_buf += p->m_SkipSize;
615         p->m_SkipSize = 0;
616     }
617     p->m_SrcMgr.next_input_byte = src_buf;
618     p->m_SrcMgr.bytes_in_buffer = src_size;
619 }
620 int CCodec_JpegModule::ReadHeader(void* pContext, int* width, int* height, int* nComps)
621 {
622     if (m_pExtProvider) {
623         return m_pExtProvider->ReadHeader(pContext, width, height, nComps);
624     }
625     FXJPEG_Context* p = (FXJPEG_Context*)pContext;
626     if (setjmp(p->m_JumpMark) == -1) {
627         return 1;
628     }
629     int ret = jpeg_read_header(&p->m_Info, true);
630     if (ret == JPEG_SUSPENDED) {
631         return 2;
632     }
633     if (ret != JPEG_HEADER_OK) {
634         return 1;
635     }
636     *width = p->m_Info.image_width;
637     *height = p->m_Info.image_height;
638     *nComps = p->m_Info.num_components;
639     return 0;
640 }
641 FX_BOOL CCodec_JpegModule::StartScanline(void* pContext, int down_scale)
642 {
643     if (m_pExtProvider) {
644         return m_pExtProvider->StartScanline(pContext, down_scale);
645     }
646     FXJPEG_Context* p = (FXJPEG_Context*)pContext;
647     if (setjmp(p->m_JumpMark) == -1) {
648         return FALSE;
649     }
650     p->m_Info.scale_denom = down_scale;
651     return jpeg_start_decompress(&p->m_Info);
652 }
653 FX_BOOL CCodec_JpegModule::ReadScanline(void* pContext, unsigned char* dest_buf)
654 {
655     if (m_pExtProvider) {
656         return m_pExtProvider->ReadScanline(pContext, dest_buf);
657     }
658     FXJPEG_Context* p = (FXJPEG_Context*)pContext;
659     if (setjmp(p->m_JumpMark) == -1) {
660         return FALSE;
661     }
662     int nlines = jpeg_read_scanlines(&p->m_Info, &dest_buf, 1);
663     return nlines == 1;
664 }
665 FX_DWORD CCodec_JpegModule::GetAvailInput(void* pContext, uint8_t** avail_buf_ptr)
666 {
667     if (m_pExtProvider) {
668         return m_pExtProvider->GetAvailInput(pContext, avail_buf_ptr);
669     }
670     if(avail_buf_ptr != NULL) {
671         *avail_buf_ptr = NULL;
672         if(((FXJPEG_Context*)pContext)->m_SrcMgr.bytes_in_buffer > 0) {
673             *avail_buf_ptr = (uint8_t*)((FXJPEG_Context*)pContext)->m_SrcMgr.next_input_byte;
674         }
675     }
676     return (FX_DWORD)((FXJPEG_Context*)pContext)->m_SrcMgr.bytes_in_buffer;
677 }