From e83ff7decdd25127f16b301aee278bd0aad6aba0 Mon Sep 17 00:00:00 2001 From: James Zern Date: Thu, 24 Jan 2013 15:37:49 -0800 Subject: [PATCH] wicdec: add ICC profile extraction Change-Id: I4522cdd5a529f802f1eb566b6d94539612e0976b --- examples/cwebp.c | 9 +++--- examples/wicdec.c | 81 +++++++++++++++++++++++++++++++++++++++++++++-- examples/wicdec.h | 4 ++- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/examples/cwebp.c b/examples/cwebp.c index 1b2bc44f..5065b913 100644 --- a/examples/cwebp.c +++ b/examples/cwebp.c @@ -79,7 +79,6 @@ static int ReadYUV(FILE* in_file, WebPPicture* const pic) { static int ReadPicture(const char* const filename, WebPPicture* const pic, int keep_alpha, Metadata* const metadata) { int ok; - (void)metadata; // TODO(jzern): add metadata extraction using WIC if (pic->width != 0 && pic->height != 0) { // If image size is specified, infer it as YUV format. FILE* in_file = fopen(filename, "rb"); @@ -91,7 +90,7 @@ static int ReadPicture(const char* const filename, WebPPicture* const pic, fclose(in_file); } else { // If no size specified, try to decode it using WIC. - ok = ReadPictureWithWIC(filename, pic, keep_alpha); + ok = ReadPictureWithWIC(filename, pic, keep_alpha, metadata); } if (!ok) { fprintf(stderr, "Error! Could not process file %s\n", filename); @@ -826,10 +825,10 @@ int main(int argc, const char *argv[]) { start = token + 1; } #ifdef HAVE_WINCODEC_H - if (keep_metadata != 0) { + if (keep_metadata != 0 && keep_metadata != METADATA_ICCP) { // TODO(jzern): remove when -metadata is supported on all platforms. - fprintf(stderr, "Warning: -metadata is currently unsupported on this" - " platform. Ignoring this option!\n"); + fprintf(stderr, "Warning: only ICC profile extraction is currently" + " supported on this platform!\n"); } #endif } else if (!strcmp(argv[c], "-v")) { diff --git a/examples/wicdec.c b/examples/wicdec.c index 619b959a..940801d9 100644 --- a/examples/wicdec.c +++ b/examples/wicdec.c @@ -28,6 +28,7 @@ #include #include "webp/encode.h" +#include "./metadata.h" #define IFS(fn) \ do { \ @@ -61,8 +62,76 @@ static HRESULT OpenInputStream(const char* filename, IStream** ppStream) { return hr; } +// Stores the first non-zero sized color profile from 'pFrame' to 'iccp'. +// Returns an HRESULT to indicate success or failure. The caller is responsible +// for freeing 'iccp->bytes' in either case. +static HRESULT ExtractICCP(IWICImagingFactory* const pFactory, + IWICBitmapFrameDecode* const pFrame, + MetadataPayload* const iccp) { + HRESULT hr = S_OK; + UINT i, count; + IWICColorContext** ppColorContext; + + IFS(IWICBitmapFrameDecode_GetColorContexts(pFrame, 0, NULL, &count)); + if (FAILED(hr) || count == 0) return hr; + + ppColorContext = (IWICColorContext**)calloc(count, sizeof(*ppColorContext)); + if (ppColorContext == NULL) return E_OUTOFMEMORY; + for (i = 0; SUCCEEDED(hr) && i < count; ++i) { + IFS(IWICImagingFactory_CreateColorContext(pFactory, &ppColorContext[i])); + } + + if (SUCCEEDED(hr)) { + UINT num_color_contexts; + IFS(IWICBitmapFrameDecode_GetColorContexts(pFrame, + count, ppColorContext, + &num_color_contexts)); + for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) { + WICColorContextType type; + IFS(IWICColorContext_GetType(ppColorContext[i], &type)); + if (SUCCEEDED(hr) && type == WICColorContextProfile) { + UINT size; + IFS(IWICColorContext_GetProfileBytes(ppColorContext[i], + 0, NULL, &size)); + if (size > 0) { + iccp->bytes = (uint8_t*)malloc(size); + if (iccp->bytes == NULL) { + hr = E_OUTOFMEMORY; + break; + } + iccp->size = size; + IFS(IWICColorContext_GetProfileBytes(ppColorContext[i], + (UINT)iccp->size, iccp->bytes, + &size)); + if (SUCCEEDED(hr) && size != iccp->size) { + fprintf(stderr, "Warning! ICC profile size (%u) != expected (%u)\n", + size, iccp->size); + iccp->size = size; + } + break; + } + } + } + } + for (i = 0; i < count; ++i) { + if (ppColorContext[i] != NULL) IUnknown_Release(ppColorContext[i]); + } + free(ppColorContext); + return hr; +} + +static HRESULT ExtractMetadata(IWICImagingFactory* const pFactory, + IWICBitmapFrameDecode* const pFrame, + Metadata* const metadata) { + // TODO(jzern): add XMP/EXIF extraction. + const HRESULT hr = ExtractICCP(pFactory, pFrame, &metadata->iccp); + if (FAILED(hr)) MetadataFree(metadata); + return hr; +} + int ReadPictureWithWIC(const char* const filename, - WebPPicture* const pic, int keep_alpha) { + WebPPicture* const pic, int keep_alpha, + Metadata* const metadata) { // From Microsoft SDK 7.0a -- wincodec.h // Create local copies for compatibility when building against earlier // versions of the SDK. @@ -191,6 +260,12 @@ int ReadPictureWithWIC(const char* const filename, if (has_alpha && keep_alpha == 2) { WebPCleanupTransparentArea(pic); } + if (metadata != NULL) { + hr = ExtractMetadata(pFactory, pFrame, metadata); + if (FAILED(hr)) { + fprintf(stderr, "Error extracting image metadata using WIC!\n"); + } + } } // Cleanup. @@ -204,10 +279,12 @@ int ReadPictureWithWIC(const char* const filename, } #else // !HAVE_WINCODEC_H int ReadPictureWithWIC(const char* const filename, - struct WebPPicture* const pic, int keep_alpha) { + struct WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata) { (void)filename; (void)pic; (void)keep_alpha; + (void)metadata; fprintf(stderr, "Windows Imaging Component (WIC) support not compiled. " "Visual Studio and mingw-w64 builds support WIC. Make sure " "wincodec.h detection is working correctly if using autoconf " diff --git a/examples/wicdec.h b/examples/wicdec.h index e94133bc..4aa7e906 100644 --- a/examples/wicdec.h +++ b/examples/wicdec.h @@ -14,6 +14,7 @@ extern "C" { #endif +struct Metadata; struct WebPPicture; // Reads an image from 'filename', returning the decoded output in 'pic'. @@ -21,7 +22,8 @@ struct WebPPicture; // RGBA otherwise it will be RGB. // Returns true on success. int ReadPictureWithWIC(const char* const filename, - struct WebPPicture* const pic, int keep_alpha); + struct WebPPicture* const pic, int keep_alpha, + struct Metadata* const metadata); #if defined(__cplusplus) || defined(c_plusplus) } // extern "C"