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
25 #include "wine/port.h"
32 #define NONAMELESSUNION
35 #define WIN32_NO_STATUS
41 #include "wincodecs_private.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs
);
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
);
89 static CRITICAL_SECTION init_png_cs
;
90 static CRITICAL_SECTION_DEBUG init_png_cs_debug
=
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)
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); \
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
);
153 result
= libpng_handle
;
155 RtlLeaveCriticalSection(&init_png_cs
);
162 struct decoder decoder
;
164 struct decoder_frame decoder_frame
;
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
)
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
);
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
);
213 int color_type
, bit_depth
;
216 png_uint_32 transparency
;
217 png_color_16p trans_values
;
218 png_uint_32 ret
, xres
, yres
;
220 png_colorp png_palette
;
224 png_bytep
*row_pointers
=NULL
;
226 png_bytep cp_profile
;
230 png_ptr
= ppng_create_read_struct(PNG_LIBPNG_VER_STRING
, NULL
, NULL
, NULL
);
236 info_ptr
= ppng_create_info_struct(png_ptr
);
239 ppng_destroy_read_struct(&png_ptr
, NULL
, NULL
);
243 /* set up setjmp/longjmp error handling */
246 hr
= WINCODEC_ERR_UNKNOWNIMAGEFORMAT
;
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
);
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. */
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
);
278 if (transparency
&& (color_type
== PNG_COLOR_TYPE_RGB
||
279 (color_type
== PNG_COLOR_TYPE_GRAY
&& bit_depth
== 16)))
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
;
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
);
294 case PNG_COLOR_TYPE_RGB_ALPHA
:
295 This
->decoder_frame
.bpp
= bit_depth
* 4;
299 ppng_set_bgr(png_ptr
);
300 This
->decoder_frame
.pixel_format
= GUID_WICPixelFormat32bppBGRA
;
302 case 16: This
->decoder_frame
.pixel_format
= GUID_WICPixelFormat64bppRGBA
; break;
304 ERR("invalid RGBA bit depth: %i\n", bit_depth
);
309 case PNG_COLOR_TYPE_GRAY
:
310 This
->decoder_frame
.bpp
= 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;
321 ERR("invalid grayscale bit depth: %i\n", bit_depth
);
327 /* else fall through */
328 case PNG_COLOR_TYPE_PALETTE
:
329 This
->decoder_frame
.bpp
= 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;
337 ERR("invalid indexed color bit depth: %i\n", bit_depth
);
342 case PNG_COLOR_TYPE_RGB
:
343 This
->decoder_frame
.bpp
= bit_depth
* 3;
347 ppng_set_bgr(png_ptr
);
348 This
->decoder_frame
.pixel_format
= GUID_WICPixelFormat24bppBGR
;
350 case 16: This
->decoder_frame
.pixel_format
= GUID_WICPixelFormat48bppRGB
; break;
352 ERR("invalid RGB color bit depth: %i\n", bit_depth
);
358 ERR("invalid color type %i\n", color_type
);
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;
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
);
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
)
390 memcpy(This
->color_profile
, cp_profile
, cp_len
);
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
);
400 ERR("paletted image with no PLTE chunk\n");
405 if (num_palette
> 256)
407 ERR("palette has %i colors?!\n", num_palette
);
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
);
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
)
448 row_pointers
= malloc(sizeof(png_bytep
)*This
->decoder_frame
.height
);
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
);
463 /* png_read_end intentionally not called to not seek to the end of the file */
465 st
->flags
= WICBitmapDecoderCapabilityCanDecodeAllImages
|
466 WICBitmapDecoderCapabilityCanDecodeSomeImages
|
467 WICBitmapDecoderCapabilityCanEnumerateMetadata
;
470 This
->stream
= stream
;
475 ppng_destroy_read_struct(&png_ptr
, &info_ptr
, NULL
);
479 free(This
->image_bits
);
480 This
->image_bits
= NULL
;
481 free(This
->color_profile
);
482 This
->color_profile
= NULL
;
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
;
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
);
509 struct decoder_block
*result
= NULL
;
513 ULONGLONG chunk_start
;
514 ULONG metadata_blocks_size
= 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
)
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
;
560 seek
= chunk_start
+ chunk_size
+ 12; /* skip data and CRC */
561 } while (memcmp(chunk_type
, "IEND", 4));
572 RtlFreeHeap(GetProcessHeap(), 0, result
);
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
;
586 return E_OUTOFMEMORY
;
588 memcpy(*data
, This
->color_profile
, This
->color_profile_len
);
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
,
611 HRESULT CDECL
png_decoder_create(struct decoder_info
*info
, struct decoder
**result
)
613 struct png_decoder
*This
;
617 ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG
);
621 This
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*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
;
640 struct png_pixelformat
{
641 const WICPixelFormatGUID
*guid
;
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},
669 struct encoder encoder
;
673 struct encoder_frame encoder_frame
;
674 const struct png_pixelformat
*format
;
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
);
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
);
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
);
715 This
->info_ptr
= ppng_create_info_struct(This
->png_ptr
);
718 ppng_destroy_write_struct(&This
->png_ptr
, NULL
);
719 This
->png_ptr
= NULL
;
723 This
->stream
= stream
;
725 /* set up setjmp/longjmp error handling */
728 ppng_destroy_write_struct(&This
->png_ptr
, &This
->info_ptr
);
729 This
->png_ptr
= NULL
;
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
);
741 HRESULT CDECL
png_encoder_get_supported_format(struct encoder
* iface
, GUID
*pixel_format
, DWORD
*bpp
, BOOL
*indexed
)
745 for (i
=0; formats
[i
].guid
; i
++)
747 if (memcmp(formats
[i
].guid
, pixel_format
, sizeof(GUID
)) == 0)
751 if (!formats
[i
].guid
)
754 *pixel_format
= *formats
[i
].guid
;
755 *bpp
= formats
[i
].bpp
;
756 *indexed
= (formats
[i
].color_type
== PNG_COLOR_TYPE_PALETTE
);
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
);
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
];
776 if (!formats
[i
].guid
)
778 ERR("invalid pixel format %s\n", wine_dbgstr_guid(&encoder_frame
->pixel_format
));
782 /* set up setjmp/longjmp error handling */
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
);
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];
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)
836 ppng_set_PLTE(This
->png_ptr
, This
->info_ptr
, png_palette
, colors
);
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
]);
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
);
876 png_byte
**row_pointers
=NULL
;
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
),
887 This
->lines_written
+= line_count
;
892 /* set up setjmp/longjmp error handling */
899 ppng_set_error_fn(This
->png_ptr
, jmpbuf
, user_error_fn
, user_warning_fn
);
901 row_pointers
= malloc(line_count
* sizeof(png_byte
*));
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
;
916 static HRESULT CDECL
png_encoder_commit_frame(struct encoder
*encoder
)
918 struct png_encoder
*This
= impl_from_encoder(encoder
);
920 png_byte
**row_pointers
=NULL
;
922 /* set up setjmp/longjmp error handling */
929 ppng_set_error_fn(This
->png_ptr
, jmpbuf
, user_error_fn
, user_warning_fn
);
931 if (This
->encoder_frame
.interlace
)
935 row_pointers
= malloc(This
->encoder_frame
.height
* sizeof(png_byte
*));
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
);
953 static HRESULT CDECL
png_encoder_commit_file(struct encoder
*encoder
)
958 static void CDECL
png_encoder_destroy(struct encoder
*encoder
)
960 struct png_encoder
*This
= impl_from_encoder(encoder
);
962 ppng_destroy_write_struct(&This
->png_ptr
, &This
->info_ptr
);
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
,
977 HRESULT CDECL
png_encoder_create(struct encoder_info
*info
, struct encoder
**result
)
979 struct png_encoder
*This
;
983 ERR("Failed reading PNG because unable to find %s\n",SONAME_LIBPNG
);
987 This
= RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*This
));
991 return E_OUTOFMEMORY
;
994 This
->encoder
.vtable
= &png_encoder_vtable
;
995 This
->png_ptr
= NULL
;
996 This
->info_ptr
= 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
;
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");
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");