65e2da4d86ba6fd50e44491672123268adc59275
[reactos.git] / reactos / dll / win32 / windowscodecs / pngformat.c
1 /*
2 * Copyright 2009 Vincent Povirk for CodeWeavers
3 * Copyright 2016 Dmitry Timoshkov
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include "wincodecs_private.h"
21
22 #include <winerror.h>
23
24 #ifdef HAVE_PNG_H
25 #include <png.h>
26 #endif
27
28 static inline ULONG read_ulong_be(BYTE* data)
29 {
30 return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
31 }
32
33 static HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_size)
34 {
35 BYTE header[8];
36 HRESULT hr;
37 ULONG bytesread;
38
39 hr = IStream_Read(stream, header, 8, &bytesread);
40 if (FAILED(hr) || bytesread < 8)
41 {
42 if (SUCCEEDED(hr))
43 hr = E_FAIL;
44 return hr;
45 }
46
47 *data_size = read_ulong_be(&header[0]);
48
49 memcpy(type, &header[4], 4);
50
51 if (data)
52 {
53 *data = HeapAlloc(GetProcessHeap(), 0, *data_size);
54 if (!*data)
55 return E_OUTOFMEMORY;
56
57 hr = IStream_Read(stream, *data, *data_size, &bytesread);
58
59 if (FAILED(hr) || bytesread < *data_size)
60 {
61 if (SUCCEEDED(hr))
62 hr = E_FAIL;
63 HeapFree(GetProcessHeap(), 0, *data);
64 *data = NULL;
65 return hr;
66 }
67
68 /* Windows ignores CRC of the chunk */
69 }
70
71 return S_OK;
72 }
73
74 static HRESULT LoadTextMetadata(IStream *stream, const GUID *preferred_vendor,
75 DWORD persist_options, MetadataItem **items, DWORD *item_count)
76 {
77 HRESULT hr;
78 BYTE type[4];
79 BYTE *data;
80 ULONG data_size;
81 ULONG name_len, value_len;
82 BYTE *name_end_ptr;
83 LPSTR name, value;
84 MetadataItem *result;
85
86 hr = read_png_chunk(stream, type, &data, &data_size);
87 if (FAILED(hr)) return hr;
88
89 name_end_ptr = memchr(data, 0, data_size);
90
91 name_len = name_end_ptr - data;
92
93 if (!name_end_ptr || name_len > 79)
94 {
95 HeapFree(GetProcessHeap(), 0, data);
96 return E_FAIL;
97 }
98
99 value_len = data_size - name_len - 1;
100
101 result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem));
102 name = HeapAlloc(GetProcessHeap(), 0, name_len + 1);
103 value = HeapAlloc(GetProcessHeap(), 0, value_len + 1);
104 if (!result || !name || !value)
105 {
106 HeapFree(GetProcessHeap(), 0, data);
107 HeapFree(GetProcessHeap(), 0, result);
108 HeapFree(GetProcessHeap(), 0, name);
109 HeapFree(GetProcessHeap(), 0, value);
110 return E_OUTOFMEMORY;
111 }
112
113 PropVariantInit(&result[0].schema);
114 PropVariantInit(&result[0].id);
115 PropVariantInit(&result[0].value);
116
117 memcpy(name, data, name_len + 1);
118 memcpy(value, name_end_ptr + 1, value_len);
119 value[value_len] = 0;
120
121 result[0].id.vt = VT_LPSTR;
122 result[0].id.u.pszVal = name;
123 result[0].value.vt = VT_LPSTR;
124 result[0].value.u.pszVal = value;
125
126 *items = result;
127 *item_count = 1;
128
129 HeapFree(GetProcessHeap(), 0, data);
130
131 return S_OK;
132 }
133
134 static const MetadataHandlerVtbl TextReader_Vtbl = {
135 0,
136 &CLSID_WICPngTextMetadataReader,
137 LoadTextMetadata
138 };
139
140 HRESULT PngTextReader_CreateInstance(REFIID iid, void** ppv)
141 {
142 return MetadataReader_Create(&TextReader_Vtbl, iid, ppv);
143 }
144
145 static HRESULT LoadGamaMetadata(IStream *stream, const GUID *preferred_vendor,
146 DWORD persist_options, MetadataItem **items, DWORD *item_count)
147 {
148 HRESULT hr;
149 BYTE type[4];
150 BYTE *data;
151 ULONG data_size;
152 ULONG gamma;
153 static const WCHAR ImageGamma[] = {'I','m','a','g','e','G','a','m','m','a',0};
154 LPWSTR name;
155 MetadataItem *result;
156
157 hr = read_png_chunk(stream, type, &data, &data_size);
158 if (FAILED(hr)) return hr;
159
160 if (data_size < 4)
161 {
162 HeapFree(GetProcessHeap(), 0, data);
163 return E_FAIL;
164 }
165
166 gamma = read_ulong_be(data);
167
168 HeapFree(GetProcessHeap(), 0, data);
169
170 result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem));
171 name = HeapAlloc(GetProcessHeap(), 0, sizeof(ImageGamma));
172 if (!result || !name)
173 {
174 HeapFree(GetProcessHeap(), 0, result);
175 HeapFree(GetProcessHeap(), 0, name);
176 return E_OUTOFMEMORY;
177 }
178
179 PropVariantInit(&result[0].schema);
180 PropVariantInit(&result[0].id);
181 PropVariantInit(&result[0].value);
182
183 memcpy(name, ImageGamma, sizeof(ImageGamma));
184
185 result[0].id.vt = VT_LPWSTR;
186 result[0].id.u.pwszVal = name;
187 result[0].value.vt = VT_UI4;
188 result[0].value.u.ulVal = gamma;
189
190 *items = result;
191 *item_count = 1;
192
193 return S_OK;
194 }
195
196 static const MetadataHandlerVtbl GamaReader_Vtbl = {
197 0,
198 &CLSID_WICPngGamaMetadataReader,
199 LoadGamaMetadata
200 };
201
202 HRESULT PngGamaReader_CreateInstance(REFIID iid, void** ppv)
203 {
204 return MetadataReader_Create(&GamaReader_Vtbl, iid, ppv);
205 }
206
207 static HRESULT LoadChrmMetadata(IStream *stream, const GUID *preferred_vendor,
208 DWORD persist_options, MetadataItem **items, DWORD *item_count)
209 {
210 HRESULT hr;
211 BYTE type[4];
212 BYTE *data;
213 ULONG data_size;
214 static const WCHAR names[8][12] = {
215 {'W','h','i','t','e','P','o','i','n','t','X',0},
216 {'W','h','i','t','e','P','o','i','n','t','Y',0},
217 {'R','e','d','X',0},
218 {'R','e','d','Y',0},
219 {'G','r','e','e','n','X',0},
220 {'G','r','e','e','n','Y',0},
221 {'B','l','u','e','X',0},
222 {'B','l','u','e','Y',0},
223 };
224 LPWSTR dyn_names[8] = {0};
225 MetadataItem *result;
226 int i;
227
228 hr = read_png_chunk(stream, type, &data, &data_size);
229 if (FAILED(hr)) return hr;
230
231 if (data_size < 32)
232 {
233 HeapFree(GetProcessHeap(), 0, data);
234 return E_FAIL;
235 }
236
237 result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MetadataItem)*8);
238 for (i=0; i<8; i++)
239 {
240 dyn_names[i] = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(lstrlenW(names[i])+1));
241 if (!dyn_names[i]) break;
242 }
243 if (!result || i < 8)
244 {
245 HeapFree(GetProcessHeap(), 0, result);
246 for (i=0; i<8; i++)
247 HeapFree(GetProcessHeap(), 0, dyn_names[i]);
248 HeapFree(GetProcessHeap(), 0, data);
249 return E_OUTOFMEMORY;
250 }
251
252 for (i=0; i<8; i++)
253 {
254 PropVariantInit(&result[i].schema);
255
256 PropVariantInit(&result[i].id);
257 result[i].id.vt = VT_LPWSTR;
258 result[i].id.u.pwszVal = dyn_names[i];
259 lstrcpyW(dyn_names[i], names[i]);
260
261 PropVariantInit(&result[i].value);
262 result[i].value.vt = VT_UI4;
263 result[i].value.u.ulVal = read_ulong_be(&data[i*4]);
264 }
265
266 *items = result;
267 *item_count = 8;
268
269 HeapFree(GetProcessHeap(), 0, data);
270
271 return S_OK;
272 }
273
274 static const MetadataHandlerVtbl ChrmReader_Vtbl = {
275 0,
276 &CLSID_WICPngChrmMetadataReader,
277 LoadChrmMetadata
278 };
279
280 HRESULT PngChrmReader_CreateInstance(REFIID iid, void** ppv)
281 {
282 return MetadataReader_Create(&ChrmReader_Vtbl, iid, ppv);
283 }
284
285 #ifdef SONAME_LIBPNG
286
287 static void *libpng_handle;
288 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
289 MAKE_FUNCPTR(png_create_read_struct);
290 MAKE_FUNCPTR(png_create_info_struct);
291 MAKE_FUNCPTR(png_create_write_struct);
292 MAKE_FUNCPTR(png_destroy_read_struct);
293 MAKE_FUNCPTR(png_destroy_write_struct);
294 MAKE_FUNCPTR(png_error);
295 MAKE_FUNCPTR(png_get_bit_depth);
296 MAKE_FUNCPTR(png_get_color_type);
297 MAKE_FUNCPTR(png_get_error_ptr);
298 MAKE_FUNCPTR(png_get_iCCP);
299 MAKE_FUNCPTR(png_get_image_height);
300 MAKE_FUNCPTR(png_get_image_width);
301 MAKE_FUNCPTR(png_get_io_ptr);
302 MAKE_FUNCPTR(png_get_pHYs);
303 MAKE_FUNCPTR(png_get_PLTE);
304 MAKE_FUNCPTR(png_get_tRNS);
305 MAKE_FUNCPTR(png_set_bgr);
306 MAKE_FUNCPTR(png_set_crc_action);
307 MAKE_FUNCPTR(png_set_error_fn);
308 MAKE_FUNCPTR(png_set_filler);
309 MAKE_FUNCPTR(png_set_filter);
310 MAKE_FUNCPTR(png_set_gray_to_rgb);
311 MAKE_FUNCPTR(png_set_interlace_handling);
312 MAKE_FUNCPTR(png_set_IHDR);
313 MAKE_FUNCPTR(png_set_pHYs);
314 MAKE_FUNCPTR(png_set_PLTE);
315 MAKE_FUNCPTR(png_set_read_fn);
316 MAKE_FUNCPTR(png_set_strip_16);
317 MAKE_FUNCPTR(png_set_tRNS);
318 MAKE_FUNCPTR(png_set_tRNS_to_alpha);
319 MAKE_FUNCPTR(png_set_write_fn);
320 MAKE_FUNCPTR(png_read_end);
321 MAKE_FUNCPTR(png_read_image);
322 MAKE_FUNCPTR(png_read_info);
323 MAKE_FUNCPTR(png_write_end);
324 MAKE_FUNCPTR(png_write_info);
325 MAKE_FUNCPTR(png_write_rows);
326 #undef MAKE_FUNCPTR
327
328 static CRITICAL_SECTION init_png_cs;
329 static CRITICAL_SECTION_DEBUG init_png_cs_debug =
330 {
331 0, 0, &init_png_cs,
332 { &init_png_cs_debug.ProcessLocksList,
333 &init_png_cs_debug.ProcessLocksList },
334 0, 0, { (DWORD_PTR)(__FILE__ ": init_png_cs") }
335 };
336 static CRITICAL_SECTION init_png_cs = { &init_png_cs_debug, -1, 0, 0, 0, 0 };
337
338 static const WCHAR wszPngInterlaceOption[] = {'I','n','t','e','r','l','a','c','e','O','p','t','i','o','n',0};
339 static const WCHAR wszPngFilterOption[] = {'F','i','l','t','e','r','O','p','t','i','o','n',0};
340
341 static void *load_libpng(void)
342 {
343 void *result;
344
345 EnterCriticalSection(&init_png_cs);
346
347 if(!libpng_handle && (libpng_handle = wine_dlopen(SONAME_LIBPNG, RTLD_NOW, NULL, 0)) != NULL) {
348
349 #define LOAD_FUNCPTR(f) \
350 if((p##f = wine_dlsym(libpng_handle, #f, NULL, 0)) == NULL) { \
351 libpng_handle = NULL; \
352 LeaveCriticalSection(&init_png_cs); \
353 return NULL; \
354 }
355 LOAD_FUNCPTR(png_create_read_struct);
356 LOAD_FUNCPTR(png_create_info_struct);
357 LOAD_FUNCPTR(png_create_write_struct);
358 LOAD_FUNCPTR(png_destroy_read_struct);
359 LOAD_FUNCPTR(png_destroy_write_struct);
360 LOAD_FUNCPTR(png_error);
361 LOAD_FUNCPTR(png_get_bit_depth);
362 LOAD_FUNCPTR(png_get_color_type);
363 LOAD_FUNCPTR(png_get_error_ptr);
364 LOAD_FUNCPTR(png_get_iCCP);
365 LOAD_FUNCPTR(png_get_image_height);
366 LOAD_FUNCPTR(png_get_image_width);
367 LOAD_FUNCPTR(png_get_io_ptr);
368 LOAD_FUNCPTR(png_get_pHYs);
369 LOAD_FUNCPTR(png_get_PLTE);
370 LOAD_FUNCPTR(png_get_tRNS);
371 LOAD_FUNCPTR(png_set_bgr);
372 LOAD_FUNCPTR(png_set_crc_action);
373 LOAD_FUNCPTR(png_set_error_fn);
374 LOAD_FUNCPTR(png_set_filler);
375 LOAD_FUNCPTR(png_set_filter);
376 LOAD_FUNCPTR(png_set_gray_to_rgb);
377 LOAD_FUNCPTR(png_set_interlace_handling);
378 LOAD_FUNCPTR(png_set_IHDR);
379 LOAD_FUNCPTR(png_set_pHYs);
380 LOAD_FUNCPTR(png_set_PLTE);
381 LOAD_FUNCPTR(png_set_read_fn);
382 LOAD_FUNCPTR(png_set_strip_16);
383 LOAD_FUNCPTR(png_set_tRNS);
384 LOAD_FUNCPTR(png_set_tRNS_to_alpha);
385 LOAD_FUNCPTR(png_set_write_fn);
386 LOAD_FUNCPTR(png_read_end);
387 LOAD_FUNCPTR(png_read_image);
388 LOAD_FUNCPTR(png_read_info);
389 LOAD_FUNCPTR(png_write_end);
390 LOAD_FUNCPTR(png_write_info);
391 LOAD_FUNCPTR(png_write_rows);
392
393 #undef LOAD_FUNCPTR
394 }
395
396 result = libpng_handle;
397
398 LeaveCriticalSection(&init_png_cs);
399
400 return result;
401 }
402
403 static void user_error_fn(png_structp png_ptr, png_const_charp error_message)
404 {
405 jmp_buf *pjmpbuf;
406
407 /* This uses setjmp/longjmp just like the default. We can't use the
408 * default because there's no way to access the jmp buffer in the png_struct
409 * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
410 WARN("PNG error: %s\n", debugstr_a(error_message));
411 pjmpbuf = ppng_get_error_ptr(png_ptr);
412 longjmp(*pjmpbuf, 1);
413 }
414
415 static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message)
416 {
417 WARN("PNG warning: %s\n", debugstr_a(warning_message));
418 }
419
420 typedef struct {
421 ULARGE_INTEGER ofs, len;
422 IWICMetadataReader* reader;
423 } metadata_block_info;
424
425 typedef struct {
426 IWICBitmapDecoder IWICBitmapDecoder_iface;
427 IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
428 IWICMetadataBlockReader IWICMetadataBlockReader_iface;
429 LONG ref;
430 IStream *stream;
431 png_structp png_ptr;
432 png_infop info_ptr;
433 png_infop end_info;
434 BOOL initialized;
435 int bpp;
436 int width, height;
437 UINT stride;
438 const WICPixelFormatGUID *format;
439 BYTE *image_bits;
440 CRITICAL_SECTION lock; /* must be held when png structures are accessed or initialized is set */
441 ULONG metadata_count;
442 metadata_block_info* metadata_blocks;
443 } PngDecoder;
444
445 static inline PngDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
446 {
447 return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapDecoder_iface);
448 }
449
450 static inline PngDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
451 {
452 return CONTAINING_RECORD(iface, PngDecoder, IWICBitmapFrameDecode_iface);
453 }
454
455 static inline PngDecoder *impl_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface)
456 {
457 return CONTAINING_RECORD(iface, PngDecoder, IWICMetadataBlockReader_iface);
458 }
459
460 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl;
461
462 static HRESULT WINAPI PngDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
463 void **ppv)
464 {
465 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
466 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
467
468 if (!ppv) return E_INVALIDARG;
469
470 if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
471 {
472 *ppv = &This->IWICBitmapDecoder_iface;
473 }
474 else
475 {
476 *ppv = NULL;
477 return E_NOINTERFACE;
478 }
479
480 IUnknown_AddRef((IUnknown*)*ppv);
481 return S_OK;
482 }
483
484 static ULONG WINAPI PngDecoder_AddRef(IWICBitmapDecoder *iface)
485 {
486 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
487 ULONG ref = InterlockedIncrement(&This->ref);
488
489 TRACE("(%p) refcount=%u\n", iface, ref);
490
491 return ref;
492 }
493
494 static ULONG WINAPI PngDecoder_Release(IWICBitmapDecoder *iface)
495 {
496 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
497 ULONG ref = InterlockedDecrement(&This->ref);
498 ULONG i;
499
500 TRACE("(%p) refcount=%u\n", iface, ref);
501
502 if (ref == 0)
503 {
504 if (This->stream)
505 IStream_Release(This->stream);
506 if (This->png_ptr)
507 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
508 This->lock.DebugInfo->Spare[0] = 0;
509 DeleteCriticalSection(&This->lock);
510 HeapFree(GetProcessHeap(), 0, This->image_bits);
511 for (i=0; i<This->metadata_count; i++)
512 {
513 if (This->metadata_blocks[i].reader)
514 IWICMetadataReader_Release(This->metadata_blocks[i].reader);
515 }
516 HeapFree(GetProcessHeap(), 0, This->metadata_blocks);
517 HeapFree(GetProcessHeap(), 0, This);
518 }
519
520 return ref;
521 }
522
523 static HRESULT WINAPI PngDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream,
524 DWORD *capability)
525 {
526 HRESULT hr;
527
528 TRACE("(%p,%p,%p)\n", iface, stream, capability);
529
530 if (!stream || !capability) return E_INVALIDARG;
531
532 hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand);
533 if (hr != S_OK) return hr;
534
535 *capability = WICBitmapDecoderCapabilityCanDecodeAllImages |
536 WICBitmapDecoderCapabilityCanDecodeSomeImages;
537 /* FIXME: WICBitmapDecoderCapabilityCanEnumerateMetadata */
538 return S_OK;
539 }
540
541 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
542 {
543 IStream *stream = ppng_get_io_ptr(png_ptr);
544 HRESULT hr;
545 ULONG bytesread;
546
547 hr = IStream_Read(stream, data, length, &bytesread);
548 if (FAILED(hr) || bytesread != length)
549 {
550 ppng_error(png_ptr, "failed reading data");
551 }
552 }
553
554 static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
555 WICDecodeOptions cacheOptions)
556 {
557 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
558 LARGE_INTEGER seek;
559 HRESULT hr=S_OK;
560 png_bytep *row_pointers=NULL;
561 UINT image_size;
562 UINT i;
563 int color_type, bit_depth;
564 png_bytep trans;
565 int num_trans;
566 png_uint_32 transparency;
567 png_color_16p trans_values;
568 png_colorp png_palette;
569 int num_palette;
570 jmp_buf jmpbuf;
571 BYTE chunk_type[4];
572 ULONG chunk_size;
573 ULARGE_INTEGER chunk_start;
574 ULONG metadata_blocks_size = 0;
575
576 TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions);
577
578 EnterCriticalSection(&This->lock);
579
580 /* initialize libpng */
581 This->png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
582 if (!This->png_ptr)
583 {
584 hr = E_FAIL;
585 goto end;
586 }
587
588 This->info_ptr = ppng_create_info_struct(This->png_ptr);
589 if (!This->info_ptr)
590 {
591 ppng_destroy_read_struct(&This->png_ptr, NULL, NULL);
592 This->png_ptr = NULL;
593 hr = E_FAIL;
594 goto end;
595 }
596
597 This->end_info = ppng_create_info_struct(This->png_ptr);
598 if (!This->end_info)
599 {
600 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, NULL);
601 This->png_ptr = NULL;
602 hr = E_FAIL;
603 goto end;
604 }
605
606 /* set up setjmp/longjmp error handling */
607 if (setjmp(jmpbuf))
608 {
609 ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info);
610 HeapFree(GetProcessHeap(), 0, row_pointers);
611 This->png_ptr = NULL;
612 hr = WINCODEC_ERR_UNKNOWNIMAGEFORMAT;
613 goto end;
614 }
615 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
616 ppng_set_crc_action(This->png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
617
618 /* seek to the start of the stream */
619 seek.QuadPart = 0;
620 hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL);
621 if (FAILED(hr)) goto end;
622
623 /* set up custom i/o handling */
624 ppng_set_read_fn(This->png_ptr, pIStream, user_read_data);
625
626 /* read the header */
627 ppng_read_info(This->png_ptr, This->info_ptr);
628
629 /* choose a pixel format */
630 color_type = ppng_get_color_type(This->png_ptr, This->info_ptr);
631 bit_depth = ppng_get_bit_depth(This->png_ptr, This->info_ptr);
632
633 /* check for color-keyed alpha */
634 transparency = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans, &num_trans, &trans_values);
635
636 if (!ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette))
637 num_palette = 0;
638
639 TRACE("color_type %d, bit_depth %d, transparency %d, num_palette %d\n",
640 color_type, bit_depth, transparency, num_palette);
641
642 switch (color_type)
643 {
644 case PNG_COLOR_TYPE_GRAY:
645 This->bpp = bit_depth;
646 switch (bit_depth)
647 {
648 case 1:
649 This->format = num_palette ? &GUID_WICPixelFormat1bppIndexed : &GUID_WICPixelFormatBlackWhite;
650 break;
651 case 2:
652 This->format = num_palette ? &GUID_WICPixelFormat2bppIndexed : &GUID_WICPixelFormat2bppGray;
653 break;
654 case 4:
655 This->format = num_palette ? &GUID_WICPixelFormat4bppIndexed : &GUID_WICPixelFormat4bppGray;
656 break;
657 case 8:
658 This->format = num_palette ? &GUID_WICPixelFormat8bppIndexed : &GUID_WICPixelFormat8bppGray;
659 break;
660 case 16: This->format = &GUID_WICPixelFormat16bppGray; break;
661 default:
662 ERR("invalid grayscale bit depth: %i\n", bit_depth);
663 hr = WINCODEC_ERR_UNKNOWNIMAGEFORMAT;
664 goto end;
665 }
666 break;
667 case PNG_COLOR_TYPE_GRAY_ALPHA:
668 /* WIC does not support grayscale alpha formats so use RGBA */
669 ppng_set_gray_to_rgb(This->png_ptr);
670 /* fall through */
671 case PNG_COLOR_TYPE_RGB_ALPHA:
672 This->bpp = bit_depth * 4;
673 switch (bit_depth)
674 {
675 case 8:
676 ppng_set_bgr(This->png_ptr);
677 This->format = &GUID_WICPixelFormat32bppBGRA;
678 break;
679 case 16: This->format = &GUID_WICPixelFormat64bppRGBA; break;
680 default:
681 ERR("invalid RGBA bit depth: %i\n", bit_depth);
682 hr = E_FAIL;
683 goto end;
684 }
685 break;
686 case PNG_COLOR_TYPE_PALETTE:
687 This->bpp = bit_depth;
688 switch (bit_depth)
689 {
690 case 1: This->format = &GUID_WICPixelFormat1bppIndexed; break;
691 case 2: This->format = &GUID_WICPixelFormat2bppIndexed; break;
692 case 4: This->format = &GUID_WICPixelFormat4bppIndexed; break;
693 case 8: This->format = &GUID_WICPixelFormat8bppIndexed; break;
694 default:
695 ERR("invalid indexed color bit depth: %i\n", bit_depth);
696 hr = E_FAIL;
697 goto end;
698 }
699 break;
700 case PNG_COLOR_TYPE_RGB:
701 This->bpp = bit_depth * 3;
702 switch (bit_depth)
703 {
704 case 8:
705 ppng_set_bgr(This->png_ptr);
706 This->format = &GUID_WICPixelFormat24bppBGR;
707 break;
708 case 16: This->format = &GUID_WICPixelFormat48bppRGB; break;
709 default:
710 ERR("invalid RGB color bit depth: %i\n", bit_depth);
711 hr = E_FAIL;
712 goto end;
713 }
714 break;
715 default:
716 ERR("invalid color type %i\n", color_type);
717 hr = E_FAIL;
718 goto end;
719 }
720
721 /* read the image data */
722 This->width = ppng_get_image_width(This->png_ptr, This->info_ptr);
723 This->height = ppng_get_image_height(This->png_ptr, This->info_ptr);
724 This->stride = (This->width * This->bpp + 7) / 8;
725 image_size = This->stride * This->height;
726
727 This->image_bits = HeapAlloc(GetProcessHeap(), 0, image_size);
728 if (!This->image_bits)
729 {
730 hr = E_OUTOFMEMORY;
731 goto end;
732 }
733
734 row_pointers = HeapAlloc(GetProcessHeap(), 0, sizeof(png_bytep)*This->height);
735 if (!row_pointers)
736 {
737 hr = E_OUTOFMEMORY;
738 goto end;
739 }
740
741 for (i=0; i<This->height; i++)
742 row_pointers[i] = This->image_bits + i * This->stride;
743
744 ppng_read_image(This->png_ptr, row_pointers);
745
746 HeapFree(GetProcessHeap(), 0, row_pointers);
747 row_pointers = NULL;
748
749 ppng_read_end(This->png_ptr, This->end_info);
750
751 /* Find the metadata chunks in the file. */
752 seek.QuadPart = 8;
753
754 do
755 {
756 hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, &chunk_start);
757 if (FAILED(hr)) goto end;
758
759 hr = read_png_chunk(pIStream, chunk_type, NULL, &chunk_size);
760 if (FAILED(hr)) goto end;
761
762 if (chunk_type[0] >= 'a' && chunk_type[0] <= 'z' &&
763 memcmp(chunk_type, "tRNS", 4) && memcmp(chunk_type, "pHYs", 4))
764 {
765 /* This chunk is considered metadata. */
766 if (This->metadata_count == metadata_blocks_size)
767 {
768 metadata_block_info* new_metadata_blocks;
769 ULONG new_metadata_blocks_size;
770
771 new_metadata_blocks_size = 4 + metadata_blocks_size * 2;
772 new_metadata_blocks = HeapAlloc(GetProcessHeap(), 0,
773 new_metadata_blocks_size * sizeof(*new_metadata_blocks));
774
775 if (!new_metadata_blocks)
776 {
777 hr = E_OUTOFMEMORY;
778 goto end;
779 }
780
781 memcpy(new_metadata_blocks, This->metadata_blocks,
782 This->metadata_count * sizeof(*new_metadata_blocks));
783
784 HeapFree(GetProcessHeap(), 0, This->metadata_blocks);
785 This->metadata_blocks = new_metadata_blocks;
786 metadata_blocks_size = new_metadata_blocks_size;
787 }
788
789 This->metadata_blocks[This->metadata_count].ofs = chunk_start;
790 This->metadata_blocks[This->metadata_count].len.QuadPart = chunk_size + 12;
791 This->metadata_blocks[This->metadata_count].reader = NULL;
792 This->metadata_count++;
793 }
794
795 seek.QuadPart = chunk_start.QuadPart + chunk_size + 12; /* skip data and CRC */
796 } while (memcmp(chunk_type, "IEND", 4));
797
798 This->stream = pIStream;
799 IStream_AddRef(This->stream);
800
801 This->initialized = TRUE;
802
803 end:
804 LeaveCriticalSection(&This->lock);
805
806 return hr;
807 }
808
809 static HRESULT WINAPI PngDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
810 GUID *pguidContainerFormat)
811 {
812 memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
813 return S_OK;
814 }
815
816 static HRESULT WINAPI PngDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
817 IWICBitmapDecoderInfo **ppIDecoderInfo)
818 {
819 HRESULT hr;
820 IWICComponentInfo *compinfo;
821
822 TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
823
824 hr = CreateComponentInfo(&CLSID_WICPngDecoder, &compinfo);
825 if (FAILED(hr)) return hr;
826
827 hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
828 (void**)ppIDecoderInfo);
829
830 IWICComponentInfo_Release(compinfo);
831
832 return hr;
833 }
834
835 static HRESULT WINAPI PngDecoder_CopyPalette(IWICBitmapDecoder *iface,
836 IWICPalette *palette)
837 {
838 TRACE("(%p,%p)\n", iface, palette);
839 return WINCODEC_ERR_PALETTEUNAVAILABLE;
840 }
841
842 static HRESULT WINAPI PngDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
843 IWICMetadataQueryReader **reader)
844 {
845 FIXME("(%p,%p): stub\n", iface, reader);
846
847 if (!reader) return E_INVALIDARG;
848
849 *reader = NULL;
850 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
851 }
852
853 static HRESULT WINAPI PngDecoder_GetPreview(IWICBitmapDecoder *iface,
854 IWICBitmapSource **ppIBitmapSource)
855 {
856 TRACE("(%p,%p)\n", iface, ppIBitmapSource);
857
858 if (!ppIBitmapSource) return E_INVALIDARG;
859
860 *ppIBitmapSource = NULL;
861 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
862 }
863
864 static HRESULT WINAPI PngDecoder_GetColorContexts(IWICBitmapDecoder *iface,
865 UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
866 {
867 TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
868 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
869 }
870
871 static HRESULT WINAPI PngDecoder_GetThumbnail(IWICBitmapDecoder *iface,
872 IWICBitmapSource **ppIThumbnail)
873 {
874 TRACE("(%p,%p)\n", iface, ppIThumbnail);
875
876 if (!ppIThumbnail) return E_INVALIDARG;
877
878 *ppIThumbnail = NULL;
879 return WINCODEC_ERR_CODECNOTHUMBNAIL;
880 }
881
882 static HRESULT WINAPI PngDecoder_GetFrameCount(IWICBitmapDecoder *iface,
883 UINT *pCount)
884 {
885 if (!pCount) return E_INVALIDARG;
886
887 *pCount = 1;
888 return S_OK;
889 }
890
891 static HRESULT WINAPI PngDecoder_GetFrame(IWICBitmapDecoder *iface,
892 UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
893 {
894 PngDecoder *This = impl_from_IWICBitmapDecoder(iface);
895 TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
896
897 if (!This->initialized) return WINCODEC_ERR_FRAMEMISSING;
898
899 if (index != 0) return E_INVALIDARG;
900
901 IWICBitmapDecoder_AddRef(iface);
902
903 *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface;
904
905 return S_OK;
906 }
907
908 static const IWICBitmapDecoderVtbl PngDecoder_Vtbl = {
909 PngDecoder_QueryInterface,
910 PngDecoder_AddRef,
911 PngDecoder_Release,
912 PngDecoder_QueryCapability,
913 PngDecoder_Initialize,
914 PngDecoder_GetContainerFormat,
915 PngDecoder_GetDecoderInfo,
916 PngDecoder_CopyPalette,
917 PngDecoder_GetMetadataQueryReader,
918 PngDecoder_GetPreview,
919 PngDecoder_GetColorContexts,
920 PngDecoder_GetThumbnail,
921 PngDecoder_GetFrameCount,
922 PngDecoder_GetFrame
923 };
924
925 static HRESULT WINAPI PngDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
926 void **ppv)
927 {
928 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
929 if (!ppv) return E_INVALIDARG;
930
931 if (IsEqualIID(&IID_IUnknown, iid) ||
932 IsEqualIID(&IID_IWICBitmapSource, iid) ||
933 IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
934 {
935 *ppv = &This->IWICBitmapFrameDecode_iface;
936 }
937 else if (IsEqualIID(&IID_IWICMetadataBlockReader, iid))
938 {
939 *ppv = &This->IWICMetadataBlockReader_iface;
940 }
941 else
942 {
943 *ppv = NULL;
944 return E_NOINTERFACE;
945 }
946
947 IUnknown_AddRef((IUnknown*)*ppv);
948 return S_OK;
949 }
950
951 static ULONG WINAPI PngDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface)
952 {
953 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
954 return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
955 }
956
957 static ULONG WINAPI PngDecoder_Frame_Release(IWICBitmapFrameDecode *iface)
958 {
959 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
960 return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
961 }
962
963 static HRESULT WINAPI PngDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface,
964 UINT *puiWidth, UINT *puiHeight)
965 {
966 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
967 *puiWidth = This->width;
968 *puiHeight = This->height;
969 TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight);
970 return S_OK;
971 }
972
973 static HRESULT WINAPI PngDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface,
974 WICPixelFormatGUID *pPixelFormat)
975 {
976 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
977 TRACE("(%p,%p)\n", iface, pPixelFormat);
978
979 memcpy(pPixelFormat, This->format, sizeof(GUID));
980
981 return S_OK;
982 }
983
984 static HRESULT WINAPI PngDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface,
985 double *pDpiX, double *pDpiY)
986 {
987 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
988 png_uint_32 ret, xres, yres;
989 int unit_type;
990
991 EnterCriticalSection(&This->lock);
992
993 ret = ppng_get_pHYs(This->png_ptr, This->info_ptr, &xres, &yres, &unit_type);
994
995 if (ret && unit_type == PNG_RESOLUTION_METER)
996 {
997 *pDpiX = xres * 0.0254;
998 *pDpiY = yres * 0.0254;
999 }
1000 else
1001 {
1002 WARN("no pHYs block present\n");
1003 *pDpiX = *pDpiY = 96.0;
1004 }
1005
1006 LeaveCriticalSection(&This->lock);
1007
1008 TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY);
1009
1010 return S_OK;
1011 }
1012
1013 static HRESULT WINAPI PngDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface,
1014 IWICPalette *pIPalette)
1015 {
1016 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1017 png_uint_32 ret;
1018 png_colorp png_palette;
1019 int num_palette;
1020 WICColor palette[256];
1021 png_bytep trans_alpha;
1022 int num_trans;
1023 png_color_16p trans_values;
1024 int i;
1025 HRESULT hr=S_OK;
1026
1027 TRACE("(%p,%p)\n", iface, pIPalette);
1028
1029 EnterCriticalSection(&This->lock);
1030
1031 ret = ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette);
1032 if (!ret)
1033 {
1034 hr = WINCODEC_ERR_PALETTEUNAVAILABLE;
1035 goto end;
1036 }
1037
1038 if (num_palette > 256)
1039 {
1040 ERR("palette has %i colors?!\n", num_palette);
1041 hr = E_FAIL;
1042 goto end;
1043 }
1044
1045 ret = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans_alpha, &num_trans, &trans_values);
1046 if (!ret) num_trans = 0;
1047
1048 for (i=0; i<num_palette; i++)
1049 {
1050 BYTE alpha = (i < num_trans) ? trans_alpha[i] : 0xff;
1051 palette[i] = (alpha << 24 |
1052 png_palette[i].red << 16|
1053 png_palette[i].green << 8|
1054 png_palette[i].blue);
1055 }
1056
1057 end:
1058
1059 LeaveCriticalSection(&This->lock);
1060
1061 if (SUCCEEDED(hr))
1062 hr = IWICPalette_InitializeCustom(pIPalette, palette, num_palette);
1063
1064 return hr;
1065 }
1066
1067 static HRESULT WINAPI PngDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface,
1068 const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
1069 {
1070 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1071 TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
1072
1073 return copy_pixels(This->bpp, This->image_bits,
1074 This->width, This->height, This->stride,
1075 prc, cbStride, cbBufferSize, pbBuffer);
1076 }
1077
1078 static HRESULT WINAPI PngDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
1079 IWICMetadataQueryReader **ppIMetadataQueryReader)
1080 {
1081 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1082
1083 TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
1084
1085 if (!ppIMetadataQueryReader)
1086 return E_INVALIDARG;
1087
1088 return MetadataQueryReader_CreateInstance(&This->IWICMetadataBlockReader_iface, ppIMetadataQueryReader);
1089 }
1090
1091 static HRESULT WINAPI PngDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface,
1092 UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
1093 {
1094 PngDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
1095 png_charp name;
1096 BYTE *profile;
1097 png_uint_32 len;
1098 int compression_type;
1099 HRESULT hr;
1100
1101 TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount);
1102
1103 if (!pcActualCount) return E_INVALIDARG;
1104
1105 EnterCriticalSection(&This->lock);
1106
1107 if (ppng_get_iCCP(This->png_ptr, This->info_ptr, &name, &compression_type, (void *)&profile, &len))
1108 {
1109 if (cCount && ppIColorContexts)
1110 {
1111 hr = IWICColorContext_InitializeFromMemory(*ppIColorContexts, profile, len);
1112 if (FAILED(hr))
1113 {
1114 LeaveCriticalSection(&This->lock);
1115 return hr;
1116 }
1117 }
1118 *pcActualCount = 1;
1119 }
1120 else
1121 *pcActualCount = 0;
1122
1123 LeaveCriticalSection(&This->lock);
1124
1125 return S_OK;
1126 }
1127
1128 static HRESULT WINAPI PngDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
1129 IWICBitmapSource **ppIThumbnail)
1130 {
1131 TRACE("(%p,%p)\n", iface, ppIThumbnail);
1132
1133 if (!ppIThumbnail) return E_INVALIDARG;
1134
1135 *ppIThumbnail = NULL;
1136 return WINCODEC_ERR_CODECNOTHUMBNAIL;
1137 }
1138
1139 static const IWICBitmapFrameDecodeVtbl PngDecoder_FrameVtbl = {
1140 PngDecoder_Frame_QueryInterface,
1141 PngDecoder_Frame_AddRef,
1142 PngDecoder_Frame_Release,
1143 PngDecoder_Frame_GetSize,
1144 PngDecoder_Frame_GetPixelFormat,
1145 PngDecoder_Frame_GetResolution,
1146 PngDecoder_Frame_CopyPalette,
1147 PngDecoder_Frame_CopyPixels,
1148 PngDecoder_Frame_GetMetadataQueryReader,
1149 PngDecoder_Frame_GetColorContexts,
1150 PngDecoder_Frame_GetThumbnail
1151 };
1152
1153 static HRESULT WINAPI PngDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid,
1154 void **ppv)
1155 {
1156 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1157 return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv);
1158 }
1159
1160 static ULONG WINAPI PngDecoder_Block_AddRef(IWICMetadataBlockReader *iface)
1161 {
1162 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1163 return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
1164 }
1165
1166 static ULONG WINAPI PngDecoder_Block_Release(IWICMetadataBlockReader *iface)
1167 {
1168 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1169 return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1170 }
1171
1172 static HRESULT WINAPI PngDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
1173 GUID *pguidContainerFormat)
1174 {
1175 if (!pguidContainerFormat) return E_INVALIDARG;
1176 memcpy(pguidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID));
1177 return S_OK;
1178 }
1179
1180 static HRESULT WINAPI PngDecoder_Block_GetCount(IWICMetadataBlockReader *iface,
1181 UINT *pcCount)
1182 {
1183 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1184
1185 TRACE("%p,%p\n", iface, pcCount);
1186
1187 if (!pcCount) return E_INVALIDARG;
1188
1189 *pcCount = This->metadata_count;
1190
1191 return S_OK;
1192 }
1193
1194 static HRESULT WINAPI PngDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
1195 UINT nIndex, IWICMetadataReader **ppIMetadataReader)
1196 {
1197 PngDecoder *This = impl_from_IWICMetadataBlockReader(iface);
1198 HRESULT hr;
1199 IWICComponentFactory* factory;
1200 IWICStream* stream;
1201
1202 TRACE("%p,%d,%p\n", iface, nIndex, ppIMetadataReader);
1203
1204 if (nIndex >= This->metadata_count || !ppIMetadataReader)
1205 return E_INVALIDARG;
1206
1207 if (!This->metadata_blocks[nIndex].reader)
1208 {
1209 hr = StreamImpl_Create(&stream);
1210
1211 if (SUCCEEDED(hr))
1212 {
1213 hr = IWICStream_InitializeFromIStreamRegion(stream, This->stream,
1214 This->metadata_blocks[nIndex].ofs, This->metadata_blocks[nIndex].len);
1215
1216 if (SUCCEEDED(hr))
1217 hr = ComponentFactory_CreateInstance(&IID_IWICComponentFactory, (void**)&factory);
1218
1219 if (SUCCEEDED(hr))
1220 {
1221 hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory,
1222 &GUID_ContainerFormatPng, NULL, WICMetadataCreationAllowUnknown,
1223 (IStream*)stream, &This->metadata_blocks[nIndex].reader);
1224
1225 IWICComponentFactory_Release(factory);
1226 }
1227
1228 IWICStream_Release(stream);
1229 }
1230
1231 if (FAILED(hr))
1232 {
1233 *ppIMetadataReader = NULL;
1234 return hr;
1235 }
1236 }
1237
1238 *ppIMetadataReader = This->metadata_blocks[nIndex].reader;
1239 IWICMetadataReader_AddRef(*ppIMetadataReader);
1240
1241 return S_OK;
1242 }
1243
1244 static HRESULT WINAPI PngDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface,
1245 IEnumUnknown **ppIEnumMetadata)
1246 {
1247 FIXME("%p,%p\n", iface, ppIEnumMetadata);
1248 return E_NOTIMPL;
1249 }
1250
1251 static const IWICMetadataBlockReaderVtbl PngDecoder_BlockVtbl = {
1252 PngDecoder_Block_QueryInterface,
1253 PngDecoder_Block_AddRef,
1254 PngDecoder_Block_Release,
1255 PngDecoder_Block_GetContainerFormat,
1256 PngDecoder_Block_GetCount,
1257 PngDecoder_Block_GetReaderByIndex,
1258 PngDecoder_Block_GetEnumerator,
1259 };
1260
1261 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
1262 {
1263 PngDecoder *This;
1264 HRESULT ret;
1265
1266 TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
1267
1268 *ppv = NULL;
1269
1270 if (!load_libpng())
1271 {
1272 ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
1273 return E_FAIL;
1274 }
1275
1276 This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngDecoder));
1277 if (!This) return E_OUTOFMEMORY;
1278
1279 This->IWICBitmapDecoder_iface.lpVtbl = &PngDecoder_Vtbl;
1280 This->IWICBitmapFrameDecode_iface.lpVtbl = &PngDecoder_FrameVtbl;
1281 This->IWICMetadataBlockReader_iface.lpVtbl = &PngDecoder_BlockVtbl;
1282 This->ref = 1;
1283 This->png_ptr = NULL;
1284 This->info_ptr = NULL;
1285 This->end_info = NULL;
1286 This->stream = NULL;
1287 This->initialized = FALSE;
1288 This->image_bits = NULL;
1289 InitializeCriticalSection(&This->lock);
1290 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngDecoder.lock");
1291 This->metadata_count = 0;
1292 This->metadata_blocks = NULL;
1293
1294 ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
1295 IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
1296
1297 return ret;
1298 }
1299
1300 struct png_pixelformat {
1301 const WICPixelFormatGUID *guid;
1302 UINT bpp;
1303 int bit_depth;
1304 int color_type;
1305 BOOL remove_filler;
1306 BOOL swap_rgb;
1307 };
1308
1309 static const struct png_pixelformat formats[] = {
1310 {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1},
1311 {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0},
1312 {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0},
1313 {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0},
1314 {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0},
1315 {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0},
1316 {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1},
1317 {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1},
1318 {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0},
1319 {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0},
1320 {&GUID_WICPixelFormat1bppIndexed, 1, 1, PNG_COLOR_TYPE_PALETTE, 0, 0},
1321 {&GUID_WICPixelFormat2bppIndexed, 2, 2, PNG_COLOR_TYPE_PALETTE, 0, 0},
1322 {&GUID_WICPixelFormat4bppIndexed, 4, 4, PNG_COLOR_TYPE_PALETTE, 0, 0},
1323 {&GUID_WICPixelFormat8bppIndexed, 8, 8, PNG_COLOR_TYPE_PALETTE, 0, 0},
1324 {NULL},
1325 };
1326
1327 typedef struct PngEncoder {
1328 IWICBitmapEncoder IWICBitmapEncoder_iface;
1329 IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
1330 LONG ref;
1331 IStream *stream;
1332 png_structp png_ptr;
1333 png_infop info_ptr;
1334 UINT frame_count;
1335 BOOL frame_initialized;
1336 const struct png_pixelformat *format;
1337 BOOL info_written;
1338 UINT width, height;
1339 double xres, yres;
1340 UINT lines_written;
1341 BOOL frame_committed;
1342 BOOL committed;
1343 CRITICAL_SECTION lock;
1344 BOOL interlace;
1345 WICPngFilterOption filter;
1346 BYTE *data;
1347 UINT stride;
1348 UINT passes;
1349 WICColor palette[256];
1350 UINT colors;
1351 } PngEncoder;
1352
1353 static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
1354 {
1355 return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapEncoder_iface);
1356 }
1357
1358 static inline PngEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
1359 {
1360 return CONTAINING_RECORD(iface, PngEncoder, IWICBitmapFrameEncode_iface);
1361 }
1362
1363 static HRESULT WINAPI PngFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
1364 void **ppv)
1365 {
1366 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1367 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1368
1369 if (!ppv) return E_INVALIDARG;
1370
1371 if (IsEqualIID(&IID_IUnknown, iid) ||
1372 IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
1373 {
1374 *ppv = &This->IWICBitmapFrameEncode_iface;
1375 }
1376 else
1377 {
1378 *ppv = NULL;
1379 return E_NOINTERFACE;
1380 }
1381
1382 IUnknown_AddRef((IUnknown*)*ppv);
1383 return S_OK;
1384 }
1385
1386 static ULONG WINAPI PngFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
1387 {
1388 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1389 return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface);
1390 }
1391
1392 static ULONG WINAPI PngFrameEncode_Release(IWICBitmapFrameEncode *iface)
1393 {
1394 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1395 return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1396 }
1397
1398 static HRESULT WINAPI PngFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
1399 IPropertyBag2 *pIEncoderOptions)
1400 {
1401 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1402 WICPngFilterOption filter;
1403 BOOL interlace;
1404 PROPBAG2 opts[2]= {{0}};
1405 VARIANT opt_values[2];
1406 HRESULT opt_hres[2];
1407 HRESULT hr;
1408
1409 TRACE("(%p,%p)\n", iface, pIEncoderOptions);
1410
1411 opts[0].pstrName = (LPOLESTR)wszPngInterlaceOption;
1412 opts[0].vt = VT_BOOL;
1413 opts[1].pstrName = (LPOLESTR)wszPngFilterOption;
1414 opts[1].vt = VT_UI1;
1415
1416 if (pIEncoderOptions)
1417 {
1418 hr = IPropertyBag2_Read(pIEncoderOptions, sizeof(opts)/sizeof(opts[0]), opts, NULL, opt_values, opt_hres);
1419
1420 if (FAILED(hr))
1421 return hr;
1422
1423 if (V_VT(&opt_values[0]) == VT_EMPTY)
1424 interlace = FALSE;
1425 else
1426 interlace = (V_BOOL(&opt_values[0]) != 0);
1427
1428 filter = V_UI1(&opt_values[1]);
1429 if (filter > WICPngFilterAdaptive)
1430 {
1431 WARN("Unrecognized filter option value %u.\n", filter);
1432 filter = WICPngFilterUnspecified;
1433 }
1434 }
1435 else
1436 {
1437 interlace = FALSE;
1438 filter = WICPngFilterUnspecified;
1439 }
1440
1441 EnterCriticalSection(&This->lock);
1442
1443 if (This->frame_initialized)
1444 {
1445 LeaveCriticalSection(&This->lock);
1446 return WINCODEC_ERR_WRONGSTATE;
1447 }
1448
1449 This->interlace = interlace;
1450 This->filter = filter;
1451
1452 This->frame_initialized = TRUE;
1453
1454 LeaveCriticalSection(&This->lock);
1455
1456 return S_OK;
1457 }
1458
1459 static HRESULT WINAPI PngFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
1460 UINT uiWidth, UINT uiHeight)
1461 {
1462 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1463 TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
1464
1465 EnterCriticalSection(&This->lock);
1466
1467 if (!This->frame_initialized || This->info_written)
1468 {
1469 LeaveCriticalSection(&This->lock);
1470 return WINCODEC_ERR_WRONGSTATE;
1471 }
1472
1473 This->width = uiWidth;
1474 This->height = uiHeight;
1475
1476 LeaveCriticalSection(&This->lock);
1477
1478 return S_OK;
1479 }
1480
1481 static HRESULT WINAPI PngFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
1482 double dpiX, double dpiY)
1483 {
1484 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1485 TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
1486
1487 EnterCriticalSection(&This->lock);
1488
1489 if (!This->frame_initialized || This->info_written)
1490 {
1491 LeaveCriticalSection(&This->lock);
1492 return WINCODEC_ERR_WRONGSTATE;
1493 }
1494
1495 This->xres = dpiX;
1496 This->yres = dpiY;
1497
1498 LeaveCriticalSection(&This->lock);
1499
1500 return S_OK;
1501 }
1502
1503 static HRESULT WINAPI PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
1504 WICPixelFormatGUID *pPixelFormat)
1505 {
1506 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1507 int i;
1508 TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
1509
1510 EnterCriticalSection(&This->lock);
1511
1512 if (!This->frame_initialized || This->info_written)
1513 {
1514 LeaveCriticalSection(&This->lock);
1515 return WINCODEC_ERR_WRONGSTATE;
1516 }
1517
1518 for (i=0; formats[i].guid; i++)
1519 {
1520 if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
1521 break;
1522 }
1523
1524 if (!formats[i].guid) i = 0;
1525
1526 This->format = &formats[i];
1527 memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
1528
1529 LeaveCriticalSection(&This->lock);
1530
1531 return S_OK;
1532 }
1533
1534 static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
1535 UINT cCount, IWICColorContext **ppIColorContext)
1536 {
1537 FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1538 return E_NOTIMPL;
1539 }
1540
1541 static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
1542 IWICPalette *palette)
1543 {
1544 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1545 HRESULT hr;
1546
1547 TRACE("(%p,%p)\n", iface, palette);
1548
1549 if (!palette) return E_INVALIDARG;
1550
1551 EnterCriticalSection(&This->lock);
1552
1553 if (This->frame_initialized)
1554 hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors);
1555 else
1556 hr = WINCODEC_ERR_NOTINITIALIZED;
1557
1558 LeaveCriticalSection(&This->lock);
1559 return hr;
1560 }
1561
1562 static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
1563 IWICBitmapSource *pIThumbnail)
1564 {
1565 FIXME("(%p,%p): stub\n", iface, pIThumbnail);
1566 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1567 }
1568
1569 static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
1570 UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
1571 {
1572 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1573 png_byte **row_pointers=NULL;
1574 UINT i;
1575 jmp_buf jmpbuf;
1576 TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
1577
1578 EnterCriticalSection(&This->lock);
1579
1580 if (!This->frame_initialized || !This->width || !This->height || !This->format)
1581 {
1582 LeaveCriticalSection(&This->lock);
1583 return WINCODEC_ERR_WRONGSTATE;
1584 }
1585
1586 if (lineCount == 0 || lineCount + This->lines_written > This->height)
1587 {
1588 LeaveCriticalSection(&This->lock);
1589 return E_INVALIDARG;
1590 }
1591
1592 /* set up setjmp/longjmp error handling */
1593 if (setjmp(jmpbuf))
1594 {
1595 LeaveCriticalSection(&This->lock);
1596 HeapFree(GetProcessHeap(), 0, row_pointers);
1597 return E_FAIL;
1598 }
1599 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1600
1601 if (!This->info_written)
1602 {
1603 if (This->interlace)
1604 {
1605 /* libpng requires us to write all data multiple times in this case. */
1606 This->stride = (This->format->bpp * This->width + 7)/8;
1607 This->data = HeapAlloc(GetProcessHeap(), 0, This->height * This->stride);
1608 if (!This->data)
1609 {
1610 LeaveCriticalSection(&This->lock);
1611 return E_OUTOFMEMORY;
1612 }
1613 }
1614
1615 ppng_set_IHDR(This->png_ptr, This->info_ptr, This->width, This->height,
1616 This->format->bit_depth, This->format->color_type,
1617 This->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
1618 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1619
1620 if (This->xres != 0.0 && This->yres != 0.0)
1621 {
1622 ppng_set_pHYs(This->png_ptr, This->info_ptr, (This->xres+0.0127) / 0.0254,
1623 (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER);
1624 }
1625
1626 if (This->format->color_type == PNG_COLOR_TYPE_PALETTE && This->colors)
1627 {
1628 png_color png_palette[256];
1629 png_byte trans[256];
1630 UINT i, num_trans = 0, colors;
1631
1632 /* Newer libpng versions don't accept larger palettes than the declared
1633 * bit depth, so we need to generate the palette of the correct length.
1634 */
1635 colors = 1 << This->format->bit_depth;
1636
1637 for (i = 0; i < colors; i++)
1638 {
1639 if (i < This->colors)
1640 {
1641 png_palette[i].red = (This->palette[i] >> 16) & 0xff;
1642 png_palette[i].green = (This->palette[i] >> 8) & 0xff;
1643 png_palette[i].blue = This->palette[i] & 0xff;
1644 trans[i] = (This->palette[i] >> 24) & 0xff;
1645 if (trans[i] != 0xff)
1646 num_trans++;
1647 }
1648 else
1649 {
1650 png_palette[i].red = 0;
1651 png_palette[i].green = 0;
1652 png_palette[i].blue = 0;
1653 }
1654 }
1655
1656 ppng_set_PLTE(This->png_ptr, This->info_ptr, png_palette, colors);
1657
1658 if (num_trans)
1659 ppng_set_tRNS(This->png_ptr, This->info_ptr, trans, colors, NULL);
1660 }
1661
1662 ppng_write_info(This->png_ptr, This->info_ptr);
1663
1664 if (This->format->remove_filler)
1665 ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER);
1666
1667 if (This->format->swap_rgb)
1668 ppng_set_bgr(This->png_ptr);
1669
1670 if (This->interlace)
1671 This->passes = ppng_set_interlace_handling(This->png_ptr);
1672
1673 if (This->filter != WICPngFilterUnspecified)
1674 {
1675 static const int png_filter_map[] =
1676 {
1677 /* WICPngFilterUnspecified */ PNG_NO_FILTERS,
1678 /* WICPngFilterNone */ PNG_FILTER_NONE,
1679 /* WICPngFilterSub */ PNG_FILTER_SUB,
1680 /* WICPngFilterUp */ PNG_FILTER_UP,
1681 /* WICPngFilterAverage */ PNG_FILTER_AVG,
1682 /* WICPngFilterPaeth */ PNG_FILTER_PAETH,
1683 /* WICPngFilterAdaptive */ PNG_ALL_FILTERS,
1684 };
1685
1686 ppng_set_filter(This->png_ptr, 0, png_filter_map[This->filter]);
1687 }
1688
1689 This->info_written = TRUE;
1690 }
1691
1692 if (This->interlace)
1693 {
1694 /* Just store the data so we can write it in multiple passes in Commit. */
1695 for (i=0; i<lineCount; i++)
1696 memcpy(This->data + This->stride * (This->lines_written + i),
1697 pbPixels + cbStride * i,
1698 This->stride);
1699
1700 This->lines_written += lineCount;
1701
1702 LeaveCriticalSection(&This->lock);
1703 return S_OK;
1704 }
1705
1706 row_pointers = HeapAlloc(GetProcessHeap(), 0, lineCount * sizeof(png_byte*));
1707 if (!row_pointers)
1708 {
1709 LeaveCriticalSection(&This->lock);
1710 return E_OUTOFMEMORY;
1711 }
1712
1713 for (i=0; i<lineCount; i++)
1714 row_pointers[i] = pbPixels + cbStride * i;
1715
1716 ppng_write_rows(This->png_ptr, row_pointers, lineCount);
1717 This->lines_written += lineCount;
1718
1719 LeaveCriticalSection(&This->lock);
1720
1721 HeapFree(GetProcessHeap(), 0, row_pointers);
1722
1723 return S_OK;
1724 }
1725
1726 static HRESULT WINAPI PngFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
1727 IWICBitmapSource *pIBitmapSource, WICRect *prc)
1728 {
1729 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1730 HRESULT hr;
1731 TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc);
1732
1733 if (!This->frame_initialized)
1734 return WINCODEC_ERR_WRONGSTATE;
1735
1736 hr = configure_write_source(iface, pIBitmapSource, prc,
1737 This->format ? This->format->guid : NULL, This->width, This->height,
1738 This->xres, This->yres);
1739
1740 if (SUCCEEDED(hr))
1741 {
1742 hr = write_source(iface, pIBitmapSource, prc,
1743 This->format->guid, This->format->bpp, This->width, This->height);
1744 }
1745
1746 return hr;
1747 }
1748
1749 static HRESULT WINAPI PngFrameEncode_Commit(IWICBitmapFrameEncode *iface)
1750 {
1751 PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1752 png_byte **row_pointers=NULL;
1753 jmp_buf jmpbuf;
1754 TRACE("(%p)\n", iface);
1755
1756 EnterCriticalSection(&This->lock);
1757
1758 if (!This->info_written || This->lines_written != This->height || This->frame_committed)
1759 {
1760 LeaveCriticalSection(&This->lock);
1761 return WINCODEC_ERR_WRONGSTATE;
1762 }
1763
1764 /* set up setjmp/longjmp error handling */
1765 if (setjmp(jmpbuf))
1766 {
1767 LeaveCriticalSection(&This->lock);
1768 HeapFree(GetProcessHeap(), 0, row_pointers);
1769 return E_FAIL;
1770 }
1771 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1772
1773 if (This->interlace)
1774 {
1775 int i;
1776
1777 row_pointers = HeapAlloc(GetProcessHeap(), 0, This->height * sizeof(png_byte*));
1778 if (!row_pointers)
1779 {
1780 LeaveCriticalSection(&This->lock);
1781 return E_OUTOFMEMORY;
1782 }
1783
1784 for (i=0; i<This->height; i++)
1785 row_pointers[i] = This->data + This->stride * i;
1786
1787 for (i=0; i<This->passes; i++)
1788 ppng_write_rows(This->png_ptr, row_pointers, This->height);
1789 }
1790
1791 ppng_write_end(This->png_ptr, This->info_ptr);
1792
1793 This->frame_committed = TRUE;
1794
1795 HeapFree(GetProcessHeap(), 0, row_pointers);
1796
1797 LeaveCriticalSection(&This->lock);
1798
1799 return S_OK;
1800 }
1801
1802 static HRESULT WINAPI PngFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
1803 IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1804 {
1805 FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
1806 return E_NOTIMPL;
1807 }
1808
1809 static const IWICBitmapFrameEncodeVtbl PngEncoder_FrameVtbl = {
1810 PngFrameEncode_QueryInterface,
1811 PngFrameEncode_AddRef,
1812 PngFrameEncode_Release,
1813 PngFrameEncode_Initialize,
1814 PngFrameEncode_SetSize,
1815 PngFrameEncode_SetResolution,
1816 PngFrameEncode_SetPixelFormat,
1817 PngFrameEncode_SetColorContexts,
1818 PngFrameEncode_SetPalette,
1819 PngFrameEncode_SetThumbnail,
1820 PngFrameEncode_WritePixels,
1821 PngFrameEncode_WriteSource,
1822 PngFrameEncode_Commit,
1823 PngFrameEncode_GetMetadataQueryWriter
1824 };
1825
1826 static HRESULT WINAPI PngEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
1827 void **ppv)
1828 {
1829 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1830 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1831
1832 if (!ppv) return E_INVALIDARG;
1833
1834 if (IsEqualIID(&IID_IUnknown, iid) ||
1835 IsEqualIID(&IID_IWICBitmapEncoder, iid))
1836 {
1837 *ppv = &This->IWICBitmapEncoder_iface;
1838 }
1839 else
1840 {
1841 *ppv = NULL;
1842 return E_NOINTERFACE;
1843 }
1844
1845 IUnknown_AddRef((IUnknown*)*ppv);
1846 return S_OK;
1847 }
1848
1849 static ULONG WINAPI PngEncoder_AddRef(IWICBitmapEncoder *iface)
1850 {
1851 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1852 ULONG ref = InterlockedIncrement(&This->ref);
1853
1854 TRACE("(%p) refcount=%u\n", iface, ref);
1855
1856 return ref;
1857 }
1858
1859 static ULONG WINAPI PngEncoder_Release(IWICBitmapEncoder *iface)
1860 {
1861 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1862 ULONG ref = InterlockedDecrement(&This->ref);
1863
1864 TRACE("(%p) refcount=%u\n", iface, ref);
1865
1866 if (ref == 0)
1867 {
1868 This->lock.DebugInfo->Spare[0] = 0;
1869 DeleteCriticalSection(&This->lock);
1870 if (This->png_ptr)
1871 ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1872 if (This->stream)
1873 IStream_Release(This->stream);
1874 HeapFree(GetProcessHeap(), 0, This->data);
1875 HeapFree(GetProcessHeap(), 0, This);
1876 }
1877
1878 return ref;
1879 }
1880
1881 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1882 {
1883 PngEncoder *This = ppng_get_io_ptr(png_ptr);
1884 HRESULT hr;
1885 ULONG byteswritten;
1886
1887 hr = IStream_Write(This->stream, data, length, &byteswritten);
1888 if (FAILED(hr) || byteswritten != length)
1889 {
1890 ppng_error(png_ptr, "failed writing data");
1891 }
1892 }
1893
1894 static void user_flush(png_structp png_ptr)
1895 {
1896 }
1897
1898 static HRESULT WINAPI PngEncoder_Initialize(IWICBitmapEncoder *iface,
1899 IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
1900 {
1901 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1902 jmp_buf jmpbuf;
1903
1904 TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
1905
1906 EnterCriticalSection(&This->lock);
1907
1908 if (This->png_ptr)
1909 {
1910 LeaveCriticalSection(&This->lock);
1911 return WINCODEC_ERR_WRONGSTATE;
1912 }
1913
1914 /* initialize libpng */
1915 This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1916 if (!This->png_ptr)
1917 {
1918 LeaveCriticalSection(&This->lock);
1919 return E_FAIL;
1920 }
1921
1922 This->info_ptr = ppng_create_info_struct(This->png_ptr);
1923 if (!This->info_ptr)
1924 {
1925 ppng_destroy_write_struct(&This->png_ptr, NULL);
1926 This->png_ptr = NULL;
1927 LeaveCriticalSection(&This->lock);
1928 return E_FAIL;
1929 }
1930
1931 IStream_AddRef(pIStream);
1932 This->stream = pIStream;
1933
1934 /* set up setjmp/longjmp error handling */
1935 if (setjmp(jmpbuf))
1936 {
1937 ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
1938 This->png_ptr = NULL;
1939 IStream_Release(This->stream);
1940 This->stream = NULL;
1941 LeaveCriticalSection(&This->lock);
1942 return E_FAIL;
1943 }
1944 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
1945
1946 /* set up custom i/o handling */
1947 ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush);
1948
1949 LeaveCriticalSection(&This->lock);
1950
1951 return S_OK;
1952 }
1953
1954 static HRESULT WINAPI PngEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
1955 GUID *pguidContainerFormat)
1956 {
1957 FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
1958 return E_NOTIMPL;
1959 }
1960
1961 static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info)
1962 {
1963 IWICComponentInfo *comp_info;
1964 HRESULT hr;
1965
1966 TRACE("%p,%p\n", iface, info);
1967
1968 if (!info) return E_INVALIDARG;
1969
1970 hr = CreateComponentInfo(&CLSID_WICPngEncoder, &comp_info);
1971 if (hr == S_OK)
1972 {
1973 hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info);
1974 IWICComponentInfo_Release(comp_info);
1975 }
1976 return hr;
1977 }
1978
1979 static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface,
1980 UINT cCount, IWICColorContext **ppIColorContext)
1981 {
1982 FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1983 return E_NOTIMPL;
1984 }
1985
1986 static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette)
1987 {
1988 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
1989 HRESULT hr;
1990
1991 TRACE("(%p,%p)\n", iface, palette);
1992
1993 EnterCriticalSection(&This->lock);
1994
1995 hr = This->stream ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED;
1996
1997 LeaveCriticalSection(&This->lock);
1998
1999 return hr;
2000 }
2001
2002 static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
2003 {
2004 TRACE("(%p,%p)\n", iface, pIThumbnail);
2005 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2006 }
2007
2008 static HRESULT WINAPI PngEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
2009 {
2010 TRACE("(%p,%p)\n", iface, pIPreview);
2011 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2012 }
2013
2014 static HRESULT WINAPI PngEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
2015 IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
2016 {
2017 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2018 HRESULT hr;
2019 PROPBAG2 opts[2]= {{0}};
2020
2021 TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
2022
2023 EnterCriticalSection(&This->lock);
2024
2025 if (This->frame_count != 0)
2026 {
2027 LeaveCriticalSection(&This->lock);
2028 return WINCODEC_ERR_UNSUPPORTEDOPERATION;
2029 }
2030
2031 if (!This->stream)
2032 {
2033 LeaveCriticalSection(&This->lock);
2034 return WINCODEC_ERR_NOTINITIALIZED;
2035 }
2036
2037 opts[0].pstrName = (LPOLESTR)wszPngInterlaceOption;
2038 opts[0].vt = VT_BOOL;
2039 opts[0].dwType = PROPBAG2_TYPE_DATA;
2040 opts[1].pstrName = (LPOLESTR)wszPngFilterOption;
2041 opts[1].vt = VT_UI1;
2042 opts[1].dwType = PROPBAG2_TYPE_DATA;
2043
2044 hr = CreatePropertyBag2(opts, sizeof(opts)/sizeof(opts[0]), ppIEncoderOptions);
2045 if (FAILED(hr))
2046 {
2047 LeaveCriticalSection(&This->lock);
2048 return hr;
2049 }
2050
2051 This->frame_count = 1;
2052
2053 LeaveCriticalSection(&This->lock);
2054
2055 IWICBitmapEncoder_AddRef(iface);
2056 *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface;
2057
2058 return S_OK;
2059 }
2060
2061 static HRESULT WINAPI PngEncoder_Commit(IWICBitmapEncoder *iface)
2062 {
2063 PngEncoder *This = impl_from_IWICBitmapEncoder(iface);
2064 TRACE("(%p)\n", iface);
2065
2066 EnterCriticalSection(&This->lock);
2067
2068 if (!This->frame_committed || This->committed)
2069 {
2070 LeaveCriticalSection(&This->lock);
2071 return WINCODEC_ERR_WRONGSTATE;
2072 }
2073
2074 This->committed = TRUE;
2075
2076 LeaveCriticalSection(&This->lock);
2077
2078 return S_OK;
2079 }
2080
2081 static HRESULT WINAPI PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
2082 IWICMetadataQueryWriter **ppIMetadataQueryWriter)
2083 {
2084 FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
2085 return E_NOTIMPL;
2086 }
2087
2088 static const IWICBitmapEncoderVtbl PngEncoder_Vtbl = {
2089 PngEncoder_QueryInterface,
2090 PngEncoder_AddRef,
2091 PngEncoder_Release,
2092 PngEncoder_Initialize,
2093 PngEncoder_GetContainerFormat,
2094 PngEncoder_GetEncoderInfo,
2095 PngEncoder_SetColorContexts,
2096 PngEncoder_SetPalette,
2097 PngEncoder_SetThumbnail,
2098 PngEncoder_SetPreview,
2099 PngEncoder_CreateNewFrame,
2100 PngEncoder_Commit,
2101 PngEncoder_GetMetadataQueryWriter
2102 };
2103
2104 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
2105 {
2106 PngEncoder *This;
2107 HRESULT ret;
2108
2109 TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
2110
2111 *ppv = NULL;
2112
2113 if (!load_libpng())
2114 {
2115 ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG);
2116 return E_FAIL;
2117 }
2118
2119 This = HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder));
2120 if (!This) return E_OUTOFMEMORY;
2121
2122 This->IWICBitmapEncoder_iface.lpVtbl = &PngEncoder_Vtbl;
2123 This->IWICBitmapFrameEncode_iface.lpVtbl = &PngEncoder_FrameVtbl;
2124 This->ref = 1;
2125 This->png_ptr = NULL;
2126 This->info_ptr = NULL;
2127 This->stream = NULL;
2128 This->frame_count = 0;
2129 This->frame_initialized = FALSE;
2130 This->format = NULL;
2131 This->info_written = FALSE;
2132 This->width = 0;
2133 This->height = 0;
2134 This->xres = 0.0;
2135 This->yres = 0.0;
2136 This->lines_written = 0;
2137 This->frame_committed = FALSE;
2138 This->committed = FALSE;
2139 This->data = NULL;
2140 This->colors = 0;
2141 InitializeCriticalSection(&This->lock);
2142 This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock");
2143
2144 ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
2145 IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
2146
2147 return ret;
2148 }
2149
2150 #else /* !HAVE_PNG_H */
2151
2152 HRESULT PngDecoder_CreateInstance(REFIID iid, void** ppv)
2153 {
2154 ERR("Trying to load PNG picture, but PNG support is not compiled in.\n");
2155 return E_FAIL;
2156 }
2157
2158 HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv)
2159 {
2160 ERR("Trying to save PNG picture, but PNG support is not compiled in.\n");
2161 return E_FAIL;
2162 }
2163
2164 #endif