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