xref: /reactos/dll/win32/windowscodecs/pngformat.c (revision c7bba39a)
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     HRESULT hr;
833     IWICComponentInfo *compinfo;
834 
835     TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
836 
837     hr = CreateComponentInfo(&CLSID_WICPngDecoder, &compinfo);
838     if (FAILED(hr)) return hr;
839 
840     hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
841         (void**)ppIDecoderInfo);
842 
843     IWICComponentInfo_Release(compinfo);
844 
845     return hr;
846 }
847 
848 static HRESULT WINAPI PngDecoder_CopyPalette(IWICBitmapDecoder *iface,
849     IWICPalette *palette)
850 {
851     TRACE("(%p,%p)\n", iface, palette);
852     return WINCODEC_ERR_PALETTEUNAVAILABLE;
853 }
854 
855 static HRESULT WINAPI PngDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
856     IWICMetadataQueryReader **reader)
857 {
858     FIXME("(%p,%p): stub\n", iface, reader);
859 
860     if (!reader) return E_INVALIDARG;
861 
862     *reader = NULL;
863     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
864 }
865 
866 static HRESULT WINAPI PngDecoder_GetPreview(IWICBitmapDecoder *iface,
867     IWICBitmapSource **ppIBitmapSource)
868 {
869     TRACE("(%p,%p)\n", iface, ppIBitmapSource);
870 
871     if (!ppIBitmapSource) return E_INVALIDARG;
872 
873     *ppIBitmapSource = NULL;
874     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
875 }
876 
877 static HRESULT WINAPI PngDecoder_GetColorContexts(IWICBitmapDecoder *iface,
878     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
879 {
880     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
881     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
882 }
883 
884 static HRESULT WINAPI PngDecoder_GetThumbnail(IWICBitmapDecoder *iface,
885     IWICBitmapSource **ppIThumbnail)
886 {
887     TRACE("(%p,%p)\n", iface, ppIThumbnail);
888 
889     if (!ppIThumbnail) return E_INVALIDARG;
890 
891     *ppIThumbnail = NULL;
892     return WINCODEC_ERR_CODECNOTHUMBNAIL;
893 }
894 
895 static HRESULT WINAPI PngDecoder_GetFrameCount(IWICBitmapDecoder *iface,
896     UINT *pCount)
897 {
898     if (!pCount) return E_INVALIDARG;
899 
900     *pCount = 1;
901     return S_OK;
902 }
903 
904 static HRESULT WINAPI PngDecoder_GetFrame(IWICBitmapDecoder *iface,
905     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
906 {
907     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
908     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
909 
910     if (!This->initialized) return WINCODEC_ERR_FRAMEMISSING;
911 
912     if (index != 0) return E_INVALIDARG;
913 
914     IWICBitmapDecoder_AddRef(iface);
915 
916     *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface;
917 
918     return S_OK;
919 }
920 
921 static const IWICBitmapDecoderVtbl PngDecoder_Vtbl = {
922     PngDecoder_QueryInterface,
923     PngDecoder_AddRef,
924     PngDecoder_Release,
925     PngDecoder_QueryCapability,
926     PngDecoder_Initialize,
927     PngDecoder_GetContainerFormat,
928     PngDecoder_GetDecoderInfo,
929     PngDecoder_CopyPalette,
930     PngDecoder_GetMetadataQueryReader,
931     PngDecoder_GetPreview,
932     PngDecoder_GetColorContexts,
933     PngDecoder_GetThumbnail,
934     PngDecoder_GetFrameCount,
935     PngDecoder_GetFrame
936 };
937 
938 static HRESULT WINAPI PngDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
939     void **ppv)
940 {
941     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
942     if (!ppv) return E_INVALIDARG;
943 
944     if (IsEqualIID(&IID_IUnknown, iid) ||
945         IsEqualIID(&IID_IWICBitmapSource, iid) ||
946         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
947     {
948         *ppv = &This->IWICBitmapFrameDecode_iface;
949     }
950     else if (IsEqualIID(&IID_IWICMetadataBlockReader, iid))
951     {
952         *ppv = &This->IWICMetadataBlockReader_iface;
953     }
954     else
955     {
956         *ppv = NULL;
957         return E_NOINTERFACE;
958     }
959 
960     IUnknown_AddRef((IUnknown*)*ppv);
961     return S_OK;
962 }
963 
964 static ULONG WINAPI PngDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface)
965 {
966     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
967     return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
968 }
969 
970 static ULONG WINAPI PngDecoder_Frame_Release(IWICBitmapFrameDecode *iface)
971 {
972     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
973     return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
974 }
975 
976 static HRESULT WINAPI PngDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface,
977     UINT *puiWidth, UINT *puiHeight)
978 {
979     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
980     *puiWidth = This->width;
981     *puiHeight = This->height;
982     TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight);
983     return S_OK;
984 }
985 
986 static HRESULT WINAPI PngDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface,
987     WICPixelFormatGUID *pPixelFormat)
988 {
989     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
990     TRACE("(%p,%p)\n", iface, pPixelFormat);
991 
992     memcpy(pPixelFormat, This->format, sizeof(GUID));
993 
994     return S_OK;
995 }
996 
997 static HRESULT WINAPI PngDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface,
998     double *pDpiX, double *pDpiY)
999 {
1000     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1001     png_uint_32 ret, xres, yres;
1002     int unit_type;
1003 
1004     EnterCriticalSection(&This->lock);
1005 
1006     ret = ppng_get_pHYs(This->png_ptr, This->info_ptr, &xres, &yres, &unit_type);
1007 
1008     if (ret && unit_type == PNG_RESOLUTION_METER)
1009     {
1010         *pDpiX = xres * 0.0254;
1011         *pDpiY = yres * 0.0254;
1012     }
1013     else
1014     {
1015         WARN("no pHYs block present\n");
1016         *pDpiX = *pDpiY = 96.0;
1017     }
1018 
1019     LeaveCriticalSection(&This->lock);
1020 
1021     TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY);
1022 
1023     return S_OK;
1024 }
1025 
1026 static HRESULT WINAPI PngDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface,
1027     IWICPalette *pIPalette)
1028 {
1029     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1030     png_uint_32 ret, color_type, bit_depth;
1031     png_colorp png_palette;
1032     int num_palette;
1033     WICColor palette[256];
1034     png_bytep trans_alpha;
1035     int num_trans;
1036     png_color_16p trans_values;
1037     int i;
1038     HRESULT hr=S_OK;
1039 
1040     TRACE("(%p,%p)\n", iface, pIPalette);
1041 
1042     EnterCriticalSection(&This->lock);
1043 
1044     color_type = ppng_get_color_type(This->png_ptr, This->info_ptr);
1045     bit_depth = ppng_get_bit_depth(This->png_ptr, This->info_ptr);
1046 
1047     if (color_type == PNG_COLOR_TYPE_PALETTE)
1048     {
1049         ret = ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette);
1050         if (!ret)
1051         {
1052             hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
1053             goto end;
1054         }
1055 
1056         if (num_palette > 256)
1057         {
1058             ERR("palette has %i colors?!\n", num_palette);
1059             hr = E_FAIL;
1060             goto end;
1061         }
1062 
1063         ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values);
1064         if (!ret) num_trans = 0;
1065 
1066         for (i=0; i<num_palette; i++)
1067         {
1068             BYTE alpha = (i < num_trans) ? trans_alpha[i] : 0xff;
1069             palette[i] = (alpha << 24 |
1070                           png_palette[i].red << 16|
1071                           png_palette[i].green << 8|
1072                           png_palette[i].blue);
1073         }
1074     }
1075     else if (color_type == PNG_COLOR_TYPE_GRAY) {
1076         ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values);
1077 
1078         if (!ret)
1079         {
1080             hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
1081             goto end;
1082         }
1083 
1084         num_palette = 1 << bit_depth;
1085 
1086         for (i=0; i<num_palette; i++)
1087         {
1088             BYTE alpha = (i == trans_values[0].gray) ? 0 : 0xff;
1089             BYTE val = i * 255 / (num_palette - 1);
1090             palette[i] = (alpha << 24 | val << 16 | val << 8 | val);
1091         }
1092     }
1093     else
1094     {
1095         hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
1096     }
1097 
1098 end:
1099 
1100     LeaveCriticalSection(&This->lock);
1101 
1102     if (SUCCEEDED(hr))
1103         hr = IWICPalette_InitializeCustom(pIPalette, palette, num_palette);
1104 
1105     return hr;
1106 }
1107 
1108 static HRESULT WINAPI PngDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface,
1109     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
1110 {
1111     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1112     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
1113 
1114     return copy_pixels(This->bpp, This->image_bits,
1115         This->width, This->height, This->stride,
1116         prc, cbStride, cbBufferSize, pbBuffer);
1117 }
1118 
1119 static HRESULT WINAPI PngDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
1120     IWICMetadataQueryReader **ppIMetadataQueryReader)
1121 {
1122     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1123 
1124     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
1125 
1126     if (!ppIMetadataQueryReader)
1127         return E_INVALIDARG;
1128 
1129     return MetadataQueryReader_CreateInstance(&This->IWICMetadataBlockReader_iface, NULL, ppIMetadataQueryReader);
1130 }
1131 
1132 static HRESULT WINAPI PngDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface,
1133     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
1134 {
1135     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1136     png_charp name;
1137     BYTE *profile;
1138     png_uint_32 len;
1139     int compression_type;
1140     HRESULT hr;
1141 
1142     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
1143 
1144     if (!pcActualCount) return E_INVALIDARG;
1145 
1146     EnterCriticalSection(&This->lock);
1147 
1148     if (ppng_get_iCCP(This->png_ptr, This->info_ptr, &name, &compression_type, (void *)&profile, &len))
1149     {
1150         if (cCount && ppIColorContexts)
1151         {
1152             hr = IWICColorContext_InitializeFromMemory(*ppIColorContexts, profile, len);
1153             if (FAILED(hr))
1154             {
1155                 LeaveCriticalSection(&This->lock);
1156                 return hr;
1157             }
1158         }
1159         *pcActualCount = 1;
1160     }
1161     else
1162         *pcActualCount = 0;
1163 
1164     LeaveCriticalSection(&This->lock);
1165 
1166     return S_OK;
1167 }
1168 
1169 static HRESULT WINAPI PngDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
1170     IWICBitmapSource **ppIThumbnail)
1171 {
1172     TRACE("(%p,%p)\n", iface, ppIThumbnail);
1173 
1174     if (!ppIThumbnail) return E_INVALIDARG;
1175 
1176     *ppIThumbnail = NULL;
1177     return WINCODEC_ERR_CODECNOTHUMBNAIL;
1178 }
1179 
1180 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl = {
1181     PngDecoder_Frame_QueryInterface,
1182     PngDecoder_Frame_AddRef,
1183     PngDecoder_Frame_Release,
1184     PngDecoder_Frame_GetSize,
1185     PngDecoder_Frame_GetPixelFormat,
1186     PngDecoder_Frame_GetResolution,
1187     PngDecoder_Frame_CopyPalette,
1188     PngDecoder_Frame_CopyPixels,
1189     PngDecoder_Frame_GetMetadataQueryReader,
1190     PngDecoder_Frame_GetColorContexts,
1191     PngDecoder_Frame_GetThumbnail
1192 };
1193 
1194 static HRESULT WINAPI PngDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid,
1195     void **ppv)
1196 {
1197     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1198     return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv);
1199 }
1200 
1201 static ULONG WINAPI PngDecoder_Block_AddRef(IWICMetadataBlockReader *iface)
1202 {
1203     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1204     return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
1205 }
1206 
1207 static ULONG WINAPI PngDecoder_Block_Release(IWICMetadataBlockReader *iface)
1208 {
1209     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1210     return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1211 }
1212 
1213 static HRESULT WINAPI PngDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
1214     GUID *pguidContainerFormat)
1215 {
1216     if (!pguidContainerFormat) return E_INVALIDARG;
1217     memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
1218     return S_OK;
1219 }
1220 
1221 static HRESULT WINAPI PngDecoder_Block_GetCount(IWICMetadataBlockReader *iface,
1222     UINT *pcCount)
1223 {
1224     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1225 
1226     TRACE("%p,%p\n", iface, pcCount);
1227 
1228     if (!pcCount) return E_INVALIDARG;
1229 
1230     *pcCount = This->metadata_count;
1231 
1232     return S_OK;
1233 }
1234 
1235 static HRESULT WINAPI PngDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
1236     UINT nIndex, IWICMetadataReader **ppIMetadataReader)
1237 {
1238     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1239     HRESULT hr;
1240     IWICComponentFactory* factory;
1241     IWICStream* stream;
1242 
1243     TRACE("%p,%d,%p\n", iface, nIndex, ppIMetadataReader);
1244 
1245     if (nIndex >= This->metadata_count || !ppIMetadataReader)
1246         return E_INVALIDARG;
1247 
1248     if (!This->metadata_blocks[nIndex].reader)
1249     {
1250         hr = StreamImpl_Create(&stream);
1251 
1252         if (SUCCEEDED(hr))
1253         {
1254             hr = IWICStream_InitializeFromIStreamRegion(stream, This->stream,
1255                 This->metadata_blocks[nIndex].ofs, This->metadata_blocks[nIndex].len);
1256 
1257             if (SUCCEEDED(hr))
1258                 hr = ComponentFactory_CreateInstance(&IID_IWICComponentFactory, (void**)&factory);
1259 
1260             if (SUCCEEDED(hr))
1261             {
1262                 hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory,
1263                     &GUID_ContainerFormatPng, NULL, WICMetadataCreationAllowUnknown,
1264                     (IStream*)stream, &This->metadata_blocks[nIndex].reader);
1265 
1266                 IWICComponentFactory_Release(factory);
1267             }
1268 
1269             IWICStream_Release(stream);
1270         }
1271 
1272         if (FAILED(hr))
1273         {
1274             *ppIMetadataReader = NULL;
1275             return hr;
1276         }
1277     }
1278 
1279     *ppIMetadataReader = This->metadata_blocks[nIndex].reader;
1280     IWICMetadataReader_AddRef(*ppIMetadataReader);
1281 
1282     return S_OK;
1283 }
1284 
1285 static HRESULT WINAPI PngDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface,
1286     IEnumUnknown **ppIEnumMetadata)
1287 {
1288     FIXME("%p,%p\n", iface, ppIEnumMetadata);
1289     return E_NOTIMPL;
1290 }
1291 
1292 static const IWICMetadataBlockReaderVtbl PngDecoder_BlockVtbl = {
1293     PngDecoder_Block_QueryInterface,
1294     PngDecoder_Block_AddRef,
1295     PngDecoder_Block_Release,
1296     PngDecoder_Block_GetContainerFormat,
1297     PngDecoder_Block_GetCount,
1298     PngDecoder_Block_GetReaderByIndex,
1299     PngDecoder_Block_GetEnumerator,
1300 };
1301 
1302 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
1303 {
1304     PngDecoder *This;
1305     HRESULT ret;
1306 
1307     TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
1308 
1309     *ppv = NULL;
1310 
1311     if (!load_libpng())
1312     {
1313         ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
1314         return E_FAIL;
1315     }
1316 
1317     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngDecoder));
1318     if (!This) return E_OUTOFMEMORY;
1319 
1320     This->IWICBitmapDecoder_iface.lpVtbl = &PngDecoder_Vtbl;
1321     This->IWICBitmapFrameDecode_iface.lpVtbl = &PngDecoder_FrameVtbl;
1322     This->IWICMetadataBlockReader_iface.lpVtbl = &PngDecoder_BlockVtbl;
1323     This->ref = 1;
1324     This->png_ptr = NULL;
1325     This->info_ptr = NULL;
1326     This->end_info = NULL;
1327     This->stream = NULL;
1328     This->initialized = FALSE;
1329     This->image_bits = NULL;
1330     InitializeCriticalSection(&This->lock);
1331     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngDecoder.lock");
1332     This->metadata_count = 0;
1333     This->metadata_blocks = NULL;
1334 
1335     ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
1336     IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1337 
1338     return ret;
1339 }
1340 
1341 struct png_pixelformat {
1342     const WICPixelFormatGUID *guid;
1343     UINT bpp;
1344     int bit_depth;
1345     int color_type;
1346     BOOL remove_filler;
1347     BOOL swap_rgb;
1348 };
1349 
1350 static const struct png_pixelformat formats[] = {
1351     {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1},
1352     {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1},
1353     {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0},
1354     {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0},
1355     {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0},
1356     {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0},
1357     {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0},
1358     {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1},
1359     {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0},
1360     {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0},
1361     {&GUID_WICPixelFormat1bppIndexed, 1, 1, PNG_COLOR_TYPE_PALETTE, 0, 0},
1362     {&GUID_WICPixelFormat2bppIndexed, 2, 2, PNG_COLOR_TYPE_PALETTE, 0, 0},
1363     {&GUID_WICPixelFormat4bppIndexed, 4, 4, PNG_COLOR_TYPE_PALETTE, 0, 0},
1364     {&GUID_WICPixelFormat8bppIndexed, 8, 8, PNG_COLOR_TYPE_PALETTE, 0, 0},
1365     {NULL},
1366 };
1367 
1368 typedef struct PngEncoder {
1369     IWICBitmapEncoder IWICBitmapEncoder_iface;
1370     IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
1371     LONG ref;
1372     IStream *stream;
1373     png_structp png_ptr;
1374     png_infop info_ptr;
1375     UINT frame_count;
1376     BOOL frame_initialized;
1377     const struct png_pixelformat *format;
1378     BOOL info_written;
1379     UINT width, height;
1380     double xres, yres;
1381     UINT lines_written;
1382     BOOL frame_committed;
1383     BOOL committed;
1384     CRITICAL_SECTION lock;
1385     BOOL interlace;
1386     WICPngFilterOption filter;
1387     BYTE *data;
1388     UINT stride;
1389     UINT passes;
1390     WICColor palette[256];
1391     UINT colors;
1392 } PngEncoder;
1393 
1394 static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
1395 {
1396     return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapEncoder_iface);
1397 }
1398 
1399 static inline PngEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
1400 {
1401     return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapFrameEncode_iface);
1402 }
1403 
1404 static HRESULT WINAPI PngFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
1405     void **ppv)
1406 {
1407     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1408     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1409 
1410     if (!ppv) return E_INVALIDARG;
1411 
1412     if (IsEqualIID(&IID_IUnknown, iid) ||
1413         IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
1414     {
1415         *ppv = &This->IWICBitmapFrameEncode_iface;
1416     }
1417     else
1418     {
1419         *ppv = NULL;
1420         return E_NOINTERFACE;
1421     }
1422 
1423     IUnknown_AddRef((IUnknown*)*ppv);
1424     return S_OK;
1425 }
1426 
1427 static ULONG WINAPI PngFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
1428 {
1429     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1430     return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface);
1431 }
1432 
1433 static ULONG WINAPI PngFrameEncode_Release(IWICBitmapFrameEncode *iface)
1434 {
1435     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1436     return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1437 }
1438 
1439 static HRESULT WINAPI PngFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
1440     IPropertyBag2 *pIEncoderOptions)
1441 {
1442     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1443     WICPngFilterOption filter;
1444     BOOL interlace;
1445     PROPBAG2 opts[2]= {{0}};
1446     VARIANT opt_values[2];
1447     HRESULT opt_hres[2];
1448     HRESULT hr;
1449 
1450     TRACE("(%p,%p)\n", iface, pIEncoderOptions);
1451 
1452     opts[0].pstrName = (LPOLESTR)wszPngInterlaceOption;
1453     opts[0].vt = VT_BOOL;
1454     opts[1].pstrName = (LPOLESTR)wszPngFilterOption;
1455     opts[1].vt = VT_UI1;
1456 
1457     if (pIEncoderOptions)
1458     {
1459         hr = IPropertyBag2_Read(pIEncoderOptions, sizeof(opts)/sizeof(opts[0]), opts, NULL, opt_values, opt_hres);
1460 
1461         if (FAILED(hr))
1462             return hr;
1463 
1464         if (V_VT(&opt_values[0]) == VT_EMPTY)
1465             interlace = FALSE;
1466         else
1467             interlace = (V_BOOL(&opt_values[0]) != 0);
1468 
1469         filter = V_UI1(&opt_values[1]);
1470         if (filter > WICPngFilterAdaptive)
1471         {
1472             WARN("Unrecognized filter option value %u.\n", filter);
1473             filter = WICPngFilterUnspecified;
1474         }
1475     }
1476     else
1477     {
1478         interlace = FALSE;
1479         filter = WICPngFilterUnspecified;
1480     }
1481 
1482     EnterCriticalSection(&This->lock);
1483 
1484     if (This->frame_initialized)
1485     {
1486         LeaveCriticalSection(&This->lock);
1487         return WINCODEC_ERR_WRONGSTATE;
1488     }
1489 
1490     This->interlace = interlace;
1491     This->filter = filter;
1492 
1493     This->frame_initialized = TRUE;
1494 
1495     LeaveCriticalSection(&This->lock);
1496 
1497     return S_OK;
1498 }
1499 
1500 static HRESULT WINAPI PngFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
1501     UINT uiWidth, UINT uiHeight)
1502 {
1503     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1504     TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
1505 
1506     EnterCriticalSection(&This->lock);
1507 
1508     if (!This->frame_initialized || This->info_written)
1509     {
1510         LeaveCriticalSection(&This->lock);
1511         return WINCODEC_ERR_WRONGSTATE;
1512     }
1513 
1514     This->width = uiWidth;
1515     This->height = uiHeight;
1516 
1517     LeaveCriticalSection(&This->lock);
1518 
1519     return S_OK;
1520 }
1521 
1522 static HRESULT WINAPI PngFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
1523     double dpiX, double dpiY)
1524 {
1525     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1526     TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
1527 
1528     EnterCriticalSection(&This->lock);
1529 
1530     if (!This->frame_initialized || This->info_written)
1531     {
1532         LeaveCriticalSection(&This->lock);
1533         return WINCODEC_ERR_WRONGSTATE;
1534     }
1535 
1536     This->xres = dpiX;
1537     This->yres = dpiY;
1538 
1539     LeaveCriticalSection(&This->lock);
1540 
1541     return S_OK;
1542 }
1543 
1544 static HRESULT WINAPI PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
1545     WICPixelFormatGUID *pPixelFormat)
1546 {
1547     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1548     int i;
1549     TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
1550 
1551     EnterCriticalSection(&This->lock);
1552 
1553     if (!This->frame_initialized || This->info_written)
1554     {
1555         LeaveCriticalSection(&This->lock);
1556         return WINCODEC_ERR_WRONGSTATE;
1557     }
1558 
1559     for (i=0; formats[i].guid; i++)
1560     {
1561         if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
1562             break;
1563     }
1564 
1565     if (!formats[i].guid) i = 0;
1566 
1567     This->format = &formats[i];
1568     memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
1569 
1570     LeaveCriticalSection(&This->lock);
1571 
1572     return S_OK;
1573 }
1574 
1575 static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
1576     UINT cCount, IWICColorContext **ppIColorContext)
1577 {
1578     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1579     return E_NOTIMPL;
1580 }
1581 
1582 static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
1583     IWICPalette *palette)
1584 {
1585     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1586     HRESULT hr;
1587 
1588     TRACE("(%p,%p)\n", iface, palette);
1589 
1590     if (!palette) return E_INVALIDARG;
1591 
1592     EnterCriticalSection(&This->lock);
1593 
1594     if (This->frame_initialized)
1595         hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors);
1596     else
1597         hr = WINCODEC_ERR_NOTINITIALIZED;
1598 
1599     LeaveCriticalSection(&This->lock);
1600     return hr;
1601 }
1602 
1603 static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
1604     IWICBitmapSource *pIThumbnail)
1605 {
1606     FIXME("(%p,%p): stub\n", iface, pIThumbnail);
1607     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1608 }
1609 
1610 static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
1611     UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
1612 {
1613     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1614     png_byte **row_pointers=NULL;
1615     UINT i;
1616     jmp_buf jmpbuf;
1617     TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
1618 
1619     EnterCriticalSection(&This->lock);
1620 
1621     if (!This->frame_initialized || !This->width || !This->height || !This->format)
1622     {
1623         LeaveCriticalSection(&This->lock);
1624         return WINCODEC_ERR_WRONGSTATE;
1625     }
1626 
1627     if (lineCount == 0 || lineCount + This->lines_written > This->height)
1628     {
1629         LeaveCriticalSection(&This->lock);
1630         return E_INVALIDARG;
1631     }
1632 
1633     /* set up setjmp/longjmp error handling */
1634     if (setjmp(jmpbuf))
1635     {
1636         LeaveCriticalSection(&This->lock);
1637         HeapFree(GetProcessHeap(), 0, row_pointers);
1638         return E_FAIL;
1639     }
1640     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1641 
1642     if (!This->info_written)
1643     {
1644         if (This->interlace)
1645         {
1646             /* libpng requires us to write all data multiple times in this case. */
1647             This->stride = (This->format->bpp * This->width + 7)/8;
1648             This->data = HeapAlloc(GetProcessHeap(), 0, This->height * This->stride);
1649             if (!This->data)
1650             {
1651                 LeaveCriticalSection(&This->lock);
1652                 return E_OUTOFMEMORY;
1653             }
1654         }
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,%p)\n", iface, pIBitmapSource, 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,
1987     GUID *pguidContainerFormat)
1988 {
1989     FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
1990     return E_NOTIMPL;
1991 }
1992 
1993 static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info)
1994 {
1995     IWICComponentInfo *comp_info;
1996     HRESULT hr;
1997 
1998     TRACE("%p,%p\n", iface, info);
1999 
2000     if (!info) return E_INVALIDARG;
2001 
2002     hr = CreateComponentInfo(&CLSID_WICPngEncoder, &comp_info);
2003     if (hr == S_OK)
2004     {
2005         hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info);
2006         IWICComponentInfo_Release(comp_info);
2007     }
2008     return hr;
2009 }
2010 
2011 static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface,
2012     UINT cCount, IWICColorContext **ppIColorContext)
2013 {
2014     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
2015     return E_NOTIMPL;
2016 }
2017 
2018 static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette)
2019 {
2020     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2021     HRESULT hr;
2022 
2023     TRACE("(%p,%p)\n", iface, palette);
2024 
2025     EnterCriticalSection(&This->lock);
2026 
2027     hr = This->stream ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED;
2028 
2029     LeaveCriticalSection(&This->lock);
2030 
2031     return hr;
2032 }
2033 
2034 static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
2035 {
2036     TRACE("(%p,%p)\n", iface, pIThumbnail);
2037     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2038 }
2039 
2040 static HRESULT WINAPI PngEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
2041 {
2042     TRACE("(%p,%p)\n", iface, pIPreview);
2043     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2044 }
2045 
2046 static HRESULT WINAPI PngEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
2047     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
2048 {
2049     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2050     HRESULT hr;
2051     static const PROPBAG2 opts[2] =
2052     {
2053         { PROPBAG2_TYPE_DATA, VT_BOOL, 0, 0, (LPOLESTR)wszPngInterlaceOption },
2054         { PROPBAG2_TYPE_DATA, VT_UI1,  0, 0, (LPOLESTR)wszPngFilterOption },
2055     };
2056 
2057     TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
2058 
2059     EnterCriticalSection(&This->lock);
2060 
2061     if (This->frame_count != 0)
2062     {
2063         LeaveCriticalSection(&This->lock);
2064         return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2065     }
2066 
2067     if (!This->stream)
2068     {
2069         LeaveCriticalSection(&This->lock);
2070         return WINCODEC_ERR_NOTINITIALIZED;
2071     }
2072 
2073     if (ppIEncoderOptions)
2074     {
2075         hr = CreatePropertyBag2(opts, sizeof(opts)/sizeof(opts[0]), ppIEncoderOptions);
2076         if (FAILED(hr))
2077         {
2078             LeaveCriticalSection(&This->lock);
2079             return hr;
2080         }
2081     }
2082 
2083     This->frame_count = 1;
2084 
2085     LeaveCriticalSection(&This->lock);
2086 
2087     IWICBitmapEncoder_AddRef(iface);
2088     *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface;
2089 
2090     return S_OK;
2091 }
2092 
2093 static HRESULT WINAPI PngEncoder_Commit(IWICBitmapEncoder *iface)
2094 {
2095     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2096     TRACE("(%p)\n", iface);
2097 
2098     EnterCriticalSection(&This->lock);
2099 
2100     if (!This->frame_committed || This->committed)
2101     {
2102         LeaveCriticalSection(&This->lock);
2103         return WINCODEC_ERR_WRONGSTATE;
2104     }
2105 
2106     This->committed = TRUE;
2107 
2108     LeaveCriticalSection(&This->lock);
2109 
2110     return S_OK;
2111 }
2112 
2113 static HRESULT WINAPI PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
2114     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
2115 {
2116     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
2117     return E_NOTIMPL;
2118 }
2119 
2120 static const IWICBitmapEncoderVtbl PngEncoder_Vtbl = {
2121     PngEncoder_QueryInterface,
2122     PngEncoder_AddRef,
2123     PngEncoder_Release,
2124     PngEncoder_Initialize,
2125     PngEncoder_GetContainerFormat,
2126     PngEncoder_GetEncoderInfo,
2127     PngEncoder_SetColorContexts,
2128     PngEncoder_SetPalette,
2129     PngEncoder_SetThumbnail,
2130     PngEncoder_SetPreview,
2131     PngEncoder_CreateNewFrame,
2132     PngEncoder_Commit,
2133     PngEncoder_GetMetadataQueryWriter
2134 };
2135 
2136 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
2137 {
2138     PngEncoder *This;
2139     HRESULT ret;
2140 
2141     TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
2142 
2143     *ppv = NULL;
2144 
2145     if (!load_libpng())
2146     {
2147         ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG);
2148         return E_FAIL;
2149     }
2150 
2151     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder));
2152     if (!This) return E_OUTOFMEMORY;
2153 
2154     This->IWICBitmapEncoder_iface.lpVtbl = &PngEncoder_Vtbl;
2155     This->IWICBitmapFrameEncode_iface.lpVtbl = &PngEncoder_FrameVtbl;
2156     This->ref = 1;
2157     This->png_ptr = NULL;
2158     This->info_ptr = NULL;
2159     This->stream = NULL;
2160     This->frame_count = 0;
2161     This->frame_initialized = FALSE;
2162     This->format = NULL;
2163     This->info_written = FALSE;
2164     This->width = 0;
2165     This->height = 0;
2166     This->xres = 0.0;
2167     This->yres = 0.0;
2168     This->lines_written = 0;
2169     This->frame_committed = FALSE;
2170     This->committed = FALSE;
2171     This->data = NULL;
2172     This->colors = 0;
2173     InitializeCriticalSection(&This->lock);
2174     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock");
2175 
2176     ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
2177     IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
2178 
2179     return ret;
2180 }
2181 
2182 #else /* !HAVE_PNG_H */
2183 
2184 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
2185 {
2186     ERR("Trying to load PNG picture, but PNG support is not compiled in.\n");
2187     return E_FAIL;
2188 }
2189 
2190 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
2191 {
2192     ERR("Trying to save PNG picture, but PNG support is not compiled in.\n");
2193     return E_FAIL;
2194 }
2195 
2196 #endif
2197