mfplat: Read queue subscriber within the critical section.
[wine/zf.git] / dlls / windowscodecs / libpng.c
blob0571f5a2aade30404fa8447fa3bb05c7ec05bcbb
1 /*
2 * Copyright 2016 Dmitry Timoshkov
3 * Copyright 2020 Esme Povirk
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.
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.
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
20 #if 0
21 #pragma makedep unix
22 #endif
24 #include "config.h"
25 #include "wine/port.h"
27 #include <stdarg.h>
28 #ifdef SONAME_LIBPNG
29 #include <png.h>
30 #endif
32 #define NONAMELESSUNION
34 #include "ntstatus.h"
35 #define WIN32_NO_STATUS
36 #include "windef.h"
37 #include "winternl.h"
38 #include "winbase.h"
39 #include "objbase.h"
41 #include "wincodecs_private.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
47 #ifdef SONAME_LIBPNG
49 static void *libpng_handle;
50 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
51 MAKE_FUNCPTR(png_create_info_struct);
52 MAKE_FUNCPTR(png_create_read_struct);
53 MAKE_FUNCPTR(png_create_write_struct);
54 MAKE_FUNCPTR(png_destroy_read_struct);
55 MAKE_FUNCPTR(png_destroy_write_struct);
56 MAKE_FUNCPTR(png_error);
57 MAKE_FUNCPTR(png_get_bit_depth);
58 MAKE_FUNCPTR(png_get_color_type);
59 MAKE_FUNCPTR(png_get_error_ptr);
60 MAKE_FUNCPTR(png_get_iCCP);
61 MAKE_FUNCPTR(png_get_image_height);
62 MAKE_FUNCPTR(png_get_image_width);
63 MAKE_FUNCPTR(png_get_io_ptr);
64 MAKE_FUNCPTR(png_get_pHYs);
65 MAKE_FUNCPTR(png_get_PLTE);
66 MAKE_FUNCPTR(png_get_tRNS);
67 MAKE_FUNCPTR(png_read_image);
68 MAKE_FUNCPTR(png_read_info);
69 MAKE_FUNCPTR(png_set_bgr);
70 MAKE_FUNCPTR(png_set_crc_action);
71 MAKE_FUNCPTR(png_set_error_fn);
72 MAKE_FUNCPTR(png_set_filler);
73 MAKE_FUNCPTR(png_set_filter);
74 MAKE_FUNCPTR(png_set_gray_to_rgb);
75 MAKE_FUNCPTR(png_set_IHDR);
76 MAKE_FUNCPTR(png_set_interlace_handling);
77 MAKE_FUNCPTR(png_set_pHYs);
78 MAKE_FUNCPTR(png_set_PLTE);
79 MAKE_FUNCPTR(png_set_read_fn);
80 MAKE_FUNCPTR(png_set_swap);
81 MAKE_FUNCPTR(png_set_tRNS);
82 MAKE_FUNCPTR(png_set_tRNS_to_alpha);
83 MAKE_FUNCPTR(png_set_write_fn);
84 MAKE_FUNCPTR(png_write_end);
85 MAKE_FUNCPTR(png_write_info);
86 MAKE_FUNCPTR(png_write_rows);
87 #undef MAKE_FUNCPTR
89 static CRITICAL_SECTION init_png_cs;
90 static CRITICAL_SECTION_DEBUG init_png_cs_debug =
92 0, 0, &init_png_cs,
93 { &init_png_cs_debug.ProcessLocksList,
94 &init_png_cs_debug.ProcessLocksList },
95 0, 0, { (DWORD_PTR)(__FILE__ ": init_png_cs") }
97 static CRITICAL_SECTION init_png_cs = { &init_png_cs_debug, -1, 0, 0, 0, 0 };
99 static void *load_libpng(void)
101 void *result;
103 RtlEnterCriticalSection(&init_png_cs);
105 if(!libpng_handle && (libpng_handle = dlopen(SONAME_LIBPNG, RTLD_NOW)) != NULL) {
107 #define LOAD_FUNCPTR(f) \
108 if((p##f = dlsym(libpng_handle, #f)) == NULL) { \
109 libpng_handle = NULL; \
110 RtlLeaveCriticalSection(&init_png_cs); \
111 return NULL; \
113 LOAD_FUNCPTR(png_create_info_struct);
114 LOAD_FUNCPTR(png_create_read_struct);
115 LOAD_FUNCPTR(png_create_write_struct);
116 LOAD_FUNCPTR(png_destroy_read_struct);
117 LOAD_FUNCPTR(png_destroy_write_struct);
118 LOAD_FUNCPTR(png_error);
119 LOAD_FUNCPTR(png_get_bit_depth);
120 LOAD_FUNCPTR(png_get_color_type);
121 LOAD_FUNCPTR(png_get_error_ptr);
122 LOAD_FUNCPTR(png_get_image_height);
123 LOAD_FUNCPTR(png_get_image_width);
124 LOAD_FUNCPTR(png_get_iCCP);
125 LOAD_FUNCPTR(png_get_io_ptr);
126 LOAD_FUNCPTR(png_get_pHYs);
127 LOAD_FUNCPTR(png_get_PLTE);
128 LOAD_FUNCPTR(png_get_tRNS);
129 LOAD_FUNCPTR(png_read_image);
130 LOAD_FUNCPTR(png_read_info);
131 LOAD_FUNCPTR(png_set_bgr);
132 LOAD_FUNCPTR(png_set_crc_action);
133 LOAD_FUNCPTR(png_set_error_fn);
134 LOAD_FUNCPTR(png_set_filler);
135 LOAD_FUNCPTR(png_set_filter);
136 LOAD_FUNCPTR(png_set_gray_to_rgb);
137 LOAD_FUNCPTR(png_set_IHDR);
138 LOAD_FUNCPTR(png_set_interlace_handling);
139 LOAD_FUNCPTR(png_set_pHYs);
140 LOAD_FUNCPTR(png_set_PLTE);
141 LOAD_FUNCPTR(png_set_read_fn);
142 LOAD_FUNCPTR(png_set_swap);
143 LOAD_FUNCPTR(png_set_tRNS);
144 LOAD_FUNCPTR(png_set_tRNS_to_alpha);
145 LOAD_FUNCPTR(png_set_write_fn);
146 LOAD_FUNCPTR(png_write_end);
147 LOAD_FUNCPTR(png_write_info);
148 LOAD_FUNCPTR(png_write_rows);
150 #undef LOAD_FUNCPTR
153 result = libpng_handle;
155 RtlLeaveCriticalSection(&init_png_cs);
157 return result;
160 struct png_decoder
162 struct decoder decoder;
163 IStream *stream;
164 struct decoder_frame decoder_frame;
165 UINT stride;
166 BYTE *image_bits;
167 BYTE *color_profile;
168 DWORD color_profile_len;
171 static inline struct png_decoder *impl_from_decoder(struct decoder* iface)
173 return CONTAINING_RECORD(iface, struct png_decoder, decoder);
176 static void user_error_fn(png_structp png_ptr, png_const_charp error_message)
178 jmp_buf *pjmpbuf;
180 /* This uses setjmp/longjmp just like the default. We can't use the
181 * default because there's no way to access the jmp buffer in the png_struct
182 * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
183 WARN("PNG error: %s\n", debugstr_a(error_message));
184 pjmpbuf = ppng_get_error_ptr(png_ptr);
185 longjmp(*pjmpbuf, 1);
188 static void user_warning_fn(png_structp png_ptr, png_const_charp warning_message)
190 WARN("PNG warning: %s\n", debugstr_a(warning_message));
193 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
195 IStream *stream = ppng_get_io_ptr(png_ptr);
196 HRESULT hr;
197 ULONG bytesread;
199 hr = stream_read(stream, data, length, &bytesread);
200 if (FAILED(hr) || bytesread != length)
202 ppng_error(png_ptr, "failed reading data");
206 HRESULT CDECL png_decoder_initialize(struct decoder *iface, IStream *stream, struct decoder_stat *st)
208 struct png_decoder *This = impl_from_decoder(iface);
209 png_structp png_ptr;
210 png_infop info_ptr;
211 jmp_buf jmpbuf;
212 HRESULT hr = E_FAIL;
213 int color_type, bit_depth;
214 png_bytep trans;
215 int num_trans;
216 png_uint_32 transparency;
217 png_color_16p trans_values;
218 png_uint_32 ret, xres, yres;
219 int unit_type;
220 png_colorp png_palette;
221 int num_palette;
222 int i;
223 UINT image_size;
224 png_bytep *row_pointers=NULL;
225 png_charp cp_name;
226 png_bytep cp_profile;
227 png_uint_32 cp_len;
228 int cp_compression;
230 png_ptr = ppng_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
231 if (!png_ptr)
233 return E_FAIL;
236 info_ptr = ppng_create_info_struct(png_ptr);
237 if (!info_ptr)
239 ppng_destroy_read_struct(&png_ptr, NULL, NULL);
240 return E_FAIL;
243 /* set up setjmp/longjmp error handling */
244 if (setjmp(jmpbuf))
246 hr = WINCODEC_ERR_UNKNOWNIMAGEFORMAT;
247 goto end;
249 ppng_set_error_fn(png_ptr, jmpbuf, user_error_fn, user_warning_fn);
250 ppng_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
252 /* seek to the start of the stream */
253 hr = stream_seek(stream, 0, STREAM_SEEK_SET, NULL);
254 if (FAILED(hr))
256 goto end;
259 /* set up custom i/o handling */
260 ppng_set_read_fn(png_ptr, stream, user_read_data);
262 /* read the header */
263 ppng_read_info(png_ptr, info_ptr);
265 /* choose a pixel format */
266 color_type = ppng_get_color_type(png_ptr, info_ptr);
267 bit_depth = ppng_get_bit_depth(png_ptr, info_ptr);
269 /* PNGs with bit-depth greater than 8 are network byte order. Windows does not expect this. */
270 if (bit_depth > 8)
271 ppng_set_swap(png_ptr);
273 /* check for color-keyed alpha */
274 transparency = ppng_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
275 if (!transparency)
276 num_trans = 0;
278 if (transparency && (color_type == PNG_COLOR_TYPE_RGB ||
279 (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16)))
281 /* expand to RGBA */
282 if (color_type == PNG_COLOR_TYPE_GRAY)
283 ppng_set_gray_to_rgb(png_ptr);
284 ppng_set_tRNS_to_alpha(png_ptr);
285 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
288 switch (color_type)
290 case PNG_COLOR_TYPE_GRAY_ALPHA:
291 /* WIC does not support grayscale alpha formats so use RGBA */
292 ppng_set_gray_to_rgb(png_ptr);
293 /* fall through */
294 case PNG_COLOR_TYPE_RGB_ALPHA:
295 This->decoder_frame.bpp = bit_depth * 4;
296 switch (bit_depth)
298 case 8:
299 ppng_set_bgr(png_ptr);
300 This->decoder_frame.pixel_format = GUID_WICPixelFormat32bppBGRA;
301 break;
302 case 16: This->decoder_frame.pixel_format = GUID_WICPixelFormat64bppRGBA; break;
303 default:
304 ERR("invalid RGBA bit depth: %i\n", bit_depth);
305 hr = E_FAIL;
306 goto end;
308 break;
309 case PNG_COLOR_TYPE_GRAY:
310 This->decoder_frame.bpp = bit_depth;
311 if (!transparency)
313 switch (bit_depth)
315 case 1: This->decoder_frame.pixel_format = GUID_WICPixelFormatBlackWhite; break;
316 case 2: This->decoder_frame.pixel_format = GUID_WICPixelFormat2bppGray; break;
317 case 4: This->decoder_frame.pixel_format = GUID_WICPixelFormat4bppGray; break;
318 case 8: This->decoder_frame.pixel_format = GUID_WICPixelFormat8bppGray; break;
319 case 16: This->decoder_frame.pixel_format = GUID_WICPixelFormat16bppGray; break;
320 default:
321 ERR("invalid grayscale bit depth: %i\n", bit_depth);
322 hr = E_FAIL;
323 goto end;
325 break;
327 /* else fall through */
328 case PNG_COLOR_TYPE_PALETTE:
329 This->decoder_frame.bpp = bit_depth;
330 switch (bit_depth)
332 case 1: This->decoder_frame.pixel_format = GUID_WICPixelFormat1bppIndexed; break;
333 case 2: This->decoder_frame.pixel_format = GUID_WICPixelFormat2bppIndexed; break;
334 case 4: This->decoder_frame.pixel_format = GUID_WICPixelFormat4bppIndexed; break;
335 case 8: This->decoder_frame.pixel_format = GUID_WICPixelFormat8bppIndexed; break;
336 default:
337 ERR("invalid indexed color bit depth: %i\n", bit_depth);
338 hr = E_FAIL;
339 goto end;
341 break;
342 case PNG_COLOR_TYPE_RGB:
343 This->decoder_frame.bpp = bit_depth * 3;
344 switch (bit_depth)
346 case 8:
347 ppng_set_bgr(png_ptr);
348 This->decoder_frame.pixel_format = GUID_WICPixelFormat24bppBGR;
349 break;
350 case 16: This->decoder_frame.pixel_format = GUID_WICPixelFormat48bppRGB; break;
351 default:
352 ERR("invalid RGB color bit depth: %i\n", bit_depth);
353 hr = E_FAIL;
354 goto end;
356 break;
357 default:
358 ERR("invalid color type %i\n", color_type);
359 hr = E_FAIL;
360 goto end;
363 This->decoder_frame.width = ppng_get_image_width(png_ptr, info_ptr);
364 This->decoder_frame.height = ppng_get_image_height(png_ptr, info_ptr);
366 ret = ppng_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type);
368 if (ret && unit_type == PNG_RESOLUTION_METER)
370 This->decoder_frame.dpix = xres * 0.0254;
371 This->decoder_frame.dpiy = yres * 0.0254;
373 else
375 WARN("no pHYs block present\n");
376 This->decoder_frame.dpix = This->decoder_frame.dpiy = 96.0;
379 ret = ppng_get_iCCP(png_ptr, info_ptr, &cp_name, &cp_compression, &cp_profile, &cp_len);
380 if (ret)
382 This->decoder_frame.num_color_contexts = 1;
383 This->color_profile_len = cp_len;
384 This->color_profile = malloc(cp_len);
385 if (!This->color_profile)
387 hr = E_OUTOFMEMORY;
388 goto end;
390 memcpy(This->color_profile, cp_profile, cp_len);
392 else
393 This->decoder_frame.num_color_contexts = 0;
395 if (color_type == PNG_COLOR_TYPE_PALETTE)
397 ret = ppng_get_PLTE(png_ptr, info_ptr, &png_palette, &num_palette);
398 if (!ret)
400 ERR("paletted image with no PLTE chunk\n");
401 hr = E_FAIL;
402 goto end;
405 if (num_palette > 256)
407 ERR("palette has %i colors?!\n", num_palette);
408 hr = E_FAIL;
409 goto end;
412 This->decoder_frame.num_colors = num_palette;
413 for (i=0; i<num_palette; i++)
415 BYTE alpha = (i < num_trans) ? trans[i] : 0xff;
416 This->decoder_frame.palette[i] = (alpha << 24 |
417 png_palette[i].red << 16|
418 png_palette[i].green << 8|
419 png_palette[i].blue);
422 else if (color_type == PNG_COLOR_TYPE_GRAY && transparency && bit_depth <= 8) {
423 num_palette = 1 << bit_depth;
425 This->decoder_frame.num_colors = num_palette;
426 for (i=0; i<num_palette; i++)
428 BYTE alpha = (i == trans_values[0].gray) ? 0 : 0xff;
429 BYTE val = i * 255 / (num_palette - 1);
430 This->decoder_frame.palette[i] = (alpha << 24 | val << 16 | val << 8 | val);
433 else
435 This->decoder_frame.num_colors = 0;
438 This->stride = (This->decoder_frame.width * This->decoder_frame.bpp + 7) / 8;
439 image_size = This->stride * This->decoder_frame.height;
441 This->image_bits = malloc(image_size);
442 if (!This->image_bits)
444 hr = E_OUTOFMEMORY;
445 goto end;
448 row_pointers = malloc(sizeof(png_bytep)*This->decoder_frame.height);
449 if (!row_pointers)
451 hr = E_OUTOFMEMORY;
452 goto end;
455 for (i=0; i<This->decoder_frame.height; i++)
456 row_pointers[i] = This->image_bits + i * This->stride;
458 ppng_read_image(png_ptr, row_pointers);
460 free(row_pointers);
461 row_pointers = NULL;
463 /* png_read_end intentionally not called to not seek to the end of the file */
465 st->flags = WICBitmapDecoderCapabilityCanDecodeAllImages |
466 WICBitmapDecoderCapabilityCanDecodeSomeImages |
467 WICBitmapDecoderCapabilityCanEnumerateMetadata;
468 st->frame_count = 1;
470 This->stream = stream;
472 hr = S_OK;
474 end:
475 ppng_destroy_read_struct(&png_ptr, &info_ptr, NULL);
476 free(row_pointers);
477 if (FAILED(hr))
479 free(This->image_bits);
480 This->image_bits = NULL;
481 free(This->color_profile);
482 This->color_profile = NULL;
484 return hr;
487 HRESULT CDECL png_decoder_get_frame_info(struct decoder *iface, UINT frame, struct decoder_frame *info)
489 struct png_decoder *This = impl_from_decoder(iface);
490 *info = This->decoder_frame;
491 return S_OK;
494 HRESULT CDECL png_decoder_copy_pixels(struct decoder *iface, UINT frame,
495 const WICRect *prc, UINT stride, UINT buffersize, BYTE *buffer)
497 struct png_decoder *This = impl_from_decoder(iface);
499 return copy_pixels(This->decoder_frame.bpp, This->image_bits,
500 This->decoder_frame.width, This->decoder_frame.height, This->stride,
501 prc, stride, buffersize, buffer);
504 HRESULT CDECL png_decoder_get_metadata_blocks(struct decoder* iface,
505 UINT frame, UINT *count, struct decoder_block **blocks)
507 struct png_decoder *This = impl_from_decoder(iface);
508 HRESULT hr;
509 struct decoder_block *result = NULL;
510 ULONGLONG seek;
511 BYTE chunk_type[4];
512 ULONG chunk_size;
513 ULONGLONG chunk_start;
514 ULONG metadata_blocks_size = 0;
516 seek = 8;
517 *count = 0;
521 hr = stream_seek(This->stream, seek, STREAM_SEEK_SET, &chunk_start);
522 if (FAILED(hr)) goto end;
524 hr = read_png_chunk(This->stream, chunk_type, NULL, &chunk_size);
525 if (FAILED(hr)) goto end;
527 if (chunk_type[0] >= 'a' && chunk_type[0] <= 'z' &&
528 memcmp(chunk_type, "tRNS", 4) && memcmp(chunk_type, "pHYs", 4))
530 /* This chunk is considered metadata. */
531 if (*count == metadata_blocks_size)
533 struct decoder_block *new_metadata_blocks;
534 ULONG new_metadata_blocks_size;
536 new_metadata_blocks_size = 4 + metadata_blocks_size * 2;
537 new_metadata_blocks = RtlAllocateHeap(GetProcessHeap(), 0,
538 new_metadata_blocks_size * sizeof(*new_metadata_blocks));
540 if (!new_metadata_blocks)
542 hr = E_OUTOFMEMORY;
543 goto end;
546 memcpy(new_metadata_blocks, result,
547 *count * sizeof(*new_metadata_blocks));
549 RtlFreeHeap(GetProcessHeap(), 0, result);
550 result = new_metadata_blocks;
551 metadata_blocks_size = new_metadata_blocks_size;
554 result[*count].offset = chunk_start;
555 result[*count].length = chunk_size + 12;
556 result[*count].options = WICMetadataCreationAllowUnknown;
557 (*count)++;
560 seek = chunk_start + chunk_size + 12; /* skip data and CRC */
561 } while (memcmp(chunk_type, "IEND", 4));
563 end:
564 if (SUCCEEDED(hr))
566 *blocks = result;
568 else
570 *count = 0;
571 *blocks = NULL;
572 RtlFreeHeap(GetProcessHeap(), 0, result);
574 return hr;
577 HRESULT CDECL png_decoder_get_color_context(struct decoder* iface, UINT frame, UINT num,
578 BYTE **data, DWORD *datasize)
580 struct png_decoder *This = impl_from_decoder(iface);
582 *data = RtlAllocateHeap(GetProcessHeap(), 0, This->color_profile_len);
583 *datasize = This->color_profile_len;
585 if (!*data)
586 return E_OUTOFMEMORY;
588 memcpy(*data, This->color_profile, This->color_profile_len);
590 return S_OK;
593 void CDECL png_decoder_destroy(struct decoder* iface)
595 struct png_decoder *This = impl_from_decoder(iface);
597 free(This->image_bits);
598 free(This->color_profile);
599 RtlFreeHeap(GetProcessHeap(), 0, This);
602 static const struct decoder_funcs png_decoder_vtable = {
603 png_decoder_initialize,
604 png_decoder_get_frame_info,
605 png_decoder_copy_pixels,
606 png_decoder_get_metadata_blocks,
607 png_decoder_get_color_context,
608 png_decoder_destroy
611 HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **result)
613 struct png_decoder *This;
615 if (!load_libpng())
617 ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
618 return E_FAIL;
621 This = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*This));
623 if (!This)
625 return E_OUTOFMEMORY;
628 This->decoder.vtable = &png_decoder_vtable;
629 This->image_bits = NULL;
630 This->color_profile = NULL;
631 *result = &This->decoder;
633 info->container_format = GUID_ContainerFormatPng;
634 info->block_format = GUID_ContainerFormatPng;
635 info->clsid = CLSID_WICPngDecoder;
637 return S_OK;
640 struct png_pixelformat {
641 const WICPixelFormatGUID *guid;
642 UINT bpp;
643 int bit_depth;
644 int color_type;
645 BOOL remove_filler;
646 BOOL swap_rgb;
649 static const struct png_pixelformat formats[] = {
650 {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1},
651 {&GUID_WICPixelFormat24bppBGR, 24, 8, PNG_COLOR_TYPE_RGB, 0, 1},
652 {&GUID_WICPixelFormatBlackWhite, 1, 1, PNG_COLOR_TYPE_GRAY, 0, 0},
653 {&GUID_WICPixelFormat2bppGray, 2, 2, PNG_COLOR_TYPE_GRAY, 0, 0},
654 {&GUID_WICPixelFormat4bppGray, 4, 4, PNG_COLOR_TYPE_GRAY, 0, 0},
655 {&GUID_WICPixelFormat8bppGray, 8, 8, PNG_COLOR_TYPE_GRAY, 0, 0},
656 {&GUID_WICPixelFormat16bppGray, 16, 16, PNG_COLOR_TYPE_GRAY, 0, 0},
657 {&GUID_WICPixelFormat32bppBGR, 32, 8, PNG_COLOR_TYPE_RGB, 1, 1},
658 {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0},
659 {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0},
660 {&GUID_WICPixelFormat1bppIndexed, 1, 1, PNG_COLOR_TYPE_PALETTE, 0, 0},
661 {&GUID_WICPixelFormat2bppIndexed, 2, 2, PNG_COLOR_TYPE_PALETTE, 0, 0},
662 {&GUID_WICPixelFormat4bppIndexed, 4, 4, PNG_COLOR_TYPE_PALETTE, 0, 0},
663 {&GUID_WICPixelFormat8bppIndexed, 8, 8, PNG_COLOR_TYPE_PALETTE, 0, 0},
664 {NULL},
667 struct png_encoder
669 struct encoder encoder;
670 IStream *stream;
671 png_structp png_ptr;
672 png_infop info_ptr;
673 struct encoder_frame encoder_frame;
674 const struct png_pixelformat *format;
675 BYTE *data;
676 UINT stride;
677 UINT passes;
678 UINT lines_written;
681 static inline struct png_encoder *impl_from_encoder(struct encoder* iface)
683 return CONTAINING_RECORD(iface, struct png_encoder, encoder);
686 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
688 struct png_encoder *This = ppng_get_io_ptr(png_ptr);
689 HRESULT hr;
690 ULONG byteswritten;
692 hr = stream_write(This->stream, data, length, &byteswritten);
693 if (FAILED(hr) || byteswritten != length)
695 ppng_error(png_ptr, "failed writing data");
699 static void user_flush(png_structp png_ptr)
703 static HRESULT CDECL png_encoder_initialize(struct encoder *encoder, IStream *stream)
705 struct png_encoder *This = impl_from_encoder(encoder);
706 jmp_buf jmpbuf;
708 TRACE("(%p,%p)\n", encoder, stream);
710 /* initialize libpng */
711 This->png_ptr = ppng_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
712 if (!This->png_ptr)
713 return E_FAIL;
715 This->info_ptr = ppng_create_info_struct(This->png_ptr);
716 if (!This->info_ptr)
718 ppng_destroy_write_struct(&This->png_ptr, NULL);
719 This->png_ptr = NULL;
720 return E_FAIL;
723 This->stream = stream;
725 /* set up setjmp/longjmp error handling */
726 if (setjmp(jmpbuf))
728 ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
729 This->png_ptr = NULL;
730 This->stream = NULL;
731 return E_FAIL;
733 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
735 /* set up custom i/o handling */
736 ppng_set_write_fn(This->png_ptr, This, user_write_data, user_flush);
738 return S_OK;
741 HRESULT CDECL png_encoder_get_supported_format(struct encoder* iface, GUID *pixel_format, DWORD *bpp, BOOL *indexed)
743 int i;
745 for (i=0; formats[i].guid; i++)
747 if (memcmp(formats[i].guid, pixel_format, sizeof(GUID)) == 0)
748 break;
751 if (!formats[i].guid)
752 i = 0;
754 *pixel_format = *formats[i].guid;
755 *bpp = formats[i].bpp;
756 *indexed = (formats[i].color_type == PNG_COLOR_TYPE_PALETTE);
758 return S_OK;
761 static HRESULT CDECL png_encoder_create_frame(struct encoder *encoder, const struct encoder_frame *encoder_frame)
763 struct png_encoder *This = impl_from_encoder(encoder);
764 jmp_buf jmpbuf;
765 int i;
767 for (i=0; formats[i].guid; i++)
769 if (memcmp(formats[i].guid, &encoder_frame->pixel_format, sizeof(GUID)) == 0)
771 This->format = &formats[i];
772 break;
776 if (!formats[i].guid)
778 ERR("invalid pixel format %s\n", wine_dbgstr_guid(&encoder_frame->pixel_format));
779 return E_FAIL;
782 /* set up setjmp/longjmp error handling */
783 if (setjmp(jmpbuf))
784 return E_FAIL;
786 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
788 This->encoder_frame = *encoder_frame;
789 This->lines_written = 0;
791 if (encoder_frame->interlace)
793 /* libpng requires us to write all data multiple times in this case. */
794 This->stride = (This->format->bpp * encoder_frame->width + 7)/8;
795 This->data = malloc(encoder_frame->height * This->stride);
796 if (!This->data)
797 return E_OUTOFMEMORY;
800 /* Tell PNG we need to byte swap if writing a >8-bpp image */
801 if (This->format->bit_depth > 8)
802 ppng_set_swap(This->png_ptr);
804 ppng_set_IHDR(This->png_ptr, This->info_ptr, encoder_frame->width, encoder_frame->height,
805 This->format->bit_depth, This->format->color_type,
806 encoder_frame->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
807 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
809 if (encoder_frame->dpix != 0.0 && encoder_frame->dpiy != 0.0)
811 ppng_set_pHYs(This->png_ptr, This->info_ptr, (encoder_frame->dpix+0.0127) / 0.0254,
812 (encoder_frame->dpiy+0.0127) / 0.0254, PNG_RESOLUTION_METER);
815 if (This->format->color_type == PNG_COLOR_TYPE_PALETTE && encoder_frame->num_colors)
817 png_color png_palette[256];
818 png_byte trans[256];
819 UINT i, num_trans = 0, colors;
821 /* Newer libpng versions don't accept larger palettes than the declared
822 * bit depth, so we need to generate the palette of the correct length.
824 colors = min(encoder_frame->num_colors, 1 << This->format->bit_depth);
826 for (i = 0; i < colors; i++)
828 png_palette[i].red = (encoder_frame->palette[i] >> 16) & 0xff;
829 png_palette[i].green = (encoder_frame->palette[i] >> 8) & 0xff;
830 png_palette[i].blue = encoder_frame->palette[i] & 0xff;
831 trans[i] = (encoder_frame->palette[i] >> 24) & 0xff;
832 if (trans[i] != 0xff)
833 num_trans = i+1;
836 ppng_set_PLTE(This->png_ptr, This->info_ptr, png_palette, colors);
838 if (num_trans)
839 ppng_set_tRNS(This->png_ptr, This->info_ptr, trans, num_trans, NULL);
842 ppng_write_info(This->png_ptr, This->info_ptr);
844 if (This->format->remove_filler)
845 ppng_set_filler(This->png_ptr, 0, PNG_FILLER_AFTER);
847 if (This->format->swap_rgb)
848 ppng_set_bgr(This->png_ptr);
850 if (encoder_frame->interlace)
851 This->passes = ppng_set_interlace_handling(This->png_ptr);
853 if (encoder_frame->filter != WICPngFilterUnspecified)
855 static const int png_filter_map[] =
857 /* WICPngFilterUnspecified */ PNG_NO_FILTERS,
858 /* WICPngFilterNone */ PNG_FILTER_NONE,
859 /* WICPngFilterSub */ PNG_FILTER_SUB,
860 /* WICPngFilterUp */ PNG_FILTER_UP,
861 /* WICPngFilterAverage */ PNG_FILTER_AVG,
862 /* WICPngFilterPaeth */ PNG_FILTER_PAETH,
863 /* WICPngFilterAdaptive */ PNG_ALL_FILTERS,
866 ppng_set_filter(This->png_ptr, 0, png_filter_map[encoder_frame->filter]);
869 return S_OK;
872 HRESULT CDECL png_encoder_write_lines(struct encoder* encoder, BYTE *data, DWORD line_count, DWORD stride)
874 struct png_encoder *This = impl_from_encoder(encoder);
875 jmp_buf jmpbuf;
876 png_byte **row_pointers=NULL;
877 UINT i;
879 if (This->encoder_frame.interlace)
881 /* Just store the data so we can write it in multiple passes in Commit. */
882 for (i=0; i<line_count; i++)
883 memcpy(This->data + This->stride * (This->lines_written + i),
884 data + stride * i,
885 This->stride);
887 This->lines_written += line_count;
889 return S_OK;
892 /* set up setjmp/longjmp error handling */
893 if (setjmp(jmpbuf))
895 free(row_pointers);
896 return E_FAIL;
899 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
901 row_pointers = malloc(line_count * sizeof(png_byte*));
902 if (!row_pointers)
903 return E_OUTOFMEMORY;
905 for (i=0; i<line_count; i++)
906 row_pointers[i] = data + stride * i;
908 ppng_write_rows(This->png_ptr, row_pointers, line_count);
909 This->lines_written += line_count;
911 free(row_pointers);
913 return S_OK;
916 static HRESULT CDECL png_encoder_commit_frame(struct encoder *encoder)
918 struct png_encoder *This = impl_from_encoder(encoder);
919 jmp_buf jmpbuf;
920 png_byte **row_pointers=NULL;
922 /* set up setjmp/longjmp error handling */
923 if (setjmp(jmpbuf))
925 free(row_pointers);
926 return E_FAIL;
929 ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn);
931 if (This->encoder_frame.interlace)
933 int i;
935 row_pointers = malloc(This->encoder_frame.height * sizeof(png_byte*));
936 if (!row_pointers)
937 return E_OUTOFMEMORY;
939 for (i=0; i<This->encoder_frame.height; i++)
940 row_pointers[i] = This->data + This->stride * i;
942 for (i=0; i<This->passes; i++)
943 ppng_write_rows(This->png_ptr, row_pointers, This->encoder_frame.height);
946 ppng_write_end(This->png_ptr, This->info_ptr);
948 free(row_pointers);
950 return S_OK;
953 static HRESULT CDECL png_encoder_commit_file(struct encoder *encoder)
955 return S_OK;
958 static void CDECL png_encoder_destroy(struct encoder *encoder)
960 struct png_encoder *This = impl_from_encoder(encoder);
961 if (This->png_ptr)
962 ppng_destroy_write_struct(&This->png_ptr, &This->info_ptr);
963 free(This->data);
964 RtlFreeHeap(GetProcessHeap(), 0, This);
967 static const struct encoder_funcs png_encoder_vtable = {
968 png_encoder_initialize,
969 png_encoder_get_supported_format,
970 png_encoder_create_frame,
971 png_encoder_write_lines,
972 png_encoder_commit_frame,
973 png_encoder_commit_file,
974 png_encoder_destroy
977 HRESULT CDECL png_encoder_create(struct encoder_info *info, struct encoder **result)
979 struct png_encoder *This;
981 if (!load_libpng())
983 ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG);
984 return E_FAIL;
987 This = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*This));
989 if (!This)
991 return E_OUTOFMEMORY;
994 This->encoder.vtable = &png_encoder_vtable;
995 This->png_ptr = NULL;
996 This->info_ptr = NULL;
997 This->data = NULL;
998 *result = &This->encoder;
1000 info->flags = ENCODER_FLAGS_SUPPORTS_METADATA;
1001 info->container_format = GUID_ContainerFormatPng;
1002 info->clsid = CLSID_WICPngEncoder;
1003 info->encoder_options[0] = ENCODER_OPTION_INTERLACE;
1004 info->encoder_options[1] = ENCODER_OPTION_FILTER;
1005 info->encoder_options[2] = ENCODER_OPTION_END;
1007 return S_OK;
1010 #else
1012 HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **result)
1014 ERR("Trying to load PNG picture, but PNG support is not compiled in.\n");
1015 return E_FAIL;
1018 HRESULT CDECL png_encoder_create(struct encoder_info *info, struct encoder **result)
1020 ERR("Trying to save PNG picture, but PNG support is not compiled in.\n");
1021 return E_FAIL;
1024 #endif