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