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