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