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