| 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 |