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