Last active
May 5, 2019 03:35
-
-
Save hawkingrei/6e08db9954d9c14d6d9d001006a8c614 to your computer and use it in GitHub Desktop.
metadata_write.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "webp/encode.h" | |
| #include "./metadata.h" | |
| int WebPMemoryWrite(const uint8_t *data, size_t data_size, | |
| WebPMemoryWriter *const w) | |
| { | |
| uint64_t next_size; | |
| if (w == NULL) | |
| { | |
| return 1; | |
| } | |
| next_size = (uint64_t)w->size + data_size; | |
| if (next_size > w->max_size) | |
| { | |
| uint8_t *new_mem; | |
| uint64_t next_max_size = 2ULL * w->max_size; | |
| if (next_max_size < next_size) | |
| next_max_size = next_size; | |
| if (next_max_size < 8192ULL) | |
| next_max_size = 8192ULL; | |
| new_mem = (uint8_t *)WebPSafeMalloc(next_max_size, 1); | |
| if (new_mem == NULL) | |
| { | |
| return 0; | |
| } | |
| if (w->size > 0) | |
| { | |
| memcpy(new_mem, w->mem, w->size); | |
| } | |
| WebPSafeFree(w->mem); | |
| w->mem = new_mem; | |
| // down-cast is ok, thanks to WebPSafeMalloc | |
| w->max_size = (size_t)next_max_size; | |
| } | |
| if (data_size > 0) | |
| { | |
| memcpy(w->mem + w->size, data, data_size); | |
| w->size += data_size; | |
| } | |
| return 1; | |
| } | |
| int WebPMemoryWriteN(const uint8_t *data, size_t data_size, size_t count, WebPMemoryWriter *const w) | |
| { | |
| for (size_t n = 0; n < count; count + 1) | |
| { | |
| if (WebPMemoryWrite(data, data_size, w) == 1) | |
| { | |
| return n; | |
| } | |
| } | |
| return count; | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // Metadata writing. | |
| enum | |
| { | |
| METADATA_EXIF = (1 << 0), | |
| METADATA_ICC = (1 << 1), | |
| METADATA_XMP = (1 << 2), | |
| METADATA_ALL = METADATA_EXIF | METADATA_ICC | 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(const WebPMemoryWriter *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 (WebPMemoryWrite(buf, num, out) == 1); | |
| } | |
| static int WriteLE24(const WebPMemoryWriter *const out, uint32_t val) | |
| { | |
| return WriteLE(out, val, 3); | |
| } | |
| static int WriteLE32(const WebPMemoryWriter *const out, uint32_t val) | |
| { | |
| return WriteLE(out, val, 4); | |
| } | |
| static int WriteMetadataChunk(const WebPMemoryWriter *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 = (WebPMemoryWrite(fourcc, kTagSize, out) == 1); | |
| ok = ok && WriteLE32(out, (uint32_t)payload->size); | |
| ok = ok && (WebPMemoryWrite(payload->bytes, payload->size, out) == 1); | |
| return ok && (WebPMemoryWriteN(&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(const WebPMemoryWriter *const out, | |
| const WebPPicture *const picture, | |
| const WebPMemoryWriter *const memory_writer, | |
| const Metadata *const metadata, | |
| int keep_metadata, | |
| int *const metadata_written) | |
| { | |
| 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_ICC), | |
| 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; | |
| *metadata_written = 0; | |
| 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 = (WebPMemoryWrite(webp, kTagSize, out) == 1); | |
| // RIFF size (file header size is not recorded) | |
| ok = ok && WriteLE32(out, riff_size); | |
| webp += kChunkHeaderSize; | |
| webp_size -= kChunkHeaderSize; | |
| // WEBP | |
| ok = ok && (WebPMemoryWrite(webp, kTagSize, out) == 1); | |
| webp += kTagSize; | |
| webp_size -= kTagSize; | |
| if (has_vp8x) | |
| { // update the existing VP8X flags | |
| webp[kChunkHeaderSize] |= (uint8_t)(flags & 0xff); | |
| ok = ok && (WebPMemoryWrite(webp, kVP8XChunkSize, out) == 1); | |
| webp += kVP8XChunkSize; | |
| webp_size -= kVP8XChunkSize; | |
| } | |
| else | |
| { | |
| const int is_lossless = !memcmp(webp, "VP8L", kTagSize); | |
| if (is_lossless) | |
| { | |
| // Presence of alpha is stored in the 37th bit (29th after the | |
| // signature) of VP8L data. | |
| if (webp[kChunkHeaderSize + 4] & (1 << 4)) | |
| flags |= kAlphaFlag; | |
| } | |
| ok = ok && (WebPMemoryWrite(kVP8XHeader, kChunkHeaderSize, 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); | |
| *metadata_written |= METADATA_ICC; | |
| } | |
| // Image | |
| ok = ok && (WebPMemoryWrite(webp, webp_size, out) == 1); | |
| if (write_exif) | |
| { | |
| ok = ok && WriteMetadataChunk(out, "EXIF", &metadata->exif); | |
| *metadata_written |= METADATA_EXIF; | |
| } | |
| if (write_xmp) | |
| { | |
| ok = ok && WriteMetadataChunk(out, "XMP ", &metadata->xmp); | |
| *metadata_written |= METADATA_XMP; | |
| } | |
| return ok; | |
| } | |
| // No metadata, just write the original image file. | |
| return (WebPMemoryWrite(webp, webp_size, out) == 1); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment