hhctrl.ocx: Add the Serbian (Latin and Cyrillic) translation.
[wine] / dlls / windowscodecs / icnsformat.c
1 /*
2  * Copyright 2010 Damjan Jovanovic
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20 #include "wine/port.h"
21
22 #include <stdarg.h>
23
24 #ifdef HAVE_ICNS_H
25 #include <icns.h>
26 #endif
27
28 #define COBJMACROS
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "objbase.h"
33 #include "wincodec.h"
34
35 #include "wincodecs_private.h"
36
37 #include "wine/debug.h"
38 #include "wine/library.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
41
42 #ifdef SONAME_LIBICNS
43
44 static void *libicns_handle;
45 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
46 MAKE_FUNCPTR(icns_create_family);
47 MAKE_FUNCPTR(icns_export_family_data);
48 MAKE_FUNCPTR(icns_free_image);
49 MAKE_FUNCPTR(icns_get_mask_type_for_icon_type);
50 MAKE_FUNCPTR(icns_get_type_from_image_info);
51 MAKE_FUNCPTR(icns_init_image_for_type);
52 MAKE_FUNCPTR(icns_new_element_from_image);
53 MAKE_FUNCPTR(icns_set_element_in_family);
54 #undef MAKE_FUNCPTR
55
56 static void *load_libicns(void)
57 {
58     if((libicns_handle = wine_dlopen(SONAME_LIBICNS, RTLD_NOW, NULL, 0)) != NULL) {
59
60 #define LOAD_FUNCPTR(f) \
61     if((p##f = wine_dlsym(libicns_handle, #f, NULL, 0)) == NULL) { \
62         libicns_handle = NULL; \
63         return NULL; \
64     }
65         LOAD_FUNCPTR(icns_create_family);
66         LOAD_FUNCPTR(icns_export_family_data);
67         LOAD_FUNCPTR(icns_free_image);
68         LOAD_FUNCPTR(icns_get_mask_type_for_icon_type);
69         LOAD_FUNCPTR(icns_get_type_from_image_info);
70         LOAD_FUNCPTR(icns_init_image_for_type);
71         LOAD_FUNCPTR(icns_new_element_from_image);
72         LOAD_FUNCPTR(icns_set_element_in_family);
73 #undef LOAD_FUNCPTR
74     }
75     return libicns_handle;
76 }
77
78 typedef struct IcnsEncoder {
79     const IWICBitmapEncoderVtbl *lpVtbl;
80     LONG ref;
81     IStream *stream;
82     icns_family_t *icns_family;
83     BOOL any_frame_committed;
84     int outstanding_commits;
85     BOOL committed;
86     CRITICAL_SECTION lock;
87 } IcnsEncoder;
88
89 typedef struct IcnsFrameEncode {
90     const IWICBitmapFrameEncodeVtbl *lpVtbl;
91     IcnsEncoder *encoder;
92     LONG ref;
93     BOOL initialized;
94     UINT width;
95     UINT height;
96     icns_type_t icns_type;
97     icns_image_t icns_image;
98     int lines_written;
99     BOOL committed;
100 } IcnsFrameEncode;
101
102 static HRESULT WINAPI IcnsFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
103     void **ppv)
104 {
105     IcnsFrameEncode *This = (IcnsFrameEncode*)iface;
106     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
107
108     if (!ppv) return E_INVALIDARG;
109
110     if (IsEqualIID(&IID_IUnknown, iid) ||
111         IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
112     {
113         *ppv = &This->lpVtbl;
114     }
115     else
116     {
117         *ppv = NULL;
118         return E_NOINTERFACE;
119     }
120
121     IUnknown_AddRef((IUnknown*)*ppv);
122     return S_OK;
123 }
124
125 static ULONG WINAPI IcnsFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
126 {
127     IcnsFrameEncode *This = (IcnsFrameEncode*)iface;
128     ULONG ref = InterlockedIncrement(&This->ref);
129
130     TRACE("(%p) refcount=%u\n", iface, ref);
131
132     return ref;
133 }
134
135 static ULONG WINAPI IcnsFrameEncode_Release(IWICBitmapFrameEncode *iface)
136 {
137     IcnsFrameEncode *This = (IcnsFrameEncode*)iface;
138     ULONG ref = InterlockedDecrement(&This->ref);
139
140     TRACE("(%p) refcount=%u\n", iface, ref);
141
142     if (ref == 0)
143     {
144         if (!This->committed)
145         {
146             EnterCriticalSection(&This->encoder->lock);
147             This->encoder->outstanding_commits--;
148             LeaveCriticalSection(&This->encoder->lock);
149         }
150         if (This->icns_image.imageData != NULL)
151             picns_free_image(&This->icns_image);
152
153         IUnknown_Release((IUnknown*)This->encoder);
154         HeapFree(GetProcessHeap(), 0, This);
155     }
156
157     return ref;
158 }
159
160 static HRESULT WINAPI IcnsFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
161     IPropertyBag2 *pIEncoderOptions)
162 {
163     IcnsFrameEncode *This = (IcnsFrameEncode*)iface;
164     HRESULT hr = S_OK;
165
166     TRACE("(%p,%p)\n", iface, pIEncoderOptions);
167
168     EnterCriticalSection(&This->encoder->lock);
169
170     if (This->initialized)
171     {
172         hr = WINCODEC_ERR_WRONGSTATE;
173         goto end;
174     }
175     This->initialized = TRUE;
176
177 end:
178     LeaveCriticalSection(&This->encoder->lock);
179     return hr;
180 }
181
182 static HRESULT WINAPI IcnsFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
183     UINT uiWidth, UINT uiHeight)
184 {
185     IcnsFrameEncode *This = (IcnsFrameEncode*)iface;
186     HRESULT hr = S_OK;
187
188     TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
189
190     EnterCriticalSection(&This->encoder->lock);
191
192     if (!This->initialized || This->icns_image.imageData)
193     {
194         hr = WINCODEC_ERR_WRONGSTATE;
195         goto end;
196     }
197
198     This->width = uiWidth;
199     This->height = uiHeight;
200
201 end:
202     LeaveCriticalSection(&This->encoder->lock);
203     return hr;
204 }
205
206 static HRESULT WINAPI IcnsFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
207     double dpiX, double dpiY)
208 {
209     IcnsFrameEncode *This = (IcnsFrameEncode*)iface;
210     HRESULT hr = S_OK;
211
212     TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
213
214     EnterCriticalSection(&This->encoder->lock);
215
216     if (!This->initialized || This->icns_image.imageData)
217     {
218         hr = WINCODEC_ERR_WRONGSTATE;
219         goto end;
220     }
221
222 end:
223     LeaveCriticalSection(&This->encoder->lock);
224     return S_OK;
225 }
226
227 static HRESULT WINAPI IcnsFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
228     WICPixelFormatGUID *pPixelFormat)
229 {
230     IcnsFrameEncode *This = (IcnsFrameEncode*)iface;
231     HRESULT hr = S_OK;
232
233     TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
234
235     EnterCriticalSection(&This->encoder->lock);
236
237     if (!This->initialized || This->icns_image.imageData)
238     {
239         hr = WINCODEC_ERR_WRONGSTATE;
240         goto end;
241     }
242
243     memcpy(pPixelFormat, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID));
244
245 end:
246     LeaveCriticalSection(&This->encoder->lock);
247     return S_OK;
248 }
249
250 static HRESULT WINAPI IcnsFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
251     UINT cCount, IWICColorContext **ppIColorContext)
252 {
253     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
254     return E_NOTIMPL;
255 }
256
257 static HRESULT WINAPI IcnsFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
258     IWICPalette *pIPalette)
259 {
260     FIXME("(%p,%p): stub\n", iface, pIPalette);
261     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
262 }
263
264 static HRESULT WINAPI IcnsFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
265     IWICBitmapSource *pIThumbnail)
266 {
267     FIXME("(%p,%p): stub\n", iface, pIThumbnail);
268     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
269 }
270
271 static HRESULT WINAPI IcnsFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
272     UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
273 {
274     IcnsFrameEncode *This = (IcnsFrameEncode*)iface;
275     HRESULT hr = S_OK;
276     UINT i;
277     int ret;
278
279     TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
280
281     EnterCriticalSection(&This->encoder->lock);
282
283     if (!This->initialized || !This->width || !This->height)
284     {
285         hr = WINCODEC_ERR_WRONGSTATE;
286         goto end;
287     }
288     if (lineCount == 0 || lineCount + This->lines_written > This->height)
289     {
290         hr = E_INVALIDARG;
291         goto end;
292     }
293
294     if (!This->icns_image.imageData)
295     {
296         icns_icon_info_t icns_info;
297         icns_info.isImage = 1;
298         icns_info.iconWidth = This->width;
299         icns_info.iconHeight = This->height;
300         icns_info.iconBitDepth = 32;
301         icns_info.iconChannels = 4;
302         icns_info.iconPixelDepth = icns_info.iconBitDepth / icns_info.iconChannels;
303         This->icns_type = picns_get_type_from_image_info(icns_info);
304         if (This->icns_type == ICNS_NULL_TYPE)
305         {
306             WARN("cannot generate ICNS icon from %dx%d image\n", This->width, This->height);
307             hr = E_INVALIDARG;
308             goto end;
309         }
310         ret = picns_init_image_for_type(This->icns_type, &This->icns_image);
311         if (ret != ICNS_STATUS_OK)
312         {
313             WARN("error %d in icns_init_image_for_type\n", ret);
314             hr = E_FAIL;
315             goto end;
316         }
317     }
318
319     for (i = 0; i < lineCount; i++)
320     {
321         BYTE *src_row, *dst_row;
322         UINT j;
323         src_row = pbPixels + cbStride * i;
324         dst_row = This->icns_image.imageData + (This->lines_written + i)*(This->width*4);
325         /* swap bgr -> rgb */
326         for (j = 0; j < This->width*4; j += 4)
327         {
328             dst_row[j] = src_row[j+2];
329             dst_row[j+1] = src_row[j+1];
330             dst_row[j+2] = src_row[j];
331             dst_row[j+3] = src_row[j+3];
332         }
333     }
334     This->lines_written += lineCount;
335
336 end:
337     LeaveCriticalSection(&This->encoder->lock);
338     return hr;
339 }
340
341 static HRESULT WINAPI IcnsFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
342     IWICBitmapSource *pIBitmapSource, WICRect *prc)
343 {
344     IcnsFrameEncode *This = (IcnsFrameEncode*)iface;
345     HRESULT hr;
346     WICRect rc;
347     WICPixelFormatGUID guid;
348     UINT stride;
349     BYTE *pixeldata = NULL;
350
351     TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc);
352
353     if (!This->initialized || !This->width || !This->height)
354     {
355         hr = WINCODEC_ERR_WRONGSTATE;
356         goto end;
357     }
358
359     hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid);
360     if (FAILED(hr))
361         goto end;
362     if (!IsEqualGUID(&guid, &GUID_WICPixelFormat32bppBGRA))
363     {
364         FIXME("format %s unsupported, could use WICConvertBitmapSource to convert\n", debugstr_guid(&guid));
365         hr = E_FAIL;
366         goto end;
367     }
368
369     if (!prc)
370     {
371         UINT width, height;
372         hr = IWICBitmapSource_GetSize(pIBitmapSource, &width, &height);
373         if (FAILED(hr))
374             goto end;
375         rc.X = 0;
376         rc.Y = 0;
377         rc.Width = width;
378         rc.Height = height;
379         prc = &rc;
380     }
381
382     if (prc->Width != This->width)
383     {
384         hr = E_INVALIDARG;
385         goto end;
386     }
387
388     stride = (32 * This->width + 7)/8;
389     pixeldata = HeapAlloc(GetProcessHeap(), 0, stride * prc->Height);
390     if (!pixeldata)
391     {
392         hr = E_OUTOFMEMORY;
393         goto end;
394     }
395
396     hr = IWICBitmapSource_CopyPixels(pIBitmapSource, prc, stride,
397         stride*prc->Height, pixeldata);
398     if (SUCCEEDED(hr))
399     {
400         hr = IWICBitmapFrameEncode_WritePixels(iface, prc->Height, stride,
401             stride*prc->Height, pixeldata);
402     }
403
404 end:
405     HeapFree(GetProcessHeap(), 0, pixeldata);
406     return hr;
407 }
408
409 static HRESULT WINAPI IcnsFrameEncode_Commit(IWICBitmapFrameEncode *iface)
410 {
411     IcnsFrameEncode *This = (IcnsFrameEncode*)iface;
412     icns_element_t *icns_element = NULL;
413     icns_image_t mask;
414     icns_element_t *mask_element = NULL;
415     int ret;
416     int i;
417     HRESULT hr = S_OK;
418
419     TRACE("(%p): stub\n", iface);
420
421     memset(&mask, 0, sizeof(mask));
422
423     EnterCriticalSection(&This->encoder->lock);
424
425     if (!This->icns_image.imageData || This->lines_written != This->height || This->committed)
426     {
427         hr = WINCODEC_ERR_WRONGSTATE;
428         goto end;
429     }
430
431     ret = picns_new_element_from_image(&This->icns_image, This->icns_type, &icns_element);
432     if (ret != ICNS_STATUS_OK && icns_element != NULL)
433     {
434         WARN("icns_new_element_from_image failed with error %d\n", ret);
435         hr = E_FAIL;
436         goto end;
437     }
438
439     if (This->icns_type != ICNS_512x512_32BIT_ARGB_DATA && This->icns_type != ICNS_256x256_32BIT_ARGB_DATA)
440     {
441         /* we need to write the mask too */
442         ret = picns_init_image_for_type(picns_get_mask_type_for_icon_type(This->icns_type), &mask);
443         if (ret != ICNS_STATUS_OK)
444         {
445             WARN("icns_init_image_from_type failed to make mask, error %d\n", ret);
446             hr = E_FAIL;
447             goto end;
448         }
449         for (i = 0; i < mask.imageHeight; i++)
450         {
451             int j;
452             for (j = 0; j < mask.imageWidth; j++)
453                 mask.imageData[i*mask.imageWidth + j] = This->icns_image.imageData[i*mask.imageWidth*4 + j*4 + 3];
454         }
455         ret = picns_new_element_from_image(&mask, picns_get_mask_type_for_icon_type(This->icns_type), &mask_element);
456         if (ret != ICNS_STATUS_OK)
457         {
458             WARN("icns_new_element_from image failed to make element from mask, error %d\n", ret);
459             hr = E_FAIL;
460             goto end;
461         }
462     }
463
464     ret = picns_set_element_in_family(&This->encoder->icns_family, icns_element);
465     if (ret != ICNS_STATUS_OK)
466     {
467         WARN("icns_set_element_in_family failed for image with error %d\n", ret);
468         hr = E_FAIL;
469         goto end;
470     }
471
472     if (mask_element)
473     {
474         ret = picns_set_element_in_family(&This->encoder->icns_family, mask_element);
475         if (ret != ICNS_STATUS_OK)
476         {
477             WARN("icns_set_element_in_family failed for mask with error %d\n", ret);
478             hr = E_FAIL;
479             goto end;
480         }
481     }
482
483     This->committed = TRUE;
484     This->encoder->any_frame_committed = TRUE;
485     This->encoder->outstanding_commits--;
486
487 end:
488     LeaveCriticalSection(&This->encoder->lock);
489     picns_free_image(&mask);
490     free(icns_element);
491     free(mask_element);
492     return hr;
493 }
494
495 static HRESULT WINAPI IcnsFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
496     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
497 {
498     FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
499     return E_NOTIMPL;
500 }
501
502 static const IWICBitmapFrameEncodeVtbl IcnsEncoder_FrameVtbl = {
503     IcnsFrameEncode_QueryInterface,
504     IcnsFrameEncode_AddRef,
505     IcnsFrameEncode_Release,
506     IcnsFrameEncode_Initialize,
507     IcnsFrameEncode_SetSize,
508     IcnsFrameEncode_SetResolution,
509     IcnsFrameEncode_SetPixelFormat,
510     IcnsFrameEncode_SetColorContexts,
511     IcnsFrameEncode_SetPalette,
512     IcnsFrameEncode_SetThumbnail,
513     IcnsFrameEncode_WritePixels,
514     IcnsFrameEncode_WriteSource,
515     IcnsFrameEncode_Commit,
516     IcnsFrameEncode_GetMetadataQueryWriter
517 };
518
519 static HRESULT WINAPI IcnsEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
520     void **ppv)
521 {
522     IcnsEncoder *This = (IcnsEncoder*)iface;
523     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
524
525     if (!ppv) return E_INVALIDARG;
526
527     if (IsEqualIID(&IID_IUnknown, iid) ||
528         IsEqualIID(&IID_IWICBitmapEncoder, iid))
529     {
530         *ppv = This;
531     }
532     else
533     {
534         *ppv = NULL;
535         return E_NOINTERFACE;
536     }
537
538     IUnknown_AddRef((IUnknown*)*ppv);
539     return S_OK;
540 }
541
542 static ULONG WINAPI IcnsEncoder_AddRef(IWICBitmapEncoder *iface)
543 {
544     IcnsEncoder *This = (IcnsEncoder*)iface;
545     ULONG ref = InterlockedIncrement(&This->ref);
546
547     TRACE("(%p) refcount=%u\n", iface, ref);
548
549     return ref;
550 }
551
552 static ULONG WINAPI IcnsEncoder_Release(IWICBitmapEncoder *iface)
553 {
554     IcnsEncoder *This = (IcnsEncoder*)iface;
555     ULONG ref = InterlockedDecrement(&This->ref);
556
557     TRACE("(%p) refcount=%u\n", iface, ref);
558
559     if (ref == 0)
560     {
561         This->lock.DebugInfo->Spare[0] = 0;
562         DeleteCriticalSection(&This->lock);
563         if (This->icns_family)
564             free(This->icns_family);
565         if (This->stream)
566             IStream_Release(This->stream);
567         HeapFree(GetProcessHeap(), 0, This);
568     }
569
570     return ref;
571 }
572
573 static HRESULT WINAPI IcnsEncoder_Initialize(IWICBitmapEncoder *iface,
574     IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
575 {
576     IcnsEncoder *This = (IcnsEncoder*)iface;
577     int ret;
578     HRESULT hr = S_OK;
579
580     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
581
582     EnterCriticalSection(&This->lock);
583
584     if (This->icns_family)
585     {
586         hr = WINCODEC_ERR_WRONGSTATE;
587         goto end;
588     }
589     ret = picns_create_family(&This->icns_family);
590     if (ret != ICNS_STATUS_OK)
591     {
592         WARN("error %d creating icns family\n", ret);
593         hr = E_FAIL;
594         goto end;
595     }
596     IStream_AddRef(pIStream);
597     This->stream = pIStream;
598
599 end:
600     LeaveCriticalSection(&This->lock);
601
602     return hr;
603 }
604
605 static HRESULT WINAPI IcnsEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
606     GUID *pguidContainerFormat)
607 {
608     FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
609     return E_NOTIMPL;
610 }
611
612 static HRESULT WINAPI IcnsEncoder_GetEncoderInfo(IWICBitmapEncoder *iface,
613     IWICBitmapEncoderInfo **ppIEncoderInfo)
614 {
615     FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo);
616     return E_NOTIMPL;
617 }
618
619 static HRESULT WINAPI IcnsEncoder_SetColorContexts(IWICBitmapEncoder *iface,
620     UINT cCount, IWICColorContext **ppIColorContext)
621 {
622     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
623     return E_NOTIMPL;
624 }
625
626 static HRESULT WINAPI IcnsEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette)
627 {
628     TRACE("(%p,%p)\n", iface, pIPalette);
629     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
630 }
631
632 static HRESULT WINAPI IcnsEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
633 {
634     TRACE("(%p,%p)\n", iface, pIThumbnail);
635     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
636 }
637
638 static HRESULT WINAPI IcnsEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
639 {
640     TRACE("(%p,%p)\n", iface, pIPreview);
641     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
642 }
643
644 static HRESULT WINAPI IcnsEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
645     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
646 {
647     IcnsEncoder *This = (IcnsEncoder*)iface;
648     HRESULT hr = S_OK;
649     IcnsFrameEncode *frameEncode = NULL;
650
651     TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
652
653     EnterCriticalSection(&This->lock);
654
655     if (!This->icns_family)
656     {
657         hr = WINCODEC_ERR_NOTINITIALIZED;
658         goto end;
659     }
660
661     hr = CreatePropertyBag2(ppIEncoderOptions);
662     if (FAILED(hr))
663         goto end;
664
665     frameEncode = HeapAlloc(GetProcessHeap(), 0, sizeof(IcnsFrameEncode));
666     if (frameEncode == NULL)
667     {
668         hr = E_OUTOFMEMORY;
669         goto end;
670     }
671     frameEncode->lpVtbl = &IcnsEncoder_FrameVtbl;
672     frameEncode->encoder = This;
673     frameEncode->ref = 1;
674     frameEncode->initialized = FALSE;
675     frameEncode->width = 0;
676     frameEncode->height = 0;
677     memset(&frameEncode->icns_image, 0, sizeof(icns_image_t));
678     frameEncode->lines_written = 0;
679     frameEncode->committed = FALSE;
680     *ppIFrameEncode = (IWICBitmapFrameEncode*)frameEncode;
681     This->outstanding_commits++;
682     IUnknown_AddRef((IUnknown*)This);
683
684 end:
685     LeaveCriticalSection(&This->lock);
686
687     return hr;
688 }
689
690 static HRESULT WINAPI IcnsEncoder_Commit(IWICBitmapEncoder *iface)
691 {
692     IcnsEncoder *This = (IcnsEncoder*)iface;
693     icns_byte_t *buffer = NULL;
694     icns_size_t buffer_size;
695     int ret;
696     HRESULT hr = S_OK;
697     ULONG byteswritten;
698
699     TRACE("(%p)\n", iface);
700
701     EnterCriticalSection(&This->lock);
702
703     if (!This->any_frame_committed || This->outstanding_commits > 0 || This->committed)
704     {
705         hr = WINCODEC_ERR_WRONGSTATE;
706         goto end;
707     }
708
709     ret = picns_export_family_data(This->icns_family, &buffer_size, &buffer);
710     if (ret != ICNS_STATUS_OK)
711     {
712         WARN("icns_export_family_data failed with error %d\n", ret);
713         hr = E_FAIL;
714         goto end;
715     }
716     hr = IStream_Write(This->stream, buffer, buffer_size, &byteswritten);
717     if (FAILED(hr) || byteswritten != buffer_size)
718     {
719         WARN("writing file failed, hr = 0x%08X\n", hr);
720         hr = E_FAIL;
721         goto end;
722     }
723
724     This->committed = TRUE;
725
726 end:
727     LeaveCriticalSection(&This->lock);
728     free(buffer);
729     return hr;
730 }
731
732 static HRESULT WINAPI IcnsEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
733     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
734 {
735     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
736     return E_NOTIMPL;
737 }
738
739 static const IWICBitmapEncoderVtbl IcnsEncoder_Vtbl = {
740     IcnsEncoder_QueryInterface,
741     IcnsEncoder_AddRef,
742     IcnsEncoder_Release,
743     IcnsEncoder_Initialize,
744     IcnsEncoder_GetContainerFormat,
745     IcnsEncoder_GetEncoderInfo,
746     IcnsEncoder_SetColorContexts,
747     IcnsEncoder_SetPalette,
748     IcnsEncoder_SetThumbnail,
749     IcnsEncoder_SetPreview,
750     IcnsEncoder_CreateNewFrame,
751     IcnsEncoder_Commit,
752     IcnsEncoder_GetMetadataQueryWriter
753 };
754
755 HRESULT IcnsEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
756 {
757     IcnsEncoder *This;
758     HRESULT ret;
759
760     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
761
762     *ppv = NULL;
763
764     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
765
766     if (!libicns_handle && !load_libicns())
767     {
768         ERR("Failed writing ICNS because unable to find %s\n",SONAME_LIBICNS);
769         return E_FAIL;
770     }
771
772     This = HeapAlloc(GetProcessHeap(), 0, sizeof(IcnsEncoder));
773     if (!This) return E_OUTOFMEMORY;
774
775     This->lpVtbl = &IcnsEncoder_Vtbl;
776     This->ref = 1;
777     This->stream = NULL;
778     This->icns_family = NULL;
779     This->any_frame_committed = FALSE;
780     This->outstanding_commits = 0;
781     This->committed = FALSE;
782     InitializeCriticalSection(&This->lock);
783     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IcnsEncoder.lock");
784
785     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
786     IUnknown_Release((IUnknown*)This);
787
788     return ret;
789 }
790
791 #else /* !defined(SONAME_LIBICNS) */
792
793 HRESULT IcnsEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
794 {
795     ERR("Trying to save ICNS picture, but ICNS support is not compiled in.\n");
796     return E_FAIL;
797 }
798
799 #endif