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_APPLICATIONSERVICES_APPLICATIONSERVICES_H
25 #define GetCurrentProcess GetCurrentProcess_Mac
26 #define GetCurrentThread GetCurrentThread_Mac
27 #define LoadResource LoadResource_Mac
28 #define AnimatePalette AnimatePalette_Mac
29 #define EqualRgn EqualRgn_Mac
30 #define FillRgn FillRgn_Mac
31 #define FrameRgn FrameRgn_Mac
32 #define GetPixel GetPixel_Mac
33 #define InvertRgn InvertRgn_Mac
34 #define LineTo LineTo_Mac
35 #define OffsetRgn OffsetRgn_Mac
36 #define PaintRgn PaintRgn_Mac
37 #define Polygon Polygon_Mac
38 #define ResizePalette ResizePalette_Mac
39 #define SetRectRgn SetRectRgn_Mac
40 #define EqualRect EqualRect_Mac
41 #define FillRect FillRect_Mac
42 #define FrameRect FrameRect_Mac
43 #define GetCursor GetCursor_Mac
44 #define InvertRect InvertRect_Mac
45 #define OffsetRect OffsetRect_Mac
46 #define PtInRect PtInRect_Mac
47 #define SetCursor SetCursor_Mac
48 #define SetRect SetRect_Mac
49 #define ShowCursor ShowCursor_Mac
50 #define UnionRect UnionRect_Mac
51 #include <ApplicationServices/ApplicationServices.h>
52 #undef GetCurrentProcess
53 #undef GetCurrentThread
54 #undef LoadResource
55 #undef AnimatePalette
56 #undef EqualRgn
57 #undef FillRgn
58 #undef FrameRgn
59 #undef GetPixel
60 #undef InvertRgn
61 #undef LineTo
62 #undef OffsetRgn
63 #undef PaintRgn
64 #undef Polygon
65 #undef ResizePalette
66 #undef SetRectRgn
67 #undef EqualRect
68 #undef FillRect
69 #undef FrameRect
70 #undef GetCursor
71 #undef InvertRect
72 #undef OffsetRect
73 #undef PtInRect
74 #undef SetCursor
75 #undef SetRect
76 #undef ShowCursor
77 #undef UnionRect
78 #endif
79 
80 #define COBJMACROS
81 
82 #include "windef.h"
83 #include "winbase.h"
84 #include "objbase.h"
85 
86 #include "wincodecs_private.h"
87 
88 #include "wine/debug.h"
89 #include "wine/library.h"
90 
91 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
92 
93 #if defined(HAVE_APPLICATIONSERVICES_APPLICATIONSERVICES_H) && \
94     MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
95 
96 typedef struct IcnsEncoder {
97     IWICBitmapEncoder IWICBitmapEncoder_iface;
98     LONG ref;
99     IStream *stream;
100     IconFamilyHandle icns_family;
101     BOOL any_frame_committed;
102     int outstanding_commits;
103     BOOL committed;
104     CRITICAL_SECTION lock;
105 } IcnsEncoder;
106 
107 static inline IcnsEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
108 {
109     return CONTAINING_RECORD(iface, IcnsEncoder, IWICBitmapEncoder_iface);
110 }
111 
112 typedef struct IcnsFrameEncode {
113     IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
114     IcnsEncoder *encoder;
115     LONG ref;
116     BOOL initialized;
117     UINT size;
118     OSType icns_type;
119     BYTE* icns_image;
120     int lines_written;
121     BOOL committed;
122 } IcnsFrameEncode;
123 
124 static inline IcnsFrameEncode *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
125 {
126     return CONTAINING_RECORD(iface, IcnsFrameEncode, IWICBitmapFrameEncode_iface);
127 }
128 
129 static HRESULT WINAPI IcnsFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
130     void **ppv)
131 {
132     IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
133     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
134 
135     if (!ppv) return E_INVALIDARG;
136 
137     if (IsEqualIID(&IID_IUnknown, iid) ||
138         IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
139     {
140         *ppv = &This->IWICBitmapFrameEncode_iface;
141     }
142     else
143     {
144         *ppv = NULL;
145         return E_NOINTERFACE;
146     }
147 
148     IUnknown_AddRef((IUnknown*)*ppv);
149     return S_OK;
150 }
151 
152 static ULONG WINAPI IcnsFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
153 {
154     IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
155     ULONG ref = InterlockedIncrement(&This->ref);
156 
157     TRACE("(%p) refcount=%u\n", iface, ref);
158 
159     return ref;
160 }
161 
162 static ULONG WINAPI IcnsFrameEncode_Release(IWICBitmapFrameEncode *iface)
163 {
164     IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
165     ULONG ref = InterlockedDecrement(&This->ref);
166 
167     TRACE("(%p) refcount=%u\n", iface, ref);
168 
169     if (ref == 0)
170     {
171         if (!This->committed)
172         {
173             EnterCriticalSection(&This->encoder->lock);
174             This->encoder->outstanding_commits--;
175             LeaveCriticalSection(&This->encoder->lock);
176         }
177         HeapFree(GetProcessHeap(), 0, This->icns_image);
178 
179         IWICBitmapEncoder_Release(&This->encoder->IWICBitmapEncoder_iface);
180         HeapFree(GetProcessHeap(), 0, This);
181     }
182 
183     return ref;
184 }
185 
186 static HRESULT WINAPI IcnsFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
187     IPropertyBag2 *pIEncoderOptions)
188 {
189     IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
190     HRESULT hr = S_OK;
191 
192     TRACE("(%p,%p)\n", iface, pIEncoderOptions);
193 
194     EnterCriticalSection(&This->encoder->lock);
195 
196     if (This->initialized)
197     {
198         hr = WINCODEC_ERR_WRONGSTATE;
199         goto end;
200     }
201     This->initialized = TRUE;
202 
203 end:
204     LeaveCriticalSection(&This->encoder->lock);
205     return hr;
206 }
207 
208 static HRESULT WINAPI IcnsFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
209     UINT uiWidth, UINT uiHeight)
210 {
211     IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
212     HRESULT hr = S_OK;
213 
214     TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
215 
216     EnterCriticalSection(&This->encoder->lock);
217 
218     if (!This->initialized || This->icns_image)
219     {
220         hr = WINCODEC_ERR_WRONGSTATE;
221         goto end;
222     }
223 
224     if (uiWidth != uiHeight)
225     {
226         WARN("cannot generate ICNS icon from %dx%d image\n", uiWidth, uiHeight);
227         hr = E_INVALIDARG;
228         goto end;
229     }
230 
231     switch (uiWidth)
232     {
233         case 16:
234         case 32:
235         case 48:
236         case 128:
237         case 256:
238         case 512:
239             break;
240         default:
241             WARN("cannot generate ICNS icon from %dx%d image\n", This->size, This->size);
242             hr = E_INVALIDARG;
243             goto end;
244     }
245 
246     This->size = uiWidth;
247 
248 end:
249     LeaveCriticalSection(&This->encoder->lock);
250     return hr;
251 }
252 
253 static HRESULT WINAPI IcnsFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
254     double dpiX, double dpiY)
255 {
256     IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
257     HRESULT hr = S_OK;
258 
259     TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
260 
261     EnterCriticalSection(&This->encoder->lock);
262 
263     if (!This->initialized || This->icns_image)
264     {
265         hr = WINCODEC_ERR_WRONGSTATE;
266         goto end;
267     }
268 
269 end:
270     LeaveCriticalSection(&This->encoder->lock);
271     return S_OK;
272 }
273 
274 static HRESULT WINAPI IcnsFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
275     WICPixelFormatGUID *pPixelFormat)
276 {
277     IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
278     HRESULT hr = S_OK;
279 
280     TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
281 
282     EnterCriticalSection(&This->encoder->lock);
283 
284     if (!This->initialized || This->icns_image)
285     {
286         hr = WINCODEC_ERR_WRONGSTATE;
287         goto end;
288     }
289 
290     memcpy(pPixelFormat, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID));
291 
292 end:
293     LeaveCriticalSection(&This->encoder->lock);
294     return S_OK;
295 }
296 
297 static HRESULT WINAPI IcnsFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
298     UINT cCount, IWICColorContext **ppIColorContext)
299 {
300     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
301     return E_NOTIMPL;
302 }
303 
304 static HRESULT WINAPI IcnsFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
305     IWICPalette *pIPalette)
306 {
307     FIXME("(%p,%p): stub\n", iface, pIPalette);
308     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
309 }
310 
311 static HRESULT WINAPI IcnsFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
312     IWICBitmapSource *pIThumbnail)
313 {
314     FIXME("(%p,%p): stub\n", iface, pIThumbnail);
315     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
316 }
317 
318 static HRESULT WINAPI IcnsFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
319     UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
320 {
321     IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
322     HRESULT hr = S_OK;
323     UINT i;
324 
325     TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
326 
327     EnterCriticalSection(&This->encoder->lock);
328 
329     if (!This->initialized || !This->size)
330     {
331         hr = WINCODEC_ERR_WRONGSTATE;
332         goto end;
333     }
334     if (lineCount == 0 || lineCount + This->lines_written > This->size)
335     {
336         hr = E_INVALIDARG;
337         goto end;
338     }
339 
340     if (!This->icns_image)
341     {
342         switch (This->size)
343         {
344             case 16:  This->icns_type = kIconServices16PixelDataARGB;  break;
345             case 32:  This->icns_type = kIconServices32PixelDataARGB;  break;
346             case 48:  This->icns_type = kIconServices48PixelDataARGB;  break;
347             case 128: This->icns_type = kIconServices128PixelDataARGB; break;
348             case 256: This->icns_type = kIconServices256PixelDataARGB; break;
349             case 512: This->icns_type = kIconServices512PixelDataARGB; break;
350             default:
351                 WARN("cannot generate ICNS icon from %dx%d image\n", This->size, This->size);
352                 hr = E_INVALIDARG;
353                 goto end;
354         }
355         This->icns_image = HeapAlloc(GetProcessHeap(), 0, This->size * This->size * 4);
356         if (!This->icns_image)
357         {
358             WARN("failed to allocate image buffer\n");
359             hr = E_FAIL;
360             goto end;
361         }
362     }
363 
364     for (i = 0; i < lineCount; i++)
365     {
366         BYTE *src_row, *dst_row;
367         UINT j;
368         src_row = pbPixels + cbStride * i;
369         dst_row = This->icns_image + (This->lines_written + i)*(This->size*4);
370         /* swap bgr -> rgb */
371         for (j = 0; j < This->size*4; j += 4)
372         {
373             dst_row[j] = src_row[j+3];
374             dst_row[j+1] = src_row[j+2];
375             dst_row[j+2] = src_row[j+1];
376             dst_row[j+3] = src_row[j];
377         }
378     }
379     This->lines_written += lineCount;
380 
381 end:
382     LeaveCriticalSection(&This->encoder->lock);
383     return hr;
384 }
385 
386 static HRESULT WINAPI IcnsFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
387     IWICBitmapSource *pIBitmapSource, WICRect *prc)
388 {
389     IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
390     HRESULT hr;
391 
392     TRACE("(%p,%p,%s)\n", iface, pIBitmapSource, debug_wic_rect(prc));
393 
394     if (!This->initialized)
395         return WINCODEC_ERR_WRONGSTATE;
396 
397     hr = configure_write_source(iface, pIBitmapSource, prc,
398         &GUID_WICPixelFormat32bppBGRA, This->size, This->size,
399         1.0, 1.0);
400 
401     if (SUCCEEDED(hr))
402     {
403         hr = write_source(iface, pIBitmapSource, prc,
404             &GUID_WICPixelFormat32bppBGRA, 32, This->size, This->size);
405     }
406 
407     return hr;
408 }
409 
410 static HRESULT WINAPI IcnsFrameEncode_Commit(IWICBitmapFrameEncode *iface)
411 {
412     IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
413     Handle handle;
414     OSErr ret;
415     HRESULT hr = S_OK;
416 
417     TRACE("(%p)\n", iface);
418 
419     EnterCriticalSection(&This->encoder->lock);
420 
421     if (!This->icns_image || This->lines_written != This->size || This->committed)
422     {
423         hr = WINCODEC_ERR_WRONGSTATE;
424         goto end;
425     }
426 
427     ret = PtrToHand(This->icns_image, &handle, This->size * This->size * 4);
428     if (ret != noErr || !handle)
429     {
430         WARN("PtrToHand failed with error %d\n", ret);
431         hr = E_FAIL;
432         goto end;
433     }
434 
435     ret = SetIconFamilyData(This->encoder->icns_family, This->icns_type, handle);
436     DisposeHandle(handle);
437 
438     if (ret != noErr)
439 	{
440         WARN("SetIconFamilyData failed for image with error %d\n", ret);
441         hr = E_FAIL;
442         goto end;
443 	}
444 
445     This->committed = TRUE;
446     This->encoder->any_frame_committed = TRUE;
447     This->encoder->outstanding_commits--;
448 
449 end:
450     LeaveCriticalSection(&This->encoder->lock);
451     return hr;
452 }
453 
454 static HRESULT WINAPI IcnsFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
455     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
456 {
457     FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
458     return E_NOTIMPL;
459 }
460 
461 static const IWICBitmapFrameEncodeVtbl IcnsEncoder_FrameVtbl = {
462     IcnsFrameEncode_QueryInterface,
463     IcnsFrameEncode_AddRef,
464     IcnsFrameEncode_Release,
465     IcnsFrameEncode_Initialize,
466     IcnsFrameEncode_SetSize,
467     IcnsFrameEncode_SetResolution,
468     IcnsFrameEncode_SetPixelFormat,
469     IcnsFrameEncode_SetColorContexts,
470     IcnsFrameEncode_SetPalette,
471     IcnsFrameEncode_SetThumbnail,
472     IcnsFrameEncode_WritePixels,
473     IcnsFrameEncode_WriteSource,
474     IcnsFrameEncode_Commit,
475     IcnsFrameEncode_GetMetadataQueryWriter
476 };
477 
478 static HRESULT WINAPI IcnsEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
479     void **ppv)
480 {
481     IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface);
482     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
483 
484     if (!ppv) return E_INVALIDARG;
485 
486     if (IsEqualIID(&IID_IUnknown, iid) ||
487         IsEqualIID(&IID_IWICBitmapEncoder, iid))
488     {
489         *ppv = &This->IWICBitmapEncoder_iface;
490     }
491     else
492     {
493         *ppv = NULL;
494         return E_NOINTERFACE;
495     }
496 
497     IUnknown_AddRef((IUnknown*)*ppv);
498     return S_OK;
499 }
500 
501 static ULONG WINAPI IcnsEncoder_AddRef(IWICBitmapEncoder *iface)
502 {
503     IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface);
504     ULONG ref = InterlockedIncrement(&This->ref);
505 
506     TRACE("(%p) refcount=%u\n", iface, ref);
507 
508     return ref;
509 }
510 
511 static ULONG WINAPI IcnsEncoder_Release(IWICBitmapEncoder *iface)
512 {
513     IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface);
514     ULONG ref = InterlockedDecrement(&This->ref);
515 
516     TRACE("(%p) refcount=%u\n", iface, ref);
517 
518     if (ref == 0)
519     {
520         This->lock.DebugInfo->Spare[0] = 0;
521         DeleteCriticalSection(&This->lock);
522         if (This->icns_family)
523             DisposeHandle((Handle)This->icns_family);
524         if (This->stream)
525             IStream_Release(This->stream);
526         HeapFree(GetProcessHeap(), 0, This);
527     }
528 
529     return ref;
530 }
531 
532 static HRESULT WINAPI IcnsEncoder_Initialize(IWICBitmapEncoder *iface,
533     IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
534 {
535     IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface);
536     HRESULT hr = S_OK;
537 
538     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
539 
540     EnterCriticalSection(&This->lock);
541 
542     if (This->icns_family)
543     {
544         hr = WINCODEC_ERR_WRONGSTATE;
545         goto end;
546     }
547     This->icns_family = (IconFamilyHandle)NewHandle(0);
548     if (!This->icns_family)
549     {
550         WARN("error creating icns family\n");
551         hr = E_FAIL;
552         goto end;
553     }
554     IStream_AddRef(pIStream);
555     This->stream = pIStream;
556 
557 end:
558     LeaveCriticalSection(&This->lock);
559 
560     return hr;
561 }
562 
563 static HRESULT WINAPI IcnsEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
564     GUID *pguidContainerFormat)
565 {
566     FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
567     return E_NOTIMPL;
568 }
569 
570 static HRESULT WINAPI IcnsEncoder_GetEncoderInfo(IWICBitmapEncoder *iface,
571     IWICBitmapEncoderInfo **ppIEncoderInfo)
572 {
573     FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo);
574     return E_NOTIMPL;
575 }
576 
577 static HRESULT WINAPI IcnsEncoder_SetColorContexts(IWICBitmapEncoder *iface,
578     UINT cCount, IWICColorContext **ppIColorContext)
579 {
580     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
581     return E_NOTIMPL;
582 }
583 
584 static HRESULT WINAPI IcnsEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette)
585 {
586     TRACE("(%p,%p)\n", iface, pIPalette);
587     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
588 }
589 
590 static HRESULT WINAPI IcnsEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
591 {
592     TRACE("(%p,%p)\n", iface, pIThumbnail);
593     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
594 }
595 
596 static HRESULT WINAPI IcnsEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
597 {
598     TRACE("(%p,%p)\n", iface, pIPreview);
599     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
600 }
601 
602 static HRESULT WINAPI IcnsEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
603     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
604 {
605     IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface);
606     HRESULT hr = S_OK;
607     IcnsFrameEncode *frameEncode = NULL;
608 
609     TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
610 
611     EnterCriticalSection(&This->lock);
612 
613     if (!This->icns_family)
614     {
615         hr = WINCODEC_ERR_NOTINITIALIZED;
616         goto end;
617     }
618 
619     if (ppIEncoderOptions)
620     {
621         hr = CreatePropertyBag2(NULL, 0, ppIEncoderOptions);
622         if (FAILED(hr))
623             goto end;
624     }
625 
626     frameEncode = HeapAlloc(GetProcessHeap(), 0, sizeof(IcnsFrameEncode));
627     if (frameEncode == NULL)
628     {
629         hr = E_OUTOFMEMORY;
630         goto end;
631     }
632     frameEncode->IWICBitmapFrameEncode_iface.lpVtbl = &IcnsEncoder_FrameVtbl;
633     frameEncode->encoder = This;
634     frameEncode->ref = 1;
635     frameEncode->initialized = FALSE;
636     frameEncode->size = 0;
637     frameEncode->icns_image = NULL;
638     frameEncode->lines_written = 0;
639     frameEncode->committed = FALSE;
640     *ppIFrameEncode = &frameEncode->IWICBitmapFrameEncode_iface;
641     This->outstanding_commits++;
642     IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface);
643 
644 end:
645     LeaveCriticalSection(&This->lock);
646 
647     return hr;
648 }
649 
650 static HRESULT WINAPI IcnsEncoder_Commit(IWICBitmapEncoder *iface)
651 {
652     IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface);
653     size_t buffer_size;
654     HRESULT hr = S_OK;
655     ULONG byteswritten;
656 
657     TRACE("(%p)\n", iface);
658 
659     EnterCriticalSection(&This->lock);
660 
661     if (!This->any_frame_committed || This->outstanding_commits > 0 || This->committed)
662     {
663         hr = WINCODEC_ERR_WRONGSTATE;
664         goto end;
665     }
666 
667     buffer_size = GetHandleSize((Handle)This->icns_family);
668     hr = IStream_Write(This->stream, *This->icns_family, buffer_size, &byteswritten);
669     if (FAILED(hr) || byteswritten != buffer_size)
670     {
671         WARN("writing file failed, hr = 0x%08X\n", hr);
672         hr = E_FAIL;
673         goto end;
674     }
675 
676     This->committed = TRUE;
677 
678 end:
679     LeaveCriticalSection(&This->lock);
680     return hr;
681 }
682 
683 static HRESULT WINAPI IcnsEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
684     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
685 {
686     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
687     return E_NOTIMPL;
688 }
689 
690 static const IWICBitmapEncoderVtbl IcnsEncoder_Vtbl = {
691     IcnsEncoder_QueryInterface,
692     IcnsEncoder_AddRef,
693     IcnsEncoder_Release,
694     IcnsEncoder_Initialize,
695     IcnsEncoder_GetContainerFormat,
696     IcnsEncoder_GetEncoderInfo,
697     IcnsEncoder_SetColorContexts,
698     IcnsEncoder_SetPalette,
699     IcnsEncoder_SetThumbnail,
700     IcnsEncoder_SetPreview,
701     IcnsEncoder_CreateNewFrame,
702     IcnsEncoder_Commit,
703     IcnsEncoder_GetMetadataQueryWriter
704 };
705 
706 HRESULT IcnsEncoder_CreateInstance(REFIID iid, void** ppv)
707 {
708     IcnsEncoder *This;
709     HRESULT ret;
710 
711     TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
712 
713     *ppv = NULL;
714 
715     This = HeapAlloc(GetProcessHeap(), 0, sizeof(IcnsEncoder));
716     if (!This) return E_OUTOFMEMORY;
717 
718     This->IWICBitmapEncoder_iface.lpVtbl = &IcnsEncoder_Vtbl;
719     This->ref = 1;
720     This->stream = NULL;
721     This->icns_family = NULL;
722     This->any_frame_committed = FALSE;
723     This->outstanding_commits = 0;
724     This->committed = FALSE;
725     InitializeCriticalSection(&This->lock);
726     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IcnsEncoder.lock");
727 
728     ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
729     IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
730 
731     return ret;
732 }
733 
734 #else /* !defined(HAVE_APPLICATIONSERVICES_APPLICATIONSERVICES_H) ||
735          MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 */
736 
737 HRESULT IcnsEncoder_CreateInstance(REFIID iid, void** ppv)
738 {
739     ERR("Trying to save ICNS picture, but ICNS support is not compiled in.\n");
740     return E_FAIL;
741 }
742 
743 #endif
744