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