* 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);
+#include <assert.h>
typedef struct {
DWORD bc2Size;
DWORD bc2AppData;
} BITMAPCOREHEADER2;
-struct BmpDecoder;
-typedef HRESULT (*ReadDataFunc)(struct BmpDecoder* This);
+typedef HRESULT (*ReadDataFunc)(BmpDecoder* This);
-typedef struct BmpDecoder {
- const IWICBitmapDecoderVtbl *lpVtbl;
- const IWICBitmapFrameDecodeVtbl *lpFrameVtbl;
+struct BmpDecoder {
+ IWICBitmapDecoder IWICBitmapDecoder_iface;
+ IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
LONG ref;
BOOL initialized;
IStream *stream;
- BITMAPFILEHEADER bfh;
+ ULONG palette_offset;
+ ULONG image_offset;
BITMAPV5HEADER bih;
const WICPixelFormatGUID *pixelformat;
int bitsperpixel;
BYTE *imagedata;
BYTE *imagedatastart;
CRITICAL_SECTION lock; /* must be held when initialized/imagedata is set or stream is accessed */
-} BmpDecoder;
+ int packed; /* If TRUE, don't look for a file header and assume a packed DIB. */
+ int icoframe; /* If TRUE, this is a frame of a .ico file. */
+};
+
+static inline BmpDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
+{
+ return CONTAINING_RECORD(iface, BmpDecoder, IWICBitmapDecoder_iface);
+}
-static inline BmpDecoder *impl_from_frame(IWICBitmapFrameDecode *iface)
+static inline BmpDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
{
- return CONTAINING_RECORD(iface, BmpDecoder, lpFrameVtbl);
+ return CONTAINING_RECORD(iface, BmpDecoder, IWICBitmapFrameDecode_iface);
}
static HRESULT WINAPI BmpFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
void **ppv)
{
+ BmpDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
+
TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
if (!ppv) return E_INVALIDARG;
IsEqualIID(&IID_IWICBitmapSource, iid) ||
IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
{
- *ppv = iface;
+ *ppv = &This->IWICBitmapFrameDecode_iface;
}
else
{
static ULONG WINAPI BmpFrameDecode_AddRef(IWICBitmapFrameDecode *iface)
{
- BmpDecoder *This = impl_from_frame(iface);
+ BmpDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
- return IUnknown_AddRef((IUnknown*)This);
+ return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
}
static ULONG WINAPI BmpFrameDecode_Release(IWICBitmapFrameDecode *iface)
{
- BmpDecoder *This = impl_from_frame(iface);
+ BmpDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
- return IUnknown_Release((IUnknown*)This);
+ return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
}
static HRESULT WINAPI BmpFrameDecode_GetSize(IWICBitmapFrameDecode *iface,
UINT *puiWidth, UINT *puiHeight)
{
- BmpDecoder *This = impl_from_frame(iface);
+ BmpDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
TRACE("(%p,%p,%p)\n", iface, puiWidth, puiHeight);
if (This->bih.bV5Size == sizeof(BITMAPCOREHEADER))
static HRESULT WINAPI BmpFrameDecode_GetPixelFormat(IWICBitmapFrameDecode *iface,
WICPixelFormatGUID *pPixelFormat)
{
- BmpDecoder *This = impl_from_frame(iface);
+ BmpDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
TRACE("(%p,%p)\n", iface, pPixelFormat);
memcpy(pPixelFormat, This->pixelformat, sizeof(GUID));
static HRESULT BmpHeader_GetResolution(BITMAPV5HEADER *bih, double *pDpiX, double *pDpiY)
{
+ LONG resx = 0, resy = 0;
+
switch (bih->bV5Size)
{
+ default:
case sizeof(BITMAPCOREHEADER):
- *pDpiX = 96.0;
- *pDpiY = 96.0;
- return S_OK;
+ break;
+
case sizeof(BITMAPCOREHEADER2):
case sizeof(BITMAPINFOHEADER):
case sizeof(BITMAPV4HEADER):
case sizeof(BITMAPV5HEADER):
- *pDpiX = bih->bV5XPelsPerMeter * 0.0254;
- *pDpiY = bih->bV5YPelsPerMeter * 0.0254;
- return S_OK;
- default:
- return E_FAIL;
+ resx = bih->bV5XPelsPerMeter;
+ resy = bih->bV5YPelsPerMeter;
+ break;
+ }
+
+ if (!resx || !resy)
+ {
+ *pDpiX = 96.0;
+ *pDpiY = 96.0;
}
+ else
+ {
+ *pDpiX = resx * 0.0254;
+ *pDpiY = resy * 0.0254;
+ }
+
+ return S_OK;
}
static HRESULT WINAPI BmpFrameDecode_GetResolution(IWICBitmapFrameDecode *iface,
double *pDpiX, double *pDpiY)
{
- BmpDecoder *This = impl_from_frame(iface);
+ BmpDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
TRACE("(%p,%p,%p)\n", iface, pDpiX, pDpiY);
return BmpHeader_GetResolution(&This->bih, pDpiX, pDpiY);
IWICPalette *pIPalette)
{
HRESULT hr;
- BmpDecoder *This = impl_from_frame(iface);
+ BmpDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
int count;
WICColor *wiccolors=NULL;
RGBTRIPLE *bgrcolors=NULL;
goto end;
}
- offset.QuadPart = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPCOREHEADER);
+ offset.QuadPart = This->palette_offset;
hr = IStream_Seek(This->stream, offset, STREAM_SEEK_SET, NULL);
if (FAILED(hr)) goto end;
if (This->bih.bV5ClrUsed == 0)
count = 1 << This->bih.bV5BitCount;
else
- count = This->bih.bV5ClrUsed;
+ count = min(This->bih.bV5ClrUsed, 1 << This->bih.bV5BitCount);
tablesize = sizeof(WICColor) * count;
wiccolors = HeapAlloc(GetProcessHeap(), 0, tablesize);
goto end;
}
- offset.QuadPart = sizeof(BITMAPFILEHEADER) + This->bih.bV5Size;
+ offset.QuadPart = This->palette_offset;
hr = IStream_Seek(This->stream, offset, STREAM_SEEK_SET, NULL);
if (FAILED(hr)) goto end;
static HRESULT WINAPI BmpFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface,
const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
{
- BmpDecoder *This = impl_from_frame(iface);
+ BmpDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
HRESULT hr=S_OK;
UINT width, height;
TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
This->imagedata = HeapAlloc(GetProcessHeap(), 0, datasize);
if (!This->imagedata) return E_OUTOFMEMORY;
- offbits.QuadPart = This->bfh.bfOffBits;
+ offbits.QuadPart = This->image_offset;
hr = IStream_Seek(This->stream, offbits, STREAM_SEEK_SET, NULL);
if (FAILED(hr)) goto fail;
return hr;
}
+static HRESULT BmpFrameDecode_ReadRGB8(BmpDecoder* This)
+{
+ HRESULT hr;
+ UINT width, height;
+
+ hr = IWICBitmapFrameDecode_GetSize(&This->IWICBitmapFrameDecode_iface, &width, &height);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = BmpFrameDecode_ReadUncompressed(This);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ reverse_bgr8(This->bitsperpixel/8, This->imagedatastart,
+ width, height, This->stride);
+ }
+
+ return hr;
+}
+
static HRESULT ReadByte(IStream *stream, BYTE *buffer, ULONG buffer_size,
ULONG *cursor, ULONG *bytesread, BYTE *result)
{
}
/* read palette */
- offbits.QuadPart = sizeof(BITMAPFILEHEADER) + This->bih.bV5Size;
+ offbits.QuadPart = This->palette_offset;
hr = IStream_Seek(This->stream, offbits, STREAM_SEEK_SET, NULL);
if (FAILED(hr)) goto fail;
if (FAILED(hr) || bytesread != palettesize) goto fail;
/* read RLE data */
- offbits.QuadPart = This->bfh.bfOffBits;
+ offbits.QuadPart = This->image_offset;
hr = IStream_Seek(This->stream, offbits, STREAM_SEEK_SET, NULL);
if (FAILED(hr)) goto fail;
}
/* read palette */
- offbits.QuadPart = sizeof(BITMAPFILEHEADER) + This->bih.bV5Size;
+ offbits.QuadPart = This->palette_offset;
hr = IStream_Seek(This->stream, offbits, STREAM_SEEK_SET, NULL);
if (FAILED(hr)) goto fail;
if (FAILED(hr) || bytesread != palettesize) goto fail;
/* read RLE data */
- offbits.QuadPart = This->bfh.bfOffBits;
+ offbits.QuadPart = This->image_offset;
hr = IStream_Seek(This->stream, offbits, STREAM_SEEK_SET, NULL);
if (FAILED(hr)) goto fail;
{16,0xf800,0x7e0,0x1f,0,&GUID_WICPixelFormat16bppBGR565,BmpFrameDecode_ReadUncompressed},
{32,0xff0000,0xff00,0xff,0,&GUID_WICPixelFormat32bppBGR,BmpFrameDecode_ReadUncompressed},
{32,0xff0000,0xff00,0xff,0xff000000,&GUID_WICPixelFormat32bppBGRA,BmpFrameDecode_ReadUncompressed},
+ {32,0xff,0xff00,0xff0000,0,&GUID_WICPixelFormat32bppBGR,BmpFrameDecode_ReadRGB8},
{0}
};
hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
if (FAILED(hr)) return hr;
- hr = IStream_Read(stream, &This->bfh, sizeof(BITMAPFILEHEADER), &bytesread);
- if (FAILED(hr)) return hr;
- if (bytesread != sizeof(BITMAPFILEHEADER) ||
- This->bfh.bfType != 0x4d42 /* "BM" */) return E_FAIL;
+ if (!This->packed)
+ {
+ BITMAPFILEHEADER bfh;
+ hr = IStream_Read(stream, &bfh, sizeof(BITMAPFILEHEADER), &bytesread);
+ if (FAILED(hr)) return hr;
+ if (bytesread != sizeof(BITMAPFILEHEADER) ||
+ bfh.bfType != 0x4d42 /* "BM" */) return E_FAIL;
+ This->image_offset = bfh.bfOffBits;
+ }
hr = IStream_Read(stream, &This->bih.bV5Size, sizeof(DWORD), &bytesread);
if (FAILED(hr)) return hr;
if (FAILED(hr)) return hr;
if (bytestoread != bytesread) return E_FAIL;
+ if (This->packed)
+ This->palette_offset = This->bih.bV5Size;
+ else
+ This->palette_offset = sizeof(BITMAPFILEHEADER) + This->bih.bV5Size;
+
+ if (This->icoframe)
+ {
+ if (This->bih.bV5Size == sizeof(BITMAPCOREHEADER))
+ {
+ BITMAPCOREHEADER *bch = (BITMAPCOREHEADER*)&This->bih;
+ bch->bcHeight /= 2;
+ }
+ else
+ {
+ This->bih.bV5Height /= 2;
+ }
+ }
+
/* if this is a BITMAPINFOHEADER with BI_BITFIELDS compression, we need to
read the extra fields */
if (This->bih.bV5Size == sizeof(BITMAPINFOHEADER) &&
if (FAILED(hr)) return hr;
if (bytesread != 12) return E_FAIL;
This->bih.bV5AlphaMask = 0;
+ This->palette_offset += 12;
}
/* decide what kind of bitmap this is and how/if we can read it */
}
}
+ if (This->packed)
+ {
+ /* In a packed DIB, the image follows the palette. */
+ ULONG palette_count, palette_size;
+ if (This->bih.bV5ClrUsed)
+ palette_count = This->bih.bV5ClrUsed;
+ else if (This->bih.bV5BitCount <= 8)
+ palette_count = 1 << This->bih.bV5BitCount;
+ else
+ palette_count = 0;
+ if (This->bih.bV5Size == sizeof(BITMAPCOREHEADER))
+ palette_size = sizeof(RGBTRIPLE) * palette_count;
+ else
+ palette_size = sizeof(RGBQUAD) * palette_count;
+ This->image_offset = This->palette_offset + palette_size;
+ }
+
This->initialized = TRUE;
return S_OK;
static HRESULT WINAPI BmpDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
void **ppv)
{
- BmpDecoder *This = (BmpDecoder*)iface;
+ BmpDecoder *This = impl_from_IWICBitmapDecoder(iface);
TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
if (!ppv) return E_INVALIDARG;
- if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
+ if (IsEqualIID(&IID_IUnknown, iid) ||
+ IsEqualIID(&IID_IWICBitmapDecoder, iid))
{
- *ppv = This;
+ *ppv = &This->IWICBitmapDecoder_iface;
}
else
{
static ULONG WINAPI BmpDecoder_AddRef(IWICBitmapDecoder *iface)
{
- BmpDecoder *This = (BmpDecoder*)iface;
+ BmpDecoder *This = impl_from_IWICBitmapDecoder(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p) refcount=%u\n", iface, ref);
static ULONG WINAPI BmpDecoder_Release(IWICBitmapDecoder *iface)
{
- BmpDecoder *This = (BmpDecoder*)iface;
+ BmpDecoder *This = impl_from_IWICBitmapDecoder(iface);
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) refcount=%u\n", iface, ref);
return ref;
}
-static HRESULT WINAPI BmpDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
- DWORD *pdwCapability)
+static HRESULT WINAPI BmpDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream,
+ DWORD *capability)
{
HRESULT hr;
- BmpDecoder *This = (BmpDecoder*)iface;
+ BmpDecoder *This = impl_from_IWICBitmapDecoder(iface);
- EnterCriticalSection(&This->lock);
- hr = BmpDecoder_ReadHeaders(This, pIStream);
- LeaveCriticalSection(&This->lock);
- if (FAILED(hr)) return hr;
+ TRACE("(%p,%p,%p)\n", iface, stream, capability);
- if (This->read_data_func == BmpFrameDecode_ReadUnsupported)
- *pdwCapability = 0;
- else
- *pdwCapability = WICBitmapDecoderCapabilityCanDecodeAllImages;
+ if (!stream || !capability) return E_INVALIDARG;
+ hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand);
+ if (hr != S_OK) return hr;
+
+ *capability = This->read_data_func == BmpFrameDecode_ReadUnsupported ? 0 : WICBitmapDecoderCapabilityCanDecodeAllImages;
return S_OK;
}
WICDecodeOptions cacheOptions)
{
HRESULT hr;
- BmpDecoder *This = (BmpDecoder*)iface;
+ BmpDecoder *This = impl_from_IWICBitmapDecoder(iface);
EnterCriticalSection(&This->lock);
hr = BmpDecoder_ReadHeaders(This, pIStream);
static HRESULT WINAPI BmpDecoder_GetFrameCount(IWICBitmapDecoder *iface,
UINT *pCount)
{
+ if (!pCount) return E_INVALIDARG;
+
*pCount = 1;
return S_OK;
}
static HRESULT WINAPI BmpDecoder_GetFrame(IWICBitmapDecoder *iface,
UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
{
- BmpDecoder *This = (BmpDecoder*)iface;
+ BmpDecoder *This = impl_from_IWICBitmapDecoder(iface);
if (index != 0) return E_INVALIDARG;
- if (!This->stream) return WINCODEC_ERR_WRONGSTATE;
+ if (!This->stream) return WINCODEC_ERR_FRAMEMISSING;
- *ppIBitmapFrame = (IWICBitmapFrameDecode*)&This->lpFrameVtbl;
+ *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface;
IWICBitmapDecoder_AddRef(iface);
return S_OK;
BmpDecoder_GetFrame
};
-HRESULT BmpDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
+static HRESULT BmpDecoder_Create(int packed, int icoframe, BmpDecoder **ppDecoder)
{
BmpDecoder *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(BmpDecoder));
if (!This) return E_OUTOFMEMORY;
- This->lpVtbl = &BmpDecoder_Vtbl;
- This->lpFrameVtbl = &BmpDecoder_FrameVtbl;
+ This->IWICBitmapDecoder_iface.lpVtbl = &BmpDecoder_Vtbl;
+ This->IWICBitmapFrameDecode_iface.lpVtbl = &BmpDecoder_FrameVtbl;
This->ref = 1;
This->initialized = FALSE;
This->stream = NULL;
This->imagedata = NULL;
InitializeCriticalSection(&This->lock);
This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": BmpDecoder.lock");
+ This->packed = packed;
+ This->icoframe = icoframe;
- ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
- IUnknown_Release((IUnknown*)This);
+ *ppDecoder = This;
+
+ return S_OK;
+}
+
+static HRESULT BmpDecoder_Construct(int packed, int icoframe, REFIID iid, void** ppv)
+{
+ BmpDecoder *This;
+ HRESULT ret;
+
+ TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
+
+ *ppv = NULL;
+
+ ret = BmpDecoder_Create(packed, icoframe, &This);
+ if (FAILED(ret)) return ret;
+
+ ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
+ IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
return ret;
}
+
+HRESULT BmpDecoder_CreateInstance(REFIID iid, void** ppv)
+{
+ return BmpDecoder_Construct(FALSE, FALSE, iid, ppv);
+}
+
+HRESULT DibDecoder_CreateInstance(REFIID iid, void** ppv)
+{
+ return BmpDecoder_Construct(TRUE, FALSE, iid, ppv);
+}
+
+HRESULT IcoDibDecoder_CreateInstance(BmpDecoder **ppDecoder)
+{
+ return BmpDecoder_Create(TRUE, TRUE, ppDecoder);
+}
+
+void BmpDecoder_GetWICDecoder(BmpDecoder *This, IWICBitmapDecoder **ppDecoder)
+{
+ *ppDecoder = &This->IWICBitmapDecoder_iface;
+}
+
+/* Return the offset where the mask of an icon might be, or 0 for failure. */
+void BmpDecoder_FindIconMask(BmpDecoder *This, ULONG *mask_offset, int *topdown)
+{
+ assert(This->stream != NULL);
+
+ if (This->read_data_func == BmpFrameDecode_ReadUncompressed)
+ {
+ /* RGB or BITFIELDS data */
+ ULONG width, height, bytesperrow, datasize;
+ IWICBitmapFrameDecode_GetSize(&This->IWICBitmapFrameDecode_iface, &width, &height);
+ bytesperrow = (((width * This->bitsperpixel)+31)/32)*4;
+ datasize = bytesperrow * height;
+ *mask_offset = This->image_offset + datasize;
+ }
+ else
+ *mask_offset = 0;
+
+ *topdown = This->stride > 0;
+}