gif2webp: Fix ICC and XMP support

Change-Id: Ib5aafef388bd191610e4cc2f8180f35cd454f1d3
This commit is contained in:
Urvang Joshi 2013-04-18 14:19:25 -07:00
parent 46089b207d
commit b26e5ad540

View File

@ -209,6 +209,8 @@ int main(int argc, const char *argv[]) {
WebPConfig config; WebPConfig config;
WebPMux* mux = NULL; WebPMux* mux = NULL;
WebPData webp_data = { NULL, 0 }; WebPData webp_data = { NULL, 0 };
int stored_icc = 0; // Whether we have already stored an ICC profile.
int stored_xmp = 0;
memset(&frame, 0, sizeof(frame)); memset(&frame, 0, sizeof(frame));
frame.id = WEBP_CHUNK_ANMF; frame.id = WEBP_CHUNK_ANMF;
@ -398,24 +400,70 @@ int main(int argc, const char *argv[]) {
if (data[0] != 3 && data[1] != 1) break; // wrong size/marker if (data[0] != 3 && data[1] != 1) break; // wrong size/marker
anim.loop_count = data[2] | (data[3] << 8); anim.loop_count = data[2] | (data[3] << 8);
if (verbose) printf("Loop count: %d\n", anim.loop_count); if (verbose) printf("Loop count: %d\n", anim.loop_count);
} else if (!memcmp(data + 1, "XMP dataXMP", 11)) { } else { // An extension containing metadata.
// Read XMP metadata. // We only store the first encountered chunk of each type.
WebPData xmp; const int is_xmp =
if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) goto End; !stored_xmp && !memcmp(data + 1, "XMP DataXMP", 11);
if (data == NULL) goto End; const int is_icc =
xmp.bytes = (uint8_t*)data; !stored_icc && !memcmp(data + 1, "ICCRGBG1012", 11);
xmp.size = data[0] + 1; if (is_xmp || is_icc) {
WebPMuxSetChunk(mux, "XMP ", &xmp, 1); const char fourccs[2][4] = { "XMP " , "ICCP" };
if (verbose) printf("XMP size: %d\n", (int)xmp.size); const char* const features[2] = { "XMP" , "ICC" };
} else if (!memcmp(data + 1, "ICCRGBG1012", 11)) { WebPData metadata = { NULL, 0 };
// Read ICC profile. // Construct metadata from sub-blocks.
WebPData icc; // Usual case (including ICC profile): In each sub-block, the
if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) goto End; // first byte specifies its size in bytes (0 to 255) and the
if (data == NULL) goto End; // rest of the bytes contain the data.
icc.bytes = (uint8_t*)data; // Special case for XMP data: In each sub-block, the first byte
icc.size = data[0] + 1; // is also part of the XMP payload. XMP in GIF also has a 257
WebPMuxSetChunk(mux, "ICCP", &icc, 1); // byte padding data. See the XMP specification for details.
if (verbose) printf("ICC size: %d\n", (int)icc.size); while (1) {
WebPData prev_metadata = metadata;
WebPData subblock;
if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) {
WebPDataClear(&metadata);
goto End;
}
if (data == NULL) break; // Finished.
subblock.size = is_xmp ? data[0] + 1 : data[0];
assert(subblock.size > 0);
subblock.bytes = is_xmp ? data : data + 1;
metadata.bytes =
(uint8_t*)realloc((void*)metadata.bytes,
prev_metadata.size + subblock.size);
if (metadata.bytes == NULL) {
WebPDataClear(&prev_metadata);
goto End;
}
metadata.size += subblock.size;
memcpy((void*)metadata.bytes + prev_metadata.size,
subblock.bytes, subblock.size);
}
if (is_xmp) {
// XMP padding data is 0x01, 0xff, 0xfe ... 0x01, 0x00.
const int xmp_pading_size = 257;
if (metadata.size > xmp_pading_size) {
metadata.size -= xmp_pading_size;
}
}
// Add metadata chunk.
err = WebPMuxSetChunk(mux, fourccs[is_icc], &metadata, 1);
if (verbose) {
printf("%s size: %d\n", features[is_icc], (int)metadata.size);
}
WebPDataClear(&metadata);
if (err != WEBP_MUX_OK) {
fprintf(stderr, "ERROR (%s): Could not set %s chunk.\n",
ErrorString(err), features[is_icc]);
goto End;
}
if (is_icc) {
stored_icc = 1;
} else if (is_xmp) {
stored_xmp = 1;
}
}
} }
break; break;
} }
@ -423,9 +471,9 @@ int main(int argc, const char *argv[]) {
break; // skip break; // skip
} }
} }
do { while (data != NULL) {
if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) goto End; if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) goto End;
} while (data != NULL); }
break; break;
} }
case TERMINATE_RECORD_TYPE: { case TERMINATE_RECORD_TYPE: {