windowscodecs: Use the unix library to find PNG metadata.

Signed-off-by: Esme Povirk <esme@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Esme Povirk 2020-10-09 14:56:20 -05:00 committed by Alexandre Julliard
parent 53ce545e69
commit 70ea6485e8
7 changed files with 174 additions and 122 deletions

View File

@ -133,6 +133,7 @@ static void *load_libpng(void)
struct png_decoder struct png_decoder
{ {
struct decoder decoder; struct decoder decoder;
IStream *stream;
struct decoder_frame decoder_frame; struct decoder_frame decoder_frame;
UINT stride; UINT stride;
BYTE *image_bits; BYTE *image_bits;
@ -425,6 +426,8 @@ HRESULT CDECL png_decoder_initialize(struct decoder *iface, IStream *stream, str
WICBitmapDecoderCapabilityCanEnumerateMetadata; WICBitmapDecoderCapabilityCanEnumerateMetadata;
st->frame_count = 1; st->frame_count = 1;
This->stream = stream;
hr = S_OK; hr = S_OK;
end: end:
@ -455,6 +458,79 @@ HRESULT CDECL png_decoder_copy_pixels(struct decoder *iface, UINT frame,
prc, stride, buffersize, buffer); prc, stride, buffersize, buffer);
} }
HRESULT CDECL png_decoder_get_metadata_blocks(struct decoder* iface,
UINT frame, UINT *count, struct decoder_block **blocks)
{
struct png_decoder *This = impl_from_decoder(iface);
HRESULT hr;
struct decoder_block *result = NULL;
ULONGLONG seek;
BYTE chunk_type[4];
ULONG chunk_size;
ULONGLONG chunk_start;
ULONG metadata_blocks_size = 0;
seek = 8;
*count = 0;
do
{
hr = stream_seek(This->stream, seek, STREAM_SEEK_SET, &chunk_start);
if (FAILED(hr)) goto end;
hr = read_png_chunk(This->stream, chunk_type, NULL, &chunk_size);
if (FAILED(hr)) goto end;
if (chunk_type[0] >= 'a' && chunk_type[0] <= 'z' &&
memcmp(chunk_type, "tRNS", 4) && memcmp(chunk_type, "pHYs", 4))
{
/* This chunk is considered metadata. */
if (*count == metadata_blocks_size)
{
struct decoder_block *new_metadata_blocks;
ULONG new_metadata_blocks_size;
new_metadata_blocks_size = 4 + metadata_blocks_size * 2;
new_metadata_blocks = RtlAllocateHeap(GetProcessHeap(), 0,
new_metadata_blocks_size * sizeof(*new_metadata_blocks));
if (!new_metadata_blocks)
{
hr = E_OUTOFMEMORY;
goto end;
}
memcpy(new_metadata_blocks, result,
*count * sizeof(*new_metadata_blocks));
RtlFreeHeap(GetProcessHeap(), 0, result);
result = new_metadata_blocks;
metadata_blocks_size = new_metadata_blocks_size;
}
result[*count].offset = chunk_start;
result[*count].length = chunk_size + 12;
result[*count].options = WICMetadataCreationAllowUnknown;
(*count)++;
}
seek = chunk_start + chunk_size + 12; /* skip data and CRC */
} while (memcmp(chunk_type, "IEND", 4));
end:
if (SUCCEEDED(hr))
{
*blocks = result;
}
else
{
*count = 0;
*blocks = NULL;
RtlFreeHeap(GetProcessHeap(), 0, result);
}
return hr;
}
void CDECL png_decoder_destroy(struct decoder* iface) void CDECL png_decoder_destroy(struct decoder* iface)
{ {
struct png_decoder *This = impl_from_decoder(iface); struct png_decoder *This = impl_from_decoder(iface);
@ -467,6 +543,7 @@ static const struct decoder_funcs png_decoder_vtable = {
png_decoder_initialize, png_decoder_initialize,
png_decoder_get_frame_info, png_decoder_get_frame_info,
png_decoder_copy_pixels, png_decoder_copy_pixels,
png_decoder_get_metadata_blocks,
png_decoder_destroy png_decoder_destroy
}; };

View File

@ -24,6 +24,7 @@
#include "windef.h" #include "windef.h"
#include "winbase.h" #include "winbase.h"
#include "winternl.h"
#include "objbase.h" #include "objbase.h"
#include "wincodecs_private.h" #include "wincodecs_private.h"

View File

@ -44,47 +44,6 @@ static inline ULONG read_ulong_be(BYTE* data)
return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
} }
static HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_size)
{
BYTE header[8];
HRESULT hr;
ULONG bytesread;
hr = IStream_Read(stream, header, 8, &bytesread);
if (FAILED(hr) || bytesread < 8)
{
if (SUCCEEDED(hr))
hr = E_FAIL;
return hr;
}
*data_size = read_ulong_be(&header[0]);
memcpy(type, &header[4], 4);
if (data)
{
*data = HeapAlloc(GetProcessHeap(), 0, *data_size);
if (!*data)
return E_OUTOFMEMORY;
hr = IStream_Read(stream, *data, *data_size, &bytesread);
if (FAILED(hr) || bytesread < *data_size)
{
if (SUCCEEDED(hr))
hr = E_FAIL;
HeapFree(GetProcessHeap(), 0, *data);
*data = NULL;
return hr;
}
/* Windows ignores CRC of the chunk */
}
return S_OK;
}
static HRESULT LoadTextMetadata(IStream *stream, const GUID *preferred_vendor, static HRESULT LoadTextMetadata(IStream *stream, const GUID *preferred_vendor,
DWORD persist_options, MetadataItem **items, DWORD *item_count) DWORD persist_options, MetadataItem **items, DWORD *item_count)
{ {
@ -459,7 +418,7 @@ typedef struct {
BYTE *image_bits; BYTE *image_bits;
CRITICAL_SECTION lock; /* must be held when png structures are accessed or initialized is set */ CRITICAL_SECTION lock; /* must be held when png structures are accessed or initialized is set */
ULONG metadata_count; ULONG metadata_count;
metadata_block_info* metadata_blocks; struct decoder_block* metadata_blocks;
} PngDecoder; } PngDecoder;
static inline PngDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface) static inline PngDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
@ -515,7 +474,6 @@ static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface)
{ {
PngDecoder *This = impl_from_IWICBitmapDecoder(iface); PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
ULONG ref = InterlockedDecrement(&This->ref); ULONG ref = InterlockedDecrement(&This->ref);
ULONG i;
TRACE("(%p) refcount=%u\n", iface, ref); TRACE("(%p) refcount=%u\n", iface, ref);
@ -530,11 +488,6 @@ static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface)
This->lock.DebugInfo->Spare[0] = 0; This->lock.DebugInfo->Spare[0] = 0;
DeleteCriticalSection(&This->lock); DeleteCriticalSection(&This->lock);
HeapFree(GetProcessHeap(), 0, This->image_bits); HeapFree(GetProcessHeap(), 0, This->image_bits);
for (i=0; i<This->metadata_count; i++)
{
if (This->metadata_blocks[i].reader)
IWICMetadataReader_Release(This->metadata_blocks[i].reader);
}
HeapFree(GetProcessHeap(), 0, This->metadata_blocks); HeapFree(GetProcessHeap(), 0, This->metadata_blocks);
HeapFree(GetProcessHeap(), 0, This); HeapFree(GetProcessHeap(), 0, This);
} }
@ -587,10 +540,6 @@ static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *p
png_uint_32 transparency; png_uint_32 transparency;
png_color_16p trans_values; png_color_16p trans_values;
jmp_buf jmpbuf; jmp_buf jmpbuf;
BYTE chunk_type[4];
ULONG chunk_size;
ULARGE_INTEGER chunk_start;
ULONG metadata_blocks_size = 0;
TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions); TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
@ -783,52 +732,7 @@ static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *p
ppng_read_end(This->png_ptr, This->end_info); ppng_read_end(This->png_ptr, This->end_info);
/* Find the metadata chunks in the file. */ decoder_get_metadata_blocks(This->png_decoder, 0, &This->metadata_count, &This->metadata_blocks);
seek.QuadPart = 8;
do
{
hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, &chunk_start);
if (FAILED(hr)) goto end;
hr = read_png_chunk(pIStream, chunk_type, NULL, &chunk_size);
if (FAILED(hr)) goto end;
if (chunk_type[0] >= 'a' && chunk_type[0] <= 'z' &&
memcmp(chunk_type, "tRNS", 4) && memcmp(chunk_type, "pHYs", 4))
{
/* This chunk is considered metadata. */
if (This->metadata_count == metadata_blocks_size)
{
metadata_block_info* new_metadata_blocks;
ULONG new_metadata_blocks_size;
new_metadata_blocks_size = 4 + metadata_blocks_size * 2;
new_metadata_blocks = HeapAlloc(GetProcessHeap(), 0,
new_metadata_blocks_size * sizeof(*new_metadata_blocks));
if (!new_metadata_blocks)
{
hr = E_OUTOFMEMORY;
goto end;
}
memcpy(new_metadata_blocks, This->metadata_blocks,
This->metadata_count * sizeof(*new_metadata_blocks));
HeapFree(GetProcessHeap(), 0, This->metadata_blocks);
This->metadata_blocks = new_metadata_blocks;
metadata_blocks_size = new_metadata_blocks_size;
}
This->metadata_blocks[This->metadata_count].ofs = chunk_start;
This->metadata_blocks[This->metadata_count].len.QuadPart = chunk_size + 12;
This->metadata_blocks[This->metadata_count].reader = NULL;
This->metadata_count++;
}
seek.QuadPart = chunk_start.QuadPart + chunk_size + 12; /* skip data and CRC */
} while (memcmp(chunk_type, "IEND", 4));
This->stream = pIStream; This->stream = pIStream;
IStream_AddRef(This->stream); IStream_AddRef(This->stream);
@ -1185,39 +1089,34 @@ static HRESULT WINAPI PngDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader
if (nIndex >= This->metadata_count || !ppIMetadataReader) if (nIndex >= This->metadata_count || !ppIMetadataReader)
return E_INVALIDARG; return E_INVALIDARG;
if (!This->metadata_blocks[nIndex].reader) hr = StreamImpl_Create(&stream);
if (SUCCEEDED(hr))
{ {
hr = StreamImpl_Create(&stream); ULARGE_INTEGER offset, length;
offset.QuadPart = This->metadata_blocks[nIndex].offset;
length.QuadPart = This->metadata_blocks[nIndex].length;
hr = IWICStream_InitializeFromIStreamRegion(stream, This->stream,
offset, length);
if (SUCCEEDED(hr))
hr = ImagingFactory_CreateInstance(&IID_IWICComponentFactory, (void**)&factory);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = IWICStream_InitializeFromIStreamRegion(stream, This->stream, hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory,
This->metadata_blocks[nIndex].ofs, This->metadata_blocks[nIndex].len); &GUID_ContainerFormatPng, NULL, This->metadata_blocks[nIndex].options,
(IStream*)stream, ppIMetadataReader);
if (SUCCEEDED(hr)) IWICComponentFactory_Release(factory);
hr = ImagingFactory_CreateInstance(&IID_IWICComponentFactory, (void**)&factory);
if (SUCCEEDED(hr))
{
hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory,
&GUID_ContainerFormatPng, NULL, WICMetadataCreationAllowUnknown,
(IStream*)stream, &This->metadata_blocks[nIndex].reader);
IWICComponentFactory_Release(factory);
}
IWICStream_Release(stream);
} }
if (FAILED(hr)) IWICStream_Release(stream);
{
*ppIMetadataReader = NULL;
return hr;
}
} }
*ppIMetadataReader = This->metadata_blocks[nIndex].reader; if (FAILED(hr))
IWICMetadataReader_AddRef(*ppIMetadataReader); *ppIMetadataReader = NULL;
return S_OK; return S_OK;
} }

