Merge "cwebp: enable '-metadata'"

This commit is contained in:
James Zern 2013-01-24 20:04:09 -08:00 committed by Gerrit Code Review
commit 522e9d6108

View File

@ -133,7 +133,6 @@ static int ReadPicture(const char* const filename, WebPPicture* const pic,
int keep_alpha, Metadata* const metadata) {
int ok = 0;
FILE* in_file = fopen(filename, "rb");
(void)metadata; // TODO(jzern): add metadata extraction to the formats below.
if (in_file == NULL) {
fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
return ok;
@ -368,6 +367,129 @@ enum {
METADATA_ALL = METADATA_EXIF | METADATA_ICCP | METADATA_XMP
};
static const int kChunkHeaderSize = 8;
static const int kTagSize = 4;
// Outputs, in little endian, 'num' bytes from 'val' to 'out'.
static int WriteLE(FILE* const out, uint32_t val, int num) {
uint8_t buf[4];
int i;
for (i = 0; i < num; ++i) {
buf[i] = (uint8_t)(val & 0xff);
val >>= 8;
}
return (fwrite(buf, num, 1, out) == 1);
}
static int WriteLE24(FILE* const out, uint32_t val) {
return WriteLE(out, val, 3);
}
static int WriteLE32(FILE* const out, uint32_t val) {
return WriteLE(out, val, 4);
}
static int WriteMetadataChunk(FILE* const out, const char fourcc[4],
const MetadataPayload* const payload) {
const uint8_t zero = 0;
const size_t need_padding = payload->size & 1;
int ok = (fwrite(fourcc, kTagSize, 1, out) == 1);
ok = ok && WriteLE32(out, (uint32_t)payload->size);
ok = ok && (fwrite(payload->bytes, payload->size, 1, out) == 1);
return ok && (fwrite(&zero, need_padding, need_padding, out) == need_padding);
}
// Sets 'flag' in 'vp8x_flags' and updates 'metadata_size' with the size of the
// chunk if there is metadata and 'keep' is true.
static int UpdateFlagsAndSize(const MetadataPayload* const payload,
int keep, int flag,
uint32_t* vp8x_flags, uint64_t* metadata_size) {
if (keep && payload->bytes != NULL && payload->size > 0) {
*vp8x_flags |= flag;
*metadata_size += kChunkHeaderSize + payload->size + (payload->size & 1);
return 1;
}
return 0;
}
// Writes a WebP file using the image contained in 'memory_writer' and the
// metadata from 'metadata'. Metadata is controlled by 'keep_metadata' and the
// availability in 'metadata'. Returns true on success.
// For details see doc/webp-container-spec.txt#extended-file-format.
static int WriteWebPWithMetadata(FILE* const out,
const WebPPicture* const picture,
const WebPMemoryWriter* const memory_writer,
const Metadata* const metadata,
int keep_metadata) {
const char kVP8XHeader[] = "VP8X\x0a\x00\x00\x00";
const int kAlphaFlag = 0x10;
const int kEXIFFlag = 0x08;
const int kICCPFlag = 0x20;
const int kXMPFlag = 0x04;
const size_t kRiffHeaderSize = 12;
const size_t kMaxChunkPayload = ~0 - kChunkHeaderSize - 1;
const size_t kMinSize = kRiffHeaderSize + kChunkHeaderSize;
uint32_t flags = 0;
uint64_t metadata_size = 0;
const int write_exif = UpdateFlagsAndSize(&metadata->exif,
!!(keep_metadata & METADATA_EXIF),
kEXIFFlag, &flags, &metadata_size);
const int write_iccp = UpdateFlagsAndSize(&metadata->iccp,
!!(keep_metadata & METADATA_ICCP),
kICCPFlag, &flags, &metadata_size);
const int write_xmp = UpdateFlagsAndSize(&metadata->xmp,
!!(keep_metadata & METADATA_XMP),
kXMPFlag, &flags, &metadata_size);
uint8_t* webp = memory_writer->mem;
size_t webp_size = memory_writer->size;
if (webp_size < kMinSize) return 0;
if (webp_size - kChunkHeaderSize + metadata_size > kMaxChunkPayload) {
fprintf(stderr, "Error! Addition of metadata would exceed "
"container size limit.\n");
return 0;
}
if (metadata_size > 0) {
const int kVP8XChunkSize = 18;
const int has_vp8x = !memcmp(webp + kRiffHeaderSize, "VP8X", kTagSize);
const uint32_t riff_size = (uint32_t)(webp_size - kChunkHeaderSize +
(has_vp8x ? 0 : kVP8XChunkSize) +
metadata_size);
// RIFF
int ok = (fwrite(webp, kTagSize, 1, out) == 1);
// RIFF size (file header size is not recorded)
ok = ok && WriteLE32(out, riff_size);
webp += kChunkHeaderSize;
webp_size -= kChunkHeaderSize;
// WEBP
ok = ok && (fwrite(webp, kTagSize, 1, out) == 1);
webp += kTagSize;
webp_size -= kTagSize;
if (has_vp8x) { // update the existing VP8X flags
webp[kChunkHeaderSize] |= (uint8_t)(flags & 0xff);
ok = ok && (fwrite(webp, kVP8XChunkSize, 1, out) == 1);
webp_size -= kVP8XChunkSize;
} else {
const int is_lossless = !memcmp(webp, "VP8L", kTagSize);
// The alpha flag is forced with lossless images.
if (is_lossless) flags |= kAlphaFlag;
ok = ok && (fwrite(kVP8XHeader, kChunkHeaderSize, 1, out) == 1);
ok = ok && WriteLE32(out, flags);
ok = ok && WriteLE24(out, picture->width - 1);
ok = ok && WriteLE24(out, picture->height - 1);
}
if (write_iccp) ok = ok && WriteMetadataChunk(out, "ICCP", &metadata->iccp);
// Image
ok = ok && (fwrite(webp, webp_size, 1, out) == 1);
if (write_exif) ok = ok && WriteMetadataChunk(out, "EXIF", &metadata->exif);
if (write_xmp) ok = ok && WriteMetadataChunk(out, "XMP ", &metadata->xmp);
return ok;
} else {
// No metadata, just write the original image file.
return (fwrite(webp, webp_size, 1, out) == 1);
}
}
//------------------------------------------------------------------------------
static int ProgressReport(int percent, const WebPPicture* const picture) {
@ -505,6 +627,7 @@ int main(int argc, const char *argv[]) {
WebPPicture original_picture; // when PSNR or SSIM is requested
WebPConfig config;
WebPAuxStats stats;
WebPMemoryWriter memory_writer;
Metadata metadata;
Stopwatch stop_watch;
@ -702,11 +825,13 @@ int main(int argc, const char *argv[]) {
}
start = token + 1;
}
#ifdef HAVE_WINCODEC_H
if (keep_metadata != 0) {
// TODO(jzern): remove when -metadata is supported on all platforms.
fprintf(stderr, "Warning: -metadata is currently unsupported on this"
" platform. Ignoring this option!\n");
}
#endif
} else if (!strcmp(argv[c], "-v")) {
verbose = 1;
} else if (argv[c][0] == '-') {
@ -768,8 +893,14 @@ int main(int argc, const char *argv[]) {
fprintf(stderr, "Saving file '%s'\n", out_file);
}
}
picture.writer = MyWriter;
picture.custom_ptr = (void*)out;
if (keep_metadata == 0) {
picture.writer = MyWriter;
picture.custom_ptr = (void*)out;
} else {
WebPMemoryWriterInit(&memory_writer);
picture.writer = WebPMemoryWrite;
picture.custom_ptr = (void*)&memory_writer;
}
} else {
out = NULL;
if (!quiet && !short_output) {
@ -825,6 +956,14 @@ int main(int argc, const char *argv[]) {
}
}
if (keep_metadata != 0 && out != NULL) {
if (!WriteWebPWithMetadata(out, &picture, &memory_writer,
&metadata, keep_metadata)) {
fprintf(stderr, "Error writing WebP file with metadata!\n");
goto Error;
}
}
if (!quiet) {
if (config.lossless) {
PrintExtraInfoLossless(&picture, short_output, in_file);