/* * Copyright 2010 Damjan Jovanovic * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include "wine/port.h" #include <stdarg.h> #ifdef HAVE_APPLICATIONSERVICES_APPLICATIONSERVICES_H #define GetCurrentProcess GetCurrentProcess_Mac #define GetCurrentThread GetCurrentThread_Mac #define LoadResource LoadResource_Mac #define EqualRect EqualRect_Mac #define FillRect FillRect_Mac #define FrameRect FrameRect_Mac #define GetCursor GetCursor_Mac #define InvertRect InvertRect_Mac #define OffsetRect OffsetRect_Mac #define PtInRect PtInRect_Mac #define SetCursor SetCursor_Mac #define SetRect SetRect_Mac #define ShowCursor ShowCursor_Mac #define UnionRect UnionRect_Mac #include <ApplicationServices/ApplicationServices.h> #undef GetCurrentProcess #undef GetCurrentThread #undef LoadResource #undef EqualRect #undef FillRect #undef FrameRect #undef GetCursor #undef InvertRect #undef OffsetRect #undef PtInRect #undef SetCursor #undef SetRect #undef ShowCursor #undef UnionRect #undef DPRINTF #endif #define COBJMACROS #include "windef.h" #include "winbase.h" #include "objbase.h" #include "wincodec.h" #include "wincodecs_private.h" #include "wine/debug.h" #include "wine/library.h" WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); #if defined(HAVE_APPLICATIONSERVICES_APPLICATIONSERVICES_H) && \ MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 typedef struct IcnsEncoder { IWICBitmapEncoder IWICBitmapEncoder_iface; LONG ref; IStream *stream; IconFamilyHandle icns_family; BOOL any_frame_committed; int outstanding_commits; BOOL committed; CRITICAL_SECTION lock; } IcnsEncoder; static inline IcnsEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface) { return CONTAINING_RECORD(iface, IcnsEncoder, IWICBitmapEncoder_iface); } typedef struct IcnsFrameEncode { IWICBitmapFrameEncode IWICBitmapFrameEncode_iface; IcnsEncoder *encoder; LONG ref; BOOL initialized; UINT size; OSType icns_type; BYTE* icns_image; int lines_written; BOOL committed; } IcnsFrameEncode; static inline IcnsFrameEncode *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface) { return CONTAINING_RECORD(iface, IcnsFrameEncode, IWICBitmapFrameEncode_iface); } static HRESULT WINAPI IcnsFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid, void **ppv) { IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); if (!ppv) return E_INVALIDARG; if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapFrameEncode, iid)) { *ppv = &This->IWICBitmapFrameEncode_iface; } else { *ppv = NULL; return E_NOINTERFACE; } IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } static ULONG WINAPI IcnsFrameEncode_AddRef(IWICBitmapFrameEncode *iface) { IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); return ref; } static ULONG WINAPI IcnsFrameEncode_Release(IWICBitmapFrameEncode *iface) { IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); if (ref == 0) { if (!This->committed) { EnterCriticalSection(&This->encoder->lock); This->encoder->outstanding_commits--; LeaveCriticalSection(&This->encoder->lock); } if (This->icns_image != NULL) HeapFree(GetProcessHeap(), 0, This->icns_image); IUnknown_Release((IUnknown*)This->encoder); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI IcnsFrameEncode_Initialize(IWICBitmapFrameEncode *iface, IPropertyBag2 *pIEncoderOptions) { IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); HRESULT hr = S_OK; TRACE("(%p,%p)\n", iface, pIEncoderOptions); EnterCriticalSection(&This->encoder->lock); if (This->initialized) { hr = WINCODEC_ERR_WRONGSTATE; goto end; } This->initialized = TRUE; end: LeaveCriticalSection(&This->encoder->lock); return hr; } static HRESULT WINAPI IcnsFrameEncode_SetSize(IWICBitmapFrameEncode *iface, UINT uiWidth, UINT uiHeight) { IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); HRESULT hr = S_OK; TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight); EnterCriticalSection(&This->encoder->lock); if (!This->initialized || This->icns_image) { hr = WINCODEC_ERR_WRONGSTATE; goto end; } if (uiWidth != uiHeight) { WARN("cannot generate ICNS icon from %dx%d image\n", uiWidth, uiHeight); hr = E_INVALIDARG; goto end; } switch (uiWidth) { case 16: case 32: case 48: case 128: case 256: case 512: break; default: WARN("cannot generate ICNS icon from %dx%d image\n", This->size, This->size); hr = E_INVALIDARG; goto end; } This->size = uiWidth; end: LeaveCriticalSection(&This->encoder->lock); return hr; } static HRESULT WINAPI IcnsFrameEncode_SetResolution(IWICBitmapFrameEncode *iface, double dpiX, double dpiY) { IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); HRESULT hr = S_OK; TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY); EnterCriticalSection(&This->encoder->lock); if (!This->initialized || This->icns_image) { hr = WINCODEC_ERR_WRONGSTATE; goto end; } end: LeaveCriticalSection(&This->encoder->lock); return S_OK; } static HRESULT WINAPI IcnsFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface, WICPixelFormatGUID *pPixelFormat) { IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); HRESULT hr = S_OK; TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat)); EnterCriticalSection(&This->encoder->lock); if (!This->initialized || This->icns_image) { hr = WINCODEC_ERR_WRONGSTATE; goto end; } memcpy(pPixelFormat, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID)); end: LeaveCriticalSection(&This->encoder->lock); return S_OK; } static HRESULT WINAPI IcnsFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface, UINT cCount, IWICColorContext **ppIColorContext) { FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext); return E_NOTIMPL; } static HRESULT WINAPI IcnsFrameEncode_SetPalette(IWICBitmapFrameEncode *iface, IWICPalette *pIPalette) { FIXME("(%p,%p): stub\n", iface, pIPalette); return WINCODEC_ERR_UNSUPPORTEDOPERATION; } static HRESULT WINAPI IcnsFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface, IWICBitmapSource *pIThumbnail) { FIXME("(%p,%p): stub\n", iface, pIThumbnail); return WINCODEC_ERR_UNSUPPORTEDOPERATION; } static HRESULT WINAPI IcnsFrameEncode_WritePixels(IWICBitmapFrameEncode *iface, UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels) { IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); HRESULT hr = S_OK; UINT i; TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels); EnterCriticalSection(&This->encoder->lock); if (!This->initialized || !This->size) { hr = WINCODEC_ERR_WRONGSTATE; goto end; } if (lineCount == 0 || lineCount + This->lines_written > This->size) { hr = E_INVALIDARG; goto end; } if (!This->icns_image) { switch (This->size) { case 16: This->icns_type = kIconServices16PixelDataARGB; break; case 32: This->icns_type = kIconServices32PixelDataARGB; break; case 48: This->icns_type = kIconServices48PixelDataARGB; break; case 128: This->icns_type = kIconServices128PixelDataARGB; break; case 256: This->icns_type = kIconServices256PixelDataARGB; break; case 512: This->icns_type = kIconServices512PixelDataARGB; break; default: WARN("cannot generate ICNS icon from %dx%d image\n", This->size, This->size); hr = E_INVALIDARG; goto end; } This->icns_image = HeapAlloc(GetProcessHeap(), 0, This->size * This->size * 4); if (!This->icns_image) { WARN("failed to allocate image buffer\n"); hr = E_FAIL; goto end; } } for (i = 0; i < lineCount; i++) { BYTE *src_row, *dst_row; UINT j; src_row = pbPixels + cbStride * i; dst_row = This->icns_image + (This->lines_written + i)*(This->size*4); /* swap bgr -> rgb */ for (j = 0; j < This->size*4; j += 4) { dst_row[j] = src_row[j+3]; dst_row[j+1] = src_row[j+2]; dst_row[j+2] = src_row[j+1]; dst_row[j+3] = src_row[j]; } } This->lines_written += lineCount; end: LeaveCriticalSection(&This->encoder->lock); return hr; } static HRESULT WINAPI IcnsFrameEncode_WriteSource(IWICBitmapFrameEncode *iface, IWICBitmapSource *pIBitmapSource, WICRect *prc) { IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); HRESULT hr; WICRect rc; WICPixelFormatGUID guid; UINT stride; BYTE *pixeldata = NULL; TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc); if (!This->initialized || !This->size) { hr = WINCODEC_ERR_WRONGSTATE; goto end; } hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid); if (FAILED(hr)) goto end; if (!IsEqualGUID(&guid, &GUID_WICPixelFormat32bppBGRA)) { FIXME("format %s unsupported, could use WICConvertBitmapSource to convert\n", debugstr_guid(&guid)); hr = E_FAIL; goto end; } if (!prc) { UINT width, height; hr = IWICBitmapSource_GetSize(pIBitmapSource, &width, &height); if (FAILED(hr)) goto end; rc.X = 0; rc.Y = 0; rc.Width = width; rc.Height = height; prc = &rc; } if (prc->Width != This->size) { hr = E_INVALIDARG; goto end; } stride = (32 * This->size + 7)/8; pixeldata = HeapAlloc(GetProcessHeap(), 0, stride * prc->Height); if (!pixeldata) { hr = E_OUTOFMEMORY; goto end; } hr = IWICBitmapSource_CopyPixels(pIBitmapSource, prc, stride, stride*prc->Height, pixeldata); if (SUCCEEDED(hr)) { hr = IWICBitmapFrameEncode_WritePixels(iface, prc->Height, stride, stride*prc->Height, pixeldata); } end: HeapFree(GetProcessHeap(), 0, pixeldata); return hr; } static HRESULT WINAPI IcnsFrameEncode_Commit(IWICBitmapFrameEncode *iface) { IcnsFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); Handle handle; OSErr ret; HRESULT hr = S_OK; TRACE("(%p): stub\n", iface); EnterCriticalSection(&This->encoder->lock); if (!This->icns_image || This->lines_written != This->size || This->committed) { hr = WINCODEC_ERR_WRONGSTATE; goto end; } ret = PtrToHand(This->icns_image, &handle, This->size * This->size * 4); if (ret != noErr || !handle) { WARN("PtrToHand failed with error %d\n", ret); hr = E_FAIL; goto end; } ret = SetIconFamilyData(This->encoder->icns_family, This->icns_type, handle); DisposeHandle(handle); if (ret != noErr) { WARN("SetIconFamilyData failed for image with error %d\n", ret); hr = E_FAIL; goto end; } This->committed = TRUE; This->encoder->any_frame_committed = TRUE; This->encoder->outstanding_commits--; end: LeaveCriticalSection(&This->encoder->lock); return hr; } static HRESULT WINAPI IcnsFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface, IWICMetadataQueryWriter **ppIMetadataQueryWriter) { FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter); return E_NOTIMPL; } static const IWICBitmapFrameEncodeVtbl IcnsEncoder_FrameVtbl = { IcnsFrameEncode_QueryInterface, IcnsFrameEncode_AddRef, IcnsFrameEncode_Release, IcnsFrameEncode_Initialize, IcnsFrameEncode_SetSize, IcnsFrameEncode_SetResolution, IcnsFrameEncode_SetPixelFormat, IcnsFrameEncode_SetColorContexts, IcnsFrameEncode_SetPalette, IcnsFrameEncode_SetThumbnail, IcnsFrameEncode_WritePixels, IcnsFrameEncode_WriteSource, IcnsFrameEncode_Commit, IcnsFrameEncode_GetMetadataQueryWriter }; static HRESULT WINAPI IcnsEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid, void **ppv) { IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface); TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); if (!ppv) return E_INVALIDARG; if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapEncoder, iid)) { *ppv = This; } else { *ppv = NULL; return E_NOINTERFACE; } IUnknown_AddRef((IUnknown*)*ppv); return S_OK; } static ULONG WINAPI IcnsEncoder_AddRef(IWICBitmapEncoder *iface) { IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); return ref; } static ULONG WINAPI IcnsEncoder_Release(IWICBitmapEncoder *iface) { IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) refcount=%u\n", iface, ref); if (ref == 0) { This->lock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->lock); if (This->icns_family) DisposeHandle((Handle)This->icns_family); if (This->stream) IStream_Release(This->stream); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI IcnsEncoder_Initialize(IWICBitmapEncoder *iface, IStream *pIStream, WICBitmapEncoderCacheOption cacheOption) { IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface); HRESULT hr = S_OK; TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption); EnterCriticalSection(&This->lock); if (This->icns_family) { hr = WINCODEC_ERR_WRONGSTATE; goto end; } This->icns_family = (IconFamilyHandle)NewHandle(0); if (!This->icns_family) { WARN("error creating icns family\n"); hr = E_FAIL; goto end; } IStream_AddRef(pIStream); This->stream = pIStream; end: LeaveCriticalSection(&This->lock); return hr; } static HRESULT WINAPI IcnsEncoder_GetContainerFormat(IWICBitmapEncoder *iface, GUID *pguidContainerFormat) { FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat)); return E_NOTIMPL; } static HRESULT WINAPI IcnsEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **ppIEncoderInfo) { FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo); return E_NOTIMPL; } static HRESULT WINAPI IcnsEncoder_SetColorContexts(IWICBitmapEncoder *iface, UINT cCount, IWICColorContext **ppIColorContext) { FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext); return E_NOTIMPL; } static HRESULT WINAPI IcnsEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette) { TRACE("(%p,%p)\n", iface, pIPalette); return WINCODEC_ERR_UNSUPPORTEDOPERATION; } static HRESULT WINAPI IcnsEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail) { TRACE("(%p,%p)\n", iface, pIThumbnail); return WINCODEC_ERR_UNSUPPORTEDOPERATION; } static HRESULT WINAPI IcnsEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview) { TRACE("(%p,%p)\n", iface, pIPreview); return WINCODEC_ERR_UNSUPPORTEDOPERATION; } static HRESULT WINAPI IcnsEncoder_CreateNewFrame(IWICBitmapEncoder *iface, IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions) { IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface); HRESULT hr = S_OK; IcnsFrameEncode *frameEncode = NULL; TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions); EnterCriticalSection(&This->lock); if (!This->icns_family) { hr = WINCODEC_ERR_NOTINITIALIZED; goto end; } hr = CreatePropertyBag2(ppIEncoderOptions); if (FAILED(hr)) goto end; frameEncode = HeapAlloc(GetProcessHeap(), 0, sizeof(IcnsFrameEncode)); if (frameEncode == NULL) { hr = E_OUTOFMEMORY; goto end; } frameEncode->IWICBitmapFrameEncode_iface.lpVtbl = &IcnsEncoder_FrameVtbl; frameEncode->encoder = This; frameEncode->ref = 1; frameEncode->initialized = FALSE; frameEncode->size = 0; frameEncode->icns_image = NULL; frameEncode->lines_written = 0; frameEncode->committed = FALSE; *ppIFrameEncode = &frameEncode->IWICBitmapFrameEncode_iface; This->outstanding_commits++; IUnknown_AddRef((IUnknown*)This); end: LeaveCriticalSection(&This->lock); return hr; } static HRESULT WINAPI IcnsEncoder_Commit(IWICBitmapEncoder *iface) { IcnsEncoder *This = impl_from_IWICBitmapEncoder(iface); size_t buffer_size; HRESULT hr = S_OK; ULONG byteswritten; TRACE("(%p)\n", iface); EnterCriticalSection(&This->lock); if (!This->any_frame_committed || This->outstanding_commits > 0 || This->committed) { hr = WINCODEC_ERR_WRONGSTATE; goto end; } buffer_size = GetHandleSize((Handle)This->icns_family); hr = IStream_Write(This->stream, *This->icns_family, buffer_size, &byteswritten); if (FAILED(hr) || byteswritten != buffer_size) { WARN("writing file failed, hr = 0x%08X\n", hr); hr = E_FAIL; goto end; } This->committed = TRUE; end: LeaveCriticalSection(&This->lock); return hr; } static HRESULT WINAPI IcnsEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface, IWICMetadataQueryWriter **ppIMetadataQueryWriter) { FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter); return E_NOTIMPL; } static const IWICBitmapEncoderVtbl IcnsEncoder_Vtbl = { IcnsEncoder_QueryInterface, IcnsEncoder_AddRef, IcnsEncoder_Release, IcnsEncoder_Initialize, IcnsEncoder_GetContainerFormat, IcnsEncoder_GetEncoderInfo, IcnsEncoder_SetColorContexts, IcnsEncoder_SetPalette, IcnsEncoder_SetThumbnail, IcnsEncoder_SetPreview, IcnsEncoder_CreateNewFrame, IcnsEncoder_Commit, IcnsEncoder_GetMetadataQueryWriter }; HRESULT IcnsEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv) { IcnsEncoder *This; HRESULT ret; TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv); *ppv = NULL; if (pUnkOuter) return CLASS_E_NOAGGREGATION; This = HeapAlloc(GetProcessHeap(), 0, sizeof(IcnsEncoder)); if (!This) return E_OUTOFMEMORY; This->IWICBitmapEncoder_iface.lpVtbl = &IcnsEncoder_Vtbl; This->ref = 1; This->stream = NULL; This->icns_family = NULL; This->any_frame_committed = FALSE; This->outstanding_commits = 0; This->committed = FALSE; InitializeCriticalSection(&This->lock); This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IcnsEncoder.lock"); ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv); IUnknown_Release((IUnknown*)This); return ret; } #else /* !defined(HAVE_APPLICATIONSERVICES_APPLICATIONSERVICES_H) || MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 */ HRESULT IcnsEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv) { ERR("Trying to save ICNS picture, but ICNS support is not compiled in.\n"); return E_FAIL; } #endif