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