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