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 decoder decoder;
IStream *stream;
struct decoder_frame decoder_frame;
UINT stride;
BYTE *image_bits;
@ -425,6 +426,8 @@ HRESULT CDECL png_decoder_initialize(struct decoder *iface, IStream *stream, str
WICBitmapDecoderCapabilityCanEnumerateMetadata;
st->frame_count = 1;
This->stream = stream;
hr = S_OK;
end:
@ -455,6 +458,79 @@ HRESULT CDECL png_decoder_copy_pixels(struct decoder *iface, UINT frame,
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)
{
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_get_frame_info,
png_decoder_copy_pixels,
png_decoder_get_metadata_blocks,
png_decoder_destroy
};

View File

@ -24,6 +24,7 @@
#include "windef.h"
#include "winbase.h"
#include "winternl.h"
#include "objbase.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];
}
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,
DWORD persist_options, MetadataItem **items, DWORD *item_count)
{
@ -459,7 +418,7 @@ typedef struct {
BYTE *image_bits;
CRITICAL_SECTION lock; /* must be held when png structures are accessed or initialized is set */
ULONG metadata_count;
metadata_block_info* metadata_blocks;
struct decoder_block* metadata_blocks;
} PngDecoder;
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);
ULONG ref = InterlockedDecrement(&This->ref);
ULONG i;
TRACE("(%p) refcount=%u\n", iface, ref);
@ -530,11 +488,6 @@ static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface)
This->lock.DebugInfo->Spare[0] = 0;
DeleteCriticalSection(&This->lock);
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);
}
@ -587,10 +540,6 @@ static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *p
png_uint_32 transparency;
png_color_16p trans_values;
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);
@ -783,52 +732,7 @@ static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *p
ppng_read_end(This->png_ptr, This->end_info);
/* Find the metadata chunks in the file. */
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));
decoder_get_metadata_blocks(This->png_decoder, 0, &This->metadata_count, &This->metadata_blocks);
This->stream = pIStream;
IStream_AddRef(This->stream);
@ -1185,39 +1089,34 @@ static HRESULT WINAPI PngDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader
if (nIndex >= This->metadata_count || !ppIMetadataReader)
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))
{
hr = IWICStream_InitializeFromIStreamRegion(stream, This->stream,
This->metadata_blocks[nIndex].ofs, This->metadata_blocks[nIndex].len);
hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory,
&GUID_ContainerFormatPng, NULL, This->metadata_blocks[nIndex].options,
(IStream*)stream, ppIMetadataReader);
if (SUCCEEDED(hr))
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);
IWICComponentFactory_Release(factory);
}
if (FAILED(hr))
{
*ppIMetadataReader = NULL;
return hr;
}
IWICStream_Release(stream);
}
*ppIMetadataReader = This->metadata_blocks[nIndex].reader;
IWICMetadataReader_AddRef(*ppIMetadataReader);
if (FAILED(hr))
*ppIMetadataReader = NULL;
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);
}
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)
{
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_get_frame_info,
decoder_wrapper_copy_pixels,
decoder_wrapper_get_metadata_blocks,
decoder_wrapper_destroy
};

View File

@ -70,6 +70,7 @@ static const struct unix_funcs unix_funcs = {
decoder_initialize,
decoder_get_frame_info,
decoder_copy_pixels,
decoder_get_metadata_blocks,
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);
}
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)
{
decoder->vtable->destroy(decoder);
@ -101,3 +106,49 @@ HRESULT copy_pixels(UINT bpp, const BYTE *srcbuffer,
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;
HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_size);
/* unixlib iface */
struct decoder_funcs;
@ -277,6 +279,13 @@ struct decoder_frame
WICColor palette[256];
};
struct decoder_block
{
ULONGLONG offset;
ULONGLONG length;
DWORD options;
};
struct decoder
{
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 *copy_pixels)(struct decoder* This, UINT frame, const WICRect *prc,
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);
};
@ -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_copy_pixels(struct decoder* This, UINT frame, const WICRect *prc,
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);
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_copy_pixels)(struct decoder* This, UINT frame, const WICRect *prc,
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);
};