View File

@ -85,6 +85,13 @@ HRESULT CDECL decoder_wrapper_copy_pixels(struct decoder* iface, UINT frame,
return unix_funcs->decoder_copy_pixels(This->unix_decoder, frame, prc, stride, buffersize, buffer); return unix_funcs->decoder_copy_pixels(This->unix_decoder, frame, prc, stride, buffersize, buffer);
} }
HRESULT CDECL decoder_wrapper_get_metadata_blocks(struct decoder* iface,
UINT frame, UINT *count, struct decoder_block **blocks)
{
struct decoder_wrapper* This = impl_from_decoder(iface);
return unix_funcs->decoder_get_metadata_blocks(This->unix_decoder, frame, count, blocks);
}
void CDECL decoder_wrapper_destroy(struct decoder* iface) void CDECL decoder_wrapper_destroy(struct decoder* iface)
{ {
struct decoder_wrapper* This = impl_from_decoder(iface); struct decoder_wrapper* This = impl_from_decoder(iface);
@ -96,6 +103,7 @@ static const struct decoder_funcs decoder_wrapper_vtable = {
decoder_wrapper_initialize, decoder_wrapper_initialize,
decoder_wrapper_get_frame_info, decoder_wrapper_get_frame_info,
decoder_wrapper_copy_pixels, decoder_wrapper_copy_pixels,
decoder_wrapper_get_metadata_blocks,
decoder_wrapper_destroy decoder_wrapper_destroy
}; };

View File

@ -70,6 +70,7 @@ static const struct unix_funcs unix_funcs = {
decoder_initialize, decoder_initialize,
decoder_get_frame_info, decoder_get_frame_info,
decoder_copy_pixels, decoder_copy_pixels,
decoder_get_metadata_blocks,
decoder_destroy decoder_destroy
}; };

