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