| /* |
| * 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 "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 { |
| IWICBitmapFrameEncode IWICBitmapFrameEncode_iface; |
| 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 inline BmpFrameEncode *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface) |
| { |
| return CONTAINING_RECORD(iface, BmpFrameEncode, IWICBitmapFrameEncode_iface); |
| } |
| |
| static HRESULT WINAPI BmpFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid, |
| void **ppv) |
| { |
| BmpFrameEncode *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 BmpFrameEncode_AddRef(IWICBitmapFrameEncode *iface) |
| { |
| BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); |
| ULONG ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p) refcount=%u\n", iface, ref); |
| |
| return ref; |
| } |
| |
| static ULONG WINAPI BmpFrameEncode_Release(IWICBitmapFrameEncode *iface) |
| { |
| BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(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 = impl_from_IWICBitmapFrameEncode(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 = impl_from_IWICBitmapFrameEncode(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 = impl_from_IWICBitmapFrameEncode(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 = impl_from_IWICBitmapFrameEncode(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 = impl_from_IWICBitmapFrameEncode(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 = impl_from_IWICBitmapFrameEncode(iface); |
| HRESULT hr; |
| TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc); |
| |
| if (!This->initialized) |
| return WINCODEC_ERR_WRONGSTATE; |
| |
| hr = configure_write_source(iface, pIBitmapSource, prc, |
| This->format ? This->format->guid : NULL, This->width, This->height, |
| This->xres, This->yres); |
| |
| if (SUCCEEDED(hr)) |
| { |
| hr = write_source(iface, pIBitmapSource, prc, |
| This->format->guid, This->format->bpp, This->width, This->height); |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI BmpFrameEncode_Commit(IWICBitmapFrameEncode *iface) |
| { |
| BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(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.bV5CSType = 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 { |
| IWICBitmapEncoder IWICBitmapEncoder_iface; |
| LONG ref; |
| IStream *stream; |
| BmpFrameEncode *frame; |
| } BmpEncoder; |
| |
| static inline BmpEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface) |
| { |
| return CONTAINING_RECORD(iface, BmpEncoder, IWICBitmapEncoder_iface); |
| } |
| |
| static HRESULT WINAPI BmpEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid, |
| void **ppv) |
| { |
| BmpEncoder *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->IWICBitmapEncoder_iface; |
| } |
| else |
| { |
| *ppv = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| IUnknown_AddRef((IUnknown*)*ppv); |
| return S_OK; |
| } |
| |
| static ULONG WINAPI BmpEncoder_AddRef(IWICBitmapEncoder *iface) |
| { |
| BmpEncoder *This = impl_from_IWICBitmapEncoder(iface); |
| ULONG ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p) refcount=%u\n", iface, ref); |
| |
| return ref; |
| } |
| |
| static ULONG WINAPI BmpEncoder_Release(IWICBitmapEncoder *iface) |
| { |
| BmpEncoder *This = impl_from_IWICBitmapEncoder(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->IWICBitmapFrameEncode_iface); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI BmpEncoder_Initialize(IWICBitmapEncoder *iface, |
| IStream *pIStream, WICBitmapEncoderCacheOption cacheOption) |
| { |
| BmpEncoder *This = impl_from_IWICBitmapEncoder(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) |
| { |
| memcpy(pguidContainerFormat, &GUID_ContainerFormatBmp, sizeof(GUID)); |
| return S_OK; |
| } |
| |
| 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 = impl_from_IWICBitmapEncoder(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(NULL, 0, ppIEncoderOptions); |
| if (FAILED(hr)) return hr; |
| |
| encode = HeapAlloc(GetProcessHeap(), 0, sizeof(BmpFrameEncode)); |
| if (!encode) |
| { |
| IPropertyBag2_Release(*ppIEncoderOptions); |
| *ppIEncoderOptions = NULL; |
| return E_OUTOFMEMORY; |
| } |
| encode->IWICBitmapFrameEncode_iface.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 = &encode->IWICBitmapFrameEncode_iface; |
| This->frame = encode; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI BmpEncoder_Commit(IWICBitmapEncoder *iface) |
| { |
| BmpEncoder *This = impl_from_IWICBitmapEncoder(iface); |
| TRACE("(%p)\n", iface); |
| |
| if (!This->frame || !This->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(REFIID iid, void** ppv) |
| { |
| BmpEncoder *This; |
| HRESULT ret; |
| |
| TRACE("(%s,%p)\n", debugstr_guid(iid), ppv); |
| |
| *ppv = NULL; |
| |
| This = HeapAlloc(GetProcessHeap(), 0, sizeof(BmpEncoder)); |
| if (!This) return E_OUTOFMEMORY; |
| |
| This->IWICBitmapEncoder_iface.lpVtbl = &BmpEncoder_Vtbl; |
| This->ref = 1; |
| This->stream = NULL; |
| This->frame = NULL; |
| |
| ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv); |
| IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface); |
| |
| return ret; |
| } |