dbghelp: Add basic support for PVOID64.
[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
35 #include "wincodecs_private.h"
36
37 #include "wine/debug.h"
38 #include "wine/library.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
41
42 #ifdef SONAME_LIBPNG
43
44 static void *libpng_handle;
45 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
46 MAKE_FUNCPTR(png_create_read_struct);
47 MAKE_FUNCPTR(png_create_info_struct);
48 MAKE_FUNCPTR(png_create_write_struct);
49 MAKE_FUNCPTR(png_destroy_read_struct);
50 MAKE_FUNCPTR(png_destroy_write_struct);
51 MAKE_FUNCPTR(png_error);
52 MAKE_FUNCPTR(png_get_bit_depth);
53 MAKE_FUNCPTR(png_get_color_type);
54 MAKE_FUNCPTR(png_get_error_ptr);
55 MAKE_FUNCPTR(png_get_image_height);
56 MAKE_FUNCPTR(png_get_image_width);
57 MAKE_FUNCPTR(png_get_io_ptr);
58 MAKE_FUNCPTR(png_get_pHYs);
59 MAKE_FUNCPTR(png_get_PLTE);
60 MAKE_FUNCPTR(png_get_tRNS);
61 MAKE_FUNCPTR(png_set_bgr);
62 MAKE_FUNCPTR(png_set_error_fn);
63 MAKE_FUNCPTR(png_set_expand_gray_1_2_4_to_8);
64 MAKE_FUNCPTR(png_set_filler);
65 MAKE_FUNCPTR(png_set_gray_to_rgb);
66 MAKE_FUNCPTR(png_set_IHDR);
67 MAKE_FUNCPTR(png_set_pHYs);
68 MAKE_FUNCPTR(png_set_read_fn);
69 MAKE_FUNCPTR(png_set_strip_16);
70 MAKE_FUNCPTR(png_set_tRNS_to_alpha);
71 MAKE_FUNCPTR(png_set_write_fn);
72 MAKE_FUNCPTR(png_read_end);
73 MAKE_FUNCPTR(png_read_image);
74 MAKE_FUNCPTR(png_read_info);
75 MAKE_FUNCPTR(png_write_end);
76 MAKE_FUNCPTR(png_write_info);
77 MAKE_FUNCPTR(png_write_rows);
78 #undef MAKE_FUNCPTR
79
80 static void *load_libpng(void)
81 {
82     if((libpng_handle = wine_dlopen(SONAME_LIBPNG, RTLD_NOW, NULL, 0)) != NULL) {
83
84 #define LOAD_FUNCPTR(f) \
85     if((p##f = wine_dlsym(libpng_handle, #f, NULL, 0)) == NULL) { \
86         libpng_handle = NULL; \
87         return NULL; \
88     }
89         LOAD_FUNCPTR(png_create_read_struct);
90         LOAD_FUNCPTR(png_create_info_struct);
91         LOAD_FUNCPTR(png_create_write_struct);
92         LOAD_FUNCPTR(png_destroy_read_struct);
93         LOAD_FUNCPTR(png_destroy_write_struct);
94         LOAD_FUNCPTR(png_error);
95         LOAD_FUNCPTR(png_get_bit_depth);
96         LOAD_FUNCPTR(png_get_color_type);
97         LOAD_FUNCPTR(png_get_error_ptr);
98         LOAD_FUNCPTR(png_get_image_height);
99         LOAD_FUNCPTR(png_get_image_width);
100         LOAD_FUNCPTR(png_get_io_ptr);
101         LOAD_FUNCPTR(png_get_pHYs);
102         LOAD_FUNCPTR(png_get_PLTE);
103         LOAD_FUNCPTR(png_get_tRNS);
104         LOAD_FUNCPTR(png_set_bgr);
105         LOAD_FUNCPTR(png_set_error_fn);
106         LOAD_FUNCPTR(png_set_expand_gray_1_2_4_to_8);
107         LOAD_FUNCPTR(png_set_filler);
108         LOAD_FUNCPTR(png_set_gray_to_rgb);
109         LOAD_FUNCPTR(png_set_IHDR);
110         LOAD_FUNCPTR(png_set_pHYs);
111         LOAD_FUNCPTR(png_set_read_fn);
112         LOAD_FUNCPTR(png_set_strip_16);
113         LOAD_FUNCPTR(png_set_tRNS_to_alpha);
114         LOAD_FUNCPTR(png_set_write_fn);
115         LOAD_FUNCPTR(png_read_end);
116         LOAD_FUNCPTR(png_read_image);
117         LOAD_FUNCPTR(png_read_info);
118         LOAD_FUNCPTR(png_write_end);
119         LOAD_FUNCPTR(png_write_info);
120         LOAD_FUNCPTR(png_write_rows);
121
122 #undef LOAD_FUNCPTR
123     }
124     return libpng_handle;
125 }
126
127 static void user_error_fn(png_structp png_ptr, png_const_charp error_message)
128 {
129     jmp_buf *pjmpbuf;
130
131     /* This uses setjmp/longjmp just like the default. We can't use the
132      * default because there's no way to access the jmp buffer in the png_struct
133      * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
134     WARN("PNG error: %s\n", debugstr_a(error_message));
135     pjmpbuf = ppng_get_error_ptr(png_ptr);
136     longjmp(*pjmpbuf, 1);
137 }
138
139 static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message)
140 {
141     WARN("PNG warning: %s\n", debugstr_a(warning_message));
142 }
143
144 typedef struct {
145     const IWICBitmapDecoderVtbl *lpVtbl;
146     const IWICBitmapFrameDecodeVtbl *lpFrameVtbl;
147     LONG ref;
148     png_structp png_ptr;
149     png_infop info_ptr;
150     png_infop end_info;
151     BOOL initialized;
152     int bpp;
153     int width, height;
154     UINT stride;
155     const WICPixelFormatGUID *format;
156     BYTE *image_bits;
157 } PngDecoder;
158
159 static inline PngDecoder *impl_from_frame(IWICBitmapFrameDecode *iface)
160 {
161     return CONTAINING_RECORD(iface, PngDecoder, lpFrameVtbl);
162 }
163
164 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl;
165
166 static HRESULT WINAPI PngDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
167     void **ppv)
168 {
169     PngDecoder *This = (PngDecoder*)iface;
170     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
171
172     if (!ppv) return E_INVALIDARG;
173
174     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
175     {
176         *ppv = This;
177     }
178     else
179     {
180         *ppv = NULL;
181         return E_NOINTERFACE;
182     }
183
184     IUnknown_AddRef((IUnknown*)*ppv);
185     return S_OK;
186 }
187
188 static ULONG WINAPI PngDecoder_AddRef(IWICBitmapDecoder *iface)
189 {
190     PngDecoder *This = (PngDecoder*)iface;
191     ULONG ref = InterlockedIncrement(&This->ref);
192
193     TRACE("(%p) refcount=%u\n", iface, ref);
194
195     return ref;
196 }
197
198 static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface)
199 {
200     PngDecoder *This = (PngDecoder*)iface;
201     ULONG ref = InterlockedDecrement(&This->ref);
202
203     TRACE("(%p) refcount=%u\n", iface, ref);
204
205     if (ref == 0)
206     {
207         if (This->png_ptr)
208             ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
209         HeapFree(GetProcessHeap(), 0, This->image_bits);
210         HeapFree(GetProcessHeap(), 0, This);
211     }
212
213     return ref;
214 }
215
216 static HRESULT WINAPI PngDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
217     DWORD *pdwCapability)
218 {
219     FIXME("(%p,%p,%p): stub\n", iface, pIStream, pdwCapability);
220     return E_NOTIMPL;
221 }
222
223 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
224 {
225     IStream *stream = ppng_get_io_ptr(png_ptr);
226     HRESULT hr;
227     ULONG bytesread;
228
229     hr = IStream_Read(stream, data, length, &bytesread);
230     if (FAILED(hr) || bytesread != length)
231     {
232         ppng_error(png_ptr, "failed reading data");
233     }
234 }
235
236 static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
237     WICDecodeOptions cacheOptions)
238 {
239     PngDecoder *This = (PngDecoder*)iface;
240     LARGE_INTEGER seek;
241     HRESULT hr;
242     png_bytep *row_pointers=NULL;
243     UINT image_size;
244     UINT i;
245     int color_type, bit_depth;
246     png_bytep trans;
247     int num_trans;
248     png_uint_32 transparency;
249     png_color_16p trans_values;
250     jmp_buf jmpbuf;
251
252     TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
253
254     /* initialize libpng */
255     This->png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
256     if (!This->png_ptr) return E_FAIL;
257
258     This->info_ptr = ppng_create_info_struct(This->png_ptr);
259     if (!This->info_ptr)
260     {
261         ppng_destroy_read_struct(&This->png_ptr, NULL, NULL);
262         This->png_ptr = NULL;
263         return E_FAIL;
264     }
265
266     This->end_info = ppng_create_info_struct(This->png_ptr);
267     if (!This->info_ptr)
268     {
269         ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, NULL);
270         This->png_ptr = NULL;
271         return E_FAIL;
272     }
273
274     /* set up setjmp/longjmp error handling */
275     if (setjmp(jmpbuf))
276     {
277         ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
278         HeapFree(GetProcessHeap(), 0, row_pointers);
279         This->png_ptr = NULL;
280         return E_FAIL;
281     }
282     ppng_set_error_fn(This->png_ptr, &jmpbuf, user_error_fn, user_warning_fn);
283
284     /* seek to the start of the stream */
285     seek.QuadPart = 0;
286     hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
287     if (FAILED(hr)) return hr;
288
289     /* set up custom i/o handling */
290     ppng_set_read_fn(This->png_ptr, pIStream, user_read_data);
291
292     /* read the header */
293     ppng_read_info(This->png_ptr, This->info_ptr);
294
295     /* choose a pixel format */
296     color_type = ppng_get_color_type(This->png_ptr, This->info_ptr);
297     bit_depth = ppng_get_bit_depth(This->png_ptr, This->info_ptr);
298
299     /* check for color-keyed alpha */
300     transparency = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans, &num_trans, &trans_values);
301
302     if (transparency && color_type != PNG_COLOR_TYPE_PALETTE)
303     {
304         /* expand to RGBA */
305         if (color_type == PNG_COLOR_TYPE_GRAY)
306         {
307             if (bit_depth < 8)
308             {
309                 ppng_set_expand_gray_1_2_4_to_8(This->png_ptr);
310                 bit_depth = 8;
311             }
312             ppng_set_gray_to_rgb(This->png_ptr);
313         }
314         ppng_set_tRNS_to_alpha(This->png_ptr);
315         color_type = PNG_COLOR_TYPE_RGB_ALPHA;
316     }
317
318     switch (color_type)
319     {
320     case PNG_COLOR_TYPE_GRAY:
321         This->bpp = bit_depth;
322         switch (bit_depth)
323         {
324         case 1: This->format = &GUID_WICPixelFormatBlackWhite; break;
325         case 2: This->format = &GUID_WICPixelFormat2bppGray; break;
326         case 4: This->format = &GUID_WICPixelFormat4bppGray; break;
327         case 8: This->format = &GUID_WICPixelFormat8bppGray; break;
328         case 16: This->format = &GUID_WICPixelFormat16bppGray; break;
329         default:
330             ERR("invalid grayscale bit depth: %i\n", bit_depth);
331             return E_FAIL;
332         }
333         break;
334     case PNG_COLOR_TYPE_GRAY_ALPHA:
335         /* WIC does not support grayscale alpha formats so use RGBA */
336         ppng_set_gray_to_rgb(This->png_ptr);
337     case PNG_COLOR_TYPE_RGB_ALPHA:
338         This->bpp = bit_depth * 4;
339         switch (bit_depth)
340         {
341         case 8:
342             ppng_set_bgr(This->png_ptr);
343             This->format = &GUID_WICPixelFormat32bppBGRA;
344             break;
345         case 16: This->format = &GUID_WICPixelFormat64bppRGBA; break;
346         default:
347             ERR("invalid RGBA bit depth: %i\n", bit_depth);
348             return E_FAIL;
349         }
350         break;
351     case PNG_COLOR_TYPE_PALETTE:
352         This->bpp = bit_depth;
353         switch (bit_depth)
354         {
355         case 1: This->format = &GUID_WICPixelFormat1bppIndexed; break;
356         case 2: This->format = &GUID_WICPixelFormat2bppIndexed; break;
357         case 4: This->format = &GUID_WICPixelFormat4bppIndexed; break;
358         case 8: This->format = &GUID_WICPixelFormat8bppIndexed; break;
359         default:
360             ERR("invalid indexed color bit depth: %i\n", bit_depth);
361             return E_FAIL;
362         }
363         break;
364     case PNG_COLOR_TYPE_RGB:
365         This->bpp = bit_depth * 3;
366         switch (bit_depth)
367         {
368         case 8:
369             ppng_set_bgr(This->png_ptr);
370             This->format = &GUID_WICPixelFormat24bppBGR;
371             break;
372         case 16: This->format = &GUID_WICPixelFormat48bppRGB; break;
373         default:
374             ERR("invalid RGB color bit depth: %i\n", bit_depth);
375             return E_FAIL;
376         }
377         break;
378     default:
379         ERR("invalid color type %i\n", color_type);
380         return E_FAIL;
381     }
382
383     /* read the image data */
384     This->width = ppng_get_image_width(This->png_ptr, This->info_ptr);
385     This->height = ppng_get_image_height(This->png_ptr, This->info_ptr);
386     This->stride = This->width * This->bpp;
387     image_size = This->stride * This->height;
388
389     This->image_bits = HeapAlloc(GetProcessHeap(), 0, image_size);
390     if (!This->image_bits) return E_OUTOFMEMORY;
391
392     row_pointers = HeapAlloc(GetProcessHeap(), 0, sizeof(png_bytep)*This->height);
393     if (!row_pointers) return E_OUTOFMEMORY;
394
395     for (i=0; i<This->height; i++)
396         row_pointers[i] = This->image_bits + i * This->stride;
397
398     ppng_read_image(This->png_ptr, row_pointers);
399
400     HeapFree(GetProcessHeap(), 0, row_pointers);
401     row_pointers = NULL;
402
403     ppng_read_end(This->png_ptr, This->end_info);
404
405     This->initialized = TRUE;
406
407     return S_OK;
408 }
409
410 static HRESULT WINAPI PngDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
411     GUID *pguidContainerFormat)
412 {
413     memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
414     return S_OK;
415 }
416
417 static HRESULT WINAPI PngDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
418     IWICBitmapDecoderInfo **ppIDecoderInfo)
419 {
420     FIXME("(%p,%p): stub\n", iface, ppIDecoderInfo);
421     return E_NOTIMPL;
422 }
423
424 static HRESULT WINAPI PngDecoder_CopyPalette(IWICBitmapDecoder *iface,
425     IWICPalette *pIPalette)
426 {
427     FIXME("(%p,%p): stub\n", iface, pIPalette);
428     return E_NOTIMPL;
429 }
430
431 static HRESULT WINAPI PngDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
432     IWICMetadataQueryReader **ppIMetadataQueryReader)
433 {
434     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
435     return E_NOTIMPL;
436 }
437
438 static HRESULT WINAPI PngDecoder_GetPreview(IWICBitmapDecoder *iface,
439     IWICBitmapSource **ppIBitmapSource)
440 {
441     FIXME("(%p,%p): stub\n", iface, ppIBitmapSource);
442     return E_NOTIMPL;
443 }
444
445 static HRESULT WINAPI PngDecoder_GetColorContexts(IWICBitmapDecoder *iface,
446     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
447 {
448     FIXME("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
449     return E_NOTIMPL;
450 }
451
452 static HRESULT WINAPI PngDecoder_GetThumbnail(IWICBitmapDecoder *iface,
453     IWICBitmapSource **ppIThumbnail)
454 {
455     TRACE("(%p,%p)\n", iface, ppIThumbnail);
456     return WINCODEC_ERR_CODECNOTHUMBNAIL;
457 }
458
459 static HRESULT WINAPI PngDecoder_GetFrameCount(IWICBitmapDecoder *iface,
460     UINT *pCount)
461 {
462     *pCount = 1;
463     return S_OK;
464 }
465
466 static HRESULT WINAPI PngDecoder_GetFrame(IWICBitmapDecoder *iface,
467     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
468 {
469     PngDecoder *This = (PngDecoder*)iface;
470     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
471
472     if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
473
474     if (index != 0) return E_INVALIDARG;
475
476     IWICBitmapDecoder_AddRef(iface);
477
478     *ppIBitmapFrame = (void*)(&This->lpFrameVtbl);
479
480     return S_OK;
481 }
482
483 static const IWICBitmapDecoderVtbl PngDecoder_Vtbl = {
484     PngDecoder_QueryInterface,
485     PngDecoder_AddRef,
486     PngDecoder_Release,
487     PngDecoder_QueryCapability,
488     PngDecoder_Initialize,
489     PngDecoder_GetContainerFormat,
490     PngDecoder_GetDecoderInfo,
491     PngDecoder_CopyPalette,
492     PngDecoder_GetMetadataQueryReader,
493     PngDecoder_GetPreview,
494     PngDecoder_GetColorContexts,
495     PngDecoder_GetThumbnail,
496     PngDecoder_GetFrameCount,
497     PngDecoder_GetFrame
498 };
499
500 static HRESULT WINAPI PngDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
501     void **ppv)
502 {
503     if (!ppv) return E_INVALIDARG;
504
505     if (IsEqualIID(&IID_IUnknown, iid) ||
506         IsEqualIID(&IID_IWICBitmapSource, iid) ||
507         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
508     {
509         *ppv = iface;
510     }
511     else
512     {
513         *ppv = NULL;
514         return E_NOINTERFACE;
515     }
516
517     IUnknown_AddRef((IUnknown*)*ppv);
518     return S_OK;
519 }
520
521 static ULONG WINAPI PngDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface)
522 {
523     PngDecoder *This = impl_from_frame(iface);
524     return IUnknown_AddRef((IUnknown*)This);
525 }
526
527 static ULONG WINAPI PngDecoder_Frame_Release(IWICBitmapFrameDecode *iface)
528 {
529     PngDecoder *This = impl_from_frame(iface);
530     return IUnknown_Release((IUnknown*)This);
531 }
532
533 static HRESULT WINAPI PngDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface,
534     UINT *puiWidth, UINT *puiHeight)
535 {
536     PngDecoder *This = impl_from_frame(iface);
537     *puiWidth = This->width;
538     *puiHeight = This->height;
539     TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight);
540     return S_OK;
541 }
542
543 static HRESULT WINAPI PngDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface,
544     WICPixelFormatGUID *pPixelFormat)
545 {
546     PngDecoder *This = impl_from_frame(iface);
547     TRACE("(%p,%p)\n", iface, pPixelFormat);
548
549     memcpy(pPixelFormat, This->format, sizeof(GUID));
550
551     return S_OK;
552 }
553
554 static HRESULT WINAPI PngDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface,
555     double *pDpiX, double *pDpiY)
556 {
557     PngDecoder *This = impl_from_frame(iface);
558     png_uint_32 ret, xres, yres;
559     int unit_type;
560
561     ret = ppng_get_pHYs(This->png_ptr, This->info_ptr, &xres, &yres, &unit_type);
562
563     if (ret && unit_type == PNG_RESOLUTION_METER)
564     {
565         *pDpiX = xres * 0.0254;
566         *pDpiY = yres * 0.0254;
567     }
568     else
569     {
570         WARN("no pHYs block present\n");
571         *pDpiX = *pDpiY = 96.0;
572     }
573
574     TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY);
575
576     return S_OK;
577 }
578
579 static HRESULT WINAPI PngDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface,
580     IWICPalette *pIPalette)
581 {
582     PngDecoder *This = impl_from_frame(iface);
583     png_uint_32 ret;
584     png_colorp png_palette;
585     int num_palette;
586     WICColor palette[256];
587     png_bytep trans;
588     int num_trans;
589     png_color_16p trans_values;
590     int i;
591
592     TRACE("(%p,%p)\n", iface, pIPalette);
593
594     ret = ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette);
595     if (!ret) return WINCODEC_ERR_PALETTEUNAVAILABLE;
596
597     if (num_palette > 256)
598     {
599         ERR("palette has %i colors?!\n", num_palette);
600         return E_FAIL;
601     }
602
603     for (i=0; i<num_palette; i++)
604     {
605         palette[i] = (0xff000000|
606                       png_palette[i].red << 16|
607                       png_palette[i].green << 8|
608                       png_palette[i].blue);
609     }
610
611     ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans, &num_trans, &trans_values);
612     if (ret)
613     {
614         for (i=0; i<num_trans; i++)
615         {
616             palette[trans[i]] = 0x00000000;
617         }
618     }
619
620     return IWICPalette_InitializeCustom(pIPalette, palette, num_palette);
621 }
622
623 static HRESULT WINAPI PngDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface,
624     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
625 {
626     PngDecoder *This = impl_from_frame(iface);
627     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
628
629     return copy_pixels(This->bpp, This->image_bits,
630         This->width, This->height, This->stride,
631         prc, cbStride, cbBufferSize, pbBuffer);
632 }
633
634 static HRESULT WINAPI PngDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
635     IWICMetadataQueryReader **ppIMetadataQueryReader)
636 {
637     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
638     return E_NOTIMPL;
639 }
640
641 static HRESULT WINAPI PngDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface,
642     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
643 {
644     FIXME("(%p,%u,%p,%p): stub\n", iface, cCount, ppIColorContexts, pcActualCount);
645     return E_NOTIMPL;
646 }
647
648 static HRESULT WINAPI PngDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
649     IWICBitmapSource **ppIThumbnail)
650 {
651     FIXME("(%p,%p): stub\n", iface, ppIThumbnail);
652     return E_NOTIMPL;
653 }
654
655 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl = {
656     PngDecoder_Frame_QueryInterface,
657     PngDecoder_Frame_AddRef,
658     PngDecoder_Frame_Release,
659     PngDecoder_Frame_GetSize,
660     PngDecoder_Frame_GetPixelFormat,
661     PngDecoder_Frame_GetResolution,
662     PngDecoder_Frame_CopyPalette,
663     PngDecoder_Frame_CopyPixels,
664     PngDecoder_Frame_GetMetadataQueryReader,
665     PngDecoder_Frame_GetColorContexts,
666     PngDecoder_Frame_GetThumbnail
667 };
668
669 HRESULT PngDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
670 {
671     PngDecoder *This;
672     HRESULT ret;
673
674     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
675
676     *ppv = NULL;
677
678     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
679
680     if (!libpng_handle && !load_libpng())
681     {
682         ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
683         return E_FAIL;
684     }
685
686     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngDecoder));
687     if (!This) return E_OUTOFMEMORY;
688
689     This->lpVtbl = &PngDecoder_Vtbl;
690     This->lpFrameVtbl = &PngDecoder_FrameVtbl;
691     This->ref = 1;
692     This->png_ptr = NULL;
693     This->info_ptr = NULL;
694     This->end_info = NULL;
695     This->initialized = FALSE;
696     This->image_bits = NULL;
697
698     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
699     IUnknown_Release((IUnknown*)This);
700
701     return ret;
702 }
703
704 struct png_pixelformat {
705     const WICPixelFormatGUID *guid;
706     UINT bpp;
707     int bit_depth;
708     int color_type;
709     BOOL remove_filler;
710     BOOL swap_rgb;
711 };
712
713 static const struct png_pixelformat formats[] = {
714     {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1},
715     {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0},
716     {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0},
717     {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0},
718     {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0},
719     {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0},
720     {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1},
721     {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1},
722     {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0},
723     {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0},
724     {NULL},
725 };
726
727 typedef struct PngEncoder {
728     const IWICBitmapEncoderVtbl *lpVtbl;
729     const IWICBitmapFrameEncodeVtbl *lpFrameVtbl;
730     LONG ref;
731     IStream *stream;
732     png_structp png_ptr;
733     png_infop info_ptr;
734     UINT frame_count;
735     BOOL frame_initialized;
736     const struct png_pixelformat *format;
737     BOOL info_written;
738     UINT width, height;
739     double xres, yres;
740     UINT lines_written;
741     BOOL frame_committed;
742     BOOL committed;
743 } PngEncoder;
744
745 static inline PngEncoder *encoder_from_frame(IWICBitmapFrameEncode *iface)
746 {
747     return CONTAINING_RECORD(iface, PngEncoder, lpFrameVtbl);
748 }
749
750 static HRESULT WINAPI PngFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
751     void **ppv)
752 {
753     PngEncoder *This = encoder_from_frame(iface);
754     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
755
756     if (!ppv) return E_INVALIDARG;
757
758     if (IsEqualIID(&IID_IUnknown, iid) ||
759         IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
760     {
761         *ppv = &This->lpFrameVtbl;
762     }
763     else
764     {
765         *ppv = NULL;
766         return E_NOINTERFACE;
767     }
768
769     IUnknown_AddRef((IUnknown*)*ppv);
770     return S_OK;
771 }
772
773 static ULONG WINAPI PngFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
774 {
775     PngEncoder *This = encoder_from_frame(iface);
776     return IUnknown_AddRef((IUnknown*)This);
777 }
778
779 static ULONG WINAPI PngFrameEncode_Release(IWICBitmapFrameEncode *iface)
780 {
781     PngEncoder *This = encoder_from_frame(iface);
782     return IUnknown_Release((IUnknown*)This);
783 }
784
785 static HRESULT WINAPI PngFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
786     IPropertyBag2 *pIEncoderOptions)
787 {
788     PngEncoder *This = encoder_from_frame(iface);
789     TRACE("(%p,%p)\n", iface, pIEncoderOptions);
790
791     if (This->frame_initialized) return WINCODEC_ERR_WRONGSTATE;
792
793     This->frame_initialized = TRUE;
794
795     return S_OK;
796 }
797
798 static HRESULT WINAPI PngFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
799     UINT uiWidth, UINT uiHeight)
800 {
801     PngEncoder *This = encoder_from_frame(iface);
802     TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
803
804     if (!This->frame_initialized || This->info_written) return WINCODEC_ERR_WRONGSTATE;
805
806     This->width = uiWidth;
807     This->height = uiHeight;
808
809     return S_OK;
810 }
811
812 static HRESULT WINAPI PngFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
813     double dpiX, double dpiY)
814 {
815     PngEncoder *This = encoder_from_frame(iface);
816     TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
817
818     if (!This->frame_initialized || This->info_written) return WINCODEC_ERR_WRONGSTATE;
819
820     This->xres = dpiX;
821     This->yres = dpiY;
822
823     return S_OK;
824 }
825
826 static HRESULT WINAPI PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
827     WICPixelFormatGUID *pPixelFormat)
828 {
829     PngEncoder *This = encoder_from_frame(iface);
830     int i;
831     TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
832
833     if (!This->frame_initialized || This->info_written) return WINCODEC_ERR_WRONGSTATE;
834
835     for (i=0; formats[i].guid; i++)
836     {
837         if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
838             break;
839     }
840
841     if (!formats[i].guid) i = 0;
842
843     This->format = &formats[i];
844     memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
845
846     return S_OK;
847 }
848
849 static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
850     UINT cCount, IWICColorContext **ppIColorContext)
851 {
852     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
853     return E_NOTIMPL;
854 }
855
856 static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
857     IWICPalette *pIPalette)
858 {
859     FIXME("(%p,%p): stub\n", iface, pIPalette);
860     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
861 }
862
863 static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
864     IWICBitmapSource *pIThumbnail)
865 {
866     FIXME("(%p,%p): stub\n", iface, pIThumbnail);
867     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
868 }
869
870 static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
871     UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
872 {
873     PngEncoder *This = encoder_from_frame(iface);
874     png_byte **row_pointers=NULL;
875     UINT i;
876     jmp_buf jmpbuf;
877     TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
878
879     if (!This->frame_initialized || !This->width || !This->height || !This->format)
880         return WINCODEC_ERR_WRONGSTATE;
881
882     if (lineCount == 0 || lineCount + This->lines_written > This->height)
883         return E_INVALIDARG;
884
885     /* set up setjmp/longjmp error handling */
886     if (setjmp(jmpbuf))
887     {
888         HeapFree(GetProcessHeap(), 0, row_pointers);
889         return E_FAIL;
890     }
891     ppng_set_error_fn(This->png_ptr, &jmpbuf, user_error_fn, user_warning_fn);
892
893     if (!This->info_written)
894     {
895         ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height,
896             This->format->bit_depth, This->format->color_type, PNG_INTERLACE_NONE,
897             PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
898
899         if (This->xres != 0.0 && This->yres != 0.0)
900         {
901             ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254,
902                 (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER);
903         }
904
905         ppng_write_info(This->png_ptr, This->info_ptr);
906
907         if (This->format->remove_filler)
908             ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER);
909
910         if (This->format->swap_rgb)
911             ppng_set_bgr(This->png_ptr);
912
913         This->info_written = TRUE;
914     }
915
916     row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*));
917     if (!row_pointers)
918         return E_OUTOFMEMORY;
919
920     for (i=0; i<lineCount; i++)
921         row_pointers[i] = pbPixels + cbStride * i;
922
923     ppng_write_rows(This->png_ptr, row_pointers, lineCount);
924     This->lines_written += lineCount;
925
926     HeapFree(GetProcessHeap(), 0, row_pointers);
927
928     return S_OK;
929 }
930
931 static HRESULT WINAPI PngFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
932     IWICBitmapSource *pIBitmapSource, WICRect *prc)
933 {
934     PngEncoder *This = encoder_from_frame(iface);
935     HRESULT hr;
936     WICRect rc;
937     WICPixelFormatGUID guid;
938     UINT stride;
939     BYTE *pixeldata;
940     TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc);
941
942     if (!This->frame_initialized || !This->width || !This->height)
943         return WINCODEC_ERR_WRONGSTATE;
944
945     if (!This->format)
946     {
947         hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid);
948         if (FAILED(hr)) return hr;
949         hr = IWICBitmapFrameEncode_SetPixelFormat(iface, &guid);
950         if (FAILED(hr)) return hr;
951     }
952
953     hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid);
954     if (FAILED(hr)) return hr;
955     if (memcmp(&guid, This->format->guid, sizeof(GUID)) != 0)
956     {
957         /* FIXME: should use WICConvertBitmapSource to convert */
958         ERR("format %s unsupported\n", debugstr_guid(&guid));
959         return E_FAIL;
960     }
961
962     if (This->xres == 0.0 || This->yres == 0.0)
963     {
964         double xres, yres;
965         hr = IWICBitmapSource_GetResolution(pIBitmapSource, &xres, &yres);
966         if (FAILED(hr)) return hr;
967         hr = IWICBitmapFrameEncode_SetResolution(iface, xres, yres);
968         if (FAILED(hr)) return hr;
969     }
970
971     if (!prc)
972     {
973         UINT width, height;
974         hr = IWICBitmapSource_GetSize(pIBitmapSource, &width, &height);
975         if (FAILED(hr)) return hr;
976         rc.X = 0;
977         rc.Y = 0;
978         rc.Width = width;
979         rc.Height = height;
980         prc = &rc;
981     }
982
983     if (prc->Width != This->width) return E_INVALIDARG;
984
985     stride = (This->format->bpp * This->width + 7)/8;
986
987     pixeldata = HeapAlloc(GetProcessHeap(), 0, stride * prc->Height);
988     if (!pixeldata) return E_OUTOFMEMORY;
989
990     hr = IWICBitmapSource_CopyPixels(pIBitmapSource, prc, stride,
991         stride*prc->Height, pixeldata);
992
993     if (SUCCEEDED(hr))
994     {
995         hr = IWICBitmapFrameEncode_WritePixels(iface, prc->Height, stride,
996             stride*prc->Height, pixeldata);
997     }
998
999     HeapFree(GetProcessHeap(), 0, pixeldata);
1000
1001     return hr;
1002 }
1003
1004 static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface)
1005 {
1006     PngEncoder *This = encoder_from_frame(iface);
1007     jmp_buf jmpbuf;
1008     TRACE("(%p)\n", iface);
1009
1010     if (!This->info_written || This->lines_written != This->height || This->frame_committed)
1011         return WINCODEC_ERR_WRONGSTATE;
1012
1013     /* set up setjmp/longjmp error handling */
1014     if (setjmp(jmpbuf))
1015     {
1016         return E_FAIL;
1017     }
1018     ppng_set_error_fn(This->png_ptr, &jmpbuf, user_error_fn, user_warning_fn);
1019
1020     ppng_write_end(This->png_ptr, This->info_ptr);
1021
1022     This->frame_committed = TRUE;
1023
1024     return S_OK;
1025 }
1026
1027 static HRESULT WINAPI PngFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
1028     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1029 {
1030     FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
1031     return E_NOTIMPL;
1032 }
1033
1034 static const IWICBitmapFrameEncodeVtbl PngEncoder_FrameVtbl = {
1035     PngFrameEncode_QueryInterface,
1036     PngFrameEncode_AddRef,
1037     PngFrameEncode_Release,
1038     PngFrameEncode_Initialize,
1039     PngFrameEncode_SetSize,
1040     PngFrameEncode_SetResolution,
1041     PngFrameEncode_SetPixelFormat,
1042     PngFrameEncode_SetColorContexts,
1043     PngFrameEncode_SetPalette,
1044     PngFrameEncode_SetThumbnail,
1045     PngFrameEncode_WritePixels,
1046     PngFrameEncode_WriteSource,
1047     PngFrameEncode_Commit,
1048     PngFrameEncode_GetMetadataQueryWriter
1049 };
1050
1051 static HRESULT WINAPI PngEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
1052     void **ppv)
1053 {
1054     PngEncoder *This = (PngEncoder*)iface;
1055     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1056
1057     if (!ppv) return E_INVALIDARG;
1058
1059     if (IsEqualIID(&IID_IUnknown, iid) ||
1060         IsEqualIID(&IID_IWICBitmapEncoder, iid))
1061     {
1062         *ppv = This;
1063     }
1064     else
1065     {
1066         *ppv = NULL;
1067         return E_NOINTERFACE;
1068     }
1069
1070     IUnknown_AddRef((IUnknown*)*ppv);
1071     return S_OK;
1072 }
1073
1074 static ULONG WINAPI PngEncoder_AddRef(IWICBitmapEncoder *iface)
1075 {
1076     PngEncoder *This = (PngEncoder*)iface;
1077     ULONG ref = InterlockedIncrement(&This->ref);
1078
1079     TRACE("(%p) refcount=%u\n", iface, ref);
1080
1081     return ref;
1082 }
1083
1084 static ULONG WINAPI PngEncoder_Release(IWICBitmapEncoder *iface)
1085 {
1086     PngEncoder *This = (PngEncoder*)iface;
1087     ULONG ref = InterlockedDecrement(&This->ref);
1088
1089     TRACE("(%p) refcount=%u\n", iface, ref);
1090
1091     if (ref == 0)
1092     {
1093         if (This->png_ptr)
1094             ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1095         if (This->stream)
1096             IStream_Release(This->stream);
1097         HeapFree(GetProcessHeap(), 0, This);
1098     }
1099
1100     return ref;
1101 }
1102
1103 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1104 {
1105     PngEncoder *This = ppng_get_io_ptr(png_ptr);
1106     HRESULT hr;
1107     ULONG byteswritten;
1108
1109     hr = IStream_Write(This->stream, data, length, &byteswritten);
1110     if (FAILED(hr) || byteswritten != length)
1111     {
1112         ppng_error(png_ptr, "failed writing data");
1113     }
1114 }
1115
1116 static void user_flush(png_structp png_ptr)
1117 {
1118 }
1119
1120 static HRESULT WINAPI PngEncoder_Initialize(IWICBitmapEncoder *iface,
1121     IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
1122 {
1123     PngEncoder *This = (PngEncoder*)iface;
1124     jmp_buf jmpbuf;
1125
1126     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
1127
1128     if (This->png_ptr)
1129         return WINCODEC_ERR_WRONGSTATE;
1130
1131     /* initialize libpng */
1132     This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1133     if (!This->png_ptr)
1134         return E_FAIL;
1135
1136     This->info_ptr = ppng_create_info_struct(This->png_ptr);
1137     if (!This->info_ptr)
1138     {
1139         ppng_destroy_write_struct(&This->png_ptr, NULL);
1140         This->png_ptr = NULL;
1141         return E_FAIL;
1142     }
1143
1144     IStream_AddRef(pIStream);
1145     This->stream = pIStream;
1146
1147     /* set up setjmp/longjmp error handling */
1148     if (setjmp(jmpbuf))
1149     {
1150         ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1151         This->png_ptr = NULL;
1152         IStream_Release(This->stream);
1153         This->stream = NULL;
1154         return E_FAIL;
1155     }
1156     ppng_set_error_fn(This->png_ptr, &jmpbuf, user_error_fn, user_warning_fn);
1157
1158     /* set up custom i/o handling */
1159     ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush);
1160
1161     return S_OK;
1162 }
1163
1164 static HRESULT WINAPI PngEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
1165     GUID *pguidContainerFormat)
1166 {
1167     FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
1168     return E_NOTIMPL;
1169 }
1170
1171 static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface,
1172     IWICBitmapEncoderInfo **ppIEncoderInfo)
1173 {
1174     FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo);
1175     return E_NOTIMPL;
1176 }
1177
1178 static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface,
1179     UINT cCount, IWICColorContext **ppIColorContext)
1180 {
1181     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1182     return E_NOTIMPL;
1183 }
1184
1185 static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette)
1186 {
1187     TRACE("(%p,%p)\n", iface, pIPalette);
1188     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1189 }
1190
1191 static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
1192 {
1193     TRACE("(%p,%p)\n", iface, pIThumbnail);
1194     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1195 }
1196
1197 static HRESULT WINAPI PngEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
1198 {
1199     TRACE("(%p,%p)\n", iface, pIPreview);
1200     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1201 }
1202
1203 static HRESULT WINAPI PngEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
1204     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
1205 {
1206     PngEncoder *This = (PngEncoder*)iface;
1207     HRESULT hr;
1208     TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
1209
1210     if (This->frame_count != 0)
1211         return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1212
1213     if (!This->stream)
1214         return WINCODEC_ERR_NOTINITIALIZED;
1215
1216     hr = CreatePropertyBag2(ppIEncoderOptions);
1217     if (FAILED(hr)) return hr;
1218
1219     This->frame_count = 1;
1220
1221     IWICBitmapEncoder_AddRef(iface);
1222     *ppIFrameEncode = (IWICBitmapFrameEncode*)&This->lpFrameVtbl;
1223
1224     return S_OK;
1225 }
1226
1227 static HRESULT WINAPI PngEncoder_Commit(IWICBitmapEncoder *iface)
1228 {
1229     PngEncoder *This = (PngEncoder*)iface;
1230     TRACE("(%p)\n", iface);
1231
1232     if (!This->frame_committed || This->committed)
1233         return WINCODEC_ERR_WRONGSTATE;
1234
1235     This->committed = TRUE;
1236
1237     return S_OK;
1238 }
1239
1240 static HRESULT WINAPI PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
1241     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1242 {
1243     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
1244     return E_NOTIMPL;
1245 }
1246
1247 static const IWICBitmapEncoderVtbl PngEncoder_Vtbl = {
1248     PngEncoder_QueryInterface,
1249     PngEncoder_AddRef,
1250     PngEncoder_Release,
1251     PngEncoder_Initialize,
1252     PngEncoder_GetContainerFormat,
1253     PngEncoder_GetEncoderInfo,
1254     PngEncoder_SetColorContexts,
1255     PngEncoder_SetPalette,
1256     PngEncoder_SetThumbnail,
1257     PngEncoder_SetPreview,
1258     PngEncoder_CreateNewFrame,
1259     PngEncoder_Commit,
1260     PngEncoder_GetMetadataQueryWriter
1261 };
1262
1263 HRESULT PngEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
1264 {
1265     PngEncoder *This;
1266     HRESULT ret;
1267
1268     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
1269
1270     *ppv = NULL;
1271
1272     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
1273
1274     if (!libpng_handle && !load_libpng())
1275     {
1276         ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG);
1277         return E_FAIL;
1278     }
1279
1280     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder));
1281     if (!This) return E_OUTOFMEMORY;
1282
1283     This->lpVtbl = &PngEncoder_Vtbl;
1284     This->lpFrameVtbl = &PngEncoder_FrameVtbl;
1285     This->ref = 1;
1286     This->png_ptr = NULL;
1287     This->info_ptr = NULL;
1288     This->stream = NULL;
1289     This->frame_count = 0;
1290     This->frame_initialized = FALSE;
1291     This->format = NULL;
1292     This->info_written = FALSE;
1293     This->width = 0;
1294     This->height = 0;
1295     This->xres = 0.0;
1296     This->yres = 0.0;
1297     This->lines_written = 0;
1298     This->frame_committed = FALSE;
1299     This->committed = FALSE;
1300
1301     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
1302     IUnknown_Release((IUnknown*)This);
1303
1304     return ret;
1305 }
1306
1307 #else /* !HAVE_PNG_H */
1308
1309 HRESULT PngDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
1310 {
1311     ERR("Trying to load PNG picture, but PNG supported not compiled in.\n");
1312     return E_FAIL;
1313 }
1314
1315 HRESULT PngEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
1316 {
1317     ERR("Trying to save PNG picture, but PNG supported not compiled in.\n");
1318     return E_FAIL;
1319 }
1320
1321 #endif