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