github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/examples/jpegdec.c (about)

     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 "config.h"
    16  #endif
    17  
    18  #include <stdio.h>
    19  
    20  #ifdef WEBP_HAVE_JPEG
    21  #include <jpeglib.h>
    22  #include <setjmp.h>
    23  #include <stdlib.h>
    24  #include <string.h>
    25  
    26  #include "webp/encode.h"
    27  #include "./metadata.h"
    28  
    29  // -----------------------------------------------------------------------------
    30  // Metadata processing
    31  
    32  #ifndef JPEG_APP1
    33  # define JPEG_APP1 (JPEG_APP0 + 1)
    34  #endif
    35  #ifndef JPEG_APP2
    36  # define JPEG_APP2 (JPEG_APP0 + 2)
    37  #endif
    38  
    39  typedef struct {
    40    const uint8_t* data;
    41    size_t data_length;
    42    int seq;  // this segment's sequence number [1, 255] for use in reassembly.
    43  } ICCPSegment;
    44  
    45  static void SaveMetadataMarkers(j_decompress_ptr dinfo) {
    46    const unsigned int max_marker_length = 0xffff;
    47    jpeg_save_markers(dinfo, JPEG_APP1, max_marker_length);  // Exif/XMP
    48    jpeg_save_markers(dinfo, JPEG_APP2, max_marker_length);  // ICC profile
    49  }
    50  
    51  static int CompareICCPSegments(const void* a, const void* b) {
    52    const ICCPSegment* s1 = (const ICCPSegment*)a;
    53    const ICCPSegment* s2 = (const ICCPSegment*)b;
    54    return s1->seq - s2->seq;
    55  }
    56  
    57  // Extract ICC profile segments from the marker list in 'dinfo', reassembling
    58  // and storing them in 'iccp'.
    59  // Returns true on success and false for memory errors and corrupt profiles.
    60  static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
    61    // ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files
    62    static const char kICCPSignature[] = "ICC_PROFILE";
    63    static const size_t kICCPSignatureLength = 12;  // signature includes '\0'
    64    static const size_t kICCPSkipLength = 14;  // signature + seq & count
    65    int expected_count = 0;
    66    int actual_count = 0;
    67    int seq_max = 0;
    68    size_t total_size = 0;
    69    ICCPSegment iccp_segments[255];
    70    jpeg_saved_marker_ptr marker;
    71  
    72    memset(iccp_segments, 0, sizeof(iccp_segments));
    73    for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
    74      if (marker->marker == JPEG_APP2 &&
    75          marker->data_length > kICCPSkipLength &&
    76          !memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) {
    77        // ICC_PROFILE\0<seq><count>; 'seq' starts at 1.
    78        const int seq = marker->data[kICCPSignatureLength];
    79        const int count = marker->data[kICCPSignatureLength + 1];
    80        const size_t segment_size = marker->data_length - kICCPSkipLength;
    81        ICCPSegment* segment;
    82  
    83        if (segment_size == 0 || count == 0 || seq == 0) {
    84          fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)"
    85                          " cannot be 0!\n",
    86                  (int)segment_size, seq, count);
    87          return 0;
    88        }
    89  
    90        if (expected_count == 0) {
    91          expected_count = count;
    92        } else if (expected_count != count) {
    93          fprintf(stderr, "[ICCP] Inconsistent segment count (%d / %d)!\n",
    94                  expected_count, count);
    95          return 0;
    96        }
    97  
    98        segment = iccp_segments + seq - 1;
    99        if (segment->data_length != 0) {
   100          fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq);
   101          return 0;
   102        }
   103  
   104        segment->data = marker->data + kICCPSkipLength;
   105        segment->data_length = segment_size;
   106        segment->seq = seq;
   107        total_size += segment_size;
   108        if (seq > seq_max) seq_max = seq;
   109        ++actual_count;
   110      }
   111    }
   112  
   113    if (actual_count == 0) return 1;
   114    if (seq_max != actual_count) {
   115      fprintf(stderr, "[ICCP] Discontinuous segments, expected: %d actual: %d!\n",
   116              actual_count, seq_max);
   117      return 0;
   118    }
   119    if (expected_count != actual_count) {
   120      fprintf(stderr, "[ICCP] Segment count: %d does not match expected: %d!\n",
   121              actual_count, expected_count);
   122      return 0;
   123    }
   124  
   125    // The segments may appear out of order in the file, sort them based on
   126    // sequence number before assembling the payload.
   127    qsort(iccp_segments, actual_count, sizeof(*iccp_segments),
   128          CompareICCPSegments);
   129  
   130    iccp->bytes = (uint8_t*)malloc(total_size);
   131    if (iccp->bytes == NULL) return 0;
   132    iccp->size = total_size;
   133  
   134    {
   135      int i;
   136      size_t offset = 0;
   137      for (i = 0; i < seq_max; ++i) {
   138        memcpy(iccp->bytes + offset,
   139               iccp_segments[i].data, iccp_segments[i].data_length);
   140        offset += iccp_segments[i].data_length;
   141      }
   142    }
   143    return 1;
   144  }
   145  
   146  // Returns true on success and false for memory errors and corrupt profiles.
   147  // The caller must use MetadataFree() on 'metadata' in all cases.
   148  static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
   149                                     Metadata* const metadata) {
   150    static const struct {
   151      int marker;
   152      const char* signature;
   153      size_t signature_length;
   154      size_t storage_offset;
   155    } kJPEGMetadataMap[] = {
   156      // Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
   157      { JPEG_APP1, "Exif\0",                        6, METADATA_OFFSET(exif) },
   158      // XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
   159      // TODO(jzern) Add support for 'ExtendedXMP'
   160      { JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) },
   161      { 0, NULL, 0, 0 },
   162    };
   163    jpeg_saved_marker_ptr marker;
   164    // Treat ICC profiles separately as they may be segmented and out of order.
   165    if (!StoreICCP(dinfo, &metadata->iccp)) return 0;
   166  
   167    for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
   168      int i;
   169      for (i = 0; kJPEGMetadataMap[i].marker != 0; ++i) {
   170        if (marker->marker == kJPEGMetadataMap[i].marker &&
   171            marker->data_length > kJPEGMetadataMap[i].signature_length &&
   172            !memcmp(marker->data, kJPEGMetadataMap[i].signature,
   173                    kJPEGMetadataMap[i].signature_length)) {
   174          MetadataPayload* const payload =
   175              (MetadataPayload*)((uint8_t*)metadata +
   176                                 kJPEGMetadataMap[i].storage_offset);
   177  
   178          if (payload->bytes == NULL) {
   179            const char* marker_data = (const char*)marker->data +
   180                                      kJPEGMetadataMap[i].signature_length;
   181            const size_t marker_data_length =
   182                marker->data_length - kJPEGMetadataMap[i].signature_length;
   183            if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0;
   184          } else {
   185            fprintf(stderr, "Ignoring additional '%s' marker\n",
   186                    kJPEGMetadataMap[i].signature);
   187          }
   188        }
   189      }
   190    }
   191    return 1;
   192  }
   193  
   194  #undef JPEG_APP1
   195  #undef JPEG_APP2
   196  
   197  // -----------------------------------------------------------------------------
   198  // JPEG decoding
   199  
   200  struct my_error_mgr {
   201    struct jpeg_error_mgr pub;
   202    jmp_buf setjmp_buffer;
   203  };
   204  
   205  static void my_error_exit(j_common_ptr dinfo) {
   206    struct my_error_mgr* myerr = (struct my_error_mgr*)dinfo->err;
   207    dinfo->err->output_message(dinfo);
   208    longjmp(myerr->setjmp_buffer, 1);
   209  }
   210  
   211  int ReadJPEG(FILE* in_file, WebPPicture* const pic, Metadata* const metadata) {
   212    int ok = 0;
   213    int stride, width, height;
   214    struct jpeg_decompress_struct dinfo;
   215    struct my_error_mgr jerr;
   216    uint8_t* rgb = NULL;
   217    JSAMPROW buffer[1];
   218  
   219    dinfo.err = jpeg_std_error(&jerr.pub);
   220    jerr.pub.error_exit = my_error_exit;
   221  
   222    if (setjmp(jerr.setjmp_buffer)) {
   223   Error:
   224      MetadataFree(metadata);
   225      jpeg_destroy_decompress(&dinfo);
   226      goto End;
   227    }
   228  
   229    jpeg_create_decompress(&dinfo);
   230    jpeg_stdio_src(&dinfo, in_file);
   231    if (metadata != NULL) SaveMetadataMarkers(&dinfo);
   232    jpeg_read_header(&dinfo, TRUE);
   233  
   234    dinfo.out_color_space = JCS_RGB;
   235    dinfo.do_fancy_upsampling = TRUE;
   236  
   237    jpeg_start_decompress(&dinfo);
   238  
   239    if (dinfo.output_components != 3) {
   240      goto Error;
   241    }
   242  
   243    width = dinfo.output_width;
   244    height = dinfo.output_height;
   245    stride = dinfo.output_width * dinfo.output_components * sizeof(*rgb);
   246  
   247    rgb = (uint8_t*)malloc(stride * height);
   248    if (rgb == NULL) {
   249      goto End;
   250    }
   251    buffer[0] = (JSAMPLE*)rgb;
   252  
   253    while (dinfo.output_scanline < dinfo.output_height) {
   254      if (jpeg_read_scanlines(&dinfo, buffer, 1) != 1) {
   255        goto End;
   256      }
   257      buffer[0] += stride;
   258    }
   259  
   260    if (metadata != NULL) {
   261      ok = ExtractMetadataFromJPEG(&dinfo, metadata);
   262      if (!ok) {
   263        fprintf(stderr, "Error extracting JPEG metadata!\n");
   264        goto Error;
   265      }
   266    }
   267  
   268    jpeg_finish_decompress(&dinfo);
   269    jpeg_destroy_decompress(&dinfo);
   270  
   271    // WebP conversion.
   272    pic->width = width;
   273    pic->height = height;
   274    ok = WebPPictureImportRGB(pic, rgb, stride);
   275    if (!ok) goto Error;
   276  
   277   End:
   278    free(rgb);
   279    return ok;
   280  }
   281  #else  // !WEBP_HAVE_JPEG
   282  int ReadJPEG(FILE* in_file, struct WebPPicture* const pic,
   283               struct Metadata* const metadata) {
   284    (void)in_file;
   285    (void)pic;
   286    (void)metadata;
   287    fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
   288            "development package before building.\n");
   289    return 0;
   290  }
   291  #endif  // WEBP_HAVE_JPEG
   292  
   293  // -----------------------------------------------------------------------------