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