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