TESTING -- override pthreads to fix gstreamer v5
[wine/multimedia.git] / dlls / windowscodecs / pngformat.c
blobf39b31aec70a06992d948467df4ec69da045cc7a
1 /*
2 * Copyright 2009 Vincent Povirk for CodeWeavers
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.
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.
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
19 #include "config.h"
20 #include "wine/port.h"
22 #include <stdarg.h>
24 #ifdef HAVE_PNG_H
25 #include <png.h>
26 #endif
28 #define NONAMELESSUNION
29 #define COBJMACROS
31 #include "windef.h"
32 #include "winbase.h"
33 #include "objbase.h"
34 #include "wincodec.h"
35 #include "wincodecsdk.h"
37 #include "wincodecs_private.h"
39 #include "wine/debug.h"
40 #include "wine/library.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
44 static const WCHAR wszPngInterlaceOption[] = {'I','n','t','e','r','l','a','c','e','O','p','t','i','o','n',0};
46 static inline ULONG read_ulong_be(BYTE* data)
48 return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
51 static HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_size)
53 BYTE header[8];
54 HRESULT hr;
55 ULONG bytesread;
57 hr = IStream_Read(stream, header, 8, &bytesread);
58 if (FAILED(hr) || bytesread < 8)
60 if (SUCCEEDED(hr))
61 hr = E_FAIL;
62 return hr;
65 *data_size = read_ulong_be(&header[0]);
67 memcpy(type, &header[4], 4);
69 if (data)
71 *data = HeapAlloc(GetProcessHeap(), 0, *data_size);
72 if (!*data)
73 return E_OUTOFMEMORY;
75 hr = IStream_Read(stream, *data, *data_size, &bytesread);
77 if (FAILED(hr) || bytesread < *data_size)
79 if (SUCCEEDED(hr))
80 hr = E_FAIL;
81 HeapFree(GetProcessHeap(), 0, *data);
82 *data = NULL;
83 return hr;
86 /* Windows ignores CRC of the chunk */
89 return S_OK;
92 static HRESULT LoadTextMetadata(IStream *stream, const GUID *preferred_vendor,
93 DWORD persist_options, MetadataItem **items, DWORD *item_count)
95 HRESULT hr;
96 BYTE type[4];
97 BYTE *data;
98 ULONG data_size;
99 ULONG name_len, value_len;
100 BYTE *name_end_ptr;
101 LPSTR name, value;
102 MetadataItem *result;
104 hr = read_png_chunk(stream, type, &data, &data_size);
105 if (FAILED(hr)) return hr;
107 name_end_ptr = memchr(data, 0, data_size);
109 name_len = name_end_ptr - data;
111 if (!name_end_ptr || name_len > 79)
113 HeapFree(GetProcessHeap(), 0, data);
114 return E_FAIL;
117 value_len = data_size - name_len - 1;
119 result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem));
120 name = HeapAlloc(GetProcessHeap(), 0, name_len + 1);
121 value = HeapAlloc(GetProcessHeap(), 0, value_len + 1);
122 if (!result || !name || !value)
124 HeapFree(GetProcessHeap(), 0, data);
125 HeapFree(GetProcessHeap(), 0, result);
126 HeapFree(GetProcessHeap(), 0, name);
127 HeapFree(GetProcessHeap(), 0, value);
128 return E_OUTOFMEMORY;
131 PropVariantInit(&result[0].schema);
132 PropVariantInit(&result[0].id);
133 PropVariantInit(&result[0].value);
135 memcpy(name, data, name_len + 1);
136 memcpy(value, name_end_ptr + 1, value_len);
137 value[value_len] = 0;
139 result[0].id.vt = VT_LPSTR;
140 result[0].id.u.pszVal = name;
141 result[0].value.vt = VT_LPSTR;
142 result[0].value.u.pszVal = value;
144 *items = result;
145 *item_count = 1;
147 HeapFree(GetProcessHeap(), 0, data);
149 return S_OK;
152 static const MetadataHandlerVtbl TextReader_Vtbl = {
154 &CLSID_WICPngTextMetadataReader,
155 LoadTextMetadata
158 HRESULT PngTextReader_CreateInstance(REFIID iid, void** ppv)
160 return MetadataReader_Create(&TextReader_Vtbl, iid, ppv);
163 static HRESULT LoadGamaMetadata(IStream *stream, const GUID *preferred_vendor,
164 DWORD persist_options, MetadataItem **items, DWORD *item_count)
166 HRESULT hr;
167 BYTE type[4];
168 BYTE *data;
169 ULONG data_size;
170 ULONG gamma;
171 static const WCHAR ImageGamma[] = {'I','m','a','g','e','G','a','m','m','a',0};
172 LPWSTR name;
173 MetadataItem *result;
175 hr = read_png_chunk(stream, type, &data, &data_size);
176 if (FAILED(hr)) return hr;
178 if (data_size < 4)
180 HeapFree(GetProcessHeap(), 0, data);
181 return E_FAIL;
184 gamma = read_ulong_be(data);
186 HeapFree(GetProcessHeap(), 0, data);
188 result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem));
189 name = HeapAlloc(GetProcessHeap(), 0, sizeof(ImageGamma));
190 if (!result || !name)
192 HeapFree(GetProcessHeap(), 0, result);
193 HeapFree(GetProcessHeap(), 0, name);
194 return E_OUTOFMEMORY;
197 PropVariantInit(&result[0].schema);
198 PropVariantInit(&result[0].id);
199 PropVariantInit(&result[0].value);
201 memcpy(name, ImageGamma, sizeof(ImageGamma));
203 result[0].id.vt = VT_LPWSTR;
204 result[0].id.u.pwszVal = name;
205 result[0].value.vt = VT_UI4;
206 result[0].value.u.ulVal = gamma;
208 *items = result;
209 *item_count = 1;
211 return S_OK;
214 static const MetadataHandlerVtbl GamaReader_Vtbl = {
216 &CLSID_WICPngGamaMetadataReader,
217 LoadGamaMetadata
220 HRESULT PngGamaReader_CreateInstance(REFIID iid, void** ppv)
222 return MetadataReader_Create(&GamaReader_Vtbl, iid, ppv);
225 #ifdef SONAME_LIBPNG
227 static void *libpng_handle;
228 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
229 MAKE_FUNCPTR(png_create_read_struct);
230 MAKE_FUNCPTR(png_create_info_struct);
231 MAKE_FUNCPTR(png_create_write_struct);
232 MAKE_FUNCPTR(png_destroy_read_struct);
233 MAKE_FUNCPTR(png_destroy_write_struct);
234 MAKE_FUNCPTR(png_error);
235 MAKE_FUNCPTR(png_get_bit_depth);
236 MAKE_FUNCPTR(png_get_color_type);
237 MAKE_FUNCPTR(png_get_error_ptr);
238 MAKE_FUNCPTR(png_get_iCCP);
239 MAKE_FUNCPTR(png_get_image_height);
240 MAKE_FUNCPTR(png_get_image_width);
241 MAKE_FUNCPTR(png_get_io_ptr);
242 MAKE_FUNCPTR(png_get_pHYs);
243 MAKE_FUNCPTR(png_get_PLTE);
244 MAKE_FUNCPTR(png_get_tRNS);
245 MAKE_FUNCPTR(png_set_bgr);
246 MAKE_FUNCPTR(png_set_crc_action);
247 MAKE_FUNCPTR(png_set_error_fn);
248 #ifdef HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8
249 MAKE_FUNCPTR(png_set_expand_gray_1_2_4_to_8);
250 #else
251 MAKE_FUNCPTR(png_set_gray_1_2_4_to_8);
252 #endif
253 MAKE_FUNCPTR(png_set_filler);
254 MAKE_FUNCPTR(png_set_gray_to_rgb);
255 MAKE_FUNCPTR(png_set_interlace_handling);
256 MAKE_FUNCPTR(png_set_IHDR);
257 MAKE_FUNCPTR(png_set_pHYs);
258 MAKE_FUNCPTR(png_set_read_fn);
259 MAKE_FUNCPTR(png_set_strip_16);
260 MAKE_FUNCPTR(png_set_tRNS_to_alpha);
261 MAKE_FUNCPTR(png_set_write_fn);
262 MAKE_FUNCPTR(png_read_end);
263 MAKE_FUNCPTR(png_read_image);
264 MAKE_FUNCPTR(png_read_info);
265 MAKE_FUNCPTR(png_write_end);
266 MAKE_FUNCPTR(png_write_info);
267 MAKE_FUNCPTR(png_write_rows);
268 #undef MAKE_FUNCPTR
270 static CRITICAL_SECTION init_png_cs;
271 static CRITICAL_SECTION_DEBUG init_png_cs_debug =
273 0, 0, &init_png_cs,
274 { &init_png_cs_debug.ProcessLocksList,
275 &init_png_cs_debug.ProcessLocksList },
276 0, 0, { (DWORD_PTR)(__FILE__ ": init_png_cs") }
278 static CRITICAL_SECTION init_png_cs = { &init_png_cs_debug, -1, 0, 0, 0, 0 };
280 static void *load_libpng(void)
282 void *result;
284 EnterCriticalSection(&init_png_cs);
286 if(!libpng_handle && (libpng_handle = wine_dlopen(SONAME_LIBPNG, RTLD_NOW, NULL, 0)) != NULL) {
288 #define LOAD_FUNCPTR(f) \
289 if((p##f = wine_dlsym(libpng_handle, #f, NULL, 0)) == NULL) { \
290 libpng_handle = NULL; \
291 LeaveCriticalSection(&init_png_cs); \
292 return NULL; \
294 LOAD_FUNCPTR(png_create_read_struct);
295 LOAD_FUNCPTR(png_create_info_struct);
296 LOAD_FUNCPTR(png_create_write_struct);
297 LOAD_FUNCPTR(png_destroy_read_struct);
298 LOAD_FUNCPTR(png_destroy_write_struct);
299 LOAD_FUNCPTR(png_error);
300 LOAD_FUNCPTR(png_get_bit_depth);
301 LOAD_FUNCPTR(png_get_color_type);
302 LOAD_FUNCPTR(png_get_error_ptr);
303 LOAD_FUNCPTR(png_get_iCCP);
304 LOAD_FUNCPTR(png_get_image_height);
305 LOAD_FUNCPTR(png_get_image_width);
306 LOAD_FUNCPTR(png_get_io_ptr);
307 LOAD_FUNCPTR(png_get_pHYs);
308 LOAD_FUNCPTR(png_get_PLTE);
309 LOAD_FUNCPTR(png_get_tRNS);
310 LOAD_FUNCPTR(png_set_bgr);
311 LOAD_FUNCPTR(png_set_crc_action);
312 LOAD_FUNCPTR(png_set_error_fn);
313 #ifdef HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8
314 LOAD_FUNCPTR(png_set_expand_gray_1_2_4_to_8);
315 #else
316 LOAD_FUNCPTR(png_set_gray_1_2_4_to_8);
317 #endif
318 LOAD_FUNCPTR(png_set_filler);
319 LOAD_FUNCPTR(png_set_gray_to_rgb);
320 LOAD_FUNCPTR(png_set_interlace_handling);
321 LOAD_FUNCPTR(png_set_IHDR);
322 LOAD_FUNCPTR(png_set_pHYs);
323 LOAD_FUNCPTR(png_set_read_fn);
324 LOAD_FUNCPTR(png_set_strip_16);
325 LOAD_FUNCPTR(png_set_tRNS_to_alpha);
326 LOAD_FUNCPTR(png_set_write_fn);
327 LOAD_FUNCPTR(png_read_end);
328 LOAD_FUNCPTR(png_read_image);
329 LOAD_FUNCPTR(png_read_info);
330 LOAD_FUNCPTR(png_write_end);
331 LOAD_FUNCPTR(png_write_info);
332 LOAD_FUNCPTR(png_write_rows);
334 #undef LOAD_FUNCPTR
337 result = libpng_handle;
339 LeaveCriticalSection(&init_png_cs);
341 return result;
344 static void user_error_fn(png_structp png_ptr, png_const_charp error_message)
346 jmp_buf *pjmpbuf;
348 /* This uses setjmp/longjmp just like the default. We can't use the
349 * default because there's no way to access the jmp buffer in the png_struct
350 * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
351 WARN("PNG error: %s\n", debugstr_a(error_message));
352 pjmpbuf = ppng_get_error_ptr(png_ptr);
353 longjmp(*pjmpbuf, 1);
356 static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message)
358 WARN("PNG warning: %s\n", debugstr_a(warning_message));
361 typedef struct {
362 ULARGE_INTEGER ofs, len;
363 IWICMetadataReader* reader;
364 } metadata_block_info;
366 typedef struct {
367 IWICBitmapDecoder IWICBitmapDecoder_iface;
368 IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
369 IWICMetadataBlockReader IWICMetadataBlockReader_iface;
370 LONG ref;
371 IStream *stream;
372 png_structp png_ptr;
373 png_infop info_ptr;
374 png_infop end_info;
375 BOOL initialized;
376 int bpp;
377 int width, height;
378 UINT stride;
379 const WICPixelFormatGUID *format;
380 BYTE *image_bits;
381 CRITICAL_SECTION lock; /* must be held when png structures are accessed or initialized is set */
382 ULONG metadata_count;
383 metadata_block_info* metadata_blocks;
384 } PngDecoder;
386 static inline PngDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
388 return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapDecoder_iface);
391 static inline PngDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
393 return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapFrameDecode_iface);
396 static inline PngDecoder *impl_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface)
398 return CONTAINING_RECORD(iface, PngDecoder, IWICMetadataBlockReader_iface);
401 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl;
403 static HRESULT WINAPI PngDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
404 void **ppv)
406 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
407 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
409 if (!ppv) return E_INVALIDARG;
411 if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
413 *ppv = &This->IWICBitmapDecoder_iface;
415 else
417 *ppv = NULL;
418 return E_NOINTERFACE;
421 IUnknown_AddRef((IUnknown*)*ppv);
422 return S_OK;
425 static ULONG WINAPI PngDecoder_AddRef(IWICBitmapDecoder *iface)
427 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
428 ULONG ref = InterlockedIncrement(&This->ref);
430 TRACE("(%p) refcount=%u\n", iface, ref);
432 return ref;
435 static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface)
437 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
438 ULONG ref = InterlockedDecrement(&This->ref);
439 ULONG i;
441 TRACE("(%p) refcount=%u\n", iface, ref);
443 if (ref == 0)
445 if (This->stream)
446 IStream_Release(This->stream);
447 if (This->png_ptr)
448 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
449 This->lock.DebugInfo->Spare[0] = 0;
450 DeleteCriticalSection(&This->lock);
451 HeapFree(GetProcessHeap(), 0, This->image_bits);
452 for (i=0; i<This->metadata_count; i++)
454 if (This->metadata_blocks[i].reader)
455 IWICMetadataReader_Release(This->metadata_blocks[i].reader);
457 HeapFree(GetProcessHeap(), 0, This->metadata_blocks);
458 HeapFree(GetProcessHeap(), 0, This);
461 return ref;
464 static HRESULT WINAPI PngDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream,
465 DWORD *capability)
467 HRESULT hr;
469 TRACE("(%p,%p,%p)\n", iface, stream, capability);
471 if (!stream || !capability) return E_INVALIDARG;
473 hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand);
474 if (hr != S_OK) return hr;
476 *capability = WICBitmapDecoderCapabilityCanDecodeAllImages |
477 WICBitmapDecoderCapabilityCanDecodeSomeImages;
478 /* FIXME: WICBitmapDecoderCapabilityCanEnumerateMetadata */
479 return S_OK;
482 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
484 IStream *stream = ppng_get_io_ptr(png_ptr);
485 HRESULT hr;
486 ULONG bytesread;
488 hr = IStream_Read(stream, data, length, &bytesread);
489 if (FAILED(hr) || bytesread != length)
491 ppng_error(png_ptr, "failed reading data");
495 static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
496 WICDecodeOptions cacheOptions)
498 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
499 LARGE_INTEGER seek;
500 HRESULT hr=S_OK;
501 png_bytep *row_pointers=NULL;
502 UINT image_size;
503 UINT i;
504 int color_type, bit_depth;
505 png_bytep trans;
506 int num_trans;
507 png_uint_32 transparency;
508 png_color_16p trans_values;
509 jmp_buf jmpbuf;
510 BYTE chunk_type[4];
511 ULONG chunk_size;
512 ULARGE_INTEGER chunk_start;
513 ULONG metadata_blocks_size = 0;
515 TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
517 EnterCriticalSection(&This->lock);
519 /* initialize libpng */
520 This->png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
521 if (!This->png_ptr)
523 hr = E_FAIL;
524 goto end;
527 This->info_ptr = ppng_create_info_struct(This->png_ptr);
528 if (!This->info_ptr)
530 ppng_destroy_read_struct(&This->png_ptr, NULL, NULL);
531 This->png_ptr = NULL;
532 hr = E_FAIL;
533 goto end;
536 This->end_info = ppng_create_info_struct(This->png_ptr);
537 if (!This->info_ptr)
539 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, NULL);
540 This->png_ptr = NULL;
541 hr = E_FAIL;
542 goto end;
545 /* set up setjmp/longjmp error handling */
546 if (setjmp(jmpbuf))
548 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
549 HeapFree(GetProcessHeap(), 0, row_pointers);
550 This->png_ptr = NULL;
551 hr = E_FAIL;
552 goto end;
554 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
555 ppng_set_crc_action(This->png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
557 /* seek to the start of the stream */
558 seek.QuadPart = 0;
559 hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
560 if (FAILED(hr)) goto end;
562 /* set up custom i/o handling */
563 ppng_set_read_fn(This->png_ptr, pIStream, user_read_data);
565 /* read the header */
566 ppng_read_info(This->png_ptr, This->info_ptr);
568 /* choose a pixel format */
569 color_type = ppng_get_color_type(This->png_ptr, This->info_ptr);
570 bit_depth = ppng_get_bit_depth(This->png_ptr, This->info_ptr);
572 /* check for color-keyed alpha */
573 transparency = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans, &num_trans, &trans_values);
575 if (transparency && color_type != PNG_COLOR_TYPE_PALETTE)
577 /* expand to RGBA */
578 if (color_type == PNG_COLOR_TYPE_GRAY)
580 if (bit_depth < 8)
582 #ifdef HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8
583 ppng_set_expand_gray_1_2_4_to_8(This->png_ptr);
584 #else
585 ppng_set_gray_1_2_4_to_8(This->png_ptr);
586 #endif
587 bit_depth = 8;
589 ppng_set_gray_to_rgb(This->png_ptr);
591 ppng_set_tRNS_to_alpha(This->png_ptr);
592 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
595 switch (color_type)
597 case PNG_COLOR_TYPE_GRAY:
598 This->bpp = bit_depth;
599 switch (bit_depth)
601 case 1: This->format = &GUID_WICPixelFormatBlackWhite; break;
602 case 2: This->format = &GUID_WICPixelFormat2bppGray; break;
603 case 4: This->format = &GUID_WICPixelFormat4bppGray; break;
604 case 8: This->format = &GUID_WICPixelFormat8bppGray; break;
605 case 16: This->format = &GUID_WICPixelFormat16bppGray; break;
606 default:
607 ERR("invalid grayscale bit depth: %i\n", bit_depth);
608 hr = E_FAIL;
609 goto end;
611 break;
612 case PNG_COLOR_TYPE_GRAY_ALPHA:
613 /* WIC does not support grayscale alpha formats so use RGBA */
614 ppng_set_gray_to_rgb(This->png_ptr);
615 /* fall through */
616 case PNG_COLOR_TYPE_RGB_ALPHA:
617 This->bpp = bit_depth * 4;
618 switch (bit_depth)
620 case 8:
621 ppng_set_bgr(This->png_ptr);
622 This->format = &GUID_WICPixelFormat32bppBGRA;
623 break;
624 case 16: This->format = &GUID_WICPixelFormat64bppRGBA; break;
625 default:
626 ERR("invalid RGBA bit depth: %i\n", bit_depth);
627 hr = E_FAIL;
628 goto end;
630 break;
631 case PNG_COLOR_TYPE_PALETTE:
632 This->bpp = bit_depth;
633 switch (bit_depth)
635 case 1: This->format = &GUID_WICPixelFormat1bppIndexed; break;
636 case 2: This->format = &GUID_WICPixelFormat2bppIndexed; break;
637 case 4: This->format = &GUID_WICPixelFormat4bppIndexed; break;
638 case 8: This->format = &GUID_WICPixelFormat8bppIndexed; break;
639 default:
640 ERR("invalid indexed color bit depth: %i\n", bit_depth);
641 hr = E_FAIL;
642 goto end;
644 break;
645 case PNG_COLOR_TYPE_RGB:
646 This->bpp = bit_depth * 3;
647 switch (bit_depth)
649 case 8:
650 ppng_set_bgr(This->png_ptr);
651 This->format = &GUID_WICPixelFormat24bppBGR;
652 break;
653 case 16: This->format = &GUID_WICPixelFormat48bppRGB; break;
654 default:
655 ERR("invalid RGB color bit depth: %i\n", bit_depth);
656 hr = E_FAIL;
657 goto end;
659 break;
660 default:
661 ERR("invalid color type %i\n", color_type);
662 hr = E_FAIL;
663 goto end;
666 /* read the image data */
667 This->width = ppng_get_image_width(This->png_ptr, This->info_ptr);
668 This->height = ppng_get_image_height(This->png_ptr, This->info_ptr);
669 This->stride = This->width * This->bpp;
670 image_size = This->stride * This->height;
672 This->image_bits = HeapAlloc(GetProcessHeap(), 0, image_size);
673 if (!This->image_bits)
675 hr = E_OUTOFMEMORY;
676 goto end;
679 row_pointers = HeapAlloc(GetProcessHeap(), 0, sizeof(png_bytep)*This->height);
680 if (!row_pointers)
682 hr = E_OUTOFMEMORY;
683 goto end;
686 for (i=0; i<This->height; i++)
687 row_pointers[i] = This->image_bits + i * This->stride;
689 ppng_read_image(This->png_ptr, row_pointers);
691 HeapFree(GetProcessHeap(), 0, row_pointers);
692 row_pointers = NULL;
694 ppng_read_end(This->png_ptr, This->end_info);
696 /* Find the metadata chunks in the file. */
697 seek.QuadPart = 8;
698 hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, &chunk_start);
699 if (FAILED(hr)) goto end;
703 hr = read_png_chunk(pIStream, chunk_type, NULL, &chunk_size);
704 if (FAILED(hr)) goto end;
706 if (chunk_type[0] >= 'a' && chunk_type[0] <= 'z' &&
707 memcmp(chunk_type, "tRNS", 4) && memcmp(chunk_type, "pHYs", 4))
709 /* This chunk is considered metadata. */
710 if (This->metadata_count == metadata_blocks_size)
712 metadata_block_info* new_metadata_blocks;
713 ULONG new_metadata_blocks_size;
715 new_metadata_blocks_size = 4 + metadata_blocks_size * 2;
716 new_metadata_blocks = HeapAlloc(GetProcessHeap(), 0,
717 new_metadata_blocks_size * sizeof(*new_metadata_blocks));
719 if (!new_metadata_blocks)
721 hr = E_OUTOFMEMORY;
722 goto end;
725 memcpy(new_metadata_blocks, This->metadata_blocks,
726 This->metadata_count * sizeof(*new_metadata_blocks));
728 HeapFree(GetProcessHeap(), 0, This->metadata_blocks);
729 This->metadata_blocks = new_metadata_blocks;
730 metadata_blocks_size = new_metadata_blocks_size;
733 This->metadata_blocks[This->metadata_count].ofs = chunk_start;
734 This->metadata_blocks[This->metadata_count].len.QuadPart = chunk_size + 12;
735 This->metadata_blocks[This->metadata_count].reader = NULL;
736 This->metadata_count++;
739 seek.QuadPart = chunk_start.QuadPart + chunk_size + 12; /* skip data and CRC */
740 hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, &chunk_start);
741 if (FAILED(hr)) goto end;
742 } while (memcmp(chunk_type, "IEND", 4));
744 This->stream = pIStream;
745 IStream_AddRef(This->stream);
747 This->initialized = TRUE;
749 end:
750 LeaveCriticalSection(&This->lock);
752 return hr;
755 static HRESULT WINAPI PngDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
756 GUID *pguidContainerFormat)
758 memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
759 return S_OK;
762 static HRESULT WINAPI PngDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
763 IWICBitmapDecoderInfo **ppIDecoderInfo)
765 HRESULT hr;
766 IWICComponentInfo *compinfo;
768 TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
770 hr = CreateComponentInfo(&CLSID_WICPngDecoder, &compinfo);
771 if (FAILED(hr)) return hr;
773 hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
774 (void**)ppIDecoderInfo);
776 IWICComponentInfo_Release(compinfo);
778 return hr;
781 static HRESULT WINAPI PngDecoder_CopyPalette(IWICBitmapDecoder *iface,
782 IWICPalette *pIPalette)
784 FIXME("(%p,%p): stub\n", iface, pIPalette);
785 return E_NOTIMPL;
788 static HRESULT WINAPI PngDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
789 IWICMetadataQueryReader **ppIMetadataQueryReader)
791 FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
792 return E_NOTIMPL;
795 static HRESULT WINAPI PngDecoder_GetPreview(IWICBitmapDecoder *iface,
796 IWICBitmapSource **ppIBitmapSource)
798 TRACE("(%p,%p)\n", iface, ppIBitmapSource);
800 if (!ppIBitmapSource) return E_INVALIDARG;
802 *ppIBitmapSource = NULL;
803 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
806 static HRESULT WINAPI PngDecoder_GetColorContexts(IWICBitmapDecoder *iface,
807 UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
809 TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
810 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
813 static HRESULT WINAPI PngDecoder_GetThumbnail(IWICBitmapDecoder *iface,
814 IWICBitmapSource **ppIThumbnail)
816 TRACE("(%p,%p)\n", iface, ppIThumbnail);
818 if (!ppIThumbnail) return E_INVALIDARG;
820 *ppIThumbnail = NULL;
821 return WINCODEC_ERR_CODECNOTHUMBNAIL;
824 static HRESULT WINAPI PngDecoder_GetFrameCount(IWICBitmapDecoder *iface,
825 UINT *pCount)
827 if (!pCount) return E_INVALIDARG;
829 *pCount = 1;
830 return S_OK;
833 static HRESULT WINAPI PngDecoder_GetFrame(IWICBitmapDecoder *iface,
834 UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
836 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
837 TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
839 if (!This->initialized) return WINCODEC_ERR_FRAMEMISSING;
841 if (index != 0) return E_INVALIDARG;
843 IWICBitmapDecoder_AddRef(iface);
845 *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface;
847 return S_OK;
850 static const IWICBitmapDecoderVtbl PngDecoder_Vtbl = {
851 PngDecoder_QueryInterface,
852 PngDecoder_AddRef,
853 PngDecoder_Release,
854 PngDecoder_QueryCapability,
855 PngDecoder_Initialize,
856 PngDecoder_GetContainerFormat,
857 PngDecoder_GetDecoderInfo,
858 PngDecoder_CopyPalette,
859 PngDecoder_GetMetadataQueryReader,
860 PngDecoder_GetPreview,
861 PngDecoder_GetColorContexts,
862 PngDecoder_GetThumbnail,
863 PngDecoder_GetFrameCount,
864 PngDecoder_GetFrame
867 static HRESULT WINAPI PngDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
868 void **ppv)
870 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
871 if (!ppv) return E_INVALIDARG;
873 if (IsEqualIID(&IID_IUnknown, iid) ||
874 IsEqualIID(&IID_IWICBitmapSource, iid) ||
875 IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
877 *ppv = &This->IWICBitmapFrameDecode_iface;
879 else if (IsEqualIID(&IID_IWICMetadataBlockReader, iid))
881 *ppv = &This->IWICMetadataBlockReader_iface;
883 else
885 *ppv = NULL;
886 return E_NOINTERFACE;
889 IUnknown_AddRef((IUnknown*)*ppv);
890 return S_OK;
893 static ULONG WINAPI PngDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface)
895 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
896 return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
899 static ULONG WINAPI PngDecoder_Frame_Release(IWICBitmapFrameDecode *iface)
901 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
902 return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
905 static HRESULT WINAPI PngDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface,
906 UINT *puiWidth, UINT *puiHeight)
908 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
909 *puiWidth = This->width;
910 *puiHeight = This->height;
911 TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight);
912 return S_OK;
915 static HRESULT WINAPI PngDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface,
916 WICPixelFormatGUID *pPixelFormat)
918 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
919 TRACE("(%p,%p)\n", iface, pPixelFormat);
921 memcpy(pPixelFormat, This->format, sizeof(GUID));
923 return S_OK;
926 static HRESULT WINAPI PngDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface,
927 double *pDpiX, double *pDpiY)
929 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
930 png_uint_32 ret, xres, yres;
931 int unit_type;
933 EnterCriticalSection(&This->lock);
935 ret = ppng_get_pHYs(This->png_ptr, This->info_ptr, &xres, &yres, &unit_type);
937 if (ret && unit_type == PNG_RESOLUTION_METER)
939 *pDpiX = xres * 0.0254;
940 *pDpiY = yres * 0.0254;
942 else
944 WARN("no pHYs block present\n");
945 *pDpiX = *pDpiY = 96.0;
948 LeaveCriticalSection(&This->lock);
950 TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY);
952 return S_OK;
955 static HRESULT WINAPI PngDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface,
956 IWICPalette *pIPalette)
958 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
959 png_uint_32 ret;
960 png_colorp png_palette;
961 int num_palette;
962 WICColor palette[256];
963 png_bytep trans_alpha;
964 int num_trans;
965 png_color_16p trans_values;
966 int i;
967 HRESULT hr=S_OK;
969 TRACE("(%p,%p)\n", iface, pIPalette);
971 EnterCriticalSection(&This->lock);
973 ret = ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette);
974 if (!ret)
976 hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
977 goto end;
980 if (num_palette > 256)
982 ERR("palette has %i colors?!\n", num_palette);
983 hr = E_FAIL;
984 goto end;
987 ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values);
988 if (!ret) num_trans = 0;
990 for (i=0; i<num_palette; i++)
992 BYTE alpha = (i < num_trans) ? trans_alpha[i] : 0xff;
993 palette[i] = (alpha << 24 |
994 png_palette[i].red << 16|
995 png_palette[i].green << 8|
996 png_palette[i].blue);
999 end:
1001 LeaveCriticalSection(&This->lock);
1003 if (SUCCEEDED(hr))
1004 hr = IWICPalette_InitializeCustom(pIPalette, palette, num_palette);
1006 return hr;
1009 static HRESULT WINAPI PngDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface,
1010 const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
1012 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1013 TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
1015 return copy_pixels(This->bpp, This->image_bits,
1016 This->width, This->height, This->stride,
1017 prc, cbStride, cbBufferSize, pbBuffer);
1020 static HRESULT WINAPI PngDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
1021 IWICMetadataQueryReader **ppIMetadataQueryReader)
1023 FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
1024 return E_NOTIMPL;
1027 static HRESULT WINAPI PngDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface,
1028 UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
1030 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1031 png_charp name;
1032 BYTE *profile;
1033 png_uint_32 len;
1034 int compression_type;
1035 HRESULT hr;
1037 TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
1039 if (!pcActualCount) return E_INVALIDARG;
1041 EnterCriticalSection(&This->lock);
1043 if (ppng_get_iCCP(This->png_ptr, This->info_ptr, &name, &compression_type, (void *)&profile, &len))
1045 if (cCount && ppIColorContexts)
1047 hr = IWICColorContext_InitializeFromMemory(*ppIColorContexts, profile, len);
1048 if (FAILED(hr))
1050 LeaveCriticalSection(&This->lock);
1051 return hr;
1054 *pcActualCount = 1;
1056 else
1057 *pcActualCount = 0;
1059 LeaveCriticalSection(&This->lock);
1061 return S_OK;
1064 static HRESULT WINAPI PngDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
1065 IWICBitmapSource **ppIThumbnail)
1067 TRACE("(%p,%p)\n", iface, ppIThumbnail);
1069 if (!ppIThumbnail) return E_INVALIDARG;
1071 *ppIThumbnail = NULL;
1072 return WINCODEC_ERR_CODECNOTHUMBNAIL;
1075 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl = {
1076 PngDecoder_Frame_QueryInterface,
1077 PngDecoder_Frame_AddRef,
1078 PngDecoder_Frame_Release,
1079 PngDecoder_Frame_GetSize,
1080 PngDecoder_Frame_GetPixelFormat,
1081 PngDecoder_Frame_GetResolution,
1082 PngDecoder_Frame_CopyPalette,
1083 PngDecoder_Frame_CopyPixels,
1084 PngDecoder_Frame_GetMetadataQueryReader,
1085 PngDecoder_Frame_GetColorContexts,
1086 PngDecoder_Frame_GetThumbnail
1089 static HRESULT WINAPI PngDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid,
1090 void **ppv)
1092 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1093 return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv);
1096 static ULONG WINAPI PngDecoder_Block_AddRef(IWICMetadataBlockReader *iface)
1098 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1099 return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
1102 static ULONG WINAPI PngDecoder_Block_Release(IWICMetadataBlockReader *iface)
1104 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1105 return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1108 static HRESULT WINAPI PngDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
1109 GUID *pguidContainerFormat)
1111 if (!pguidContainerFormat) return E_INVALIDARG;
1112 memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
1113 return S_OK;
1116 static HRESULT WINAPI PngDecoder_Block_GetCount(IWICMetadataBlockReader *iface,
1117 UINT *pcCount)
1119 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1121 TRACE("%p,%p\n", iface, pcCount);
1123 if (!pcCount) return E_INVALIDARG;
1125 *pcCount = This->metadata_count;
1127 return S_OK;
1130 static HRESULT WINAPI PngDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
1131 UINT nIndex, IWICMetadataReader **ppIMetadataReader)
1133 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1134 HRESULT hr;
1135 IWICComponentFactory* factory;
1136 IWICStream* stream;
1138 TRACE("%p,%d,%p\n", iface, nIndex, ppIMetadataReader);
1140 if (nIndex >= This->metadata_count || !ppIMetadataReader)
1141 return E_INVALIDARG;
1143 if (!This->metadata_blocks[nIndex].reader)
1145 hr = StreamImpl_Create(&stream);
1147 if (SUCCEEDED(hr))
1149 hr = IWICStream_InitializeFromIStreamRegion(stream, This->stream,
1150 This->metadata_blocks[nIndex].ofs, This->metadata_blocks[nIndex].len);
1152 if (SUCCEEDED(hr))
1153 hr = ComponentFactory_CreateInstance(&IID_IWICComponentFactory, (void**)&factory);
1155 if (SUCCEEDED(hr))
1157 hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory,
1158 &GUID_ContainerFormatPng, NULL, WICMetadataCreationAllowUnknown,
1159 (IStream*)stream, &This->metadata_blocks[nIndex].reader);
1161 IWICComponentFactory_Release(factory);
1164 IWICStream_Release(stream);
1167 if (FAILED(hr))
1169 *ppIMetadataReader = NULL;
1170 return hr;
1174 *ppIMetadataReader = This->metadata_blocks[nIndex].reader;
1175 IWICMetadataReader_AddRef(*ppIMetadataReader);
1177 return S_OK;
1180 static HRESULT WINAPI PngDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface,
1181 IEnumUnknown **ppIEnumMetadata)
1183 FIXME("%p,%p\n", iface, ppIEnumMetadata);
1184 return E_NOTIMPL;
1187 static const IWICMetadataBlockReaderVtbl PngDecoder_BlockVtbl = {
1188 PngDecoder_Block_QueryInterface,
1189 PngDecoder_Block_AddRef,
1190 PngDecoder_Block_Release,
1191 PngDecoder_Block_GetContainerFormat,
1192 PngDecoder_Block_GetCount,
1193 PngDecoder_Block_GetReaderByIndex,
1194 PngDecoder_Block_GetEnumerator,
1197 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
1199 PngDecoder *This;
1200 HRESULT ret;
1202 TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
1204 *ppv = NULL;
1206 if (!load_libpng())
1208 ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
1209 return E_FAIL;
1212 This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngDecoder));
1213 if (!This) return E_OUTOFMEMORY;
1215 This->IWICBitmapDecoder_iface.lpVtbl = &PngDecoder_Vtbl;
1216 This->IWICBitmapFrameDecode_iface.lpVtbl = &PngDecoder_FrameVtbl;
1217 This->IWICMetadataBlockReader_iface.lpVtbl = &PngDecoder_BlockVtbl;
1218 This->ref = 1;
1219 This->png_ptr = NULL;
1220 This->info_ptr = NULL;
1221 This->end_info = NULL;
1222 This->stream = NULL;
1223 This->initialized = FALSE;
1224 This->image_bits = NULL;
1225 InitializeCriticalSection(&This->lock);
1226 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngDecoder.lock");
1227 This->metadata_count = 0;
1228 This->metadata_blocks = NULL;
1230 ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
1231 IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1233 return ret;
1236 struct png_pixelformat {
1237 const WICPixelFormatGUID *guid;
1238 UINT bpp;
1239 int bit_depth;
1240 int color_type;
1241 BOOL remove_filler;
1242 BOOL swap_rgb;
1245 static const struct png_pixelformat formats[] = {
1246 {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1},
1247 {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0},
1248 {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0},
1249 {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0},
1250 {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0},
1251 {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0},
1252 {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1},
1253 {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1},
1254 {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0},
1255 {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0},
1256 {NULL},
1259 typedef struct PngEncoder {
1260 IWICBitmapEncoder IWICBitmapEncoder_iface;
1261 IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
1262 LONG ref;
1263 IStream *stream;
1264 png_structp png_ptr;
1265 png_infop info_ptr;
1266 UINT frame_count;
1267 BOOL frame_initialized;
1268 const struct png_pixelformat *format;
1269 BOOL info_written;
1270 UINT width, height;
1271 double xres, yres;
1272 UINT lines_written;
1273 BOOL frame_committed;
1274 BOOL committed;
1275 CRITICAL_SECTION lock;
1276 BOOL interlace;
1277 BYTE *data;
1278 UINT stride;
1279 UINT passes;
1280 } PngEncoder;
1282 static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
1284 return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapEncoder_iface);
1287 static inline PngEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
1289 return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapFrameEncode_iface);
1292 static HRESULT WINAPI PngFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
1293 void **ppv)
1295 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1296 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1298 if (!ppv) return E_INVALIDARG;
1300 if (IsEqualIID(&IID_IUnknown, iid) ||
1301 IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
1303 *ppv = &This->IWICBitmapFrameEncode_iface;
1305 else
1307 *ppv = NULL;
1308 return E_NOINTERFACE;
1311 IUnknown_AddRef((IUnknown*)*ppv);
1312 return S_OK;
1315 static ULONG WINAPI PngFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
1317 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1318 return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface);
1321 static ULONG WINAPI PngFrameEncode_Release(IWICBitmapFrameEncode *iface)
1323 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1324 return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1327 static HRESULT WINAPI PngFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
1328 IPropertyBag2 *pIEncoderOptions)
1330 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1331 BOOL interlace;
1332 PROPBAG2 opts[1]= {{0}};
1333 VARIANT opt_values[1];
1334 HRESULT opt_hres[1];
1335 HRESULT hr;
1337 TRACE("(%p,%p)\n", iface, pIEncoderOptions);
1339 opts[0].pstrName = (LPOLESTR)wszPngInterlaceOption;
1340 opts[0].vt = VT_BOOL;
1342 if (pIEncoderOptions)
1344 hr = IPropertyBag2_Read(pIEncoderOptions, 1, opts, NULL, opt_values, opt_hres);
1346 if (FAILED(hr))
1347 return hr;
1349 else
1350 memset(opt_values, 0, sizeof(opt_values));
1352 if (V_VT(&opt_values[0]) == VT_EMPTY)
1353 interlace = FALSE;
1354 else
1355 interlace = (V_BOOL(&opt_values[0]) != 0);
1357 EnterCriticalSection(&This->lock);
1359 if (This->frame_initialized)
1361 LeaveCriticalSection(&This->lock);
1362 return WINCODEC_ERR_WRONGSTATE;
1365 This->interlace = interlace;
1367 This->frame_initialized = TRUE;
1369 LeaveCriticalSection(&This->lock);
1371 return S_OK;
1374 static HRESULT WINAPI PngFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
1375 UINT uiWidth, UINT uiHeight)
1377 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1378 TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
1380 EnterCriticalSection(&This->lock);
1382 if (!This->frame_initialized || This->info_written)
1384 LeaveCriticalSection(&This->lock);
1385 return WINCODEC_ERR_WRONGSTATE;
1388 This->width = uiWidth;
1389 This->height = uiHeight;
1391 LeaveCriticalSection(&This->lock);
1393 return S_OK;
1396 static HRESULT WINAPI PngFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
1397 double dpiX, double dpiY)
1399 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1400 TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
1402 EnterCriticalSection(&This->lock);
1404 if (!This->frame_initialized || This->info_written)
1406 LeaveCriticalSection(&This->lock);
1407 return WINCODEC_ERR_WRONGSTATE;
1410 This->xres = dpiX;
1411 This->yres = dpiY;
1413 LeaveCriticalSection(&This->lock);
1415 return S_OK;
1418 static HRESULT WINAPI PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
1419 WICPixelFormatGUID *pPixelFormat)
1421 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1422 int i;
1423 TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
1425 EnterCriticalSection(&This->lock);
1427 if (!This->frame_initialized || This->info_written)
1429 LeaveCriticalSection(&This->lock);
1430 return WINCODEC_ERR_WRONGSTATE;
1433 for (i=0; formats[i].guid; i++)
1435 if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
1436 break;
1439 if (!formats[i].guid) i = 0;
1441 This->format = &formats[i];
1442 memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
1444 LeaveCriticalSection(&This->lock);
1446 return S_OK;
1449 static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
1450 UINT cCount, IWICColorContext **ppIColorContext)
1452 FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1453 return E_NOTIMPL;
1456 static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
1457 IWICPalette *pIPalette)
1459 FIXME("(%p,%p): stub\n", iface, pIPalette);
1460 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1463 static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
1464 IWICBitmapSource *pIThumbnail)
1466 FIXME("(%p,%p): stub\n", iface, pIThumbnail);
1467 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1470 static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
1471 UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
1473 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1474 png_byte **row_pointers=NULL;
1475 UINT i;
1476 jmp_buf jmpbuf;
1477 TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
1479 EnterCriticalSection(&This->lock);
1481 if (!This->frame_initialized || !This->width || !This->height || !This->format)
1483 LeaveCriticalSection(&This->lock);
1484 return WINCODEC_ERR_WRONGSTATE;
1487 if (lineCount == 0 || lineCount + This->lines_written > This->height)
1489 LeaveCriticalSection(&This->lock);
1490 return E_INVALIDARG;
1493 /* set up setjmp/longjmp error handling */
1494 if (setjmp(jmpbuf))
1496 LeaveCriticalSection(&This->lock);
1497 HeapFree(GetProcessHeap(), 0, row_pointers);
1498 return E_FAIL;
1500 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1502 if (!This->info_written)
1504 if (This->interlace)
1506 /* libpng requires us to write all data multiple times in this case. */
1507 This->stride = (This->format->bpp * This->width + 7)/8;
1508 This->data = HeapAlloc(GetProcessHeap(), 0, This->height * This->stride);
1509 if (!This->data)
1511 LeaveCriticalSection(&This->lock);
1512 return E_OUTOFMEMORY;
1516 ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height,
1517 This->format->bit_depth, This->format->color_type,
1518 This->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
1519 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1521 if (This->xres != 0.0 && This->yres != 0.0)
1523 ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254,
1524 (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER);
1527 ppng_write_info(This->png_ptr, This->info_ptr);
1529 if (This->format->remove_filler)
1530 ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER);
1532 if (This->format->swap_rgb)
1533 ppng_set_bgr(This->png_ptr);
1535 if (This->interlace)
1536 This->passes = ppng_set_interlace_handling(This->png_ptr);
1538 This->info_written = TRUE;
1541 if (This->interlace)
1543 /* Just store the data so we can write it in multiple passes in Commit. */
1544 for (i=0; i<lineCount; i++)
1545 memcpy(This->data + This->stride * (This->lines_written + i),
1546 pbPixels + cbStride * i,
1547 This->stride);
1549 This->lines_written += lineCount;
1551 LeaveCriticalSection(&This->lock);
1552 return S_OK;
1555 row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*));
1556 if (!row_pointers)
1558 LeaveCriticalSection(&This->lock);
1559 return E_OUTOFMEMORY;
1562 for (i=0; i<lineCount; i++)
1563 row_pointers[i] = pbPixels + cbStride * i;
1565 ppng_write_rows(This->png_ptr, row_pointers, lineCount);
1566 This->lines_written += lineCount;
1568 LeaveCriticalSection(&This->lock);
1570 HeapFree(GetProcessHeap(), 0, row_pointers);
1572 return S_OK;
1575 static HRESULT WINAPI PngFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
1576 IWICBitmapSource *pIBitmapSource, WICRect *prc)
1578 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1579 HRESULT hr;
1580 TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc);
1582 if (!This->frame_initialized)
1583 return WINCODEC_ERR_WRONGSTATE;
1585 hr = configure_write_source(iface, pIBitmapSource, prc,
1586 This->format ? This->format->guid : NULL, This->width, This->height,
1587 This->xres, This->yres);
1589 if (SUCCEEDED(hr))
1591 hr = write_source(iface, pIBitmapSource, prc,
1592 This->format->guid, This->format->bpp, This->width, This->height);
1595 return hr;
1598 static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface)
1600 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1601 png_byte **row_pointers=NULL;
1602 jmp_buf jmpbuf;
1603 TRACE("(%p)\n", iface);
1605 EnterCriticalSection(&This->lock);
1607 if (!This->info_written || This->lines_written != This->height || This->frame_committed)
1609 LeaveCriticalSection(&This->lock);
1610 return WINCODEC_ERR_WRONGSTATE;
1613 /* set up setjmp/longjmp error handling */
1614 if (setjmp(jmpbuf))
1616 LeaveCriticalSection(&This->lock);
1617 HeapFree(GetProcessHeap(), 0, row_pointers);
1618 return E_FAIL;
1620 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1622 if (This->interlace)
1624 int i;
1626 row_pointers = HeapAlloc(GetProcessHeap(), 0, This->height * sizeof(png_byte*));
1627 if (!row_pointers)
1629 LeaveCriticalSection(&This->lock);
1630 return E_OUTOFMEMORY;
1633 for (i=0; i<This->height; i++)
1634 row_pointers[i] = This->data + This->stride * i;
1636 for (i=0; i<This->passes; i++)
1637 ppng_write_rows(This->png_ptr, row_pointers, This->height);
1640 ppng_write_end(This->png_ptr, This->info_ptr);
1642 This->frame_committed = TRUE;
1644 HeapFree(GetProcessHeap(), 0, row_pointers);
1646 LeaveCriticalSection(&This->lock);
1648 return S_OK;
1651 static HRESULT WINAPI PngFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
1652 IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1654 FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
1655 return E_NOTIMPL;
1658 static const IWICBitmapFrameEncodeVtbl PngEncoder_FrameVtbl = {
1659 PngFrameEncode_QueryInterface,
1660 PngFrameEncode_AddRef,
1661 PngFrameEncode_Release,
1662 PngFrameEncode_Initialize,
1663 PngFrameEncode_SetSize,
1664 PngFrameEncode_SetResolution,
1665 PngFrameEncode_SetPixelFormat,
1666 PngFrameEncode_SetColorContexts,
1667 PngFrameEncode_SetPalette,
1668 PngFrameEncode_SetThumbnail,
1669 PngFrameEncode_WritePixels,
1670 PngFrameEncode_WriteSource,
1671 PngFrameEncode_Commit,
1672 PngFrameEncode_GetMetadataQueryWriter
1675 static HRESULT WINAPI PngEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
1676 void **ppv)
1678 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1679 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1681 if (!ppv) return E_INVALIDARG;
1683 if (IsEqualIID(&IID_IUnknown, iid) ||
1684 IsEqualIID(&IID_IWICBitmapEncoder, iid))
1686 *ppv = &This->IWICBitmapEncoder_iface;
1688 else
1690 *ppv = NULL;
1691 return E_NOINTERFACE;
1694 IUnknown_AddRef((IUnknown*)*ppv);
1695 return S_OK;
1698 static ULONG WINAPI PngEncoder_AddRef(IWICBitmapEncoder *iface)
1700 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1701 ULONG ref = InterlockedIncrement(&This->ref);
1703 TRACE("(%p) refcount=%u\n", iface, ref);
1705 return ref;
1708 static ULONG WINAPI PngEncoder_Release(IWICBitmapEncoder *iface)
1710 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1711 ULONG ref = InterlockedDecrement(&This->ref);
1713 TRACE("(%p) refcount=%u\n", iface, ref);
1715 if (ref == 0)
1717 This->lock.DebugInfo->Spare[0] = 0;
1718 DeleteCriticalSection(&This->lock);
1719 if (This->png_ptr)
1720 ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1721 if (This->stream)
1722 IStream_Release(This->stream);
1723 HeapFree(GetProcessHeap(), 0, This->data);
1724 HeapFree(GetProcessHeap(), 0, This);
1727 return ref;
1730 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1732 PngEncoder *This = ppng_get_io_ptr(png_ptr);
1733 HRESULT hr;
1734 ULONG byteswritten;
1736 hr = IStream_Write(This->stream, data, length, &byteswritten);
1737 if (FAILED(hr) || byteswritten != length)
1739 ppng_error(png_ptr, "failed writing data");
1743 static void user_flush(png_structp png_ptr)
1747 static HRESULT WINAPI PngEncoder_Initialize(IWICBitmapEncoder *iface,
1748 IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
1750 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1751 jmp_buf jmpbuf;
1753 TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
1755 EnterCriticalSection(&This->lock);
1757 if (This->png_ptr)
1759 LeaveCriticalSection(&This->lock);
1760 return WINCODEC_ERR_WRONGSTATE;
1763 /* initialize libpng */
1764 This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1765 if (!This->png_ptr)
1767 LeaveCriticalSection(&This->lock);
1768 return E_FAIL;
1771 This->info_ptr = ppng_create_info_struct(This->png_ptr);
1772 if (!This->info_ptr)
1774 ppng_destroy_write_struct(&This->png_ptr, NULL);
1775 This->png_ptr = NULL;
1776 LeaveCriticalSection(&This->lock);
1777 return E_FAIL;
1780 IStream_AddRef(pIStream);
1781 This->stream = pIStream;
1783 /* set up setjmp/longjmp error handling */
1784 if (setjmp(jmpbuf))
1786 ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1787 This->png_ptr = NULL;
1788 IStream_Release(This->stream);
1789 This->stream = NULL;
1790 LeaveCriticalSection(&This->lock);
1791 return E_FAIL;
1793 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1795 /* set up custom i/o handling */
1796 ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush);
1798 LeaveCriticalSection(&This->lock);
1800 return S_OK;
1803 static HRESULT WINAPI PngEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
1804 GUID *pguidContainerFormat)
1806 FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
1807 return E_NOTIMPL;
1810 static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface,
1811 IWICBitmapEncoderInfo **ppIEncoderInfo)
1813 FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo);
1814 return E_NOTIMPL;
1817 static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface,
1818 UINT cCount, IWICColorContext **ppIColorContext)
1820 FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1821 return E_NOTIMPL;
1824 static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette)
1826 TRACE("(%p,%p)\n", iface, pIPalette);
1827 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1830 static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
1832 TRACE("(%p,%p)\n", iface, pIThumbnail);
1833 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1836 static HRESULT WINAPI PngEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
1838 TRACE("(%p,%p)\n", iface, pIPreview);
1839 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1842 static HRESULT WINAPI PngEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
1843 IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
1845 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1846 HRESULT hr;
1847 PROPBAG2 opts[1]= {{0}};
1849 TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
1851 EnterCriticalSection(&This->lock);
1853 if (This->frame_count != 0)
1855 LeaveCriticalSection(&This->lock);
1856 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1859 if (!This->stream)
1861 LeaveCriticalSection(&This->lock);
1862 return WINCODEC_ERR_NOTINITIALIZED;
1865 opts[0].pstrName = (LPOLESTR)wszPngInterlaceOption;
1866 opts[0].vt = VT_BOOL;
1867 opts[0].dwType = PROPBAG2_TYPE_DATA;
1869 hr = CreatePropertyBag2(opts, 1, ppIEncoderOptions);
1870 if (FAILED(hr))
1872 LeaveCriticalSection(&This->lock);
1873 return hr;
1876 This->frame_count = 1;
1878 LeaveCriticalSection(&This->lock);
1880 IWICBitmapEncoder_AddRef(iface);
1881 *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface;
1883 return S_OK;
1886 static HRESULT WINAPI PngEncoder_Commit(IWICBitmapEncoder *iface)
1888 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1889 TRACE("(%p)\n", iface);
1891 EnterCriticalSection(&This->lock);
1893 if (!This->frame_committed || This->committed)
1895 LeaveCriticalSection(&This->lock);
1896 return WINCODEC_ERR_WRONGSTATE;
1899 This->committed = TRUE;
1901 LeaveCriticalSection(&This->lock);
1903 return S_OK;
1906 static HRESULT WINAPI PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
1907 IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1909 FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
1910 return E_NOTIMPL;
1913 static const IWICBitmapEncoderVtbl PngEncoder_Vtbl = {
1914 PngEncoder_QueryInterface,
1915 PngEncoder_AddRef,
1916 PngEncoder_Release,
1917 PngEncoder_Initialize,
1918 PngEncoder_GetContainerFormat,
1919 PngEncoder_GetEncoderInfo,
1920 PngEncoder_SetColorContexts,
1921 PngEncoder_SetPalette,
1922 PngEncoder_SetThumbnail,
1923 PngEncoder_SetPreview,
1924 PngEncoder_CreateNewFrame,
1925 PngEncoder_Commit,
1926 PngEncoder_GetMetadataQueryWriter
1929 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
1931 PngEncoder *This;
1932 HRESULT ret;
1934 TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
1936 *ppv = NULL;
1938 if (!load_libpng())
1940 ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG);
1941 return E_FAIL;
1944 This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder));
1945 if (!This) return E_OUTOFMEMORY;
1947 This->IWICBitmapEncoder_iface.lpVtbl = &PngEncoder_Vtbl;
1948 This->IWICBitmapFrameEncode_iface.lpVtbl = &PngEncoder_FrameVtbl;
1949 This->ref = 1;
1950 This->png_ptr = NULL;
1951 This->info_ptr = NULL;
1952 This->stream = NULL;
1953 This->frame_count = 0;
1954 This->frame_initialized = FALSE;
1955 This->format = NULL;
1956 This->info_written = FALSE;
1957 This->width = 0;
1958 This->height = 0;
1959 This->xres = 0.0;
1960 This->yres = 0.0;
1961 This->lines_written = 0;
1962 This->frame_committed = FALSE;
1963 This->committed = FALSE;
1964 This->data = NULL;
1965 InitializeCriticalSection(&This->lock);
1966 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock");
1968 ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
1969 IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1971 return ret;
1974 #else /* !HAVE_PNG_H */
1976 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
1978 ERR("Trying to load PNG picture, but PNG support is not compiled in.\n");
1979 return E_FAIL;
1982 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
1984 ERR("Trying to save PNG picture, but PNG support is not compiled in.\n");
1985 return E_FAIL;
1988 #endif