2 * Copyright 2009 Vincent Povirk for CodeWeavers
3 * Copyright 2016 Dmitry Timoshkov
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
21 #include "wine/port.h"
29 #define NONAMELESSUNION
36 #include "wincodecs_private.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs
);
42 static inline ULONG
read_ulong_be(BYTE
* data
)
44 return data
[0] << 24 | data
[1] << 16 | data
[2] << 8 | data
[3];
47 static HRESULT
LoadTextMetadata(IStream
*stream
, const GUID
*preferred_vendor
,
48 DWORD persist_options
, MetadataItem
**items
, DWORD
*item_count
)
54 ULONG name_len
, value_len
;
59 hr
= read_png_chunk(stream
, type
, &data
, &data_size
);
60 if (FAILED(hr
)) return hr
;
62 name_end_ptr
= memchr(data
, 0, data_size
);
64 name_len
= name_end_ptr
- data
;
66 if (!name_end_ptr
|| name_len
> 79)
68 HeapFree(GetProcessHeap(), 0, data
);
72 value_len
= data_size
- name_len
- 1;
74 result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MetadataItem
));
75 name
= HeapAlloc(GetProcessHeap(), 0, name_len
+ 1);
76 value
= HeapAlloc(GetProcessHeap(), 0, value_len
+ 1);
77 if (!result
|| !name
|| !value
)
79 HeapFree(GetProcessHeap(), 0, data
);
80 HeapFree(GetProcessHeap(), 0, result
);
81 HeapFree(GetProcessHeap(), 0, name
);
82 HeapFree(GetProcessHeap(), 0, value
);
86 PropVariantInit(&result
[0].schema
);
87 PropVariantInit(&result
[0].id
);
88 PropVariantInit(&result
[0].value
);
90 memcpy(name
, data
, name_len
+ 1);
91 memcpy(value
, name_end_ptr
+ 1, value_len
);
94 result
[0].id
.vt
= VT_LPSTR
;
95 result
[0].id
.u
.pszVal
= name
;
96 result
[0].value
.vt
= VT_LPSTR
;
97 result
[0].value
.u
.pszVal
= value
;
102 HeapFree(GetProcessHeap(), 0, data
);
107 static const MetadataHandlerVtbl TextReader_Vtbl
= {
109 &CLSID_WICPngTextMetadataReader
,
113 HRESULT
PngTextReader_CreateInstance(REFIID iid
, void** ppv
)
115 return MetadataReader_Create(&TextReader_Vtbl
, iid
, ppv
);
118 static HRESULT
LoadGamaMetadata(IStream
*stream
, const GUID
*preferred_vendor
,
119 DWORD persist_options
, MetadataItem
**items
, DWORD
*item_count
)
126 static const WCHAR ImageGamma
[] = {'I','m','a','g','e','G','a','m','m','a',0};
128 MetadataItem
*result
;
130 hr
= read_png_chunk(stream
, type
, &data
, &data_size
);
131 if (FAILED(hr
)) return hr
;
135 HeapFree(GetProcessHeap(), 0, data
);
139 gamma
= read_ulong_be(data
);
141 HeapFree(GetProcessHeap(), 0, data
);
143 result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MetadataItem
));
144 name
= HeapAlloc(GetProcessHeap(), 0, sizeof(ImageGamma
));
145 if (!result
|| !name
)
147 HeapFree(GetProcessHeap(), 0, result
);
148 HeapFree(GetProcessHeap(), 0, name
);
149 return E_OUTOFMEMORY
;
152 PropVariantInit(&result
[0].schema
);
153 PropVariantInit(&result
[0].id
);
154 PropVariantInit(&result
[0].value
);
156 memcpy(name
, ImageGamma
, sizeof(ImageGamma
));
158 result
[0].id
.vt
= VT_LPWSTR
;
159 result
[0].id
.u
.pwszVal
= name
;
160 result
[0].value
.vt
= VT_UI4
;
161 result
[0].value
.u
.ulVal
= gamma
;
169 static const MetadataHandlerVtbl GamaReader_Vtbl
= {
171 &CLSID_WICPngGamaMetadataReader
,
175 HRESULT
PngGamaReader_CreateInstance(REFIID iid
, void** ppv
)
177 return MetadataReader_Create(&GamaReader_Vtbl
, iid
, ppv
);
180 static HRESULT
LoadChrmMetadata(IStream
*stream
, const GUID
*preferred_vendor
,
181 DWORD persist_options
, MetadataItem
**items
, DWORD
*item_count
)
187 static const WCHAR names
[8][12] = {
188 {'W','h','i','t','e','P','o','i','n','t','X',0},
189 {'W','h','i','t','e','P','o','i','n','t','Y',0},
192 {'G','r','e','e','n','X',0},
193 {'G','r','e','e','n','Y',0},
194 {'B','l','u','e','X',0},
195 {'B','l','u','e','Y',0},
197 LPWSTR dyn_names
[8] = {0};
198 MetadataItem
*result
;
201 hr
= read_png_chunk(stream
, type
, &data
, &data_size
);
202 if (FAILED(hr
)) return hr
;
206 HeapFree(GetProcessHeap(), 0, data
);
210 result
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(MetadataItem
)*8);
213 dyn_names
[i
] = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*(lstrlenW(names
[i
])+1));
214 if (!dyn_names
[i
]) break;
216 if (!result
|| i
< 8)
218 HeapFree(GetProcessHeap(), 0, result
);
220 HeapFree(GetProcessHeap(), 0, dyn_names
[i
]);
221 HeapFree(GetProcessHeap(), 0, data
);
222 return E_OUTOFMEMORY
;
227 PropVariantInit(&result
[i
].schema
);
229 PropVariantInit(&result
[i
].id
);
230 result
[i
].id
.vt
= VT_LPWSTR
;
231 result
[i
].id
.u
.pwszVal
= dyn_names
[i
];
232 lstrcpyW(dyn_names
[i
], names
[i
]);
234 PropVariantInit(&result
[i
].value
);
235 result
[i
].value
.vt
= VT_UI4
;
236 result
[i
].value
.u
.ulVal
= read_ulong_be(&data
[i
*4]);
242 HeapFree(GetProcessHeap(), 0, data
);
247 static const MetadataHandlerVtbl ChrmReader_Vtbl
= {
249 &CLSID_WICPngChrmMetadataReader
,
253 HRESULT
PngChrmReader_CreateInstance(REFIID iid
, void** ppv
)
255 return MetadataReader_Create(&ChrmReader_Vtbl
, iid
, ppv
);
260 static void *libpng_handle
;
261 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
262 MAKE_FUNCPTR(png_create_read_struct
);
263 MAKE_FUNCPTR(png_create_info_struct
);
264 MAKE_FUNCPTR(png_create_write_struct
);
265 MAKE_FUNCPTR(png_destroy_read_struct
);
266 MAKE_FUNCPTR(png_destroy_write_struct
);
267 MAKE_FUNCPTR(png_error
);
268 MAKE_FUNCPTR(png_get_bit_depth
);
269 MAKE_FUNCPTR(png_get_color_type
);
270 MAKE_FUNCPTR(png_get_error_ptr
);
271 MAKE_FUNCPTR(png_get_iCCP
);
272 MAKE_FUNCPTR(png_get_image_height
);
273 MAKE_FUNCPTR(png_get_image_width
);
274 MAKE_FUNCPTR(png_get_io_ptr
);
275 MAKE_FUNCPTR(png_get_pHYs
);
276 MAKE_FUNCPTR(png_get_PLTE
);
277 MAKE_FUNCPTR(png_get_tRNS
);
278 MAKE_FUNCPTR(png_set_bgr
);
279 MAKE_FUNCPTR(png_set_crc_action
);
280 MAKE_FUNCPTR(png_set_error_fn
);
281 MAKE_FUNCPTR(png_set_filler
);
282 MAKE_FUNCPTR(png_set_filter
);
283 MAKE_FUNCPTR(png_set_gray_to_rgb
);
284 MAKE_FUNCPTR(png_set_interlace_handling
);
285 MAKE_FUNCPTR(png_set_IHDR
);
286 MAKE_FUNCPTR(png_set_pHYs
);
287 MAKE_FUNCPTR(png_set_PLTE
);
288 MAKE_FUNCPTR(png_set_read_fn
);
289 MAKE_FUNCPTR(png_set_strip_16
);
290 MAKE_FUNCPTR(png_set_tRNS
);
291 MAKE_FUNCPTR(png_set_tRNS_to_alpha
);
292 MAKE_FUNCPTR(png_set_write_fn
);
293 MAKE_FUNCPTR(png_set_swap
);
294 MAKE_FUNCPTR(png_read_end
);
295 MAKE_FUNCPTR(png_read_image
);
296 MAKE_FUNCPTR(png_read_info
);
297 MAKE_FUNCPTR(png_write_end
);
298 MAKE_FUNCPTR(png_write_info
);
299 MAKE_FUNCPTR(png_write_rows
);
302 static CRITICAL_SECTION init_png_cs
;
303 static CRITICAL_SECTION_DEBUG init_png_cs_debug
=
306 { &init_png_cs_debug
.ProcessLocksList
,
307 &init_png_cs_debug
.ProcessLocksList
},
308 0, 0, { (DWORD_PTR
)(__FILE__
": init_png_cs") }
310 static CRITICAL_SECTION init_png_cs
= { &init_png_cs_debug
, -1, 0, 0, 0, 0 };
312 static const WCHAR wszPngInterlaceOption
[] = {'I','n','t','e','r','l','a','c','e','O','p','t','i','o','n',0};
313 static const WCHAR wszPngFilterOption
[] = {'F','i','l','t','e','r','O','p','t','i','o','n',0};
315 static void *load_libpng(void)
319 EnterCriticalSection(&init_png_cs
);
321 if(!libpng_handle
&& (libpng_handle
= dlopen(SONAME_LIBPNG
, RTLD_NOW
)) != NULL
) {
323 #define LOAD_FUNCPTR(f) \
324 if((p##f = dlsym(libpng_handle, #f)) == NULL) { \
325 libpng_handle = NULL; \
326 LeaveCriticalSection(&init_png_cs); \
329 LOAD_FUNCPTR(png_create_read_struct
);
330 LOAD_FUNCPTR(png_create_info_struct
);
331 LOAD_FUNCPTR(png_create_write_struct
);
332 LOAD_FUNCPTR(png_destroy_read_struct
);
333 LOAD_FUNCPTR(png_destroy_write_struct
);
334 LOAD_FUNCPTR(png_error
);
335 LOAD_FUNCPTR(png_get_bit_depth
);
336 LOAD_FUNCPTR(png_get_color_type
);
337 LOAD_FUNCPTR(png_get_error_ptr
);
338 LOAD_FUNCPTR(png_get_iCCP
);
339 LOAD_FUNCPTR(png_get_image_height
);
340 LOAD_FUNCPTR(png_get_image_width
);
341 LOAD_FUNCPTR(png_get_io_ptr
);
342 LOAD_FUNCPTR(png_get_pHYs
);
343 LOAD_FUNCPTR(png_get_PLTE
);
344 LOAD_FUNCPTR(png_get_tRNS
);
345 LOAD_FUNCPTR(png_set_bgr
);
346 LOAD_FUNCPTR(png_set_crc_action
);
347 LOAD_FUNCPTR(png_set_error_fn
);
348 LOAD_FUNCPTR(png_set_filler
);
349 LOAD_FUNCPTR(png_set_filter
);
350 LOAD_FUNCPTR(png_set_gray_to_rgb
);
351 LOAD_FUNCPTR(png_set_interlace_handling
);
352 LOAD_FUNCPTR(png_set_IHDR
);
353 LOAD_FUNCPTR(png_set_pHYs
);
354 LOAD_FUNCPTR(png_set_PLTE
);
355 LOAD_FUNCPTR(png_set_read_fn
);
356 LOAD_FUNCPTR(png_set_strip_16
);
357 LOAD_FUNCPTR(png_set_tRNS
);
358 LOAD_FUNCPTR(png_set_tRNS_to_alpha
);
359 LOAD_FUNCPTR(png_set_write_fn
);
360 LOAD_FUNCPTR(png_set_swap
);
361 LOAD_FUNCPTR(png_read_end
);
362 LOAD_FUNCPTR(png_read_image
);
363 LOAD_FUNCPTR(png_read_info
);
364 LOAD_FUNCPTR(png_write_end
);
365 LOAD_FUNCPTR(png_write_info
);
366 LOAD_FUNCPTR(png_write_rows
);
371 result
= libpng_handle
;
373 LeaveCriticalSection(&init_png_cs
);
378 static void user_error_fn(png_structp png_ptr
, png_const_charp error_message
)
382 /* This uses setjmp/longjmp just like the default. We can't use the
383 * default because there's no way to access the jmp buffer in the png_struct
384 * that works in 1.2 and 1.4 and allows us to dynamically load libpng. */
385 WARN("PNG error: %s\n", debugstr_a(error_message
));
386 pjmpbuf
= ppng_get_error_ptr(png_ptr
);
387 longjmp(*pjmpbuf
, 1);
390 static void user_warning_fn(png_structp png_ptr
, png_const_charp warning_message
)
392 WARN("PNG warning: %s\n", debugstr_a(warning_message
));
395 struct png_pixelformat
{
396 const WICPixelFormatGUID
*guid
;
404 static const struct png_pixelformat formats
[] = {
405 {&GUID_WICPixelFormat32bppBGRA
, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA
, 0, 1},
406 {&GUID_WICPixelFormat24bppBGR
, 24, 8, PNG_COLOR_TYPE_RGB
, 0, 1},
407 {&GUID_WICPixelFormatBlackWhite
, 1, 1, PNG_COLOR_TYPE_GRAY
, 0, 0},
408 {&GUID_WICPixelFormat2bppGray
, 2, 2, PNG_COLOR_TYPE_GRAY
, 0, 0},
409 {&GUID_WICPixelFormat4bppGray
, 4, 4, PNG_COLOR_TYPE_GRAY
, 0, 0},
410 {&GUID_WICPixelFormat8bppGray
, 8, 8, PNG_COLOR_TYPE_GRAY
, 0, 0},
411 {&GUID_WICPixelFormat16bppGray
, 16, 16, PNG_COLOR_TYPE_GRAY
, 0, 0},
412 {&GUID_WICPixelFormat32bppBGR
, 32, 8, PNG_COLOR_TYPE_RGB
, 1, 1},
413 {&GUID_WICPixelFormat48bppRGB
, 48, 16, PNG_COLOR_TYPE_RGB
, 0, 0},
414 {&GUID_WICPixelFormat64bppRGBA
, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA
, 0, 0},
415 {&GUID_WICPixelFormat1bppIndexed
, 1, 1, PNG_COLOR_TYPE_PALETTE
, 0, 0},
416 {&GUID_WICPixelFormat2bppIndexed
, 2, 2, PNG_COLOR_TYPE_PALETTE
, 0, 0},
417 {&GUID_WICPixelFormat4bppIndexed
, 4, 4, PNG_COLOR_TYPE_PALETTE
, 0, 0},
418 {&GUID_WICPixelFormat8bppIndexed
, 8, 8, PNG_COLOR_TYPE_PALETTE
, 0, 0},
422 typedef struct PngEncoder
{
423 IWICBitmapEncoder IWICBitmapEncoder_iface
;
424 IWICBitmapFrameEncode IWICBitmapFrameEncode_iface
;
430 BOOL frame_initialized
;
431 const struct png_pixelformat
*format
;
436 BOOL frame_committed
;
438 CRITICAL_SECTION lock
;
440 WICPngFilterOption filter
;
444 WICColor palette
[256];
448 static inline PngEncoder
*impl_from_IWICBitmapEncoder(IWICBitmapEncoder
*iface
)
450 return CONTAINING_RECORD(iface
, PngEncoder
, IWICBitmapEncoder_iface
);
453 static inline PngEncoder
*impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode
*iface
)
455 return CONTAINING_RECORD(iface
, PngEncoder
, IWICBitmapFrameEncode_iface
);
458 static HRESULT WINAPI
PngFrameEncode_QueryInterface(IWICBitmapFrameEncode
*iface
, REFIID iid
,
461 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
462 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(iid
), ppv
);
464 if (!ppv
) return E_INVALIDARG
;
466 if (IsEqualIID(&IID_IUnknown
, iid
) ||
467 IsEqualIID(&IID_IWICBitmapFrameEncode
, iid
))
469 *ppv
= &This
->IWICBitmapFrameEncode_iface
;
474 return E_NOINTERFACE
;
477 IUnknown_AddRef((IUnknown
*)*ppv
);
481 static ULONG WINAPI
PngFrameEncode_AddRef(IWICBitmapFrameEncode
*iface
)
483 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
484 return IWICBitmapEncoder_AddRef(&This
->IWICBitmapEncoder_iface
);
487 static ULONG WINAPI
PngFrameEncode_Release(IWICBitmapFrameEncode
*iface
)
489 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
490 return IWICBitmapEncoder_Release(&This
->IWICBitmapEncoder_iface
);
493 static HRESULT WINAPI
PngFrameEncode_Initialize(IWICBitmapFrameEncode
*iface
,
494 IPropertyBag2
*pIEncoderOptions
)
496 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
497 WICPngFilterOption filter
;
499 PROPBAG2 opts
[2]= {{0}};
500 VARIANT opt_values
[2];
504 TRACE("(%p,%p)\n", iface
, pIEncoderOptions
);
506 opts
[0].pstrName
= (LPOLESTR
)wszPngInterlaceOption
;
507 opts
[0].vt
= VT_BOOL
;
508 opts
[1].pstrName
= (LPOLESTR
)wszPngFilterOption
;
511 if (pIEncoderOptions
)
513 hr
= IPropertyBag2_Read(pIEncoderOptions
, ARRAY_SIZE(opts
), opts
, NULL
, opt_values
, opt_hres
);
518 if (V_VT(&opt_values
[0]) == VT_EMPTY
)
521 interlace
= (V_BOOL(&opt_values
[0]) != 0);
523 filter
= V_UI1(&opt_values
[1]);
524 if (filter
> WICPngFilterAdaptive
)
526 WARN("Unrecognized filter option value %u.\n", filter
);
527 filter
= WICPngFilterUnspecified
;
533 filter
= WICPngFilterUnspecified
;
536 EnterCriticalSection(&This
->lock
);
538 if (This
->frame_initialized
)
540 LeaveCriticalSection(&This
->lock
);
541 return WINCODEC_ERR_WRONGSTATE
;
544 This
->interlace
= interlace
;
545 This
->filter
= filter
;
547 This
->frame_initialized
= TRUE
;
549 LeaveCriticalSection(&This
->lock
);
554 static HRESULT WINAPI
PngFrameEncode_SetSize(IWICBitmapFrameEncode
*iface
,
555 UINT uiWidth
, UINT uiHeight
)
557 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
558 TRACE("(%p,%u,%u)\n", iface
, uiWidth
, uiHeight
);
560 EnterCriticalSection(&This
->lock
);
562 if (!This
->frame_initialized
|| This
->info_written
)
564 LeaveCriticalSection(&This
->lock
);
565 return WINCODEC_ERR_WRONGSTATE
;
568 This
->width
= uiWidth
;
569 This
->height
= uiHeight
;
571 LeaveCriticalSection(&This
->lock
);
576 static HRESULT WINAPI
PngFrameEncode_SetResolution(IWICBitmapFrameEncode
*iface
,
577 double dpiX
, double dpiY
)
579 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
580 TRACE("(%p,%0.2f,%0.2f)\n", iface
, dpiX
, dpiY
);
582 EnterCriticalSection(&This
->lock
);
584 if (!This
->frame_initialized
|| This
->info_written
)
586 LeaveCriticalSection(&This
->lock
);
587 return WINCODEC_ERR_WRONGSTATE
;
593 LeaveCriticalSection(&This
->lock
);
598 static HRESULT WINAPI
PngFrameEncode_SetPixelFormat(IWICBitmapFrameEncode
*iface
,
599 WICPixelFormatGUID
*pPixelFormat
)
601 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
603 TRACE("(%p,%s)\n", iface
, debugstr_guid(pPixelFormat
));
605 EnterCriticalSection(&This
->lock
);
607 if (!This
->frame_initialized
|| This
->info_written
)
609 LeaveCriticalSection(&This
->lock
);
610 return WINCODEC_ERR_WRONGSTATE
;
613 for (i
=0; formats
[i
].guid
; i
++)
615 if (memcmp(formats
[i
].guid
, pPixelFormat
, sizeof(GUID
)) == 0)
619 if (!formats
[i
].guid
) i
= 0;
621 This
->format
= &formats
[i
];
622 memcpy(pPixelFormat
, This
->format
->guid
, sizeof(GUID
));
624 LeaveCriticalSection(&This
->lock
);
629 static HRESULT WINAPI
PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode
*iface
,
630 UINT cCount
, IWICColorContext
**ppIColorContext
)
632 FIXME("(%p,%u,%p): stub\n", iface
, cCount
, ppIColorContext
);
636 static HRESULT WINAPI
PngFrameEncode_SetPalette(IWICBitmapFrameEncode
*iface
,
637 IWICPalette
*palette
)
639 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
642 TRACE("(%p,%p)\n", iface
, palette
);
644 if (!palette
) return E_INVALIDARG
;
646 EnterCriticalSection(&This
->lock
);
648 if (This
->frame_initialized
)
649 hr
= IWICPalette_GetColors(palette
, 256, This
->palette
, &This
->colors
);
651 hr
= WINCODEC_ERR_NOTINITIALIZED
;
653 LeaveCriticalSection(&This
->lock
);
657 static HRESULT WINAPI
PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode
*iface
,
658 IWICBitmapSource
*pIThumbnail
)
660 FIXME("(%p,%p): stub\n", iface
, pIThumbnail
);
661 return WINCODEC_ERR_UNSUPPORTEDOPERATION
;
664 static HRESULT WINAPI
PngFrameEncode_WritePixels(IWICBitmapFrameEncode
*iface
,
665 UINT lineCount
, UINT cbStride
, UINT cbBufferSize
, BYTE
*pbPixels
)
667 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
668 png_byte
**row_pointers
=NULL
;
671 TRACE("(%p,%u,%u,%u,%p)\n", iface
, lineCount
, cbStride
, cbBufferSize
, pbPixels
);
673 EnterCriticalSection(&This
->lock
);
675 if (!This
->frame_initialized
|| !This
->width
|| !This
->height
|| !This
->format
)
677 LeaveCriticalSection(&This
->lock
);
678 return WINCODEC_ERR_WRONGSTATE
;
681 if (lineCount
== 0 || lineCount
+ This
->lines_written
> This
->height
)
683 LeaveCriticalSection(&This
->lock
);
687 /* set up setjmp/longjmp error handling */
690 LeaveCriticalSection(&This
->lock
);
691 HeapFree(GetProcessHeap(), 0, row_pointers
);
694 ppng_set_error_fn(This
->png_ptr
, jmpbuf
, user_error_fn
, user_warning_fn
);
696 if (!This
->info_written
)
700 /* libpng requires us to write all data multiple times in this case. */
701 This
->stride
= (This
->format
->bpp
* This
->width
+ 7)/8;
702 This
->data
= HeapAlloc(GetProcessHeap(), 0, This
->height
* This
->stride
);
705 LeaveCriticalSection(&This
->lock
);
706 return E_OUTOFMEMORY
;
710 /* Tell PNG we need to byte swap if writing a >8-bpp image */
711 if (This
->format
->bit_depth
> 8)
712 ppng_set_swap(This
->png_ptr
);
714 ppng_set_IHDR(This
->png_ptr
, This
->info_ptr
, This
->width
, This
->height
,
715 This
->format
->bit_depth
, This
->format
->color_type
,
716 This
->interlace
? PNG_INTERLACE_ADAM7
: PNG_INTERLACE_NONE
,
717 PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
719 if (This
->xres
!= 0.0 && This
->yres
!= 0.0)
721 ppng_set_pHYs(This
->png_ptr
, This
->info_ptr
, (This
->xres
+0.0127) / 0.0254,
722 (This
->yres
+0.0127) / 0.0254, PNG_RESOLUTION_METER
);
725 if (This
->format
->color_type
== PNG_COLOR_TYPE_PALETTE
&& This
->colors
)
727 png_color png_palette
[256];
729 UINT i
, num_trans
= 0, colors
;
731 /* Newer libpng versions don't accept larger palettes than the declared
732 * bit depth, so we need to generate the palette of the correct length.
734 colors
= min(This
->colors
, 1 << This
->format
->bit_depth
);
736 for (i
= 0; i
< colors
; i
++)
738 png_palette
[i
].red
= (This
->palette
[i
] >> 16) & 0xff;
739 png_palette
[i
].green
= (This
->palette
[i
] >> 8) & 0xff;
740 png_palette
[i
].blue
= This
->palette
[i
] & 0xff;
741 trans
[i
] = (This
->palette
[i
] >> 24) & 0xff;
742 if (trans
[i
] != 0xff)
746 ppng_set_PLTE(This
->png_ptr
, This
->info_ptr
, png_palette
, colors
);
749 ppng_set_tRNS(This
->png_ptr
, This
->info_ptr
, trans
, num_trans
, NULL
);
752 ppng_write_info(This
->png_ptr
, This
->info_ptr
);
754 if (This
->format
->remove_filler
)
755 ppng_set_filler(This
->png_ptr
, 0, PNG_FILLER_AFTER
);
757 if (This
->format
->swap_rgb
)
758 ppng_set_bgr(This
->png_ptr
);
761 This
->passes
= ppng_set_interlace_handling(This
->png_ptr
);
763 if (This
->filter
!= WICPngFilterUnspecified
)
765 static const int png_filter_map
[] =
767 /* WICPngFilterUnspecified */ PNG_NO_FILTERS
,
768 /* WICPngFilterNone */ PNG_FILTER_NONE
,
769 /* WICPngFilterSub */ PNG_FILTER_SUB
,
770 /* WICPngFilterUp */ PNG_FILTER_UP
,
771 /* WICPngFilterAverage */ PNG_FILTER_AVG
,
772 /* WICPngFilterPaeth */ PNG_FILTER_PAETH
,
773 /* WICPngFilterAdaptive */ PNG_ALL_FILTERS
,
776 ppng_set_filter(This
->png_ptr
, 0, png_filter_map
[This
->filter
]);
779 This
->info_written
= TRUE
;
784 /* Just store the data so we can write it in multiple passes in Commit. */
785 for (i
=0; i
<lineCount
; i
++)
786 memcpy(This
->data
+ This
->stride
* (This
->lines_written
+ i
),
787 pbPixels
+ cbStride
* i
,
790 This
->lines_written
+= lineCount
;
792 LeaveCriticalSection(&This
->lock
);
796 row_pointers
= HeapAlloc(GetProcessHeap(), 0, lineCount
* sizeof(png_byte
*));
799 LeaveCriticalSection(&This
->lock
);
800 return E_OUTOFMEMORY
;
803 for (i
=0; i
<lineCount
; i
++)
804 row_pointers
[i
] = pbPixels
+ cbStride
* i
;
806 ppng_write_rows(This
->png_ptr
, row_pointers
, lineCount
);
807 This
->lines_written
+= lineCount
;
809 LeaveCriticalSection(&This
->lock
);
811 HeapFree(GetProcessHeap(), 0, row_pointers
);
816 static HRESULT WINAPI
PngFrameEncode_WriteSource(IWICBitmapFrameEncode
*iface
,
817 IWICBitmapSource
*pIBitmapSource
, WICRect
*prc
)
819 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
821 TRACE("(%p,%p,%s)\n", iface
, pIBitmapSource
, debug_wic_rect(prc
));
823 if (!This
->frame_initialized
)
824 return WINCODEC_ERR_WRONGSTATE
;
826 hr
= configure_write_source(iface
, pIBitmapSource
, prc
,
827 This
->format
? This
->format
->guid
: NULL
, This
->width
, This
->height
,
828 This
->xres
, This
->yres
);
832 hr
= write_source(iface
, pIBitmapSource
, prc
,
833 This
->format
->guid
, This
->format
->bpp
,
834 !This
->colors
&& This
->format
->color_type
== PNG_COLOR_TYPE_PALETTE
,
835 This
->width
, This
->height
);
841 static HRESULT WINAPI
PngFrameEncode_Commit(IWICBitmapFrameEncode
*iface
)
843 PngEncoder
*This
= impl_from_IWICBitmapFrameEncode(iface
);
844 png_byte
**row_pointers
=NULL
;
846 TRACE("(%p)\n", iface
);
848 EnterCriticalSection(&This
->lock
);
850 if (!This
->info_written
|| This
->lines_written
!= This
->height
|| This
->frame_committed
)
852 LeaveCriticalSection(&This
->lock
);
853 return WINCODEC_ERR_WRONGSTATE
;
856 /* set up setjmp/longjmp error handling */
859 LeaveCriticalSection(&This
->lock
);
860 HeapFree(GetProcessHeap(), 0, row_pointers
);
863 ppng_set_error_fn(This
->png_ptr
, jmpbuf
, user_error_fn
, user_warning_fn
);
869 row_pointers
= HeapAlloc(GetProcessHeap(), 0, This
->height
* sizeof(png_byte
*));
872 LeaveCriticalSection(&This
->lock
);
873 return E_OUTOFMEMORY
;
876 for (i
=0; i
<This
->height
; i
++)
877 row_pointers
[i
] = This
->data
+ This
->stride
* i
;
879 for (i
=0; i
<This
->passes
; i
++)
880 ppng_write_rows(This
->png_ptr
, row_pointers
, This
->height
);
883 ppng_write_end(This
->png_ptr
, This
->info_ptr
);
885 This
->frame_committed
= TRUE
;
887 HeapFree(GetProcessHeap(), 0, row_pointers
);
889 LeaveCriticalSection(&This
->lock
);
894 static HRESULT WINAPI
PngFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode
*iface
,
895 IWICMetadataQueryWriter
**ppIMetadataQueryWriter
)
897 FIXME("(%p, %p): stub\n", iface
, ppIMetadataQueryWriter
);
901 static const IWICBitmapFrameEncodeVtbl PngEncoder_FrameVtbl
= {
902 PngFrameEncode_QueryInterface
,
903 PngFrameEncode_AddRef
,
904 PngFrameEncode_Release
,
905 PngFrameEncode_Initialize
,
906 PngFrameEncode_SetSize
,
907 PngFrameEncode_SetResolution
,
908 PngFrameEncode_SetPixelFormat
,
909 PngFrameEncode_SetColorContexts
,
910 PngFrameEncode_SetPalette
,
911 PngFrameEncode_SetThumbnail
,
912 PngFrameEncode_WritePixels
,
913 PngFrameEncode_WriteSource
,
914 PngFrameEncode_Commit
,
915 PngFrameEncode_GetMetadataQueryWriter
918 static HRESULT WINAPI
PngEncoder_QueryInterface(IWICBitmapEncoder
*iface
, REFIID iid
,
921 PngEncoder
*This
= impl_from_IWICBitmapEncoder(iface
);
922 TRACE("(%p,%s,%p)\n", iface
, debugstr_guid(iid
), ppv
);
924 if (!ppv
) return E_INVALIDARG
;
926 if (IsEqualIID(&IID_IUnknown
, iid
) ||
927 IsEqualIID(&IID_IWICBitmapEncoder
, iid
))
929 *ppv
= &This
->IWICBitmapEncoder_iface
;
934 return E_NOINTERFACE
;
937 IUnknown_AddRef((IUnknown
*)*ppv
);
941 static ULONG WINAPI
PngEncoder_AddRef(IWICBitmapEncoder
*iface
)
943 PngEncoder
*This
= impl_from_IWICBitmapEncoder(iface
);
944 ULONG ref
= InterlockedIncrement(&This
->ref
);
946 TRACE("(%p) refcount=%u\n", iface
, ref
);
951 static ULONG WINAPI
PngEncoder_Release(IWICBitmapEncoder
*iface
)
953 PngEncoder
*This
= impl_from_IWICBitmapEncoder(iface
);
954 ULONG ref
= InterlockedDecrement(&This
->ref
);
956 TRACE("(%p) refcount=%u\n", iface
, ref
);
960 This
->lock
.DebugInfo
->Spare
[0] = 0;
961 DeleteCriticalSection(&This
->lock
);
963 ppng_destroy_write_struct(&This
->png_ptr
, &This
->info_ptr
);
965 IStream_Release(This
->stream
);
966 HeapFree(GetProcessHeap(), 0, This
->data
);
967 HeapFree(GetProcessHeap(), 0, This
);
973 static void user_write_data(png_structp png_ptr
, png_bytep data
, png_size_t length
)
975 PngEncoder
*This
= ppng_get_io_ptr(png_ptr
);
979 hr
= IStream_Write(This
->stream
, data
, length
, &byteswritten
);
980 if (FAILED(hr
) || byteswritten
!= length
)
982 ppng_error(png_ptr
, "failed writing data");
986 static void user_flush(png_structp png_ptr
)
990 static HRESULT WINAPI
PngEncoder_Initialize(IWICBitmapEncoder
*iface
,
991 IStream
*pIStream
, WICBitmapEncoderCacheOption cacheOption
)
993 PngEncoder
*This
= impl_from_IWICBitmapEncoder(iface
);
996 TRACE("(%p,%p,%u)\n", iface
, pIStream
, cacheOption
);
998 EnterCriticalSection(&This
->lock
);
1002 LeaveCriticalSection(&This
->lock
);
1003 return WINCODEC_ERR_WRONGSTATE
;
1006 /* initialize libpng */
1007 This
->png_ptr
= ppng_create_write_struct(PNG_LIBPNG_VER_STRING
, NULL
, NULL
, NULL
);
1010 LeaveCriticalSection(&This
->lock
);
1014 This
->info_ptr
= ppng_create_info_struct(This
->png_ptr
);
1015 if (!This
->info_ptr
)
1017 ppng_destroy_write_struct(&This
->png_ptr
, NULL
);
1018 This
->png_ptr
= NULL
;
1019 LeaveCriticalSection(&This
->lock
);
1023 IStream_AddRef(pIStream
);
1024 This
->stream
= pIStream
;
1026 /* set up setjmp/longjmp error handling */
1029 ppng_destroy_write_struct(&This
->png_ptr
, &This
->info_ptr
);
1030 This
->png_ptr
= NULL
;
1031 IStream_Release(This
->stream
);
1032 This
->stream
= NULL
;
1033 LeaveCriticalSection(&This
->lock
);
1036 ppng_set_error_fn(This
->png_ptr
, jmpbuf
, user_error_fn
, user_warning_fn
);
1038 /* set up custom i/o handling */
1039 ppng_set_write_fn(This
->png_ptr
, This
, user_write_data
, user_flush
);
1041 LeaveCriticalSection(&This
->lock
);
1046 static HRESULT WINAPI
PngEncoder_GetContainerFormat(IWICBitmapEncoder
*iface
, GUID
*format
)
1048 TRACE("(%p,%p)\n", iface
, format
);
1051 return E_INVALIDARG
;
1053 memcpy(format
, &GUID_ContainerFormatPng
, sizeof(*format
));
1057 static HRESULT WINAPI
PngEncoder_GetEncoderInfo(IWICBitmapEncoder
*iface
, IWICBitmapEncoderInfo
**info
)
1059 IWICComponentInfo
*comp_info
;
1062 TRACE("%p,%p\n", iface
, info
);
1064 if (!info
) return E_INVALIDARG
;
1066 hr
= CreateComponentInfo(&CLSID_WICPngEncoder
, &comp_info
);
1069 hr
= IWICComponentInfo_QueryInterface(comp_info
, &IID_IWICBitmapEncoderInfo
, (void **)info
);
1070 IWICComponentInfo_Release(comp_info
);
1075 static HRESULT WINAPI
PngEncoder_SetColorContexts(IWICBitmapEncoder
*iface
,
1076 UINT cCount
, IWICColorContext
**ppIColorContext
)
1078 FIXME("(%p,%u,%p): stub\n", iface
, cCount
, ppIColorContext
);
1082 static HRESULT WINAPI
PngEncoder_SetPalette(IWICBitmapEncoder
*iface
, IWICPalette
*palette
)
1084 PngEncoder
*This
= impl_from_IWICBitmapEncoder(iface
);
1087 TRACE("(%p,%p)\n", iface
, palette
);
1089 EnterCriticalSection(&This
->lock
);
1091 hr
= This
->stream
? WINCODEC_ERR_UNSUPPORTEDOPERATION
: WINCODEC_ERR_NOTINITIALIZED
;
1093 LeaveCriticalSection(&This
->lock
);
1098 static HRESULT WINAPI
PngEncoder_SetThumbnail(IWICBitmapEncoder
*iface
, IWICBitmapSource
*pIThumbnail
)
1100 TRACE("(%p,%p)\n", iface
, pIThumbnail
);
1101 return WINCODEC_ERR_UNSUPPORTEDOPERATION
;
1104 static HRESULT WINAPI
PngEncoder_SetPreview(IWICBitmapEncoder
*iface
, IWICBitmapSource
*pIPreview
)
1106 TRACE("(%p,%p)\n", iface
, pIPreview
);
1107 return WINCODEC_ERR_UNSUPPORTEDOPERATION
;
1110 static HRESULT WINAPI
PngEncoder_CreateNewFrame(IWICBitmapEncoder
*iface
,
1111 IWICBitmapFrameEncode
**ppIFrameEncode
, IPropertyBag2
**ppIEncoderOptions
)
1113 PngEncoder
*This
= impl_from_IWICBitmapEncoder(iface
);
1115 static const PROPBAG2 opts
[2] =
1117 { PROPBAG2_TYPE_DATA
, VT_BOOL
, 0, 0, (LPOLESTR
)wszPngInterlaceOption
},
1118 { PROPBAG2_TYPE_DATA
, VT_UI1
, 0, 0, (LPOLESTR
)wszPngFilterOption
},
1121 TRACE("(%p,%p,%p)\n", iface
, ppIFrameEncode
, ppIEncoderOptions
);
1123 EnterCriticalSection(&This
->lock
);
1125 if (This
->frame_count
!= 0)
1127 LeaveCriticalSection(&This
->lock
);
1128 return WINCODEC_ERR_UNSUPPORTEDOPERATION
;
1133 LeaveCriticalSection(&This
->lock
);
1134 return WINCODEC_ERR_NOTINITIALIZED
;
1137 if (ppIEncoderOptions
)
1139 hr
= CreatePropertyBag2(opts
, ARRAY_SIZE(opts
), ppIEncoderOptions
);
1142 LeaveCriticalSection(&This
->lock
);
1147 This
->frame_count
= 1;
1149 LeaveCriticalSection(&This
->lock
);
1151 IWICBitmapEncoder_AddRef(iface
);
1152 *ppIFrameEncode
= &This
->IWICBitmapFrameEncode_iface
;
1157 static HRESULT WINAPI
PngEncoder_Commit(IWICBitmapEncoder
*iface
)
1159 PngEncoder
*This
= impl_from_IWICBitmapEncoder(iface
);
1160 TRACE("(%p)\n", iface
);
1162 EnterCriticalSection(&This
->lock
);
1164 if (!This
->frame_committed
|| This
->committed
)
1166 LeaveCriticalSection(&This
->lock
);
1167 return WINCODEC_ERR_WRONGSTATE
;
1170 This
->committed
= TRUE
;
1172 LeaveCriticalSection(&This
->lock
);
1177 static HRESULT WINAPI
PngEncoder_GetMetadataQueryWriter(IWICBitmapEncoder
*iface
,
1178 IWICMetadataQueryWriter
**ppIMetadataQueryWriter
)
1180 FIXME("(%p,%p): stub\n", iface
, ppIMetadataQueryWriter
);
1184 static const IWICBitmapEncoderVtbl PngEncoder_Vtbl
= {
1185 PngEncoder_QueryInterface
,
1188 PngEncoder_Initialize
,
1189 PngEncoder_GetContainerFormat
,
1190 PngEncoder_GetEncoderInfo
,
1191 PngEncoder_SetColorContexts
,
1192 PngEncoder_SetPalette
,
1193 PngEncoder_SetThumbnail
,
1194 PngEncoder_SetPreview
,
1195 PngEncoder_CreateNewFrame
,
1197 PngEncoder_GetMetadataQueryWriter
1200 HRESULT
PngEncoder_CreateInstance(REFIID iid
, void** ppv
)
1205 TRACE("(%s,%p)\n", debugstr_guid(iid
), ppv
);
1211 ERR("Failed writing PNG because unable to find %s\n",SONAME_LIBPNG
);
1215 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(PngEncoder
));
1216 if (!This
) return E_OUTOFMEMORY
;
1218 This
->IWICBitmapEncoder_iface
.lpVtbl
= &PngEncoder_Vtbl
;
1219 This
->IWICBitmapFrameEncode_iface
.lpVtbl
= &PngEncoder_FrameVtbl
;
1221 This
->png_ptr
= NULL
;
1222 This
->info_ptr
= NULL
;
1223 This
->stream
= NULL
;
1224 This
->frame_count
= 0;
1225 This
->frame_initialized
= FALSE
;
1226 This
->format
= NULL
;
1227 This
->info_written
= FALSE
;
1232 This
->lines_written
= 0;
1233 This
->frame_committed
= FALSE
;
1234 This
->committed
= FALSE
;
1237 InitializeCriticalSection(&This
->lock
);
1238 This
->lock
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": PngEncoder.lock");
1240 ret
= IWICBitmapEncoder_QueryInterface(&This
->IWICBitmapEncoder_iface
, iid
, ppv
);
1241 IWICBitmapEncoder_Release(&This
->IWICBitmapEncoder_iface
);
1246 #else /* !SONAME_LIBPNG */
1248 HRESULT
PngEncoder_CreateInstance(REFIID iid
, void** ppv
)
1250 ERR("Trying to save PNG picture, but PNG support is not compiled in.\n");
1256 HRESULT
PngDecoder_CreateInstance(REFIID iid
, void** ppv
)
1259 struct decoder
*decoder
;
1260 struct decoder_info decoder_info
;
1262 hr
= get_unix_decoder(&CLSID_WICPngDecoder
, &decoder_info
, &decoder
);
1265 hr
= CommonDecoder_CreateInstance(decoder
, &decoder_info
, iid
, ppv
);