Blame imageio/jpegdec.c

Packit 9c6abc
// Copyright 2012 Google Inc. All Rights Reserved.
Packit 9c6abc
//
Packit 9c6abc
// Use of this source code is governed by a BSD-style license
Packit 9c6abc
// that can be found in the COPYING file in the root of the source
Packit 9c6abc
// tree. An additional intellectual property rights grant can be found
Packit 9c6abc
// in the file PATENTS. All contributing project authors may
Packit 9c6abc
// be found in the AUTHORS file in the root of the source tree.
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
//
Packit 9c6abc
// JPEG decode.
Packit 9c6abc
Packit 9c6abc
#include "./jpegdec.h"
Packit 9c6abc
Packit 9c6abc
#ifdef HAVE_CONFIG_H
Packit 9c6abc
#include "webp/config.h"
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
#include <stdio.h>
Packit 9c6abc
Packit 9c6abc
#ifdef WEBP_HAVE_JPEG
Packit 9c6abc
#include <jpeglib.h>
Packit 9c6abc
#include <jerror.h>
Packit 9c6abc
#include <setjmp.h>
Packit 9c6abc
#include <stdlib.h>
Packit 9c6abc
#include <string.h>
Packit 9c6abc
Packit 9c6abc
#include "webp/encode.h"
Packit 9c6abc
#include "./imageio_util.h"
Packit 9c6abc
#include "./metadata.h"
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Metadata processing
Packit 9c6abc
Packit 9c6abc
#ifndef JPEG_APP1
Packit 9c6abc
# define JPEG_APP1 (JPEG_APP0 + 1)
Packit 9c6abc
#endif
Packit 9c6abc
#ifndef JPEG_APP2
Packit 9c6abc
# define JPEG_APP2 (JPEG_APP0 + 2)
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
typedef struct {
Packit 9c6abc
  const uint8_t* data;
Packit 9c6abc
  size_t data_length;
Packit 9c6abc
  int seq;  // this segment's sequence number [1, 255] for use in reassembly.
Packit 9c6abc
} ICCPSegment;
Packit 9c6abc
Packit 9c6abc
static void SaveMetadataMarkers(j_decompress_ptr dinfo) {
Packit 9c6abc
  const unsigned int max_marker_length = 0xffff;
Packit 9c6abc
  jpeg_save_markers(dinfo, JPEG_APP1, max_marker_length);  // Exif/XMP
Packit 9c6abc
  jpeg_save_markers(dinfo, JPEG_APP2, max_marker_length);  // ICC profile
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static int CompareICCPSegments(const void* a, const void* b) {
Packit 9c6abc
  const ICCPSegment* s1 = (const ICCPSegment*)a;
Packit 9c6abc
  const ICCPSegment* s2 = (const ICCPSegment*)b;
Packit 9c6abc
  return s1->seq - s2->seq;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Extract ICC profile segments from the marker list in 'dinfo', reassembling
Packit 9c6abc
// and storing them in 'iccp'.
Packit 9c6abc
// Returns true on success and false for memory errors and corrupt profiles.
Packit 9c6abc
static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
Packit 9c6abc
  // ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files
Packit 9c6abc
  static const char kICCPSignature[] = "ICC_PROFILE";
Packit 9c6abc
  static const size_t kICCPSignatureLength = 12;  // signature includes '\0'
Packit 9c6abc
  static const size_t kICCPSkipLength = 14;  // signature + seq & count
Packit 9c6abc
  int expected_count = 0;
Packit 9c6abc
  int actual_count = 0;
Packit 9c6abc
  int seq_max = 0;
Packit 9c6abc
  size_t total_size = 0;
Packit 9c6abc
  ICCPSegment iccp_segments[255];
Packit 9c6abc
  jpeg_saved_marker_ptr marker;
Packit 9c6abc
Packit 9c6abc
  memset(iccp_segments, 0, sizeof(iccp_segments));
Packit 9c6abc
  for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
Packit 9c6abc
    if (marker->marker == JPEG_APP2 &&
Packit 9c6abc
        marker->data_length > kICCPSkipLength &&
Packit 9c6abc
        !memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) {
Packit 9c6abc
      // ICC_PROFILE\0<seq><count>; 'seq' starts at 1.
Packit 9c6abc
      const int seq = marker->data[kICCPSignatureLength];
Packit 9c6abc
      const int count = marker->data[kICCPSignatureLength + 1];
Packit 9c6abc
      const size_t segment_size = marker->data_length - kICCPSkipLength;
Packit 9c6abc
      ICCPSegment* segment;
Packit 9c6abc
Packit 9c6abc
      if (segment_size == 0 || count == 0 || seq == 0) {
Packit 9c6abc
        fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)"
Packit 9c6abc
                        " cannot be 0!\n",
Packit 9c6abc
                (int)segment_size, seq, count);
Packit 9c6abc
        return 0;
Packit 9c6abc
      }
Packit 9c6abc
Packit 9c6abc
      if (expected_count == 0) {
Packit 9c6abc
        expected_count = count;
Packit 9c6abc
      } else if (expected_count != count) {
Packit 9c6abc
        fprintf(stderr, "[ICCP] Inconsistent segment count (%d / %d)!\n",
Packit 9c6abc
                expected_count, count);
Packit 9c6abc
        return 0;
Packit 9c6abc
      }
Packit 9c6abc
Packit 9c6abc
      segment = iccp_segments + seq - 1;
Packit 9c6abc
      if (segment->data_length != 0) {
Packit 9c6abc
        fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq);
Packit 9c6abc
        return 0;
Packit 9c6abc
      }
