xref: /aosp_15_r20/external/webp/imageio/jpegdec.c (revision b2055c353e87c8814eb2b6b1b11112a1562253bd)
1*b2055c35SXin Li // Copyright 2012 Google Inc. All Rights Reserved.
2*b2055c35SXin Li //
3*b2055c35SXin Li // Use of this source code is governed by a BSD-style license
4*b2055c35SXin Li // that can be found in the COPYING file in the root of the source
5*b2055c35SXin Li // tree. An additional intellectual property rights grant can be found
6*b2055c35SXin Li // in the file PATENTS. All contributing project authors may
7*b2055c35SXin Li // be found in the AUTHORS file in the root of the source tree.
8*b2055c35SXin Li // -----------------------------------------------------------------------------
9*b2055c35SXin Li //
10*b2055c35SXin Li // JPEG decode.
11*b2055c35SXin Li 
12*b2055c35SXin Li #include "./jpegdec.h"
13*b2055c35SXin Li 
14*b2055c35SXin Li #ifdef HAVE_CONFIG_H
15*b2055c35SXin Li #include "webp/config.h"
16*b2055c35SXin Li #endif
17*b2055c35SXin Li 
18*b2055c35SXin Li #include <stdio.h>
19*b2055c35SXin Li 
20*b2055c35SXin Li #ifdef WEBP_HAVE_JPEG
21*b2055c35SXin Li #include <jpeglib.h>
22*b2055c35SXin Li #include <jerror.h>
23*b2055c35SXin Li #include <setjmp.h>
24*b2055c35SXin Li #include <stdlib.h>
25*b2055c35SXin Li #include <string.h>
26*b2055c35SXin Li 
27*b2055c35SXin Li #include "webp/encode.h"
28*b2055c35SXin Li #include "./imageio_util.h"
29*b2055c35SXin Li #include "./metadata.h"
30*b2055c35SXin Li 
31*b2055c35SXin Li // -----------------------------------------------------------------------------
32*b2055c35SXin Li // Metadata processing
33*b2055c35SXin Li 
34*b2055c35SXin Li #ifndef JPEG_APP1
35*b2055c35SXin Li # define JPEG_APP1 (JPEG_APP0 + 1)
36*b2055c35SXin Li #endif
37*b2055c35SXin Li #ifndef JPEG_APP2
38*b2055c35SXin Li # define JPEG_APP2 (JPEG_APP0 + 2)
39*b2055c35SXin Li #endif
40*b2055c35SXin Li 
41*b2055c35SXin Li typedef struct {
42*b2055c35SXin Li   const uint8_t* data;
43*b2055c35SXin Li   size_t data_length;
44*b2055c35SXin Li   int seq;  // this segment's sequence number [1, 255] for use in reassembly.
45*b2055c35SXin Li } ICCPSegment;
46*b2055c35SXin Li 
SaveMetadataMarkers(j_decompress_ptr dinfo)47*b2055c35SXin Li static void SaveMetadataMarkers(j_decompress_ptr dinfo) {
48*b2055c35SXin Li   const unsigned int max_marker_length = 0xffff;
49*b2055c35SXin Li   jpeg_save_markers(dinfo, JPEG_APP1, max_marker_length);  // Exif/XMP
50*b2055c35SXin Li   jpeg_save_markers(dinfo, JPEG_APP2, max_marker_length);  // ICC profile
51*b2055c35SXin Li }
52*b2055c35SXin Li 
CompareICCPSegments(const void * a,const void * b)53*b2055c35SXin Li static int CompareICCPSegments(const void* a, const void* b) {
54*b2055c35SXin Li   const ICCPSegment* s1 = (const ICCPSegment*)a;
55*b2055c35SXin Li   const ICCPSegment* s2 = (const ICCPSegment*)b;
56*b2055c35SXin Li   return s1->seq - s2->seq;
57*b2055c35SXin Li }
58*b2055c35SXin Li 
59*b2055c35SXin Li // Extract ICC profile segments from the marker list in 'dinfo', reassembling
60*b2055c35SXin Li // and storing them in 'iccp'.
61*b2055c35SXin Li // Returns true on success and false for memory errors and corrupt profiles.
StoreICCP(j_decompress_ptr dinfo,MetadataPayload * const iccp)62*b2055c35SXin Li static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
63*b2055c35SXin Li   // ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files
64*b2055c35SXin Li   static const char kICCPSignature[] = "ICC_PROFILE";
65*b2055c35SXin Li   static const size_t kICCPSignatureLength = 12;  // signature includes '\0'
66*b2055c35SXin Li   static const size_t kICCPSkipLength = 14;  // signature + seq & count
67*b2055c35SXin Li   int expected_count = 0;
68*b2055c35SXin Li   int actual_count = 0;
69*b2055c35SXin Li   int seq_max = 0;
70*b2055c35SXin Li   size_t total_size = 0;
71*b2055c35SXin Li   ICCPSegment iccp_segments[255];
72*b2055c35SXin Li   jpeg_saved_marker_ptr marker;
73*b2055c35SXin Li 
74*b2055c35SXin Li   memset(iccp_segments, 0, sizeof(iccp_segments));
75*b2055c35SXin Li   for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
76*b2055c35SXin Li     if (marker->marker == JPEG_APP2 &&
77*b2055c35SXin Li         marker->data_length > kICCPSkipLength &&
78*b2055c35SXin Li         !memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) {
79*b2055c35SXin Li       // ICC_PROFILE\0<seq><count>; 'seq' starts at 1.
80*b2055c35SXin Li       const int seq = marker->data[kICCPSignatureLength];
81*b2055c35SXin Li       const int count = marker->data[kICCPSignatureLength + 1];
82*b2055c35SXin Li       const size_t segment_size = marker->data_length - kICCPSkipLength;
83*b2055c35SXin Li       ICCPSegment* segment;
84*b2055c35SXin Li 
85*b2055c35SXin Li       if (segment_size == 0 || count == 0 || seq == 0) {
86*b2055c35SXin Li         fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)"
87*b2055c35SXin Li                         " cannot be 0!\n",
88*b2055c35SXin Li                 (int)segment_size, seq, count);
89*b2055c35SXin Li         return 0;
90*b2055c35SXin Li       }
91*b2055c35SXin Li 
92*b2055c35SXin Li       if (expected_count == 0) {
93*b2055c35SXin Li         expected_count = count;
94*b2055c35SXin Li       } else if (expected_count != count) {
95*b2055c35SXin Li         fprintf(stderr, "[ICCP] Inconsistent segment count (%d / %d)!\n",
96*b2055c35SXin Li                 expected_count, count);
97*b2055c35SXin Li         return 0;
98*b2055c35SXin Li       }
99*b2055c35SXin Li 
100*b2055c35SXin Li       segment = iccp_segments + seq - 1;
101*b2055c35SXin Li       if (segment->data_length != 0) {
102*b2055c35SXin Li         fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq);
103*b2055c35SXin Li         return 0;
104*b2055c35SXin Li       }
105*b2055c35SXin Li 
106*b2055c35SXin Li       segment->data = marker->data + kICCPSkipLength;
107*b2055c35SXin Li       segment->data_length = segment_size;
108*b2055c35SXin Li       segment->seq = seq;
109*b2055c35SXin Li       total_size += segment_size;
110*b2055c35SXin Li       if (seq > seq_max) seq_max = seq;
111*b2055c35SXin Li       ++actual_count;
112*b2055c35SXin Li     }
113*b2055c35SXin Li   }
114*b2055c35SXin Li 
115*b2055c35SXin Li   if (actual_count == 0) return 1;
116*b2055c35SXin Li   if (seq_max != actual_count) {
117*b2055c35SXin Li     fprintf(stderr, "[ICCP] Discontinuous segments, expected: %d actual: %d!\n",
118*b2055c35SXin Li             actual_count, seq_max);
119*b2055c35SXin Li     return 0;
120*b2055c35SXin Li   }
121*b2055c35SXin Li   if (expected_count != actual_count) {
122*b2055c35SXin Li     fprintf(stderr, "[ICCP] Segment count: %d does not match expected: %d!\n",
123*b2055c35SXin Li             actual_count, expected_count);
124*b2055c35SXin Li     return 0;
125*b2055c35SXin Li   }
126*b2055c35SXin Li 
127*b2055c35SXin Li   // The segments may appear out of order in the file, sort them based on
128*b2055c35SXin Li   // sequence number before assembling the payload.
129*b2055c35SXin Li   qsort(iccp_segments, actual_count, sizeof(*iccp_segments),
130*b2055c35SXin Li         CompareICCPSegments);
131*b2055c35SXin Li 
132*b2055c35SXin Li   iccp->bytes = (uint8_t*)malloc(total_size);
133*b2055c35SXin Li   if (iccp->bytes == NULL) return 0;
134*b2055c35SXin Li   iccp->size = total_size;
135*b2055c35SXin Li 
136*b2055c35SXin Li   {
137*b2055c35SXin Li     int i;
138*b2055c35SXin Li     size_t offset = 0;
139*b2055c35SXin Li     for (i = 0; i < seq_max; ++i) {
140*b2055c35SXin Li       memcpy(iccp->bytes + offset,
141*b2055c35SXin Li              iccp_segments[i].data, iccp_segments[i].data_length);
142*b2055c35SXin Li       offset += iccp_segments[i].data_length;
143*b2055c35SXin Li     }
144*b2055c35SXin Li   }
145*b2055c35SXin Li   return 1;
146*b2055c35SXin Li }
147*b2055c35SXin Li 
148*b2055c35SXin Li // Returns true on success and false for memory errors and corrupt profiles.
149*b2055c35SXin Li // The caller must use MetadataFree() on 'metadata' in all cases.
ExtractMetadataFromJPEG(j_decompress_ptr dinfo,Metadata * const metadata)150*b2055c35SXin Li static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
151*b2055c35SXin Li                                    Metadata* const metadata) {
152*b2055c35SXin Li   static const struct {
153*b2055c35SXin Li     int marker;
154*b2055c35SXin Li     const char* signature;
155*b2055c35SXin Li     size_t signature_length;
156*b2055c35SXin Li     size_t storage_offset;
157*b2055c35SXin Li   } kJPEGMetadataMap[] = {
158*b2055c35SXin Li     // Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
159*b2055c35SXin Li     { JPEG_APP1, "Exif\0",                        6, METADATA_OFFSET(exif) },
160*b2055c35SXin Li     // XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
161*b2055c35SXin Li     // TODO(jzern) Add support for 'ExtendedXMP'
162*b2055c35SXin Li     { JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) },
163*b2055c35SXin Li     { 0, NULL, 0, 0 },
164*b2055c35SXin Li   };
165*b2055c35SXin Li   jpeg_saved_marker_ptr marker;
166*b2055c35SXin Li   // Treat ICC profiles separately as they may be segmented and out of order.
167*b2055c35SXin Li   if (!StoreICCP(dinfo, &metadata->iccp)) return 0;
168*b2055c35SXin Li 
169*b2055c35SXin Li   for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
170*b2055c35SXin Li     int i;
171*b2055c35SXin Li     for (i = 0; kJPEGMetadataMap[i].marker != 0; ++i) {
172*b2055c35SXin Li       if (marker->marker == kJPEGMetadataMap[i].marker &&
173*b2055c35SXin Li           marker->data_length > kJPEGMetadataMap[i].signature_length &&
174*b2055c35SXin Li           !memcmp(marker->data, kJPEGMetadataMap[i].signature,
175*b2055c35SXin Li                   kJPEGMetadataMap[i].signature_length)) {
176*b2055c35SXin Li         MetadataPayload* const payload =
177*b2055c35SXin Li             (MetadataPayload*)((uint8_t*)metadata +
178*b2055c35SXin Li                                kJPEGMetadataMap[i].storage_offset);
179*b2055c35SXin Li 
180*b2055c35SXin Li         if (payload->bytes == NULL) {
181*b2055c35SXin Li           const char* marker_data = (const char*)marker->data +
182*b2055c35SXin Li                                     kJPEGMetadataMap[i].signature_length;
183*b2055c35SXin Li           const size_t marker_data_length =
184*b2055c35SXin Li               marker->data_length - kJPEGMetadataMap[i].signature_length;
185*b2055c35SXin Li           if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0;
186*b2055c35SXin Li         } else {
187*b2055c35SXin Li           fprintf(stderr, "Ignoring additional '%s' marker\n",
188*b2055c35SXin Li                   kJPEGMetadataMap[i].signature);
189*b2055c35SXin Li         }
190*b2055c35SXin Li       }
191*b2055c35SXin Li     }
192*b2055c35SXin Li   }
193*b2055c35SXin Li   return 1;
194*b2055c35SXin Li }
195*b2055c35SXin Li 
196*b2055c35SXin Li #undef JPEG_APP1
197*b2055c35SXin Li #undef JPEG_APP2
198*b2055c35SXin Li 
199*b2055c35SXin Li // -----------------------------------------------------------------------------
200*b2055c35SXin Li // JPEG decoding
201*b2055c35SXin Li 
202*b2055c35SXin Li struct my_error_mgr {
203*b2055c35SXin Li   struct jpeg_error_mgr pub;
204*b2055c35SXin Li   jmp_buf setjmp_buffer;
205*b2055c35SXin Li };
206*b2055c35SXin Li 
my_error_exit(j_common_ptr dinfo)207*b2055c35SXin Li static void my_error_exit(j_common_ptr dinfo) {
208*b2055c35SXin Li   struct my_error_mgr* myerr = (struct my_error_mgr*)dinfo->err;
209*b2055c35SXin Li   fprintf(stderr, "libjpeg error: ");
210*b2055c35SXin Li   dinfo->err->output_message(dinfo);
211*b2055c35SXin Li   longjmp(myerr->setjmp_buffer, 1);
212*b2055c35SXin Li }
213*b2055c35SXin Li 
214*b2055c35SXin Li typedef struct {
215*b2055c35SXin Li   struct jpeg_source_mgr pub;
216*b2055c35SXin Li   const uint8_t* data;
217*b2055c35SXin Li   size_t data_size;
218*b2055c35SXin Li } JPEGReadContext;
219*b2055c35SXin Li 
ContextInit(j_decompress_ptr cinfo)220*b2055c35SXin Li static void ContextInit(j_decompress_ptr cinfo) {
221*b2055c35SXin Li   JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
222*b2055c35SXin Li   ctx->pub.next_input_byte = ctx->data;
223*b2055c35SXin Li   ctx->pub.bytes_in_buffer = ctx->data_size;
224*b2055c35SXin Li }
225*b2055c35SXin Li 
ContextFill(j_decompress_ptr cinfo)226*b2055c35SXin Li static boolean ContextFill(j_decompress_ptr cinfo) {
227*b2055c35SXin Li   // we shouldn't get here.
228*b2055c35SXin Li   ERREXIT(cinfo, JERR_FILE_READ);
229*b2055c35SXin Li   return FALSE;
230*b2055c35SXin Li }
231*b2055c35SXin Li 
ContextSkip(j_decompress_ptr cinfo,long jump_size)232*b2055c35SXin Li static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
233*b2055c35SXin Li   JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
234*b2055c35SXin Li   size_t jump = (size_t)jump_size;
235*b2055c35SXin Li   if (jump > ctx->pub.bytes_in_buffer) {  // Don't overflow the buffer.
236*b2055c35SXin Li     jump = ctx->pub.bytes_in_buffer;
237*b2055c35SXin Li   }
238*b2055c35SXin Li   ctx->pub.bytes_in_buffer -= jump;
239*b2055c35SXin Li   ctx->pub.next_input_byte += jump;
240*b2055c35SXin Li }
241*b2055c35SXin Li 
ContextTerm(j_decompress_ptr cinfo)242*b2055c35SXin Li static void ContextTerm(j_decompress_ptr cinfo) {
243*b2055c35SXin Li   (void)cinfo;
244*b2055c35SXin Li }
245*b2055c35SXin Li 
ContextSetup(volatile struct jpeg_decompress_struct * const cinfo,JPEGReadContext * const ctx)246*b2055c35SXin Li static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
247*b2055c35SXin Li                          JPEGReadContext* const ctx) {
248*b2055c35SXin Li   cinfo->src = (struct jpeg_source_mgr*)ctx;
249*b2055c35SXin Li   ctx->pub.init_source = ContextInit;
250*b2055c35SXin Li   ctx->pub.fill_input_buffer = ContextFill;
251*b2055c35SXin Li   ctx->pub.skip_input_data = ContextSkip;
252*b2055c35SXin Li   ctx->pub.resync_to_restart = jpeg_resync_to_restart;
253*b2055c35SXin Li   ctx->pub.term_source = ContextTerm;
254*b2055c35SXin Li   ctx->pub.bytes_in_buffer = 0;
255*b2055c35SXin Li   ctx->pub.next_input_byte = NULL;
256*b2055c35SXin Li }
257*b2055c35SXin Li 
ReadJPEG(const uint8_t * const data,size_t data_size,WebPPicture * const pic,int keep_alpha,Metadata * const metadata)258*b2055c35SXin Li int ReadJPEG(const uint8_t* const data, size_t data_size,
259*b2055c35SXin Li              WebPPicture* const pic, int keep_alpha,
260*b2055c35SXin Li              Metadata* const metadata) {
261*b2055c35SXin Li   volatile int ok = 0;
262*b2055c35SXin Li   int width, height;
263*b2055c35SXin Li   int64_t stride;
264*b2055c35SXin Li   volatile struct jpeg_decompress_struct dinfo;
265*b2055c35SXin Li   struct my_error_mgr jerr;
266*b2055c35SXin Li   uint8_t* volatile rgb = NULL;
267*b2055c35SXin Li   JSAMPROW buffer[1];
268*b2055c35SXin Li   JPEGReadContext ctx;
269*b2055c35SXin Li 
270*b2055c35SXin Li   if (data == NULL || data_size == 0 || pic == NULL) return 0;
271*b2055c35SXin Li 
272*b2055c35SXin Li   (void)keep_alpha;
273*b2055c35SXin Li   memset(&ctx, 0, sizeof(ctx));
274*b2055c35SXin Li   ctx.data = data;
275*b2055c35SXin Li   ctx.data_size = data_size;
276*b2055c35SXin Li 
277*b2055c35SXin Li   memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo));   // for setjmp safety
278*b2055c35SXin Li   dinfo.err = jpeg_std_error(&jerr.pub);
279*b2055c35SXin Li   jerr.pub.error_exit = my_error_exit;
280*b2055c35SXin Li 
281*b2055c35SXin Li   if (setjmp(jerr.setjmp_buffer)) {
282*b2055c35SXin Li  Error:
283*b2055c35SXin Li     MetadataFree(metadata);
284*b2055c35SXin Li     jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
285*b2055c35SXin Li     goto End;
286*b2055c35SXin Li   }
287*b2055c35SXin Li 
288*b2055c35SXin Li   jpeg_create_decompress((j_decompress_ptr)&dinfo);
289*b2055c35SXin Li   ContextSetup(&dinfo, &ctx);
290*b2055c35SXin Li   if (metadata != NULL) SaveMetadataMarkers((j_decompress_ptr)&dinfo);
291*b2055c35SXin Li   jpeg_read_header((j_decompress_ptr)&dinfo, TRUE);
292*b2055c35SXin Li 
293*b2055c35SXin Li   dinfo.out_color_space = JCS_RGB;
294*b2055c35SXin Li   dinfo.do_fancy_upsampling = TRUE;
295*b2055c35SXin Li 
296*b2055c35SXin Li   jpeg_start_decompress((j_decompress_ptr)&dinfo);
297*b2055c35SXin Li 
298*b2055c35SXin Li   if (dinfo.output_components != 3) {
299*b2055c35SXin Li     goto Error;
300*b2055c35SXin Li   }
301*b2055c35SXin Li 
302*b2055c35SXin Li   width = dinfo.output_width;
303*b2055c35SXin Li   height = dinfo.output_height;
304*b2055c35SXin Li   stride = (int64_t)dinfo.output_width * dinfo.output_components * sizeof(*rgb);
305*b2055c35SXin Li 
306*b2055c35SXin Li   if (stride != (int)stride ||
307*b2055c35SXin Li       !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
308*b2055c35SXin Li     goto Error;
309*b2055c35SXin Li   }
310*b2055c35SXin Li 
311*b2055c35SXin Li   rgb = (uint8_t*)malloc((size_t)stride * height);
312*b2055c35SXin Li   if (rgb == NULL) {
313*b2055c35SXin Li     goto Error;
314*b2055c35SXin Li   }
315*b2055c35SXin Li   buffer[0] = (JSAMPLE*)rgb;
316*b2055c35SXin Li 
317*b2055c35SXin Li   while (dinfo.output_scanline < dinfo.output_height) {
318*b2055c35SXin Li     if (jpeg_read_scanlines((j_decompress_ptr)&dinfo, buffer, 1) != 1) {
319*b2055c35SXin Li       goto Error;
320*b2055c35SXin Li     }
321*b2055c35SXin Li     buffer[0] += stride;
322*b2055c35SXin Li   }
323*b2055c35SXin Li 
324*b2055c35SXin Li   if (metadata != NULL) {
325*b2055c35SXin Li     ok = ExtractMetadataFromJPEG((j_decompress_ptr)&dinfo, metadata);
326*b2055c35SXin Li     if (!ok) {
327*b2055c35SXin Li       fprintf(stderr, "Error extracting JPEG metadata!\n");
328*b2055c35SXin Li       goto Error;
329*b2055c35SXin Li     }
330*b2055c35SXin Li   }
331*b2055c35SXin Li 
332*b2055c35SXin Li   jpeg_finish_decompress((j_decompress_ptr)&dinfo);
333*b2055c35SXin Li   jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
334*b2055c35SXin Li 
335*b2055c35SXin Li   // WebP conversion.
336*b2055c35SXin Li   pic->width = width;
337*b2055c35SXin Li   pic->height = height;
338*b2055c35SXin Li   ok = WebPPictureImportRGB(pic, rgb, (int)stride);
339*b2055c35SXin Li   if (!ok) {
340*b2055c35SXin Li     pic->width = 0;   // WebPPictureImportRGB() barely touches 'pic' on failure.
341*b2055c35SXin Li     pic->height = 0;  // Just reset dimensions but keep any 'custom_ptr' etc.
342*b2055c35SXin Li     MetadataFree(metadata);  // In case the caller forgets to free it on error.
343*b2055c35SXin Li   }
344*b2055c35SXin Li 
345*b2055c35SXin Li  End:
346*b2055c35SXin Li   free(rgb);
347*b2055c35SXin Li   return ok;
348*b2055c35SXin Li }
349*b2055c35SXin Li #else  // !WEBP_HAVE_JPEG
ReadJPEG(const uint8_t * const data,size_t data_size,struct WebPPicture * const pic,int keep_alpha,struct Metadata * const metadata)350*b2055c35SXin Li int ReadJPEG(const uint8_t* const data, size_t data_size,
351*b2055c35SXin Li              struct WebPPicture* const pic, int keep_alpha,
352*b2055c35SXin Li              struct Metadata* const metadata) {
353*b2055c35SXin Li   (void)data;
354*b2055c35SXin Li   (void)data_size;
355*b2055c35SXin Li   (void)pic;
356*b2055c35SXin Li   (void)keep_alpha;
357*b2055c35SXin Li   (void)metadata;
358*b2055c35SXin Li   fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
359*b2055c35SXin Li           "development package before building.\n");
360*b2055c35SXin Li   return 0;
361*b2055c35SXin Li }
362*b2055c35SXin Li #endif  // WEBP_HAVE_JPEG
363*b2055c35SXin Li 
364*b2055c35SXin Li // -----------------------------------------------------------------------------
365