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