| /* | 
 |  * Copyright 2009 Vincent Povirk for CodeWeavers | 
 |  * | 
 |  * 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 <stdarg.h> | 
 |  | 
 | #define COBJMACROS | 
 |  | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "winreg.h" | 
 | #include "wingdi.h" | 
 | #include "objbase.h" | 
 | #include "wincodec.h" | 
 |  | 
 | #include "wincodecs_private.h" | 
 |  | 
 | #include "wine/debug.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); | 
 |  | 
 | struct bmp_pixelformat { | 
 |     const WICPixelFormatGUID *guid; | 
 |     UINT bpp; | 
 |     DWORD compression; | 
 |     DWORD redmask; | 
 |     DWORD greenmask; | 
 |     DWORD bluemask; | 
 |     DWORD alphamask; | 
 | }; | 
 |  | 
 | static const struct bmp_pixelformat formats[] = { | 
 |     {&GUID_WICPixelFormat24bppBGR, 24, BI_RGB}, | 
 |     {&GUID_WICPixelFormat16bppBGR555, 16, BI_RGB}, | 
 |     {&GUID_WICPixelFormat16bppBGR565, 16, BI_BITFIELDS, 0xf800, 0x7e0, 0x1f, 0}, | 
 |     {&GUID_WICPixelFormat32bppBGR, 32, BI_RGB}, | 
 | #if 0 | 
 |     /* Windows doesn't seem to support this one. */ | 
 |     {&GUID_WICPixelFormat32bppBGRA, 32, BI_BITFIELDS, 0xff0000, 0xff00, 0xff, 0xff000000}, | 
 | #endif | 
 |     {NULL} | 
 | }; | 
 |  | 
 | typedef struct BmpFrameEncode { | 
 |     const IWICBitmapFrameEncodeVtbl *lpVtbl; | 
 |     LONG ref; | 
 |     IStream *stream; | 
 |     BOOL initialized; | 
 |     UINT width, height; | 
 |     BYTE *bits; | 
 |     const struct bmp_pixelformat *format; | 
 |     double xres, yres; | 
 |     UINT lineswritten; | 
 |     UINT stride; | 
 |     BOOL committed; | 
 | } BmpFrameEncode; | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid, | 
 |     void **ppv) | 
 | { | 
 |     BmpFrameEncode *This = (BmpFrameEncode*)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; | 
 |     } | 
 |     else | 
 |     { | 
 |         *ppv = NULL; | 
 |         return E_NOINTERFACE; | 
 |     } | 
 |  | 
 |     IUnknown_AddRef((IUnknown*)*ppv); | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static ULONG WINAPI BmpFrameEncode_AddRef(IWICBitmapFrameEncode *iface) | 
 | { | 
 |     BmpFrameEncode *This = (BmpFrameEncode*)iface; | 
 |     ULONG ref = InterlockedIncrement(&This->ref); | 
 |  | 
 |     TRACE("(%p) refcount=%u\n", iface, ref); | 
 |  | 
 |     return ref; | 
 | } | 
 |  | 
 | static ULONG WINAPI BmpFrameEncode_Release(IWICBitmapFrameEncode *iface) | 
 | { | 
 |     BmpFrameEncode *This = (BmpFrameEncode*)iface; | 
 |     ULONG ref = InterlockedDecrement(&This->ref); | 
 |  | 
 |     TRACE("(%p) refcount=%u\n", iface, ref); | 
 |  | 
 |     if (ref == 0) | 
 |     { | 
 |         if (This->stream) IStream_Release(This->stream); | 
 |         HeapFree(GetProcessHeap(), 0, This->bits); | 
 |         HeapFree(GetProcessHeap(), 0, This); | 
 |     } | 
 |  | 
 |     return ref; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_Initialize(IWICBitmapFrameEncode *iface, | 
 |     IPropertyBag2 *pIEncoderOptions) | 
 | { | 
 |     BmpFrameEncode *This = (BmpFrameEncode*)iface; | 
 |     TRACE("(%p,%p)\n", iface, pIEncoderOptions); | 
 |  | 
 |     if (This->initialized) return WINCODEC_ERR_WRONGSTATE; | 
 |  | 
 |     This->initialized = TRUE; | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_SetSize(IWICBitmapFrameEncode *iface, | 
 |     UINT uiWidth, UINT uiHeight) | 
 | { | 
 |     BmpFrameEncode *This = (BmpFrameEncode*)iface; | 
 |     TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight); | 
 |  | 
 |     if (!This->initialized || This->bits) return WINCODEC_ERR_WRONGSTATE; | 
 |  | 
 |     This->width = uiWidth; | 
 |     This->height = uiHeight; | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_SetResolution(IWICBitmapFrameEncode *iface, | 
 |     double dpiX, double dpiY) | 
 | { | 
 |     BmpFrameEncode *This = (BmpFrameEncode*)iface; | 
 |     TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY); | 
 |  | 
 |     if (!This->initialized || This->bits) return WINCODEC_ERR_WRONGSTATE; | 
 |  | 
 |     This->xres = dpiX; | 
 |     This->yres = dpiY; | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface, | 
 |     WICPixelFormatGUID *pPixelFormat) | 
 | { | 
 |     BmpFrameEncode *This = (BmpFrameEncode*)iface; | 
 |     int i; | 
 |     TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat)); | 
 |  | 
 |     if (!This->initialized || This->bits) return WINCODEC_ERR_WRONGSTATE; | 
 |  | 
 |     for (i=0; formats[i].guid; i++) | 
 |     { | 
 |         if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0) | 
 |             break; | 
 |     } | 
 |  | 
 |     if (!formats[i].guid) i = 0; | 
 |  | 
 |     This->format = &formats[i]; | 
 |     memcpy(pPixelFormat, This->format->guid, sizeof(GUID)); | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface, | 
 |     UINT cCount, IWICColorContext **ppIColorContext) | 
 | { | 
 |     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_SetPalette(IWICBitmapFrameEncode *iface, | 
 |     IWICPalette *pIPalette) | 
 | { | 
 |     FIXME("(%p,%p): stub\n", iface, pIPalette); | 
 |     return WINCODEC_ERR_UNSUPPORTEDOPERATION; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface, | 
 |     IWICBitmapSource *pIThumbnail) | 
 | { | 
 |     FIXME("(%p,%p): stub\n", iface, pIThumbnail); | 
 |     return WINCODEC_ERR_UNSUPPORTEDOPERATION; | 
 | } | 
 |  | 
 | static HRESULT BmpFrameEncode_AllocateBits(BmpFrameEncode *This) | 
 | { | 
 |     if (!This->bits) | 
 |     { | 
 |         if (!This->initialized || !This->width || !This->height || !This->format) | 
 |             return WINCODEC_ERR_WRONGSTATE; | 
 |  | 
 |         This->stride = (((This->width * This->format->bpp)+31)/32)*4; | 
 |         This->bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->stride * This->height); | 
 |         if (!This->bits) return E_OUTOFMEMORY; | 
 |     } | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_WritePixels(IWICBitmapFrameEncode *iface, | 
 |     UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels) | 
 | { | 
 |     BmpFrameEncode *This = (BmpFrameEncode*)iface; | 
 |     HRESULT hr; | 
 |     WICRect rc; | 
 |     TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels); | 
 |  | 
 |     if (!This->initialized || !This->width || !This->height || !This->format) | 
 |         return WINCODEC_ERR_WRONGSTATE; | 
 |  | 
 |     hr = BmpFrameEncode_AllocateBits(This); | 
 |     if (FAILED(hr)) return hr; | 
 |  | 
 |     rc.X = 0; | 
 |     rc.Y = 0; | 
 |     rc.Width = This->width; | 
 |     rc.Height = lineCount; | 
 |  | 
 |     hr = copy_pixels(This->format->bpp, pbPixels, This->width, lineCount, cbStride, | 
 |         &rc, This->stride, This->stride*(This->height-This->lineswritten), | 
 |         This->bits + This->stride*This->lineswritten); | 
 |  | 
 |     if (SUCCEEDED(hr)) | 
 |         This->lineswritten += lineCount; | 
 |  | 
 |     return hr; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_WriteSource(IWICBitmapFrameEncode *iface, | 
 |     IWICBitmapSource *pIBitmapSource, WICRect *prc) | 
 | { | 
 |     BmpFrameEncode *This = (BmpFrameEncode*)iface; | 
 |     HRESULT hr; | 
 |     WICRect rc; | 
 |     WICPixelFormatGUID guid; | 
 |     TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc); | 
 |  | 
 |     if (!This->initialized || !This->width || !This->height) | 
 |         return WINCODEC_ERR_WRONGSTATE; | 
 |  | 
 |     if (!This->format) | 
 |     { | 
 |         hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid); | 
 |         if (FAILED(hr)) return hr; | 
 |         hr = BmpFrameEncode_SetPixelFormat(iface, &guid); | 
 |         if (FAILED(hr)) return hr; | 
 |     } | 
 |  | 
 |     hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid); | 
 |     if (FAILED(hr)) return hr; | 
 |     if (memcmp(&guid, This->format->guid, sizeof(GUID)) != 0) | 
 |     { | 
 |         /* should use WICConvertBitmapSource to convert, but that's unimplemented */ | 
 |         ERR("format %s unsupported\n", debugstr_guid(&guid)); | 
 |         return E_FAIL; | 
 |     } | 
 |  | 
 |     if (This->xres == 0.0 || This->yres == 0.0) | 
 |     { | 
 |         double xres, yres; | 
 |         hr = IWICBitmapSource_GetResolution(pIBitmapSource, &xres, &yres); | 
 |         if (FAILED(hr)) return hr; | 
 |         hr = BmpFrameEncode_SetResolution(iface, xres, yres); | 
 |         if (FAILED(hr)) return hr; | 
 |     } | 
 |  | 
 |     if (!prc) | 
 |     { | 
 |         UINT width, height; | 
 |         hr = IWICBitmapSource_GetSize(pIBitmapSource, &width, &height); | 
 |         if (FAILED(hr)) return hr; | 
 |         rc.X = 0; | 
 |         rc.Y = 0; | 
 |         rc.Width = width; | 
 |         rc.Height = height; | 
 |         prc = &rc; | 
 |     } | 
 |  | 
 |     if (prc->Width != This->width) return E_INVALIDARG; | 
 |  | 
 |     hr = BmpFrameEncode_AllocateBits(This); | 
 |     if (FAILED(hr)) return hr; | 
 |  | 
 |     hr = IWICBitmapSource_CopyPixels(pIBitmapSource, prc, This->stride, | 
 |         This->stride*(This->height-This->lineswritten), | 
 |         This->bits + This->stride*This->lineswritten); | 
 |  | 
 |     This->lineswritten += rc.Height; | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_Commit(IWICBitmapFrameEncode *iface) | 
 | { | 
 |     BmpFrameEncode *This = (BmpFrameEncode*)iface; | 
 |     BITMAPFILEHEADER bfh; | 
 |     BITMAPV5HEADER bih; | 
 |     UINT info_size; | 
 |     LARGE_INTEGER pos; | 
 |     ULONG byteswritten; | 
 |     HRESULT hr; | 
 |  | 
 |     TRACE("(%p)\n", iface); | 
 |  | 
 |     if (!This->bits || This->committed || This->height != This->lineswritten) | 
 |         return WINCODEC_ERR_WRONGSTATE; | 
 |  | 
 |     bfh.bfType = 0x4d42; /* "BM" */ | 
 |     bfh.bfReserved1 = 0; | 
 |     bfh.bfReserved2 = 0; | 
 |  | 
 |     bih.bV5Size = info_size = sizeof(BITMAPINFOHEADER); | 
 |     bih.bV5Width = This->width; | 
 |     bih.bV5Height = -This->height; /* top-down bitmap */ | 
 |     bih.bV5Planes = 1; | 
 |     bih.bV5BitCount = This->format->bpp; | 
 |     bih.bV5Compression = This->format->compression; | 
 |     bih.bV5SizeImage = This->stride*This->height; | 
 |     bih.bV5XPelsPerMeter = (This->xres+0.0127) / 0.0254; | 
 |     bih.bV5YPelsPerMeter = (This->yres+0.0127) / 0.0254; | 
 |     bih.bV5ClrUsed = 0; | 
 |     bih.bV5ClrImportant = 0; | 
 |  | 
 |     if (This->format->compression == BI_BITFIELDS) | 
 |     { | 
 |         if (This->format->alphamask) | 
 |             bih.bV5Size = info_size = sizeof(BITMAPV4HEADER); | 
 |         else | 
 |             info_size = sizeof(BITMAPINFOHEADER)+12; | 
 |         bih.bV5RedMask = This->format->redmask; | 
 |         bih.bV5GreenMask = This->format->greenmask; | 
 |         bih.bV5BlueMask = This->format->bluemask; | 
 |         bih.bV5AlphaMask = This->format->alphamask; | 
 |         bih.bV5AlphaMask = LCS_DEVICE_RGB; | 
 |     } | 
 |  | 
 |     bfh.bfSize = sizeof(BITMAPFILEHEADER) + info_size + bih.bV5SizeImage; | 
 |     bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + info_size; | 
 |  | 
 |     pos.QuadPart = 0; | 
 |     hr = IStream_Seek(This->stream, pos, STREAM_SEEK_SET, NULL); | 
 |     if (FAILED(hr)) return hr; | 
 |  | 
 |     hr = IStream_Write(This->stream, &bfh, sizeof(BITMAPFILEHEADER), &byteswritten); | 
 |     if (FAILED(hr)) return hr; | 
 |     if (byteswritten != sizeof(BITMAPFILEHEADER)) return E_FAIL; | 
 |  | 
 |     hr = IStream_Write(This->stream, &bih, info_size, &byteswritten); | 
 |     if (FAILED(hr)) return hr; | 
 |     if (byteswritten != info_size) return E_FAIL; | 
 |  | 
 |     hr = IStream_Write(This->stream, This->bits, bih.bV5SizeImage, &byteswritten); | 
 |     if (FAILED(hr)) return hr; | 
 |     if (byteswritten != bih.bV5SizeImage) return E_FAIL; | 
 |  | 
 |     This->committed = TRUE; | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface, | 
 |     IWICMetadataQueryWriter **ppIMetadataQueryWriter) | 
 | { | 
 |     FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static const IWICBitmapFrameEncodeVtbl BmpFrameEncode_Vtbl = { | 
 |     BmpFrameEncode_QueryInterface, | 
 |     BmpFrameEncode_AddRef, | 
 |     BmpFrameEncode_Release, | 
 |     BmpFrameEncode_Initialize, | 
 |     BmpFrameEncode_SetSize, | 
 |     BmpFrameEncode_SetResolution, | 
 |     BmpFrameEncode_SetPixelFormat, | 
 |     BmpFrameEncode_SetColorContexts, | 
 |     BmpFrameEncode_SetPalette, | 
 |     BmpFrameEncode_SetThumbnail, | 
 |     BmpFrameEncode_WritePixels, | 
 |     BmpFrameEncode_WriteSource, | 
 |     BmpFrameEncode_Commit, | 
 |     BmpFrameEncode_GetMetadataQueryWriter | 
 | }; | 
 |  | 
 | typedef struct BmpEncoder { | 
 |     const IWICBitmapEncoderVtbl *lpVtbl; | 
 |     LONG ref; | 
 |     IStream *stream; | 
 |     IWICBitmapFrameEncode *frame; | 
 | } BmpEncoder; | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid, | 
 |     void **ppv) | 
 | { | 
 |     BmpEncoder *This = (BmpEncoder*)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 BmpEncoder_AddRef(IWICBitmapEncoder *iface) | 
 | { | 
 |     BmpEncoder *This = (BmpEncoder*)iface; | 
 |     ULONG ref = InterlockedIncrement(&This->ref); | 
 |  | 
 |     TRACE("(%p) refcount=%u\n", iface, ref); | 
 |  | 
 |     return ref; | 
 | } | 
 |  | 
 | static ULONG WINAPI BmpEncoder_Release(IWICBitmapEncoder *iface) | 
 | { | 
 |     BmpEncoder *This = (BmpEncoder*)iface; | 
 |     ULONG ref = InterlockedDecrement(&This->ref); | 
 |  | 
 |     TRACE("(%p) refcount=%u\n", iface, ref); | 
 |  | 
 |     if (ref == 0) | 
 |     { | 
 |         if (This->stream) IStream_Release(This->stream); | 
 |         if (This->frame) IWICBitmapFrameEncode_Release(This->frame); | 
 |         HeapFree(GetProcessHeap(), 0, This); | 
 |     } | 
 |  | 
 |     return ref; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_Initialize(IWICBitmapEncoder *iface, | 
 |     IStream *pIStream, WICBitmapEncoderCacheOption cacheOption) | 
 | { | 
 |     BmpEncoder *This = (BmpEncoder*)iface; | 
 |  | 
 |     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption); | 
 |  | 
 |     IStream_AddRef(pIStream); | 
 |     This->stream = pIStream; | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_GetContainerFormat(IWICBitmapEncoder *iface, | 
 |     GUID *pguidContainerFormat) | 
 | { | 
 |     FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat)); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, | 
 |     IWICBitmapEncoderInfo **ppIEncoderInfo) | 
 | { | 
 |     FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_SetColorContexts(IWICBitmapEncoder *iface, | 
 |     UINT cCount, IWICColorContext **ppIColorContext) | 
 | { | 
 |     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette) | 
 | { | 
 |     TRACE("(%p,%p)\n", iface, pIPalette); | 
 |     return WINCODEC_ERR_UNSUPPORTEDOPERATION; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail) | 
 | { | 
 |     TRACE("(%p,%p)\n", iface, pIThumbnail); | 
 |     return WINCODEC_ERR_UNSUPPORTEDOPERATION; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview) | 
 | { | 
 |     TRACE("(%p,%p)\n", iface, pIPreview); | 
 |     return WINCODEC_ERR_UNSUPPORTEDOPERATION; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_CreateNewFrame(IWICBitmapEncoder *iface, | 
 |     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions) | 
 | { | 
 |     BmpEncoder *This = (BmpEncoder*)iface; | 
 |     BmpFrameEncode *encode; | 
 |     HRESULT hr; | 
 |  | 
 |     TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions); | 
 |  | 
 |     if (This->frame) return WINCODEC_ERR_UNSUPPORTEDOPERATION; | 
 |  | 
 |     if (!This->stream) return WINCODEC_ERR_NOTINITIALIZED; | 
 |  | 
 |     hr = CreatePropertyBag2(ppIEncoderOptions); | 
 |     if (FAILED(hr)) return hr; | 
 |  | 
 |     encode = HeapAlloc(GetProcessHeap(), 0, sizeof(BmpFrameEncode)); | 
 |     if (!encode) | 
 |     { | 
 |         IPropertyBag2_Release(*ppIEncoderOptions); | 
 |         *ppIEncoderOptions = NULL; | 
 |         return E_OUTOFMEMORY; | 
 |     } | 
 |     encode->lpVtbl = &BmpFrameEncode_Vtbl; | 
 |     encode->ref = 2; | 
 |     IStream_AddRef(This->stream); | 
 |     encode->stream = This->stream; | 
 |     encode->initialized = FALSE; | 
 |     encode->width = 0; | 
 |     encode->height = 0; | 
 |     encode->bits = NULL; | 
 |     encode->format = NULL; | 
 |     encode->xres = 0.0; | 
 |     encode->yres = 0.0; | 
 |     encode->lineswritten = 0; | 
 |     encode->committed = FALSE; | 
 |  | 
 |     *ppIFrameEncode = (IWICBitmapFrameEncode*)encode; | 
 |     This->frame = (IWICBitmapFrameEncode*)encode; | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_Commit(IWICBitmapEncoder *iface) | 
 | { | 
 |     BmpEncoder *This = (BmpEncoder*)iface; | 
 |     BmpFrameEncode *frame = (BmpFrameEncode*)This->frame; | 
 |     TRACE("(%p)\n", iface); | 
 |  | 
 |     if (!frame || !frame->committed) return WINCODEC_ERR_WRONGSTATE; | 
 |  | 
 |     return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI BmpEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface, | 
 |     IWICMetadataQueryWriter **ppIMetadataQueryWriter) | 
 | { | 
 |     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter); | 
 |     return E_NOTIMPL; | 
 | } | 
 |  | 
 | static const IWICBitmapEncoderVtbl BmpEncoder_Vtbl = { | 
 |     BmpEncoder_QueryInterface, | 
 |     BmpEncoder_AddRef, | 
 |     BmpEncoder_Release, | 
 |     BmpEncoder_Initialize, | 
 |     BmpEncoder_GetContainerFormat, | 
 |     BmpEncoder_GetEncoderInfo, | 
 |     BmpEncoder_SetColorContexts, | 
 |     BmpEncoder_SetPalette, | 
 |     BmpEncoder_SetThumbnail, | 
 |     BmpEncoder_SetPreview, | 
 |     BmpEncoder_CreateNewFrame, | 
 |     BmpEncoder_Commit, | 
 |     BmpEncoder_GetMetadataQueryWriter | 
 | }; | 
 |  | 
 | HRESULT BmpEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv) | 
 | { | 
 |     BmpEncoder *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(BmpEncoder)); | 
 |     if (!This) return E_OUTOFMEMORY; | 
 |  | 
 |     This->lpVtbl = &BmpEncoder_Vtbl; | 
 |     This->ref = 1; | 
 |     This->stream = NULL; | 
 |     This->frame = NULL; | 
 |  | 
 |     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv); | 
 |     IUnknown_Release((IUnknown*)This); | 
 |  | 
 |     return ret; | 
 | } |