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