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