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