windowscodecs: Use standard dlopen() instead of the libwine wrappers.
[wine/zf.git] / dlls / windowscodecs / pngformat.c
blob3cf47b4211f4b7f2f4312d4ff47d8456542cb799
1 /*
2 * Copyright 2009 Vincent Povirk for CodeWeavers
3 * Copyright 2016 Dmitry Timoshkov
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include "config.h"
21 #include "wine/port.h"
23 #include <stdarg.h>
25 #ifdef SONAME_LIBPNG
26 #include <png.h>
27 #endif
29 #define NONAMELESSUNION
30 #define COBJMACROS
32 #include "windef.h"
33 #include "winbase.h"
34 #include "objbase.h"
36 #include "wincodecs_private.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
42 static inline ULONG read_ulong_be(BYTE* data)
44 return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
47 static HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_size)
49 BYTE header[8];
50 HRESULT hr;
51 ULONG bytesread;
53 hr = IStream_Read(stream, header, 8, &bytesread);
54 if (FAILED(hr) || bytesread < 8)
56 if (SUCCEEDED(hr))
57 hr = E_FAIL;
58 return hr;
61 *data_size = read_ulong_be(&header[0]);
63 memcpy(type, &header[4], 4);
65 if (data)
67 *data = HeapAlloc(GetProcessHeap(), 0, *data_size);
68 if (!*data)
69 return E_OUTOFMEMORY;
71 hr = IStream_Read(stream, *data, *data_size, &bytesread);
73 if (FAILED(hr) || bytesread < *data_size)
75 if (SUCCEEDED(hr))
76 hr = E_FAIL;
77 HeapFree(GetProcessHeap(), 0, *data);
78 *data = NULL;
79 return hr;
82 /* Windows ignores CRC of the chunk */
85 return S_OK;
88 static HRESULT LoadTextMetadata(IStream *stream, const GUID *preferred_vendor,
89 DWORD persist_options, MetadataItem **items, DWORD *item_count)
91 HRESULT hr;
92 BYTE type[4];
93 BYTE *data;
94 ULONG data_size;
95 ULONG name_len, value_len;
96 BYTE *name_end_ptr;
97 LPSTR name, value;
98 MetadataItem *result;
100 hr = read_png_chunk(stream, type, &data, &data_size);
101 if (FAILED(hr)) return hr;
103 name_end_ptr = memchr(data, 0, data_size);
105 name_len = name_end_ptr - data;
107 if (!name_end_ptr || name_len > 79)
109 HeapFree(GetProcessHeap(), 0, data);
110 return E_FAIL;
113 value_len = data_size - name_len - 1;
115 result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem));
116 name = HeapAlloc(GetProcessHeap(), 0, name_len + 1);
117 value = HeapAlloc(GetProcessHeap(), 0, value_len + 1);
118 if (!result || !name || !value)
120 HeapFree(GetProcessHeap(), 0, data);
121 HeapFree(GetProcessHeap(), 0, result);
122 HeapFree(GetProcessHeap(), 0, name);
123 HeapFree(GetProcessHeap(), 0, value);
124 return E_OUTOFMEMORY;
127 PropVariantInit(&result[0].schema);
128 PropVariantInit(&result[0].id);
129 PropVariantInit(&result[0].value);
131 memcpy(name, data, name_len + 1);
132 memcpy(value, name_end_ptr + 1, value_len);
133 value[value_len] = 0;
135 result[0].id.vt = VT_LPSTR;
136 result[0].id.u.pszVal = name;
137 result[0].value.vt = VT_LPSTR;
138 result[0].value.u.pszVal = value;
140 *items = result;
141 *item_count = 1;
143 HeapFree(GetProcessHeap(), 0, data);
145 return S_OK;
148 static const MetadataHandlerVtbl TextReader_Vtbl = {
150 &CLSID_WICPngTextMetadataReader,
151 LoadTextMetadata
154 HRESULT PngTextReader_CreateInstance(REFIID iid, void** ppv)
156 return MetadataReader_Create(&TextReader_Vtbl, iid, ppv);
159 static HRESULT LoadGamaMetadata(IStream *stream, const GUID *preferred_vendor,
160 DWORD persist_options, MetadataItem **items, DWORD *item_count)
162 HRESULT hr;
163 BYTE type[4];
164 BYTE *data;
165 ULONG data_size;
166 ULONG gamma;
167 static const WCHAR ImageGamma[] = {'I','m','a','g','e','G','a','m','m','a',0};
168 LPWSTR name;
169 MetadataItem *result;
171 hr = read_png_chunk(stream, type, &data, &data_size);
172 if (FAILED(hr)) return hr;
174 if (data_size < 4)
176 HeapFree(GetProcessHeap(), 0, data);
177 return E_FAIL;
180 gamma = read_ulong_be(data);
182 HeapFree(GetProcessHeap(), 0, data);
184 result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem));
185 name = HeapAlloc(GetProcessHeap(), 0, sizeof(ImageGamma));
186 if (!result || !name)
188 HeapFree(GetProcessHeap(), 0, result);
189 HeapFree(GetProcessHeap(), 0, name);
190 return E_OUTOFMEMORY;
193 PropVariantInit(&result[0].schema);
194 PropVariantInit(&result[0].id);
195 PropVariantInit(&result[0].value);
197 memcpy(name, ImageGamma, sizeof(ImageGamma));
199 result[0].id.vt = VT_LPWSTR;
200 result[0].id.u.pwszVal = name;
201 result[0].value.vt = VT_UI4;
202 result[0].value.u.ulVal = gamma;
204 *items = result;
205 *item_count = 1;
207 return S_OK;
210 static const MetadataHandlerVtbl GamaReader_Vtbl = {
212 &CLSID_WICPngGamaMetadataReader,
213 LoadGamaMetadata
216 HRESULT PngGamaReader_CreateInstance(REFIID iid, void** ppv)
218 return MetadataReader_Create(&GamaReader_Vtbl, iid, ppv);
221 static HRESULT LoadChrmMetadata(IStream *stream, const GUID *preferred_vendor,
222 DWORD persist_options, MetadataItem **items, DWORD *item_count)
224 HRESULT hr;
225 BYTE type[4];
226 BYTE *data;
227 ULONG data_size;
228 static const WCHAR names[8][12] = {
229 {'W','h','i','t','e','P','o','i','n','t','X',0},
230 {'W','h','i','t','e','P','o','i','n','t','Y',0},
231 {'R','e','d','X',0},
232 {'R','e','d','Y',0},
233 {'G','r','e','e','n','X',0},
234 {'G','r','e','e','n','Y',0},
235 {'B','l','u','e','X',0},
236 {'B','l','u','e','Y',0},
238 LPWSTR dyn_names[8] = {0};
239 MetadataItem *result;
240 int i;
242 hr = read_png_chunk(stream, type, &data, &data_size);
243 if (FAILED(hr)) return hr;
245 if (data_size < 32)
247 HeapFree(GetProcessHeap(), 0, data);
248 return E_FAIL;
251 result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem)*8);
252 for (i=0; i<8; i++)
254 dyn_names[i] = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(lstrlenW(names[i])+1));
255 if (!dyn_names[i]) break;
257 if (!result || i < 8)
259 HeapFree(GetProcessHeap(), 0, result);
260 for (i=0; i<8; i++)
261 HeapFree(GetProcessHeap(), 0, dyn_names[i]);
262 HeapFree(GetProcessHeap(), 0, data);
263 return E_OUTOFMEMORY;
266 for (i=0; i<8; i++)
268 PropVariantInit(&result[i].schema);
270 PropVariantInit(&result[i].id);
271 result[i].id.vt = VT_LPWSTR;
272 result[i].id.u.pwszVal = dyn_names[i];
273 lstrcpyW(dyn_names[i], names[i]);
275 PropVariantInit(&result[i].value);
276 result[i].value.vt = VT_UI4;
277 result[i].value.u.ulVal = read_ulong_be(&data[i*4]);
280 *items = result;
281 *item_count = 8;
283 HeapFree(GetProcessHeap(), 0, data);
285 return S_OK;
288 static const MetadataHandlerVtbl ChrmReader_Vtbl = {
290 &CLSID_WICPngChrmMetadataReader,
291 LoadChrmMetadata
294 HRESULT PngChrmReader_CreateInstance(REFIID iid, void** ppv)
296 return MetadataReader_Create(&ChrmReader_Vtbl, iid, ppv);
299 #ifdef SONAME_LIBPNG
301 static void *libpng_handle;
302 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
303 MAKE_FUNCPTR(png_create_read_struct);
304 MAKE_FUNCPTR(png_create_info_struct);
305 MAKE_FUNCPTR(png_create_write_struct);
306 MAKE_FUNCPTR(png_destroy_read_struct);
307 MAKE_FUNCPTR(png_destroy_write_struct);
308 MAKE_FUNCPTR(png_error);
309 MAKE_FUNCPTR(png_get_bit_depth);
310 MAKE_FUNCPTR(png_get_color_type);
311 MAKE_FUNCPTR(png_get_error_ptr);
312 MAKE_FUNCPTR(png_get_iCCP);
313 MAKE_FUNCPTR(png_get_image_height);
314 MAKE_FUNCPTR(png_get_image_width);
315 MAKE_FUNCPTR(png_get_io_ptr);
316 MAKE_FUNCPTR(png_get_pHYs);
317 MAKE_FUNCPTR(png_get_PLTE);
318 MAKE_FUNCPTR(png_get_tRNS);
319 MAKE_FUNCPTR(png_set_bgr);
320 MAKE_FUNCPTR(png_set_crc_action);
321 MAKE_FUNCPTR(png_set_error_fn);
322 MAKE_FUNCPTR(png_set_filler);
323 MAKE_FUNCPTR(png_set_filter);
324 MAKE_FUNCPTR(png_set_gray_to_rgb);
325 MAKE_FUNCPTR(png_set_interlace_handling);
326 MAKE_FUNCPTR(png_set_IHDR);
327 MAKE_FUNCPTR(png_set_pHYs);
328 MAKE_FUNCPTR(png_set_PLTE);
329 MAKE_FUNCPTR(png_set_read_fn);
330 MAKE_FUNCPTR(png_set_strip_16);
331 MAKE_FUNCPTR(png_set_tRNS);
332 MAKE_FUNCPTR(png_set_tRNS_to_alpha);
333 MAKE_FUNCPTR(png_set_write_fn);
334 MAKE_FUNCPTR(png_set_swap);
335 MAKE_FUNCPTR(png_read_end);
336 MAKE_FUNCPTR(png_read_image);
337 MAKE_FUNCPTR(png_read_info);
338 MAKE_FUNCPTR(png_write_end);
339 MAKE_FUNCPTR(png_write_info);
340 MAKE_FUNCPTR(png_write_rows);
341 #undef MAKE_FUNCPTR
343 static CRITICAL_SECTION init_png_cs;
344 static CRITICAL_SECTION_DEBUG init_png_cs_debug =
346 0, 0, &init_png_cs,
347 { &init_png_cs_debug.ProcessLocksList,
348 &init_png_cs_debug.ProcessLocksList },
349 0, 0, { (DWORD_PTR)(__FILE__ ": init_png_cs") }
351 static CRITICAL_SECTION init_png_cs = { &init_png_cs_debug, -1, 0, 0, 0, 0 };
353 static const WCHAR wszPngInterlaceOption[] = {'I','n','t','e','r','l','a','c','e','O','p','t','i','o','n',0};
354 static const WCHAR wszPngFilterOption[] = {'F','i','l','t','e','r','O','p','t','i','o','n',0};
356 static void *load_libpng(void)
358 void *result;
360 EnterCriticalSection(&init_png_cs);
362 if(!libpng_handle && (libpng_handle = dlopen(SONAME_LIBPNG, RTLD_NOW)) != NULL) {
364 #define LOAD_FUNCPTR(f) \
365 if((p##f = dlsym(libpng_handle, #f)) == NULL) { \
366 libpng_handle = NULL; \
367 LeaveCriticalSection(&init_png_cs); \
368 return NULL; \
370 LOAD_FUNCPTR(png_create_read_struct);
371 LOAD_FUNCPTR(png_create_info_struct);
372 LOAD_FUNCPTR(png_create_write_struct);
373 LOAD_FUNCPTR(png_destroy_read_struct);
374 LOAD_FUNCPTR(png_destroy_write_struct);
375 LOAD_FUNCPTR(png_error);
376 LOAD_FUNCPTR(png_get_bit_depth);
377 LOAD_FUNCPTR(png_get_color_type);
378 LOAD_FUNCPTR(png_get_error_ptr);
379 LOAD_FUNCPTR(png_get_iCCP);
380 LOAD_FUNCPTR(png_get_image_height);
381 LOAD_FUNCPTR(png_get_image_width);
382 LOAD_FUNCPTR(png_get_io_ptr);
383 LOAD_FUNCPTR(png_get_pHYs);
384 LOAD_FUNCPTR(png_get_PLTE);
385 LOAD_FUNCPTR(png_get_tRNS);
386 LOAD_FUNCPTR(png_set_bgr);
387 LOAD_FUNCPTR(png_set_crc_action);
388 LOAD_FUNCPTR(png_set_error_fn);
389 LOAD_FUNCPTR(png_set_filler);
390 LOAD_FUNCPTR(png_set_filter);
391 LOAD_FUNCPTR(png_set_gray_to_rgb);
392 LOAD_FUNCPTR(png_set_interlace_handling);
393 LOAD_FUNCPTR(png_set_IHDR);
394 LOAD_FUNCPTR(png_set_pHYs);
395 LOAD_FUNCPTR(png_set_PLTE);
396 LOAD_FUNCPTR(png_set_read_fn);
397 LOAD_FUNCPTR(png_set_strip_16);
398 LOAD_FUNCPTR(png_set_tRNS);
399 LOAD_FUNCPTR(png_set_tRNS_to_alpha);
400 LOAD_FUNCPTR(png_set_write_fn);
401 LOAD_FUNCPTR(png_set_swap);
402 LOAD_FUNCPTR(png_read_end);
403 LOAD_FUNCPTR(png_read_image);
404 LOAD_FUNCPTR(png_read_info);
405 LOAD_FUNCPTR(png_write_end);
406 LOAD_FUNCPTR(png_write_info);
407 LOAD_FUNCPTR(png_write_rows);
409 #undef LOAD_FUNCPTR
412 result = libpng_handle;
414 LeaveCriticalSection(&init_png_cs);
416 return result;
419 static void user_error_fn(png_structp png_ptr, png_const_charp error_message)
421 jmp_buf *pjmpbuf;
423 /* This uses setjmp/longjmp just like the default. We can't use the
424 * default because there's no way to access the jmp buffer in the png_struct
425 * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
426 WARN("PNG error: %s\n", debugstr_a(error_message));
427 pjmpbuf = ppng_get_error_ptr(png_ptr);
428 longjmp(*pjmpbuf, 1);
431 static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message)
433 WARN("PNG warning: %s\n", debugstr_a(warning_message));
436 typedef struct {
437 ULARGE_INTEGER ofs, len;
438 IWICMetadataReader* reader;
439 } metadata_block_info;
441 typedef struct {
442 IWICBitmapDecoder IWICBitmapDecoder_iface;
443 IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
444 IWICMetadataBlockReader IWICMetadataBlockReader_iface;
445 LONG ref;
446 IStream *stream;
447 png_structp png_ptr;
448 png_infop info_ptr;
449 png_infop end_info;
450 BOOL initialized;
451 int bpp;
452 int width, height;
453 UINT stride;
454 const WICPixelFormatGUID *format;
455 BYTE *image_bits;
456 CRITICAL_SECTION lock; /* must be held when png structures are accessed or initialized is set */
457 ULONG metadata_count;
458 metadata_block_info* metadata_blocks;
459 } PngDecoder;
461 static inline PngDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
463 return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapDecoder_iface);
466 static inline PngDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
468 return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapFrameDecode_iface);
471 static inline PngDecoder *impl_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface)
473 return CONTAINING_RECORD(iface, PngDecoder, IWICMetadataBlockReader_iface);
476 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl;
478 static HRESULT WINAPI PngDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
479 void **ppv)
481 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
482 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
484 if (!ppv) return E_INVALIDARG;
486 if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
488 *ppv = &This->IWICBitmapDecoder_iface;
490 else
492 *ppv = NULL;
493 return E_NOINTERFACE;
496 IUnknown_AddRef((IUnknown*)*ppv);
497 return S_OK;
500 static ULONG WINAPI PngDecoder_AddRef(IWICBitmapDecoder *iface)
502 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
503 ULONG ref = InterlockedIncrement(&This->ref);
505 TRACE("(%p) refcount=%u\n", iface, ref);
507 return ref;
510 static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface)
512 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
513 ULONG ref = InterlockedDecrement(&This->ref);
514 ULONG i;
516 TRACE("(%p) refcount=%u\n", iface, ref);
518 if (ref == 0)
520 if (This->stream)
521 IStream_Release(This->stream);
522 if (This->png_ptr)
523 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
524 This->lock.DebugInfo->Spare[0] = 0;
525 DeleteCriticalSection(&This->lock);
526 HeapFree(GetProcessHeap(), 0, This->image_bits);
527 for (i=0; i<This->metadata_count; i++)
529 if (This->metadata_blocks[i].reader)
530 IWICMetadataReader_Release(This->metadata_blocks[i].reader);
532 HeapFree(GetProcessHeap(), 0, This->metadata_blocks);
533 HeapFree(GetProcessHeap(), 0, This);
536 return ref;
539 static HRESULT WINAPI PngDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream,
540 DWORD *capability)
542 HRESULT hr;
544 TRACE("(%p,%p,%p)\n", iface, stream, capability);
546 if (!stream || !capability) return E_INVALIDARG;
548 hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand);
549 if (hr != S_OK) return hr;
551 *capability = WICBitmapDecoderCapabilityCanDecodeAllImages |
552 WICBitmapDecoderCapabilityCanDecodeSomeImages;
553 /* FIXME: WICBitmapDecoderCapabilityCanEnumerateMetadata */
554 return S_OK;
557 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
559 IStream *stream = ppng_get_io_ptr(png_ptr);
560 HRESULT hr;
561 ULONG bytesread;
563 hr = IStream_Read(stream, data, length, &bytesread);
564 if (FAILED(hr) || bytesread != length)
566 ppng_error(png_ptr, "failed reading data");
570 static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
571 WICDecodeOptions cacheOptions)
573 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
574 LARGE_INTEGER seek;
575 HRESULT hr=S_OK;
576 png_bytep *row_pointers=NULL;
577 UINT image_size;
578 UINT i;
579 int color_type, bit_depth;
580 png_bytep trans;
581 int num_trans;
582 png_uint_32 transparency;
583 png_color_16p trans_values;
584 jmp_buf jmpbuf;
585 BYTE chunk_type[4];
586 ULONG chunk_size;
587 ULARGE_INTEGER chunk_start;
588 ULONG metadata_blocks_size = 0;
590 TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
592 EnterCriticalSection(&This->lock);
594 /* initialize libpng */
595 This->png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
596 if (!This->png_ptr)
598 hr = E_FAIL;
599 goto end;
602 This->info_ptr = ppng_create_info_struct(This->png_ptr);
603 if (!This->info_ptr)
605 ppng_destroy_read_struct(&This->png_ptr, NULL, NULL);
606 This->png_ptr = NULL;
607 hr = E_FAIL;
608 goto end;
611 This->end_info = ppng_create_info_struct(This->png_ptr);
612 if (!This->end_info)
614 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, NULL);
615 This->png_ptr = NULL;
616 hr = E_FAIL;
617 goto end;
620 /* set up setjmp/longjmp error handling */
621 if (setjmp(jmpbuf))
623 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
624 This->png_ptr = NULL;
625 hr = WINCODEC_ERR_UNKNOWNIMAGEFORMAT;
626 goto end;
628 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
629 ppng_set_crc_action(This->png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
631 /* seek to the start of the stream */
632 seek.QuadPart = 0;
633 hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
634 if (FAILED(hr)) goto end;
636 /* set up custom i/o handling */
637 ppng_set_read_fn(This->png_ptr, pIStream, user_read_data);
639 /* read the header */
640 ppng_read_info(This->png_ptr, This->info_ptr);
642 /* choose a pixel format */
643 color_type = ppng_get_color_type(This->png_ptr, This->info_ptr);
644 bit_depth = ppng_get_bit_depth(This->png_ptr, This->info_ptr);
646 /* PNGs with bit-depth greater than 8 are network byte order. Windows does not expect this. */
647 if (bit_depth > 8)
648 ppng_set_swap(This->png_ptr);
650 /* check for color-keyed alpha */
651 transparency = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans, &num_trans, &trans_values);
653 if (transparency && (color_type == PNG_COLOR_TYPE_RGB ||
654 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16)))
656 /* expand to RGBA */
657 if (color_type == PNG_COLOR_TYPE_GRAY)
658 ppng_set_gray_to_rgb(This->png_ptr);
659 ppng_set_tRNS_to_alpha(This->png_ptr);
660 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
663 switch (color_type)
665 case PNG_COLOR_TYPE_GRAY_ALPHA:
666 /* WIC does not support grayscale alpha formats so use RGBA */
667 ppng_set_gray_to_rgb(This->png_ptr);
668 /* fall through */
669 case PNG_COLOR_TYPE_RGB_ALPHA:
670 This->bpp = bit_depth * 4;
671 switch (bit_depth)
673 case 8:
674 ppng_set_bgr(This->png_ptr);
675 This->format = &GUID_WICPixelFormat32bppBGRA;
676 break;
677 case 16: This->format = &GUID_WICPixelFormat64bppRGBA; break;
678 default:
679 ERR("invalid RGBA bit depth: %i\n", bit_depth);
680 hr = E_FAIL;
681 goto end;
683 break;
684 case PNG_COLOR_TYPE_GRAY:
685 This->bpp = bit_depth;
686 if (!transparency)
688 switch (bit_depth)
690 case 1: This->format = &GUID_WICPixelFormatBlackWhite; break;
691 case 2: This->format = &GUID_WICPixelFormat2bppGray; break;
692 case 4: This->format = &GUID_WICPixelFormat4bppGray; break;
693 case 8: This->format = &GUID_WICPixelFormat8bppGray; break;
694 case 16: This->format = &GUID_WICPixelFormat16bppGray; break;
695 default:
696 ERR("invalid grayscale bit depth: %i\n", bit_depth);
697 hr = E_FAIL;
698 goto end;
700 break;
702 /* else fall through */
703 case PNG_COLOR_TYPE_PALETTE:
704 This->bpp = bit_depth;
705 switch (bit_depth)
707 case 1: This->format = &GUID_WICPixelFormat1bppIndexed; break;
708 case 2: This->format = &GUID_WICPixelFormat2bppIndexed; break;
709 case 4: This->format = &GUID_WICPixelFormat4bppIndexed; break;
710 case 8: This->format = &GUID_WICPixelFormat8bppIndexed; break;
711 default:
712 ERR("invalid indexed color bit depth: %i\n", bit_depth);
713 hr = E_FAIL;
714 goto end;
716 break;
717 case PNG_COLOR_TYPE_RGB:
718 This->bpp = bit_depth * 3;
719 switch (bit_depth)
721 case 8:
722 ppng_set_bgr(This->png_ptr);
723 This->format = &GUID_WICPixelFormat24bppBGR;
724 break;
725 case 16: This->format = &GUID_WICPixelFormat48bppRGB; break;
726 default:
727 ERR("invalid RGB color bit depth: %i\n", bit_depth);
728 hr = E_FAIL;
729 goto end;
731 break;
732 default:
733 ERR("invalid color type %i\n", color_type);
734 hr = E_FAIL;
735 goto end;
738 /* read the image data */
739 This->width = ppng_get_image_width(This->png_ptr, This->info_ptr);
740 This->height = ppng_get_image_height(This->png_ptr, This->info_ptr);
741 This->stride = (This->width * This->bpp + 7) / 8;
742 image_size = This->stride * This->height;
744 This->image_bits = HeapAlloc(GetProcessHeap(), 0, image_size);
745 if (!This->image_bits)
747 hr = E_OUTOFMEMORY;
748 goto end;
751 row_pointers = HeapAlloc(GetProcessHeap(), 0, sizeof(png_bytep)*This->height);
752 if (!row_pointers)
754 hr = E_OUTOFMEMORY;
755 goto end;
758 for (i=0; i<This->height; i++)
759 row_pointers[i] = This->image_bits + i * This->stride;
761 ppng_read_image(This->png_ptr, row_pointers);
763 HeapFree(GetProcessHeap(), 0, row_pointers);
764 row_pointers = NULL;
766 ppng_read_end(This->png_ptr, This->end_info);
768 /* Find the metadata chunks in the file. */
769 seek.QuadPart = 8;
773 hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, &chunk_start);
774 if (FAILED(hr)) goto end;
776 hr = read_png_chunk(pIStream, chunk_type, NULL, &chunk_size);
777 if (FAILED(hr)) goto end;
779 if (chunk_type[0] >= 'a' && chunk_type[0] <= 'z' &&
780 memcmp(chunk_type, "tRNS", 4) && memcmp(chunk_type, "pHYs", 4))
782 /* This chunk is considered metadata. */
783 if (This->metadata_count == metadata_blocks_size)
785 metadata_block_info* new_metadata_blocks;
786 ULONG new_metadata_blocks_size;
788 new_metadata_blocks_size = 4 + metadata_blocks_size * 2;
789 new_metadata_blocks = HeapAlloc(GetProcessHeap(), 0,
790 new_metadata_blocks_size * sizeof(*new_metadata_blocks));
792 if (!new_metadata_blocks)
794 hr = E_OUTOFMEMORY;
795 goto end;
798 memcpy(new_metadata_blocks, This->metadata_blocks,
799 This->metadata_count * sizeof(*new_metadata_blocks));
801 HeapFree(GetProcessHeap(), 0, This->metadata_blocks);
802 This->metadata_blocks = new_metadata_blocks;
803 metadata_blocks_size = new_metadata_blocks_size;
806 This->metadata_blocks[This->metadata_count].ofs = chunk_start;
807 This->metadata_blocks[This->metadata_count].len.QuadPart = chunk_size + 12;
808 This->metadata_blocks[This->metadata_count].reader = NULL;
809 This->metadata_count++;
812 seek.QuadPart = chunk_start.QuadPart + chunk_size + 12; /* skip data and CRC */
813 } while (memcmp(chunk_type, "IEND", 4));
815 This->stream = pIStream;
816 IStream_AddRef(This->stream);
818 This->initialized = TRUE;
820 end:
821 LeaveCriticalSection(&This->lock);
823 HeapFree(GetProcessHeap(), 0, row_pointers);
825 return hr;
828 static HRESULT WINAPI PngDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
829 GUID *pguidContainerFormat)
831 memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
832 return S_OK;
835 static HRESULT WINAPI PngDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
836 IWICBitmapDecoderInfo **ppIDecoderInfo)
838 TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
840 return get_decoder_info(&CLSID_WICPngDecoder, ppIDecoderInfo);
843 static HRESULT WINAPI PngDecoder_CopyPalette(IWICBitmapDecoder *iface,
844 IWICPalette *palette)
846 TRACE("(%p,%p)\n", iface, palette);
847 return WINCODEC_ERR_PALETTEUNAVAILABLE;
850 static HRESULT WINAPI PngDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
851 IWICMetadataQueryReader **reader)
853 TRACE("(%p,%p)\n", iface, reader);
855 if (!reader) return E_INVALIDARG;
857 *reader = NULL;
858 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
861 static HRESULT WINAPI PngDecoder_GetPreview(IWICBitmapDecoder *iface,
862 IWICBitmapSource **ppIBitmapSource)
864 TRACE("(%p,%p)\n", iface, ppIBitmapSource);
866 if (!ppIBitmapSource) return E_INVALIDARG;
868 *ppIBitmapSource = NULL;
869 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
872 static HRESULT WINAPI PngDecoder_GetColorContexts(IWICBitmapDecoder *iface,
873 UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
875 TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
876 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
879 static HRESULT WINAPI PngDecoder_GetThumbnail(IWICBitmapDecoder *iface,
880 IWICBitmapSource **ppIThumbnail)
882 TRACE("(%p,%p)\n", iface, ppIThumbnail);
884 if (!ppIThumbnail) return E_INVALIDARG;
886 *ppIThumbnail = NULL;
887 return WINCODEC_ERR_CODECNOTHUMBNAIL;
890 static HRESULT WINAPI PngDecoder_GetFrameCount(IWICBitmapDecoder *iface,
891 UINT *pCount)
893 if (!pCount) return E_INVALIDARG;
895 *pCount = 1;
896 return S_OK;
899 static HRESULT WINAPI PngDecoder_GetFrame(IWICBitmapDecoder *iface,
900 UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
902 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
903 TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
905 if (!This->initialized) return WINCODEC_ERR_FRAMEMISSING;
907 if (index != 0) return E_INVALIDARG;
909 IWICBitmapDecoder_AddRef(iface);
911 *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface;
913 return S_OK;
916 static const IWICBitmapDecoderVtbl PngDecoder_Vtbl = {
917 PngDecoder_QueryInterface,
918 PngDecoder_AddRef,
919 PngDecoder_Release,
920 PngDecoder_QueryCapability,
921 PngDecoder_Initialize,
922 PngDecoder_GetContainerFormat,
923 PngDecoder_GetDecoderInfo,
924 PngDecoder_CopyPalette,
925 PngDecoder_GetMetadataQueryReader,
926 PngDecoder_GetPreview,
927 PngDecoder_GetColorContexts,
928 PngDecoder_GetThumbnail,
929 PngDecoder_GetFrameCount,
930 PngDecoder_GetFrame
933 static HRESULT WINAPI PngDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
934 void **ppv)
936 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
937 if (!ppv) return E_INVALIDARG;
939 if (IsEqualIID(&IID_IUnknown, iid) ||
940 IsEqualIID(&IID_IWICBitmapSource, iid) ||
941 IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
943 *ppv = &This->IWICBitmapFrameDecode_iface;
945 else if (IsEqualIID(&IID_IWICMetadataBlockReader, iid))
947 *ppv = &This->IWICMetadataBlockReader_iface;
949 else
951 *ppv = NULL;
952 return E_NOINTERFACE;
955 IUnknown_AddRef((IUnknown*)*ppv);
956 return S_OK;
959 static ULONG WINAPI PngDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface)
961 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
962 return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
965 static ULONG WINAPI PngDecoder_Frame_Release(IWICBitmapFrameDecode *iface)
967 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
968 return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
971 static HRESULT WINAPI PngDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface,
972 UINT *puiWidth, UINT *puiHeight)
974 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
975 *puiWidth = This->width;
976 *puiHeight = This->height;
977 TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight);
978 return S_OK;
981 static HRESULT WINAPI PngDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface,
982 WICPixelFormatGUID *pPixelFormat)
984 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
985 TRACE("(%p,%p)\n", iface, pPixelFormat);
987 memcpy(pPixelFormat, This->format, sizeof(GUID));
989 return S_OK;
992 static HRESULT WINAPI PngDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface,
993 double *pDpiX, double *pDpiY)
995 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
996 png_uint_32 ret, xres, yres;
997 int unit_type;
999 EnterCriticalSection(&This->lock);
1001 ret = ppng_get_pHYs(This->png_ptr, This->info_ptr, &xres, &yres, &unit_type);
1003 if (ret && unit_type == PNG_RESOLUTION_METER)
1005 *pDpiX = xres * 0.0254;
1006 *pDpiY = yres * 0.0254;
1008 else
1010 WARN("no pHYs block present\n");
1011 *pDpiX = *pDpiY = 96.0;
1014 LeaveCriticalSection(&This->lock);
1016 TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY);
1018 return S_OK;
1021 static HRESULT WINAPI PngDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface,
1022 IWICPalette *pIPalette)
1024 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1025 png_uint_32 ret, color_type, bit_depth;
1026 png_colorp png_palette;
1027 int num_palette;
1028 WICColor palette[256];
1029 png_bytep trans_alpha;
1030 int num_trans;
1031 png_color_16p trans_values;
1032 int i;
1033 HRESULT hr=S_OK;
1035 TRACE("(%p,%p)\n", iface, pIPalette);
1037 EnterCriticalSection(&This->lock);
1039 color_type = ppng_get_color_type(This->png_ptr, This->info_ptr);
1040 bit_depth = ppng_get_bit_depth(This->png_ptr, This->info_ptr);
1042 if (color_type == PNG_COLOR_TYPE_PALETTE)
1044 ret = ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette);
1045 if (!ret)
1047 hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
1048 goto end;
1051 if (num_palette > 256)
1053 ERR("palette has %i colors?!\n", num_palette);
1054 hr = E_FAIL;
1055 goto end;
1058 ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values);
1059 if (!ret) num_trans = 0;
1061 for (i=0; i<num_palette; i++)
1063 BYTE alpha = (i < num_trans) ? trans_alpha[i] : 0xff;
1064 palette[i] = (alpha << 24 |
1065 png_palette[i].red << 16|
1066 png_palette[i].green << 8|
1067 png_palette[i].blue);
1070 else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth <= 8) {
1071 ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values);
1073 if (!ret)
1075 hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
1076 goto end;
1079 num_palette = 1 << bit_depth;
1081 for (i=0; i<num_palette; i++)
1083 BYTE alpha = (i == trans_values[0].gray) ? 0 : 0xff;
1084 BYTE val = i * 255 / (num_palette - 1);
1085 palette[i] = (alpha << 24 | val << 16 | val << 8 | val);
1088 else
1090 hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
1093 end:
1095 LeaveCriticalSection(&This->lock);
1097 if (SUCCEEDED(hr))
1098 hr = IWICPalette_InitializeCustom(pIPalette, palette, num_palette);
1100 return hr;
1103 static HRESULT WINAPI PngDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface,
1104 const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
1106 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1107 TRACE("(%p,%s,%u,%u,%p)\n", iface, debug_wic_rect(prc), cbStride, cbBufferSize, pbBuffer);
1109 return copy_pixels(This->bpp, This->image_bits,
1110 This->width, This->height, This->stride,
1111 prc, cbStride, cbBufferSize, pbBuffer);
1114 static HRESULT WINAPI PngDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
1115 IWICMetadataQueryReader **ppIMetadataQueryReader)
1117 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1119 TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
1121 if (!ppIMetadataQueryReader)
1122 return E_INVALIDARG;
1124 return MetadataQueryReader_CreateInstance(&This->IWICMetadataBlockReader_iface, NULL, ppIMetadataQueryReader);
1127 static HRESULT WINAPI PngDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface,
1128 UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
1130 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1131 png_charp name;
1132 BYTE *profile;
1133 png_uint_32 len;
1134 int compression_type;
1135 HRESULT hr;
1137 TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
1139 if (!pcActualCount) return E_INVALIDARG;
1141 EnterCriticalSection(&This->lock);
1143 if (ppng_get_iCCP(This->png_ptr, This->info_ptr, &name, &compression_type, (void *)&profile, &len))
1145 if (cCount && ppIColorContexts)
1147 hr = IWICColorContext_InitializeFromMemory(*ppIColorContexts, profile, len);
1148 if (FAILED(hr))
1150 LeaveCriticalSection(&This->lock);
1151 return hr;
1154 *pcActualCount = 1;
1156 else
1157 *pcActualCount = 0;
1159 LeaveCriticalSection(&This->lock);
1161 return S_OK;
1164 static HRESULT WINAPI PngDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
1165 IWICBitmapSource **ppIThumbnail)
1167 TRACE("(%p,%p)\n", iface, ppIThumbnail);
1169 if (!ppIThumbnail) return E_INVALIDARG;
1171 *ppIThumbnail = NULL;
1172 return WINCODEC_ERR_CODECNOTHUMBNAIL;
1175 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl = {
1176 PngDecoder_Frame_QueryInterface,
1177 PngDecoder_Frame_AddRef,
1178 PngDecoder_Frame_Release,
1179 PngDecoder_Frame_GetSize,
1180 PngDecoder_Frame_GetPixelFormat,
1181 PngDecoder_Frame_GetResolution,
1182 PngDecoder_Frame_CopyPalette,
1183 PngDecoder_Frame_CopyPixels,
1184 PngDecoder_Frame_GetMetadataQueryReader,
1185 PngDecoder_Frame_GetColorContexts,
1186 PngDecoder_Frame_GetThumbnail
1189 static HRESULT WINAPI PngDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid,
1190 void **ppv)
1192 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1193 return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv);
1196 static ULONG WINAPI PngDecoder_Block_AddRef(IWICMetadataBlockReader *iface)
1198 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1199 return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
1202 static ULONG WINAPI PngDecoder_Block_Release(IWICMetadataBlockReader *iface)
1204 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1205 return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1208 static HRESULT WINAPI PngDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
1209 GUID *pguidContainerFormat)
1211 if (!pguidContainerFormat) return E_INVALIDARG;
1212 memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
1213 return S_OK;
1216 static HRESULT WINAPI PngDecoder_Block_GetCount(IWICMetadataBlockReader *iface,
1217 UINT *pcCount)
1219 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1221 TRACE("%p,%p\n", iface, pcCount);
1223 if (!pcCount) return E_INVALIDARG;
1225 *pcCount = This->metadata_count;
1227 return S_OK;
1230 static HRESULT WINAPI PngDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
1231 UINT nIndex, IWICMetadataReader **ppIMetadataReader)
1233 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1234 HRESULT hr;
1235 IWICComponentFactory* factory;
1236 IWICStream* stream;
1238 TRACE("%p,%d,%p\n", iface, nIndex, ppIMetadataReader);
1240 if (nIndex >= This->metadata_count || !ppIMetadataReader)
1241 return E_INVALIDARG;
1243 if (!This->metadata_blocks[nIndex].reader)
1245 hr = StreamImpl_Create(&stream);
1247 if (SUCCEEDED(hr))
1249 hr = IWICStream_InitializeFromIStreamRegion(stream, This->stream,
1250 This->metadata_blocks[nIndex].ofs, This->metadata_blocks[nIndex].len);
1252 if (SUCCEEDED(hr))
1253 hr = ImagingFactory_CreateInstance(&IID_IWICComponentFactory, (void**)&factory);
1255 if (SUCCEEDED(hr))
1257 hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory,
1258 &GUID_ContainerFormatPng, NULL, WICMetadataCreationAllowUnknown,
1259 (IStream*)stream, &This->metadata_blocks[nIndex].reader);
1261 IWICComponentFactory_Release(factory);
1264 IWICStream_Release(stream);
1267 if (FAILED(hr))
1269 *ppIMetadataReader = NULL;
1270 return hr;
1274 *ppIMetadataReader = This->metadata_blocks[nIndex].reader;
1275 IWICMetadataReader_AddRef(*ppIMetadataReader);
1277 return S_OK;
1280 static HRESULT WINAPI PngDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface,
1281 IEnumUnknown **ppIEnumMetadata)
1283 FIXME("%p,%p\n", iface, ppIEnumMetadata);
1284 return E_NOTIMPL;
1287 static const IWICMetadataBlockReaderVtbl PngDecoder_BlockVtbl = {
1288 PngDecoder_Block_QueryInterface,
1289 PngDecoder_Block_AddRef,
1290 PngDecoder_Block_Release,
1291 PngDecoder_Block_GetContainerFormat,
1292 PngDecoder_Block_GetCount,
1293 PngDecoder_Block_GetReaderByIndex,
1294 PngDecoder_Block_GetEnumerator,
1297 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
1299 PngDecoder *This;
1300 HRESULT ret;
1302 TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
1304 *ppv = NULL;
1306 if (!load_libpng())
1308 ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
1309 return E_FAIL;
1312 This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngDecoder));
1313 if (!This) return E_OUTOFMEMORY;
1315 This->IWICBitmapDecoder_iface.lpVtbl = &PngDecoder_Vtbl;
1316 This->IWICBitmapFrameDecode_iface.lpVtbl = &PngDecoder_FrameVtbl;
1317 This->IWICMetadataBlockReader_iface.lpVtbl = &PngDecoder_BlockVtbl;
1318 This->ref = 1;
1319 This->png_ptr = NULL;
1320 This->info_ptr = NULL;
1321 This->end_info = NULL;
1322 This->stream = NULL;
1323 This->initialized = FALSE;
1324 This->image_bits = NULL;
1325 InitializeCriticalSection(&This->lock);
1326 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngDecoder.lock");
1327 This->metadata_count = 0;
1328 This->metadata_blocks = NULL;
1330 ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
1331 IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1333 return ret;
1336 struct png_pixelformat {
1337 const WICPixelFormatGUID *guid;
1338 UINT bpp;
1339 int bit_depth;
1340 int color_type;
1341 BOOL remove_filler;
1342 BOOL swap_rgb;
1345 static const struct png_pixelformat formats[] = {
1346 {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1},
1347 {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1},
1348 {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0},
1349 {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0},
1350 {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0},
1351 {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0},
1352 {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0},
1353 {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1},
1354 {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0},
1355 {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0},
1356 {&GUID_WICPixelFormat1bppIndexed, 1, 1, PNG_COLOR_TYPE_PALETTE, 0, 0},
1357 {&GUID_WICPixelFormat2bppIndexed, 2, 2, PNG_COLOR_TYPE_PALETTE, 0, 0},
1358 {&GUID_WICPixelFormat4bppIndexed, 4, 4, PNG_COLOR_TYPE_PALETTE, 0, 0},
1359 {&GUID_WICPixelFormat8bppIndexed, 8, 8, PNG_COLOR_TYPE_PALETTE, 0, 0},
1360 {NULL},
1363 typedef struct PngEncoder {
1364 IWICBitmapEncoder IWICBitmapEncoder_iface;
1365 IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
1366 LONG ref;
1367 IStream *stream;
1368 png_structp png_ptr;
1369 png_infop info_ptr;
1370 UINT frame_count;
1371 BOOL frame_initialized;
1372 const struct png_pixelformat *format;
1373 BOOL info_written;
1374 UINT width, height;
1375 double xres, yres;
1376 UINT lines_written;
1377 BOOL frame_committed;
1378 BOOL committed;
1379 CRITICAL_SECTION lock;
1380 BOOL interlace;
1381 WICPngFilterOption filter;
1382 BYTE *data;
1383 UINT stride;
1384 UINT passes;
1385 WICColor palette[256];
1386 UINT colors;
1387 } PngEncoder;
1389 static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
1391 return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapEncoder_iface);
1394 static inline PngEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
1396 return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapFrameEncode_iface);
1399 static HRESULT WINAPI PngFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
1400 void **ppv)
1402 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1403 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1405 if (!ppv) return E_INVALIDARG;
1407 if (IsEqualIID(&IID_IUnknown, iid) ||
1408 IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
1410 *ppv = &This->IWICBitmapFrameEncode_iface;
1412 else
1414 *ppv = NULL;
1415 return E_NOINTERFACE;
1418 IUnknown_AddRef((IUnknown*)*ppv);
1419 return S_OK;
1422 static ULONG WINAPI PngFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
1424 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1425 return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface);
1428 static ULONG WINAPI PngFrameEncode_Release(IWICBitmapFrameEncode *iface)
1430 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1431 return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1434 static HRESULT WINAPI PngFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
1435 IPropertyBag2 *pIEncoderOptions)
1437 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1438 WICPngFilterOption filter;
1439 BOOL interlace;
1440 PROPBAG2 opts[2]= {{0}};
1441 VARIANT opt_values[2];
1442 HRESULT opt_hres[2];
1443 HRESULT hr;
1445 TRACE("(%p,%p)\n", iface, pIEncoderOptions);
1447 opts[0].pstrName = (LPOLESTR)wszPngInterlaceOption;
1448 opts[0].vt = VT_BOOL;
1449 opts[1].pstrName = (LPOLESTR)wszPngFilterOption;
1450 opts[1].vt = VT_UI1;
1452 if (pIEncoderOptions)
1454 hr = IPropertyBag2_Read(pIEncoderOptions, ARRAY_SIZE(opts), opts, NULL, opt_values, opt_hres);
1456 if (FAILED(hr))
1457 return hr;
1459 if (V_VT(&opt_values[0]) == VT_EMPTY)
1460 interlace = FALSE;
1461 else
1462 interlace = (V_BOOL(&opt_values[0]) != 0);
1464 filter = V_UI1(&opt_values[1]);
1465 if (filter > WICPngFilterAdaptive)
1467 WARN("Unrecognized filter option value %u.\n", filter);
1468 filter = WICPngFilterUnspecified;
1471 else
1473 interlace = FALSE;
1474 filter = WICPngFilterUnspecified;
1477 EnterCriticalSection(&This->lock);
1479 if (This->frame_initialized)
1481 LeaveCriticalSection(&This->lock);
1482 return WINCODEC_ERR_WRONGSTATE;
1485 This->interlace = interlace;
1486 This->filter = filter;
1488 This->frame_initialized = TRUE;
1490 LeaveCriticalSection(&This->lock);
1492 return S_OK;
1495 static HRESULT WINAPI PngFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
1496 UINT uiWidth, UINT uiHeight)
1498 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1499 TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
1501 EnterCriticalSection(&This->lock);
1503 if (!This->frame_initialized || This->info_written)
1505 LeaveCriticalSection(&This->lock);
1506 return WINCODEC_ERR_WRONGSTATE;
1509 This->width = uiWidth;
1510 This->height = uiHeight;
1512 LeaveCriticalSection(&This->lock);
1514 return S_OK;
1517 static HRESULT WINAPI PngFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
1518 double dpiX, double dpiY)
1520 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1521 TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
1523 EnterCriticalSection(&This->lock);
1525 if (!This->frame_initialized || This->info_written)
1527 LeaveCriticalSection(&This->lock);
1528 return WINCODEC_ERR_WRONGSTATE;
1531 This->xres = dpiX;
1532 This->yres = dpiY;
1534 LeaveCriticalSection(&This->lock);
1536 return S_OK;
1539 static HRESULT WINAPI PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
1540 WICPixelFormatGUID *pPixelFormat)
1542 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1543 int i;
1544 TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
1546 EnterCriticalSection(&This->lock);
1548 if (!This->frame_initialized || This->info_written)
1550 LeaveCriticalSection(&This->lock);
1551 return WINCODEC_ERR_WRONGSTATE;
1554 for (i=0; formats[i].guid; i++)
1556 if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
1557 break;
1560 if (!formats[i].guid) i = 0;
1562 This->format = &formats[i];
1563 memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
1565 LeaveCriticalSection(&This->lock);
1567 return S_OK;
1570 static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
1571 UINT cCount, IWICColorContext **ppIColorContext)
1573 FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1574 return E_NOTIMPL;
1577 static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
1578 IWICPalette *palette)
1580 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1581 HRESULT hr;
1583 TRACE("(%p,%p)\n", iface, palette);
1585 if (!palette) return E_INVALIDARG;
1587 EnterCriticalSection(&This->lock);
1589 if (This->frame_initialized)
1590 hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors);
1591 else
1592 hr = WINCODEC_ERR_NOTINITIALIZED;
1594 LeaveCriticalSection(&This->lock);
1595 return hr;
1598 static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
1599 IWICBitmapSource *pIThumbnail)
1601 FIXME("(%p,%p): stub\n", iface, pIThumbnail);
1602 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1605 static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
1606 UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
1608 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1609 png_byte **row_pointers=NULL;
1610 UINT i;
1611 jmp_buf jmpbuf;
1612 TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
1614 EnterCriticalSection(&This->lock);
1616 if (!This->frame_initialized || !This->width || !This->height || !This->format)
1618 LeaveCriticalSection(&This->lock);
1619 return WINCODEC_ERR_WRONGSTATE;
1622 if (lineCount == 0 || lineCount + This->lines_written > This->height)
1624 LeaveCriticalSection(&This->lock);
1625 return E_INVALIDARG;
1628 /* set up setjmp/longjmp error handling */
1629 if (setjmp(jmpbuf))
1631 LeaveCriticalSection(&This->lock);
1632 HeapFree(GetProcessHeap(), 0, row_pointers);
1633 return E_FAIL;
1635 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1637 if (!This->info_written)
1639 if (This->interlace)
1641 /* libpng requires us to write all data multiple times in this case. */
1642 This->stride = (This->format->bpp * This->width + 7)/8;
1643 This->data = HeapAlloc(GetProcessHeap(), 0, This->height * This->stride);
1644 if (!This->data)
1646 LeaveCriticalSection(&This->lock);
1647 return E_OUTOFMEMORY;
1651 /* Tell PNG we need to byte swap if writing a >8-bpp image */
1652 if (This->format->bit_depth > 8)
1653 ppng_set_swap(This->png_ptr);
1655 ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height,
1656 This->format->bit_depth, This->format->color_type,
1657 This->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
1658 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1660 if (This->xres != 0.0 && This->yres != 0.0)
1662 ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254,
1663 (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER);
1666 if (This->format->color_type == PNG_COLOR_TYPE_PALETTE && This->colors)
1668 png_color png_palette[256];
1669 png_byte trans[256];
1670 UINT i, num_trans = 0, colors;
1672 /* Newer libpng versions don't accept larger palettes than the declared
1673 * bit depth, so we need to generate the palette of the correct length.
1675 colors = min(This->colors, 1 << This->format->bit_depth);
1677 for (i = 0; i < colors; i++)
1679 png_palette[i].red = (This->palette[i] >> 16) & 0xff;
1680 png_palette[i].green = (This->palette[i] >> 8) & 0xff;
1681 png_palette[i].blue = This->palette[i] & 0xff;
1682 trans[i] = (This->palette[i] >> 24) & 0xff;
1683 if (trans[i] != 0xff)
1684 num_trans = i+1;
1687 ppng_set_PLTE(This->png_ptr, This->info_ptr, png_palette, colors);
1689 if (num_trans)
1690 ppng_set_tRNS(This->png_ptr, This->info_ptr, trans, num_trans, NULL);
1693 ppng_write_info(This->png_ptr, This->info_ptr);
1695 if (This->format->remove_filler)
1696 ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER);
1698 if (This->format->swap_rgb)
1699 ppng_set_bgr(This->png_ptr);
1701 if (This->interlace)
1702 This->passes = ppng_set_interlace_handling(This->png_ptr);
1704 if (This->filter != WICPngFilterUnspecified)
1706 static const int png_filter_map[] =
1708 /* WICPngFilterUnspecified */ PNG_NO_FILTERS,
1709 /* WICPngFilterNone */ PNG_FILTER_NONE,
1710 /* WICPngFilterSub */ PNG_FILTER_SUB,
1711 /* WICPngFilterUp */ PNG_FILTER_UP,
1712 /* WICPngFilterAverage */ PNG_FILTER_AVG,
1713 /* WICPngFilterPaeth */ PNG_FILTER_PAETH,
1714 /* WICPngFilterAdaptive */ PNG_ALL_FILTERS,
1717 ppng_set_filter(This->png_ptr, 0, png_filter_map[This->filter]);
1720 This->info_written = TRUE;
1723 if (This->interlace)
1725 /* Just store the data so we can write it in multiple passes in Commit. */
1726 for (i=0; i<lineCount; i++)
1727 memcpy(This->data + This->stride * (This->lines_written + i),
1728 pbPixels + cbStride * i,
1729 This->stride);
1731 This->lines_written += lineCount;
1733 LeaveCriticalSection(&This->lock);
1734 return S_OK;
1737 row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*));
1738 if (!row_pointers)
1740 LeaveCriticalSection(&This->lock);
1741 return E_OUTOFMEMORY;
1744 for (i=0; i<lineCount; i++)
1745 row_pointers[i] = pbPixels + cbStride * i;
1747 ppng_write_rows(This->png_ptr, row_pointers, lineCount);
1748 This->lines_written += lineCount;
1750 LeaveCriticalSection(&This->lock);
1752 HeapFree(GetProcessHeap(), 0, row_pointers);
1754 return S_OK;
1757 static HRESULT WINAPI PngFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
1758 IWICBitmapSource *pIBitmapSource, WICRect *prc)
1760 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1761 HRESULT hr;
1762 TRACE("(%p,%p,%s)\n", iface, pIBitmapSource, debug_wic_rect(prc));
1764 if (!This->frame_initialized)
1765 return WINCODEC_ERR_WRONGSTATE;
1767 hr = configure_write_source(iface, pIBitmapSource, prc,
1768 This->format ? This->format->guid : NULL, This->width, This->height,
1769 This->xres, This->yres);
1771 if (SUCCEEDED(hr))
1773 hr = write_source(iface, pIBitmapSource, prc,
1774 This->format->guid, This->format->bpp,
1775 !This->colors && This->format->color_type == PNG_COLOR_TYPE_PALETTE,
1776 This->width, This->height);
1779 return hr;
1782 static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface)
1784 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1785 png_byte **row_pointers=NULL;
1786 jmp_buf jmpbuf;
1787 TRACE("(%p)\n", iface);
1789 EnterCriticalSection(&This->lock);
1791 if (!This->info_written || This->lines_written != This->height || This->frame_committed)
1793 LeaveCriticalSection(&This->lock);
1794 return WINCODEC_ERR_WRONGSTATE;
1797 /* set up setjmp/longjmp error handling */
1798 if (setjmp(jmpbuf))
1800 LeaveCriticalSection(&This->lock);
1801 HeapFree(GetProcessHeap(), 0, row_pointers);
1802 return E_FAIL;
1804 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1806 if (This->interlace)
1808 int i;
1810 row_pointers = HeapAlloc(GetProcessHeap(), 0, This->height * sizeof(png_byte*));
1811 if (!row_pointers)
1813 LeaveCriticalSection(&This->lock);
1814 return E_OUTOFMEMORY;
1817 for (i=0; i<This->height; i++)
1818 row_pointers[i] = This->data + This->stride * i;
1820 for (i=0; i<This->passes; i++)
1821 ppng_write_rows(This->png_ptr, row_pointers, This->height);
1824 ppng_write_end(This->png_ptr, This->info_ptr);
1826 This->frame_committed = TRUE;
1828 HeapFree(GetProcessHeap(), 0, row_pointers);
1830 LeaveCriticalSection(&This->lock);
1832 return S_OK;
1835 static HRESULT WINAPI PngFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
1836 IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1838 FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
1839 return E_NOTIMPL;
1842 static const IWICBitmapFrameEncodeVtbl PngEncoder_FrameVtbl = {
1843 PngFrameEncode_QueryInterface,
1844 PngFrameEncode_AddRef,
1845 PngFrameEncode_Release,
1846 PngFrameEncode_Initialize,
1847 PngFrameEncode_SetSize,
1848 PngFrameEncode_SetResolution,
1849 PngFrameEncode_SetPixelFormat,
1850 PngFrameEncode_SetColorContexts,
1851 PngFrameEncode_SetPalette,
1852 PngFrameEncode_SetThumbnail,
1853 PngFrameEncode_WritePixels,
1854 PngFrameEncode_WriteSource,
1855 PngFrameEncode_Commit,
1856 PngFrameEncode_GetMetadataQueryWriter
1859 static HRESULT WINAPI PngEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
1860 void **ppv)
1862 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1863 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1865 if (!ppv) return E_INVALIDARG;
1867 if (IsEqualIID(&IID_IUnknown, iid) ||
1868 IsEqualIID(&IID_IWICBitmapEncoder, iid))
1870 *ppv = &This->IWICBitmapEncoder_iface;
1872 else
1874 *ppv = NULL;
1875 return E_NOINTERFACE;
1878 IUnknown_AddRef((IUnknown*)*ppv);
1879 return S_OK;
1882 static ULONG WINAPI PngEncoder_AddRef(IWICBitmapEncoder *iface)
1884 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1885 ULONG ref = InterlockedIncrement(&This->ref);
1887 TRACE("(%p) refcount=%u\n", iface, ref);
1889 return ref;
1892 static ULONG WINAPI PngEncoder_Release(IWICBitmapEncoder *iface)
1894 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1895 ULONG ref = InterlockedDecrement(&This->ref);
1897 TRACE("(%p) refcount=%u\n", iface, ref);
1899 if (ref == 0)
1901 This->lock.DebugInfo->Spare[0] = 0;
1902 DeleteCriticalSection(&This->lock);
1903 if (This->png_ptr)
1904 ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1905 if (This->stream)
1906 IStream_Release(This->stream);
1907 HeapFree(GetProcessHeap(), 0, This->data);
1908 HeapFree(GetProcessHeap(), 0, This);
1911 return ref;
1914 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1916 PngEncoder *This = ppng_get_io_ptr(png_ptr);
1917 HRESULT hr;
1918 ULONG byteswritten;
1920 hr = IStream_Write(This->stream, data, length, &byteswritten);
1921 if (FAILED(hr) || byteswritten != length)
1923 ppng_error(png_ptr, "failed writing data");
1927 static void user_flush(png_structp png_ptr)
1931 static HRESULT WINAPI PngEncoder_Initialize(IWICBitmapEncoder *iface,
1932 IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
1934 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1935 jmp_buf jmpbuf;
1937 TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
1939 EnterCriticalSection(&This->lock);
1941 if (This->png_ptr)
1943 LeaveCriticalSection(&This->lock);
1944 return WINCODEC_ERR_WRONGSTATE;
1947 /* initialize libpng */
1948 This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1949 if (!This->png_ptr)
1951 LeaveCriticalSection(&This->lock);
1952 return E_FAIL;
1955 This->info_ptr = ppng_create_info_struct(This->png_ptr);
1956 if (!This->info_ptr)
1958 ppng_destroy_write_struct(&This->png_ptr, NULL);
1959 This->png_ptr = NULL;
1960 LeaveCriticalSection(&This->lock);
1961 return E_FAIL;
1964 IStream_AddRef(pIStream);
1965 This->stream = pIStream;
1967 /* set up setjmp/longjmp error handling */
1968 if (setjmp(jmpbuf))
1970 ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1971 This->png_ptr = NULL;
1972 IStream_Release(This->stream);
1973 This->stream = NULL;
1974 LeaveCriticalSection(&This->lock);
1975 return E_FAIL;
1977 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1979 /* set up custom i/o handling */
1980 ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush);
1982 LeaveCriticalSection(&This->lock);
1984 return S_OK;
1987 static HRESULT WINAPI PngEncoder_GetContainerFormat(IWICBitmapEncoder *iface, GUID *format)
1989 TRACE("(%p,%p)\n", iface, format);
1991 if (!format)
1992 return E_INVALIDARG;
1994 memcpy(format, &GUID_ContainerFormatPng, sizeof(*format));
1995 return S_OK;
1998 static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info)
2000 IWICComponentInfo *comp_info;
2001 HRESULT hr;
2003 TRACE("%p,%p\n", iface, info);
2005 if (!info) return E_INVALIDARG;
2007 hr = CreateComponentInfo(&CLSID_WICPngEncoder, &comp_info);
2008 if (hr == S_OK)
2010 hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info);
2011 IWICComponentInfo_Release(comp_info);
2013 return hr;
2016 static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface,
2017 UINT cCount, IWICColorContext **ppIColorContext)
2019 FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
2020 return E_NOTIMPL;
2023 static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette)
2025 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2026 HRESULT hr;
2028 TRACE("(%p,%p)\n", iface, palette);
2030 EnterCriticalSection(&This->lock);
2032 hr = This->stream ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED;
2034 LeaveCriticalSection(&This->lock);
2036 return hr;
2039 static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
2041 TRACE("(%p,%p)\n", iface, pIThumbnail);
2042 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2045 static HRESULT WINAPI PngEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
2047 TRACE("(%p,%p)\n", iface, pIPreview);
2048 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2051 static HRESULT WINAPI PngEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
2052 IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
2054 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2055 HRESULT hr;
2056 static const PROPBAG2 opts[2] =
2058 { PROPBAG2_TYPE_DATA, VT_BOOL, 0, 0, (LPOLESTR)wszPngInterlaceOption },
2059 { PROPBAG2_TYPE_DATA, VT_UI1, 0, 0, (LPOLESTR)wszPngFilterOption },
2062 TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
2064 EnterCriticalSection(&This->lock);
2066 if (This->frame_count != 0)
2068 LeaveCriticalSection(&This->lock);
2069 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2072 if (!This->stream)
2074 LeaveCriticalSection(&This->lock);
2075 return WINCODEC_ERR_NOTINITIALIZED;
2078 if (ppIEncoderOptions)
2080 hr = CreatePropertyBag2(opts, ARRAY_SIZE(opts), ppIEncoderOptions);
2081 if (FAILED(hr))
2083 LeaveCriticalSection(&This->lock);
2084 return hr;
2088 This->frame_count = 1;
2090 LeaveCriticalSection(&This->lock);
2092 IWICBitmapEncoder_AddRef(iface);
2093 *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface;
2095 return S_OK;
2098 static HRESULT WINAPI PngEncoder_Commit(IWICBitmapEncoder *iface)
2100 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2101 TRACE("(%p)\n", iface);
2103 EnterCriticalSection(&This->lock);
2105 if (!This->frame_committed || This->committed)
2107 LeaveCriticalSection(&This->lock);
2108 return WINCODEC_ERR_WRONGSTATE;
2111 This->committed = TRUE;
2113 LeaveCriticalSection(&This->lock);
2115 return S_OK;
2118 static HRESULT WINAPI PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
2119 IWICMetadataQueryWriter **ppIMetadataQueryWriter)
2121 FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
2122 return E_NOTIMPL;
2125 static const IWICBitmapEncoderVtbl PngEncoder_Vtbl = {
2126 PngEncoder_QueryInterface,
2127 PngEncoder_AddRef,
2128 PngEncoder_Release,
2129 PngEncoder_Initialize,
2130 PngEncoder_GetContainerFormat,
2131 PngEncoder_GetEncoderInfo,
2132 PngEncoder_SetColorContexts,
2133 PngEncoder_SetPalette,
2134 PngEncoder_SetThumbnail,
2135 PngEncoder_SetPreview,
2136 PngEncoder_CreateNewFrame,
2137 PngEncoder_Commit,
2138 PngEncoder_GetMetadataQueryWriter
2141 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
2143 PngEncoder *This;
2144 HRESULT ret;
2146 TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
2148 *ppv = NULL;
2150 if (!load_libpng())
2152 ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG);
2153 return E_FAIL;
2156 This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder));
2157 if (!This) return E_OUTOFMEMORY;
2159 This->IWICBitmapEncoder_iface.lpVtbl = &PngEncoder_Vtbl;
2160 This->IWICBitmapFrameEncode_iface.lpVtbl = &PngEncoder_FrameVtbl;
2161 This->ref = 1;
2162 This->png_ptr = NULL;
2163 This->info_ptr = NULL;
2164 This->stream = NULL;
2165 This->frame_count = 0;
2166 This->frame_initialized = FALSE;
2167 This->format = NULL;
2168 This->info_written = FALSE;
2169 This->width = 0;
2170 This->height = 0;
2171 This->xres = 0.0;
2172 This->yres = 0.0;
2173 This->lines_written = 0;
2174 This->frame_committed = FALSE;
2175 This->committed = FALSE;
2176 This->data = NULL;
2177 This->colors = 0;
2178 InitializeCriticalSection(&This->lock);
2179 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock");
2181 ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
2182 IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
2184 return ret;
2187 #else /* !SONAME_LIBPNG */
2189 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
2191 ERR("Trying to load PNG picture, but PNG support is not compiled in.\n");
2192 return E_FAIL;
2195 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
2197 ERR("Trying to save PNG picture, but PNG support is not compiled in.\n");
2198 return E_FAIL;
2201 #endif