View File

@ -32,6 +32,11 @@ HRESULT CDECL decoder_copy_pixels(struct decoder *decoder, UINT frame,
return decoder->vtable->copy_pixels(decoder, frame, prc, stride, buffersize, buffer); return decoder->vtable->copy_pixels(decoder, frame, prc, stride, buffersize, buffer);
} }
HRESULT CDECL decoder_get_metadata_blocks(struct decoder *decoder, UINT frame, UINT *count, struct decoder_block **blocks)
{
return decoder->vtable->get_metadata_blocks(decoder, frame, count, blocks);
}
void CDECL decoder_destroy(struct decoder *decoder) void CDECL decoder_destroy(struct decoder *decoder)
{ {
decoder->vtable->destroy(decoder); decoder->vtable->destroy(decoder);
@ -101,3 +106,49 @@ HRESULT copy_pixels(UINT bpp, const BYTE *srcbuffer,
return E_FAIL; return E_FAIL;
} }
} }
static inline ULONG read_ulong_be(BYTE* data)
{
return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
}
HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_size)
{
BYTE header[8];
HRESULT hr;
ULONG bytesread;
hr = stream_read(stream, header, 8, &bytesread);
if (FAILED(hr) || bytesread < 8)
{
if (SUCCEEDED(hr))
hr = E_FAIL;
return hr;
}
*data_size = read_ulong_be(&header[0]);
memcpy(type, &header[4], 4);
if (data)
{
*data = RtlAllocateHeap(GetProcessHeap(), 0, *data_size);
if (!*data)
return E_OUTOFMEMORY;
hr = stream_read(stream, *data, *data_size, &bytesread);
if (FAILED(hr) || bytesread < *data_size)
{
if (SUCCEEDED(hr))
hr = E_FAIL;
RtlFreeHeap(GetProcessHeap(), 0, *data);
*data = NULL;
return hr;
}
/* Windows ignores CRC of the chunk */
}
return S_OK;
}

