mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-27 06:08:21 +01:00
7861578bd6
This is to infer the needed conversion to YUV(A) or RGB(A). This is useful to avoid some conversion steps between ARGB and YUVA. For instance, if the input file is a JPEG, we decode to RGB and convert to YUV right away, without the intermediate step to ARGB. The only caveat is that cropping/scaling might give slightly different result, because of YUV420 downsampling. Therefore, we omit this feature at cwebp level, when -crop or -rescale is used. Change-Id: I5a3abe5108982f2a4570e841e3d9baffc73f5bee
362 lines
14 KiB
C
362 lines
14 KiB
C
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the COPYING file in the root of the source
|
|
// tree. An additional intellectual property rights grant can be found
|
|
// in the file PATENTS. All contributing project authors may
|
|
// be found in the AUTHORS file in the root of the source tree.
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// Windows Imaging Component (WIC) decode.
|
|
|
|
#include "./wicdec.h"
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "webp/config.h"
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
|
|
#ifdef HAVE_WINCODEC_H
|
|
#ifdef __MINGW32__
|
|
#define INITGUID // Without this GUIDs are declared extern and fail to link
|
|
#endif
|
|
#define CINTERFACE
|
|
#define COBJMACROS
|
|
#define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
|
|
// code with COBJMACROS.
|
|
#include <shlwapi.h>
|
|
#include <windows.h>
|
|
#include <wincodec.h>
|
|
|
|
#include "webp/encode.h"
|
|
#include "./metadata.h"
|
|
|
|
#define IFS(fn) \
|
|
do { \
|
|
if (SUCCEEDED(hr)) { \
|
|
hr = (fn); \
|
|
if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
|
|
} \
|
|
} while (0)
|
|
|
|
// modified version of DEFINE_GUID from guiddef.h.
|
|
#define WEBP_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
static const GUID name = \
|
|
{ l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
|
|
|
#ifdef __cplusplus
|
|
#define MAKE_REFGUID(x) (x)
|
|
#else
|
|
#define MAKE_REFGUID(x) &(x)
|
|
#endif
|
|
|
|
typedef struct WICFormatImporter {
|
|
const GUID* pixel_format;
|
|
int bytes_per_pixel;
|
|
int (*import)(WebPPicture* const, const uint8_t* const, int);
|
|
} WICFormatImporter;
|
|
|
|
// From Microsoft SDK 7.0a -- wincodec.h
|
|
// Create local copies for compatibility when building against earlier
|
|
// versions of the SDK.
|
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppBGR_,
|
|
0x6fddc324, 0x4e03, 0x4bfe,
|
|
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c);
|
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_,
|
|
0x6fddc324, 0x4e03, 0x4bfe,
|
|
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
|
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_,
|
|
0x6fddc324, 0x4e03, 0x4bfe,
|
|
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f);
|
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
|
|
0xf5c7ad2d, 0x6a8d, 0x43dd,
|
|
0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
|
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_,
|
|
0x1562ff7c, 0xd352, 0x46f9,
|
|
0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46);
|
|
WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_,
|
|
0x6fddc324, 0x4e03, 0x4bfe,
|
|
0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16);
|
|
|
|
static HRESULT OpenInputStream(const char* filename, IStream** stream) {
|
|
HRESULT hr = S_OK;
|
|
IFS(SHCreateStreamOnFileA(filename, STGM_READ, stream));
|
|
if (FAILED(hr)) {
|
|
fprintf(stderr, "Error opening input file %s (%08lx)\n", filename, hr);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Metadata processing
|
|
|
|
// Stores the first non-zero sized color profile from 'frame' 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 factory,
|
|
IWICBitmapFrameDecode* const frame,
|
|
MetadataPayload* const iccp) {
|
|
HRESULT hr = S_OK;
|
|
UINT i, count;
|
|
IWICColorContext** color_contexts;
|
|
|
|
IFS(IWICBitmapFrameDecode_GetColorContexts(frame, 0, NULL, &count));
|
|
if (FAILED(hr) || count == 0) return hr;
|
|
|
|
color_contexts = (IWICColorContext**)calloc(count, sizeof(*color_contexts));
|
|
if (color_contexts == NULL) return E_OUTOFMEMORY;
|
|
for (i = 0; SUCCEEDED(hr) && i < count; ++i) {
|
|
IFS(IWICImagingFactory_CreateColorContext(factory, &color_contexts[i]));
|
|
}
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
UINT num_color_contexts;
|
|
IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
|
|
count, color_contexts,
|
|
&num_color_contexts));
|
|
assert(FAILED(hr) || num_color_contexts <= count);
|
|
for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
|
|
WICColorContextType type;
|
|
IFS(IWICColorContext_GetType(color_contexts[i], &type));
|
|
if (SUCCEEDED(hr) && type == WICColorContextProfile) {
|
|
UINT size;
|
|
IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
|
|
0, NULL, &size));
|
|
if (SUCCEEDED(hr) && size > 0) {
|
|
iccp->bytes = (uint8_t*)malloc(size);
|
|
if (iccp->bytes == NULL) {
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
iccp->size = size;
|
|
IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
|
|
(UINT)iccp->size, iccp->bytes,
|
|
&size));
|
|
if (SUCCEEDED(hr) && size != iccp->size) {
|
|
fprintf(stderr, "Warning! ICC profile size (%u) != expected (%u)\n",
|
|
size, (uint32_t)iccp->size);
|
|
iccp->size = size;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < count; ++i) {
|
|
if (color_contexts[i] != NULL) IUnknown_Release(color_contexts[i]);
|
|
}
|
|
free(color_contexts);
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT ExtractMetadata(IWICImagingFactory* const factory,
|
|
IWICBitmapFrameDecode* const frame,
|
|
Metadata* const metadata) {
|
|
// TODO(jzern): add XMP/EXIF extraction.
|
|
const HRESULT hr = ExtractICCP(factory, frame, &metadata->iccp);
|
|
if (FAILED(hr)) MetadataFree(metadata);
|
|
return hr;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
static int HasPalette(GUID pixel_format) {
|
|
return (IsEqualGUID(MAKE_REFGUID(pixel_format),
|
|
MAKE_REFGUID(GUID_WICPixelFormat1bppIndexed)) ||
|
|
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
|
MAKE_REFGUID(GUID_WICPixelFormat2bppIndexed)) ||
|
|
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
|
MAKE_REFGUID(GUID_WICPixelFormat4bppIndexed)) ||
|
|
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
|
MAKE_REFGUID(GUID_WICPixelFormat8bppIndexed)));
|
|
}
|
|
|
|
static int HasAlpha(IWICImagingFactory* const factory,
|
|
IWICBitmapDecoder* const decoder,
|
|
IWICBitmapFrameDecode* const frame,
|
|
GUID pixel_format) {
|
|
int has_alpha;
|
|
if (HasPalette(pixel_format)) {
|
|
IWICPalette* frame_palette = NULL;
|
|
IWICPalette* global_palette = NULL;
|
|
BOOL frame_palette_has_alpha = FALSE;
|
|
BOOL global_palette_has_alpha = FALSE;
|
|
|
|
// A palette may exist at the frame or container level,
|
|
// check IWICPalette::HasAlpha() for both if present.
|
|
if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &frame_palette)) &&
|
|
SUCCEEDED(IWICBitmapFrameDecode_CopyPalette(frame, frame_palette))) {
|
|
IWICPalette_HasAlpha(frame_palette, &frame_palette_has_alpha);
|
|
}
|
|
if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &global_palette)) &&
|
|
SUCCEEDED(IWICBitmapDecoder_CopyPalette(decoder, global_palette))) {
|
|
IWICPalette_HasAlpha(global_palette, &global_palette_has_alpha);
|
|
}
|
|
has_alpha = frame_palette_has_alpha || global_palette_has_alpha;
|
|
|
|
if (frame_palette != NULL) IUnknown_Release(frame_palette);
|
|
if (global_palette != NULL) IUnknown_Release(global_palette);
|
|
} else {
|
|
has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format),
|
|
MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
|
|
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
|
MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_)) ||
|
|
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
|
MAKE_REFGUID(GUID_WICPixelFormat64bppRGBA_)) ||
|
|
IsEqualGUID(MAKE_REFGUID(pixel_format),
|
|
MAKE_REFGUID(GUID_WICPixelFormat64bppBGRA_));
|
|
}
|
|
return has_alpha;
|
|
}
|
|
|
|
int ReadPictureWithWIC(const char* const filename,
|
|
WebPPicture* const pic, int keep_alpha,
|
|
Metadata* const metadata) {
|
|
// From Microsoft SDK 6.0a -- ks.h
|
|
// Define a local copy to avoid link errors under mingw.
|
|
WEBP_DEFINE_GUID(GUID_NULL_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
static const WICFormatImporter kAlphaFormatImporters[] = {
|
|
{ &GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA },
|
|
{ &GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA },
|
|
{ NULL, 0, NULL },
|
|
};
|
|
static const WICFormatImporter kNonAlphaFormatImporters[] = {
|
|
{ &GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR },
|
|
{ &GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB },
|
|
{ NULL, 0, NULL },
|
|
};
|
|
HRESULT hr = S_OK;
|
|
IWICBitmapFrameDecode* frame = NULL;
|
|
IWICFormatConverter* converter = NULL;
|
|
IWICImagingFactory* factory = NULL;
|
|
IWICBitmapDecoder* decoder = NULL;
|
|
IStream* stream = NULL;
|
|
UINT frame_count = 0;
|
|
UINT width = 0, height = 0;
|
|
BYTE* rgb = NULL;
|
|
WICPixelFormatGUID src_pixel_format = GUID_WICPixelFormatUndefined;
|
|
const WICFormatImporter* importer = NULL;
|
|
GUID src_container_format = GUID_NULL_;
|
|
static const GUID* kAlphaContainers[] = {
|
|
&GUID_ContainerFormatBmp,
|
|
&GUID_ContainerFormatPng,
|
|
&GUID_ContainerFormatTiff,
|
|
NULL
|
|
};
|
|
int has_alpha = 0;
|
|
int stride;
|
|
|
|
IFS(CoInitialize(NULL));
|
|
IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
MAKE_REFGUID(IID_IWICImagingFactory),
|
|
(LPVOID*)&factory));
|
|
if (hr == REGDB_E_CLASSNOTREG) {
|
|
fprintf(stderr,
|
|
"Couldn't access Windows Imaging Component (are you running "
|
|
"Windows XP SP3 or newer?). Most formats not available. "
|
|
"Use -s for the available YUV input.\n");
|
|
}
|
|
// Prepare for image decoding.
|
|
IFS(OpenInputStream(filename, &stream));
|
|
IFS(IWICImagingFactory_CreateDecoderFromStream(
|
|
factory, stream, NULL,
|
|
WICDecodeMetadataCacheOnDemand, &decoder));
|
|
IFS(IWICBitmapDecoder_GetFrameCount(decoder, &frame_count));
|
|
if (SUCCEEDED(hr) && frame_count == 0) {
|
|
fprintf(stderr, "No frame found in input file.\n");
|
|
hr = E_FAIL;
|
|
}
|
|
IFS(IWICBitmapDecoder_GetFrame(decoder, 0, &frame));
|
|
IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format));
|
|
IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format));
|
|
|
|
if (SUCCEEDED(hr) && keep_alpha) {
|
|
const GUID** guid;
|
|
for (guid = kAlphaContainers; *guid != NULL; ++guid) {
|
|
if (IsEqualGUID(MAKE_REFGUID(src_container_format),
|
|
MAKE_REFGUID(**guid))) {
|
|
has_alpha = HasAlpha(factory, decoder, frame, src_pixel_format);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prepare for pixel format conversion (if necessary).
|
|
IFS(IWICImagingFactory_CreateFormatConverter(factory, &converter));
|
|
|
|
for (importer = has_alpha ? kAlphaFormatImporters : kNonAlphaFormatImporters;
|
|
hr == S_OK && importer->import != NULL; ++importer) {
|
|
BOOL can_convert;
|
|
const HRESULT cchr = IWICFormatConverter_CanConvert(
|
|
converter,
|
|
MAKE_REFGUID(src_pixel_format),
|
|
MAKE_REFGUID(*importer->pixel_format),
|
|
&can_convert);
|
|
if (SUCCEEDED(cchr) && can_convert) break;
|
|
}
|
|
if (importer->import == NULL) hr = E_FAIL;
|
|
|
|
IFS(IWICFormatConverter_Initialize(converter, (IWICBitmapSource*)frame,
|
|
importer->pixel_format,
|
|
WICBitmapDitherTypeNone,
|
|
NULL, 0.0, WICBitmapPaletteTypeCustom));
|
|
|
|
// Decode.
|
|
IFS(IWICFormatConverter_GetSize(converter, &width, &height));
|
|
stride = importer->bytes_per_pixel * width * sizeof(*rgb);
|
|
if (SUCCEEDED(hr)) {
|
|
rgb = (BYTE*)malloc(stride * height);
|
|
if (rgb == NULL)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
IFS(IWICFormatConverter_CopyPixels(converter, NULL,
|
|
stride, stride * height, rgb));
|
|
|
|
// WebP conversion.
|
|
if (SUCCEEDED(hr)) {
|
|
int ok;
|
|
pic->width = width;
|
|
pic->height = height;
|
|
pic->use_argb = 1; // For WIC, we always force to argb
|
|
ok = importer->import(pic, rgb, stride);
|
|
if (!ok) hr = E_FAIL;
|
|
}
|
|
if (SUCCEEDED(hr)) {
|
|
if (metadata != NULL) {
|
|
hr = ExtractMetadata(factory, frame, metadata);
|
|
if (FAILED(hr)) {
|
|
fprintf(stderr, "Error extracting image metadata using WIC!\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cleanup.
|
|
if (converter != NULL) IUnknown_Release(converter);
|
|
if (frame != NULL) IUnknown_Release(frame);
|
|
if (decoder != NULL) IUnknown_Release(decoder);
|
|
if (factory != NULL) IUnknown_Release(factory);
|
|
if (stream != NULL) IUnknown_Release(stream);
|
|
free(rgb);
|
|
return SUCCEEDED(hr);
|
|
}
|
|
#else // !HAVE_WINCODEC_H
|
|
int ReadPictureWithWIC(const char* const filename,
|
|
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 "
|
|
"and HAVE_WINCODEC_H is defined before building.\n");
|
|
return 0;
|
|
}
|
|
#endif // HAVE_WINCODEC_H
|
|
|
|
// -----------------------------------------------------------------------------
|