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