oleaut32: Add a test for loading/saving an empty picture.
[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_FRAMEMISSING;
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     if (!pcActualCount) return E_INVALIDARG;
858
859     EnterCriticalSection(&This->lock);
860
861     if (ppng_get_iCCP(This->png_ptr, This->info_ptr, &name, &compression_type, &profile, &len))
862     {
863         if (cCount && ppIColorContexts)
864         {
865             hr = IWICColorContext_InitializeFromMemory(*ppIColorContexts, (const BYTE *)profile, len);
866             if (FAILED(hr))
867             {
868                 LeaveCriticalSection(&This->lock);
869                 return hr;
870             }
871         }
872         *pcActualCount = 1;
873     }
874     else
875         *pcActualCount = 0;
876
877     LeaveCriticalSection(&This->lock);
878
879     return S_OK;
880 }
881
882 static HRESULT WINAPI PngDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
883     IWICBitmapSource **ppIThumbnail)
884 {
885     FIXME("(%p,%p): stub\n", iface, ppIThumbnail);
886     return E_NOTIMPL;
887 }
888
889 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl = {
890     PngDecoder_Frame_QueryInterface,
891     PngDecoder_Frame_AddRef,
892     PngDecoder_Frame_Release,
893     PngDecoder_Frame_GetSize,
894     PngDecoder_Frame_GetPixelFormat,
895     PngDecoder_Frame_GetResolution,
896     PngDecoder_Frame_CopyPalette,
897     PngDecoder_Frame_CopyPixels,
898     PngDecoder_Frame_GetMetadataQueryReader,
899     PngDecoder_Frame_GetColorContexts,
900     PngDecoder_Frame_GetThumbnail
901 };
902
903 static HRESULT WINAPI PngDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid,
904     void **ppv)
905 {
906     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
907     return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv);
908 }
909
910 static ULONG WINAPI PngDecoder_Block_AddRef(IWICMetadataBlockReader *iface)
911 {
912     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
913     return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
914 }
915
916 static ULONG WINAPI PngDecoder_Block_Release(IWICMetadataBlockReader *iface)
917 {
918     PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
919     return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
920 }
921
922 static HRESULT WINAPI PngDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
923     GUID *pguidContainerFormat)
924 {
925     if (!pguidContainerFormat) return E_INVALIDARG;
926     memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
927     return S_OK;
928 }
929
930 static HRESULT WINAPI PngDecoder_Block_GetCount(IWICMetadataBlockReader *iface,
931     UINT *pcCount)
932 {
933     FIXME("%p,%p: stub\n", iface, pcCount);
934     return E_NOTIMPL;
935 }
936
937 static HRESULT WINAPI PngDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
938     UINT nIndex, IWICMetadataReader **ppIMetadataReader)
939 {
940     FIXME("%p,%d,%p\n", iface, nIndex, ppIMetadataReader);
941     return E_NOTIMPL;
942 }
943
944 static HRESULT WINAPI PngDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface,
945     IEnumUnknown **ppIEnumMetadata)
946 {
947     FIXME("%p,%p\n", iface, ppIEnumMetadata);
948     return E_NOTIMPL;
949 }
950
951 static const IWICMetadataBlockReaderVtbl PngDecoder_BlockVtbl = {
952     PngDecoder_Block_QueryInterface,
953     PngDecoder_Block_AddRef,
954     PngDecoder_Block_Release,
955     PngDecoder_Block_GetContainerFormat,
956     PngDecoder_Block_GetCount,
957     PngDecoder_Block_GetReaderByIndex,
958     PngDecoder_Block_GetEnumerator,
959 };
960
961 HRESULT PngDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
962 {
963     PngDecoder *This;
964     HRESULT ret;
965
966     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
967
968     *ppv = NULL;
969
970     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
971
972     if (!libpng_handle && !load_libpng())
973     {
974         ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
975         return E_FAIL;
976     }
977
978     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngDecoder));
979     if (!This) return E_OUTOFMEMORY;
980
981     This->IWICBitmapDecoder_iface.lpVtbl = &PngDecoder_Vtbl;
982     This->IWICBitmapFrameDecode_iface.lpVtbl = &PngDecoder_FrameVtbl;
983     This->IWICMetadataBlockReader_iface.lpVtbl = &PngDecoder_BlockVtbl;
984     This->ref = 1;
985     This->png_ptr = NULL;
986     This->info_ptr = NULL;
987     This->end_info = NULL;
988     This->initialized = FALSE;
989     This->image_bits = NULL;
990     InitializeCriticalSection(&This->lock);
991     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngDecoder.lock");
992
993     ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
994     IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
995
996     return ret;
997 }
998
999 struct png_pixelformat {
1000     const WICPixelFormatGUID *guid;
1001     UINT bpp;
1002     int bit_depth;
1003     int color_type;
1004     BOOL remove_filler;
1005     BOOL swap_rgb;
1006 };
1007
1008 static const struct png_pixelformat formats[] = {
1009     {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1},
1010     {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0},
1011     {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0},
1012     {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0},
1013     {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0},
1014     {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0},
1015     {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1},
1016     {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1},
1017     {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0},
1018     {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0},
1019     {NULL},
1020 };
1021
1022 typedef struct PngEncoder {
1023     IWICBitmapEncoder IWICBitmapEncoder_iface;
1024     IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
1025     LONG ref;
1026     IStream *stream;
1027     png_structp png_ptr;
1028     png_infop info_ptr;
1029     UINT frame_count;
1030     BOOL frame_initialized;
1031     const struct png_pixelformat *format;
1032     BOOL info_written;
1033     UINT width, height;
1034     double xres, yres;
1035     UINT lines_written;
1036     BOOL frame_committed;
1037     BOOL committed;
1038     CRITICAL_SECTION lock;
1039 } PngEncoder;
1040
1041 static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
1042 {
1043     return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapEncoder_iface);
1044 }
1045
1046 static inline PngEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
1047 {
1048     return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapFrameEncode_iface);
1049 }
1050
1051 static HRESULT WINAPI PngFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
1052     void **ppv)
1053 {
1054     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1055     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1056
1057     if (!ppv) return E_INVALIDARG;
1058
1059     if (IsEqualIID(&IID_IUnknown, iid) ||
1060         IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
1061     {
1062         *ppv = &This->IWICBitmapFrameEncode_iface;
1063     }
1064     else
1065     {
1066         *ppv = NULL;
1067         return E_NOINTERFACE;
1068     }
1069
1070     IUnknown_AddRef((IUnknown*)*ppv);
1071     return S_OK;
1072 }
1073
1074 static ULONG WINAPI PngFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
1075 {
1076     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1077     return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface);
1078 }
1079
1080 static ULONG WINAPI PngFrameEncode_Release(IWICBitmapFrameEncode *iface)
1081 {
1082     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1083     return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1084 }
1085
1086 static HRESULT WINAPI PngFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
1087     IPropertyBag2 *pIEncoderOptions)
1088 {
1089     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1090     TRACE("(%p,%p)\n", iface, pIEncoderOptions);
1091
1092     EnterCriticalSection(&This->lock);
1093
1094     if (This->frame_initialized)
1095     {
1096         LeaveCriticalSection(&This->lock);
1097         return WINCODEC_ERR_WRONGSTATE;
1098     }
1099
1100     This->frame_initialized = TRUE;
1101
1102     LeaveCriticalSection(&This->lock);
1103
1104     return S_OK;
1105 }
1106
1107 static HRESULT WINAPI PngFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
1108     UINT uiWidth, UINT uiHeight)
1109 {
1110     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1111     TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
1112
1113     EnterCriticalSection(&This->lock);
1114
1115     if (!This->frame_initialized || This->info_written)
1116     {
1117         LeaveCriticalSection(&This->lock);
1118         return WINCODEC_ERR_WRONGSTATE;
1119     }
1120
1121     This->width = uiWidth;
1122     This->height = uiHeight;
1123
1124     LeaveCriticalSection(&This->lock);
1125
1126     return S_OK;
1127 }
1128
1129 static HRESULT WINAPI PngFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
1130     double dpiX, double dpiY)
1131 {
1132     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1133     TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
1134
1135     EnterCriticalSection(&This->lock);
1136
1137     if (!This->frame_initialized || This->info_written)
1138     {
1139         LeaveCriticalSection(&This->lock);
1140         return WINCODEC_ERR_WRONGSTATE;
1141     }
1142
1143     This->xres = dpiX;
1144     This->yres = dpiY;
1145
1146     LeaveCriticalSection(&This->lock);
1147
1148     return S_OK;
1149 }
1150
1151 static HRESULT WINAPI PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
1152     WICPixelFormatGUID *pPixelFormat)
1153 {
1154     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1155     int i;
1156     TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
1157
1158     EnterCriticalSection(&This->lock);
1159
1160     if (!This->frame_initialized || This->info_written)
1161     {
1162         LeaveCriticalSection(&This->lock);
1163         return WINCODEC_ERR_WRONGSTATE;
1164     }
1165
1166     for (i=0; formats[i].guid; i++)
1167     {
1168         if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
1169             break;
1170     }
1171
1172     if (!formats[i].guid) i = 0;
1173
1174     This->format = &formats[i];
1175     memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
1176
1177     LeaveCriticalSection(&This->lock);
1178
1179     return S_OK;
1180 }
1181
1182 static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
1183     UINT cCount, IWICColorContext **ppIColorContext)
1184 {
1185     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1186     return E_NOTIMPL;
1187 }
1188
1189 static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
1190     IWICPalette *pIPalette)
1191 {
1192     FIXME("(%p,%p): stub\n", iface, pIPalette);
1193     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1194 }
1195
1196 static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
1197     IWICBitmapSource *pIThumbnail)
1198 {
1199     FIXME("(%p,%p): stub\n", iface, pIThumbnail);
1200     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1201 }
1202
1203 static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
1204     UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
1205 {
1206     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1207     png_byte **row_pointers=NULL;
1208     UINT i;
1209     jmp_buf jmpbuf;
1210     TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
1211
1212     EnterCriticalSection(&This->lock);
1213
1214     if (!This->frame_initialized || !This->width || !This->height || !This->format)
1215     {
1216         LeaveCriticalSection(&This->lock);
1217         return WINCODEC_ERR_WRONGSTATE;
1218     }
1219
1220     if (lineCount == 0 || lineCount + This->lines_written > This->height)
1221     {
1222         LeaveCriticalSection(&This->lock);
1223         return E_INVALIDARG;
1224     }
1225
1226     /* set up setjmp/longjmp error handling */
1227     if (setjmp(jmpbuf))
1228     {
1229         LeaveCriticalSection(&This->lock);
1230         HeapFree(GetProcessHeap(), 0, row_pointers);
1231         return E_FAIL;
1232     }
1233     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1234
1235     if (!This->info_written)
1236     {
1237         ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height,
1238             This->format->bit_depth, This->format->color_type, PNG_INTERLACE_NONE,
1239             PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1240
1241         if (This->xres != 0.0 && This->yres != 0.0)
1242         {
1243             ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254,
1244                 (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER);
1245         }
1246
1247         ppng_write_info(This->png_ptr, This->info_ptr);
1248
1249         if (This->format->remove_filler)
1250             ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER);
1251
1252         if (This->format->swap_rgb)
1253             ppng_set_bgr(This->png_ptr);
1254
1255         This->info_written = TRUE;
1256     }
1257
1258     row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*));
1259     if (!row_pointers)
1260     {
1261         LeaveCriticalSection(&This->lock);
1262         return E_OUTOFMEMORY;
1263     }
1264
1265     for (i=0; i<lineCount; i++)
1266         row_pointers[i] = pbPixels + cbStride * i;
1267
1268     ppng_write_rows(This->png_ptr, row_pointers, lineCount);
1269     This->lines_written += lineCount;
1270
1271     LeaveCriticalSection(&This->lock);
1272
1273     HeapFree(GetProcessHeap(), 0, row_pointers);
1274
1275     return S_OK;
1276 }
1277
1278 static HRESULT WINAPI PngFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
1279     IWICBitmapSource *pIBitmapSource, WICRect *prc)
1280 {
1281     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1282     HRESULT hr;
1283     WICRect rc;
1284     WICPixelFormatGUID guid;
1285     UINT stride;
1286     BYTE *pixeldata;
1287     TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc);
1288
1289     if (!This->frame_initialized || !This->width || !This->height)
1290         return WINCODEC_ERR_WRONGSTATE;
1291
1292     if (!This->format)
1293     {
1294         hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid);
1295         if (FAILED(hr)) return hr;
1296         hr = IWICBitmapFrameEncode_SetPixelFormat(iface, &guid);
1297         if (FAILED(hr)) return hr;
1298     }
1299
1300     hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid);
1301     if (FAILED(hr)) return hr;
1302     if (memcmp(&guid, This->format->guid, sizeof(GUID)) != 0)
1303     {
1304         /* FIXME: should use WICConvertBitmapSource to convert */
1305         ERR("format %s unsupported\n", debugstr_guid(&guid));
1306         return E_FAIL;
1307     }
1308
1309     if (This->xres == 0.0 || This->yres == 0.0)
1310     {
1311         double xres, yres;
1312         hr = IWICBitmapSource_GetResolution(pIBitmapSource, &xres, &yres);
1313         if (FAILED(hr)) return hr;
1314         hr = IWICBitmapFrameEncode_SetResolution(iface, xres, yres);
1315         if (FAILED(hr)) return hr;
1316     }
1317
1318     if (!prc)
1319     {
1320         UINT width, height;
1321         hr = IWICBitmapSource_GetSize(pIBitmapSource, &width, &height);
1322         if (FAILED(hr)) return hr;
1323         rc.X = 0;
1324         rc.Y = 0;
1325         rc.Width = width;
1326         rc.Height = height;
1327         prc = &rc;
1328     }
1329
1330     if (prc->Width != This->width) return E_INVALIDARG;
1331
1332     stride = (This->format->bpp * This->width + 7)/8;
1333
1334     pixeldata = HeapAlloc(GetProcessHeap(), 0, stride * prc->Height);
1335     if (!pixeldata) return E_OUTOFMEMORY;
1336
1337     hr = IWICBitmapSource_CopyPixels(pIBitmapSource, prc, stride,
1338         stride*prc->Height, pixeldata);
1339
1340     if (SUCCEEDED(hr))
1341     {
1342         hr = IWICBitmapFrameEncode_WritePixels(iface, prc->Height, stride,
1343             stride*prc->Height, pixeldata);
1344     }
1345
1346     HeapFree(GetProcessHeap(), 0, pixeldata);
1347
1348     return hr;
1349 }
1350
1351 static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface)
1352 {
1353     PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1354     jmp_buf jmpbuf;
1355     TRACE("(%p)\n", iface);
1356
1357     EnterCriticalSection(&This->lock);
1358
1359     if (!This->info_written || This->lines_written != This->height || This->frame_committed)
1360     {
1361         LeaveCriticalSection(&This->lock);
1362         return WINCODEC_ERR_WRONGSTATE;
1363     }
1364
1365     /* set up setjmp/longjmp error handling */
1366     if (setjmp(jmpbuf))
1367     {
1368         LeaveCriticalSection(&This->lock);
1369         return E_FAIL;
1370     }
1371     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1372
1373     ppng_write_end(This->png_ptr, This->info_ptr);
1374
1375     This->frame_committed = TRUE;
1376
1377     LeaveCriticalSection(&This->lock);
1378
1379     return S_OK;
1380 }
1381
1382 static HRESULT WINAPI PngFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
1383     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1384 {
1385     FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
1386     return E_NOTIMPL;
1387 }
1388
1389 static const IWICBitmapFrameEncodeVtbl PngEncoder_FrameVtbl = {
1390     PngFrameEncode_QueryInterface,
1391     PngFrameEncode_AddRef,
1392     PngFrameEncode_Release,
1393     PngFrameEncode_Initialize,
1394     PngFrameEncode_SetSize,
1395     PngFrameEncode_SetResolution,
1396     PngFrameEncode_SetPixelFormat,
1397     PngFrameEncode_SetColorContexts,
1398     PngFrameEncode_SetPalette,
1399     PngFrameEncode_SetThumbnail,
1400     PngFrameEncode_WritePixels,
1401     PngFrameEncode_WriteSource,
1402     PngFrameEncode_Commit,
1403     PngFrameEncode_GetMetadataQueryWriter
1404 };
1405
1406 static HRESULT WINAPI PngEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
1407     void **ppv)
1408 {
1409     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1410     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1411
1412     if (!ppv) return E_INVALIDARG;
1413
1414     if (IsEqualIID(&IID_IUnknown, iid) ||
1415         IsEqualIID(&IID_IWICBitmapEncoder, iid))
1416     {
1417         *ppv = &This->IWICBitmapEncoder_iface;
1418     }
1419     else
1420     {
1421         *ppv = NULL;
1422         return E_NOINTERFACE;
1423     }
1424
1425     IUnknown_AddRef((IUnknown*)*ppv);
1426     return S_OK;
1427 }
1428
1429 static ULONG WINAPI PngEncoder_AddRef(IWICBitmapEncoder *iface)
1430 {
1431     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1432     ULONG ref = InterlockedIncrement(&This->ref);
1433
1434     TRACE("(%p) refcount=%u\n", iface, ref);
1435
1436     return ref;
1437 }
1438
1439 static ULONG WINAPI PngEncoder_Release(IWICBitmapEncoder *iface)
1440 {
1441     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1442     ULONG ref = InterlockedDecrement(&This->ref);
1443
1444     TRACE("(%p) refcount=%u\n", iface, ref);
1445
1446     if (ref == 0)
1447     {
1448         This->lock.DebugInfo->Spare[0] = 0;
1449         DeleteCriticalSection(&This->lock);
1450         if (This->png_ptr)
1451             ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1452         if (This->stream)
1453             IStream_Release(This->stream);
1454         HeapFree(GetProcessHeap(), 0, This);
1455     }
1456
1457     return ref;
1458 }
1459
1460 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1461 {
1462     PngEncoder *This = ppng_get_io_ptr(png_ptr);
1463     HRESULT hr;
1464     ULONG byteswritten;
1465
1466     hr = IStream_Write(This->stream, data, length, &byteswritten);
1467     if (FAILED(hr) || byteswritten != length)
1468     {
1469         ppng_error(png_ptr, "failed writing data");
1470     }
1471 }
1472
1473 static void user_flush(png_structp png_ptr)
1474 {
1475 }
1476
1477 static HRESULT WINAPI PngEncoder_Initialize(IWICBitmapEncoder *iface,
1478     IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
1479 {
1480     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1481     jmp_buf jmpbuf;
1482
1483     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
1484
1485     EnterCriticalSection(&This->lock);
1486
1487     if (This->png_ptr)
1488     {
1489         LeaveCriticalSection(&This->lock);
1490         return WINCODEC_ERR_WRONGSTATE;
1491     }
1492
1493     /* initialize libpng */
1494     This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1495     if (!This->png_ptr)
1496     {
1497         LeaveCriticalSection(&This->lock);
1498         return E_FAIL;
1499     }
1500
1501     This->info_ptr = ppng_create_info_struct(This->png_ptr);
1502     if (!This->info_ptr)
1503     {
1504         ppng_destroy_write_struct(&This->png_ptr, NULL);
1505         This->png_ptr = NULL;
1506         LeaveCriticalSection(&This->lock);
1507         return E_FAIL;
1508     }
1509
1510     IStream_AddRef(pIStream);
1511     This->stream = pIStream;
1512
1513     /* set up setjmp/longjmp error handling */
1514     if (setjmp(jmpbuf))
1515     {
1516         ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1517         This->png_ptr = NULL;
1518         IStream_Release(This->stream);
1519         This->stream = NULL;
1520         LeaveCriticalSection(&This->lock);
1521         return E_FAIL;
1522     }
1523     ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1524
1525     /* set up custom i/o handling */
1526     ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush);
1527
1528     LeaveCriticalSection(&This->lock);
1529
1530     return S_OK;
1531 }
1532
1533 static HRESULT WINAPI PngEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
1534     GUID *pguidContainerFormat)
1535 {
1536     FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
1537     return E_NOTIMPL;
1538 }
1539
1540 static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface,
1541     IWICBitmapEncoderInfo **ppIEncoderInfo)
1542 {
1543     FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo);
1544     return E_NOTIMPL;
1545 }
1546
1547 static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface,
1548     UINT cCount, IWICColorContext **ppIColorContext)
1549 {
1550     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1551     return E_NOTIMPL;
1552 }
1553
1554 static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette)
1555 {
1556     TRACE("(%p,%p)\n", iface, pIPalette);
1557     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1558 }
1559
1560 static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
1561 {
1562     TRACE("(%p,%p)\n", iface, pIThumbnail);
1563     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1564 }
1565
1566 static HRESULT WINAPI PngEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
1567 {
1568     TRACE("(%p,%p)\n", iface, pIPreview);
1569     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1570 }
1571
1572 static HRESULT WINAPI PngEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
1573     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
1574 {
1575     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1576     HRESULT hr;
1577     TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
1578
1579     EnterCriticalSection(&This->lock);
1580
1581     if (This->frame_count != 0)
1582     {
1583         LeaveCriticalSection(&This->lock);
1584         return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1585     }
1586
1587     if (!This->stream)
1588     {
1589         LeaveCriticalSection(&This->lock);
1590         return WINCODEC_ERR_NOTINITIALIZED;
1591     }
1592
1593     hr = CreatePropertyBag2(ppIEncoderOptions);
1594     if (FAILED(hr))
1595     {
1596         LeaveCriticalSection(&This->lock);
1597         return hr;
1598     }
1599
1600     This->frame_count = 1;
1601
1602     LeaveCriticalSection(&This->lock);
1603
1604     IWICBitmapEncoder_AddRef(iface);
1605     *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface;
1606
1607     return S_OK;
1608 }
1609
1610 static HRESULT WINAPI PngEncoder_Commit(IWICBitmapEncoder *iface)
1611 {
1612     PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1613     TRACE("(%p)\n", iface);
1614
1615     EnterCriticalSection(&This->lock);
1616
1617     if (!This->frame_committed || This->committed)
1618     {
1619         LeaveCriticalSection(&This->lock);
1620         return WINCODEC_ERR_WRONGSTATE;
1621     }
1622
1623     This->committed = TRUE;
1624
1625     LeaveCriticalSection(&This->lock);
1626
1627     return S_OK;
1628 }
1629
1630 static HRESULT WINAPI PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
1631     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1632 {
1633     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
1634     return E_NOTIMPL;
1635 }
1636
1637 static const IWICBitmapEncoderVtbl PngEncoder_Vtbl = {
1638     PngEncoder_QueryInterface,
1639     PngEncoder_AddRef,
1640     PngEncoder_Release,
1641     PngEncoder_Initialize,
1642     PngEncoder_GetContainerFormat,
1643     PngEncoder_GetEncoderInfo,
1644     PngEncoder_SetColorContexts,
1645     PngEncoder_SetPalette,
1646     PngEncoder_SetThumbnail,
1647     PngEncoder_SetPreview,
1648     PngEncoder_CreateNewFrame,
1649     PngEncoder_Commit,
1650     PngEncoder_GetMetadataQueryWriter
1651 };
1652
1653 HRESULT PngEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
1654 {
1655     PngEncoder *This;
1656     HRESULT ret;
1657
1658     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
1659
1660     *ppv = NULL;
1661
1662     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
1663
1664     if (!libpng_handle && !load_libpng())
1665     {
1666         ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG);
1667         return E_FAIL;
1668     }
1669
1670     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder));
1671     if (!This) return E_OUTOFMEMORY;
1672
1673     This->IWICBitmapEncoder_iface.lpVtbl = &PngEncoder_Vtbl;
1674     This->IWICBitmapFrameEncode_iface.lpVtbl = &PngEncoder_FrameVtbl;
1675     This->ref = 1;
1676     This->png_ptr = NULL;
1677     This->info_ptr = NULL;
1678     This->stream = NULL;
1679     This->frame_count = 0;
1680     This->frame_initialized = FALSE;
1681     This->format = NULL;
1682     This->info_written = FALSE;
1683     This->width = 0;
1684     This->height = 0;
1685     This->xres = 0.0;
1686     This->yres = 0.0;
1687     This->lines_written = 0;
1688     This->frame_committed = FALSE;
1689     This->committed = FALSE;
1690     InitializeCriticalSection(&This->lock);
1691     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock");
1692
1693     ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
1694     IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1695
1696     return ret;
1697 }
1698
1699 #else /* !HAVE_PNG_H */
1700
1701 HRESULT PngDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
1702 {
1703     ERR("Trying to load PNG picture, but PNG support is not compiled in.\n");
1704     return E_FAIL;
1705 }
1706
1707 HRESULT PngEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
1708 {
1709     ERR("Trying to save PNG picture, but PNG support is not compiled in.\n");
1710     return E_FAIL;
1711 }
1712
1713 #endif