1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fxcodec/jpeg/jpegmodule.h"
8
9 #include <setjmp.h>
10
11 #include <memory>
12 #include <utility>
13
14 #include "build/build_config.h"
15 #include "core/fxcodec/cfx_codec_memory.h"
16 #include "core/fxcodec/fx_codec.h"
17 #include "core/fxcodec/scanlinedecoder.h"
18 #include "core/fxcrt/fx_memory_wrappers.h"
19 #include "core/fxcrt/fx_safe_types.h"
20 #include "core/fxge/dib/cfx_dibbase.h"
21 #include "core/fxge/fx_dib.h"
22 #include "third_party/base/logging.h"
23 #include "third_party/base/optional.h"
24 #include "third_party/base/ptr_util.h"
25
26 extern "C" {
27 #undef FAR
28 #if defined(USE_SYSTEM_LIBJPEG)
29 #include <jerror.h>
30 #include <jpeglib.h>
31 #elif defined(USE_LIBJPEG_TURBO)
32 #include "third_party/libjpeg_turbo/jerror.h"
33 #include "third_party/libjpeg_turbo/jpeglib.h"
34 #else
35 #include "third_party/libjpeg/jerror.h"
36 #include "third_party/libjpeg/jpeglib.h"
37 #endif
38 } // extern "C"
39
40 class CJpegContext final : public ModuleIface::Context {
41 public:
42 CJpegContext();
43 ~CJpegContext() override;
44
GetJumpMark()45 jmp_buf& GetJumpMark() { return m_JumpMark; }
46
47 jmp_buf m_JumpMark;
48 jpeg_decompress_struct m_Info = {};
49 jpeg_error_mgr m_ErrMgr = {};
50 jpeg_source_mgr m_SrcMgr = {};
51 unsigned int m_SkipSize = 0;
52 void* (*m_AllocFunc)(unsigned int);
53 void (*m_FreeFunc)(void*);
54 };
55
JpegScanSOI(pdfium::span<const uint8_t> src_span)56 static pdfium::span<const uint8_t> JpegScanSOI(
57 pdfium::span<const uint8_t> src_span) {
58 ASSERT(!src_span.empty());
59
60 for (size_t offset = 0; offset < src_span.size() - 1; ++offset) {
61 if (src_span[offset] == 0xff && src_span[offset + 1] == 0xd8)
62 return src_span.subspan(offset);
63 }
64 return src_span;
65 }
66
67 extern "C" {
68
src_do_nothing(jpeg_decompress_struct * cinfo)69 static void src_do_nothing(jpeg_decompress_struct* cinfo) {}
70
error_fatal(j_common_ptr cinfo)71 static void error_fatal(j_common_ptr cinfo) {
72 longjmp(*(jmp_buf*)cinfo->client_data, -1);
73 }
74
src_skip_data(jpeg_decompress_struct * cinfo,long num)75 static void src_skip_data(jpeg_decompress_struct* cinfo, long num) {
76 if (num > (long)cinfo->src->bytes_in_buffer) {
77 error_fatal((j_common_ptr)cinfo);
78 }
79 cinfo->src->next_input_byte += num;
80 cinfo->src->bytes_in_buffer -= num;
81 }
82
src_fill_buffer(j_decompress_ptr cinfo)83 static boolean src_fill_buffer(j_decompress_ptr cinfo) {
84 return FALSE;
85 }
86
src_resync(j_decompress_ptr cinfo,int desired)87 static boolean src_resync(j_decompress_ptr cinfo, int desired) {
88 return FALSE;
89 }
90
error_do_nothing(j_common_ptr cinfo)91 static void error_do_nothing(j_common_ptr cinfo) {}
92
error_do_nothing1(j_common_ptr cinfo,int)93 static void error_do_nothing1(j_common_ptr cinfo, int) {}
94
error_do_nothing2(j_common_ptr cinfo,char *)95 static void error_do_nothing2(j_common_ptr cinfo, char*) {}
96
97 #if defined(OS_WIN)
dest_do_nothing(j_compress_ptr cinfo)98 static void dest_do_nothing(j_compress_ptr cinfo) {}
99
dest_empty(j_compress_ptr cinfo)100 static boolean dest_empty(j_compress_ptr cinfo) {
101 return false;
102 }
103 #endif // defined(OS_WIN)
104
error_fatal1(j_common_ptr cinfo)105 static void error_fatal1(j_common_ptr cinfo) {
106 auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
107 longjmp(pContext->m_JumpMark, -1);
108 }
109
src_skip_data1(jpeg_decompress_struct * cinfo,long num)110 static void src_skip_data1(jpeg_decompress_struct* cinfo, long num) {
111 if (cinfo->src->bytes_in_buffer < static_cast<size_t>(num)) {
112 auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
113 pContext->m_SkipSize = (unsigned int)(num - cinfo->src->bytes_in_buffer);
114 cinfo->src->bytes_in_buffer = 0;
115 } else {
116 cinfo->src->next_input_byte += num;
117 cinfo->src->bytes_in_buffer -= num;
118 }
119 }
120
jpeg_alloc_func(unsigned int size)121 static void* jpeg_alloc_func(unsigned int size) {
122 return FX_Alloc(char, size);
123 }
124
jpeg_free_func(void * p)125 static void jpeg_free_func(void* p) {
126 FX_Free(p);
127 }
128
129 } // extern "C"
130
131 #ifdef PDF_ENABLE_XFA
JpegLoadAttribute(const jpeg_decompress_struct & info,CFX_DIBAttribute * pAttribute)132 static void JpegLoadAttribute(const jpeg_decompress_struct& info,
133 CFX_DIBAttribute* pAttribute) {
134 pAttribute->m_nXDPI = info.X_density;
135 pAttribute->m_nYDPI = info.Y_density;
136 pAttribute->m_wDPIUnit = info.density_unit;
137 }
138 #endif // PDF_ENABLE_XFA
139
JpegLoadInfo(pdfium::span<const uint8_t> src_span,int * width,int * height,int * num_components,int * bits_per_components,bool * color_transform)140 static bool JpegLoadInfo(pdfium::span<const uint8_t> src_span,
141 int* width,
142 int* height,
143 int* num_components,
144 int* bits_per_components,
145 bool* color_transform) {
146 src_span = JpegScanSOI(src_span);
147 jpeg_decompress_struct cinfo;
148 jpeg_error_mgr jerr;
149 jerr.error_exit = error_fatal;
150 jerr.emit_message = error_do_nothing1;
151 jerr.output_message = error_do_nothing;
152 jerr.format_message = error_do_nothing2;
153 jerr.reset_error_mgr = error_do_nothing;
154 jerr.trace_level = 0;
155 cinfo.err = &jerr;
156 jmp_buf mark;
157 cinfo.client_data = &mark;
158 if (setjmp(mark) == -1)
159 return false;
160
161 jpeg_create_decompress(&cinfo);
162 jpeg_source_mgr src;
163 src.init_source = src_do_nothing;
164 src.term_source = src_do_nothing;
165 src.skip_input_data = src_skip_data;
166 src.fill_input_buffer = src_fill_buffer;
167 src.resync_to_restart = src_resync;
168 src.bytes_in_buffer = src_span.size();
169 src.next_input_byte = src_span.data();
170 cinfo.src = &src;
171 if (setjmp(mark) == -1) {
172 jpeg_destroy_decompress(&cinfo);
173 return false;
174 }
175 int ret = jpeg_read_header(&cinfo, TRUE);
176 if (ret != JPEG_HEADER_OK) {
177 jpeg_destroy_decompress(&cinfo);
178 return false;
179 }
180 *width = cinfo.image_width;
181 *height = cinfo.image_height;
182 *num_components = cinfo.num_components;
183 *color_transform =
184 cinfo.jpeg_color_space == JCS_YCbCr || cinfo.jpeg_color_space == JCS_YCCK;
185 *bits_per_components = cinfo.data_precision;
186 jpeg_destroy_decompress(&cinfo);
187 return true;
188 }
189
CJpegContext()190 CJpegContext::CJpegContext()
191 : m_AllocFunc(jpeg_alloc_func), m_FreeFunc(jpeg_free_func) {
192 m_Info.client_data = this;
193 m_Info.err = &m_ErrMgr;
194
195 m_ErrMgr.error_exit = error_fatal1;
196 m_ErrMgr.emit_message = error_do_nothing1;
197 m_ErrMgr.output_message = error_do_nothing;
198 m_ErrMgr.format_message = error_do_nothing2;
199 m_ErrMgr.reset_error_mgr = error_do_nothing;
200
201 m_SrcMgr.init_source = src_do_nothing;
202 m_SrcMgr.term_source = src_do_nothing;
203 m_SrcMgr.skip_input_data = src_skip_data1;
204 m_SrcMgr.fill_input_buffer = src_fill_buffer;
205 m_SrcMgr.resync_to_restart = src_resync;
206 }
207
~CJpegContext()208 CJpegContext::~CJpegContext() {
209 jpeg_destroy_decompress(&m_Info);
210 }
211
212 namespace fxcodec {
213
214 namespace {
215
216 constexpr size_t kKnownBadHeaderWithInvalidHeightByteOffsetStarts[] = {94, 163};
217
218 class JpegDecoder final : public ScanlineDecoder {
219 public:
220 JpegDecoder();
221 ~JpegDecoder() override;
222
223 bool Create(pdfium::span<const uint8_t> src_span,
224 int width,
225 int height,
226 int nComps,
227 bool ColorTransform);
228
229 // ScanlineDecoder:
230 bool v_Rewind() override;
231 uint8_t* v_GetNextLine() override;
232 uint32_t GetSrcOffset() override;
233
234 bool InitDecode(bool bAcceptKnownBadHeader);
235
236 jmp_buf m_JmpBuf;
237 jpeg_decompress_struct m_Cinfo;
238 jpeg_error_mgr m_Jerr;
239 jpeg_source_mgr m_Src;
240 pdfium::span<const uint8_t> m_SrcSpan;
241 std::unique_ptr<uint8_t, FxFreeDeleter> m_pScanlineBuf;
242 bool m_bInited = false;
243 bool m_bStarted = false;
244 bool m_bJpegTransform = false;
245
246 private:
247 void CalcPitch();
248 void InitDecompressSrc();
249
250 // Can only be called inside a jpeg_read_header() setjmp handler.
251 bool HasKnownBadHeaderWithInvalidHeight(size_t dimension_offset) const;
252
253 // Is a JPEG SOFn marker, which is defined as 0xff, 0xc[0-9a-f].
254 bool IsSofSegment(size_t marker_offset) const;
255
256 // Patch up the in-memory JPEG header for known bad JPEGs.
257 void PatchUpKnownBadHeaderWithInvalidHeight(size_t dimension_offset);
258
259 // Patch up the JPEG trailer, even if it is correct.
260 void PatchUpTrailer();
261
262 uint8_t* GetWritableSrcData();
263
264 // For a given invalid height byte offset in
265 // |kKnownBadHeaderWithInvalidHeightByteOffsetStarts|, the SOFn marker should
266 // be this many bytes before that.
267 static const size_t kSofMarkerByteOffset = 5;
268
269 uint32_t m_nDefaultScaleDenom = 1;
270 };
271
JpegDecoder()272 JpegDecoder::JpegDecoder() {
273 memset(&m_Cinfo, 0, sizeof(m_Cinfo));
274 memset(&m_Jerr, 0, sizeof(m_Jerr));
275 memset(&m_Src, 0, sizeof(m_Src));
276 }
277
~JpegDecoder()278 JpegDecoder::~JpegDecoder() {
279 if (m_bInited)
280 jpeg_destroy_decompress(&m_Cinfo);
281 }
282
InitDecode(bool bAcceptKnownBadHeader)283 bool JpegDecoder::InitDecode(bool bAcceptKnownBadHeader) {
284 m_Cinfo.err = &m_Jerr;
285 m_Cinfo.client_data = &m_JmpBuf;
286 if (setjmp(m_JmpBuf) == -1)
287 return false;
288
289 jpeg_create_decompress(&m_Cinfo);
290 InitDecompressSrc();
291 m_bInited = true;
292
293 if (setjmp(m_JmpBuf) == -1) {
294 Optional<size_t> known_bad_header_offset;
295 if (bAcceptKnownBadHeader) {
296 for (size_t offset : kKnownBadHeaderWithInvalidHeightByteOffsetStarts) {
297 if (HasKnownBadHeaderWithInvalidHeight(offset)) {
298 known_bad_header_offset = offset;
299 break;
300 }
301 }
302 }
303 jpeg_destroy_decompress(&m_Cinfo);
304 if (!known_bad_header_offset.has_value()) {
305 m_bInited = false;
306 return false;
307 }
308
309 PatchUpKnownBadHeaderWithInvalidHeight(known_bad_header_offset.value());
310
311 jpeg_create_decompress(&m_Cinfo);
312 InitDecompressSrc();
313 }
314 m_Cinfo.image_width = m_OrigWidth;
315 m_Cinfo.image_height = m_OrigHeight;
316 int ret = jpeg_read_header(&m_Cinfo, TRUE);
317 if (ret != JPEG_HEADER_OK)
318 return false;
319
320 if (m_Cinfo.saw_Adobe_marker)
321 m_bJpegTransform = true;
322
323 if (m_Cinfo.num_components == 3 && !m_bJpegTransform)
324 m_Cinfo.out_color_space = m_Cinfo.jpeg_color_space;
325
326 m_OrigWidth = m_Cinfo.image_width;
327 m_OrigHeight = m_Cinfo.image_height;
328 m_OutputWidth = m_OrigWidth;
329 m_OutputHeight = m_OrigHeight;
330 m_nDefaultScaleDenom = m_Cinfo.scale_denom;
331 return true;
332 }
333
Create(pdfium::span<const uint8_t> src_span,int width,int height,int nComps,bool ColorTransform)334 bool JpegDecoder::Create(pdfium::span<const uint8_t> src_span,
335 int width,
336 int height,
337 int nComps,
338 bool ColorTransform) {
339 m_SrcSpan = JpegScanSOI(src_span);
340 if (m_SrcSpan.size() < 2)
341 return false;
342
343 PatchUpTrailer();
344
345 m_Jerr.error_exit = error_fatal;
346 m_Jerr.emit_message = error_do_nothing1;
347 m_Jerr.output_message = error_do_nothing;
348 m_Jerr.format_message = error_do_nothing2;
349 m_Jerr.reset_error_mgr = error_do_nothing;
350 m_Src.init_source = src_do_nothing;
351 m_Src.term_source = src_do_nothing;
352 m_Src.skip_input_data = src_skip_data;
353 m_Src.fill_input_buffer = src_fill_buffer;
354 m_Src.resync_to_restart = src_resync;
355 m_bJpegTransform = ColorTransform;
356 m_OutputWidth = m_OrigWidth = width;
357 m_OutputHeight = m_OrigHeight = height;
358 if (!InitDecode(/*bAcceptKnownBadHeader=*/true))
359 return false;
360
361 if (m_Cinfo.num_components < nComps)
362 return false;
363
364 if (static_cast<int>(m_Cinfo.image_width) < width)
365 return false;
366
367 CalcPitch();
368 m_pScanlineBuf.reset(FX_Alloc(uint8_t, m_Pitch));
369 m_nComps = m_Cinfo.num_components;
370 m_bpc = 8;
371 m_bStarted = false;
372 return true;
373 }
374
v_Rewind()375 bool JpegDecoder::v_Rewind() {
376 if (m_bStarted) {
377 jpeg_destroy_decompress(&m_Cinfo);
378 if (!InitDecode(/*bAcceptKnownBadHeader=*/false)) {
379 return false;
380 }
381 }
382 if (setjmp(m_JmpBuf) == -1) {
383 return false;
384 }
385 m_Cinfo.scale_denom = m_nDefaultScaleDenom;
386 m_OutputWidth = m_OrigWidth;
387 m_OutputHeight = m_OrigHeight;
388 if (!jpeg_start_decompress(&m_Cinfo)) {
389 jpeg_destroy_decompress(&m_Cinfo);
390 return false;
391 }
392 if (static_cast<int>(m_Cinfo.output_width) > m_OrigWidth) {
393 NOTREACHED();
394 return false;
395 }
396 m_bStarted = true;
397 return true;
398 }
399
v_GetNextLine()400 uint8_t* JpegDecoder::v_GetNextLine() {
401 if (setjmp(m_JmpBuf) == -1)
402 return nullptr;
403
404 uint8_t* row_array[] = {m_pScanlineBuf.get()};
405 int nlines = jpeg_read_scanlines(&m_Cinfo, row_array, 1);
406 return nlines > 0 ? m_pScanlineBuf.get() : nullptr;
407 }
408
GetSrcOffset()409 uint32_t JpegDecoder::GetSrcOffset() {
410 return static_cast<uint32_t>(m_SrcSpan.size() - m_Src.bytes_in_buffer);
411 }
412
CalcPitch()413 void JpegDecoder::CalcPitch() {
414 m_Pitch = static_cast<uint32_t>(m_Cinfo.image_width) * m_Cinfo.num_components;
415 m_Pitch += 3;
416 m_Pitch /= 4;
417 m_Pitch *= 4;
418 }
419
InitDecompressSrc()420 void JpegDecoder::InitDecompressSrc() {
421 m_Cinfo.src = &m_Src;
422 m_Src.bytes_in_buffer = m_SrcSpan.size();
423 m_Src.next_input_byte = m_SrcSpan.data();
424 }
425
HasKnownBadHeaderWithInvalidHeight(size_t dimension_offset) const426 bool JpegDecoder::HasKnownBadHeaderWithInvalidHeight(
427 size_t dimension_offset) const {
428 // Perform lots of possibly redundant checks to make sure this has no false
429 // positives.
430 bool bDimensionChecks = m_Cinfo.err->msg_code == JERR_IMAGE_TOO_BIG &&
431 m_Cinfo.image_width < JPEG_MAX_DIMENSION &&
432 m_Cinfo.image_height == 0xffff && m_OrigWidth > 0 &&
433 m_OrigWidth <= JPEG_MAX_DIMENSION &&
434 m_OrigHeight > 0 &&
435 m_OrigHeight <= JPEG_MAX_DIMENSION;
436 if (!bDimensionChecks)
437 return false;
438
439 if (m_SrcSpan.size() <= dimension_offset + 3u)
440 return false;
441
442 if (!IsSofSegment(dimension_offset - kSofMarkerByteOffset))
443 return false;
444
445 const uint8_t* pHeaderDimensions = &m_SrcSpan[dimension_offset];
446 uint8_t nExpectedWidthByte1 = (m_OrigWidth >> 8) & 0xff;
447 uint8_t nExpectedWidthByte2 = m_OrigWidth & 0xff;
448 // Height high byte, height low byte, width high byte, width low byte.
449 return pHeaderDimensions[0] == 0xff && pHeaderDimensions[1] == 0xff &&
450 pHeaderDimensions[2] == nExpectedWidthByte1 &&
451 pHeaderDimensions[3] == nExpectedWidthByte2;
452 }
453
IsSofSegment(size_t marker_offset) const454 bool JpegDecoder::IsSofSegment(size_t marker_offset) const {
455 const uint8_t* pHeaderMarker = &m_SrcSpan[marker_offset];
456 return pHeaderMarker[0] == 0xff && pHeaderMarker[1] >= 0xc0 &&
457 pHeaderMarker[1] <= 0xcf;
458 }
459
PatchUpKnownBadHeaderWithInvalidHeight(size_t dimension_offset)460 void JpegDecoder::PatchUpKnownBadHeaderWithInvalidHeight(
461 size_t dimension_offset) {
462 ASSERT(m_SrcSpan.size() > dimension_offset + 1u);
463 uint8_t* pData = GetWritableSrcData() + dimension_offset;
464 pData[0] = (m_OrigHeight >> 8) & 0xff;
465 pData[1] = m_OrigHeight & 0xff;
466 }
467
PatchUpTrailer()468 void JpegDecoder::PatchUpTrailer() {
469 uint8_t* pData = GetWritableSrcData();
470 pData[m_SrcSpan.size() - 2] = 0xff;
471 pData[m_SrcSpan.size() - 1] = 0xd9;
472 }
473
GetWritableSrcData()474 uint8_t* JpegDecoder::GetWritableSrcData() {
475 return const_cast<uint8_t*>(m_SrcSpan.data());
476 }
477
478 } // namespace
479
CreateDecoder(pdfium::span<const uint8_t> src_span,int width,int height,int nComps,bool ColorTransform)480 std::unique_ptr<ScanlineDecoder> JpegModule::CreateDecoder(
481 pdfium::span<const uint8_t> src_span,
482 int width,
483 int height,
484 int nComps,
485 bool ColorTransform) {
486 ASSERT(!src_span.empty());
487
488 auto pDecoder = pdfium::MakeUnique<JpegDecoder>();
489 if (!pDecoder->Create(src_span, width, height, nComps, ColorTransform))
490 return nullptr;
491
492 return std::move(pDecoder);
493 }
494
LoadInfo(pdfium::span<const uint8_t> src_span)495 Optional<JpegModule::JpegImageInfo> JpegModule::LoadInfo(
496 pdfium::span<const uint8_t> src_span) {
497 JpegImageInfo info;
498 if (!JpegLoadInfo(src_span, &info.width, &info.height, &info.num_components,
499 &info.bits_per_components, &info.color_transform)) {
500 return pdfium::nullopt;
501 }
502 return info;
503 }
504
Start()505 std::unique_ptr<ModuleIface::Context> JpegModule::Start() {
506 // Use ordinary pointer until past the possibility of a longjump.
507 auto* pContext = new CJpegContext();
508 if (setjmp(pContext->m_JumpMark) == -1) {
509 delete pContext;
510 return nullptr;
511 }
512
513 jpeg_create_decompress(&pContext->m_Info);
514 pContext->m_Info.src = &pContext->m_SrcMgr;
515 pContext->m_SkipSize = 0;
516 return pdfium::WrapUnique(pContext);
517 }
518
Input(Context * pContext,RetainPtr<CFX_CodecMemory> codec_memory,CFX_DIBAttribute *)519 bool JpegModule::Input(Context* pContext,
520 RetainPtr<CFX_CodecMemory> codec_memory,
521 CFX_DIBAttribute*) {
522 pdfium::span<uint8_t> src_buf = codec_memory->GetSpan();
523 auto* ctx = static_cast<CJpegContext*>(pContext);
524 if (ctx->m_SkipSize) {
525 if (ctx->m_SkipSize > src_buf.size()) {
526 ctx->m_SrcMgr.bytes_in_buffer = 0;
527 ctx->m_SkipSize -= src_buf.size();
528 return true;
529 }
530 src_buf = src_buf.subspan(ctx->m_SkipSize);
531 ctx->m_SkipSize = 0;
532 }
533 ctx->m_SrcMgr.next_input_byte = src_buf.data();
534 ctx->m_SrcMgr.bytes_in_buffer = src_buf.size();
535 return true;
536 }
537
538 #ifdef PDF_ENABLE_XFA
ReadHeader(Context * pContext,int * width,int * height,int * nComps,CFX_DIBAttribute * pAttribute)539 int JpegModule::ReadHeader(Context* pContext,
540 int* width,
541 int* height,
542 int* nComps,
543 CFX_DIBAttribute* pAttribute) {
544 ASSERT(pAttribute);
545
546 auto* ctx = static_cast<CJpegContext*>(pContext);
547 int ret = jpeg_read_header(&ctx->m_Info, TRUE);
548 if (ret == JPEG_SUSPENDED)
549 return 2;
550 if (ret != JPEG_HEADER_OK)
551 return 1;
552
553 *width = ctx->m_Info.image_width;
554 *height = ctx->m_Info.image_height;
555 *nComps = ctx->m_Info.num_components;
556 JpegLoadAttribute(ctx->m_Info, pAttribute);
557 return 0;
558 }
559 #endif // PDF_ENABLE_XFA
560
StartScanline(Context * pContext,int down_scale)561 bool JpegModule::StartScanline(Context* pContext, int down_scale) {
562 auto* ctx = static_cast<CJpegContext*>(pContext);
563 ctx->m_Info.scale_denom = static_cast<unsigned int>(down_scale);
564 return !!jpeg_start_decompress(&ctx->m_Info);
565 }
566
ReadScanline(Context * pContext,unsigned char * dest_buf)567 bool JpegModule::ReadScanline(Context* pContext, unsigned char* dest_buf) {
568 auto* ctx = static_cast<CJpegContext*>(pContext);
569 unsigned int nlines = jpeg_read_scanlines(&ctx->m_Info, &dest_buf, 1);
570 return nlines == 1;
571 }
572
GetAvailInput(Context * pContext) const573 FX_FILESIZE JpegModule::GetAvailInput(Context* pContext) const {
574 auto* ctx = static_cast<CJpegContext*>(pContext);
575 return static_cast<FX_FILESIZE>(ctx->m_SrcMgr.bytes_in_buffer);
576 }
577
GetJumpMark(Context * pContext)578 jmp_buf& JpegModule::GetJumpMark(Context* pContext) {
579 return static_cast<CJpegContext*>(pContext)->GetJumpMark();
580 }
581
582 #if defined(OS_WIN)
JpegEncode(const RetainPtr<CFX_DIBBase> & pSource,uint8_t ** dest_buf,size_t * dest_size)583 bool JpegModule::JpegEncode(const RetainPtr<CFX_DIBBase>& pSource,
584 uint8_t** dest_buf,
585 size_t* dest_size) {
586 jpeg_error_mgr jerr;
587 jerr.error_exit = error_do_nothing;
588 jerr.emit_message = error_do_nothing1;
589 jerr.output_message = error_do_nothing;
590 jerr.format_message = error_do_nothing2;
591 jerr.reset_error_mgr = error_do_nothing;
592
593 jpeg_compress_struct cinfo;
594 memset(&cinfo, 0, sizeof(cinfo));
595 cinfo.err = &jerr;
596 jpeg_create_compress(&cinfo);
597 int Bpp = pSource->GetBPP() / 8;
598 uint32_t nComponents = Bpp >= 3 ? (pSource->IsCmykImage() ? 4 : 3) : 1;
599 uint32_t pitch = pSource->GetPitch();
600 uint32_t width = pdfium::base::checked_cast<uint32_t>(pSource->GetWidth());
601 uint32_t height = pdfium::base::checked_cast<uint32_t>(pSource->GetHeight());
602 FX_SAFE_UINT32 safe_buf_len = width;
603 safe_buf_len *= height;
604 safe_buf_len *= nComponents;
605 safe_buf_len += 1024;
606 if (!safe_buf_len.IsValid())
607 return false;
608
609 uint32_t dest_buf_length = safe_buf_len.ValueOrDie();
610 *dest_buf = FX_TryAlloc(uint8_t, dest_buf_length);
611 const int MIN_TRY_BUF_LEN = 1024;
612 while (!(*dest_buf) && dest_buf_length > MIN_TRY_BUF_LEN) {
613 dest_buf_length >>= 1;
614 *dest_buf = FX_TryAlloc(uint8_t, dest_buf_length);
615 }
616 if (!(*dest_buf))
617 return false;
618
619 jpeg_destination_mgr dest;
620 dest.init_destination = dest_do_nothing;
621 dest.term_destination = dest_do_nothing;
622 dest.empty_output_buffer = dest_empty;
623 dest.next_output_byte = *dest_buf;
624 dest.free_in_buffer = dest_buf_length;
625 cinfo.dest = &dest;
626 cinfo.image_width = width;
627 cinfo.image_height = height;
628 cinfo.input_components = nComponents;
629 if (nComponents == 1) {
630 cinfo.in_color_space = JCS_GRAYSCALE;
631 } else if (nComponents == 3) {
632 cinfo.in_color_space = JCS_RGB;
633 } else {
634 cinfo.in_color_space = JCS_CMYK;
635 }
636 uint8_t* line_buf = nullptr;
637 if (nComponents > 1)
638 line_buf = FX_Alloc2D(uint8_t, width, nComponents);
639
640 jpeg_set_defaults(&cinfo);
641 jpeg_start_compress(&cinfo, TRUE);
642 JSAMPROW row_pointer[1];
643 JDIMENSION row;
644 while (cinfo.next_scanline < cinfo.image_height) {
645 const uint8_t* src_scan = pSource->GetScanline(cinfo.next_scanline);
646 if (nComponents > 1) {
647 uint8_t* dest_scan = line_buf;
648 if (nComponents == 3) {
649 for (uint32_t i = 0; i < width; i++) {
650 dest_scan[0] = src_scan[2];
651 dest_scan[1] = src_scan[1];
652 dest_scan[2] = src_scan[0];
653 dest_scan += 3;
654 src_scan += Bpp;
655 }
656 } else {
657 for (uint32_t i = 0; i < pitch; i++) {
658 *dest_scan++ = ~*src_scan++;
659 }
660 }
661 row_pointer[0] = line_buf;
662 } else {
663 row_pointer[0] = const_cast<uint8_t*>(src_scan);
664 }
665 row = cinfo.next_scanline;
666 jpeg_write_scanlines(&cinfo, row_pointer, 1);
667 if (cinfo.next_scanline == row) {
668 constexpr size_t kJpegBlockSize = 1048576;
669 *dest_buf =
670 FX_Realloc(uint8_t, *dest_buf, dest_buf_length + kJpegBlockSize);
671 dest.next_output_byte = *dest_buf + dest_buf_length - dest.free_in_buffer;
672 dest_buf_length += kJpegBlockSize;
673 dest.free_in_buffer += kJpegBlockSize;
674 }
675 }
676 jpeg_finish_compress(&cinfo);
677 jpeg_destroy_compress(&cinfo);
678 FX_Free(line_buf);
679 *dest_size = dest_buf_length - static_cast<size_t>(dest.free_in_buffer);
680
681 return true;
682 }
683 #endif // defined(OS_WIN)
684
685 } // namespace fxcodec
686