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