windowscodecs: Implement IWICBitmapFrameDecode::GetColorContexts for PNG images.
[wine] / dlls / windowscodecs / pngformat.c
1 /*
2  * Copyright 2009 Vincent Povirk for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20 #include "wine/port.h"
21
22 #include <stdarg.h>
23
24 #ifdef HAVE_PNG_H
25 #include <png.h>
26 #endif
27
28 #define NONAMELESSUNION
29 #define COBJMACROS
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "objbase.h"
34 #include "wincodec.h"
35 #include "wincodecsdk.h"
36
37 #include "wincodecs_private.h"
38
39 #include "wine/debug.h"
40 #include "wine/library.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
43
44 static HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_size)
45 {
46     BYTE header[8];
47     HRESULT hr;
48     ULONG bytesread;
49
50     hr = IStream_Read(stream, header, 8, &bytesread);
51     if (FAILED(hr) || bytesread < 8)
52     {
53         if (SUCCEEDED(hr))
54             hr = E_FAIL;
55         return hr;
56     }
57
58     *data_size = header[0] << 24 | header[1] << 16 | header[2] << 8 | header[3];
59
60     memcpy(type, &header[4], 4);
61
62     if (data)
63     {
64         *data = HeapAlloc(GetProcessHeap(), 0, *data_size);
65         if (!*data)
66             return E_OUTOFMEMORY;
67
68         hr = IStream_Read(stream, *data, *data_size, &bytesread);
69
70         if (FAILED(hr) || bytesread < *data_size)
71         {
72             if (SUCCEEDED(hr))
73                 hr = E_FAIL;
74             HeapFree(GetProcessHeap(), 0, *data);
75             *data = NULL;
76             return hr;
77         }
78
79         /* FIXME: Verify the CRC? */
80     }
81
82     return S_OK;
83 }
84
85 static HRESULT LoadTextMetadata(IStream *stream, const GUID *preferred_vendor,
86     DWORD persist_options, MetadataItem **items, DWORD *item_count)
87 {
88     HRESULT hr;
89     BYTE type[4];
90     BYTE *data;
91     ULONG data_size;
92     ULONG name_len, value_len;
93     BYTE *name_end_ptr;
94     LPSTR name, value;
95     MetadataItem *result;
96
97     hr = read_png_chunk(stream, type, &data, &data_size);
98     if (FAILED(hr)) return hr;
99
100     name_end_ptr = memchr(data, 0, data_size);
101
102     name_len = name_end_ptr - data;
103
104     if (!name_end_ptr || name_len > 79)
105     {
106         HeapFree(GetProcessHeap(), 0, data);
107         return E_FAIL;
108     }
109
110     value_len = data_size - name_len - 1;
111
112     result = HeapAlloc(GetProcessHeap(), 0, sizeof(MetadataItem));
113     name = HeapAlloc(GetProcessHeap(), 0, name_len + 1);
114     value = HeapAlloc(GetProcessHeap(), 0, value_len + 1);
115     if (!result || !name || !value)
116     {
117         HeapFree(GetProcessHeap(), 0, data);
118         HeapFree(GetProcessHeap(), 0, result);
119         HeapFree(GetProcessHeap(), 0, name);
120         HeapFree(GetProcessHeap(), 0, value);
121         return E_OUTOFMEMORY;
122     }
123
124     PropVariantInit(&result[0].schema);
125     PropVariantInit(&result[0].id);
126     PropVariantInit(&result[0].value);
127
128     memcpy(name, data, name_len + 1);
129     memcpy(value, name_end_ptr + 1, value_len);
130     value[value_len] = 0;
131
132     result[0].id.vt = VT_LPSTR;
133     result[0].id.u.pszVal = name;
134     result[0].value.vt = VT_LPSTR;
135     result[0].value.u.pszVal = value;
136
137     *items = result;
138     *item_count = 1;
139
140     HeapFree(GetProcessHeap(), 0, data);
141
142     return S_OK;
143 }
144
145 static const MetadataHandlerVtbl TextReader_Vtbl = {
146     0,
147     &CLSID_WICPngTextMetadataReader,
148     LoadTextMetadata
149 };
150
151 HRESULT PngTextReader_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
152 {
153     return MetadataReader_Create(&TextReader_Vtbl, pUnkOuter, iid, ppv);
154 }
155
156 #ifdef SONAME_LIBPNG
157
158 static void *libpng_handle;
159 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
160 MAKE_FUNCPTR(png_create_read_struct);
161 MAKE_FUNCPTR(png_create_info_struct);
162 MAKE_FUNCPTR(png_create_write_struct);
163 MAKE_FUNCPTR(png_destroy_read_struct);
164 MAKE_FUNCPTR(png_destroy_write_struct);
165 MAKE_FUNCPTR(png_error);
166 MAKE_FUNCPTR(png_get_bit_depth);
167 MAKE_FUNCPTR(png_get_color_type);
168 MAKE_FUNCPTR(png_get_error_ptr);
169 MAKE_FUNCPTR(png_get_iCCP);
170 MAKE_FUNCPTR(png_get_image_height);
171 MAKE_FUNCPTR(png_get_image_width);
172 MAKE_FUNCPTR(png_get_io_ptr);
173 MAKE_FUNCPTR(png_get_pHYs);
174 MAKE_FUNCPTR(png_get_PLTE);
175 MAKE_FUNCPTR(png_get_tRNS);
176 MAKE_FUNCPTR(png_set_bgr);
177 MAKE_FUNCPTR(png_set_error_fn);
178 #if HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8
179 MAKE_FUNCPTR(png_set_expand_gray_1_2_4_to_8);
180 #else
181 MAKE_FUNCPTR(png_set_gray_1_2_4_to_8);
182 #endif
183 MAKE_FUNCPTR(png_set_filler);
184 MAKE_FUNCPTR(png_set_gray_to_rgb);
185 MAKE_FUNCPTR(png_set_IHDR);
186 MAKE_FUNCPTR(png_set_pHYs);
187 MAKE_FUNCPTR(png_set_read_fn);
188 MAKE_FUNCPTR(png_set_strip_16);
189 MAKE_FUNCPTR(png_set_tRNS_to_alpha);
190 MAKE_FUNCPTR(png_set_write_fn);
191 MAKE_FUNCPTR(png_read_end);
192 MAKE_FUNCPTR(png_read_image);
193 MAKE_FUNCPTR(png_read_info);
194 MAKE_FUNCPTR(png_write_end);
195 MAKE_FUNCPTR(png_write_info);
196 MAKE_FUNCPTR(png_write_rows);
197 #undef MAKE_FUNCPTR
198
199 static void *load_libpng(void)
200 {
201     if((libpng_handle = wine_dlopen(SONAME_LIBPNG, RTLD_NOW, NULL, 0)) != NULL) {
202
203 #define LOAD_FUNCPTR(f) \
204     if((p##f = wine_dlsym(libpng_handle, #f, NULL, 0)) == NULL) { \
205         libpng_handle = NULL; \
206         return NULL; \
207     }
208         LOAD_FUNCPTR(png_create_read_struct);
209         LOAD_FUNCPTR(png_create_info_struct);
210         LOAD_FUNCPTR(png_create_write_struct);
211         LOAD_FUNCPTR(png_destroy_read_struct);
212         LOAD_FUNCPTR(png_destroy_write_struct);
213         LOAD_FUNCPTR(png_error);
214         LOAD_FUNCPTR(png_get_bit_depth);
215         LOAD_FUNCPTR(png_get_color_type);
216         LOAD_FUNCPTR(png_get_error_ptr);
217         LOAD_FUNCPTR(png_get_iCCP);
218         LOAD_FUNCPTR(png_get_image_height);
219         LOAD_FUNCPTR(png_get_image_width);
220         LOAD_FUNCPTR(png_get_io_ptr);
221         LOAD_FUNCPTR(png_get_pHYs);
222         LOAD_FUNCPTR(png_get_PLTE);
223         LOAD_FUNCPTR(png_get_tRNS);
224         LOAD_FUNCPTR(png_set_bgr);
225         LOAD_FUNCPTR(png_set_error_fn);
226 #if HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8
227         LOAD_FUNCPTR(png_set_expand_gray_1_2_4_to_8);
228 #else
229         LOAD_FUNCPTR(png_set_gray_1_2_4_to_8);
230 #endif
231         LOAD_FUNCPTR(png_set_filler);
232         LOAD_FUNCPTR(png_set_gray_to_rgb);
233         LOAD_FUNCPTR(png_set_IHDR);
234         LOAD_FUNCPTR(png_set_pHYs);
235         LOAD_FUNCPTR(png_set_read_fn);
236         LOAD_FUNCPTR(png_set_strip_16);
237         LOAD_FUNCPTR(png_set_tRNS_to_alpha);
238         LOAD_FUNCPTR(png_set_write_fn);
239         LOAD_FUNCPTR(png_read_end);
240         LOAD_FUNCPTR(png_read_image);
241         LOAD_FUNCPTR(png_read_info);
242         LOAD_FUNCPTR(png_write_end);
243         LOAD_FUNCPTR(png_write_info);
244         LOAD_FUNCPTR(png_write_rows);
245
246 #undef LOAD_FUNCPTR
247     }
248     return libpng_handle;
249 }
250
251 static void user_error_fn(png_structp png_ptr, png_const_charp error_message)
252 {
253     jmp_buf *pjmpbuf;
254
255     /* This uses setjmp/longjmp just like the default. We can't use the
256      * default because there's no way to access the jmp buffer in the png_struct
257      * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
258     WARN("PNG error: %s\n", debugstr_a(error_message));
259     pjmpbuf = ppng_get_error_ptr(png_ptr);
260     longjmp(*pjmpbuf, 1);
261 }
262
263 static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message)
264 {
265     WARN("PNG warning: %s\n", debugstr_a(warning_message));
266 }
267
268 typedef struct {
269     IWICBitmapDecoder IWICBitmapDecoder_iface;
270     IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
271     IWICMetadataBlockReader IWICMetadataBlockReader_iface;
272     LONG ref;
273     png_structp png_ptr;
274     png_infop info_ptr;
275     png_infop end_info;
276     BOOL initialized;
277     int bpp;
278     int width, height;
279     UINT stride;
280     const WICPixelFormatGUID *format;
281     BYTE *image_bits;
282     CRITICAL_SECTION lock; /* must be held when png structures are accessed or initialized is set */
283 } PngDecoder;
284
285 static inline PngDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
286 {
287     return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapDecoder_iface);
288 }
289
290 static inline PngDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
291 {
292     return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapFrameDecode_iface);
293 }
294
295 static inline PngDecoder *impl_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface)
296 {
297     return CONTAINING_RECORD(iface, PngDecoder, IWICMetadataBlockReader_iface);
298 }
299
300 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl;
301
302 static HRESULT WINAPI PngDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
303     void **ppv)
304 {
305     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
306     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
307
308     if (!ppv) return E_INVALIDARG;
309
310     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
311     {
312         *ppv = &This->IWICBitmapDecoder_iface;
313     }
314     else
315     {
316         *ppv = NULL;
317         return E_NOINTERFACE;
318     }
319
320     IUnknown_AddRef((IUnknown*)*ppv);
321     return S_OK;
322 }
323
324 static ULONG WINAPI PngDecoder_AddRef(IWICBitmapDecoder *iface)
325 {
326     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
327     ULONG ref = InterlockedIncrement(&This->ref);
328
329     TRACE("(%p) refcount=%u\n", iface, ref);
330
331     return ref;
332 }
333
334 static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface)
335 {
336     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
337     ULONG ref = InterlockedDecrement(&This->ref);
338
339     TRACE("(%p) refcount=%u\n", iface, ref);
340
341     if (ref == 0)
342     {
343         if (This->png_ptr)
344             ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
345         This->lock.DebugInfo->Spare[0] = 0;
346         DeleteCriticalSection(&This->lock);
347         HeapFree(GetProcessHeap(), 0, This->image_bits);
348         HeapFree(GetProcessHeap(), 0, This);
349     }
350
351     return ref;
352 }
353
354 static HRESULT WINAPI PngDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
355     DWORD *pdwCapability)
356 {
357     FIXME("(%p,%p,%p): stub\n", iface, pIStream, pdwCapability);
358     return E_NOTIMPL;
359 }
360
361 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
362 {
363     IStream *stream = ppng_get_io_ptr(png_ptr);
364     HRESULT hr;
365     ULONG bytesread;
366
367     hr = IStream_Read(stream, data, length, &bytesread);
368     if (FAILED(hr) || bytesread != length)
369     {
370         ppng_error(png_ptr, "failed reading data");
371     }
372 }
373
374 static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
375     WICDecodeOptions cacheOptions)
376 {
377     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
378     LARGE_INTEGER seek;
379     HRESULT hr=S_OK;
380     png_bytep *row_pointers=NULL;
381     UINT image_size;
382     UINT i;
383     int color_type, bit_depth;
384     png_bytep trans;
385     int num_trans;
386     png_uint_32 transparency;
387     png_color_16p trans_values;
388     jmp_buf jmpbuf;
389
390     TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
391
392     EnterCriticalSection(&This->lock);
393
394     /* initialize libpng */
395     This->png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
396     if (!This->png_ptr)
397     {
398         hr = E_FAIL;
399         goto end;
400     }
401
402     This->info_ptr = ppng_create_info_struct(This->png_ptr);
403     if (!This->info_ptr)
404     {
405         ppng_destroy_read_struct(&This->png_ptr, NULL, NULL);
406         This->png_ptr = NULL;
407         hr = E_FAIL;
408         goto end;
409     }
410
411     This->end_info = ppng_create_info_struct(This->png_ptr);
412     if (!This->info_ptr)
413     {
414         ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, NULL);
415         This->png_ptr = NULL;
416         hr = E_FAIL;
417         goto end;
418     }
419
420     /* set up setjmp/longjmp error handling */
421     if (setjmp(jmpbuf))
422     {
423         ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
424         HeapFree(GetProcessHeap(), 0, row_pointers);
425         This->png_ptr = NULL;
426         hr = E_FAIL;
427         goto end;
428     }
429     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
430
431     /* seek to the start of the stream */
432     seek.QuadPart = 0;
433     hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
434     if (FAILED(hr)) goto end;
435
436     /* set up custom i/o handling */
437     ppng_set_read_fn(This->png_ptr, pIStream, user_read_data);
438
439     /* read the header */
440     ppng_read_info(This->png_ptr, This->info_ptr);
441
442     /* choose a pixel format */
443     color_type = ppng_get_color_type(This->png_ptr, This->info_ptr);
444     bit_depth = ppng_get_bit_depth(This->png_ptr, This->info_ptr);
445
446     /* check for color-keyed alpha */
447     transparency = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans, &num_trans, &trans_values);
448
449     if (transparency && color_type != PNG_COLOR_TYPE_PALETTE)
450     {
451         /* expand to RGBA */
452         if (color_type == PNG_COLOR_TYPE_GRAY)
453         {
454             if (bit_depth < 8)
455             {
456 #if HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8
457                 ppng_set_expand_gray_1_2_4_to_8(This->png_ptr);
458 #else
459                 ppng_set_gray_1_2_4_to_8(This->png_ptr);
460 #endif
461                 bit_depth = 8;
462             }
463             ppng_set_gray_to_rgb(This->png_ptr);
464         }
465         ppng_set_tRNS_to_alpha(This->png_ptr);
466         color_type = PNG_COLOR_TYPE_RGB_ALPHA;
467     }
468
469     switch (color_type)
470     {
471     case PNG_COLOR_TYPE_GRAY:
472         This->bpp = bit_depth;
473         switch (bit_depth)
474         {
475         case 1: This->format = &GUID_WICPixelFormatBlackWhite; break;
476         case 2: This->format = &GUID_WICPixelFormat2bppGray; break;
477         case 4: This->format = &GUID_WICPixelFormat4bppGray; break;
478         case 8: This->format = &GUID_WICPixelFormat8bppGray; break;
479         case 16: This->format = &GUID_WICPixelFormat16bppGray; break;
480         default:
481             ERR("invalid grayscale bit depth: %i\n", bit_depth);
482             hr = E_FAIL;
483             goto end;
484         }
485         break;
486     case PNG_COLOR_TYPE_GRAY_ALPHA:
487         /* WIC does not support grayscale alpha formats so use RGBA */
488         ppng_set_gray_to_rgb(This->png_ptr);
489         /* fall through */
490     case PNG_COLOR_TYPE_RGB_ALPHA:
491         This->bpp = bit_depth * 4;
492         switch (bit_depth)
493         {
494         case 8:
495             ppng_set_bgr(This->png_ptr);
496             This->format = &GUID_WICPixelFormat32bppBGRA;
497             break;
498         case 16: This->format = &GUID_WICPixelFormat64bppRGBA; break;
499         default:
500             ERR("invalid RGBA bit depth: %i\n", bit_depth);
501             hr = E_FAIL;
502             goto end;
503         }
504         break;
505     case PNG_COLOR_TYPE_PALETTE:
506         This->bpp = bit_depth;
507         switch (bit_depth)
508         {
509         case 1: This->format = &GUID_WICPixelFormat1bppIndexed; break;
510         case 2: This->format = &GUID_WICPixelFormat2bppIndexed; break;
511         case 4: This->format = &GUID_WICPixelFormat4bppIndexed; break;
512         case 8: This->format = &GUID_WICPixelFormat8bppIndexed; break;
513         default:
514             ERR("invalid indexed color bit depth: %i\n", bit_depth);
515             hr = E_FAIL;
516             goto end;
517         }
518         break;
519     case PNG_COLOR_TYPE_RGB:
520         This->bpp = bit_depth * 3;
521         switch (bit_depth)
522         {
523         case 8:
524             ppng_set_bgr(This->png_ptr);
525             This->format = &GUID_WICPixelFormat24bppBGR;
526             break;
527         case 16: This->format = &GUID_WICPixelFormat48bppRGB; break;
528         default:
529             ERR("invalid RGB color bit depth: %i\n", bit_depth);
530             hr = E_FAIL;
531             goto end;
532         }
533         break;
534     default:
535         ERR("invalid color type %i\n", color_type);
536         hr = E_FAIL;
537         goto end;
538     }
539
540     /* read the image data */
541     This->width = ppng_get_image_width(This->png_ptr, This->info_ptr);
542     This->height = ppng_get_image_height(This->png_ptr, This->info_ptr);
543     This->stride = This->width * This->bpp;
544     image_size = This->stride * This->height;
545
546     This->image_bits = HeapAlloc(GetProcessHeap(), 0, image_size);
547     if (!This->image_bits)
548     {
549         hr = E_OUTOFMEMORY;
550         goto end;
551     }
552
553     row_pointers = HeapAlloc(GetProcessHeap(), 0, sizeof(png_bytep)*This->height);
554     if (!row_pointers)
555     {
556         hr = E_OUTOFMEMORY;
557         goto end;
558     }
559
560     for (i=0; i<This->height; i++)
561         row_pointers[i] = This->image_bits + i * This->stride;
562
563     ppng_read_image(This->png_ptr, row_pointers);
564
565     HeapFree(GetProcessHeap(), 0, row_pointers);
566     row_pointers = NULL;
567
568     ppng_read_end(This->png_ptr, This->end_info);
569
570     This->initialized = TRUE;
571
572 end:
573
574     LeaveCriticalSection(&This->lock);
575
576     return hr;
577 }
578
579 static HRESULT WINAPI PngDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
580     GUID *pguidContainerFormat)
581 {
582     memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
583     return S_OK;
584 }
585
586 static HRESULT WINAPI PngDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
587     IWICBitmapDecoderInfo **ppIDecoderInfo)
588 {
589     HRESULT hr;
590     IWICComponentInfo *compinfo;
591
592     TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
593
594     hr = CreateComponentInfo(&CLSID_WICPngDecoder, &compinfo);
595     if (FAILED(hr)) return hr;
596
597     hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
598         (void**)ppIDecoderInfo);
599
600     IWICComponentInfo_Release(compinfo);
601
602     return hr;
603 }
604
605 static HRESULT WINAPI PngDecoder_CopyPalette(IWICBitmapDecoder *iface,
606     IWICPalette *pIPalette)
607 {
608     FIXME("(%p,%p): stub\n", iface, pIPalette);
609     return E_NOTIMPL;
610 }
611
612 static HRESULT WINAPI PngDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
613     IWICMetadataQueryReader **ppIMetadataQueryReader)
614 {
615     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
616     return E_NOTIMPL;
617 }
618
619 static HRESULT WINAPI PngDecoder_GetPreview(IWICBitmapDecoder *iface,
620     IWICBitmapSource **ppIBitmapSource)
621 {
622     FIXME("(%p,%p): stub\n", iface, ppIBitmapSource);
623     return E_NOTIMPL;
624 }
625
626 static HRESULT WINAPI PngDecoder_GetColorContexts(IWICBitmapDecoder *iface,
627     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
628 {
629     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
630     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
631 }
632
633 static HRESULT WINAPI PngDecoder_GetThumbnail(IWICBitmapDecoder *iface,
634     IWICBitmapSource **ppIThumbnail)
635 {
636     TRACE("(%p,%p)\n", iface, ppIThumbnail);
637     return WINCODEC_ERR_CODECNOTHUMBNAIL;
638 }
639
640 static HRESULT WINAPI PngDecoder_GetFrameCount(IWICBitmapDecoder *iface,
641     UINT *pCount)
642 {
643     *pCount = 1;
644     return S_OK;
645 }
646
647 static HRESULT WINAPI PngDecoder_GetFrame(IWICBitmapDecoder *iface,
648     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
649 {
650     PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
651     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
652
653     if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
654
655     if (index != 0) return E_INVALIDARG;
656
657     IWICBitmapDecoder_AddRef(iface);
658
659     *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface;
660
661     return S_OK;
662 }
663
664 static const IWICBitmapDecoderVtbl PngDecoder_Vtbl = {
665     PngDecoder_QueryInterface,
666     PngDecoder_AddRef,
667     PngDecoder_Release,
668     PngDecoder_QueryCapability,
669     PngDecoder_Initialize,
670     PngDecoder_GetContainerFormat,
671     PngDecoder_GetDecoderInfo,
672     PngDecoder_CopyPalette,
673     PngDecoder_GetMetadataQueryReader,
674     PngDecoder_GetPreview,
675     PngDecoder_GetColorContexts,
676     PngDecoder_GetThumbnail,
677     PngDecoder_GetFrameCount,
678     PngDecoder_GetFrame
679 };
680
681 static HRESULT WINAPI PngDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
682     void **ppv)
683 {
684     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
685     if (!ppv) return E_INVALIDARG;
686
687     if (IsEqualIID(&IID_IUnknown, iid) ||
688         IsEqualIID(&IID_IWICBitmapSource, iid) ||
689         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
690     {
691         *ppv = &This->IWICBitmapFrameDecode_iface;
692     }
693     else if (IsEqualIID(&IID_IWICMetadataBlockReader, iid))
694     {
695         *ppv = &This->IWICMetadataBlockReader_iface;
696     }
697     else
698     {
699         *ppv = NULL;
700         return E_NOINTERFACE;
701     }
702
703     IUnknown_AddRef((IUnknown*)*ppv);
704     return S_OK;
705 }
706
707 static ULONG WINAPI PngDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface)
708 {
709     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
710     return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
711 }
712
713 static ULONG WINAPI PngDecoder_Frame_Release(IWICBitmapFrameDecode *iface)
714 {
715     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
716     return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
717 }
718
719 static HRESULT WINAPI PngDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface,
720     UINT *puiWidth, UINT *puiHeight)
721 {
722     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
723     *puiWidth = This->width;
724     *puiHeight = This->height;
725     TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight);
726     return S_OK;
727 }
728
729 static HRESULT WINAPI PngDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface,
730     WICPixelFormatGUID *pPixelFormat)
731 {
732     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
733     TRACE("(%p,%p)\n", iface, pPixelFormat);
734
735     memcpy(pPixelFormat, This->format, sizeof(GUID));
736
737     return S_OK;
738 }
739
740 static HRESULT WINAPI PngDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface,
741     double *pDpiX, double *pDpiY)
742 {
743     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
744     png_uint_32 ret, xres, yres;
745     int unit_type;
746
747     EnterCriticalSection(&This->lock);
748
749     ret = ppng_get_pHYs(This->png_ptr, This->info_ptr, &xres, &yres, &unit_type);
750
751     if (ret && unit_type == PNG_RESOLUTION_METER)
752     {
753         *pDpiX = xres * 0.0254;
754         *pDpiY = yres * 0.0254;
755     }
756     else
757     {
758         WARN("no pHYs block present\n");
759         *pDpiX = *pDpiY = 96.0;
760     }
761
762     LeaveCriticalSection(&This->lock);
763
764     TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY);
765
766     return S_OK;
767 }
768
769 static HRESULT WINAPI PngDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface,
770     IWICPalette *pIPalette)
771 {
772     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
773     png_uint_32 ret;
774     png_colorp png_palette;
775     int num_palette;
776     WICColor palette[256];
777     png_bytep trans;
778     int num_trans;
779     png_color_16p trans_values;
780     int i;
781     HRESULT hr=S_OK;
782
783     TRACE("(%p,%p)\n", iface, pIPalette);
784
785     EnterCriticalSection(&This->lock);
786
787     ret = ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette);
788     if (!ret)
789     {
790         hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
791         goto end;
792     }
793
794     if (num_palette > 256)
795     {
796         ERR("palette has %i colors?!\n", num_palette);
797         hr = E_FAIL;
798         goto end;
799     }
800
801     for (i=0; i<num_palette; i++)
802     {
803         palette[i] = (0xff000000|
804                       png_palette[i].red << 16|
805                       png_palette[i].green << 8|
806                       png_palette[i].blue);
807     }
808
809     ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans, &num_trans, &trans_values);
810     if (ret)
811     {
812         for (i=0; i<num_trans; i++)
813         {
814             palette[trans[i]] = 0x00000000;
815         }
816     }
817
818 end:
819
820     LeaveCriticalSection(&This->lock);
821
822     if (SUCCEEDED(hr))
823         hr = IWICPalette_InitializeCustom(pIPalette, palette, num_palette);
824
825     return hr;
826 }
827
828 static HRESULT WINAPI PngDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface,
829     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
830 {
831     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
832     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
833
834     return copy_pixels(This->bpp, This->image_bits,
835         This->width, This->height, This->stride,
836         prc, cbStride, cbBufferSize, pbBuffer);
837 }
838
839 static HRESULT WINAPI PngDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
840     IWICMetadataQueryReader **ppIMetadataQueryReader)
841 {
842     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
843     return E_NOTIMPL;
844 }
845
846 static HRESULT WINAPI PngDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface,
847     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
848 {
849     PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
850     png_charp name, profile;
851     png_uint_32 len;
852     int compression_type;
853     HRESULT hr;
854
855     TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
856
857     EnterCriticalSection(&This->lock);
858
859     if (ppng_get_iCCP(This->png_ptr, This->info_ptr, &name, &compression_type, &profile, &len))
860     {
861         if (cCount && ppIColorContexts)
862         {
863             hr = IWICColorContext_InitializeFromMemory(*ppIColorContexts, (const BYTE *)profile, len);
864             if (FAILED(hr))
865             {
866                 LeaveCriticalSection(&This->lock);
867                 return hr;
868             }
869         }
870         *pcActualCount = 1;
871     }
872     else
873         *pcActualCount = 0;
874
875     LeaveCriticalSection(&This->lock);
876
877     return S_OK;
878 }
879
880 static HRESULT WINAPI PngDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
881     IWICBitmapSource **ppIThumbnail)
882 {
883     FIXME("(%p,%p): stub\n", iface, ppIThumbnail);
884     return E_NOTIMPL;
885 }
886
887 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl = {
888     PngDecoder_Frame_QueryInterface,
889     PngDecoder_Frame_AddRef,
890     PngDecoder_Frame_Release,
891     PngDecoder_Frame_GetSize,
892     PngDecoder_Frame_GetPixelFormat,
893     PngDecoder_Frame_GetResolution,
894     PngDecoder_Frame_CopyPalette,
895     PngDecoder_Frame_CopyPixels,
896     PngDecoder_Frame_GetMetadataQueryReader,
897     PngDecoder_Frame_GetColorContexts,
898     PngDecoder_Frame_GetThumbnail
899 };
900
901 static HRESULT WINAPI PngDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid,
902     void **ppv)
903 {
904     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
905     return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv);
906 }
907
908 static ULONG WINAPI PngDecoder_Block_AddRef(IWICMetadataBlockReader *iface)
909 {
910     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
911     return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
912 }
913
914 static ULONG WINAPI PngDecoder_Block_Release(IWICMetadataBlockReader *iface)
915 {
916     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
917     return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
918 }
919
920 static HRESULT WINAPI PngDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
921     GUID *pguidContainerFormat)
922 {
923     if (!pguidContainerFormat) return E_INVALIDARG;
924     memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
925     return S_OK;
926 }
927
928 static HRESULT WINAPI PngDecoder_Block_GetCount(IWICMetadataBlockReader *iface,
929     UINT *pcCount)
930 {
931     FIXME("%p,%p: stub\n", iface, pcCount);
932     return E_NOTIMPL;
933 }
934
935 static HRESULT WINAPI PngDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
936     UINT nIndex, IWICMetadataReader **ppIMetadataReader)
937 {
938     FIXME("%p,%d,%p\n", iface, nIndex, ppIMetadataReader);
939     return E_NOTIMPL;
940 }
941
942 static HRESULT WINAPI PngDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface,
943     IEnumUnknown **ppIEnumMetadata)
944 {
945     FIXME("%p,%p\n", iface, ppIEnumMetadata);
946     return E_NOTIMPL;
947 }
948
949 static const IWICMetadataBlockReaderVtbl PngDecoder_BlockVtbl = {
950     PngDecoder_Block_QueryInterface,
951     PngDecoder_Block_AddRef,
952     PngDecoder_Block_Release,
953     PngDecoder_Block_GetContainerFormat,
954     PngDecoder_Block_GetCount,
955     PngDecoder_Block_GetReaderByIndex,
956     PngDecoder_Block_GetEnumerator,
957 };
958
959 HRESULT PngDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
960 {
961     PngDecoder *This;
962     HRESULT ret;
963
964     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
965
966     *ppv = NULL;
967
968     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
969
970     if (!libpng_handle && !load_libpng())
971     {
972         ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
973         return E_FAIL;
974     }
975
976     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngDecoder));
977     if (!This) return E_OUTOFMEMORY;
978
979     This->IWICBitmapDecoder_iface.lpVtbl = &PngDecoder_Vtbl;
980     This->IWICBitmapFrameDecode_iface.lpVtbl = &PngDecoder_FrameVtbl;
981     This->IWICMetadataBlockReader_iface.lpVtbl = &PngDecoder_BlockVtbl;
982     This->ref = 1;
983     This->png_ptr = NULL;
984     This->info_ptr = NULL;
985     This->end_info = NULL;
986     This->initialized = FALSE;
987     This->image_bits = NULL;
988     InitializeCriticalSection(&This->lock);
989     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngDecoder.lock");
990
991     ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
992     IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
993
994     return ret;
995 }
996
997 struct png_pixelformat {
998     const WICPixelFormatGUID *guid;
999     UINT bpp;
1000     int bit_depth;
1001     int color_type;
1002     BOOL remove_filler;
1003     BOOL swap_rgb;
1004 };
1005
1006 static const struct png_pixelformat formats[] = {
1007     {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1},
1008     {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0},
1009     {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0},
1010     {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0},
1011     {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0},
1012     {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0},
1013     {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1},
1014     {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1},
1015     {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0},
1016     {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0},
1017     {NULL},
1018 };
1019
1020 typedef struct PngEncoder {
1021     IWICBitmapEncoder IWICBitmapEncoder_iface;
1022     IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
1023     LONG ref;
1024     IStream *stream;
1025     png_structp png_ptr;
1026     png_infop info_ptr;
1027     UINT frame_count;
1028     BOOL frame_initialized;
1029     const struct png_pixelformat *format;
1030     BOOL info_written;
1031     UINT width, height;
1032     double xres, yres;
1033     UINT lines_written;
1034     BOOL frame_committed;
1035     BOOL committed;
1036     CRITICAL_SECTION lock;
1037 } PngEncoder;
1038
1039 static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
1040 {
1041     return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapEncoder_iface);
1042 }
1043
1044 static inline PngEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
1045 {
1046     return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapFrameEncode_iface);
1047 }
1048
1049 static HRESULT WINAPI PngFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
1050     void **ppv)
1051 {
1052     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1053     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1054
1055     if (!ppv) return E_INVALIDARG;
1056
1057     if (IsEqualIID(&IID_IUnknown, iid) ||
1058         IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
1059     {
1060         *ppv = &This->IWICBitmapFrameEncode_iface;
1061     }
1062     else
1063     {
1064         *ppv = NULL;
1065         return E_NOINTERFACE;
1066     }
1067
1068     IUnknown_AddRef((IUnknown*)*ppv);
1069     return S_OK;
1070 }
1071
1072 static ULONG WINAPI PngFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
1073 {
1074     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1075     return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface);
1076 }
1077
1078 static ULONG WINAPI PngFrameEncode_Release(IWICBitmapFrameEncode *iface)
1079 {
1080     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1081     return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1082 }
1083
1084 static HRESULT WINAPI PngFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
1085     IPropertyBag2 *pIEncoderOptions)
1086 {
1087     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1088     TRACE("(%p,%p)\n", iface, pIEncoderOptions);
1089
1090     EnterCriticalSection(&This->lock);
1091
1092     if (This->frame_initialized)
1093     {
1094         LeaveCriticalSection(&This->lock);
1095         return WINCODEC_ERR_WRONGSTATE;
1096     }
1097
1098     This->frame_initialized = TRUE;
1099
1100     LeaveCriticalSection(&This->lock);
1101
1102     return S_OK;
1103 }
1104
1105 static HRESULT WINAPI PngFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
1106     UINT uiWidth, UINT uiHeight)
1107 {
1108     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1109     TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
1110
1111     EnterCriticalSection(&This->lock);
1112
1113     if (!This->frame_initialized || This->info_written)
1114     {
1115         LeaveCriticalSection(&This->lock);
1116         return WINCODEC_ERR_WRONGSTATE;
1117     }
1118
1119     This->width = uiWidth;
1120     This->height = uiHeight;
1121
1122     LeaveCriticalSection(&This->lock);
1123
1124     return S_OK;
1125 }
1126
1127 static HRESULT WINAPI PngFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
1128     double dpiX, double dpiY)
1129 {
1130     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1131     TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
1132
1133     EnterCriticalSection(&This->lock);
1134
1135     if (!This->frame_initialized || This->info_written)
1136     {
1137         LeaveCriticalSection(&This->lock);
1138         return WINCODEC_ERR_WRONGSTATE;
1139     }
1140
1141     This->xres = dpiX;
1142     This->yres = dpiY;
1143
1144     LeaveCriticalSection(&This->lock);
1145
1146     return S_OK;
1147 }
1148
1149 static HRESULT WINAPI PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
1150     WICPixelFormatGUID *pPixelFormat)
1151 {
1152     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1153     int i;
1154     TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
1155
1156     EnterCriticalSection(&This->lock);
1157
1158     if (!This->frame_initialized || This->info_written)
1159     {
1160         LeaveCriticalSection(&This->lock);
1161         return WINCODEC_ERR_WRONGSTATE;
1162     }
1163
1164     for (i=0; formats[i].guid; i++)
1165     {
1166         if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
1167             break;
1168     }
1169
1170     if (!formats[i].guid) i = 0;
1171
1172     This->format = &formats[i];
1173     memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
1174
1175     LeaveCriticalSection(&This->lock);
1176
1177     return S_OK;
1178 }
1179
1180 static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
1181     UINT cCount, IWICColorContext **ppIColorContext)
1182 {
1183     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1184     return E_NOTIMPL;
1185 }
1186
1187 static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
1188     IWICPalette *pIPalette)
1189 {
1190     FIXME("(%p,%p): stub\n", iface, pIPalette);
1191     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1192 }
1193
1194 static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
1195     IWICBitmapSource *pIThumbnail)
1196 {
1197     FIXME("(%p,%p): stub\n", iface, pIThumbnail);
1198     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1199 }
1200
1201 static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
1202     UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
1203 {
1204     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1205     png_byte **row_pointers=NULL;
1206     UINT i;
1207     jmp_buf jmpbuf;
1208     TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
1209
1210     EnterCriticalSection(&This->lock);
1211
1212     if (!This->frame_initialized || !This->width || !This->height || !This->format)
1213     {
1214         LeaveCriticalSection(&This->lock);
1215         return WINCODEC_ERR_WRONGSTATE;
1216     }
1217
1218     if (lineCount == 0 || lineCount + This->lines_written > This->height)
1219     {
1220         LeaveCriticalSection(&This->lock);
1221         return E_INVALIDARG;
1222     }
1223
1224     /* set up setjmp/longjmp error handling */
1225     if (setjmp(jmpbuf))
1226     {
1227         LeaveCriticalSection(&This->lock);
1228         HeapFree(GetProcessHeap(), 0, row_pointers);
1229         return E_FAIL;
1230     }
1231     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1232
1233     if (!This->info_written)
1234     {
1235         ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height,
1236             This->format->bit_depth, This->format->color_type, PNG_INTERLACE_NONE,
1237             PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1238
1239         if (This->xres != 0.0 && This->yres != 0.0)
1240         {
1241             ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254,
1242                 (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER);
1243         }
1244
1245         ppng_write_info(This->png_ptr, This->info_ptr);
1246
1247         if (This->format->remove_filler)
1248             ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER);
1249
1250         if (This->format->swap_rgb)
1251             ppng_set_bgr(This->png_ptr);
1252
1253         This->info_written = TRUE;
1254     }
1255
1256     row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*));
1257     if (!row_pointers)
1258     {
1259         LeaveCriticalSection(&This->lock);
1260         return E_OUTOFMEMORY;
1261     }
1262
1263     for (i=0; i<lineCount; i++)
1264         row_pointers[i] = pbPixels + cbStride * i;
1265
1266     ppng_write_rows(This->png_ptr, row_pointers, lineCount);
1267     This->lines_written += lineCount;
1268
1269     LeaveCriticalSection(&This->lock);
1270
1271     HeapFree(GetProcessHeap(), 0, row_pointers);
1272
1273     return S_OK;
1274 }
1275
1276 static HRESULT WINAPI PngFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
1277     IWICBitmapSource *pIBitmapSource, WICRect *prc)
1278 {
1279     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1280     HRESULT hr;
1281     WICRect rc;
1282     WICPixelFormatGUID guid;
1283     UINT stride;
1284     BYTE *pixeldata;
1285     TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc);
1286
1287     if (!This->frame_initialized || !This->width || !This->height)
1288         return WINCODEC_ERR_WRONGSTATE;
1289
1290     if (!This->format)
1291     {
1292         hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid);
1293         if (FAILED(hr)) return hr;
1294         hr = IWICBitmapFrameEncode_SetPixelFormat(iface, &guid);
1295         if (FAILED(hr)) return hr;
1296     }
1297
1298     hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid);
1299     if (FAILED(hr)) return hr;
1300     if (memcmp(&guid, This->format->guid, sizeof(GUID)) != 0)
1301     {
1302         /* FIXME: should use WICConvertBitmapSource to convert */
1303         ERR("format %s unsupported\n", debugstr_guid(&guid));
1304         return E_FAIL;
1305     }
1306
1307     if (This->xres == 0.0 || This->yres == 0.0)
1308     {
1309         double xres, yres;
1310         hr = IWICBitmapSource_GetResolution(pIBitmapSource, &xres, &yres);
1311         if (FAILED(hr)) return hr;
1312         hr = IWICBitmapFrameEncode_SetResolution(iface, xres, yres);
1313         if (FAILED(hr)) return hr;
1314     }
1315
1316     if (!prc)
1317     {
1318         UINT width, height;
1319         hr = IWICBitmapSource_GetSize(pIBitmapSource, &width, &height);
1320         if (FAILED(hr)) return hr;
1321         rc.X = 0;
1322         rc.Y = 0;
1323         rc.Width = width;
1324         rc.Height = height;
1325         prc = &rc;
1326     }
1327
1328     if (prc->Width != This->width) return E_INVALIDARG;
1329
1330     stride = (This->format->bpp * This->width + 7)/8;
1331
1332     pixeldata = HeapAlloc(GetProcessHeap(), 0, stride * prc->Height);
1333     if (!pixeldata) return E_OUTOFMEMORY;
1334
1335     hr = IWICBitmapSource_CopyPixels(pIBitmapSource, prc, stride,
1336         stride*prc->Height, pixeldata);
1337
1338     if (SUCCEEDED(hr))
1339     {
1340         hr = IWICBitmapFrameEncode_WritePixels(iface, prc->Height, stride,
1341             stride*prc->Height, pixeldata);
1342     }
1343
1344     HeapFree(GetProcessHeap(), 0, pixeldata);
1345
1346     return hr;
1347 }
1348
1349 static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface)
1350 {
1351     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1352     jmp_buf jmpbuf;
1353     TRACE("(%p)\n", iface);
1354
1355     EnterCriticalSection(&This->lock);
1356
1357     if (!This->info_written || This->lines_written != This->height || This->frame_committed)
1358     {
1359         LeaveCriticalSection(&This->lock);
1360         return WINCODEC_ERR_WRONGSTATE;
1361     }
1362
1363     /* set up setjmp/longjmp error handling */
1364     if (setjmp(jmpbuf))
1365     {
1366         LeaveCriticalSection(&This->lock);
1367         return E_FAIL;
1368     }
1369     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1370
1371     ppng_write_end(This->png_ptr, This->info_ptr);
1372
1373     This->frame_committed = TRUE;
1374
1375     LeaveCriticalSection(&This->lock);
1376
1377     return S_OK;
1378 }
1379
1380 static HRESULT WINAPI PngFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
1381     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1382 {
1383     FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
1384     return E_NOTIMPL;
1385 }
1386
1387 static const IWICBitmapFrameEncodeVtbl PngEncoder_FrameVtbl = {
1388     PngFrameEncode_QueryInterface,
1389     PngFrameEncode_AddRef,
1390     PngFrameEncode_Release,
1391     PngFrameEncode_Initialize,
1392     PngFrameEncode_SetSize,
1393     PngFrameEncode_SetResolution,
1394     PngFrameEncode_SetPixelFormat,
1395     PngFrameEncode_SetColorContexts,
1396     PngFrameEncode_SetPalette,
1397     PngFrameEncode_SetThumbnail,
1398     PngFrameEncode_WritePixels,
1399     PngFrameEncode_WriteSource,
1400     PngFrameEncode_Commit,
1401     PngFrameEncode_GetMetadataQueryWriter
1402 };
1403
1404 static HRESULT WINAPI PngEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
1405     void **ppv)
1406 {
1407     PngEncoder *This = impl_from_IWICBitmapEncoder(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_IWICBitmapEncoder, iid))
1414     {
1415         *ppv = &This->IWICBitmapEncoder_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 PngEncoder_AddRef(IWICBitmapEncoder *iface)
1428 {
1429     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1430     ULONG ref = InterlockedIncrement(&This->ref);
1431
1432     TRACE("(%p) refcount=%u\n", iface, ref);
1433
1434     return ref;
1435 }
1436
1437 static ULONG WINAPI PngEncoder_Release(IWICBitmapEncoder *iface)
1438 {
1439     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1440     ULONG ref = InterlockedDecrement(&This->ref);
1441
1442     TRACE("(%p) refcount=%u\n", iface, ref);
1443
1444     if (ref == 0)
1445     {
1446         This->lock.DebugInfo->Spare[0] = 0;
1447         DeleteCriticalSection(&This->lock);
1448         if (This->png_ptr)
1449             ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1450         if (This->stream)
1451             IStream_Release(This->stream);
1452         HeapFree(GetProcessHeap(), 0, This);
1453     }
1454
1455     return ref;
1456 }
1457
1458 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1459 {
1460     PngEncoder *This = ppng_get_io_ptr(png_ptr);
1461     HRESULT hr;
1462     ULONG byteswritten;
1463
1464     hr = IStream_Write(This->stream, data, length, &byteswritten);
1465     if (FAILED(hr) || byteswritten != length)
1466     {
1467         ppng_error(png_ptr, "failed writing data");
1468     }
1469 }
1470
1471 static void user_flush(png_structp png_ptr)
1472 {
1473 }
1474
1475 static HRESULT WINAPI PngEncoder_Initialize(IWICBitmapEncoder *iface,
1476     IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
1477 {
1478     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1479     jmp_buf jmpbuf;
1480
1481     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
1482
1483     EnterCriticalSection(&This->lock);
1484
1485     if (This->png_ptr)
1486     {
1487         LeaveCriticalSection(&This->lock);
1488         return WINCODEC_ERR_WRONGSTATE;
1489     }
1490
1491     /* initialize libpng */
1492     This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1493     if (!This->png_ptr)
1494     {
1495         LeaveCriticalSection(&This->lock);
1496         return E_FAIL;
1497     }
1498
1499     This->info_ptr = ppng_create_info_struct(This->png_ptr);
1500     if (!This->info_ptr)
1501     {
1502         ppng_destroy_write_struct(&This->png_ptr, NULL);
1503         This->png_ptr = NULL;
1504         LeaveCriticalSection(&This->lock);
1505         return E_FAIL;
1506     }
1507
1508     IStream_AddRef(pIStream);
1509     This->stream = pIStream;
1510
1511     /* set up setjmp/longjmp error handling */
1512     if (setjmp(jmpbuf))
1513     {
1514         ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1515         This->png_ptr = NULL;
1516         IStream_Release(This->stream);
1517         This->stream = NULL;
1518         LeaveCriticalSection(&This->lock);
1519         return E_FAIL;
1520     }
1521     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1522
1523     /* set up custom i/o handling */
1524     ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush);
1525
1526     LeaveCriticalSection(&This->lock);
1527
1528     return S_OK;
1529 }
1530
1531 static HRESULT WINAPI PngEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
1532     GUID *pguidContainerFormat)
1533 {
1534     FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
1535     return E_NOTIMPL;
1536 }
1537
1538 static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface,
1539     IWICBitmapEncoderInfo **ppIEncoderInfo)
1540 {
1541     FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo);
1542     return E_NOTIMPL;
1543 }
1544
1545 static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface,
1546     UINT cCount, IWICColorContext **ppIColorContext)
1547 {
1548     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1549     return E_NOTIMPL;
1550 }
1551
1552 static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette)
1553 {
1554     TRACE("(%p,%p)\n", iface, pIPalette);
1555     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1556 }
1557
1558 static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
1559 {
1560     TRACE("(%p,%p)\n", iface, pIThumbnail);
1561     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1562 }
1563
1564 static HRESULT WINAPI PngEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
1565 {
1566     TRACE("(%p,%p)\n", iface, pIPreview);
1567     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1568 }
1569
1570 static HRESULT WINAPI PngEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
1571     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
1572 {
1573     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1574     HRESULT hr;
1575     TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
1576
1577     EnterCriticalSection(&This->lock);
1578
1579     if (This->frame_count != 0)
1580     {
1581         LeaveCriticalSection(&This->lock);
1582         return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1583     }
1584
1585     if (!This->stream)
1586     {
1587         LeaveCriticalSection(&This->lock);
1588         return WINCODEC_ERR_NOTINITIALIZED;
1589     }
1590
1591     hr = CreatePropertyBag2(ppIEncoderOptions);
1592     if (FAILED(hr))
1593     {
1594         LeaveCriticalSection(&This->lock);
1595         return hr;
1596     }
1597
1598     This->frame_count = 1;
1599
1600     LeaveCriticalSection(&This->lock);
1601
1602     IWICBitmapEncoder_AddRef(iface);
1603     *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface;
1604
1605     return S_OK;
1606 }
1607
1608 static HRESULT WINAPI PngEncoder_Commit(IWICBitmapEncoder *iface)
1609 {
1610     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1611     TRACE("(%p)\n", iface);
1612
1613     EnterCriticalSection(&This->lock);
1614
1615     if (!This->frame_committed || This->committed)
1616     {
1617         LeaveCriticalSection(&This->lock);
1618         return WINCODEC_ERR_WRONGSTATE;
1619     }
1620
1621     This->committed = TRUE;
1622
1623     LeaveCriticalSection(&This->lock);
1624
1625     return S_OK;
1626 }
1627
1628 static HRESULT WINAPI PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
1629     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1630 {
1631     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
1632     return E_NOTIMPL;
1633 }
1634
1635 static const IWICBitmapEncoderVtbl PngEncoder_Vtbl = {
1636     PngEncoder_QueryInterface,
1637     PngEncoder_AddRef,
1638     PngEncoder_Release,
1639     PngEncoder_Initialize,
1640     PngEncoder_GetContainerFormat,
1641     PngEncoder_GetEncoderInfo,
1642     PngEncoder_SetColorContexts,
1643     PngEncoder_SetPalette,
1644     PngEncoder_SetThumbnail,
1645     PngEncoder_SetPreview,
1646     PngEncoder_CreateNewFrame,
1647     PngEncoder_Commit,
1648     PngEncoder_GetMetadataQueryWriter
1649 };
1650
1651 HRESULT PngEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
1652 {
1653     PngEncoder *This;
1654     HRESULT ret;
1655
1656     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
1657
1658     *ppv = NULL;
1659
1660     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
1661
1662     if (!libpng_handle && !load_libpng())
1663     {
1664         ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG);
1665         return E_FAIL;
1666     }
1667
1668     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder));
1669     if (!This) return E_OUTOFMEMORY;
1670
1671     This->IWICBitmapEncoder_iface.lpVtbl = &PngEncoder_Vtbl;
1672     This->IWICBitmapFrameEncode_iface.lpVtbl = &PngEncoder_FrameVtbl;
1673     This->ref = 1;
1674     This->png_ptr = NULL;
1675     This->info_ptr = NULL;
1676     This->stream = NULL;
1677     This->frame_count = 0;
1678     This->frame_initialized = FALSE;
1679     This->format = NULL;
1680     This->info_written = FALSE;
1681     This->width = 0;
1682     This->height = 0;
1683     This->xres = 0.0;
1684     This->yres = 0.0;
1685     This->lines_written = 0;
1686     This->frame_committed = FALSE;
1687     This->committed = FALSE;
1688     InitializeCriticalSection(&This->lock);
1689     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock");
1690
1691     ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
1692     IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1693
1694     return ret;
1695 }
1696
1697 #else /* !HAVE_PNG_H */
1698
1699 HRESULT PngDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
1700 {
1701     ERR("Trying to load PNG picture, but PNG support is not compiled in.\n");
1702     return E_FAIL;
1703 }
1704
1705 HRESULT PngEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
1706 {
1707     ERR("Trying to save PNG picture, but PNG support is not compiled in.\n");
1708     return E_FAIL;
1709 }
1710
1711 #endif