go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

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 // -----------------------------------------------------------------------------