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