msvcrt: Add _mbctokata implementation.
[wine.git] / dlls / windowscodecs / pngformat.c
blob260330fad88c7fe5ab3145fa64798d9b8be968eb
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 HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_size)
48 BYTE header[8];
49 HRESULT hr;
50 ULONG bytesread;
52 hr = IStream_Read(stream, header, 8, &bytesread);
53 if (FAILED(hr) || bytesread < 8)
55 if (SUCCEEDED(hr))
56 hr = E_FAIL;
57 return hr;
60 *data_size = header[0] << 24 | header[1] << 16 | header[2] << 8 | header[3];
62 memcpy(type, &header[4], 4);
64 if (data)
66 *data = HeapAlloc(GetProcessHeap(), 0, *data_size);
67 if (!*data)
68 return E_OUTOFMEMORY;
70 hr = IStream_Read(stream, *data, *data_size, &bytesread);
72 if (FAILED(hr) || bytesread < *data_size)
74 if (SUCCEEDED(hr))
75 hr = E_FAIL;
76 HeapFree(GetProcessHeap(), 0, *data);
77 *data = NULL;
78 return hr;
81 /* Windows ignores CRC of the chunk */
84 return S_OK;
87 static HRESULT LoadTextMetadata(IStream *stream, const GUID *preferred_vendor,
88 DWORD persist_options, MetadataItem **items, DWORD *item_count)
90 HRESULT hr;
91 BYTE type[4];
92 BYTE *data;
93 ULONG data_size;
94 ULONG name_len, value_len;
95 BYTE *name_end_ptr;
96 LPSTR name, value;
97 MetadataItem *result;
99 hr = read_png_chunk(stream, type, &data, &data_size);
100 if (FAILED(hr)) return hr;
102 name_end_ptr = memchr(data, 0, data_size);
104 name_len = name_end_ptr - data;
106 if (!name_end_ptr || name_len > 79)
108 HeapFree(GetProcessHeap(), 0, data);
109 return E_FAIL;
112 value_len = data_size - name_len - 1;
114 result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem));
115 name = HeapAlloc(GetProcessHeap(), 0, name_len + 1);
116 value = HeapAlloc(GetProcessHeap(), 0, value_len + 1);
117 if (!result || !name || !value)
119 HeapFree(GetProcessHeap(), 0, data);
120 HeapFree(GetProcessHeap(), 0, result);
121 HeapFree(GetProcessHeap(), 0, name);
122 HeapFree(GetProcessHeap(), 0, value);
123 return E_OUTOFMEMORY;
126 PropVariantInit(&result[0].schema);
127 PropVariantInit(&result[0].id);
128 PropVariantInit(&result[0].value);
130 memcpy(name, data, name_len + 1);
131 memcpy(value, name_end_ptr + 1, value_len);
132 value[value_len] = 0;
134 result[0].id.vt = VT_LPSTR;
135 result[0].id.u.pszVal = name;
136 result[0].value.vt = VT_LPSTR;
137 result[0].value.u.pszVal = value;
139 *items = result;
140 *item_count = 1;
142 HeapFree(GetProcessHeap(), 0, data);
144 return S_OK;
147 static const MetadataHandlerVtbl TextReader_Vtbl = {
149 &CLSID_WICPngTextMetadataReader,
150 LoadTextMetadata
153 HRESULT PngTextReader_CreateInstance(REFIID iid, void** ppv)
155 return MetadataReader_Create(&TextReader_Vtbl, iid, ppv);
158 #ifdef SONAME_LIBPNG
160 static void *libpng_handle;
161 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
162 MAKE_FUNCPTR(png_create_read_struct);
163 MAKE_FUNCPTR(png_create_info_struct);
164 MAKE_FUNCPTR(png_create_write_struct);
165 MAKE_FUNCPTR(png_destroy_read_struct);
166 MAKE_FUNCPTR(png_destroy_write_struct);
167 MAKE_FUNCPTR(png_error);
168 MAKE_FUNCPTR(png_get_bit_depth);
169 MAKE_FUNCPTR(png_get_color_type);
170 MAKE_FUNCPTR(png_get_error_ptr);
171 MAKE_FUNCPTR(png_get_iCCP);
172 MAKE_FUNCPTR(png_get_image_height);
173 MAKE_FUNCPTR(png_get_image_width);
174 MAKE_FUNCPTR(png_get_io_ptr);
175 MAKE_FUNCPTR(png_get_pHYs);
176 MAKE_FUNCPTR(png_get_PLTE);
177 MAKE_FUNCPTR(png_get_tRNS);
178 MAKE_FUNCPTR(png_set_bgr);
179 MAKE_FUNCPTR(png_set_crc_action);
180 MAKE_FUNCPTR(png_set_error_fn);
181 #ifdef HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8
182 MAKE_FUNCPTR(png_set_expand_gray_1_2_4_to_8);
183 #else
184 MAKE_FUNCPTR(png_set_gray_1_2_4_to_8);
185 #endif
186 MAKE_FUNCPTR(png_set_filler);
187 MAKE_FUNCPTR(png_set_gray_to_rgb);
188 MAKE_FUNCPTR(png_set_interlace_handling);
189 MAKE_FUNCPTR(png_set_IHDR);
190 MAKE_FUNCPTR(png_set_pHYs);
191 MAKE_FUNCPTR(png_set_read_fn);
192 MAKE_FUNCPTR(png_set_strip_16);
193 MAKE_FUNCPTR(png_set_tRNS_to_alpha);
194 MAKE_FUNCPTR(png_set_write_fn);
195 MAKE_FUNCPTR(png_read_end);
196 MAKE_FUNCPTR(png_read_image);
197 MAKE_FUNCPTR(png_read_info);
198 MAKE_FUNCPTR(png_write_end);
199 MAKE_FUNCPTR(png_write_info);
200 MAKE_FUNCPTR(png_write_rows);
201 #undef MAKE_FUNCPTR
203 static CRITICAL_SECTION init_png_cs;
204 static CRITICAL_SECTION_DEBUG init_png_cs_debug =
206 0, 0, &init_png_cs,
207 { &init_png_cs_debug.ProcessLocksList,
208 &init_png_cs_debug.ProcessLocksList },
209 0, 0, { (DWORD_PTR)(__FILE__ ": init_png_cs") }
211 static CRITICAL_SECTION init_png_cs = { &init_png_cs_debug, -1, 0, 0, 0, 0 };
213 static void *load_libpng(void)
215 void *result;
217 EnterCriticalSection(&init_png_cs);
219 if(!libpng_handle && (libpng_handle = wine_dlopen(SONAME_LIBPNG, RTLD_NOW, NULL, 0)) != NULL) {
221 #define LOAD_FUNCPTR(f) \
222 if((p##f = wine_dlsym(libpng_handle, #f, NULL, 0)) == NULL) { \
223 libpng_handle = NULL; \
224 LeaveCriticalSection(&init_png_cs); \
225 return NULL; \
227 LOAD_FUNCPTR(png_create_read_struct);
228 LOAD_FUNCPTR(png_create_info_struct);
229 LOAD_FUNCPTR(png_create_write_struct);
230 LOAD_FUNCPTR(png_destroy_read_struct);
231 LOAD_FUNCPTR(png_destroy_write_struct);
232 LOAD_FUNCPTR(png_error);
233 LOAD_FUNCPTR(png_get_bit_depth);
234 LOAD_FUNCPTR(png_get_color_type);
235 LOAD_FUNCPTR(png_get_error_ptr);
236 LOAD_FUNCPTR(png_get_iCCP);
237 LOAD_FUNCPTR(png_get_image_height);
238 LOAD_FUNCPTR(png_get_image_width);
239 LOAD_FUNCPTR(png_get_io_ptr);
240 LOAD_FUNCPTR(png_get_pHYs);
241 LOAD_FUNCPTR(png_get_PLTE);
242 LOAD_FUNCPTR(png_get_tRNS);
243 LOAD_FUNCPTR(png_set_bgr);
244 LOAD_FUNCPTR(png_set_crc_action);
245 LOAD_FUNCPTR(png_set_error_fn);
246 #ifdef HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8
247 LOAD_FUNCPTR(png_set_expand_gray_1_2_4_to_8);
248 #else
249 LOAD_FUNCPTR(png_set_gray_1_2_4_to_8);
250 #endif
251 LOAD_FUNCPTR(png_set_filler);
252 LOAD_FUNCPTR(png_set_gray_to_rgb);
253 LOAD_FUNCPTR(png_set_interlace_handling);
254 LOAD_FUNCPTR(png_set_IHDR);
255 LOAD_FUNCPTR(png_set_pHYs);
256 LOAD_FUNCPTR(png_set_read_fn);
257 LOAD_FUNCPTR(png_set_strip_16);
258 LOAD_FUNCPTR(png_set_tRNS_to_alpha);
259 LOAD_FUNCPTR(png_set_write_fn);
260 LOAD_FUNCPTR(png_read_end);
261 LOAD_FUNCPTR(png_read_image);
262 LOAD_FUNCPTR(png_read_info);
263 LOAD_FUNCPTR(png_write_end);
264 LOAD_FUNCPTR(png_write_info);
265 LOAD_FUNCPTR(png_write_rows);
267 #undef LOAD_FUNCPTR
270 result = libpng_handle;
272 LeaveCriticalSection(&init_png_cs);
274 return result;
277 static void user_error_fn(png_structp png_ptr, png_const_charp error_message)
279 jmp_buf *pjmpbuf;
281 /* This uses setjmp/longjmp just like the default. We can't use the
282 * default because there's no way to access the jmp buffer in the png_struct
283 * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
284 WARN("PNG error: %s\n", debugstr_a(error_message));
285 pjmpbuf = ppng_get_error_ptr(png_ptr);
286 longjmp(*pjmpbuf, 1);
289 static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message)
291 WARN("PNG warning: %s\n", debugstr_a(warning_message));
294 typedef struct {
295 IWICBitmapDecoder IWICBitmapDecoder_iface;
296 IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
297 IWICMetadataBlockReader IWICMetadataBlockReader_iface;
298 LONG ref;
299 IStream *stream;
300 png_structp png_ptr;
301 png_infop info_ptr;
302 png_infop end_info;
303 BOOL initialized;
304 int bpp;
305 int width, height;
306 UINT stride;
307 const WICPixelFormatGUID *format;
308 BYTE *image_bits;
309 CRITICAL_SECTION lock; /* must be held when png structures are accessed or initialized is set */
310 } PngDecoder;
312 static inline PngDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
314 return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapDecoder_iface);
317 static inline PngDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
319 return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapFrameDecode_iface);
322 static inline PngDecoder *impl_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface)
324 return CONTAINING_RECORD(iface, PngDecoder, IWICMetadataBlockReader_iface);
327 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl;
329 static HRESULT WINAPI PngDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
330 void **ppv)
332 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
333 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
335 if (!ppv) return E_INVALIDARG;
337 if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
339 *ppv = &This->IWICBitmapDecoder_iface;
341 else
343 *ppv = NULL;
344 return E_NOINTERFACE;
347 IUnknown_AddRef((IUnknown*)*ppv);
348 return S_OK;
351 static ULONG WINAPI PngDecoder_AddRef(IWICBitmapDecoder *iface)
353 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
354 ULONG ref = InterlockedIncrement(&This->ref);
356 TRACE("(%p) refcount=%u\n", iface, ref);
358 return ref;
361 static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface)
363 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
364 ULONG ref = InterlockedDecrement(&This->ref);
366 TRACE("(%p) refcount=%u\n", iface, ref);
368 if (ref == 0)
370 IStream_Release(This->stream);
371 if (This->png_ptr)
372 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
373 This->lock.DebugInfo->Spare[0] = 0;
374 DeleteCriticalSection(&This->lock);
375 HeapFree(GetProcessHeap(), 0, This->image_bits);
376 HeapFree(GetProcessHeap(), 0, This);
379 return ref;
382 static HRESULT WINAPI PngDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream,
383 DWORD *capability)
385 HRESULT hr;
387 TRACE("(%p,%p,%p)\n", iface, stream, capability);
389 if (!stream || !capability) return E_INVALIDARG;
391 hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand);
392 if (hr != S_OK) return hr;
394 *capability = WICBitmapDecoderCapabilityCanDecodeAllImages |
395 WICBitmapDecoderCapabilityCanDecodeSomeImages;
396 /* FIXME: WICBitmapDecoderCapabilityCanEnumerateMetadata */
397 return S_OK;
400 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
402 IStream *stream = ppng_get_io_ptr(png_ptr);
403 HRESULT hr;
404 ULONG bytesread;
406 hr = IStream_Read(stream, data, length, &bytesread);
407 if (FAILED(hr) || bytesread != length)
409 ppng_error(png_ptr, "failed reading data");
413 static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
414 WICDecodeOptions cacheOptions)
416 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
417 LARGE_INTEGER seek;
418 HRESULT hr=S_OK;
419 png_bytep *row_pointers=NULL;
420 UINT image_size;
421 UINT i;
422 int color_type, bit_depth;
423 png_bytep trans;
424 int num_trans;
425 png_uint_32 transparency;
426 png_color_16p trans_values;
427 jmp_buf jmpbuf;
429 TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
431 EnterCriticalSection(&This->lock);
433 /* initialize libpng */
434 This->png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
435 if (!This->png_ptr)
437 hr = E_FAIL;
438 goto end;
441 This->info_ptr = ppng_create_info_struct(This->png_ptr);
442 if (!This->info_ptr)
444 ppng_destroy_read_struct(&This->png_ptr, NULL, NULL);
445 This->png_ptr = NULL;
446 hr = E_FAIL;
447 goto end;
450 This->end_info = ppng_create_info_struct(This->png_ptr);
451 if (!This->info_ptr)
453 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, NULL);
454 This->png_ptr = NULL;
455 hr = E_FAIL;
456 goto end;
459 /* set up setjmp/longjmp error handling */
460 if (setjmp(jmpbuf))
462 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
463 HeapFree(GetProcessHeap(), 0, row_pointers);
464 This->png_ptr = NULL;
465 hr = E_FAIL;
466 goto end;
468 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
469 ppng_set_crc_action(This->png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
471 /* seek to the start of the stream */
472 seek.QuadPart = 0;
473 hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
474 if (FAILED(hr)) goto end;
476 /* set up custom i/o handling */
477 ppng_set_read_fn(This->png_ptr, pIStream, user_read_data);
479 /* read the header */
480 ppng_read_info(This->png_ptr, This->info_ptr);
482 /* choose a pixel format */
483 color_type = ppng_get_color_type(This->png_ptr, This->info_ptr);
484 bit_depth = ppng_get_bit_depth(This->png_ptr, This->info_ptr);
486 /* check for color-keyed alpha */
487 transparency = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans, &num_trans, &trans_values);
489 if (transparency && color_type != PNG_COLOR_TYPE_PALETTE)
491 /* expand to RGBA */
492 if (color_type == PNG_COLOR_TYPE_GRAY)
494 if (bit_depth < 8)
496 #ifdef HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8
497 ppng_set_expand_gray_1_2_4_to_8(This->png_ptr);
498 #else
499 ppng_set_gray_1_2_4_to_8(This->png_ptr);
500 #endif
501 bit_depth = 8;
503 ppng_set_gray_to_rgb(This->png_ptr);
505 ppng_set_tRNS_to_alpha(This->png_ptr);
506 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
509 switch (color_type)
511 case PNG_COLOR_TYPE_GRAY:
512 This->bpp = bit_depth;
513 switch (bit_depth)
515 case 1: This->format = &GUID_WICPixelFormatBlackWhite; break;
516 case 2: This->format = &GUID_WICPixelFormat2bppGray; break;
517 case 4: This->format = &GUID_WICPixelFormat4bppGray; break;
518 case 8: This->format = &GUID_WICPixelFormat8bppGray; break;
519 case 16: This->format = &GUID_WICPixelFormat16bppGray; break;
520 default:
521 ERR("invalid grayscale bit depth: %i\n", bit_depth);
522 hr = E_FAIL;
523 goto end;
525 break;
526 case PNG_COLOR_TYPE_GRAY_ALPHA:
527 /* WIC does not support grayscale alpha formats so use RGBA */
528 ppng_set_gray_to_rgb(This->png_ptr);
529 /* fall through */
530 case PNG_COLOR_TYPE_RGB_ALPHA:
531 This->bpp = bit_depth * 4;
532 switch (bit_depth)
534 case 8:
535 ppng_set_bgr(This->png_ptr);
536 This->format = &GUID_WICPixelFormat32bppBGRA;
537 break;
538 case 16: This->format = &GUID_WICPixelFormat64bppRGBA; break;
539 default:
540 ERR("invalid RGBA bit depth: %i\n", bit_depth);
541 hr = E_FAIL;
542 goto end;
544 break;
545 case PNG_COLOR_TYPE_PALETTE:
546 This->bpp = bit_depth;
547 switch (bit_depth)
549 case 1: This->format = &GUID_WICPixelFormat1bppIndexed; break;
550 case 2: This->format = &GUID_WICPixelFormat2bppIndexed; break;
551 case 4: This->format = &GUID_WICPixelFormat4bppIndexed; break;
552 case 8: This->format = &GUID_WICPixelFormat8bppIndexed; break;
553 default:
554 ERR("invalid indexed color bit depth: %i\n", bit_depth);
555 hr = E_FAIL;
556 goto end;
558 break;
559 case PNG_COLOR_TYPE_RGB:
560 This->bpp = bit_depth * 3;
561 switch (bit_depth)
563 case 8:
564 ppng_set_bgr(This->png_ptr);
565 This->format = &GUID_WICPixelFormat24bppBGR;
566 break;
567 case 16: This->format = &GUID_WICPixelFormat48bppRGB; break;
568 default:
569 ERR("invalid RGB color bit depth: %i\n", bit_depth);
570 hr = E_FAIL;
571 goto end;
573 break;
574 default:
575 ERR("invalid color type %i\n", color_type);
576 hr = E_FAIL;
577 goto end;
580 /* read the image data */
581 This->width = ppng_get_image_width(This->png_ptr, This->info_ptr);
582 This->height = ppng_get_image_height(This->png_ptr, This->info_ptr);
583 This->stride = This->width * This->bpp;
584 image_size = This->stride * This->height;
586 This->image_bits = HeapAlloc(GetProcessHeap(), 0, image_size);
587 if (!This->image_bits)
589 hr = E_OUTOFMEMORY;
590 goto end;
593 row_pointers = HeapAlloc(GetProcessHeap(), 0, sizeof(png_bytep)*This->height);
594 if (!row_pointers)
596 hr = E_OUTOFMEMORY;
597 goto end;
600 for (i=0; i<This->height; i++)
601 row_pointers[i] = This->image_bits + i * This->stride;
603 ppng_read_image(This->png_ptr, row_pointers);
605 HeapFree(GetProcessHeap(), 0, row_pointers);
606 row_pointers = NULL;
608 ppng_read_end(This->png_ptr, This->end_info);
610 This->stream = pIStream;
611 IStream_AddRef(This->stream);
613 This->initialized = TRUE;
615 end:
617 LeaveCriticalSection(&This->lock);
619 return hr;
622 static HRESULT WINAPI PngDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
623 GUID *pguidContainerFormat)
625 memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
626 return S_OK;
629 static HRESULT WINAPI PngDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
630 IWICBitmapDecoderInfo **ppIDecoderInfo)
632 HRESULT hr;
633 IWICComponentInfo *compinfo;
635 TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
637 hr = CreateComponentInfo(&CLSID_WICPngDecoder, &compinfo);
638 if (FAILED(hr)) return hr;
640 hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
641 (void**)ppIDecoderInfo);
643 IWICComponentInfo_Release(compinfo);
645 return hr;
648 static HRESULT WINAPI PngDecoder_CopyPalette(IWICBitmapDecoder *iface,
649 IWICPalette *pIPalette)
651 FIXME("(%p,%p): stub\n", iface, pIPalette);
652 return E_NOTIMPL;
655 static HRESULT WINAPI PngDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
656 IWICMetadataQueryReader **ppIMetadataQueryReader)
658 FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
659 return E_NOTIMPL;
662 static HRESULT WINAPI PngDecoder_GetPreview(IWICBitmapDecoder *iface,
663 IWICBitmapSource **ppIBitmapSource)
665 TRACE("(%p,%p)\n", iface, ppIBitmapSource);
667 if (!ppIBitmapSource) return E_INVALIDARG;
669 *ppIBitmapSource = NULL;
670 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
673 static HRESULT WINAPI PngDecoder_GetColorContexts(IWICBitmapDecoder *iface,
674 UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
676 TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
677 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
680 static HRESULT WINAPI PngDecoder_GetThumbnail(IWICBitmapDecoder *iface,
681 IWICBitmapSource **ppIThumbnail)
683 TRACE("(%p,%p)\n", iface, ppIThumbnail);
685 if (!ppIThumbnail) return E_INVALIDARG;
687 *ppIThumbnail = NULL;
688 return WINCODEC_ERR_CODECNOTHUMBNAIL;
691 static HRESULT WINAPI PngDecoder_GetFrameCount(IWICBitmapDecoder *iface,
692 UINT *pCount)
694 if (!pCount) return E_INVALIDARG;
696 *pCount = 1;
697 return S_OK;
700 static HRESULT WINAPI PngDecoder_GetFrame(IWICBitmapDecoder *iface,
701 UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
703 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
704 TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
706 if (!This->initialized) return WINCODEC_ERR_FRAMEMISSING;
708 if (index != 0) return E_INVALIDARG;
710 IWICBitmapDecoder_AddRef(iface);
712 *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface;
714 return S_OK;
717 static const IWICBitmapDecoderVtbl PngDecoder_Vtbl = {
718 PngDecoder_QueryInterface,
719 PngDecoder_AddRef,
720 PngDecoder_Release,
721 PngDecoder_QueryCapability,
722 PngDecoder_Initialize,
723 PngDecoder_GetContainerFormat,
724 PngDecoder_GetDecoderInfo,
725 PngDecoder_CopyPalette,
726 PngDecoder_GetMetadataQueryReader,
727 PngDecoder_GetPreview,
728 PngDecoder_GetColorContexts,
729 PngDecoder_GetThumbnail,
730 PngDecoder_GetFrameCount,
731 PngDecoder_GetFrame
734 static HRESULT WINAPI PngDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
735 void **ppv)
737 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
738 if (!ppv) return E_INVALIDARG;
740 if (IsEqualIID(&IID_IUnknown, iid) ||
741 IsEqualIID(&IID_IWICBitmapSource, iid) ||
742 IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
744 *ppv = &This->IWICBitmapFrameDecode_iface;
746 else if (IsEqualIID(&IID_IWICMetadataBlockReader, iid))
748 *ppv = &This->IWICMetadataBlockReader_iface;
750 else
752 *ppv = NULL;
753 return E_NOINTERFACE;
756 IUnknown_AddRef((IUnknown*)*ppv);
757 return S_OK;
760 static ULONG WINAPI PngDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface)
762 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
763 return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
766 static ULONG WINAPI PngDecoder_Frame_Release(IWICBitmapFrameDecode *iface)
768 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
769 return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
772 static HRESULT WINAPI PngDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface,
773 UINT *puiWidth, UINT *puiHeight)
775 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
776 *puiWidth = This->width;
777 *puiHeight = This->height;
778 TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight);
779 return S_OK;
782 static HRESULT WINAPI PngDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface,
783 WICPixelFormatGUID *pPixelFormat)
785 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
786 TRACE("(%p,%p)\n", iface, pPixelFormat);
788 memcpy(pPixelFormat, This->format, sizeof(GUID));
790 return S_OK;
793 static HRESULT WINAPI PngDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface,
794 double *pDpiX, double *pDpiY)
796 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
797 png_uint_32 ret, xres, yres;
798 int unit_type;
800 EnterCriticalSection(&This->lock);
802 ret = ppng_get_pHYs(This->png_ptr, This->info_ptr, &xres, &yres, &unit_type);
804 if (ret && unit_type == PNG_RESOLUTION_METER)
806 *pDpiX = xres * 0.0254;
807 *pDpiY = yres * 0.0254;
809 else
811 WARN("no pHYs block present\n");
812 *pDpiX = *pDpiY = 96.0;
815 LeaveCriticalSection(&This->lock);
817 TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY);
819 return S_OK;
822 static HRESULT WINAPI PngDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface,
823 IWICPalette *pIPalette)
825 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
826 png_uint_32 ret;
827 png_colorp png_palette;
828 int num_palette;
829 WICColor palette[256];
830 png_bytep trans_alpha;
831 int num_trans;
832 png_color_16p trans_values;
833 int i;
834 HRESULT hr=S_OK;
836 TRACE("(%p,%p)\n", iface, pIPalette);
838 EnterCriticalSection(&This->lock);
840 ret = ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette);
841 if (!ret)
843 hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
844 goto end;
847 if (num_palette > 256)
849 ERR("palette has %i colors?!\n", num_palette);
850 hr = E_FAIL;
851 goto end;
854 ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values);
855 if (!ret) num_trans = 0;
857 for (i=0; i<num_palette; i++)
859 BYTE alpha = (i < num_trans) ? trans_alpha[i] : 0xff;
860 palette[i] = (alpha << 24 |
861 png_palette[i].red << 16|
862 png_palette[i].green << 8|
863 png_palette[i].blue);
866 end:
868 LeaveCriticalSection(&This->lock);
870 if (SUCCEEDED(hr))
871 hr = IWICPalette_InitializeCustom(pIPalette, palette, num_palette);
873 return hr;
876 static HRESULT WINAPI PngDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface,
877 const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
879 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
880 TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
882 return copy_pixels(This->bpp, This->image_bits,
883 This->width, This->height, This->stride,
884 prc, cbStride, cbBufferSize, pbBuffer);
887 static HRESULT WINAPI PngDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
888 IWICMetadataQueryReader **ppIMetadataQueryReader)
890 FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
891 return E_NOTIMPL;
894 static HRESULT WINAPI PngDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface,
895 UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
897 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
898 png_charp name;
899 BYTE *profile;
900 png_uint_32 len;
901 int compression_type;
902 HRESULT hr;
904 TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
906 if (!pcActualCount) return E_INVALIDARG;
908 EnterCriticalSection(&This->lock);
910 if (ppng_get_iCCP(This->png_ptr, This->info_ptr, &name, &compression_type, (void *)&profile, &len))
912 if (cCount && ppIColorContexts)
914 hr = IWICColorContext_InitializeFromMemory(*ppIColorContexts, profile, len);
915 if (FAILED(hr))
917 LeaveCriticalSection(&This->lock);
918 return hr;
921 *pcActualCount = 1;
923 else
924 *pcActualCount = 0;
926 LeaveCriticalSection(&This->lock);
928 return S_OK;
931 static HRESULT WINAPI PngDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
932 IWICBitmapSource **ppIThumbnail)
934 TRACE("(%p,%p)\n", iface, ppIThumbnail);
936 if (!ppIThumbnail) return E_INVALIDARG;
938 *ppIThumbnail = NULL;
939 return WINCODEC_ERR_CODECNOTHUMBNAIL;
942 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl = {
943 PngDecoder_Frame_QueryInterface,
944 PngDecoder_Frame_AddRef,
945 PngDecoder_Frame_Release,
946 PngDecoder_Frame_GetSize,
947 PngDecoder_Frame_GetPixelFormat,
948 PngDecoder_Frame_GetResolution,
949 PngDecoder_Frame_CopyPalette,
950 PngDecoder_Frame_CopyPixels,
951 PngDecoder_Frame_GetMetadataQueryReader,
952 PngDecoder_Frame_GetColorContexts,
953 PngDecoder_Frame_GetThumbnail
956 static HRESULT WINAPI PngDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid,
957 void **ppv)
959 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
960 return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv);
963 static ULONG WINAPI PngDecoder_Block_AddRef(IWICMetadataBlockReader *iface)
965 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
966 return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
969 static ULONG WINAPI PngDecoder_Block_Release(IWICMetadataBlockReader *iface)
971 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
972 return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
975 static HRESULT WINAPI PngDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
976 GUID *pguidContainerFormat)
978 if (!pguidContainerFormat) return E_INVALIDARG;
979 memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
980 return S_OK;
983 static HRESULT WINAPI PngDecoder_Block_GetCount(IWICMetadataBlockReader *iface,
984 UINT *pcCount)
986 static int once;
987 TRACE("%p,%p\n", iface, pcCount);
988 if (!once++) FIXME("stub\n");
989 return E_NOTIMPL;
992 static HRESULT WINAPI PngDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
993 UINT nIndex, IWICMetadataReader **ppIMetadataReader)
995 FIXME("%p,%d,%p\n", iface, nIndex, ppIMetadataReader);
996 return E_NOTIMPL;
999 static HRESULT WINAPI PngDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface,
1000 IEnumUnknown **ppIEnumMetadata)
1002 FIXME("%p,%p\n", iface, ppIEnumMetadata);
1003 return E_NOTIMPL;
1006 static const IWICMetadataBlockReaderVtbl PngDecoder_BlockVtbl = {
1007 PngDecoder_Block_QueryInterface,
1008 PngDecoder_Block_AddRef,
1009 PngDecoder_Block_Release,
1010 PngDecoder_Block_GetContainerFormat,
1011 PngDecoder_Block_GetCount,
1012 PngDecoder_Block_GetReaderByIndex,
1013 PngDecoder_Block_GetEnumerator,
1016 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
1018 PngDecoder *This;
1019 HRESULT ret;
1021 TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
1023 *ppv = NULL;
1025 if (!load_libpng())
1027 ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
1028 return E_FAIL;
1031 This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngDecoder));
1032 if (!This) return E_OUTOFMEMORY;
1034 This->IWICBitmapDecoder_iface.lpVtbl = &PngDecoder_Vtbl;
1035 This->IWICBitmapFrameDecode_iface.lpVtbl = &PngDecoder_FrameVtbl;
1036 This->IWICMetadataBlockReader_iface.lpVtbl = &PngDecoder_BlockVtbl;
1037 This->ref = 1;
1038 This->png_ptr = NULL;
1039 This->info_ptr = NULL;
1040 This->end_info = NULL;
1041 This->initialized = FALSE;
1042 This->image_bits = NULL;
1043 InitializeCriticalSection(&This->lock);
1044 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngDecoder.lock");
1046 ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
1047 IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1049 return ret;
1052 struct png_pixelformat {
1053 const WICPixelFormatGUID *guid;
1054 UINT bpp;
1055 int bit_depth;
1056 int color_type;
1057 BOOL remove_filler;
1058 BOOL swap_rgb;
1061 static const struct png_pixelformat formats[] = {
1062 {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1},
1063 {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0},
1064 {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0},
1065 {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0},
1066 {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0},
1067 {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0},
1068 {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1},
1069 {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1},
1070 {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0},
1071 {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0},
1072 {NULL},
1075 typedef struct PngEncoder {
1076 IWICBitmapEncoder IWICBitmapEncoder_iface;
1077 IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
1078 LONG ref;
1079 IStream *stream;
1080 png_structp png_ptr;
1081 png_infop info_ptr;
1082 UINT frame_count;
1083 BOOL frame_initialized;
1084 const struct png_pixelformat *format;
1085 BOOL info_written;
1086 UINT width, height;
1087 double xres, yres;
1088 UINT lines_written;
1089 BOOL frame_committed;
1090 BOOL committed;
1091 CRITICAL_SECTION lock;
1092 BOOL interlace;
1093 BYTE *data;
1094 UINT stride;
1095 UINT passes;
1096 } PngEncoder;
1098 static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
1100 return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapEncoder_iface);
1103 static inline PngEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
1105 return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapFrameEncode_iface);
1108 static HRESULT WINAPI PngFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
1109 void **ppv)
1111 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1112 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1114 if (!ppv) return E_INVALIDARG;
1116 if (IsEqualIID(&IID_IUnknown, iid) ||
1117 IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
1119 *ppv = &This->IWICBitmapFrameEncode_iface;
1121 else
1123 *ppv = NULL;
1124 return E_NOINTERFACE;
1127 IUnknown_AddRef((IUnknown*)*ppv);
1128 return S_OK;
1131 static ULONG WINAPI PngFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
1133 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1134 return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface);
1137 static ULONG WINAPI PngFrameEncode_Release(IWICBitmapFrameEncode *iface)
1139 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1140 return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1143 static HRESULT WINAPI PngFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
1144 IPropertyBag2 *pIEncoderOptions)
1146 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1147 BOOL interlace;
1148 PROPBAG2 opts[1]= {{0}};
1149 VARIANT opt_values[1];
1150 HRESULT opt_hres[1];
1151 HRESULT hr;
1153 TRACE("(%p,%p)\n", iface, pIEncoderOptions);
1155 opts[0].pstrName = (LPOLESTR)wszPngInterlaceOption;
1156 opts[0].vt = VT_BOOL;
1158 if (pIEncoderOptions)
1160 hr = IPropertyBag2_Read(pIEncoderOptions, 1, opts, NULL, opt_values, opt_hres);
1162 if (FAILED(hr))
1163 return hr;
1165 else
1166 memset(opt_values, 0, sizeof(opt_values));
1168 if (V_VT(&opt_values[0]) == VT_EMPTY)
1169 interlace = FALSE;
1170 else
1171 interlace = (V_BOOL(&opt_values[0]) != 0);
1173 EnterCriticalSection(&This->lock);
1175 if (This->frame_initialized)
1177 LeaveCriticalSection(&This->lock);
1178 return WINCODEC_ERR_WRONGSTATE;
1181 This->interlace = interlace;
1183 This->frame_initialized = TRUE;
1185 LeaveCriticalSection(&This->lock);
1187 return S_OK;
1190 static HRESULT WINAPI PngFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
1191 UINT uiWidth, UINT uiHeight)
1193 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1194 TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
1196 EnterCriticalSection(&This->lock);
1198 if (!This->frame_initialized || This->info_written)
1200 LeaveCriticalSection(&This->lock);
1201 return WINCODEC_ERR_WRONGSTATE;
1204 This->width = uiWidth;
1205 This->height = uiHeight;
1207 LeaveCriticalSection(&This->lock);
1209 return S_OK;
1212 static HRESULT WINAPI PngFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
1213 double dpiX, double dpiY)
1215 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1216 TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
1218 EnterCriticalSection(&This->lock);
1220 if (!This->frame_initialized || This->info_written)
1222 LeaveCriticalSection(&This->lock);
1223 return WINCODEC_ERR_WRONGSTATE;
1226 This->xres = dpiX;
1227 This->yres = dpiY;
1229 LeaveCriticalSection(&This->lock);
1231 return S_OK;
1234 static HRESULT WINAPI PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
1235 WICPixelFormatGUID *pPixelFormat)
1237 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1238 int i;
1239 TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
1241 EnterCriticalSection(&This->lock);
1243 if (!This->frame_initialized || This->info_written)
1245 LeaveCriticalSection(&This->lock);
1246 return WINCODEC_ERR_WRONGSTATE;
1249 for (i=0; formats[i].guid; i++)
1251 if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
1252 break;
1255 if (!formats[i].guid) i = 0;
1257 This->format = &formats[i];
1258 memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
1260 LeaveCriticalSection(&This->lock);
1262 return S_OK;
1265 static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
1266 UINT cCount, IWICColorContext **ppIColorContext)
1268 FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1269 return E_NOTIMPL;
1272 static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
1273 IWICPalette *pIPalette)
1275 FIXME("(%p,%p): stub\n", iface, pIPalette);
1276 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1279 static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
1280 IWICBitmapSource *pIThumbnail)
1282 FIXME("(%p,%p): stub\n", iface, pIThumbnail);
1283 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1286 static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
1287 UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
1289 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1290 png_byte **row_pointers=NULL;
1291 UINT i;
1292 jmp_buf jmpbuf;
1293 TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
1295 EnterCriticalSection(&This->lock);
1297 if (!This->frame_initialized || !This->width || !This->height || !This->format)
1299 LeaveCriticalSection(&This->lock);
1300 return WINCODEC_ERR_WRONGSTATE;
1303 if (lineCount == 0 || lineCount + This->lines_written > This->height)
1305 LeaveCriticalSection(&This->lock);
1306 return E_INVALIDARG;
1309 /* set up setjmp/longjmp error handling */
1310 if (setjmp(jmpbuf))
1312 LeaveCriticalSection(&This->lock);
1313 HeapFree(GetProcessHeap(), 0, row_pointers);
1314 return E_FAIL;
1316 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1318 if (!This->info_written)
1320 if (This->interlace)
1322 /* libpng requires us to write all data multiple times in this case. */
1323 This->stride = (This->format->bpp * This->width + 7)/8;
1324 This->data = HeapAlloc(GetProcessHeap(), 0, This->height * This->stride);
1325 if (!This->data)
1327 LeaveCriticalSection(&This->lock);
1328 return E_OUTOFMEMORY;
1332 ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height,
1333 This->format->bit_depth, This->format->color_type,
1334 This->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
1335 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1337 if (This->xres != 0.0 && This->yres != 0.0)
1339 ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254,
1340 (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER);
1343 ppng_write_info(This->png_ptr, This->info_ptr);
1345 if (This->format->remove_filler)
1346 ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER);
1348 if (This->format->swap_rgb)
1349 ppng_set_bgr(This->png_ptr);
1351 if (This->interlace)
1352 This->passes = ppng_set_interlace_handling(This->png_ptr);
1354 This->info_written = TRUE;
1357 if (This->interlace)
1359 /* Just store the data so we can write it in multiple passes in Commit. */
1360 for (i=0; i<lineCount; i++)
1361 memcpy(This->data + This->stride * (This->lines_written + i),
1362 pbPixels + cbStride * i,
1363 This->stride);
1365 This->lines_written += lineCount;
1367 LeaveCriticalSection(&This->lock);
1368 return S_OK;
1371 row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*));
1372 if (!row_pointers)
1374 LeaveCriticalSection(&This->lock);
1375 return E_OUTOFMEMORY;
1378 for (i=0; i<lineCount; i++)
1379 row_pointers[i] = pbPixels + cbStride * i;
1381 ppng_write_rows(This->png_ptr, row_pointers, lineCount);
1382 This->lines_written += lineCount;
1384 LeaveCriticalSection(&This->lock);
1386 HeapFree(GetProcessHeap(), 0, row_pointers);
1388 return S_OK;
1391 static HRESULT WINAPI PngFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
1392 IWICBitmapSource *pIBitmapSource, WICRect *prc)
1394 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1395 HRESULT hr;
1396 TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc);
1398 if (!This->frame_initialized)
1399 return WINCODEC_ERR_WRONGSTATE;
1401 hr = configure_write_source(iface, pIBitmapSource, prc,
1402 This->format ? This->format->guid : NULL, This->width, This->height,
1403 This->xres, This->yres);
1405 if (SUCCEEDED(hr))
1407 hr = write_source(iface, pIBitmapSource, prc,
1408 This->format->guid, This->format->bpp, This->width, This->height);
1411 return hr;
1414 static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface)
1416 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1417 png_byte **row_pointers=NULL;
1418 jmp_buf jmpbuf;
1419 TRACE("(%p)\n", iface);
1421 EnterCriticalSection(&This->lock);
1423 if (!This->info_written || This->lines_written != This->height || This->frame_committed)
1425 LeaveCriticalSection(&This->lock);
1426 return WINCODEC_ERR_WRONGSTATE;
1429 /* set up setjmp/longjmp error handling */
1430 if (setjmp(jmpbuf))
1432 LeaveCriticalSection(&This->lock);
1433 HeapFree(GetProcessHeap(), 0, row_pointers);
1434 return E_FAIL;
1436 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1438 if (This->interlace)
1440 int i;
1442 row_pointers = HeapAlloc(GetProcessHeap(), 0, This->height * sizeof(png_byte*));
1443 if (!row_pointers)
1445 LeaveCriticalSection(&This->lock);
1446 return E_OUTOFMEMORY;
1449 for (i=0; i<This->height; i++)
1450 row_pointers[i] = This->data + This->stride * i;
1452 for (i=0; i<This->passes; i++)
1453 ppng_write_rows(This->png_ptr, row_pointers, This->height);
1456 ppng_write_end(This->png_ptr, This->info_ptr);
1458 This->frame_committed = TRUE;
1460 HeapFree(GetProcessHeap(), 0, row_pointers);
1462 LeaveCriticalSection(&This->lock);
1464 return S_OK;
1467 static HRESULT WINAPI PngFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
1468 IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1470 FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
1471 return E_NOTIMPL;
1474 static const IWICBitmapFrameEncodeVtbl PngEncoder_FrameVtbl = {
1475 PngFrameEncode_QueryInterface,
1476 PngFrameEncode_AddRef,
1477 PngFrameEncode_Release,
1478 PngFrameEncode_Initialize,
1479 PngFrameEncode_SetSize,
1480 PngFrameEncode_SetResolution,
1481 PngFrameEncode_SetPixelFormat,
1482 PngFrameEncode_SetColorContexts,
1483 PngFrameEncode_SetPalette,
1484 PngFrameEncode_SetThumbnail,
1485 PngFrameEncode_WritePixels,
1486 PngFrameEncode_WriteSource,
1487 PngFrameEncode_Commit,
1488 PngFrameEncode_GetMetadataQueryWriter
1491 static HRESULT WINAPI PngEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
1492 void **ppv)
1494 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1495 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1497 if (!ppv) return E_INVALIDARG;
1499 if (IsEqualIID(&IID_IUnknown, iid) ||
1500 IsEqualIID(&IID_IWICBitmapEncoder, iid))
1502 *ppv = &This->IWICBitmapEncoder_iface;
1504 else
1506 *ppv = NULL;
1507 return E_NOINTERFACE;
1510 IUnknown_AddRef((IUnknown*)*ppv);
1511 return S_OK;
1514 static ULONG WINAPI PngEncoder_AddRef(IWICBitmapEncoder *iface)
1516 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1517 ULONG ref = InterlockedIncrement(&This->ref);
1519 TRACE("(%p) refcount=%u\n", iface, ref);
1521 return ref;
1524 static ULONG WINAPI PngEncoder_Release(IWICBitmapEncoder *iface)
1526 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1527 ULONG ref = InterlockedDecrement(&This->ref);
1529 TRACE("(%p) refcount=%u\n", iface, ref);
1531 if (ref == 0)
1533 This->lock.DebugInfo->Spare[0] = 0;
1534 DeleteCriticalSection(&This->lock);
1535 if (This->png_ptr)
1536 ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1537 if (This->stream)
1538 IStream_Release(This->stream);
1539 HeapFree(GetProcessHeap(), 0, This->data);
1540 HeapFree(GetProcessHeap(), 0, This);
1543 return ref;
1546 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1548 PngEncoder *This = ppng_get_io_ptr(png_ptr);
1549 HRESULT hr;
1550 ULONG byteswritten;
1552 hr = IStream_Write(This->stream, data, length, &byteswritten);
1553 if (FAILED(hr) || byteswritten != length)
1555 ppng_error(png_ptr, "failed writing data");
1559 static void user_flush(png_structp png_ptr)
1563 static HRESULT WINAPI PngEncoder_Initialize(IWICBitmapEncoder *iface,
1564 IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
1566 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1567 jmp_buf jmpbuf;
1569 TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
1571 EnterCriticalSection(&This->lock);
1573 if (This->png_ptr)
1575 LeaveCriticalSection(&This->lock);
1576 return WINCODEC_ERR_WRONGSTATE;
1579 /* initialize libpng */
1580 This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1581 if (!This->png_ptr)
1583 LeaveCriticalSection(&This->lock);
1584 return E_FAIL;
1587 This->info_ptr = ppng_create_info_struct(This->png_ptr);
1588 if (!This->info_ptr)
1590 ppng_destroy_write_struct(&This->png_ptr, NULL);
1591 This->png_ptr = NULL;
1592 LeaveCriticalSection(&This->lock);
1593 return E_FAIL;
1596 IStream_AddRef(pIStream);
1597 This->stream = pIStream;
1599 /* set up setjmp/longjmp error handling */
1600 if (setjmp(jmpbuf))
1602 ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1603 This->png_ptr = NULL;
1604 IStream_Release(This->stream);
1605 This->stream = NULL;
1606 LeaveCriticalSection(&This->lock);
1607 return E_FAIL;
1609 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1611 /* set up custom i/o handling */
1612 ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush);
1614 LeaveCriticalSection(&This->lock);
1616 return S_OK;
1619 static HRESULT WINAPI PngEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
1620 GUID *pguidContainerFormat)
1622 FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
1623 return E_NOTIMPL;
1626 static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface,
1627 IWICBitmapEncoderInfo **ppIEncoderInfo)
1629 FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo);
1630 return E_NOTIMPL;
1633 static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface,
1634 UINT cCount, IWICColorContext **ppIColorContext)
1636 FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1637 return E_NOTIMPL;
1640 static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette)
1642 TRACE("(%p,%p)\n", iface, pIPalette);
1643 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1646 static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
1648 TRACE("(%p,%p)\n", iface, pIThumbnail);
1649 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1652 static HRESULT WINAPI PngEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
1654 TRACE("(%p,%p)\n", iface, pIPreview);
1655 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1658 static HRESULT WINAPI PngEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
1659 IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
1661 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1662 HRESULT hr;
1663 PROPBAG2 opts[1]= {{0}};
1665 TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
1667 EnterCriticalSection(&This->lock);
1669 if (This->frame_count != 0)
1671 LeaveCriticalSection(&This->lock);
1672 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1675 if (!This->stream)
1677 LeaveCriticalSection(&This->lock);
1678 return WINCODEC_ERR_NOTINITIALIZED;
1681 opts[0].pstrName = (LPOLESTR)wszPngInterlaceOption;
1682 opts[0].vt = VT_BOOL;
1683 opts[0].dwType = PROPBAG2_TYPE_DATA;
1685 hr = CreatePropertyBag2(opts, 1, ppIEncoderOptions);
1686 if (FAILED(hr))
1688 LeaveCriticalSection(&This->lock);
1689 return hr;
1692 This->frame_count = 1;
1694 LeaveCriticalSection(&This->lock);
1696 IWICBitmapEncoder_AddRef(iface);
1697 *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface;
1699 return S_OK;
1702 static HRESULT WINAPI PngEncoder_Commit(IWICBitmapEncoder *iface)
1704 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1705 TRACE("(%p)\n", iface);
1707 EnterCriticalSection(&This->lock);
1709 if (!This->frame_committed || This->committed)
1711 LeaveCriticalSection(&This->lock);
1712 return WINCODEC_ERR_WRONGSTATE;
1715 This->committed = TRUE;
1717 LeaveCriticalSection(&This->lock);
1719 return S_OK;
1722 static HRESULT WINAPI PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
1723 IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1725 FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
1726 return E_NOTIMPL;
1729 static const IWICBitmapEncoderVtbl PngEncoder_Vtbl = {
1730 PngEncoder_QueryInterface,
1731 PngEncoder_AddRef,
1732 PngEncoder_Release,
1733 PngEncoder_Initialize,
1734 PngEncoder_GetContainerFormat,
1735 PngEncoder_GetEncoderInfo,
1736 PngEncoder_SetColorContexts,
1737 PngEncoder_SetPalette,
1738 PngEncoder_SetThumbnail,
1739 PngEncoder_SetPreview,
1740 PngEncoder_CreateNewFrame,
1741 PngEncoder_Commit,
1742 PngEncoder_GetMetadataQueryWriter
1745 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
1747 PngEncoder *This;
1748 HRESULT ret;
1750 TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
1752 *ppv = NULL;
1754 if (!load_libpng())
1756 ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG);
1757 return E_FAIL;
1760 This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder));
1761 if (!This) return E_OUTOFMEMORY;
1763 This->IWICBitmapEncoder_iface.lpVtbl = &PngEncoder_Vtbl;
1764 This->IWICBitmapFrameEncode_iface.lpVtbl = &PngEncoder_FrameVtbl;
1765 This->ref = 1;
1766 This->png_ptr = NULL;
1767 This->info_ptr = NULL;
1768 This->stream = NULL;
1769 This->frame_count = 0;
1770 This->frame_initialized = FALSE;
1771 This->format = NULL;
1772 This->info_written = FALSE;
1773 This->width = 0;
1774 This->height = 0;
1775 This->xres = 0.0;
1776 This->yres = 0.0;
1777 This->lines_written = 0;
1778 This->frame_committed = FALSE;
1779 This->committed = FALSE;
1780 This->data = NULL;
1781 InitializeCriticalSection(&This->lock);
1782 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock");
1784 ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
1785 IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1787 return ret;
1790 #else /* !HAVE_PNG_H */
1792 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
1794 ERR("Trying to load PNG picture, but PNG support is not compiled in.\n");
1795 return E_FAIL;
1798 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
1800 ERR("Trying to save PNG picture, but PNG support is not compiled in.\n");
1801 return E_FAIL;
1804 #endif