xref: /reactos/dll/win32/windowscodecs/pngformat.c (revision 5bfe6a53)
1 /*
2  * Copyright 2009 Vincent Povirk for CodeWeavers
3  * Copyright 2016 Dmitry Timoshkov
4  *
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.
9  *
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.
14  *
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
18  */
19 
20 #include "config.h"
21 #include "wine/port.h"
22 
23 #include <stdarg.h>
24 
25 #ifdef HAVE_PNG_H
26 #include <png.h>
27 #endif
28 
29 #define NONAMELESSUNION
30 #define COBJMACROS
31 
32 #include "windef.h"
33 #include "winbase.h"
34 #include "objbase.h"
35 
36 #include "wincodecs_private.h"
37 
38 #include "wine/debug.h"
39 #include "wine/library.h"
40 
41 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
42 
43 static inline ULONG read_ulong_be(BYTE* data)
44 {
45     return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
46 }
47 
48 static HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_size)
49 {
50     BYTE header[8];
51     HRESULT hr;
52     ULONG bytesread;
53 
54     hr = IStream_Read(stream, header, 8, &bytesread);
55     if (FAILED(hr) || bytesread < 8)
56     {
57         if (SUCCEEDED(hr))
58             hr = E_FAIL;
59         return hr;
60     }
61 
62     *data_size = read_ulong_be(&header[0]);
63 
64     memcpy(type, &header[4], 4);
65 
66     if (data)
67     {
68         *data = HeapAlloc(GetProcessHeap(), 0, *data_size);
69         if (!*data)
70             return E_OUTOFMEMORY;
71 
72         hr = IStream_Read(stream, *data, *data_size, &bytesread);
73 
74         if (FAILED(hr) || bytesread < *data_size)
75         {
76             if (SUCCEEDED(hr))
77                 hr = E_FAIL;
78             HeapFree(GetProcessHeap(), 0, *data);
79             *data = NULL;
80             return hr;
81         }
82 
83         /* Windows ignores CRC of the chunk */
84     }
85 
86     return S_OK;
87 }
88 
89 static HRESULT LoadTextMetadata(IStream *stream, const GUID *preferred_vendor,
90     DWORD persist_options, MetadataItem **items, DWORD *item_count)
91 {
92     HRESULT hr;
93     BYTE type[4];
94     BYTE *data;
95     ULONG data_size;
96     ULONG name_len, value_len;
97     BYTE *name_end_ptr;
98     LPSTR name, value;
99     MetadataItem *result;
100 
101     hr = read_png_chunk(stream, type, &data, &data_size);
102     if (FAILED(hr)) return hr;
103 
104     name_end_ptr = memchr(data, 0, data_size);
105 
106     name_len = name_end_ptr - data;
107 
108     if (!name_end_ptr || name_len > 79)
109     {
110         HeapFree(GetProcessHeap(), 0, data);
111         return E_FAIL;
112     }
113 
114     value_len = data_size - name_len - 1;
115 
116     result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem));
117     name = HeapAlloc(GetProcessHeap(), 0, name_len + 1);
118     value = HeapAlloc(GetProcessHeap(), 0, value_len + 1);
119     if (!result || !name || !value)
120     {
121         HeapFree(GetProcessHeap(), 0, data);
122         HeapFree(GetProcessHeap(), 0, result);
123         HeapFree(GetProcessHeap(), 0, name);
124         HeapFree(GetProcessHeap(), 0, value);
125         return E_OUTOFMEMORY;
126     }
127 
128     PropVariantInit(&result[0].schema);
129     PropVariantInit(&result[0].id);
130     PropVariantInit(&result[0].value);
131 
132     memcpy(name, data, name_len + 1);
133     memcpy(value, name_end_ptr + 1, value_len);
134     value[value_len] = 0;
135 
136     result[0].id.vt = VT_LPSTR;
137     result[0].id.u.pszVal = name;
138     result[0].value.vt = VT_LPSTR;
139     result[0].value.u.pszVal = value;
140 
141     *items = result;
142     *item_count = 1;
143 
144     HeapFree(GetProcessHeap(), 0, data);
145 
146     return S_OK;
147 }
148 
149 static const MetadataHandlerVtbl TextReader_Vtbl = {
150     0,
151     &CLSID_WICPngTextMetadataReader,
152     LoadTextMetadata
153 };
154 
155 HRESULT PngTextReader_CreateInstance(REFIID iid, void** ppv)
156 {
157     return MetadataReader_Create(&TextReader_Vtbl, iid, ppv);
158 }
159 
160 static HRESULT LoadGamaMetadata(IStream *stream, const GUID *preferred_vendor,
161     DWORD persist_options, MetadataItem **items, DWORD *item_count)
162 {
163     HRESULT hr;
164     BYTE type[4];
165     BYTE *data;
166     ULONG data_size;
167     ULONG gamma;
168     static const WCHAR ImageGamma[] = {'I','m','a','g','e','G','a','m','m','a',0};
169     LPWSTR name;
170     MetadataItem *result;
171 
172     hr = read_png_chunk(stream, type, &data, &data_size);
173     if (FAILED(hr)) return hr;
174 
175     if (data_size < 4)
176     {
177         HeapFree(GetProcessHeap(), 0, data);
178         return E_FAIL;
179     }
180 
181     gamma = read_ulong_be(data);
182 
183     HeapFree(GetProcessHeap(), 0, data);
184 
185     result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem));
186     name = HeapAlloc(GetProcessHeap(), 0, sizeof(ImageGamma));
187     if (!result || !name)
188     {
189         HeapFree(GetProcessHeap(), 0, result);
190         HeapFree(GetProcessHeap(), 0, name);
191         return E_OUTOFMEMORY;
192     }
193 
194     PropVariantInit(&result[0].schema);
195     PropVariantInit(&result[0].id);
196     PropVariantInit(&result[0].value);
197 
198     memcpy(name, ImageGamma, sizeof(ImageGamma));
199 
200     result[0].id.vt = VT_LPWSTR;
201     result[0].id.u.pwszVal = name;
202     result[0].value.vt = VT_UI4;
203     result[0].value.u.ulVal = gamma;
204 
205     *items = result;
206     *item_count = 1;
207 
208     return S_OK;
209 }
210 
211 static const MetadataHandlerVtbl GamaReader_Vtbl = {
212     0,
213     &CLSID_WICPngGamaMetadataReader,
214     LoadGamaMetadata
215 };
216 
217 HRESULT PngGamaReader_CreateInstance(REFIID iid, void** ppv)
218 {
219     return MetadataReader_Create(&GamaReader_Vtbl, iid, ppv);
220 }
221 
222 static HRESULT LoadChrmMetadata(IStream *stream, const GUID *preferred_vendor,
223     DWORD persist_options, MetadataItem **items, DWORD *item_count)
224 {
225     HRESULT hr;
226     BYTE type[4];
227     BYTE *data;
228     ULONG data_size;
229     static const WCHAR names[8][12] = {
230         {'W','h','i','t','e','P','o','i','n','t','X',0},
231         {'W','h','i','t','e','P','o','i','n','t','Y',0},
232         {'R','e','d','X',0},
233         {'R','e','d','Y',0},
234         {'G','r','e','e','n','X',0},
235         {'G','r','e','e','n','Y',0},
236         {'B','l','u','e','X',0},
237         {'B','l','u','e','Y',0},
238     };
239     LPWSTR dyn_names[8] = {0};
240     MetadataItem *result;
241     int i;
242 
243     hr = read_png_chunk(stream, type, &data, &data_size);
244     if (FAILED(hr)) return hr;
245 
246     if (data_size < 32)
247     {
248         HeapFree(GetProcessHeap(), 0, data);
249         return E_FAIL;
250     }
251 
252     result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem)*8);
253     for (i=0; i<8; i++)
254     {
255         dyn_names[i] = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(lstrlenW(names[i])+1));
256         if (!dyn_names[i]) break;
257     }
258     if (!result || i < 8)
259     {
260         HeapFree(GetProcessHeap(), 0, result);
261         for (i=0; i<8; i++)
262             HeapFree(GetProcessHeap(), 0, dyn_names[i]);
263         HeapFree(GetProcessHeap(), 0, data);
264         return E_OUTOFMEMORY;
265     }
266 
267     for (i=0; i<8; i++)
268     {
269         PropVariantInit(&result[i].schema);
270 
271         PropVariantInit(&result[i].id);
272         result[i].id.vt = VT_LPWSTR;
273         result[i].id.u.pwszVal = dyn_names[i];
274         lstrcpyW(dyn_names[i], names[i]);
275 
276         PropVariantInit(&result[i].value);
277         result[i].value.vt = VT_UI4;
278         result[i].value.u.ulVal = read_ulong_be(&data[i*4]);
279     }
280 
281     *items = result;
282     *item_count = 8;
283 
284     HeapFree(GetProcessHeap(), 0, data);
285 
286     return S_OK;
287 }
288 
289 static const MetadataHandlerVtbl ChrmReader_Vtbl = {
290     0,
291     &CLSID_WICPngChrmMetadataReader,
292     LoadChrmMetadata
293 };
294 
295 HRESULT PngChrmReader_CreateInstance(REFIID iid, void** ppv)
296 {
297     return MetadataReader_Create(&ChrmReader_Vtbl, iid, ppv);
298 }
299 
300 #ifdef SONAME_LIBPNG
301 
302 static void *libpng_handle;
303 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
304 MAKE_FUNCPTR(png_create_read_struct);
305 MAKE_FUNCPTR(png_create_info_struct);
306 MAKE_FUNCPTR(png_create_write_struct);
307 MAKE_FUNCPTR(png_destroy_read_struct);
308 MAKE_FUNCPTR(png_destroy_write_struct);
309 MAKE_FUNCPTR(png_error);
310 MAKE_FUNCPTR(png_get_bit_depth);
311 MAKE_FUNCPTR(png_get_color_type);
312 MAKE_FUNCPTR(png_get_error_ptr);
313 MAKE_FUNCPTR(png_get_iCCP);
314 MAKE_FUNCPTR(png_get_image_height);
315 MAKE_FUNCPTR(png_get_image_width);
316 MAKE_FUNCPTR(png_get_io_ptr);
317 MAKE_FUNCPTR(png_get_pHYs);
318 MAKE_FUNCPTR(png_get_PLTE);
319 MAKE_FUNCPTR(png_get_tRNS);
320 MAKE_FUNCPTR(png_set_bgr);
321 MAKE_FUNCPTR(png_set_crc_action);
322 MAKE_FUNCPTR(png_set_error_fn);
323 MAKE_FUNCPTR(png_set_filler);
324 MAKE_FUNCPTR(png_set_filter);
325 MAKE_FUNCPTR(png_set_gray_to_rgb);
326 MAKE_FUNCPTR(png_set_interlace_handling);
327 MAKE_FUNCPTR(png_set_IHDR);
328 MAKE_FUNCPTR(png_set_pHYs);
329 MAKE_FUNCPTR(png_set_PLTE);
330 MAKE_FUNCPTR(png_set_read_fn);
331 MAKE_FUNCPTR(png_set_strip_16);
332 MAKE_FUNCPTR(png_set_tRNS);
333 MAKE_FUNCPTR(png_set_tRNS_to_alpha);
334 MAKE_FUNCPTR(png_set_write_fn);
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
342 
343 static CRITICAL_SECTION init_png_cs;
344 static CRITICAL_SECTION_DEBUG init_png_cs_debug =
345 {
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") }
350 };
351 static CRITICAL_SECTION init_png_cs = { &init_png_cs_debug, -1, 0, 0, 0, 0 };
352 
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};
355 
356 static void *load_libpng(void)
357 {
358     void *result;
359 
360     EnterCriticalSection(&init_png_cs);
361 
362     if(!libpng_handle && (libpng_handle = wine_dlopen(SONAME_LIBPNG, RTLD_NOW, NULL, 0)) != NULL) {
363 
364 #define LOAD_FUNCPTR(f) \
365     if((p##f = wine_dlsym(libpng_handle, #f, NULL, 0)) == NULL) { \
366         libpng_handle = NULL; \
367         LeaveCriticalSection(&init_png_cs); \
368         return NULL; \
369     }
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_read_end);
402         LOAD_FUNCPTR(png_read_image);
403         LOAD_FUNCPTR(png_read_info);
404         LOAD_FUNCPTR(png_write_end);
405         LOAD_FUNCPTR(png_write_info);
406         LOAD_FUNCPTR(png_write_rows);
407 
408 #undef LOAD_FUNCPTR
409     }
410 
411     result = libpng_handle;
412 
413     LeaveCriticalSection(&init_png_cs);
414 
415     return result;
416 }
417 
418 static void user_error_fn(png_structp png_ptr, png_const_charp error_message)
419 {
420     jmp_buf *pjmpbuf;
421 
422     /* This uses setjmp/longjmp just like the default. We can't use the
423      * default because there's no way to access the jmp buffer in the png_struct
424      * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
425     WARN("PNG error: %s\n", debugstr_a(error_message));
426     pjmpbuf = ppng_get_error_ptr(png_ptr);
427     longjmp(*pjmpbuf, 1);
428 }
429 
430 static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message)
431 {
432     WARN("PNG warning: %s\n", debugstr_a(warning_message));
433 }
434 
435 typedef struct {
436     ULARGE_INTEGER ofs, len;
437     IWICMetadataReader* reader;
438 } metadata_block_info;
439 
440 typedef struct {
441     IWICBitmapDecoder IWICBitmapDecoder_iface;
442     IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
443     IWICMetadataBlockReader IWICMetadataBlockReader_iface;
444     LONG ref;
445     IStream *stream;
446     png_structp png_ptr;
447     png_infop info_ptr;
448     png_infop end_info;
449     BOOL initialized;
450     int bpp;
451     int width, height;
452     UINT stride;
453     const WICPixelFormatGUID *format;
454     BYTE *image_bits;
455     CRITICAL_SECTION lock; /* must be held when png structures are accessed or initialized is set */
456     ULONG metadata_count;
457     metadata_block_info* metadata_blocks;
458 } PngDecoder;
459 
460 static inline PngDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
461 {
462     return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapDecoder_iface);
463 }
464 
465 static inline PngDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
466 {
467     return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapFrameDecode_iface);
468 }
469 
470 static inline PngDecoder *impl_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface)
471 {
472     return CONTAINING_RECORD(iface, PngDecoder, IWICMetadataBlockReader_iface);
473 }
474 
475 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl;
476 
477 static HRESULT WINAPI PngDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
478     void **ppv)
479 {
480     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
481     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
482 
483     if (!ppv) return E_INVALIDARG;
484 
485     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
486     {
487         *ppv = &This->IWICBitmapDecoder_iface;
488     }
489     else
490     {
491         *ppv = NULL;
492         return E_NOINTERFACE;
493     }
494 
495     IUnknown_AddRef((IUnknown*)*ppv);
496     return S_OK;
497 }
498 
499 static ULONG WINAPI PngDecoder_AddRef(IWICBitmapDecoder *iface)
500 {
501     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
502     ULONG ref = InterlockedIncrement(&This->ref);
503 
504     TRACE("(%p) refcount=%u\n", iface, ref);
505 
506     return ref;
507 }
508 
509 static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface)
510 {
511     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
512     ULONG ref = InterlockedDecrement(&This->ref);
513     ULONG i;
514 
515     TRACE("(%p) refcount=%u\n", iface, ref);
516 
517     if (ref == 0)
518     {
519         if (This->stream)
520             IStream_Release(This->stream);
521         if (This->png_ptr)
522             ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
523         This->lock.DebugInfo->Spare[0] = 0;
524         DeleteCriticalSection(&This->lock);
525         HeapFree(GetProcessHeap(), 0, This->image_bits);
526         for (i=0; i<This->metadata_count; i++)
527         {
528             if (This->metadata_blocks[i].reader)
529                 IWICMetadataReader_Release(This->metadata_blocks[i].reader);
530         }
531         HeapFree(GetProcessHeap(), 0, This->metadata_blocks);
532         HeapFree(GetProcessHeap(), 0, This);
533     }
534 
535     return ref;
536 }
537 
538 static HRESULT WINAPI PngDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream,
539     DWORD *capability)
540 {
541     HRESULT hr;
542 
543     TRACE("(%p,%p,%p)\n", iface, stream, capability);
544 
545     if (!stream || !capability) return E_INVALIDARG;
546 
547     hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand);
548     if (hr != S_OK) return hr;
549 
550     *capability = WICBitmapDecoderCapabilityCanDecodeAllImages |
551                   WICBitmapDecoderCapabilityCanDecodeSomeImages;
552     /* FIXME: WICBitmapDecoderCapabilityCanEnumerateMetadata */
553     return S_OK;
554 }
555 
556 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
557 {
558     IStream *stream = ppng_get_io_ptr(png_ptr);
559     HRESULT hr;
560     ULONG bytesread;
561 
562     hr = IStream_Read(stream, data, length, &bytesread);
563     if (FAILED(hr) || bytesread != length)
564     {
565         ppng_error(png_ptr, "failed reading data");
566     }
567 }
568 
569 static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
570     WICDecodeOptions cacheOptions)
571 {
572     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
573     LARGE_INTEGER seek;
574     HRESULT hr=S_OK;
575     png_bytep *row_pointers=NULL;
576     UINT image_size;
577     UINT i;
578     int color_type, bit_depth;
579     png_bytep trans;
580     int num_trans;
581     png_uint_32 transparency;
582     png_color_16p trans_values;
583     jmp_buf jmpbuf;
584     BYTE chunk_type[4];
585     ULONG chunk_size;
586     ULARGE_INTEGER chunk_start;
587     ULONG metadata_blocks_size = 0;
588 
589     TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
590 
591     EnterCriticalSection(&This->lock);
592 
593     /* initialize libpng */
594     This->png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
595     if (!This->png_ptr)
596     {
597         hr = E_FAIL;
598         goto end;
599     }
600 
601     This->info_ptr = ppng_create_info_struct(This->png_ptr);
602     if (!This->info_ptr)
603     {
604         ppng_destroy_read_struct(&This->png_ptr, NULL, NULL);
605         This->png_ptr = NULL;
606         hr = E_FAIL;
607         goto end;
608     }
609 
610     This->end_info = ppng_create_info_struct(This->png_ptr);
611     if (!This->end_info)
612     {
613         ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, NULL);
614         This->png_ptr = NULL;
615         hr = E_FAIL;
616         goto end;
617     }
618 
619     /* set up setjmp/longjmp error handling */
620     if (setjmp(jmpbuf))
621     {
622         ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
623         HeapFree(GetProcessHeap(), 0, row_pointers);
624         This->png_ptr = NULL;
625         hr = WINCODEC_ERR_UNKNOWNIMAGEFORMAT;
626         goto end;
627     }
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);
630 
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;
635 
636     /* set up custom i/o handling */
637     ppng_set_read_fn(This->png_ptr, pIStream, user_read_data);
638 
639     /* read the header */
640     ppng_read_info(This->png_ptr, This->info_ptr);
641 
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);
645 
646     /* check for color-keyed alpha */
647     transparency = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans, &num_trans, &trans_values);
648 
649     if (transparency && (color_type == PNG_COLOR_TYPE_RGB ||
650         (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16)))
651     {
652         /* expand to RGBA */
653         if (color_type == PNG_COLOR_TYPE_GRAY)
654             ppng_set_gray_to_rgb(This->png_ptr);
655         ppng_set_tRNS_to_alpha(This->png_ptr);
656         color_type = PNG_COLOR_TYPE_RGB_ALPHA;
657     }
658 
659     switch (color_type)
660     {
661     case PNG_COLOR_TYPE_GRAY_ALPHA:
662         /* WIC does not support grayscale alpha formats so use RGBA */
663         ppng_set_gray_to_rgb(This->png_ptr);
664         /* fall through */
665     case PNG_COLOR_TYPE_RGB_ALPHA:
666         This->bpp = bit_depth * 4;
667         switch (bit_depth)
668         {
669         case 8:
670             ppng_set_bgr(This->png_ptr);
671             This->format = &GUID_WICPixelFormat32bppBGRA;
672             break;
673         case 16: This->format = &GUID_WICPixelFormat64bppRGBA; break;
674         default:
675             ERR("invalid RGBA bit depth: %i\n", bit_depth);
676             hr = E_FAIL;
677             goto end;
678         }
679         break;
680     case PNG_COLOR_TYPE_GRAY:
681         This->bpp = bit_depth;
682         if (!transparency)
683         {
684             switch (bit_depth)
685             {
686             case 1: This->format = &GUID_WICPixelFormatBlackWhite; break;
687             case 2: This->format = &GUID_WICPixelFormat2bppGray; break;
688             case 4: This->format = &GUID_WICPixelFormat4bppGray; break;
689             case 8: This->format = &GUID_WICPixelFormat8bppGray; break;
690             case 16: This->format = &GUID_WICPixelFormat16bppGray; break;
691             default:
692                 ERR("invalid grayscale bit depth: %i\n", bit_depth);
693                 hr = E_FAIL;
694                 goto end;
695             }
696             break;
697         }
698         /* else fall through */
699     case PNG_COLOR_TYPE_PALETTE:
700         This->bpp = bit_depth;
701         switch (bit_depth)
702         {
703         case 1: This->format = &GUID_WICPixelFormat1bppIndexed; break;
704         case 2: This->format = &GUID_WICPixelFormat2bppIndexed; break;
705         case 4: This->format = &GUID_WICPixelFormat4bppIndexed; break;
706         case 8: This->format = &GUID_WICPixelFormat8bppIndexed; break;
707         default:
708             ERR("invalid indexed color bit depth: %i\n", bit_depth);
709             hr = E_FAIL;
710             goto end;
711         }
712         break;
713     case PNG_COLOR_TYPE_RGB:
714         This->bpp = bit_depth * 3;
715         switch (bit_depth)
716         {
717         case 8:
718             ppng_set_bgr(This->png_ptr);
719             This->format = &GUID_WICPixelFormat24bppBGR;
720             break;
721         case 16: This->format = &GUID_WICPixelFormat48bppRGB; break;
722         default:
723             ERR("invalid RGB color bit depth: %i\n", bit_depth);
724             hr = E_FAIL;
725             goto end;
726         }
727         break;
728     default:
729         ERR("invalid color type %i\n", color_type);
730         hr = E_FAIL;
731         goto end;
732     }
733 
734     /* read the image data */
735     This->width = ppng_get_image_width(This->png_ptr, This->info_ptr);
736     This->height = ppng_get_image_height(This->png_ptr, This->info_ptr);
737     This->stride = (This->width * This->bpp + 7) / 8;
738     image_size = This->stride * This->height;
739 
740     This->image_bits = HeapAlloc(GetProcessHeap(), 0, image_size);
741     if (!This->image_bits)
742     {
743         hr = E_OUTOFMEMORY;
744         goto end;
745     }
746 
747     row_pointers = HeapAlloc(GetProcessHeap(), 0, sizeof(png_bytep)*This->height);
748     if (!row_pointers)
749     {
750         hr = E_OUTOFMEMORY;
751         goto end;
752     }
753 
754     for (i=0; i<This->height; i++)
755         row_pointers[i] = This->image_bits + i * This->stride;
756 
757     ppng_read_image(This->png_ptr, row_pointers);
758 
759     HeapFree(GetProcessHeap(), 0, row_pointers);
760     row_pointers = NULL;
761 
762     ppng_read_end(This->png_ptr, This->end_info);
763 
764     /* Find the metadata chunks in the file. */
765     seek.QuadPart = 8;
766 
767     do
768     {
769         hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, &chunk_start);
770         if (FAILED(hr)) goto end;
771 
772         hr = read_png_chunk(pIStream, chunk_type, NULL, &chunk_size);
773         if (FAILED(hr)) goto end;
774 
775         if (chunk_type[0] >= 'a' && chunk_type[0] <= 'z' &&
776             memcmp(chunk_type, "tRNS", 4) && memcmp(chunk_type, "pHYs", 4))
777         {
778             /* This chunk is considered metadata. */
779             if (This->metadata_count == metadata_blocks_size)
780             {
781                 metadata_block_info* new_metadata_blocks;
782                 ULONG new_metadata_blocks_size;
783 
784                 new_metadata_blocks_size = 4 + metadata_blocks_size * 2;
785                 new_metadata_blocks = HeapAlloc(GetProcessHeap(), 0,
786                     new_metadata_blocks_size * sizeof(*new_metadata_blocks));
787 
788                 if (!new_metadata_blocks)
789                 {
790                     hr = E_OUTOFMEMORY;
791                     goto end;
792                 }
793 
794                 memcpy(new_metadata_blocks, This->metadata_blocks,
795                     This->metadata_count * sizeof(*new_metadata_blocks));
796 
797                 HeapFree(GetProcessHeap(), 0, This->metadata_blocks);
798                 This->metadata_blocks = new_metadata_blocks;
799                 metadata_blocks_size = new_metadata_blocks_size;
800             }
801 
802             This->metadata_blocks[This->metadata_count].ofs = chunk_start;
803             This->metadata_blocks[This->metadata_count].len.QuadPart = chunk_size + 12;
804             This->metadata_blocks[This->metadata_count].reader = NULL;
805             This->metadata_count++;
806         }
807 
808         seek.QuadPart = chunk_start.QuadPart + chunk_size + 12; /* skip data and CRC */
809     } while (memcmp(chunk_type, "IEND", 4));
810 
811     This->stream = pIStream;
812     IStream_AddRef(This->stream);
813 
814     This->initialized = TRUE;
815 
816 end:
817     LeaveCriticalSection(&This->lock);
818 
819     return hr;
820 }
821 
822 static HRESULT WINAPI PngDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
823     GUID *pguidContainerFormat)
824 {
825     memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
826     return S_OK;
827 }
828 
829 static HRESULT WINAPI PngDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
830     IWICBitmapDecoderInfo **ppIDecoderInfo)
831 {
832     TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
833 
834     return get_decoder_info(&CLSID_WICPngDecoder, ppIDecoderInfo);
835 }
836 
837 static HRESULT WINAPI PngDecoder_CopyPalette(IWICBitmapDecoder *iface,
838     IWICPalette *palette)
839 {
840     TRACE("(%p,%p)\n", iface, palette);
841     return WINCODEC_ERR_PALETTEUNAVAILABLE;
842 }
843 
844 static HRESULT WINAPI PngDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
845     IWICMetadataQueryReader **reader)
846 {
847     TRACE("(%p,%p)\n", iface, reader);
848 
849     if (!reader) return E_INVALIDARG;
850 
851     *reader = NULL;
852     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
853 }
854 
855 static HRESULT WINAPI PngDecoder_GetPreview(IWICBitmapDecoder *iface,
856     IWICBitmapSource **ppIBitmapSource)
857 {
858     TRACE("(%p,%p)\n", iface, ppIBitmapSource);
859 
860     if (!ppIBitmapSource) return E_INVALIDARG;
861 
862     *ppIBitmapSource = NULL;
863     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
864 }
865 
866 static HRESULT WINAPI PngDecoder_GetColorContexts(IWICBitmapDecoder *iface,
867     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
868 {
869     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
870     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
871 }
872 
873 static HRESULT WINAPI PngDecoder_GetThumbnail(IWICBitmapDecoder *iface,
874     IWICBitmapSource **ppIThumbnail)
875 {
876     TRACE("(%p,%p)\n", iface, ppIThumbnail);
877 
878     if (!ppIThumbnail) return E_INVALIDARG;
879 
880     *ppIThumbnail = NULL;
881     return WINCODEC_ERR_CODECNOTHUMBNAIL;
882 }
883 
884 static HRESULT WINAPI PngDecoder_GetFrameCount(IWICBitmapDecoder *iface,
885     UINT *pCount)
886 {
887     if (!pCount) return E_INVALIDARG;
888 
889     *pCount = 1;
890     return S_OK;
891 }
892 
893 static HRESULT WINAPI PngDecoder_GetFrame(IWICBitmapDecoder *iface,
894     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
895 {
896     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
897     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
898 
899     if (!This->initialized) return WINCODEC_ERR_FRAMEMISSING;
900 
901     if (index != 0) return E_INVALIDARG;
902 
903     IWICBitmapDecoder_AddRef(iface);
904 
905     *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface;
906 
907     return S_OK;
908 }
909 
910 static const IWICBitmapDecoderVtbl PngDecoder_Vtbl = {
911     PngDecoder_QueryInterface,
912     PngDecoder_AddRef,
913     PngDecoder_Release,
914     PngDecoder_QueryCapability,
915     PngDecoder_Initialize,
916     PngDecoder_GetContainerFormat,
917     PngDecoder_GetDecoderInfo,
918     PngDecoder_CopyPalette,
919     PngDecoder_GetMetadataQueryReader,
920     PngDecoder_GetPreview,
921     PngDecoder_GetColorContexts,
922     PngDecoder_GetThumbnail,
923     PngDecoder_GetFrameCount,
924     PngDecoder_GetFrame
925 };
926 
927 static HRESULT WINAPI PngDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
928     void **ppv)
929 {
930     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
931     if (!ppv) return E_INVALIDARG;
932 
933     if (IsEqualIID(&IID_IUnknown, iid) ||
934         IsEqualIID(&IID_IWICBitmapSource, iid) ||
935         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
936     {
937         *ppv = &This->IWICBitmapFrameDecode_iface;
938     }
939     else if (IsEqualIID(&IID_IWICMetadataBlockReader, iid))
940     {
941         *ppv = &This->IWICMetadataBlockReader_iface;
942     }
943     else
944     {
945         *ppv = NULL;
946         return E_NOINTERFACE;
947     }
948 
949     IUnknown_AddRef((IUnknown*)*ppv);
950     return S_OK;
951 }
952 
953 static ULONG WINAPI PngDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface)
954 {
955     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
956     return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
957 }
958 
959 static ULONG WINAPI PngDecoder_Frame_Release(IWICBitmapFrameDecode *iface)
960 {
961     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
962     return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
963 }
964 
965 static HRESULT WINAPI PngDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface,
966     UINT *puiWidth, UINT *puiHeight)
967 {
968     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
969     *puiWidth = This->width;
970     *puiHeight = This->height;
971     TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight);
972     return S_OK;
973 }
974 
975 static HRESULT WINAPI PngDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface,
976     WICPixelFormatGUID *pPixelFormat)
977 {
978     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
979     TRACE("(%p,%p)\n", iface, pPixelFormat);
980 
981     memcpy(pPixelFormat, This->format, sizeof(GUID));
982 
983     return S_OK;
984 }
985 
986 static HRESULT WINAPI PngDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface,
987     double *pDpiX, double *pDpiY)
988 {
989     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
990     png_uint_32 ret, xres, yres;
991     int unit_type;
992 
993     EnterCriticalSection(&This->lock);
994 
995     ret = ppng_get_pHYs(This->png_ptr, This->info_ptr, &xres, &yres, &unit_type);
996 
997     if (ret && unit_type == PNG_RESOLUTION_METER)
998     {
999         *pDpiX = xres * 0.0254;
1000         *pDpiY = yres * 0.0254;
1001     }
1002     else
1003     {
1004         WARN("no pHYs block present\n");
1005         *pDpiX = *pDpiY = 96.0;
1006     }
1007 
1008     LeaveCriticalSection(&This->lock);
1009 
1010     TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY);
1011 
1012     return S_OK;
1013 }
1014 
1015 static HRESULT WINAPI PngDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface,
1016     IWICPalette *pIPalette)
1017 {
1018     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1019     png_uint_32 ret, color_type, bit_depth;
1020     png_colorp png_palette;
1021     int num_palette;
1022     WICColor palette[256];
1023     png_bytep trans_alpha;
1024     int num_trans;
1025     png_color_16p trans_values;
1026     int i;
1027     HRESULT hr=S_OK;
1028 
1029     TRACE("(%p,%p)\n", iface, pIPalette);
1030 
1031     EnterCriticalSection(&This->lock);
1032 
1033     color_type = ppng_get_color_type(This->png_ptr, This->info_ptr);
1034     bit_depth = ppng_get_bit_depth(This->png_ptr, This->info_ptr);
1035 
1036     if (color_type == PNG_COLOR_TYPE_PALETTE)
1037     {
1038         ret = ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette);
1039         if (!ret)
1040         {
1041             hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
1042             goto end;
1043         }
1044 
1045         if (num_palette > 256)
1046         {
1047             ERR("palette has %i colors?!\n", num_palette);
1048             hr = E_FAIL;
1049             goto end;
1050         }
1051 
1052         ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values);
1053         if (!ret) num_trans = 0;
1054 
1055         for (i=0; i<num_palette; i++)
1056         {
1057             BYTE alpha = (i < num_trans) ? trans_alpha[i] : 0xff;
1058             palette[i] = (alpha << 24 |
1059                           png_palette[i].red << 16|
1060                           png_palette[i].green << 8|
1061                           png_palette[i].blue);
1062         }
1063     }
1064     else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth <= 8) {
1065         ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values);
1066 
1067         if (!ret)
1068         {
1069             hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
1070             goto end;
1071         }
1072 
1073         num_palette = 1 << bit_depth;
1074 
1075         for (i=0; i<num_palette; i++)
1076         {
1077             BYTE alpha = (i == trans_values[0].gray) ? 0 : 0xff;
1078             BYTE val = i * 255 / (num_palette - 1);
1079             palette[i] = (alpha << 24 | val << 16 | val << 8 | val);
1080         }
1081     }
1082     else
1083     {
1084         hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
1085     }
1086 
1087 end:
1088 
1089     LeaveCriticalSection(&This->lock);
1090 
1091     if (SUCCEEDED(hr))
1092         hr = IWICPalette_InitializeCustom(pIPalette, palette, num_palette);
1093 
1094     return hr;
1095 }
1096 
1097 static HRESULT WINAPI PngDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface,
1098     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
1099 {
1100     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1101     TRACE("(%p,%s,%u,%u,%p)\n", iface, debug_wic_rect(prc), cbStride, cbBufferSize, pbBuffer);
1102 
1103     return copy_pixels(This->bpp, This->image_bits,
1104         This->width, This->height, This->stride,
1105         prc, cbStride, cbBufferSize, pbBuffer);
1106 }
1107 
1108 static HRESULT WINAPI PngDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
1109     IWICMetadataQueryReader **ppIMetadataQueryReader)
1110 {
1111     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1112 
1113     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
1114 
1115     if (!ppIMetadataQueryReader)
1116         return E_INVALIDARG;
1117 
1118     return MetadataQueryReader_CreateInstance(&This->IWICMetadataBlockReader_iface, NULL, ppIMetadataQueryReader);
1119 }
1120 
1121 static HRESULT WINAPI PngDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface,
1122     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
1123 {
1124     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1125     png_charp name;
1126     BYTE *profile;
1127     png_uint_32 len;
1128     int compression_type;
1129     HRESULT hr;
1130 
1131     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
1132 
1133     if (!pcActualCount) return E_INVALIDARG;
1134 
1135     EnterCriticalSection(&This->lock);
1136 
1137     if (ppng_get_iCCP(This->png_ptr, This->info_ptr, &name, &compression_type, (void *)&profile, &len))
1138     {
1139         if (cCount && ppIColorContexts)
1140         {
1141             hr = IWICColorContext_InitializeFromMemory(*ppIColorContexts, profile, len);
1142             if (FAILED(hr))
1143             {
1144                 LeaveCriticalSection(&This->lock);
1145                 return hr;
1146             }
1147         }
1148         *pcActualCount = 1;
1149     }
1150     else
1151         *pcActualCount = 0;
1152 
1153     LeaveCriticalSection(&This->lock);
1154 
1155     return S_OK;
1156 }
1157 
1158 static HRESULT WINAPI PngDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
1159     IWICBitmapSource **ppIThumbnail)
1160 {
1161     TRACE("(%p,%p)\n", iface, ppIThumbnail);
1162 
1163     if (!ppIThumbnail) return E_INVALIDARG;
1164 
1165     *ppIThumbnail = NULL;
1166     return WINCODEC_ERR_CODECNOTHUMBNAIL;
1167 }
1168 
1169 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl = {
1170     PngDecoder_Frame_QueryInterface,
1171     PngDecoder_Frame_AddRef,
1172     PngDecoder_Frame_Release,
1173     PngDecoder_Frame_GetSize,
1174     PngDecoder_Frame_GetPixelFormat,
1175     PngDecoder_Frame_GetResolution,
1176     PngDecoder_Frame_CopyPalette,
1177     PngDecoder_Frame_CopyPixels,
1178     PngDecoder_Frame_GetMetadataQueryReader,
1179     PngDecoder_Frame_GetColorContexts,
1180     PngDecoder_Frame_GetThumbnail
1181 };
1182 
1183 static HRESULT WINAPI PngDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid,
1184     void **ppv)
1185 {
1186     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1187     return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv);
1188 }
1189 
1190 static ULONG WINAPI PngDecoder_Block_AddRef(IWICMetadataBlockReader *iface)
1191 {
1192     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1193     return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
1194 }
1195 
1196 static ULONG WINAPI PngDecoder_Block_Release(IWICMetadataBlockReader *iface)
1197 {
1198     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1199     return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1200 }
1201 
1202 static HRESULT WINAPI PngDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
1203     GUID *pguidContainerFormat)
1204 {
1205     if (!pguidContainerFormat) return E_INVALIDARG;
1206     memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
1207     return S_OK;
1208 }
1209 
1210 static HRESULT WINAPI PngDecoder_Block_GetCount(IWICMetadataBlockReader *iface,
1211     UINT *pcCount)
1212 {
1213     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1214 
1215     TRACE("%p,%p\n", iface, pcCount);
1216 
1217     if (!pcCount) return E_INVALIDARG;
1218 
1219     *pcCount = This->metadata_count;
1220 
1221     return S_OK;
1222 }
1223 
1224 static HRESULT WINAPI PngDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
1225     UINT nIndex, IWICMetadataReader **ppIMetadataReader)
1226 {
1227     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1228     HRESULT hr;
1229     IWICComponentFactory* factory;
1230     IWICStream* stream;
1231 
1232     TRACE("%p,%d,%p\n", iface, nIndex, ppIMetadataReader);
1233 
1234     if (nIndex >= This->metadata_count || !ppIMetadataReader)
1235         return E_INVALIDARG;
1236 
1237     if (!This->metadata_blocks[nIndex].reader)
1238     {
1239         hr = StreamImpl_Create(&stream);
1240 
1241         if (SUCCEEDED(hr))
1242         {
1243             hr = IWICStream_InitializeFromIStreamRegion(stream, This->stream,
1244                 This->metadata_blocks[nIndex].ofs, This->metadata_blocks[nIndex].len);
1245 
1246             if (SUCCEEDED(hr))
1247                 hr = ImagingFactory_CreateInstance(&IID_IWICComponentFactory, (void**)&factory);
1248 
1249             if (SUCCEEDED(hr))
1250             {
1251                 hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory,
1252                     &GUID_ContainerFormatPng, NULL, WICMetadataCreationAllowUnknown,
1253                     (IStream*)stream, &This->metadata_blocks[nIndex].reader);
1254 
1255                 IWICComponentFactory_Release(factory);
1256             }
1257 
1258             IWICStream_Release(stream);
1259         }
1260 
1261         if (FAILED(hr))
1262         {
1263             *ppIMetadataReader = NULL;
1264             return hr;
1265         }
1266     }
1267 
1268     *ppIMetadataReader = This->metadata_blocks[nIndex].reader;
1269     IWICMetadataReader_AddRef(*ppIMetadataReader);
1270 
1271     return S_OK;
1272 }
1273 
1274 static HRESULT WINAPI PngDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface,
1275     IEnumUnknown **ppIEnumMetadata)
1276 {
1277     FIXME("%p,%p\n", iface, ppIEnumMetadata);
1278     return E_NOTIMPL;
1279 }
1280 
1281 static const IWICMetadataBlockReaderVtbl PngDecoder_BlockVtbl = {
1282     PngDecoder_Block_QueryInterface,
1283     PngDecoder_Block_AddRef,
1284     PngDecoder_Block_Release,
1285     PngDecoder_Block_GetContainerFormat,
1286     PngDecoder_Block_GetCount,
1287     PngDecoder_Block_GetReaderByIndex,
1288     PngDecoder_Block_GetEnumerator,
1289 };
1290 
1291 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
1292 {
1293     PngDecoder *This;
1294     HRESULT ret;
1295 
1296     TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
1297 
1298     *ppv = NULL;
1299 
1300     if (!load_libpng())
1301     {
1302         ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
1303         return E_FAIL;
1304     }
1305 
1306     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngDecoder));
1307     if (!This) return E_OUTOFMEMORY;
1308 
1309     This->IWICBitmapDecoder_iface.lpVtbl = &PngDecoder_Vtbl;
1310     This->IWICBitmapFrameDecode_iface.lpVtbl = &PngDecoder_FrameVtbl;
1311     This->IWICMetadataBlockReader_iface.lpVtbl = &PngDecoder_BlockVtbl;
1312     This->ref = 1;
1313     This->png_ptr = NULL;
1314     This->info_ptr = NULL;
1315     This->end_info = NULL;
1316     This->stream = NULL;
1317     This->initialized = FALSE;
1318     This->image_bits = NULL;
1319     InitializeCriticalSection(&This->lock);
1320     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngDecoder.lock");
1321     This->metadata_count = 0;
1322     This->metadata_blocks = NULL;
1323 
1324     ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
1325     IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1326 
1327     return ret;
1328 }
1329 
1330 struct png_pixelformat {
1331     const WICPixelFormatGUID *guid;
1332     UINT bpp;
1333     int bit_depth;
1334     int color_type;
1335     BOOL remove_filler;
1336     BOOL swap_rgb;
1337 };
1338 
1339 static const struct png_pixelformat formats[] = {
1340     {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1},
1341     {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1},
1342     {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0},
1343     {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0},
1344     {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0},
1345     {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0},
1346     {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0},
1347     {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1},
1348     {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0},
1349     {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0},
1350     {&GUID_WICPixelFormat1bppIndexed, 1, 1, PNG_COLOR_TYPE_PALETTE, 0, 0},
1351     {&GUID_WICPixelFormat2bppIndexed, 2, 2, PNG_COLOR_TYPE_PALETTE, 0, 0},
1352     {&GUID_WICPixelFormat4bppIndexed, 4, 4, PNG_COLOR_TYPE_PALETTE, 0, 0},
1353     {&GUID_WICPixelFormat8bppIndexed, 8, 8, PNG_COLOR_TYPE_PALETTE, 0, 0},
1354     {NULL},
1355 };
1356 
1357 typedef struct PngEncoder {
1358     IWICBitmapEncoder IWICBitmapEncoder_iface;
1359     IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
1360     LONG ref;
1361     IStream *stream;
1362     png_structp png_ptr;
1363     png_infop info_ptr;
1364     UINT frame_count;
1365     BOOL frame_initialized;
1366     const struct png_pixelformat *format;
1367     BOOL info_written;
1368     UINT width, height;
1369     double xres, yres;
1370     UINT lines_written;
1371     BOOL frame_committed;
1372     BOOL committed;
1373     CRITICAL_SECTION lock;
1374     BOOL interlace;
1375     WICPngFilterOption filter;
1376     BYTE *data;
1377     UINT stride;
1378     UINT passes;
1379     WICColor palette[256];
1380     UINT colors;
1381 } PngEncoder;
1382 
1383 static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
1384 {
1385     return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapEncoder_iface);
1386 }
1387 
1388 static inline PngEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
1389 {
1390     return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapFrameEncode_iface);
1391 }
1392 
1393 static HRESULT WINAPI PngFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
1394     void **ppv)
1395 {
1396     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1397     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1398 
1399     if (!ppv) return E_INVALIDARG;
1400 
1401     if (IsEqualIID(&IID_IUnknown, iid) ||
1402         IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
1403     {
1404         *ppv = &This->IWICBitmapFrameEncode_iface;
1405     }
1406     else
1407     {
1408         *ppv = NULL;
1409         return E_NOINTERFACE;
1410     }
1411 
1412     IUnknown_AddRef((IUnknown*)*ppv);
1413     return S_OK;
1414 }
1415 
1416 static ULONG WINAPI PngFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
1417 {
1418     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1419     return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface);
1420 }
1421 
1422 static ULONG WINAPI PngFrameEncode_Release(IWICBitmapFrameEncode *iface)
1423 {
1424     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1425     return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1426 }
1427 
1428 static HRESULT WINAPI PngFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
1429     IPropertyBag2 *pIEncoderOptions)
1430 {
1431     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1432     WICPngFilterOption filter;
1433     BOOL interlace;
1434     PROPBAG2 opts[2]= {{0}};
1435     VARIANT opt_values[2];
1436     HRESULT opt_hres[2];
1437     HRESULT hr;
1438 
1439     TRACE("(%p,%p)\n", iface, pIEncoderOptions);
1440 
1441     opts[0].pstrName = (LPOLESTR)wszPngInterlaceOption;
1442     opts[0].vt = VT_BOOL;
1443     opts[1].pstrName = (LPOLESTR)wszPngFilterOption;
1444     opts[1].vt = VT_UI1;
1445 
1446     if (pIEncoderOptions)
1447     {
1448         hr = IPropertyBag2_Read(pIEncoderOptions, ARRAY_SIZE(opts), opts, NULL, opt_values, opt_hres);
1449 
1450         if (FAILED(hr))
1451             return hr;
1452 
1453         if (V_VT(&opt_values[0]) == VT_EMPTY)
1454             interlace = FALSE;
1455         else
1456             interlace = (V_BOOL(&opt_values[0]) != 0);
1457 
1458         filter = V_UI1(&opt_values[1]);
1459         if (filter > WICPngFilterAdaptive)
1460         {
1461             WARN("Unrecognized filter option value %u.\n", filter);
1462             filter = WICPngFilterUnspecified;
1463         }
1464     }
1465     else
1466     {
1467         interlace = FALSE;
1468         filter = WICPngFilterUnspecified;
1469     }
1470 
1471     EnterCriticalSection(&This->lock);
1472 
1473     if (This->frame_initialized)
1474     {
1475         LeaveCriticalSection(&This->lock);
1476         return WINCODEC_ERR_WRONGSTATE;
1477     }
1478 
1479     This->interlace = interlace;
1480     This->filter = filter;
1481 
1482     This->frame_initialized = TRUE;
1483 
1484     LeaveCriticalSection(&This->lock);
1485 
1486     return S_OK;
1487 }
1488 
1489 static HRESULT WINAPI PngFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
1490     UINT uiWidth, UINT uiHeight)
1491 {
1492     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1493     TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
1494 
1495     EnterCriticalSection(&This->lock);
1496 
1497     if (!This->frame_initialized || This->info_written)
1498     {
1499         LeaveCriticalSection(&This->lock);
1500         return WINCODEC_ERR_WRONGSTATE;
1501     }
1502 
1503     This->width = uiWidth;
1504     This->height = uiHeight;
1505 
1506     LeaveCriticalSection(&This->lock);
1507 
1508     return S_OK;
1509 }
1510 
1511 static HRESULT WINAPI PngFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
1512     double dpiX, double dpiY)
1513 {
1514     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1515     TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
1516 
1517     EnterCriticalSection(&This->lock);
1518 
1519     if (!This->frame_initialized || This->info_written)
1520     {
1521         LeaveCriticalSection(&This->lock);
1522         return WINCODEC_ERR_WRONGSTATE;
1523     }
1524 
1525     This->xres = dpiX;
1526     This->yres = dpiY;
1527 
1528     LeaveCriticalSection(&This->lock);
1529 
1530     return S_OK;
1531 }
1532 
1533 static HRESULT WINAPI PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
1534     WICPixelFormatGUID *pPixelFormat)
1535 {
1536     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1537     int i;
1538     TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
1539 
1540     EnterCriticalSection(&This->lock);
1541 
1542     if (!This->frame_initialized || This->info_written)
1543     {
1544         LeaveCriticalSection(&This->lock);
1545         return WINCODEC_ERR_WRONGSTATE;
1546     }
1547 
1548     for (i=0; formats[i].guid; i++)
1549     {
1550         if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
1551             break;
1552     }
1553 
1554     if (!formats[i].guid) i = 0;
1555 
1556     This->format = &formats[i];
1557     memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
1558 
1559     LeaveCriticalSection(&This->lock);
1560 
1561     return S_OK;
1562 }
1563 
1564 static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
1565     UINT cCount, IWICColorContext **ppIColorContext)
1566 {
1567     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1568     return E_NOTIMPL;
1569 }
1570 
1571 static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
1572     IWICPalette *palette)
1573 {
1574     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1575     HRESULT hr;
1576 
1577     TRACE("(%p,%p)\n", iface, palette);
1578 
1579     if (!palette) return E_INVALIDARG;
1580 
1581     EnterCriticalSection(&This->lock);
1582 
1583     if (This->frame_initialized)
1584         hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors);
1585     else
1586         hr = WINCODEC_ERR_NOTINITIALIZED;
1587 
1588     LeaveCriticalSection(&This->lock);
1589     return hr;
1590 }
1591 
1592 static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
1593     IWICBitmapSource *pIThumbnail)
1594 {
1595     FIXME("(%p,%p): stub\n", iface, pIThumbnail);
1596     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1597 }
1598 
1599 static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
1600     UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
1601 {
1602     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1603     png_byte **row_pointers=NULL;
1604     UINT i;
1605     jmp_buf jmpbuf;
1606     TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
1607 
1608     EnterCriticalSection(&This->lock);
1609 
1610     if (!This->frame_initialized || !This->width || !This->height || !This->format)
1611     {
1612         LeaveCriticalSection(&This->lock);
1613         return WINCODEC_ERR_WRONGSTATE;
1614     }
1615 
1616     if (lineCount == 0 || lineCount + This->lines_written > This->height)
1617     {
1618         LeaveCriticalSection(&This->lock);
1619         return E_INVALIDARG;
1620     }
1621 
1622     /* set up setjmp/longjmp error handling */
1623     if (setjmp(jmpbuf))
1624     {
1625         LeaveCriticalSection(&This->lock);
1626         HeapFree(GetProcessHeap(), 0, row_pointers);
1627         return E_FAIL;
1628     }
1629     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1630 
1631     if (!This->info_written)
1632     {
1633         if (This->interlace)
1634         {
1635             /* libpng requires us to write all data multiple times in this case. */
1636             This->stride = (This->format->bpp * This->width + 7)/8;
1637             This->data = HeapAlloc(GetProcessHeap(), 0, This->height * This->stride);
1638             if (!This->data)
1639             {
1640                 LeaveCriticalSection(&This->lock);
1641                 return E_OUTOFMEMORY;
1642             }
1643         }
1644 
1645         ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height,
1646             This->format->bit_depth, This->format->color_type,
1647             This->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
1648             PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1649 
1650         if (This->xres != 0.0 && This->yres != 0.0)
1651         {
1652             ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254,
1653                 (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER);
1654         }
1655 
1656         if (This->format->color_type == PNG_COLOR_TYPE_PALETTE && This->colors)
1657         {
1658             png_color png_palette[256];
1659             png_byte trans[256];
1660             UINT i, num_trans = 0, colors;
1661 
1662             /* Newer libpng versions don't accept larger palettes than the declared
1663              * bit depth, so we need to generate the palette of the correct length.
1664              */
1665             colors = min(This->colors, 1 << This->format->bit_depth);
1666 
1667             for (i = 0; i < colors; i++)
1668             {
1669                 png_palette[i].red = (This->palette[i] >> 16) & 0xff;
1670                 png_palette[i].green = (This->palette[i] >> 8) & 0xff;
1671                 png_palette[i].blue = This->palette[i] & 0xff;
1672                 trans[i] = (This->palette[i] >> 24) & 0xff;
1673                 if (trans[i] != 0xff)
1674                     num_trans = i+1;
1675             }
1676 
1677             ppng_set_PLTE(This->png_ptr, This->info_ptr, png_palette, colors);
1678 
1679             if (num_trans)
1680                 ppng_set_tRNS(This->png_ptr, This->info_ptr, trans, num_trans, NULL);
1681         }
1682 
1683         ppng_write_info(This->png_ptr, This->info_ptr);
1684 
1685         if (This->format->remove_filler)
1686             ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER);
1687 
1688         if (This->format->swap_rgb)
1689             ppng_set_bgr(This->png_ptr);
1690 
1691         if (This->interlace)
1692             This->passes = ppng_set_interlace_handling(This->png_ptr);
1693 
1694         if (This->filter != WICPngFilterUnspecified)
1695         {
1696             static const int png_filter_map[] =
1697             {
1698                 /* WICPngFilterUnspecified */ PNG_NO_FILTERS,
1699                 /* WICPngFilterNone */        PNG_FILTER_NONE,
1700                 /* WICPngFilterSub */         PNG_FILTER_SUB,
1701                 /* WICPngFilterUp */          PNG_FILTER_UP,
1702                 /* WICPngFilterAverage */     PNG_FILTER_AVG,
1703                 /* WICPngFilterPaeth */       PNG_FILTER_PAETH,
1704                 /* WICPngFilterAdaptive */    PNG_ALL_FILTERS,
1705             };
1706 
1707             ppng_set_filter(This->png_ptr, 0, png_filter_map[This->filter]);
1708         }
1709 
1710         This->info_written = TRUE;
1711     }
1712 
1713     if (This->interlace)
1714     {
1715         /* Just store the data so we can write it in multiple passes in Commit. */
1716         for (i=0; i<lineCount; i++)
1717             memcpy(This->data + This->stride * (This->lines_written + i),
1718                    pbPixels + cbStride * i,
1719                    This->stride);
1720 
1721         This->lines_written += lineCount;
1722 
1723         LeaveCriticalSection(&This->lock);
1724         return S_OK;
1725     }
1726 
1727     row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*));
1728     if (!row_pointers)
1729     {
1730         LeaveCriticalSection(&This->lock);
1731         return E_OUTOFMEMORY;
1732     }
1733 
1734     for (i=0; i<lineCount; i++)
1735         row_pointers[i] = pbPixels + cbStride * i;
1736 
1737     ppng_write_rows(This->png_ptr, row_pointers, lineCount);
1738     This->lines_written += lineCount;
1739 
1740     LeaveCriticalSection(&This->lock);
1741 
1742     HeapFree(GetProcessHeap(), 0, row_pointers);
1743 
1744     return S_OK;
1745 }
1746 
1747 static HRESULT WINAPI PngFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
1748     IWICBitmapSource *pIBitmapSource, WICRect *prc)
1749 {
1750     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1751     HRESULT hr;
1752     TRACE("(%p,%p,%s)\n", iface, pIBitmapSource, debug_wic_rect(prc));
1753 
1754     if (!This->frame_initialized)
1755         return WINCODEC_ERR_WRONGSTATE;
1756 
1757     hr = configure_write_source(iface, pIBitmapSource, prc,
1758         This->format ? This->format->guid : NULL, This->width, This->height,
1759         This->xres, This->yres);
1760 
1761     if (SUCCEEDED(hr))
1762     {
1763         hr = write_source(iface, pIBitmapSource, prc,
1764             This->format->guid, This->format->bpp, This->width, This->height);
1765     }
1766 
1767     return hr;
1768 }
1769 
1770 static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface)
1771 {
1772     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1773     png_byte **row_pointers=NULL;
1774     jmp_buf jmpbuf;
1775     TRACE("(%p)\n", iface);
1776 
1777     EnterCriticalSection(&This->lock);
1778 
1779     if (!This->info_written || This->lines_written != This->height || This->frame_committed)
1780     {
1781         LeaveCriticalSection(&This->lock);
1782         return WINCODEC_ERR_WRONGSTATE;
1783     }
1784 
1785     /* set up setjmp/longjmp error handling */
1786     if (setjmp(jmpbuf))
1787     {
1788         LeaveCriticalSection(&This->lock);
1789         HeapFree(GetProcessHeap(), 0, row_pointers);
1790         return E_FAIL;
1791     }
1792     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1793 
1794     if (This->interlace)
1795     {
1796         int i;
1797 
1798         row_pointers = HeapAlloc(GetProcessHeap(), 0, This->height * sizeof(png_byte*));
1799         if (!row_pointers)
1800         {
1801             LeaveCriticalSection(&This->lock);
1802             return E_OUTOFMEMORY;
1803         }
1804 
1805         for (i=0; i<This->height; i++)
1806             row_pointers[i] = This->data + This->stride * i;
1807 
1808         for (i=0; i<This->passes; i++)
1809             ppng_write_rows(This->png_ptr, row_pointers, This->height);
1810     }
1811 
1812     ppng_write_end(This->png_ptr, This->info_ptr);
1813 
1814     This->frame_committed = TRUE;
1815 
1816     HeapFree(GetProcessHeap(), 0, row_pointers);
1817 
1818     LeaveCriticalSection(&This->lock);
1819 
1820     return S_OK;
1821 }
1822 
1823 static HRESULT WINAPI PngFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
1824     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1825 {
1826     FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
1827     return E_NOTIMPL;
1828 }
1829 
1830 static const IWICBitmapFrameEncodeVtbl PngEncoder_FrameVtbl = {
1831     PngFrameEncode_QueryInterface,
1832     PngFrameEncode_AddRef,
1833     PngFrameEncode_Release,
1834     PngFrameEncode_Initialize,
1835     PngFrameEncode_SetSize,
1836     PngFrameEncode_SetResolution,
1837     PngFrameEncode_SetPixelFormat,
1838     PngFrameEncode_SetColorContexts,
1839     PngFrameEncode_SetPalette,
1840     PngFrameEncode_SetThumbnail,
1841     PngFrameEncode_WritePixels,
1842     PngFrameEncode_WriteSource,
1843     PngFrameEncode_Commit,
1844     PngFrameEncode_GetMetadataQueryWriter
1845 };
1846 
1847 static HRESULT WINAPI PngEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
1848     void **ppv)
1849 {
1850     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1851     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1852 
1853     if (!ppv) return E_INVALIDARG;
1854 
1855     if (IsEqualIID(&IID_IUnknown, iid) ||
1856         IsEqualIID(&IID_IWICBitmapEncoder, iid))
1857     {
1858         *ppv = &This->IWICBitmapEncoder_iface;
1859     }
1860     else
1861     {
1862         *ppv = NULL;
1863         return E_NOINTERFACE;
1864     }
1865 
1866     IUnknown_AddRef((IUnknown*)*ppv);
1867     return S_OK;
1868 }
1869 
1870 static ULONG WINAPI PngEncoder_AddRef(IWICBitmapEncoder *iface)
1871 {
1872     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1873     ULONG ref = InterlockedIncrement(&This->ref);
1874 
1875     TRACE("(%p) refcount=%u\n", iface, ref);
1876 
1877     return ref;
1878 }
1879 
1880 static ULONG WINAPI PngEncoder_Release(IWICBitmapEncoder *iface)
1881 {
1882     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1883     ULONG ref = InterlockedDecrement(&This->ref);
1884 
1885     TRACE("(%p) refcount=%u\n", iface, ref);
1886 
1887     if (ref == 0)
1888     {
1889         This->lock.DebugInfo->Spare[0] = 0;
1890         DeleteCriticalSection(&This->lock);
1891         if (This->png_ptr)
1892             ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1893         if (This->stream)
1894             IStream_Release(This->stream);
1895         HeapFree(GetProcessHeap(), 0, This->data);
1896         HeapFree(GetProcessHeap(), 0, This);
1897     }
1898 
1899     return ref;
1900 }
1901 
1902 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1903 {
1904     PngEncoder *This = ppng_get_io_ptr(png_ptr);
1905     HRESULT hr;
1906     ULONG byteswritten;
1907 
1908     hr = IStream_Write(This->stream, data, length, &byteswritten);
1909     if (FAILED(hr) || byteswritten != length)
1910     {
1911         ppng_error(png_ptr, "failed writing data");
1912     }
1913 }
1914 
1915 static void user_flush(png_structp png_ptr)
1916 {
1917 }
1918 
1919 static HRESULT WINAPI PngEncoder_Initialize(IWICBitmapEncoder *iface,
1920     IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
1921 {
1922     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1923     jmp_buf jmpbuf;
1924 
1925     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
1926 
1927     EnterCriticalSection(&This->lock);
1928 
1929     if (This->png_ptr)
1930     {
1931         LeaveCriticalSection(&This->lock);
1932         return WINCODEC_ERR_WRONGSTATE;
1933     }
1934 
1935     /* initialize libpng */
1936     This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1937     if (!This->png_ptr)
1938     {
1939         LeaveCriticalSection(&This->lock);
1940         return E_FAIL;
1941     }
1942 
1943     This->info_ptr = ppng_create_info_struct(This->png_ptr);
1944     if (!This->info_ptr)
1945     {
1946         ppng_destroy_write_struct(&This->png_ptr, NULL);
1947         This->png_ptr = NULL;
1948         LeaveCriticalSection(&This->lock);
1949         return E_FAIL;
1950     }
1951 
1952     IStream_AddRef(pIStream);
1953     This->stream = pIStream;
1954 
1955     /* set up setjmp/longjmp error handling */
1956     if (setjmp(jmpbuf))
1957     {
1958         ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1959         This->png_ptr = NULL;
1960         IStream_Release(This->stream);
1961         This->stream = NULL;
1962         LeaveCriticalSection(&This->lock);
1963         return E_FAIL;
1964     }
1965     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1966 
1967     /* set up custom i/o handling */
1968     ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush);
1969 
1970     LeaveCriticalSection(&This->lock);
1971 
1972     return S_OK;
1973 }
1974 
1975 static HRESULT WINAPI PngEncoder_GetContainerFormat(IWICBitmapEncoder *iface, GUID *format)
1976 {
1977     TRACE("(%p,%p)\n", iface, format);
1978 
1979     if (!format)
1980         return E_INVALIDARG;
1981 
1982     memcpy(format, &GUID_ContainerFormatPng, sizeof(*format));
1983     return S_OK;
1984 }
1985 
1986 static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info)
1987 {
1988     IWICComponentInfo *comp_info;
1989     HRESULT hr;
1990 
1991     TRACE("%p,%p\n", iface, info);
1992 
1993     if (!info) return E_INVALIDARG;
1994 
1995     hr = CreateComponentInfo(&CLSID_WICPngEncoder, &comp_info);
1996     if (hr == S_OK)
1997     {
1998         hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info);
1999         IWICComponentInfo_Release(comp_info);
2000     }
2001     return hr;
2002 }
2003 
2004 static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface,
2005     UINT cCount, IWICColorContext **ppIColorContext)
2006 {
2007     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
2008     return E_NOTIMPL;
2009 }
2010 
2011 static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette)
2012 {
2013     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2014     HRESULT hr;
2015 
2016     TRACE("(%p,%p)\n", iface, palette);
2017 
2018     EnterCriticalSection(&This->lock);
2019 
2020     hr = This->stream ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED;
2021 
2022     LeaveCriticalSection(&This->lock);
2023 
2024     return hr;
2025 }
2026 
2027 static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
2028 {
2029     TRACE("(%p,%p)\n", iface, pIThumbnail);
2030     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2031 }
2032 
2033 static HRESULT WINAPI PngEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
2034 {
2035     TRACE("(%p,%p)\n", iface, pIPreview);
2036     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2037 }
2038 
2039 static HRESULT WINAPI PngEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
2040     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
2041 {
2042     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2043     HRESULT hr;
2044     static const PROPBAG2 opts[2] =
2045     {
2046         { PROPBAG2_TYPE_DATA, VT_BOOL, 0, 0, (LPOLESTR)wszPngInterlaceOption },
2047         { PROPBAG2_TYPE_DATA, VT_UI1,  0, 0, (LPOLESTR)wszPngFilterOption },
2048     };
2049 
2050     TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
2051 
2052     EnterCriticalSection(&This->lock);
2053 
2054     if (This->frame_count != 0)
2055     {
2056         LeaveCriticalSection(&This->lock);
2057         return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2058     }
2059 
2060     if (!This->stream)
2061     {
2062         LeaveCriticalSection(&This->lock);
2063         return WINCODEC_ERR_NOTINITIALIZED;
2064     }
2065 
2066     if (ppIEncoderOptions)
2067     {
2068         hr = CreatePropertyBag2(opts, ARRAY_SIZE(opts), ppIEncoderOptions);
2069         if (FAILED(hr))
2070         {
2071             LeaveCriticalSection(&This->lock);
2072             return hr;
2073         }
2074     }
2075 
2076     This->frame_count = 1;
2077 
2078     LeaveCriticalSection(&This->lock);
2079 
2080     IWICBitmapEncoder_AddRef(iface);
2081     *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface;
2082 
2083     return S_OK;
2084 }
2085 
2086 static HRESULT WINAPI PngEncoder_Commit(IWICBitmapEncoder *iface)
2087 {
2088     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2089     TRACE("(%p)\n", iface);
2090 
2091     EnterCriticalSection(&This->lock);
2092 
2093     if (!This->frame_committed || This->committed)
2094     {
2095         LeaveCriticalSection(&This->lock);
2096         return WINCODEC_ERR_WRONGSTATE;
2097     }
2098 
2099     This->committed = TRUE;
2100 
2101     LeaveCriticalSection(&This->lock);
2102 
2103     return S_OK;
2104 }
2105 
2106 static HRESULT WINAPI PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
2107     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
2108 {
2109     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
2110     return E_NOTIMPL;
2111 }
2112 
2113 static const IWICBitmapEncoderVtbl PngEncoder_Vtbl = {
2114     PngEncoder_QueryInterface,
2115     PngEncoder_AddRef,
2116     PngEncoder_Release,
2117     PngEncoder_Initialize,
2118     PngEncoder_GetContainerFormat,
2119     PngEncoder_GetEncoderInfo,
2120     PngEncoder_SetColorContexts,
2121     PngEncoder_SetPalette,
2122     PngEncoder_SetThumbnail,
2123     PngEncoder_SetPreview,
2124     PngEncoder_CreateNewFrame,
2125     PngEncoder_Commit,
2126     PngEncoder_GetMetadataQueryWriter
2127 };
2128 
2129 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
2130 {
2131     PngEncoder *This;
2132     HRESULT ret;
2133 
2134     TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
2135 
2136     *ppv = NULL;
2137 
2138     if (!load_libpng())
2139     {
2140         ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG);
2141         return E_FAIL;
2142     }
2143 
2144     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder));
2145     if (!This) return E_OUTOFMEMORY;
2146 
2147     This->IWICBitmapEncoder_iface.lpVtbl = &PngEncoder_Vtbl;
2148     This->IWICBitmapFrameEncode_iface.lpVtbl = &PngEncoder_FrameVtbl;
2149     This->ref = 1;
2150     This->png_ptr = NULL;
2151     This->info_ptr = NULL;
2152     This->stream = NULL;
2153     This->frame_count = 0;
2154     This->frame_initialized = FALSE;
2155     This->format = NULL;
2156     This->info_written = FALSE;
2157     This->width = 0;
2158     This->height = 0;
2159     This->xres = 0.0;
2160     This->yres = 0.0;
2161     This->lines_written = 0;
2162     This->frame_committed = FALSE;
2163     This->committed = FALSE;
2164     This->data = NULL;
2165     This->colors = 0;
2166     InitializeCriticalSection(&This->lock);
2167     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock");
2168 
2169     ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
2170     IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
2171 
2172     return ret;
2173 }
2174 
2175 #else /* !HAVE_PNG_H */
2176 
2177 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
2178 {
2179     ERR("Trying to load PNG picture, but PNG support is not compiled in.\n");
2180     return E_FAIL;
2181 }
2182 
2183 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
2184 {
2185     ERR("Trying to save PNG picture, but PNG support is not compiled in.\n");
2186     return E_FAIL;
2187 }
2188 
2189 #endif
2190