View File

@ -249,6 +249,8 @@ static inline const char *debug_wic_rect(const WICRect *rect)
extern HMODULE windowscodecs_module; extern HMODULE windowscodecs_module;
HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_size);
/* unixlib iface */ /* unixlib iface */
struct decoder_funcs; struct decoder_funcs;
@ -277,6 +279,13 @@ struct decoder_frame
WICColor palette[256]; WICColor palette[256];
}; };
struct decoder_block
{
ULONGLONG offset;
ULONGLONG length;
DWORD options;
};
struct decoder struct decoder
{ {
const struct decoder_funcs *vtable; const struct decoder_funcs *vtable;
@ -288,6 +297,8 @@ struct decoder_funcs
HRESULT (CDECL *get_frame_info)(struct decoder* This, UINT frame, struct decoder_frame *info); HRESULT (CDECL *get_frame_info)(struct decoder* This, UINT frame, struct decoder_frame *info);
HRESULT (CDECL *copy_pixels)(struct decoder* This, UINT frame, const WICRect *prc, HRESULT (CDECL *copy_pixels)(struct decoder* This, UINT frame, const WICRect *prc,
UINT stride, UINT buffersize, BYTE *buffer); UINT stride, UINT buffersize, BYTE *buffer);
HRESULT (CDECL *get_metadata_blocks)(struct decoder* This, UINT frame, UINT *count,
struct decoder_block **blocks);
void (CDECL *destroy)(struct decoder* This); void (CDECL *destroy)(struct decoder* This);
}; };
@ -305,6 +316,8 @@ HRESULT CDECL decoder_initialize(struct decoder *This, IStream *stream, struct d
HRESULT CDECL decoder_get_frame_info(struct decoder* This, UINT frame, struct decoder_frame *info); HRESULT CDECL decoder_get_frame_info(struct decoder* This, UINT frame, struct decoder_frame *info);
HRESULT CDECL decoder_copy_pixels(struct decoder* This, UINT frame, const WICRect *prc, HRESULT CDECL decoder_copy_pixels(struct decoder* This, UINT frame, const WICRect *prc,
UINT stride, UINT buffersize, BYTE *buffer); UINT stride, UINT buffersize, BYTE *buffer);
HRESULT CDECL decoder_get_metadata_blocks(struct decoder* This, UINT frame, UINT *count,
struct decoder_block **blocks);
void CDECL decoder_destroy(struct decoder *This); void CDECL decoder_destroy(struct decoder *This);
HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **result); HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **result);
@ -316,6 +329,8 @@ struct unix_funcs
HRESULT (CDECL *decoder_get_frame_info)(struct decoder* This, UINT frame, struct decoder_frame *info); HRESULT (CDECL *decoder_get_frame_info)(struct decoder* This, UINT frame, struct decoder_frame *info);
HRESULT (CDECL *decoder_copy_pixels)(struct decoder* This, UINT frame, const WICRect *prc, HRESULT (CDECL *decoder_copy_pixels)(struct decoder* This, UINT frame, const WICRect *prc,
UINT stride, UINT buffersize, BYTE *buffer); UINT stride, UINT buffersize, BYTE *buffer);
HRESULT (CDECL *decoder_get_metadata_blocks)(struct decoder* This, UINT frame, UINT *count,
struct decoder_block **blocks);
void (CDECL *decoder_destroy)(struct decoder* This); void (CDECL *decoder_destroy)(struct decoder* This);
}; };