mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-26 13:48:21 +01:00
Merge "cwebp: enable '-metadata'"
This commit is contained in:
commit
522e9d6108
145
examples/cwebp.c
145
examples/cwebp.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user