• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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