Packit 9c6abc
Packit 9c6abc
      segment->data = marker->data + kICCPSkipLength;
Packit 9c6abc
      segment->data_length = segment_size;
Packit 9c6abc
      segment->seq = seq;
Packit 9c6abc
      total_size += segment_size;
Packit 9c6abc
      if (seq > seq_max) seq_max = seq;
Packit 9c6abc
      ++actual_count;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (actual_count == 0) return 1;
Packit 9c6abc
  if (seq_max != actual_count) {
Packit 9c6abc
    fprintf(stderr, "[ICCP] Discontinuous segments, expected: %d actual: %d!\n",
Packit 9c6abc
            actual_count, seq_max);
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
  if (expected_count != actual_count) {
Packit 9c6abc
    fprintf(stderr, "[ICCP] Segment count: %d does not match expected: %d!\n",
Packit 9c6abc
            actual_count, expected_count);
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  // The segments may appear out of order in the file, sort them based on
Packit 9c6abc
  // sequence number before assembling the payload.
Packit 9c6abc
  qsort(iccp_segments, actual_count, sizeof(*iccp_segments),
Packit 9c6abc
        CompareICCPSegments);
Packit 9c6abc
Packit 9c6abc
  iccp->bytes = (uint8_t*)malloc(total_size);
Packit 9c6abc
  if (iccp->bytes == NULL) return 0;
Packit 9c6abc
  iccp->size = total_size;
Packit 9c6abc
Packit 9c6abc
  {
Packit 9c6abc
    int i;
Packit 9c6abc
    size_t offset = 0;
Packit 9c6abc
    for (i = 0; i < seq_max; ++i) {
Packit 9c6abc
      memcpy(iccp->bytes + offset,
Packit 9c6abc
             iccp_segments[i].data, iccp_segments[i].data_length);
Packit 9c6abc
      offset += iccp_segments[i].data_length;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Returns true on success and false for memory errors and corrupt profiles.
Packit 9c6abc
// The caller must use MetadataFree() on 'metadata' in all cases.
Packit 9c6abc
static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
Packit 9c6abc
                                   Metadata* const metadata) {
Packit 9c6abc
  static const struct {
Packit 9c6abc
    int marker;
Packit 9c6abc
    const char* signature;
Packit 9c6abc
    size_t signature_length;
Packit 9c6abc
    size_t storage_offset;
Packit 9c6abc
  } kJPEGMetadataMap[] = {
Packit 9c6abc
    // Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
Packit 9c6abc
    { JPEG_APP1, "Exif\0",                        6, METADATA_OFFSET(exif) },
Packit 9c6abc
    // XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
Packit 9c6abc
    // TODO(jzern) Add support for 'ExtendedXMP'
Packit 9c6abc
    { JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) },
Packit 9c6abc
    { 0, NULL, 0, 0 },
Packit 9c6abc
  };
Packit 9c6abc
  jpeg_saved_marker_ptr marker;
Packit 9c6abc
  // Treat ICC profiles separately as they may be segmented and out of order.
Packit 9c6abc
  if (!StoreICCP(dinfo, &metadata->iccp)) return 0;
Packit 9c6abc
Packit 9c6abc
  for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
Packit 9c6abc
    int i;
Packit 9c6abc
    for (i = 0; kJPEGMetadataMap[i].marker != 0; ++i) {
Packit 9c6abc
      if (marker->marker == kJPEGMetadataMap[i].marker &&
Packit 9c6abc
          marker->data_length > kJPEGMetadataMap[i].signature_length &&
Packit 9c6abc
          !memcmp(marker->data, kJPEGMetadataMap[i].signature,
Packit 9c6abc
                  kJPEGMetadataMap[i].signature_length)) {
Packit 9c6abc
        MetadataPayload* const payload =
Packit 9c6abc
            (MetadataPayload*)((uint8_t*)metadata +
Packit 9c6abc
                               kJPEGMetadataMap[i].storage_offset);
Packit 9c6abc
Packit 9c6abc
        if (payload->bytes == NULL) {
Packit 9c6abc
          const char* marker_data = (const char*)marker->data +
Packit 9c6abc
                                    kJPEGMetadataMap[i].signature_length;
Packit 9c6abc
          const size_t marker_data_length =
Packit 9c6abc
              marker->data_length - kJPEGMetadataMap[i].signature_length;
Packit 9c6abc
          if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0;
Packit 9c6abc
        } else {
Packit 9c6abc
          fprintf(stderr, "Ignoring additional '%s' marker\n",
Packit 9c6abc
                  kJPEGMetadataMap[i].signature);
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#undef JPEG_APP1
Packit 9c6abc
#undef JPEG_APP2
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// JPEG decoding
Packit 9c6abc
Packit 9c6abc
struct my_error_mgr {
Packit 9c6abc
  struct jpeg_error_mgr pub;
Packit 9c6abc
  jmp_buf setjmp_buffer;
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
static void my_error_exit(j_common_ptr dinfo) {
Packit 9c6abc
  struct my_error_mgr* myerr = (struct my_error_mgr*)dinfo->err;
Packit 9c6abc
  dinfo->err->output_message(dinfo);
Packit 9c6abc
  longjmp(myerr->setjmp_buffer, 1);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
typedef struct {
Packit 9c6abc
  struct jpeg_source_mgr pub;
Packit 9c6abc
  const uint8_t* data;
Packit 9c6abc
  size_t data_size;
Packit 9c6abc
} JPEGReadContext;
Packit 9c6abc
Packit 9c6abc
static void ContextInit(j_decompress_ptr cinfo) {
Packit 9c6abc
  JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
Packit 9c6abc
  ctx->pub.next_input_byte = ctx->data;
Packit 9c6abc
  ctx->pub.bytes_in_buffer = ctx->data_size;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static boolean ContextFill(j_decompress_ptr cinfo) {
Packit 9c6abc
  // we shouldn't get here.
Packit 9c6abc
  ERREXIT(cinfo, JERR_FILE_READ);
Packit 9c6abc
  return FALSE;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
Packit 9c6abc
  JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
Packit 9c6abc
  size_t jump = (size_t)jump_size;
Packit 9c6abc
  if (jump > ctx->pub.bytes_in_buffer) {  // Don't overflow the buffer.
Packit 9c6abc
    jump = ctx->pub.bytes_in_buffer;
Packit 9c6abc
  }
Packit 9c6abc
  ctx->pub.bytes_in_buffer -= jump;
Packit 9c6abc
  ctx->pub.next_input_byte += jump;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void ContextTerm(j_decompress_ptr cinfo) {
Packit 9c6abc
  (void)cinfo;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
Packit 9c6abc
                         JPEGReadContext* const ctx) {
Packit 9c6abc
  cinfo->src = (struct jpeg_source_mgr*)ctx;
Packit 9c6abc
  ctx->pub.init_source = ContextInit;
Packit 9c6abc
  ctx->pub.fill_input_buffer = ContextFill;
Packit 9c6abc
  ctx->pub.skip_input_data = ContextSkip;
Packit 9c6abc
  ctx->pub.resync_to_restart = jpeg_resync_to_restart;
Packit 9c6abc
  ctx->pub.term_source = ContextTerm;
Packit 9c6abc
  ctx->pub.bytes_in_buffer = 0;
Packit 9c6abc
  ctx->pub.next_input_byte = NULL;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
int ReadJPEG(const uint8_t* const data, size_t data_size,
Packit 9c6abc
             WebPPicture* const pic, int keep_alpha,
Packit 9c6abc
             Metadata* const metadata) {
Packit 9c6abc
  volatile int ok = 0;
Packit 9c6abc
  int width, height;
Packit 9c6abc
  int64_t stride;
Packit 9c6abc
  volatile struct jpeg_decompress_struct dinfo;
Packit 9c6abc
  struct my_error_mgr jerr;
Packit 9c6abc
  uint8_t* volatile rgb = NULL;
Packit 9c6abc
  JSAMPROW buffer[1];
Packit 9c6abc
  JPEGReadContext ctx;
Packit 9c6abc
Packit 9c6abc
  if (data == NULL || data_size == 0 || pic == NULL) return 0;
Packit 9c6abc
Packit 9c6abc
  (void)keep_alpha;
Packit 9c6abc
  memset(&ctx, 0, sizeof(ctx));
Packit 9c6abc
  ctx.data = data;
Packit 9c6abc
  ctx.data_size = data_size;
Packit 9c6abc
Packit 9c6abc
  memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo));   // for setjmp sanity
Packit 9c6abc
  dinfo.err = jpeg_std_error(&jerr.pub);
Packit 9c6abc
  jerr.pub.error_exit = my_error_exit;
Packit 9c6abc
Packit 9c6abc
  if (setjmp(jerr.setjmp_buffer)) {
Packit 9c6abc
 Error:
Packit 9c6abc
    MetadataFree(metadata);
Packit 9c6abc
    jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
Packit 9c6abc
    goto End;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  jpeg_create_decompress((j_decompress_ptr)&dinfo);
Packit 9c6abc
  ContextSetup(&dinfo, &ctx;;
Packit 9c6abc
  if (metadata != NULL) SaveMetadataMarkers((j_decompress_ptr)&dinfo);
Packit 9c6abc
  jpeg_read_header((j_decompress_ptr)&dinfo, TRUE);
Packit 9c6abc
Packit 9c6abc
  dinfo.out_color_space = JCS_RGB;
Packit 9c6abc
  dinfo.do_fancy_upsampling = TRUE;
Packit 9c6abc
Packit 9c6abc
  jpeg_start_decompress((j_decompress_ptr)&dinfo);
Packit 9c6abc
Packit 9c6abc
  if (dinfo.output_components != 3) {
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  width = dinfo.output_width;
Packit 9c6abc
  height = dinfo.output_height;
Packit 9c6abc
  stride = (int64_t)dinfo.output_width * dinfo.output_components * sizeof(*rgb);
Packit 9c6abc
Packit 9c6abc
  if (stride != (int)stride ||
Packit 9c6abc
      !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  rgb = (uint8_t*)malloc((size_t)stride * height);
Packit 9c6abc
  if (rgb == NULL) {
Packit 9c6abc
    goto Error;
Packit 9c6abc
  }
Packit 9c6abc
  buffer[0] = (JSAMPLE*)rgb;
Packit 9c6abc
Packit 9c6abc
  while (dinfo.output_scanline < dinfo.output_height) {
Packit 9c6abc
    if (jpeg_read_scanlines((j_decompress_ptr)&dinfo, buffer, 1) != 1) {
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
    buffer[0] += stride;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (metadata != NULL) {
Packit 9c6abc
    ok = ExtractMetadataFromJPEG((j_decompress_ptr)&dinfo, metadata);
Packit 9c6abc
    if (!ok) {
Packit 9c6abc
      fprintf(stderr, "Error extracting JPEG metadata!\n");
Packit 9c6abc
      goto Error;
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  jpeg_finish_decompress((j_decompress_ptr)&dinfo);
Packit 9c6abc
  jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
Packit 9c6abc
Packit 9c6abc
  // WebP conversion.
Packit 9c6abc
  pic->width = width;
Packit 9c6abc
  pic->height = height;
Packit 9c6abc
  ok = WebPPictureImportRGB(pic, rgb, (int)stride);
Packit 9c6abc
  if (!ok) goto Error;
Packit 9c6abc
Packit 9c6abc
 End:
Packit 9c6abc
  free(rgb);
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
#else  // !WEBP_HAVE_JPEG
Packit 9c6abc
int ReadJPEG(const uint8_t* const data, size_t data_size,
Packit 9c6abc
             struct WebPPicture* const pic, int keep_alpha,
Packit 9c6abc
             struct Metadata* const metadata) {
Packit 9c6abc
  (void)data;
Packit 9c6abc
  (void)data_size;
Packit 9c6abc
  (void)pic;
Packit 9c6abc
  (void)keep_alpha;
Packit 9c6abc
  (void)metadata;
Packit 9c6abc
  fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
Packit 9c6abc
          "development package before building.\n");
Packit 9c6abc
  return 0;
Packit 9c6abc
}
Packit 9c6abc
#endif  // WEBP_HAVE_JPEG
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------