From 8b849e814bcf28368324620c8742bad888ac55d9 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 9 Oct 2020 14:56:18 -0500 Subject: [PATCH] windowscodecs: Get PNG frame information from unix lib. Signed-off-by: Esme Povirk Signed-off-by: Alexandre Julliard --- dlls/windowscodecs/libpng.c | 201 +++++++++++++++++++++++++ dlls/windowscodecs/pngformat.c | 96 ++---------- dlls/windowscodecs/unix_iface.c | 7 + dlls/windowscodecs/unix_lib.c | 1 + dlls/windowscodecs/wincodecs_common.h | 5 + dlls/windowscodecs/wincodecs_private.h | 13 ++ 6 files changed, 238 insertions(+), 85 deletions(-) diff --git a/dlls/windowscodecs/libpng.c b/dlls/windowscodecs/libpng.c index d232382209e..b28897115c3 100644 --- a/dlls/windowscodecs/libpng.c +++ b/dlls/windowscodecs/libpng.c @@ -1,4 +1,5 @@ /* + * Copyright 2016 Dmitry Timoshkov * Copyright 2020 Esme Povirk * * This library is free software; you can redistribute it and/or @@ -50,12 +51,23 @@ MAKE_FUNCPTR(png_create_info_struct); MAKE_FUNCPTR(png_create_read_struct); MAKE_FUNCPTR(png_destroy_read_struct); MAKE_FUNCPTR(png_error); +MAKE_FUNCPTR(png_get_bit_depth); +MAKE_FUNCPTR(png_get_color_type); MAKE_FUNCPTR(png_get_error_ptr); +MAKE_FUNCPTR(png_get_image_height); +MAKE_FUNCPTR(png_get_image_width); MAKE_FUNCPTR(png_get_io_ptr); +MAKE_FUNCPTR(png_get_pHYs); +MAKE_FUNCPTR(png_get_PLTE); +MAKE_FUNCPTR(png_get_tRNS); MAKE_FUNCPTR(png_read_info); +MAKE_FUNCPTR(png_set_bgr); MAKE_FUNCPTR(png_set_crc_action); MAKE_FUNCPTR(png_set_error_fn); +MAKE_FUNCPTR(png_set_gray_to_rgb); MAKE_FUNCPTR(png_set_read_fn); +MAKE_FUNCPTR(png_set_swap); +MAKE_FUNCPTR(png_set_tRNS_to_alpha); #undef MAKE_FUNCPTR static CRITICAL_SECTION init_png_cs; @@ -86,12 +98,23 @@ static void *load_libpng(void) LOAD_FUNCPTR(png_create_read_struct); LOAD_FUNCPTR(png_destroy_read_struct); LOAD_FUNCPTR(png_error); + LOAD_FUNCPTR(png_get_bit_depth); + LOAD_FUNCPTR(png_get_color_type); LOAD_FUNCPTR(png_get_error_ptr); + LOAD_FUNCPTR(png_get_image_height); + LOAD_FUNCPTR(png_get_image_width); LOAD_FUNCPTR(png_get_io_ptr); + LOAD_FUNCPTR(png_get_pHYs); + LOAD_FUNCPTR(png_get_PLTE); + LOAD_FUNCPTR(png_get_tRNS); LOAD_FUNCPTR(png_read_info); + LOAD_FUNCPTR(png_set_bgr); LOAD_FUNCPTR(png_set_crc_action); LOAD_FUNCPTR(png_set_error_fn); + LOAD_FUNCPTR(png_set_gray_to_rgb); LOAD_FUNCPTR(png_set_read_fn); + LOAD_FUNCPTR(png_set_swap); + LOAD_FUNCPTR(png_set_tRNS_to_alpha); #undef LOAD_FUNCPTR } @@ -106,6 +129,7 @@ static void *load_libpng(void) struct png_decoder { struct decoder decoder; + struct decoder_frame decoder_frame; }; static inline struct png_decoder *impl_from_decoder(struct decoder* iface) @@ -145,11 +169,22 @@ static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t lengt HRESULT CDECL png_decoder_initialize(struct decoder *iface, IStream *stream, struct decoder_stat *st) { + struct png_decoder *This = impl_from_decoder(iface); png_structp png_ptr; png_infop info_ptr; png_infop end_info; jmp_buf jmpbuf; HRESULT hr = E_FAIL; + int color_type, bit_depth; + png_bytep trans; + int num_trans; + png_uint_32 transparency; + png_color_16p trans_values; + png_uint_32 ret, xres, yres; + int unit_type; + png_colorp png_palette; + int num_palette; + int i; png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) @@ -193,10 +228,168 @@ HRESULT CDECL png_decoder_initialize(struct decoder *iface, IStream *stream, str /* read the header */ ppng_read_info(png_ptr, info_ptr); + /* choose a pixel format */ + color_type = ppng_get_color_type(png_ptr, info_ptr); + bit_depth = ppng_get_bit_depth(png_ptr, info_ptr); + + /* PNGs with bit-depth greater than 8 are network byte order. Windows does not expect this. */ + if (bit_depth > 8) + ppng_set_swap(png_ptr); + + /* check for color-keyed alpha */ + transparency = ppng_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values); + if (!transparency) + num_trans = 0; + + if (transparency && (color_type == PNG_COLOR_TYPE_RGB || + (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16))) + { + /* expand to RGBA */ + if (color_type == PNG_COLOR_TYPE_GRAY) + ppng_set_gray_to_rgb(png_ptr); + ppng_set_tRNS_to_alpha(png_ptr); + color_type = PNG_COLOR_TYPE_RGB_ALPHA; + } + + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY_ALPHA: + /* WIC does not support grayscale alpha formats so use RGBA */ + ppng_set_gray_to_rgb(png_ptr); + /* fall through */ + case PNG_COLOR_TYPE_RGB_ALPHA: + This->decoder_frame.bpp = bit_depth * 4; + switch (bit_depth) + { + case 8: + ppng_set_bgr(png_ptr); + This->decoder_frame.pixel_format = GUID_WICPixelFormat32bppBGRA; + break; + case 16: This->decoder_frame.pixel_format = GUID_WICPixelFormat64bppRGBA; break; + default: + ERR("invalid RGBA bit depth: %i\n", bit_depth); + hr = E_FAIL; + goto end; + } + break; + case PNG_COLOR_TYPE_GRAY: + This->decoder_frame.bpp = bit_depth; + if (!transparency) + { + switch (bit_depth) + { + case 1: This->decoder_frame.pixel_format = GUID_WICPixelFormatBlackWhite; break; + case 2: This->decoder_frame.pixel_format = GUID_WICPixelFormat2bppGray; break; + case 4: This->decoder_frame.pixel_format = GUID_WICPixelFormat4bppGray; break; + case 8: This->decoder_frame.pixel_format = GUID_WICPixelFormat8bppGray; break; + case 16: This->decoder_frame.pixel_format = GUID_WICPixelFormat16bppGray; break; + default: + ERR("invalid grayscale bit depth: %i\n", bit_depth); + hr = E_FAIL; + goto end; + } + break; + } + /* else fall through */ + case PNG_COLOR_TYPE_PALETTE: + This->decoder_frame.bpp = bit_depth; + switch (bit_depth) + { + case 1: This->decoder_frame.pixel_format = GUID_WICPixelFormat1bppIndexed; break; + case 2: This->decoder_frame.pixel_format = GUID_WICPixelFormat2bppIndexed; break; + case 4: This->decoder_frame.pixel_format = GUID_WICPixelFormat4bppIndexed; break; + case 8: This->decoder_frame.pixel_format = GUID_WICPixelFormat8bppIndexed; break; + default: + ERR("invalid indexed color bit depth: %i\n", bit_depth); + hr = E_FAIL; + goto end; + } + break; + case PNG_COLOR_TYPE_RGB: + This->decoder_frame.bpp = bit_depth * 3; + switch (bit_depth) + { + case 8: + ppng_set_bgr(png_ptr); + This->decoder_frame.pixel_format = GUID_WICPixelFormat24bppBGR; + break; + case 16: This->decoder_frame.pixel_format = GUID_WICPixelFormat48bppRGB; break; + default: + ERR("invalid RGB color bit depth: %i\n", bit_depth); + hr = E_FAIL; + goto end; + } + break; + default: + ERR("invalid color type %i\n", color_type); + hr = E_FAIL; + goto end; + } + + This->decoder_frame.width = ppng_get_image_width(png_ptr, info_ptr); + This->decoder_frame.height = ppng_get_image_height(png_ptr, info_ptr); + + ret = ppng_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type); + + if (ret && unit_type == PNG_RESOLUTION_METER) + { + This->decoder_frame.dpix = xres * 0.0254; + This->decoder_frame.dpiy = yres * 0.0254; + } + else + { + WARN("no pHYs block present\n"); + This->decoder_frame.dpix = This->decoder_frame.dpiy = 96.0; + } + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + ret = ppng_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette); + if (!ret) + { + ERR("paletted image with no PLTE chunk\n"); + hr = E_FAIL; + goto end; + } + + if (num_palette > 256) + { + ERR("palette has %i colors?!\n", num_palette); + hr = E_FAIL; + goto end; + } + + This->decoder_frame.num_colors = num_palette; + for (i=0; idecoder_frame.palette[i] = (alpha << 24 | + png_palette[i].red << 16| + png_palette[i].green << 8| + png_palette[i].blue); + } + } + else if (color_type == PNG_COLOR_TYPE_GRAY && transparency && bit_depth <= 8) { + num_palette = 1 << bit_depth; + + This->decoder_frame.num_colors = num_palette; + for (i=0; idecoder_frame.palette[i] = (alpha << 24 | val << 16 | val << 8 | val); + } + } + else + { + This->decoder_frame.num_colors = 0; + } + st->flags = WICBitmapDecoderCapabilityCanDecodeAllImages | WICBitmapDecoderCapabilityCanDecodeSomeImages | WICBitmapDecoderCapabilityCanEnumerateMetadata; st->frame_count = 1; + hr = S_OK; end: @@ -204,6 +397,13 @@ end: return hr; } +HRESULT CDECL png_decoder_get_frame_info(struct decoder *iface, UINT frame, struct decoder_frame *info) +{ + struct png_decoder *This = impl_from_decoder(iface); + *info = This->decoder_frame; + return S_OK; +} + void CDECL png_decoder_destroy(struct decoder* iface) { struct png_decoder *This = impl_from_decoder(iface); @@ -213,6 +413,7 @@ void CDECL png_decoder_destroy(struct decoder* iface) static const struct decoder_funcs png_decoder_vtable = { png_decoder_initialize, + png_decoder_get_frame_info, png_decoder_destroy }; diff --git a/dlls/windowscodecs/pngformat.c b/dlls/windowscodecs/pngformat.c index 5a4d94ae4f3..d10afcedbc3 100644 --- a/dlls/windowscodecs/pngformat.c +++ b/dlls/windowscodecs/pngformat.c @@ -447,6 +447,7 @@ typedef struct { struct decoder *png_decoder; struct decoder_info decoder_info; struct decoder_stat file_info; + struct decoder_frame decoder_frame; png_structp png_ptr; png_infop info_ptr; png_infop end_info; @@ -605,6 +606,9 @@ static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *p if (FAILED(hr)) goto end; + /* this function cannot fail in PNG decoder */ + decoder_get_frame_info(This->png_decoder, 0, &This->decoder_frame); + /* initialize libpng */ This->png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!This->png_ptr) @@ -991,8 +995,8 @@ static HRESULT WINAPI PngDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface, UINT *puiWidth, UINT *puiHeight) { PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface); - *puiWidth = This->width; - *puiHeight = This->height; + *puiWidth = This->decoder_frame.width; + *puiHeight = This->decoder_frame.height; TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight); return S_OK; } @@ -1003,7 +1007,7 @@ static HRESULT WINAPI PngDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *ifa PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface); TRACE("(%p,%p)\n", iface, pPixelFormat); - memcpy(pPixelFormat, This->format, sizeof(GUID)); + memcpy(pPixelFormat, &This->decoder_frame.pixel_format, sizeof(GUID)); return S_OK; } @@ -1012,25 +1016,9 @@ static HRESULT WINAPI PngDecoder_Frame_GetResolution(IWICBitmapFrameDecode *ifac double *pDpiX, double *pDpiY) { PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface); - png_uint_32 ret, xres, yres; - int unit_type; - EnterCriticalSection(&This->lock); - - ret = ppng_get_pHYs(This->png_ptr, This->info_ptr, &xres, &yres, &unit_type); - - if (ret && unit_type == PNG_RESOLUTION_METER) - { - *pDpiX = xres * 0.0254; - *pDpiY = yres * 0.0254; - } - else - { - WARN("no pHYs block present\n"); - *pDpiX = *pDpiY = 96.0; - } - - LeaveCriticalSection(&This->lock); + *pDpiX = This->decoder_frame.dpix; + *pDpiY = This->decoder_frame.dpiy; TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY); @@ -1041,81 +1029,19 @@ static HRESULT WINAPI PngDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface, IWICPalette *pIPalette) { PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface); - png_uint_32 ret, color_type, bit_depth; - png_colorp png_palette; - int num_palette; - WICColor palette[256]; - png_bytep trans_alpha; - int num_trans; - png_color_16p trans_values; - int i; HRESULT hr=S_OK; TRACE("(%p,%p)\n", iface, pIPalette); - EnterCriticalSection(&This->lock); - - color_type = ppng_get_color_type(This->png_ptr, This->info_ptr); - bit_depth = ppng_get_bit_depth(This->png_ptr, This->info_ptr); - - if (color_type == PNG_COLOR_TYPE_PALETTE) + if (This->decoder_frame.num_colors) { - ret = ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette); - if (!ret) - { - hr = WINCODEC_ERR_PALETTEUNAVAILABLE; - goto end; - } - - if (num_palette > 256) - { - ERR("palette has %i colors?!\n", num_palette); - hr = E_FAIL; - goto end; - } - - ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values); - if (!ret) num_trans = 0; - - for (i=0; ipng_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values); - - if (!ret) - { - hr = WINCODEC_ERR_PALETTEUNAVAILABLE; - goto end; - } - - num_palette = 1 << bit_depth; - - for (i=0; idecoder_frame.palette, This->decoder_frame.num_colors); } else { hr = WINCODEC_ERR_PALETTEUNAVAILABLE; } -end: - - LeaveCriticalSection(&This->lock); - - if (SUCCEEDED(hr)) - hr = IWICPalette_InitializeCustom(pIPalette, palette, num_palette); - return hr; } diff --git a/dlls/windowscodecs/unix_iface.c b/dlls/windowscodecs/unix_iface.c index 811e45f692e..6f9cf894f79 100644 --- a/dlls/windowscodecs/unix_iface.c +++ b/dlls/windowscodecs/unix_iface.c @@ -72,6 +72,12 @@ HRESULT CDECL decoder_wrapper_initialize(struct decoder* iface, IStream* stream, return unix_funcs->decoder_initialize(This->unix_decoder, stream, st); } +HRESULT CDECL decoder_wrapper_get_frame_info(struct decoder* iface, UINT frame, struct decoder_frame *info) +{ + struct decoder_wrapper* This = impl_from_decoder(iface); + return unix_funcs->decoder_get_frame_info(This->unix_decoder, frame, info); +} + void CDECL decoder_wrapper_destroy(struct decoder* iface) { struct decoder_wrapper* This = impl_from_decoder(iface); @@ -81,6 +87,7 @@ void CDECL decoder_wrapper_destroy(struct decoder* iface) static const struct decoder_funcs decoder_wrapper_vtable = { decoder_wrapper_initialize, + decoder_wrapper_get_frame_info, decoder_wrapper_destroy }; diff --git a/dlls/windowscodecs/unix_lib.c b/dlls/windowscodecs/unix_lib.c index 5a7477bdeb1..0086e0d5326 100644 --- a/dlls/windowscodecs/unix_lib.c +++ b/dlls/windowscodecs/unix_lib.c @@ -66,6 +66,7 @@ HRESULT CDECL decoder_create(const CLSID *decoder_clsid, struct decoder_info *in static const struct unix_funcs unix_funcs = { decoder_create, decoder_initialize, + decoder_get_frame_info, decoder_destroy }; diff --git a/dlls/windowscodecs/wincodecs_common.h b/dlls/windowscodecs/wincodecs_common.h index dade447140d..e89e8867b1e 100644 --- a/dlls/windowscodecs/wincodecs_common.h +++ b/dlls/windowscodecs/wincodecs_common.h @@ -21,6 +21,11 @@ HRESULT CDECL decoder_initialize(struct decoder *decoder, IStream *stream, struc return decoder->vtable->initialize(decoder, stream, st); } +HRESULT CDECL decoder_get_frame_info(struct decoder *decoder, UINT frame, struct decoder_frame *info) +{ + return decoder->vtable->get_frame_info(decoder, frame, info); +} + void CDECL decoder_destroy(struct decoder *decoder) { decoder->vtable->destroy(decoder); diff --git a/dlls/windowscodecs/wincodecs_private.h b/dlls/windowscodecs/wincodecs_private.h index b82c671f88d..78ad977dc25 100644 --- a/dlls/windowscodecs/wincodecs_private.h +++ b/dlls/windowscodecs/wincodecs_private.h @@ -267,6 +267,16 @@ struct decoder_stat DWORD frame_count; }; +struct decoder_frame +{ + CLSID pixel_format; + UINT width, height; + UINT bpp; + double dpix, dpiy; + DWORD num_colors; + WICColor palette[256]; +}; + struct decoder { const struct decoder_funcs *vtable; @@ -275,6 +285,7 @@ struct decoder struct decoder_funcs { HRESULT (CDECL *initialize)(struct decoder* This, IStream *stream, struct decoder_stat *st); + HRESULT (CDECL *get_frame_info)(struct decoder* This, UINT frame, struct decoder_frame *info); void (CDECL *destroy)(struct decoder* This); }; @@ -289,6 +300,7 @@ struct win32_funcs HRESULT CDECL decoder_create(const CLSID *decoder_clsid, struct decoder_info *info, struct decoder **result); HRESULT CDECL decoder_initialize(struct decoder *This, IStream *stream, struct decoder_stat *st); +HRESULT CDECL decoder_get_frame_info(struct decoder* This, UINT frame, struct decoder_frame *info); void CDECL decoder_destroy(struct decoder *This); HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **result); @@ -297,6 +309,7 @@ struct unix_funcs { HRESULT (CDECL *decoder_create)(const CLSID *decoder_clsid, struct decoder_info *info, struct decoder **result); HRESULT (CDECL *decoder_initialize)(struct decoder *This, IStream *stream, struct decoder_stat *st); + HRESULT (CDECL *decoder_get_frame_info)(struct decoder* This, UINT frame, struct decoder_frame *info); void (CDECL *decoder_destroy)(struct decoder* This); };