diff --git a/reactos/dll/win32/windowscodecs/bmpdecode.c b/reactos/dll/win32/windowscodecs/bmpdecode.c index 5039694a764..395f71f1d8c 100644 --- a/reactos/dll/win32/windowscodecs/bmpdecode.c +++ b/reactos/dll/win32/windowscodecs/bmpdecode.c @@ -256,7 +256,7 @@ static HRESULT WINAPI BmpFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface, 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); diff --git a/reactos/dll/win32/windowscodecs/bmpencode.c b/reactos/dll/win32/windowscodecs/bmpencode.c index 682939f0e56..5bc043a4dbb 100644 --- a/reactos/dll/win32/windowscodecs/bmpencode.c +++ b/reactos/dll/win32/windowscodecs/bmpencode.c @@ -1,5 +1,6 @@ /* * Copyright 2009 Vincent Povirk for CodeWeavers + * Copyright 2016 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,6 +24,7 @@ struct bmp_pixelformat { const WICPixelFormatGUID *guid; UINT bpp; + UINT colors; /* palette size */ DWORD compression; DWORD redmask; DWORD greenmask; @@ -31,13 +33,18 @@ struct bmp_pixelformat { }; 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}, + {&GUID_WICPixelFormat24bppBGR, 24, 0, BI_RGB}, + {&GUID_WICPixelFormatBlackWhite, 1, 2, BI_RGB}, + {&GUID_WICPixelFormat1bppIndexed, 1, 2, BI_RGB}, + {&GUID_WICPixelFormat2bppIndexed, 2, 4, BI_RGB}, + {&GUID_WICPixelFormat4bppIndexed, 4, 16, BI_RGB}, + {&GUID_WICPixelFormat8bppIndexed, 8, 256, BI_RGB}, + {&GUID_WICPixelFormat16bppBGR555, 16, 0, BI_RGB}, + {&GUID_WICPixelFormat16bppBGR565, 16, 0, BI_BITFIELDS, 0xf800, 0x7e0, 0x1f, 0}, + {&GUID_WICPixelFormat32bppBGR, 32, 0, BI_RGB}, #if 0 /* Windows doesn't seem to support this one. */ - {&GUID_WICPixelFormat32bppBGRA, 32, BI_BITFIELDS, 0xff0000, 0xff00, 0xff, 0xff000000}, + {&GUID_WICPixelFormat32bppBGRA, 32, 0, BI_BITFIELDS, 0xff0000, 0xff00, 0xff, 0xff000000}, #endif {NULL} }; @@ -53,6 +60,8 @@ typedef struct BmpFrameEncode { double xres, yres; UINT lineswritten; UINT stride; + WICColor palette[256]; + UINT colors; BOOL committed; } BmpFrameEncode; @@ -163,11 +172,13 @@ static HRESULT WINAPI BmpFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface for (i=0; formats[i].guid; i++) { - if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0) + if (IsEqualGUID(formats[i].guid, pPixelFormat)) break; } if (!formats[i].guid) i = 0; + else if (IsEqualGUID(pPixelFormat, &GUID_WICPixelFormatBlackWhite)) + i = 2; /* GUID_WICPixelFormat1bppIndexed */ This->format = &formats[i]; memcpy(pPixelFormat, This->format->guid, sizeof(GUID)); @@ -183,10 +194,26 @@ static HRESULT WINAPI BmpFrameEncode_SetColorContexts(IWICBitmapFrameEncode *ifa } static HRESULT WINAPI BmpFrameEncode_SetPalette(IWICBitmapFrameEncode *iface, - IWICPalette *pIPalette) + IWICPalette *palette) { - FIXME("(%p,%p): stub\n", iface, pIPalette); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; + BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("(%p,%p)\n", iface, palette); + + if (!palette) return E_INVALIDARG; + + if (!This->initialized) + return WINCODEC_ERR_NOTINITIALIZED; + + hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); + if (hr == S_OK) + { + UINT i; + for (i = 0; i < This->colors; i++) + This->palette[i] |= 0xff000000; /* BMP palette has no alpha */ + } + return hr; } static HRESULT WINAPI BmpFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface, @@ -268,10 +295,11 @@ static HRESULT WINAPI BmpFrameEncode_Commit(IWICBitmapFrameEncode *iface) BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); BITMAPFILEHEADER bfh; BITMAPV5HEADER bih; - UINT info_size; + UINT info_size, i; LARGE_INTEGER pos; ULONG byteswritten; HRESULT hr; + const BYTE *bits; TRACE("(%p)\n", iface); @@ -284,15 +312,15 @@ static HRESULT WINAPI BmpFrameEncode_Commit(IWICBitmapFrameEncode *iface) bih.bV5Size = info_size = sizeof(BITMAPINFOHEADER); bih.bV5Width = This->width; - bih.bV5Height = -This->height; /* top-down bitmap */ + bih.bV5Height = This->height; /* bottom-top 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; + bih.bV5ClrUsed = (This->format->bpp <= 8) ? This->colors : 0; + bih.bV5ClrImportant = bih.bV5ClrUsed; if (This->format->compression == BI_BITFIELDS) { @@ -309,6 +337,7 @@ static HRESULT WINAPI BmpFrameEncode_Commit(IWICBitmapFrameEncode *iface) bfh.bfSize = sizeof(BITMAPFILEHEADER) + info_size + bih.bV5SizeImage; bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + info_size; + bfh.bfOffBits += bih.bV5ClrUsed * sizeof(WICColor); pos.QuadPart = 0; hr = IStream_Seek(This->stream, pos, STREAM_SEEK_SET, NULL); @@ -322,9 +351,23 @@ static HRESULT WINAPI BmpFrameEncode_Commit(IWICBitmapFrameEncode *iface) 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; + /* write the palette */ + if (This->format->colors) + { + hr = IStream_Write(This->stream, This->palette, This->colors * sizeof(WICColor), &byteswritten); + if (FAILED(hr)) return hr; + if (byteswritten != This->colors * sizeof(WICColor)) return E_FAIL; + } + + /* write the image bits as a bottom-top array */ + bits = This->bits + bih.bV5SizeImage; + for (i = 0; i < This->height; i++) + { + bits -= This->stride; + hr = IStream_Write(This->stream, bits, This->stride, &byteswritten); + if (FAILED(hr)) return hr; + if (byteswritten != This->stride) return E_FAIL; + } This->committed = TRUE; @@ -437,11 +480,22 @@ static HRESULT WINAPI BmpEncoder_GetContainerFormat(IWICBitmapEncoder *iface, return S_OK; } -static HRESULT WINAPI BmpEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, - IWICBitmapEncoderInfo **ppIEncoderInfo) +static HRESULT WINAPI BmpEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) { - FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo); - return E_NOTIMPL; + IWICComponentInfo *comp_info; + HRESULT hr; + + TRACE("%p,%p\n", iface, info); + + if (!info) return E_INVALIDARG; + + hr = CreateComponentInfo(&CLSID_WICBmpEncoder, &comp_info); + if (hr == S_OK) + { + hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); + IWICComponentInfo_Release(comp_info); + } + return hr; } static HRESULT WINAPI BmpEncoder_SetColorContexts(IWICBitmapEncoder *iface, @@ -451,10 +505,12 @@ static HRESULT WINAPI BmpEncoder_SetColorContexts(IWICBitmapEncoder *iface, return E_NOTIMPL; } -static HRESULT WINAPI BmpEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette) +static HRESULT WINAPI BmpEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette) { - TRACE("(%p,%p)\n", iface, pIPalette); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; + BmpEncoder *This = impl_from_IWICBitmapEncoder(iface); + + TRACE("(%p,%p)\n", iface, palette); + return This->stream ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED; } static HRESULT WINAPI BmpEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail) @@ -504,6 +560,7 @@ static HRESULT WINAPI BmpEncoder_CreateNewFrame(IWICBitmapEncoder *iface, encode->xres = 0.0; encode->yres = 0.0; encode->lineswritten = 0; + encode->colors = 0; encode->committed = FALSE; *ppIFrameEncode = &encode->IWICBitmapFrameEncode_iface; diff --git a/reactos/dll/win32/windowscodecs/clsfactory.c b/reactos/dll/win32/windowscodecs/clsfactory.c index 2116ff9e8b4..079ae762b8e 100644 --- a/reactos/dll/win32/windowscodecs/clsfactory.c +++ b/reactos/dll/win32/windowscodecs/clsfactory.c @@ -32,6 +32,7 @@ static const classinfo wic_classes[] = { {&CLSID_WICPngEncoder, PngEncoder_CreateInstance}, {&CLSID_WICBmpEncoder, BmpEncoder_CreateInstance}, {&CLSID_WICGifDecoder, GifDecoder_CreateInstance}, + {&CLSID_WICGifEncoder, GifEncoder_CreateInstance}, {&CLSID_WICIcoDecoder, IcoDecoder_CreateInstance}, {&CLSID_WICJpegDecoder, JpegDecoder_CreateInstance}, {&CLSID_WICJpegEncoder, JpegEncoder_CreateInstance}, diff --git a/reactos/dll/win32/windowscodecs/converter.c b/reactos/dll/win32/windowscodecs/converter.c index cc45eb6130f..790953d421d 100644 --- a/reactos/dll/win32/windowscodecs/converter.c +++ b/reactos/dll/win32/windowscodecs/converter.c @@ -63,7 +63,7 @@ typedef struct FormatConverter { const struct pixelformatinfo *dst_format, *src_format; WICBitmapDitherType dither; double alpha_threshold; - WICBitmapPaletteType palette_type; + IWICPalette *palette; CRITICAL_SECTION lock; /* must be held when initialized */ } FormatConverter; @@ -91,7 +91,7 @@ static void from_sRGB(BYTE *bgr) r = from_sRGB_component(r); g = from_sRGB_component(g); - g = from_sRGB_component(b); + b = from_sRGB_component(b); bgr[2] = (BYTE)(r * 255.0f); bgr[1] = (BYTE)(g * 255.0f); @@ -108,7 +108,7 @@ static void to_sRGB(BYTE *bgr) r = to_sRGB_component(r); g = to_sRGB_component(g); - g = to_sRGB_component(b); + b = to_sRGB_component(b); bgr[2] = (BYTE)(r * 255.0f); bgr[1] = (BYTE)(g * 255.0f); @@ -244,7 +244,7 @@ static HRESULT copypixels_to_32bppBGRA(struct FormatConverter *This, const WICRe *dstpixel++ = colors[srcval>>6]; if (x+1 < prc->Width) *dstpixel++ = colors[srcval>>4&0x3]; if (x+2 < prc->Width) *dstpixel++ = colors[srcval>>2&0x3]; - if (x+1 < prc->Width) *dstpixel++ = colors[srcval&0x3]; + if (x+3 < prc->Width) *dstpixel++ = colors[srcval&0x3]; } srcrow += srcstride; dstrow += cbStride; @@ -1035,6 +1035,7 @@ static HRESULT copypixels_to_24bppRGB(struct FormatConverter *This, const WICRec const BYTE *srcpixel; BYTE *dstrow; BYTE *dstpixel; + BYTE tmppixel[3]; srcstride = 4 * prc->Width; srcdatasize = srcstride * prc->Height; @@ -1052,16 +1053,18 @@ static HRESULT copypixels_to_24bppRGB(struct FormatConverter *This, const WICRec srcpixel=srcrow; dstpixel=dstrow; for (x=0; xWidth; x++) { - *dstpixel++=*srcpixel++; /* blue */ - *dstpixel++=*srcpixel++; /* green */ - *dstpixel++=*srcpixel++; /* red */ + tmppixel[0]=*srcpixel++; /* blue */ + tmppixel[1]=*srcpixel++; /* green */ + tmppixel[2]=*srcpixel++; /* red */ srcpixel++; /* alpha */ + + *dstpixel++=tmppixel[2]; /* red */ + *dstpixel++=tmppixel[1]; /* green */ + *dstpixel++=tmppixel[0]; /* blue */ } srcrow += srcstride; dstrow += cbStride; } - - reverse_bgr8(3, pbBuffer, prc->Width, prc->Height, cbStride); } HeapFree(GetProcessHeap(), 0, srcdata); @@ -1171,11 +1174,95 @@ static HRESULT copypixels_to_8bppGray(struct FormatConverter *This, const WICRec return hr; } +static UINT rgb_to_palette_index(BYTE r, BYTE g, BYTE b, WICColor *colors, UINT count) +{ + UINT best_diff, best_index, i; + + best_diff = ~0; + best_index = 0; + + for (i = 0; i < count; i++) + { + BYTE pal_r, pal_g, pal_b; + DWORD diff_r, diff_g, diff_b, diff; + + pal_r = colors[i] >> 16; + pal_g = colors[i] >> 8; + pal_b = colors[i]; + + diff_r = r - pal_r; + diff_g = g - pal_g; + diff_b = b - pal_b; + + diff = diff_r * diff_r + diff_g * diff_g + diff_b * diff_b; + if (diff == 0) return i; + + if (diff < best_diff) + { + best_diff = diff; + best_index = i; + } + } + + return best_index; +} + +static HRESULT copypixels_to_8bppIndexed(struct FormatConverter *This, const WICRect *prc, + UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer, enum pixelformat source_format) +{ + HRESULT hr; + BYTE *srcdata; + WICColor colors[256]; + UINT srcstride, srcdatasize, count; + + if (source_format == format_8bppIndexed) + { + if (prc) + return IWICBitmapSource_CopyPixels(This->source, prc, cbStride, cbBufferSize, pbBuffer); + + return S_OK; + } + + if (!This->palette) return WINCODEC_ERR_WRONGSTATE; + + hr = IWICPalette_GetColors(This->palette, 256, colors, &count); + if (hr != S_OK) return hr; + + srcstride = 3 * prc->Width; + srcdatasize = srcstride * prc->Height; + + srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize); + if (!srcdata) return E_OUTOFMEMORY; + + hr = copypixels_to_24bppBGR(This, prc, srcstride, srcdatasize, srcdata, source_format); + if (SUCCEEDED(hr) && prc) + { + INT x, y; + BYTE *src = srcdata, *dst = pbBuffer; + + for (y = 0; y < prc->Height; y++) + { + BYTE *bgr = src; + + for (x = 0; x < prc->Width; x++) + { + dst[x] = rgb_to_palette_index(bgr[2], bgr[1], bgr[0], colors, count); + bgr += 3; + } + src += srcstride; + dst += cbStride; + } + } + + HeapFree(GetProcessHeap(), 0, srcdata); + return hr; +} + static const struct pixelformatinfo supported_formats[] = { {format_1bppIndexed, &GUID_WICPixelFormat1bppIndexed, NULL}, {format_2bppIndexed, &GUID_WICPixelFormat2bppIndexed, NULL}, {format_4bppIndexed, &GUID_WICPixelFormat4bppIndexed, NULL}, - {format_8bppIndexed, &GUID_WICPixelFormat8bppIndexed, NULL}, + {format_8bppIndexed, &GUID_WICPixelFormat8bppIndexed, copypixels_to_8bppIndexed}, {format_BlackWhite, &GUID_WICPixelFormatBlackWhite, NULL}, {format_2bppGray, &GUID_WICPixelFormat2bppGray, NULL}, {format_4bppGray, &GUID_WICPixelFormat4bppGray, NULL}, @@ -1252,6 +1339,7 @@ static ULONG WINAPI FormatConverter_Release(IWICFormatConverter *iface) This->lock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->lock); if (This->source) IWICBitmapSource_Release(This->source); + if (This->palette) IWICPalette_Release(This->palette); HeapFree(GetProcessHeap(), 0, This); } @@ -1276,7 +1364,7 @@ static HRESULT WINAPI FormatConverter_GetPixelFormat(IWICFormatConverter *iface, { FormatConverter *This = impl_from_IWICFormatConverter(iface); - TRACE("(%p,%p): stub\n", iface, pPixelFormat); + TRACE("(%p,%p)\n", iface, pPixelFormat); if (This->source) memcpy(pPixelFormat, This->dst_format->guid, sizeof(GUID)); @@ -1291,7 +1379,7 @@ static HRESULT WINAPI FormatConverter_GetResolution(IWICFormatConverter *iface, { FormatConverter *This = impl_from_IWICFormatConverter(iface); - TRACE("(%p,%p,%p): stub\n", iface, pDpiX, pDpiY); + TRACE("(%p,%p,%p)\n", iface, pDpiX, pDpiY); if (This->source) return IWICBitmapSource_GetResolution(This->source, pDpiX, pDpiY); @@ -1300,10 +1388,27 @@ static HRESULT WINAPI FormatConverter_GetResolution(IWICFormatConverter *iface, } static HRESULT WINAPI FormatConverter_CopyPalette(IWICFormatConverter *iface, - IWICPalette *pIPalette) + IWICPalette *palette) { - FIXME("(%p,%p): stub\n", iface, pIPalette); - return E_NOTIMPL; + FormatConverter *This = impl_from_IWICFormatConverter(iface); + + TRACE("(%p,%p)\n", iface, palette); + + if (!palette) return E_INVALIDARG; + if (!This->source) return WINCODEC_ERR_WRONGSTATE; + + if (!This->palette) + { + HRESULT hr; + UINT bpp; + + hr = get_pixelformat_bpp(This->dst_format->guid, &bpp); + if (hr != S_OK) return hr; + if (bpp <= 8) return WINCODEC_ERR_WRONGSTATE; + return IWICBitmapSource_CopyPalette(This->source, palette); + } + + return IWICPalette_InitializeFromPalette(palette, This->palette); } static HRESULT WINAPI FormatConverter_CopyPixels(IWICFormatConverter *iface, @@ -1332,23 +1437,59 @@ static HRESULT WINAPI FormatConverter_CopyPixels(IWICFormatConverter *iface, pbBuffer, This->src_format->format); } else - return WINCODEC_ERR_NOTINITIALIZED; + return WINCODEC_ERR_WRONGSTATE; } static HRESULT WINAPI FormatConverter_Initialize(IWICFormatConverter *iface, - IWICBitmapSource *pISource, REFWICPixelFormatGUID dstFormat, WICBitmapDitherType dither, - IWICPalette *pIPalette, double alphaThresholdPercent, WICBitmapPaletteType paletteTranslate) + IWICBitmapSource *source, REFWICPixelFormatGUID dstFormat, WICBitmapDitherType dither, + IWICPalette *palette, double alpha_threshold, WICBitmapPaletteType palette_type) { FormatConverter *This = impl_from_IWICFormatConverter(iface); const struct pixelformatinfo *srcinfo, *dstinfo; - static INT fixme=0; GUID srcFormat; - HRESULT res=S_OK; + HRESULT res; - TRACE("(%p,%p,%s,%u,%p,%0.1f,%u)\n", iface, pISource, debugstr_guid(dstFormat), - dither, pIPalette, alphaThresholdPercent, paletteTranslate); + TRACE("(%p,%p,%s,%u,%p,%0.3f,%u)\n", iface, source, debugstr_guid(dstFormat), + dither, palette, alpha_threshold, palette_type); - if (pIPalette && !fixme++) FIXME("ignoring palette\n"); + if (!palette) + { + UINT bpp; + res = get_pixelformat_bpp(dstFormat, &bpp); + if (res != S_OK) return res; + + res = PaletteImpl_Create(&palette); + if (res != S_OK) return res; + + switch (palette_type) + { + case WICBitmapPaletteTypeCustom: + IWICPalette_Release(palette); + palette = NULL; + if (bpp <= 8) return E_INVALIDARG; + break; + + case WICBitmapPaletteTypeMedianCut: + { + if (bpp <= 8) + res = IWICPalette_InitializeFromBitmap(palette, source, 1 << bpp, FALSE); + break; + } + + default: + if (bpp <= 8) + res = IWICPalette_InitializePredefined(palette, palette_type, FALSE); + break; + } + + if (res != S_OK) + { + IWICPalette_Release(palette); + return res; + } + } + else + IWICPalette_AddRef(palette); EnterCriticalSection(&This->lock); @@ -1358,7 +1499,7 @@ static HRESULT WINAPI FormatConverter_Initialize(IWICFormatConverter *iface, goto end; } - res = IWICBitmapSource_GetPixelFormat(pISource, &srcFormat); + res = IWICBitmapSource_GetPixelFormat(source, &srcFormat); if (FAILED(res)) goto end; srcinfo = get_formatinfo(&srcFormat); @@ -1379,13 +1520,13 @@ static HRESULT WINAPI FormatConverter_Initialize(IWICFormatConverter *iface, if (dstinfo->copy_function) { - IWICBitmapSource_AddRef(pISource); + IWICBitmapSource_AddRef(source); This->src_format = srcinfo; This->dst_format = dstinfo; This->dither = dither; - This->alpha_threshold = alphaThresholdPercent; - This->palette_type = paletteTranslate; - This->source = pISource; + This->alpha_threshold = alpha_threshold; + This->palette = palette; + This->source = source; } else { @@ -1397,6 +1538,9 @@ end: LeaveCriticalSection(&This->lock); + if (res != S_OK && palette) + IWICPalette_Release(palette); + return res; } @@ -1464,6 +1608,7 @@ HRESULT FormatConverter_CreateInstance(REFIID iid, void** ppv) This->IWICFormatConverter_iface.lpVtbl = &FormatConverter_Vtbl; This->ref = 1; This->source = NULL; + This->palette = NULL; InitializeCriticalSection(&This->lock); This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FormatConverter.lock"); diff --git a/reactos/dll/win32/windowscodecs/gifformat.c b/reactos/dll/win32/windowscodecs/gifformat.c index 18bc99cabd8..3cd7d028a70 100644 --- a/reactos/dll/win32/windowscodecs/gifformat.c +++ b/reactos/dll/win32/windowscodecs/gifformat.c @@ -1,6 +1,6 @@ /* * Copyright 2009 Vincent Povirk for CodeWeavers - * Copyright 2012 Dmitry Timoshkov + * Copyright 2012,2016 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,6 +23,40 @@ #include "ungif.h" +#include "pshpack1.h" + +struct logical_screen_descriptor +{ + char signature[6]; + USHORT width; + USHORT height; + BYTE packed; + /* global_color_table_flag : 1; + * color_resolution : 3; + * sort_flag : 1; + * global_color_table_size : 3; + */ + BYTE background_color_index; + BYTE pixel_aspect_ratio; +}; + +struct image_descriptor +{ + USHORT left; + USHORT top; + USHORT width; + USHORT height; + BYTE packed; + /* local_color_table_flag : 1; + * interlace_flag : 1; + * sort_flag : 1; + * reserved : 2; + * local_color_table_size : 3; + */ +}; + +#include "poppack.h" + static LPWSTR strdupAtoW(const char *src) { int len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0); @@ -34,22 +68,7 @@ static LPWSTR strdupAtoW(const char *src) static HRESULT load_LSD_metadata(IStream *stream, const GUID *vendor, DWORD options, MetadataItem **items, DWORD *count) { -#include "pshpack1.h" - struct logical_screen_descriptor - { - char signature[6]; - USHORT width; - USHORT height; - BYTE packed; - /* global_color_table_flag : 1; - * color_resolution : 3; - * sort_flag : 1; - * global_color_table_size : 3; - */ - BYTE background_color_index; - BYTE pixel_aspect_ratio; - } lsd_data; -#include "poppack.h" + struct logical_screen_descriptor lsd_data; HRESULT hr; ULONG bytesread, i; MetadataItem *result; @@ -134,23 +153,6 @@ HRESULT LSDReader_CreateInstance(REFIID iid, void **ppv) return MetadataReader_Create(&LSDReader_Vtbl, iid, ppv); } -#include "pshpack1.h" -struct image_descriptor -{ - USHORT left; - USHORT top; - USHORT width; - USHORT height; - BYTE packed; - /* local_color_table_flag : 1; - * interlace_flag : 1; - * sort_flag : 1; - * reserved : 2; - * local_color_table_size : 3; - */ -}; -#include "poppack.h" - static HRESULT load_IMD_metadata(IStream *stream, const GUID *vendor, DWORD options, MetadataItem **items, DWORD *count) { @@ -1183,6 +1185,9 @@ static HRESULT WINAPI GifDecoder_CopyPalette(IWICBitmapDecoder *iface, IWICPalet TRACE("(%p,%p)\n", iface, palette); + if (!This->gif) + return WINCODEC_ERR_WRONGSTATE; + cm = This->gif->SColorMap; if (cm) { @@ -1442,3 +1447,945 @@ HRESULT GifDecoder_CreateInstance(REFIID iid, void** ppv) return ret; } + +typedef struct GifEncoder +{ + IWICBitmapEncoder IWICBitmapEncoder_iface; + LONG ref; + IStream *stream; + CRITICAL_SECTION lock; + BOOL initialized, info_written, committed; + UINT n_frames; + WICColor palette[256]; + UINT colors; +} GifEncoder; + +static inline GifEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface) +{ + return CONTAINING_RECORD(iface, GifEncoder, IWICBitmapEncoder_iface); +} + +typedef struct GifFrameEncode +{ + IWICBitmapFrameEncode IWICBitmapFrameEncode_iface; + LONG ref; + GifEncoder *encoder; + BOOL initialized, interlace, committed; + UINT width, height, lines; + double xres, yres; + WICColor palette[256]; + UINT colors; + BYTE *image_data; +} GifFrameEncode; + +static inline GifFrameEncode *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface) +{ + return CONTAINING_RECORD(iface, GifFrameEncode, IWICBitmapFrameEncode_iface); +} + +static HRESULT WINAPI GifFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid, void **ppv) +{ + TRACE("%p,%s,%p\n", iface, debugstr_guid(iid), ppv); + + if (!ppv) return E_INVALIDARG; + + if (IsEqualIID(&IID_IUnknown, iid) || + IsEqualIID(&IID_IWICBitmapFrameEncode, iid)) + { + IWICBitmapFrameEncode_AddRef(iface); + *ppv = iface; + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI GifFrameEncode_AddRef(IWICBitmapFrameEncode *iface) +{ + GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("%p -> %u\n", iface, ref); + return ref; +} + +static ULONG WINAPI GifFrameEncode_Release(IWICBitmapFrameEncode *iface) +{ + GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("%p -> %u\n", iface, ref); + + if (!ref) + { + IWICBitmapEncoder_Release(&This->encoder->IWICBitmapEncoder_iface); + HeapFree(GetProcessHeap(), 0, This->image_data); + HeapFree(GetProcessHeap(), 0, This); + } + + return ref; +} + +static HRESULT WINAPI GifFrameEncode_Initialize(IWICBitmapFrameEncode *iface, IPropertyBag2 *options) +{ + GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("%p,%p\n", iface, options); + + EnterCriticalSection(&This->encoder->lock); + + if (!This->initialized) + { + This->initialized = TRUE; + hr = S_OK; + } + else + hr = WINCODEC_ERR_WRONGSTATE; + + LeaveCriticalSection(&This->encoder->lock); + + return hr; +} + +static HRESULT WINAPI GifFrameEncode_SetSize(IWICBitmapFrameEncode *iface, UINT width, UINT height) +{ + GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("%p,%u,%u\n", iface, width, height); + + if (!width || !height) return E_INVALIDARG; + + EnterCriticalSection(&This->encoder->lock); + + if (This->initialized) + { + HeapFree(GetProcessHeap(), 0, This->image_data); + + This->image_data = HeapAlloc(GetProcessHeap(), 0, width * height); + if (This->image_data) + { + This->width = width; + This->height = height; + hr = S_OK; + } + else + hr = E_OUTOFMEMORY; + } + else + hr = WINCODEC_ERR_WRONGSTATE; + + LeaveCriticalSection(&This->encoder->lock); + + return hr; +} + +static HRESULT WINAPI GifFrameEncode_SetResolution(IWICBitmapFrameEncode *iface, double xres, double yres) +{ + GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("%p,%f,%f\n", iface, xres, yres); + + EnterCriticalSection(&This->encoder->lock); + + if (This->initialized) + { + This->xres = xres; + This->yres = yres; + hr = S_OK; + } + else + hr = WINCODEC_ERR_WRONGSTATE; + + LeaveCriticalSection(&This->encoder->lock); + + return hr; +} + +static HRESULT WINAPI GifFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface, WICPixelFormatGUID *format) +{ + GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("%p,%s\n", iface, debugstr_guid(format)); + + if (!format) return E_INVALIDARG; + + EnterCriticalSection(&This->encoder->lock); + + if (This->initialized) + { + *format = GUID_WICPixelFormat8bppIndexed; + hr = S_OK; + } + else + hr = WINCODEC_ERR_WRONGSTATE; + + LeaveCriticalSection(&This->encoder->lock); + + return hr; +} + +static HRESULT WINAPI GifFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface, UINT count, IWICColorContext **context) +{ + FIXME("%p,%u,%p: stub\n", iface, count, context); + return E_NOTIMPL; +} + +static HRESULT WINAPI GifFrameEncode_SetPalette(IWICBitmapFrameEncode *iface, IWICPalette *palette) +{ + GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("%p,%p\n", iface, palette); + + if (!palette) return E_INVALIDARG; + + EnterCriticalSection(&This->encoder->lock); + + if (This->initialized) + hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); + else + hr = WINCODEC_ERR_NOTINITIALIZED; + + LeaveCriticalSection(&This->encoder->lock); + return hr; +} + +static HRESULT WINAPI GifFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface, IWICBitmapSource *thumbnail) +{ + FIXME("%p,%p: stub\n", iface, thumbnail); + return E_NOTIMPL; +} + +static HRESULT WINAPI GifFrameEncode_WritePixels(IWICBitmapFrameEncode *iface, UINT lines, UINT stride, UINT size, BYTE *pixels) +{ + GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("%p,%u,%u,%u,%p\n", iface, lines, stride, size, pixels); + + if (!pixels) return E_INVALIDARG; + + EnterCriticalSection(&This->encoder->lock); + + if (This->initialized && This->image_data) + { + if (This->lines + lines <= This->height) + { + UINT i; + BYTE *src, *dst; + + src = pixels; + dst = This->image_data + This->lines * This->width; + + for (i = 0; i < lines; i++) + { + memcpy(dst, src, This->width); + src += stride; + dst += This->width; + } + + This->lines += lines; + hr = S_OK; + } + else + hr = E_INVALIDARG; + } + else + hr = WINCODEC_ERR_WRONGSTATE; + + LeaveCriticalSection(&This->encoder->lock); + return hr; +} + +static HRESULT WINAPI GifFrameEncode_WriteSource(IWICBitmapFrameEncode *iface, IWICBitmapSource *source, WICRect *rc) +{ + GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("%p,%p,%p\n", iface, source, rc); + + if (!source) return E_INVALIDARG; + + EnterCriticalSection(&This->encoder->lock); + + if (This->initialized) + { + const GUID *format = &GUID_WICPixelFormat8bppIndexed; + + hr = configure_write_source(iface, source, rc, format, + This->width, This->height, This->xres, This->yres); + if (hr == S_OK) + hr = write_source(iface, source, rc, format, 8, This->width, This->height); + } + else + hr = WINCODEC_ERR_WRONGSTATE; + + LeaveCriticalSection(&This->encoder->lock); + return hr; +} + +#define LZW_DICT_SIZE (1 << 12) + +struct lzw_dict +{ + short prefix[LZW_DICT_SIZE]; + unsigned char suffix[LZW_DICT_SIZE]; +}; + +struct lzw_state +{ + struct lzw_dict dict; + short init_code_bits, code_bits, next_code, clear_code, eof_code; + unsigned bits_buf; + int bits_count; + int (*user_write_data)(void *user_ptr, void *data, int length); + void *user_ptr; +}; + +struct input_stream +{ + unsigned len; + const BYTE *in; +}; + +struct output_stream +{ + struct + { + unsigned char len; + char data[255]; + } gif_block; + IStream *out; +}; + +static int lzw_output_code(struct lzw_state *state, short code) +{ + state->bits_buf |= code << state->bits_count; + state->bits_count += state->code_bits; + + while (state->bits_count >= 8) + { + unsigned char byte = (unsigned char)state->bits_buf; + if (state->user_write_data(state->user_ptr, &byte, 1) != 1) + return 0; + state->bits_buf >>= 8; + state->bits_count -= 8; + } + + return 1; +} + +static inline int lzw_output_clear_code(struct lzw_state *state) +{ + return lzw_output_code(state, state->clear_code); +} + +static inline int lzw_output_eof_code(struct lzw_state *state) +{ + return lzw_output_code(state, state->eof_code); +} + +static int lzw_flush_bits(struct lzw_state *state) +{ + unsigned char byte; + + while (state->bits_count >= 8) + { + byte = (unsigned char)state->bits_buf; + if (state->user_write_data(state->user_ptr, &byte, 1) != 1) + return 0; + state->bits_buf >>= 8; + state->bits_count -= 8; + } + + if (state->bits_count) + { + static const char mask[8] = { 0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f }; + + byte = (unsigned char)state->bits_buf & mask[state->bits_count]; + if (state->user_write_data(state->user_ptr, &byte, 1) != 1) + return 0; + } + + state->bits_buf = 0; + state->bits_count = 0; + + return 1; +} + +static void lzw_dict_reset(struct lzw_state *state) +{ + int i; + + state->code_bits = state->init_code_bits + 1; + state->next_code = (1 << state->init_code_bits) + 2; + + for(i = 0; i < LZW_DICT_SIZE; i++) + { + state->dict.prefix[i] = 1 << 12; /* impossible LZW code value */ + state->dict.suffix[i] = 0; + } +} + +static void lzw_state_init(struct lzw_state *state, short init_code_bits, void *user_write_data, void *user_ptr) +{ + state->init_code_bits = init_code_bits; + state->clear_code = 1 << init_code_bits; + state->eof_code = state->clear_code + 1; + state->bits_buf = 0; + state->bits_count = 0; + state->user_write_data = user_write_data; + state->user_ptr = user_ptr; + + lzw_dict_reset(state); +} + +static int lzw_dict_add(struct lzw_state *state, short prefix, unsigned char suffix) +{ + if (state->next_code < LZW_DICT_SIZE) + { + state->dict.prefix[state->next_code] = prefix; + state->dict.suffix[state->next_code] = suffix; + + if ((state->next_code & (state->next_code - 1)) == 0) + state->code_bits++; + + state->next_code++; + return state->next_code; + } + + return -1; +} + +static short lzw_dict_lookup(const struct lzw_state *state, short prefix, unsigned char suffix) +{ + short i; + + for (i = 0; i < state->next_code; i++) + { + if (state->dict.prefix[i] == prefix && state->dict.suffix[i] == suffix) + return i; + } + + return -1; +} + +static inline int write_byte(struct output_stream *out, char byte) +{ + if (out->gif_block.len == 255) + { + if (IStream_Write(out->out, &out->gif_block, sizeof(out->gif_block), NULL) != S_OK) + return 0; + + out->gif_block.len = 0; + } + + out->gif_block.data[out->gif_block.len++] = byte; + + return 1; +} + +static int write_data(void *user_ptr, void *user_data, int length) +{ + unsigned char *data = user_data; + struct output_stream *out = user_ptr; + int len = length; + + while (len-- > 0) + { + if (!write_byte(out, *data++)) return 0; + } + + return length; +} + +static int flush_output_data(void *user_ptr) +{ + struct output_stream *out = user_ptr; + + if (out->gif_block.len) + { + if (IStream_Write(out->out, &out->gif_block, out->gif_block.len + sizeof(out->gif_block.len), NULL) != S_OK) + return 0; + } + + /* write GIF block terminator */ + out->gif_block.len = 0; + return IStream_Write(out->out, &out->gif_block, sizeof(out->gif_block.len), NULL) == S_OK; +} + +static inline int read_byte(struct input_stream *in, unsigned char *byte) +{ + if (in->len) + { + in->len--; + *byte = *in->in++; + return 1; + } + + return 0; +} + +static HRESULT gif_compress(IStream *out_stream, const BYTE *in_data, ULONG in_size) +{ + struct input_stream in; + struct output_stream out; + struct lzw_state state; + short init_code_bits, prefix, code; + unsigned char suffix; + + in.in = in_data; + in.len = in_size; + + out.gif_block.len = 0; + out.out = out_stream; + + init_code_bits = suffix = 8; + if (IStream_Write(out.out, &suffix, sizeof(suffix), NULL) != S_OK) + return E_FAIL; + + lzw_state_init(&state, init_code_bits, write_data, &out); + + if (!lzw_output_clear_code(&state)) + return E_FAIL; + + if (read_byte(&in, &suffix)) + { + prefix = suffix; + + while (read_byte(&in, &suffix)) + { + code = lzw_dict_lookup(&state, prefix, suffix); + if (code == -1) + { + if (!lzw_output_code(&state, prefix)) + return E_FAIL; + + if (lzw_dict_add(&state, prefix, suffix) == -1) + { + if (!lzw_output_clear_code(&state)) + return E_FAIL; + lzw_dict_reset(&state); + } + + prefix = suffix; + } + else + prefix = code; + } + + if (!lzw_output_code(&state, prefix)) + return E_FAIL; + if (!lzw_output_eof_code(&state)) + return E_FAIL; + if (!lzw_flush_bits(&state)) + return E_FAIL; + } + + return flush_output_data(&out) ? S_OK : E_FAIL; +} + +static HRESULT WINAPI GifFrameEncode_Commit(IWICBitmapFrameEncode *iface) +{ + GifFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("%p\n", iface); + + EnterCriticalSection(&This->encoder->lock); + + if (This->image_data && This->lines == This->height && !This->committed) + { + BYTE gif_palette[256][3]; + + hr = S_OK; + + if (!This->encoder->info_written) + { + struct logical_screen_descriptor lsd; + + /* Logical Screen Descriptor */ + memcpy(lsd.signature, "GIF89a", 6); + lsd.width = This->width; + lsd.height = This->height; + lsd.packed = 0; + if (This->encoder->colors) + lsd.packed |= 0x80; /* global color table flag */ + lsd.packed |= 0x07 << 4; /* color resolution */ + lsd.packed |= 0x07; /* global color table size */ + lsd.background_color_index = 0; /* FIXME */ + lsd.pixel_aspect_ratio = 0; + hr = IStream_Write(This->encoder->stream, &lsd, sizeof(lsd), NULL); + if (hr == S_OK && This->encoder->colors) + { + UINT i; + + /* Global Color Table */ + memset(gif_palette, 0, sizeof(gif_palette)); + for (i = 0; i < This->encoder->colors; i++) + { + gif_palette[i][0] = (This->encoder->palette[i] >> 16) & 0xff; + gif_palette[i][1] = (This->encoder->palette[i] >> 8) & 0xff; + gif_palette[i][2] = This->encoder->palette[i] & 0xff; + } + hr = IStream_Write(This->encoder->stream, gif_palette, sizeof(gif_palette), NULL); + } + + /* FIXME: write GCE, APE, etc. GIF extensions */ + + if (hr == S_OK) + This->encoder->info_written = TRUE; + } + + if (hr == S_OK) + { + char image_separator = 0x2c; + + hr = IStream_Write(This->encoder->stream, &image_separator, sizeof(image_separator), NULL); + if (hr == S_OK) + { + struct image_descriptor imd; + + /* Image Descriptor */ + imd.left = 0; + imd.top = 0; + imd.width = This->width; + imd.height = This->height; + imd.packed = 0; + if (This->colors) + { + imd.packed |= 0x80; /* local color table flag */ + imd.packed |= 0x07; /* local color table size */ + } + /* FIXME: interlace flag */ + hr = IStream_Write(This->encoder->stream, &imd, sizeof(imd), NULL); + if (hr == S_OK && This->colors) + { + UINT i; + + /* Local Color Table */ + memset(gif_palette, 0, sizeof(gif_palette)); + for (i = 0; i < This->colors; i++) + { + gif_palette[i][0] = (This->palette[i] >> 16) & 0xff; + gif_palette[i][1] = (This->palette[i] >> 8) & 0xff; + gif_palette[i][2] = This->palette[i] & 0xff; + } + hr = IStream_Write(This->encoder->stream, gif_palette, sizeof(gif_palette), NULL); + if (hr == S_OK) + { + /* Image Data */ + hr = gif_compress(This->encoder->stream, This->image_data, This->width * This->height); + if (hr == S_OK) + This->committed = TRUE; + } + } + } + } + } + else + hr = WINCODEC_ERR_WRONGSTATE; + + LeaveCriticalSection(&This->encoder->lock); + return hr; +} + +static HRESULT WINAPI GifFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface, IWICMetadataQueryWriter **writer) +{ + FIXME("%p, %p: stub\n", iface, writer); + return E_NOTIMPL; +} + +static const IWICBitmapFrameEncodeVtbl GifFrameEncode_Vtbl = +{ + GifFrameEncode_QueryInterface, + GifFrameEncode_AddRef, + GifFrameEncode_Release, + GifFrameEncode_Initialize, + GifFrameEncode_SetSize, + GifFrameEncode_SetResolution, + GifFrameEncode_SetPixelFormat, + GifFrameEncode_SetColorContexts, + GifFrameEncode_SetPalette, + GifFrameEncode_SetThumbnail, + GifFrameEncode_WritePixels, + GifFrameEncode_WriteSource, + GifFrameEncode_Commit, + GifFrameEncode_GetMetadataQueryWriter +}; + +static HRESULT WINAPI GifEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid, void **ppv) +{ + TRACE("%p,%s,%p\n", iface, debugstr_guid(iid), ppv); + + if (!ppv) return E_INVALIDARG; + + if (IsEqualIID(&IID_IUnknown, iid) || + IsEqualIID(&IID_IWICBitmapEncoder, iid)) + { + IWICBitmapEncoder_AddRef(iface); + *ppv = iface; + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI GifEncoder_AddRef(IWICBitmapEncoder *iface) +{ + GifEncoder *This = impl_from_IWICBitmapEncoder(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("%p -> %u\n", iface, ref); + return ref; +} + +static ULONG WINAPI GifEncoder_Release(IWICBitmapEncoder *iface) +{ + GifEncoder *This = impl_from_IWICBitmapEncoder(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("%p -> %u\n", iface, ref); + + if (!ref) + { + if (This->stream) IStream_Release(This->stream); + This->lock.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&This->lock); + HeapFree(GetProcessHeap(), 0, This); + } + + return ref; +} + +static HRESULT WINAPI GifEncoder_Initialize(IWICBitmapEncoder *iface, IStream *stream, WICBitmapEncoderCacheOption option) +{ + GifEncoder *This = impl_from_IWICBitmapEncoder(iface); + HRESULT hr; + + TRACE("%p,%p,%#x\n", iface, stream, option); + + if (!stream) return E_INVALIDARG; + + EnterCriticalSection(&This->lock); + + if (!This->initialized) + { + IStream_AddRef(stream); + This->stream = stream; + This->initialized = TRUE; + hr = S_OK; + } + else + hr = WINCODEC_ERR_WRONGSTATE; + + LeaveCriticalSection(&This->lock); + + return hr; +} + +static HRESULT WINAPI GifEncoder_GetContainerFormat(IWICBitmapEncoder *iface, GUID *format) +{ + if (!format) return E_INVALIDARG; + + *format = GUID_ContainerFormatGif; + return S_OK; +} + +static HRESULT WINAPI GifEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) +{ + IWICComponentInfo *comp_info; + HRESULT hr; + + TRACE("%p,%p\n", iface, info); + + if (!info) return E_INVALIDARG; + + hr = CreateComponentInfo(&CLSID_WICGifEncoder, &comp_info); + if (hr == S_OK) + { + hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); + IWICComponentInfo_Release(comp_info); + } + return hr; +} + +static HRESULT WINAPI GifEncoder_SetColorContexts(IWICBitmapEncoder *iface, UINT count, IWICColorContext **context) +{ + FIXME("%p,%u,%p: stub\n", iface, count, context); + return E_NOTIMPL; +} + +static HRESULT WINAPI GifEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette) +{ + GifEncoder *This = impl_from_IWICBitmapEncoder(iface); + HRESULT hr; + + TRACE("%p,%p\n", iface, palette); + + if (!palette) return E_INVALIDARG; + + EnterCriticalSection(&This->lock); + + if (This->initialized) + hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); + else + hr = WINCODEC_ERR_NOTINITIALIZED; + + LeaveCriticalSection(&This->lock); + return hr; +} + +static HRESULT WINAPI GifEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *thumbnail) +{ + TRACE("%p,%p\n", iface, thumbnail); + return WINCODEC_ERR_UNSUPPORTEDOPERATION; +} + +static HRESULT WINAPI GifEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *preview) +{ + TRACE("%p,%p\n", iface, preview); + return WINCODEC_ERR_UNSUPPORTEDOPERATION; +} + +static HRESULT WINAPI GifEncoder_CreateNewFrame(IWICBitmapEncoder *iface, IWICBitmapFrameEncode **frame, IPropertyBag2 **options) +{ + GifEncoder *This = impl_from_IWICBitmapEncoder(iface); + HRESULT hr; + + TRACE("%p,%p,%p\n", iface, frame, options); + + if (!frame) return E_INVALIDARG; + + EnterCriticalSection(&This->lock); + + if (This->initialized && !This->committed) + { + GifFrameEncode *ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret)); + if (ret) + { + This->n_frames++; + + ret->IWICBitmapFrameEncode_iface.lpVtbl = &GifFrameEncode_Vtbl; + ret->ref = 1; + ret->encoder = This; + ret->initialized = FALSE; + ret->interlace = FALSE; /* FIXME: read from the properties */ + ret->committed = FALSE; + ret->width = 0; + ret->height = 0; + ret->lines = 0; + ret->xres = 0.0; + ret->yres = 0.0; + ret->colors = 0; + ret->image_data = NULL; + IWICBitmapEncoder_AddRef(iface); + *frame = &ret->IWICBitmapFrameEncode_iface; + + hr = S_OK; + + if (options) + { + hr = CreatePropertyBag2(NULL, 0, options); + if (hr != S_OK) + { + IWICBitmapFrameEncode_Release(*frame); + *frame = NULL; + } + } + } + else + hr = E_OUTOFMEMORY; + } + else + hr = WINCODEC_ERR_WRONGSTATE; + + LeaveCriticalSection(&This->lock); + + return hr; + +} + +static HRESULT WINAPI GifEncoder_Commit(IWICBitmapEncoder *iface) +{ + GifEncoder *This = impl_from_IWICBitmapEncoder(iface); + HRESULT hr; + + TRACE("%p\n", iface); + + EnterCriticalSection(&This->lock); + + if (This->initialized && !This->committed) + { + char gif_trailer = 0x3b; + + /* FIXME: write text, comment GIF extensions */ + + hr = IStream_Write(This->stream, &gif_trailer, sizeof(gif_trailer), NULL); + if (hr == S_OK) + This->committed = TRUE; + } + else + hr = WINCODEC_ERR_WRONGSTATE; + + LeaveCriticalSection(&This->lock); + return hr; +} + +static HRESULT WINAPI GifEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface, IWICMetadataQueryWriter **writer) +{ + FIXME("%p,%p: stub\n", iface, writer); + return E_NOTIMPL; +} + +static const IWICBitmapEncoderVtbl GifEncoder_Vtbl = +{ + GifEncoder_QueryInterface, + GifEncoder_AddRef, + GifEncoder_Release, + GifEncoder_Initialize, + GifEncoder_GetContainerFormat, + GifEncoder_GetEncoderInfo, + GifEncoder_SetColorContexts, + GifEncoder_SetPalette, + GifEncoder_SetThumbnail, + GifEncoder_SetPreview, + GifEncoder_CreateNewFrame, + GifEncoder_Commit, + GifEncoder_GetMetadataQueryWriter +}; + +HRESULT GifEncoder_CreateInstance(REFIID iid, void **ppv) +{ + GifEncoder *This; + HRESULT ret; + + TRACE("%s,%p\n", debugstr_guid(iid), ppv); + + *ppv = NULL; + + This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + if (!This) return E_OUTOFMEMORY; + + This->IWICBitmapEncoder_iface.lpVtbl = &GifEncoder_Vtbl; + This->ref = 1; + This->stream = NULL; + InitializeCriticalSection(&This->lock); + This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": GifEncoder.lock"); + This->initialized = FALSE; + This->info_written = FALSE; + This->committed = FALSE; + This->n_frames = 0; + This->colors = 0; + + ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv); + IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface); + + return ret; +} diff --git a/reactos/dll/win32/windowscodecs/icnsformat.c b/reactos/dll/win32/windowscodecs/icnsformat.c index c70b4e752ac..e7d6f96226d 100644 --- a/reactos/dll/win32/windowscodecs/icnsformat.c +++ b/reactos/dll/win32/windowscodecs/icnsformat.c @@ -399,7 +399,7 @@ static HRESULT WINAPI IcnsFrameEncode_Commit(IWICBitmapFrameEncode *iface) OSErr ret; HRESULT hr = S_OK; - TRACE("(%p): stub\n", iface); + TRACE("(%p)\n", iface); EnterCriticalSection(&This->encoder->lock); diff --git a/reactos/dll/win32/windowscodecs/imgfactory.c b/reactos/dll/win32/windowscodecs/imgfactory.c index 8692b7720db..c49dee6a9ce 100644 --- a/reactos/dll/win32/windowscodecs/imgfactory.c +++ b/reactos/dll/win32/windowscodecs/imgfactory.c @@ -104,22 +104,23 @@ static HRESULT WINAPI ComponentFactory_CreateDecoderFromFilename( return hr; } -static IWICBitmapDecoder *find_decoder(IStream *pIStream, const GUID *pguidVendor, - WICDecodeOptions metadataOptions) +static HRESULT find_decoder(IStream *pIStream, const GUID *pguidVendor, + WICDecodeOptions metadataOptions, IWICBitmapDecoder **decoder) { IEnumUnknown *enumdecoders; IUnknown *unkdecoderinfo; IWICBitmapDecoderInfo *decoderinfo; - IWICBitmapDecoder *decoder = NULL; GUID vendor; HRESULT res; ULONG num_fetched; BOOL matches; - res = CreateComponentEnumerator(WICDecoder, WICComponentEnumerateDefault, &enumdecoders); - if (FAILED(res)) return NULL; + *decoder = NULL; - while (!decoder) + res = CreateComponentEnumerator(WICDecoder, WICComponentEnumerateDefault, &enumdecoders); + if (FAILED(res)) return res; + + while (!*decoder) { res = IEnumUnknown_Next(enumdecoders, 1, &unkdecoderinfo, &num_fetched); @@ -144,18 +145,21 @@ static IWICBitmapDecoder *find_decoder(IStream *pIStream, const GUID *pguidVendo if (SUCCEEDED(res) && matches) { - res = IWICBitmapDecoderInfo_CreateInstance(decoderinfo, &decoder); + res = IWICBitmapDecoderInfo_CreateInstance(decoderinfo, decoder); /* FIXME: should use QueryCapability to choose a decoder */ if (SUCCEEDED(res)) { - res = IWICBitmapDecoder_Initialize(decoder, pIStream, metadataOptions); + res = IWICBitmapDecoder_Initialize(*decoder, pIStream, metadataOptions); if (FAILED(res)) { - IWICBitmapDecoder_Release(decoder); - decoder = NULL; + IWICBitmapDecoder_Release(*decoder); + IWICBitmapDecoderInfo_Release(decoderinfo); + IUnknown_Release(unkdecoderinfo); + *decoder = NULL; + return res; } } } @@ -171,7 +175,7 @@ static IWICBitmapDecoder *find_decoder(IStream *pIStream, const GUID *pguidVendo IEnumUnknown_Release(enumdecoders); - return decoder; + return WINCODEC_ERR_COMPONENTNOTFOUND; } static HRESULT WINAPI ComponentFactory_CreateDecoderFromStream( @@ -185,9 +189,9 @@ static HRESULT WINAPI ComponentFactory_CreateDecoderFromStream( metadataOptions, ppIDecoder); if (pguidVendor) - decoder = find_decoder(pIStream, pguidVendor, metadataOptions); + res = find_decoder(pIStream, pguidVendor, metadataOptions, &decoder); if (!decoder) - decoder = find_decoder(pIStream, NULL, metadataOptions); + res = find_decoder(pIStream, NULL, metadataOptions, &decoder); if (decoder) { @@ -202,17 +206,17 @@ static HRESULT WINAPI ComponentFactory_CreateDecoderFromStream( BYTE data[4]; ULONG bytesread; - WARN("failed to load from a stream\n"); + WARN("failed to load from a stream %#x\n", res); seek.QuadPart = 0; - res = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL); - if (SUCCEEDED(res)) - res = IStream_Read(pIStream, data, 4, &bytesread); - if (SUCCEEDED(res)) - WARN("first %i bytes of stream=%x %x %x %x\n", bytesread, data[0], data[1], data[2], data[3]); + if (IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL) == S_OK) + { + if (IStream_Read(pIStream, data, 4, &bytesread) == S_OK) + WARN("first %i bytes of stream=%x %x %x %x\n", bytesread, data[0], data[1], data[2], data[3]); + } } *ppIDecoder = NULL; - return WINCODEC_ERR_COMPONENTNOTFOUND; + return res; } } diff --git a/reactos/dll/win32/windowscodecs/info.c b/reactos/dll/win32/windowscodecs/info.c index ca1c1960e02..54710a5b117 100644 --- a/reactos/dll/win32/windowscodecs/info.c +++ b/reactos/dll/win32/windowscodecs/info.c @@ -853,8 +853,12 @@ static HRESULT WINAPI BitmapEncoderInfo_GetMimeTypes(IWICBitmapEncoderInfo *ifac static HRESULT WINAPI BitmapEncoderInfo_GetFileExtensions(IWICBitmapEncoderInfo *iface, UINT cchFileExtensions, WCHAR *wzFileExtensions, UINT *pcchActual) { - FIXME("(%p,%u,%p,%p): stub\n", iface, cchFileExtensions, wzFileExtensions, pcchActual); - return E_NOTIMPL; + BitmapEncoderInfo *This = impl_from_IWICBitmapEncoderInfo(iface); + + TRACE("(%p,%u,%p,%p)\n", iface, cchFileExtensions, wzFileExtensions, pcchActual); + + return ComponentInfo_GetStringValue(This->classkey, fileextensions_valuename, + cchFileExtensions, wzFileExtensions, pcchActual); } static HRESULT WINAPI BitmapEncoderInfo_DoesSupportAnimation(IWICBitmapEncoderInfo *iface, @@ -2317,7 +2321,7 @@ HRESULT WINAPI WICConvertBitmapSource(REFWICPixelFormatGUID dstFormat, IWICBitma if (SUCCEEDED(res) && canconvert) res = IWICFormatConverter_Initialize(converter, pISrc, dstFormat, WICBitmapDitherTypeNone, - NULL, 0.0, WICBitmapPaletteTypeCustom); + NULL, 0.0, WICBitmapPaletteTypeMedianCut); if (FAILED(res) || !canconvert) { diff --git a/reactos/dll/win32/windowscodecs/jpegformat.c b/reactos/dll/win32/windowscodecs/jpegformat.c index 074907453b2..910669abc28 100644 --- a/reactos/dll/win32/windowscodecs/jpegformat.c +++ b/reactos/dll/win32/windowscodecs/jpegformat.c @@ -398,10 +398,14 @@ static HRESULT WINAPI JpegDecoder_CopyPalette(IWICBitmapDecoder *iface, } static HRESULT WINAPI JpegDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface, - IWICMetadataQueryReader **ppIMetadataQueryReader) + IWICMetadataQueryReader **reader) { - FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader); - return E_NOTIMPL; + FIXME("(%p,%p): stub\n", iface, reader); + + if (!reader) return E_INVALIDARG; + + *reader = NULL; + return WINCODEC_ERR_UNSUPPORTEDOPERATION; } static HRESULT WINAPI JpegDecoder_GetPreview(IWICBitmapDecoder *iface, @@ -843,6 +847,8 @@ typedef struct JpegEncoder { double xres, yres; const jpeg_compress_format *format; IStream *stream; + WICColor palette[256]; + UINT colors; CRITICAL_SECTION lock; BYTE dest_buffer[1024]; } JpegEncoder; @@ -1045,10 +1051,24 @@ static HRESULT WINAPI JpegEncoder_Frame_SetColorContexts(IWICBitmapFrameEncode * } static HRESULT WINAPI JpegEncoder_Frame_SetPalette(IWICBitmapFrameEncode *iface, - IWICPalette *pIPalette) + IWICPalette *palette) { - FIXME("(%p,%p): stub\n", iface, pIPalette); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; + JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("(%p,%p)\n", iface, palette); + + if (!palette) return E_INVALIDARG; + + EnterCriticalSection(&This->lock); + + if (This->initialized) + hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); + else + hr = WINCODEC_ERR_NOTINITIALIZED; + + LeaveCriticalSection(&This->lock); + return hr; } static HRESULT WINAPI JpegEncoder_Frame_SetThumbnail(IWICBitmapFrameEncode *iface, @@ -1352,11 +1372,22 @@ static HRESULT WINAPI JpegEncoder_GetContainerFormat(IWICBitmapEncoder *iface, return E_NOTIMPL; } -static HRESULT WINAPI JpegEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, - IWICBitmapEncoderInfo **ppIEncoderInfo) +static HRESULT WINAPI JpegEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) { - FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo); - return E_NOTIMPL; + IWICComponentInfo *comp_info; + HRESULT hr; + + TRACE("%p,%p\n", iface, info); + + if (!info) return E_INVALIDARG; + + hr = CreateComponentInfo(&CLSID_WICJpegEncoder, &comp_info); + if (hr == S_OK) + { + hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); + IWICComponentInfo_Release(comp_info); + } + return hr; } static HRESULT WINAPI JpegEncoder_SetColorContexts(IWICBitmapEncoder *iface, @@ -1406,11 +1437,14 @@ static HRESULT WINAPI JpegEncoder_CreateNewFrame(IWICBitmapEncoder *iface, return WINCODEC_ERR_NOTINITIALIZED; } - hr = CreatePropertyBag2(NULL, 0, ppIEncoderOptions); - if (FAILED(hr)) + if (ppIEncoderOptions) { - LeaveCriticalSection(&This->lock); - return hr; + hr = CreatePropertyBag2(NULL, 0, ppIEncoderOptions); + if (FAILED(hr)) + { + LeaveCriticalSection(&This->lock); + return hr; + } } This->frame_count = 1; @@ -1498,6 +1532,7 @@ HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv) This->xres = This->yres = 0.0; This->format = NULL; This->stream = NULL; + This->colors = 0; InitializeCriticalSection(&This->lock); This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JpegEncoder.lock"); diff --git a/reactos/dll/win32/windowscodecs/main.c b/reactos/dll/win32/windowscodecs/main.c index 4812fca087c..d4762bf1632 100644 --- a/reactos/dll/win32/windowscodecs/main.c +++ b/reactos/dll/win32/windowscodecs/main.c @@ -103,6 +103,12 @@ HRESULT copy_pixels(UINT bpp, const BYTE *srcbuffer, } } +static BOOL is_1bpp_format(const WICPixelFormatGUID *format) +{ + return IsEqualGUID(format, &GUID_WICPixelFormatBlackWhite) || + IsEqualGUID(format, &GUID_WICPixelFormat1bppIndexed); +} + HRESULT configure_write_source(IWICBitmapFrameEncode *iface, IWICBitmapSource *source, const WICRect *prc, const WICPixelFormatGUID *format, @@ -127,11 +133,11 @@ HRESULT configure_write_source(IWICBitmapFrameEncode *iface, format = &dst_format; } - if (!IsEqualGUID(&src_format, format)) + if (!IsEqualGUID(&src_format, format) && !(is_1bpp_format(&src_format) && is_1bpp_format(format))) { /* FIXME: should use WICConvertBitmapSource to convert */ - ERR("format %s unsupported\n", debugstr_guid(&src_format)); - return E_FAIL; + FIXME("format %s unsupported\n", debugstr_guid(&src_format)); + return E_NOTIMPL; } if (xres == 0.0 || yres == 0.0) diff --git a/reactos/dll/win32/windowscodecs/metadataquery.c b/reactos/dll/win32/windowscodecs/metadataquery.c index 4fdd29c2b1d..531506e125b 100644 --- a/reactos/dll/win32/windowscodecs/metadataquery.c +++ b/reactos/dll/win32/windowscodecs/metadataquery.c @@ -95,7 +95,7 @@ static HRESULT WINAPI mqr_GetMetadataByName(IWICMetadataQueryReader *iface, { QueryReader *This = impl_from_IWICMetadataQueryReader(iface); FIXME("(%p,%s,%p)\n", This, wine_dbgstr_w(wzName), pvarValue); - return E_NOTIMPL; + return WINCODEC_ERR_PROPERTYNOTFOUND; } static HRESULT WINAPI mqr_GetEnumerator(IWICMetadataQueryReader *iface, diff --git a/reactos/dll/win32/windowscodecs/palette.c b/reactos/dll/win32/windowscodecs/palette.c index 89b10b7ba94..d31cb554d84 100644 --- a/reactos/dll/win32/windowscodecs/palette.c +++ b/reactos/dll/win32/windowscodecs/palette.c @@ -1,6 +1,7 @@ /* * Copyright 2009 Vincent Povirk for CodeWeavers - * Copyright 2012 Dmitry Timoshkov + * Copyright 2012,2016 Dmitry Timoshkov + * Copyright 2016 Sebastian Lackner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -431,11 +432,279 @@ static HRESULT WINAPI PaletteImpl_InitializeCustom(IWICPalette *iface, return S_OK; } -static HRESULT WINAPI PaletteImpl_InitializeFromBitmap(IWICPalette *iface, - IWICBitmapSource *pISurface, UINT colorCount, BOOL fAddTransparentColor) +#define R_COUNT (1 << 5) +#define R_SHIFT (8 - 5) +#define R_SCALE 2 + +#define G_COUNT (1 << 6) +#define G_SHIFT (8 - 6) +#define G_SCALE 3 + +#define B_COUNT (1 << 5) +#define B_SHIFT (8 - 5) +#define B_SCALE 1 + +struct histogram { - FIXME("(%p,%p,%u,%i): stub\n", iface, pISurface, colorCount, fAddTransparentColor); - return E_NOTIMPL; + unsigned int data[R_COUNT][G_COUNT][B_COUNT]; +}; + +struct box +{ + int r_min, r_max; + int g_min, g_max; + int b_min, b_max; + unsigned int count; + unsigned int score; +}; + +/* count nonzero elements in the histogram range [r_min, r_max] x [g_min, g_max] x [b_min, b_max] */ +static inline unsigned int histogram_count(struct histogram *h, int r_min, int r_max, + int g_min, int g_max, int b_min, int b_max) +{ + unsigned int count = 0; + int r, g, b; + for (r = r_min; r <= r_max; r++) + for (g = g_min; g <= g_max; g++) + for (b = b_min; b <= b_max; b++) + if (h->data[r][g][b] != 0) count++; + return count; +} + +/* compute weighted average color in the range [r_min, r_max] x [g_min, g_max] x [b_min, b_max] */ +static unsigned int histogram_color(struct histogram *h, int r_min, int r_max, + int g_min, int g_max, int b_min, int b_max) +{ + unsigned long long r_sum = 0, g_sum = 0, b_sum = 0; + unsigned int tmp, count = 0; + int r, g, b; + + for (r = r_min; r <= r_max; r++) + for (g = g_min; g <= g_max; g++) + for (b = b_min; b <= b_max; b++) + { + if (!(tmp = h->data[r][g][b])) continue; + r_sum += ((r << R_SHIFT) + ((1 << R_SHIFT) / 2)) * tmp; + g_sum += ((g << G_SHIFT) + ((1 << G_SHIFT) / 2)) * tmp; + b_sum += ((b << B_SHIFT) + ((1 << B_SHIFT) / 2)) * tmp; + count += tmp; + } + + return ((b_sum + (count / 2)) / count) | + ((g_sum + (count / 2)) / count) << 8 | + ((r_sum + (count / 2)) / count) << 16 | 0xff000000; +} + +/* same as histogram_count */ +static inline unsigned int box_count(struct histogram *h, struct box *b) +{ + return histogram_count(h, b->r_min, b->r_max, b->g_min, b->g_max, b->b_min, b->b_max); +} + +/* same as histogram_color */ +static inline unsigned int box_color(struct histogram *h, struct box *b) +{ + return histogram_color(h, b->r_min, b->r_max, b->g_min, b->g_max, b->b_min, b->b_max); +} + +/* compute score used to determine best split (also called "volume") */ +static inline unsigned int box_score(struct box *b) +{ + unsigned int tmp, sum = 0; + tmp = ((b->r_max - b->r_min) << R_SHIFT) * R_SCALE; sum += tmp * tmp; + tmp = ((b->g_max - b->g_min) << G_SHIFT) * G_SCALE; sum += tmp * tmp; + tmp = ((b->b_max - b->b_min) << B_SHIFT) * B_SCALE; sum += tmp * tmp; + return sum; +} + +/* attempt to shrink a box */ +static void shrink_box(struct histogram *h, struct box *b) +{ + int i; + for (i = b->r_min; i <= b->r_max; i++) + if (histogram_count(h, i, i, b->g_min, b->g_max, b->b_min, b->b_max)) { b->r_min = i; break; } + for (i = b->r_max; i >= b->r_min; i--) + if (histogram_count(h, i, i, b->g_min, b->g_max, b->b_min, b->b_max)) { b->r_max = i; break; } + for (i = b->g_min; i <= b->g_max; i++) + if (histogram_count(h, b->r_min, b->r_max, i, i, b->b_min, b->b_max)) { b->g_min = i; break; } + for (i = b->g_max; i >= b->g_min; i--) + if (histogram_count(h, b->r_min, b->r_max, i, i, b->b_min, b->b_max)) { b->g_max = i; break; } + for (i = b->b_min; i <= b->b_max; i++) + if (histogram_count(h, b->r_min, b->r_max, b->g_min, b->g_max, i, i)) { b->b_min = i; break; } + for (i = b->b_max; i >= b->b_min; i--) + if (histogram_count(h, b->r_min, b->r_max, b->g_min, b->g_max, i, i)) { b->b_max = i; break; } + b->count = box_count(h, b); + b->score = box_score(b); +} + +/* helper for split_box */ +static inline void set_avg(int *min, int *max) +{ + int avg = (*min + *max) / 2; + *min = avg + 1; + *max = avg; +} + +/* split a box based on the best axis */ +static void split_box(struct histogram *h, struct box *b1, struct box *b2) +{ + int r = ((b1->r_max - b1->r_min) << R_SHIFT) * R_SCALE; + int g = ((b1->g_max - b1->g_min) << G_SHIFT) * G_SCALE; + int b = ((b1->b_max - b1->b_min) << B_SHIFT) * B_SCALE; + + *b2 = *b1; + + if (r > g) + { + if (b > r) set_avg(&b1->b_min, &b2->b_max); + else set_avg(&b1->r_min, &b2->r_max); + } + else + { + if (b > g) set_avg(&b1->b_min, &b2->b_max); + else set_avg(&b1->g_min, &b2->g_max); + } + + shrink_box(h, b1); + shrink_box(h, b2); +} + +/* find box suitable for split based on count */ +static struct box *find_box_max_count(struct box *b, int count) +{ + struct box *best = NULL; + for (; count--; b++) + if (b->score && (!best || b->count > best->count)) best = b; + return best; +} + +/* find box suitable for split based on score */ +static struct box *find_box_max_score(struct box *b, int count) +{ + struct box *best = NULL; + for (; count--; b++) + if (b->score && (!best || b->score > best->score)) best = b; + return best; +} + +/* compute color map with at most 'desired' colors + * image must be in 24bpp BGR format and colors are returned in 0xAARRGGBB format */ +static int median_cut(unsigned char *image, unsigned int width, unsigned int height, + unsigned int stride, int desired, unsigned int *colors) +{ + struct box boxes[256]; + struct histogram *h; + unsigned int x, y; + unsigned char *p; + struct box *b1, *b2; + int numboxes, i; + + if (!(h = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*h)))) + return 0; + + for (y = 0; y < height; y++) + for (x = 0, p = image + y * stride; x < width; x++, p += 3) + h->data[p[2] >> R_SHIFT][p[1] >> G_SHIFT][p[0] >> B_SHIFT]++; + + numboxes = 1; + boxes[0].r_min = 0; boxes[0].r_max = R_COUNT - 1; + boxes[0].g_min = 0; boxes[0].g_max = G_COUNT - 1; + boxes[0].b_min = 0; boxes[0].b_max = B_COUNT - 1; + shrink_box(h, &boxes[0]); + + while (numboxes <= desired / 2) + { + if (!(b1 = find_box_max_count(boxes, numboxes))) break; + b2 = &boxes[numboxes++]; + split_box(h, b1, b2); + } + while (numboxes < desired) + { + if (!(b1 = find_box_max_score(boxes, numboxes))) break; + b2 = &boxes[numboxes++]; + split_box(h, b1, b2); + } + + for (i = 0; i < numboxes; i++) + colors[i] = box_color(h, &boxes[i]); + + HeapFree(GetProcessHeap(), 0, h); + return numboxes; +} + + +static HRESULT WINAPI PaletteImpl_InitializeFromBitmap(IWICPalette *palette, + IWICBitmapSource *source, UINT desired, BOOL add_transparent) +{ + IWICImagingFactory *factory = NULL; + IWICBitmap *rgb24_bitmap = NULL; + IWICBitmapSource *rgb24_source; + IWICBitmapLock *lock = NULL; + WICPixelFormatGUID format; + HRESULT hr; + UINT width, height, stride, size, actual_number_of_colors; + BYTE *src; + WICColor colors[256]; + + TRACE("(%p,%p,%u,%d)\n", palette, source, desired, add_transparent); + + if (!source || desired < 2 || desired > 256) + return E_INVALIDARG; + + hr = IWICBitmapSource_GetPixelFormat(source, &format); + if (hr != S_OK) return hr; + + /* For interoperability with gdiplus where PixelFormat24bppRGB actully stored + * as BGR (and there is no a corresponding RGB format) we have to use 24bppBGR + * to avoid format conversions. + */ + if (!IsEqualGUID(&format, &GUID_WICPixelFormat24bppBGR)) + { + hr = WICConvertBitmapSource(&GUID_WICPixelFormat24bppBGR, source, &rgb24_source); + if (hr != S_OK) return hr; + } + else + rgb24_source = source; + + hr = ComponentFactory_CreateInstance(&IID_IWICImagingFactory, (void **)&factory); + if (hr != S_OK) goto fail; + + hr = IWICImagingFactory_CreateBitmapFromSource(factory, rgb24_source, WICBitmapCacheOnLoad, &rgb24_bitmap); + if (hr != S_OK) goto fail; + + hr = IWICBitmap_Lock(rgb24_bitmap, NULL, WICBitmapLockRead, &lock); + if (hr != S_OK) goto fail; + + IWICBitmapLock_GetSize(lock, &width, &height); + IWICBitmapLock_GetStride(lock, &stride); + IWICBitmapLock_GetDataPointer(lock, &size, &src); + + actual_number_of_colors = median_cut(src, width, height, stride, add_transparent ? desired - 1 : desired, colors); + TRACE("actual number of colors: %u\n", actual_number_of_colors); + + if (actual_number_of_colors) + { + if (add_transparent) colors[actual_number_of_colors++] = 0; + + hr = IWICPalette_InitializeCustom(palette, colors, actual_number_of_colors); + } + else + hr = E_OUTOFMEMORY; + +fail: + if (lock) + IWICBitmapLock_Release(lock); + + if (rgb24_bitmap) + IWICBitmap_Release(rgb24_bitmap); + + if (factory) + IWICImagingFactory_Release(factory); + + if (rgb24_source != source) + IWICBitmapSource_Release(rgb24_source); + + return hr; } static HRESULT WINAPI PaletteImpl_InitializeFromPalette(IWICPalette *iface, diff --git a/reactos/dll/win32/windowscodecs/pngformat.c b/reactos/dll/win32/windowscodecs/pngformat.c index 4e523b07465..e4f85c09b5a 100644 --- a/reactos/dll/win32/windowscodecs/pngformat.c +++ b/reactos/dll/win32/windowscodecs/pngformat.c @@ -1,5 +1,6 @@ /* * Copyright 2009 Vincent Povirk for CodeWeavers + * Copyright 2016 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,6 +19,8 @@ #include "wincodecs_private.h" +#include + #ifdef HAVE_PNG_H #include #endif @@ -304,18 +307,15 @@ MAKE_FUNCPTR(png_get_tRNS); MAKE_FUNCPTR(png_set_bgr); MAKE_FUNCPTR(png_set_crc_action); MAKE_FUNCPTR(png_set_error_fn); -#ifdef HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8 -MAKE_FUNCPTR(png_set_expand_gray_1_2_4_to_8); -#else -MAKE_FUNCPTR(png_set_gray_1_2_4_to_8); -#endif MAKE_FUNCPTR(png_set_filler); MAKE_FUNCPTR(png_set_gray_to_rgb); MAKE_FUNCPTR(png_set_interlace_handling); MAKE_FUNCPTR(png_set_IHDR); MAKE_FUNCPTR(png_set_pHYs); +MAKE_FUNCPTR(png_set_PLTE); MAKE_FUNCPTR(png_set_read_fn); MAKE_FUNCPTR(png_set_strip_16); +MAKE_FUNCPTR(png_set_tRNS); MAKE_FUNCPTR(png_set_tRNS_to_alpha); MAKE_FUNCPTR(png_set_write_fn); MAKE_FUNCPTR(png_read_end); @@ -369,18 +369,15 @@ static void *load_libpng(void) LOAD_FUNCPTR(png_set_bgr); LOAD_FUNCPTR(png_set_crc_action); LOAD_FUNCPTR(png_set_error_fn); -#ifdef HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8 - LOAD_FUNCPTR(png_set_expand_gray_1_2_4_to_8); -#else - LOAD_FUNCPTR(png_set_gray_1_2_4_to_8); -#endif LOAD_FUNCPTR(png_set_filler); LOAD_FUNCPTR(png_set_gray_to_rgb); LOAD_FUNCPTR(png_set_interlace_handling); LOAD_FUNCPTR(png_set_IHDR); LOAD_FUNCPTR(png_set_pHYs); + LOAD_FUNCPTR(png_set_PLTE); LOAD_FUNCPTR(png_set_read_fn); LOAD_FUNCPTR(png_set_strip_16); + LOAD_FUNCPTR(png_set_tRNS); LOAD_FUNCPTR(png_set_tRNS_to_alpha); LOAD_FUNCPTR(png_set_write_fn); LOAD_FUNCPTR(png_read_end); @@ -565,6 +562,8 @@ static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *p int num_trans; png_uint_32 transparency; png_color_16p trans_values; + png_colorp png_palette; + int num_palette; jmp_buf jmpbuf; BYTE chunk_type[4]; ULONG chunk_size; @@ -607,7 +606,7 @@ static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *p ppng_destroy_read_struct(&This->png_ptr, &This->info_ptr, &This->end_info); HeapFree(GetProcessHeap(), 0, row_pointers); This->png_ptr = NULL; - hr = E_FAIL; + hr = WINCODEC_ERR_UNKNOWNIMAGEFORMAT; goto end; } ppng_set_error_fn(This->png_ptr, jmpbuf, user_error_fn, user_warning_fn); @@ -631,25 +630,11 @@ static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *p /* check for color-keyed alpha */ transparency = ppng_get_tRNS(This->png_ptr, This->info_ptr, &trans, &num_trans, &trans_values); - if (transparency && color_type != PNG_COLOR_TYPE_PALETTE) - { - /* expand to RGBA */ - if (color_type == PNG_COLOR_TYPE_GRAY) - { - if (bit_depth < 8) - { -#ifdef HAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8 - ppng_set_expand_gray_1_2_4_to_8(This->png_ptr); -#else - ppng_set_gray_1_2_4_to_8(This->png_ptr); -#endif - bit_depth = 8; - } - ppng_set_gray_to_rgb(This->png_ptr); - } - ppng_set_tRNS_to_alpha(This->png_ptr); - color_type = PNG_COLOR_TYPE_RGB_ALPHA; - } + if (!ppng_get_PLTE(This->png_ptr, This->info_ptr, &png_palette, &num_palette)) + num_palette = 0; + + TRACE("color_type %d, bit_depth %d, transparency %d, num_palette %d\n", + color_type, bit_depth, transparency, num_palette); switch (color_type) { @@ -657,14 +642,22 @@ static HRESULT WINAPI PngDecoder_Initialize(IWICBitmapDecoder *iface, IStream *p This->bpp = bit_depth; switch (bit_depth) { - case 1: This->format = &GUID_WICPixelFormatBlackWhite; break; - case 2: This->format = &GUID_WICPixelFormat2bppGray; break; - case 4: This->format = &GUID_WICPixelFormat4bppGray; break; - case 8: This->format = &GUID_WICPixelFormat8bppGray; break; + case 1: + This->format = num_palette ? &GUID_WICPixelFormat1bppIndexed : &GUID_WICPixelFormatBlackWhite; + break; + case 2: + This->format = num_palette ? &GUID_WICPixelFormat2bppIndexed : &GUID_WICPixelFormat2bppGray; + break; + case 4: + This->format = num_palette ? &GUID_WICPixelFormat4bppIndexed : &GUID_WICPixelFormat4bppGray; + break; + case 8: + This->format = num_palette ? &GUID_WICPixelFormat8bppIndexed : &GUID_WICPixelFormat8bppGray; + break; case 16: This->format = &GUID_WICPixelFormat16bppGray; break; default: ERR("invalid grayscale bit depth: %i\n", bit_depth); - hr = E_FAIL; + hr = WINCODEC_ERR_UNKNOWNIMAGEFORMAT; goto end; } break; @@ -838,17 +831,21 @@ static HRESULT WINAPI PngDecoder_GetDecoderInfo(IWICBitmapDecoder *iface, } static HRESULT WINAPI PngDecoder_CopyPalette(IWICBitmapDecoder *iface, - IWICPalette *pIPalette) + IWICPalette *palette) { - FIXME("(%p,%p): stub\n", iface, pIPalette); - return E_NOTIMPL; + TRACE("(%p,%p)\n", iface, palette); + return WINCODEC_ERR_PALETTEUNAVAILABLE; } static HRESULT WINAPI PngDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface, - IWICMetadataQueryReader **ppIMetadataQueryReader) + IWICMetadataQueryReader **reader) { - FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader); - return E_NOTIMPL; + FIXME("(%p,%p): stub\n", iface, reader); + + if (!reader) return E_INVALIDARG; + + *reader = NULL; + return WINCODEC_ERR_UNSUPPORTEDOPERATION; } static HRESULT WINAPI PngDecoder_GetPreview(IWICBitmapDecoder *iface, @@ -1318,6 +1315,10 @@ static const struct png_pixelformat formats[] = { {&GUID_WICPixelFormat32bppBGRA, 32, 8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 1}, {&GUID_WICPixelFormat48bppRGB, 48, 16, PNG_COLOR_TYPE_RGB, 0, 0}, {&GUID_WICPixelFormat64bppRGBA, 64, 16, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0}, + {&GUID_WICPixelFormat1bppIndexed, 1, 1, PNG_COLOR_TYPE_PALETTE, 0, 0}, + {&GUID_WICPixelFormat2bppIndexed, 2, 2, PNG_COLOR_TYPE_PALETTE, 0, 0}, + {&GUID_WICPixelFormat4bppIndexed, 4, 4, PNG_COLOR_TYPE_PALETTE, 0, 0}, + {&GUID_WICPixelFormat8bppIndexed, 8, 8, PNG_COLOR_TYPE_PALETTE, 0, 0}, {NULL}, }; @@ -1342,6 +1343,8 @@ typedef struct PngEncoder { BYTE *data; UINT stride; UINT passes; + WICColor palette[256]; + UINT colors; } PngEncoder; static inline PngEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface) @@ -1519,10 +1522,24 @@ static HRESULT WINAPI PngFrameEncode_SetColorContexts(IWICBitmapFrameEncode *ifa } static HRESULT WINAPI PngFrameEncode_SetPalette(IWICBitmapFrameEncode *iface, - IWICPalette *pIPalette) + IWICPalette *palette) { - FIXME("(%p,%p): stub\n", iface, pIPalette); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; + PngEncoder *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("(%p,%p)\n", iface, palette); + + if (!palette) return E_INVALIDARG; + + EnterCriticalSection(&This->lock); + + if (This->frame_initialized) + hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); + else + hr = WINCODEC_ERR_NOTINITIALIZED; + + LeaveCriticalSection(&This->lock); + return hr; } static HRESULT WINAPI PngFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface, @@ -1589,6 +1606,42 @@ static HRESULT WINAPI PngFrameEncode_WritePixels(IWICBitmapFrameEncode *iface, (This->yres+0.0127) / 0.0254, PNG_RESOLUTION_METER); } + if (This->format->color_type == PNG_COLOR_TYPE_PALETTE && This->colors) + { + png_color png_palette[256]; + png_byte trans[256]; + UINT i, num_trans = 0, colors; + + /* Newer libpng versions don't accept larger palettes than the declared + * bit depth, so we need to generate the palette of the correct length. + */ + colors = 1 << This->format->bit_depth; + + for (i = 0; i < colors; i++) + { + if (i < This->colors) + { + png_palette[i].red = (This->palette[i] >> 16) & 0xff; + png_palette[i].green = (This->palette[i] >> 8) & 0xff; + png_palette[i].blue = This->palette[i] & 0xff; + trans[i] = (This->palette[i] >> 24) & 0xff; + if (trans[i] != 0xff) + num_trans++; + } + else + { + png_palette[i].red = 0; + png_palette[i].green = 0; + png_palette[i].blue = 0; + } + } + + ppng_set_PLTE(This->png_ptr, This->info_ptr, png_palette, colors); + + if (num_trans) + ppng_set_tRNS(This->png_ptr, This->info_ptr, trans, colors, NULL); + } + ppng_write_info(This->png_ptr, This->info_ptr); if (This->format->remove_filler) @@ -1872,11 +1925,22 @@ static HRESULT WINAPI PngEncoder_GetContainerFormat(IWICBitmapEncoder *iface, return E_NOTIMPL; } -static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, - IWICBitmapEncoderInfo **ppIEncoderInfo) +static HRESULT WINAPI PngEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) { - FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo); - return E_NOTIMPL; + IWICComponentInfo *comp_info; + HRESULT hr; + + TRACE("%p,%p\n", iface, info); + + if (!info) return E_INVALIDARG; + + hr = CreateComponentInfo(&CLSID_WICPngEncoder, &comp_info); + if (hr == S_OK) + { + hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); + IWICComponentInfo_Release(comp_info); + } + return hr; } static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface, @@ -1886,10 +1950,20 @@ static HRESULT WINAPI PngEncoder_SetColorContexts(IWICBitmapEncoder *iface, return E_NOTIMPL; } -static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette) +static HRESULT WINAPI PngEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette) { - TRACE("(%p,%p)\n", iface, pIPalette); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; + PngEncoder *This = impl_from_IWICBitmapEncoder(iface); + HRESULT hr; + + TRACE("(%p,%p)\n", iface, palette); + + EnterCriticalSection(&This->lock); + + hr = This->stream ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED; + + LeaveCriticalSection(&This->lock); + + return hr; } static HRESULT WINAPI PngEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail) @@ -2027,6 +2101,7 @@ HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv) This->frame_committed = FALSE; This->committed = FALSE; This->data = NULL; + This->colors = 0; InitializeCriticalSection(&This->lock); This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PngEncoder.lock"); diff --git a/reactos/dll/win32/windowscodecs/regsvr.c b/reactos/dll/win32/windowscodecs/regsvr.c index 29807a012de..c9003e893a3 100644 --- a/reactos/dll/win32/windowscodecs/regsvr.c +++ b/reactos/dll/win32/windowscodecs/regsvr.c @@ -1194,6 +1194,8 @@ static GUID const * const tiff_decode_formats[] = { &GUID_WICPixelFormatBlackWhite, &GUID_WICPixelFormat4bppGray, &GUID_WICPixelFormat8bppGray, + &GUID_WICPixelFormat1bppIndexed, + &GUID_WICPixelFormat2bppIndexed, &GUID_WICPixelFormat4bppIndexed, &GUID_WICPixelFormat8bppIndexed, &GUID_WICPixelFormat24bppBGR, @@ -1328,6 +1330,11 @@ static GUID const * const bmp_encode_formats[] = { &GUID_WICPixelFormat16bppBGR565, &GUID_WICPixelFormat24bppBGR, &GUID_WICPixelFormat32bppBGR, + &GUID_WICPixelFormatBlackWhite, + &GUID_WICPixelFormat1bppIndexed, + &GUID_WICPixelFormat2bppIndexed, + &GUID_WICPixelFormat4bppIndexed, + &GUID_WICPixelFormat8bppIndexed, NULL }; @@ -1342,6 +1349,10 @@ static GUID const * const png_encode_formats[] = { &GUID_WICPixelFormat32bppBGRA, &GUID_WICPixelFormat48bppRGB, &GUID_WICPixelFormat64bppRGBA, + &GUID_WICPixelFormat1bppIndexed, + &GUID_WICPixelFormat2bppIndexed, + &GUID_WICPixelFormat4bppIndexed, + &GUID_WICPixelFormat8bppIndexed, NULL }; @@ -1349,6 +1360,10 @@ static GUID const * const tiff_encode_formats[] = { &GUID_WICPixelFormatBlackWhite, &GUID_WICPixelFormat4bppGray, &GUID_WICPixelFormat8bppGray, + &GUID_WICPixelFormat1bppIndexed, + &GUID_WICPixelFormat2bppIndexed, + &GUID_WICPixelFormat4bppIndexed, + &GUID_WICPixelFormat8bppIndexed, &GUID_WICPixelFormat24bppBGR, &GUID_WICPixelFormat32bppBGRA, &GUID_WICPixelFormat32bppPBGRA, @@ -1374,6 +1389,16 @@ static struct regsvr_encoder const encoder_list[] = { ".bmp,.dib,.rle", bmp_encode_formats }, + { &CLSID_WICGifEncoder, + "The Wine Project", + "GIF Encoder", + "1.0.0.0", + &GUID_VendorMicrosoft, + &GUID_ContainerFormatGif, + "image/gif", + ".gif", + gif_formats + }, { &CLSID_WICJpegEncoder, "The Wine Project", "JPEG Encoder", @@ -1706,6 +1731,8 @@ static BYTE const channel_mask_16bit2[] = { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, static BYTE const channel_mask_16bit3[] = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00 }; static BYTE const channel_mask_16bit4[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }; +static BYTE const channel_mask_32bit[] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }; + static BYTE const channel_mask_5bit[] = { 0x1f, 0x00 }; static BYTE const channel_mask_5bit2[] = { 0xe0, 0x03 }; static BYTE const channel_mask_5bit3[] = { 0x00, 0x7c }; @@ -1722,6 +1749,8 @@ static BYTE const * const channel_masks_8bit[] = { channel_mask_8bit, static BYTE const * const channel_masks_16bit[] = { channel_mask_16bit, channel_mask_16bit2, channel_mask_16bit3, channel_mask_16bit4}; +static BYTE const * const channel_masks_32bit[] = { channel_mask_32bit }; + static BYTE const * const channel_masks_BGRA5551[] = { channel_mask_5bit, channel_mask_5bit2, channel_mask_5bit3, channel_mask_5bit4 }; @@ -1916,6 +1945,17 @@ static struct regsvr_pixelformat const pixelformat_list[] = { WICPixelFormatNumericRepresentationUnsignedInteger, 1 }, + { &GUID_WICPixelFormat32bppGrayFloat, + "The Wine Project", + "32bpp GrayFloat", + NULL, /* no version */ + &GUID_VendorMicrosoft, + 32, /* bitsperpixel */ + 1, /* channel count */ + channel_masks_32bit, + WICPixelFormatNumericRepresentationFloat, + 1 + }, { &GUID_WICPixelFormat48bppRGB, "The Wine Project", "48bpp RGB", diff --git a/reactos/dll/win32/windowscodecs/tiffformat.c b/reactos/dll/win32/windowscodecs/tiffformat.c index db3ea19c5e2..b290b6bae3e 100644 --- a/reactos/dll/win32/windowscodecs/tiffformat.c +++ b/reactos/dll/win32/windowscodecs/tiffformat.c @@ -1,5 +1,6 @@ /* * Copyright 2010 Vincent Povirk for CodeWeavers + * Copyright 2016 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -446,6 +447,12 @@ static HRESULT tiff_get_decode_info(TIFF *tiff, tiff_decode_info *decode_info) decode_info->bpp = bps; switch (bps) { + case 1: + decode_info->format = &GUID_WICPixelFormat1bppIndexed; + break; + case 2: + decode_info->format = &GUID_WICPixelFormat2bppIndexed; + break; case 4: decode_info->format = &GUID_WICPixelFormat4bppIndexed; break; @@ -611,7 +618,7 @@ static HRESULT WINAPI TiffDecoder_Initialize(IWICBitmapDecoder *iface, IStream * TIFF *tiff; HRESULT hr=S_OK; - TRACE("(%p,%p,%x): stub\n", iface, pIStream, cacheOptions); + TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions); EnterCriticalSection(&This->lock); @@ -668,17 +675,21 @@ static HRESULT WINAPI TiffDecoder_GetDecoderInfo(IWICBitmapDecoder *iface, } static HRESULT WINAPI TiffDecoder_CopyPalette(IWICBitmapDecoder *iface, - IWICPalette *pIPalette) + IWICPalette *palette) { - FIXME("(%p,%p): stub\n", iface, pIPalette); - return E_NOTIMPL; + TRACE("(%p,%p)\n", iface, palette); + return WINCODEC_ERR_PALETTEUNAVAILABLE; } static HRESULT WINAPI TiffDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface, IWICMetadataQueryReader **ppIMetadataQueryReader) { - FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader); - return E_NOTIMPL; + TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader); + + if (!ppIMetadataQueryReader) return E_INVALIDARG; + + *ppIMetadataQueryReader = NULL; + return WINCODEC_ERR_UNSUPPORTEDOPERATION; } static HRESULT WINAPI TiffDecoder_GetPreview(IWICBitmapDecoder *iface, @@ -1080,7 +1091,7 @@ static HRESULT WINAPI TiffFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface, if (cbStride < bytesperrow) return E_INVALIDARG; - if ((cbStride * prc->Height) > cbBufferSize) + if ((cbStride * (prc->Height-1)) + ((prc->Width * This->decode_info.bpp) + 7)/8 > cbBufferSize) return E_INVALIDARG; min_tile_x = prc->X / This->decode_info.tile_width; @@ -1394,6 +1405,10 @@ static const struct tiff_encode_format formats[] = { {&GUID_WICPixelFormat48bppRGB, 2, 16, 3, 48, 0, 0, 0}, {&GUID_WICPixelFormat64bppRGBA, 2, 16, 4, 64, 1, 2, 0}, {&GUID_WICPixelFormat64bppPRGBA, 2, 16, 4, 64, 1, 1, 0}, + {&GUID_WICPixelFormat1bppIndexed, 3, 1, 1, 1, 0, 0, 0}, + {&GUID_WICPixelFormat2bppIndexed, 3, 2, 1, 2, 0, 0, 0}, + {&GUID_WICPixelFormat4bppIndexed, 3, 4, 1, 4, 0, 0, 0}, + {&GUID_WICPixelFormat8bppIndexed, 3, 8, 1, 8, 0, 0, 0}, {0} }; @@ -1426,6 +1441,8 @@ typedef struct TiffFrameEncode { UINT width, height; double xres, yres; UINT lines_written; + WICColor palette[256]; + UINT colors; } TiffFrameEncode; static inline TiffFrameEncode *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface) @@ -1587,10 +1604,24 @@ static HRESULT WINAPI TiffFrameEncode_SetColorContexts(IWICBitmapFrameEncode *if } static HRESULT WINAPI TiffFrameEncode_SetPalette(IWICBitmapFrameEncode *iface, - IWICPalette *pIPalette) + IWICPalette *palette) { - FIXME("(%p,%p): stub\n", iface, pIPalette); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; + TiffFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface); + HRESULT hr; + + TRACE("(%p,%p)\n", iface, palette); + + if (!palette) return E_INVALIDARG; + + EnterCriticalSection(&This->parent->lock); + + if (This->initialized) + hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); + else + hr = WINCODEC_ERR_NOTINITIALIZED; + + LeaveCriticalSection(&This->parent->lock); + return hr; } static HRESULT WINAPI TiffFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface, @@ -1660,6 +1691,21 @@ static HRESULT WINAPI TiffFrameEncode_WritePixels(IWICBitmapFrameEncode *iface, pTIFFSetField(This->parent->tiff, TIFFTAG_YRESOLUTION, (float)This->yres); } + if (This->format->bpp <= 8 && This->colors && !IsEqualGUID(This->format->guid, &GUID_WICPixelFormatBlackWhite)) + { + uint16 red[256], green[256], blue[256]; + UINT i; + + for (i = 0; i < This->colors; i++) + { + red[i] = (This->palette[i] >> 0) & 0xff00; + green[i] = This->palette[i] & 0xff00; + blue[i] = (This->palette[i] << 8) & 0xff00; + } + + pTIFFSetField(This->parent->tiff, TIFFTAG_COLORMAP, red, green, blue); + } + This->info_written = TRUE; } @@ -1858,11 +1904,22 @@ static HRESULT WINAPI TiffEncoder_GetContainerFormat(IWICBitmapEncoder *iface, return S_OK; } -static HRESULT WINAPI TiffEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, - IWICBitmapEncoderInfo **ppIEncoderInfo) +static HRESULT WINAPI TiffEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) { - FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo); - return E_NOTIMPL; + IWICComponentInfo *comp_info; + HRESULT hr; + + TRACE("%p,%p\n", iface, info); + + if (!info) return E_INVALIDARG; + + hr = CreateComponentInfo(&CLSID_WICTiffEncoder, &comp_info); + if (hr == S_OK) + { + hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); + IWICComponentInfo_Release(comp_info); + } + return hr; } static HRESULT WINAPI TiffEncoder_SetColorContexts(IWICBitmapEncoder *iface, @@ -1872,10 +1929,20 @@ static HRESULT WINAPI TiffEncoder_SetColorContexts(IWICBitmapEncoder *iface, return E_NOTIMPL; } -static HRESULT WINAPI TiffEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette) +static HRESULT WINAPI TiffEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette) { - TRACE("(%p,%p)\n", iface, pIPalette); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; + TiffEncoder *This = impl_from_IWICBitmapEncoder(iface); + HRESULT hr; + + TRACE("(%p,%p)\n", iface, palette); + + EnterCriticalSection(&This->lock); + + hr = This->stream ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED; + + LeaveCriticalSection(&This->lock); + + return hr; } static HRESULT WINAPI TiffEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail) @@ -1912,7 +1979,7 @@ static HRESULT WINAPI TiffEncoder_CreateNewFrame(IWICBitmapEncoder *iface, hr = E_FAIL; } - if (SUCCEEDED(hr)) + if (SUCCEEDED(hr) && ppIEncoderOptions) { PROPBAG2 opts[2]= {{0}}; opts[0].pstrName = (LPOLESTR)wszTiffCompressionMethod; @@ -1930,7 +1997,7 @@ static HRESULT WINAPI TiffEncoder_CreateNewFrame(IWICBitmapEncoder *iface, VARIANT v; VariantInit(&v); V_VT(&v) = VT_UI1; - V_UNION(&v, bVal) = WICTiffCompressionDontCare; + V_UI1(&v) = WICTiffCompressionDontCare; hr = IPropertyBag2_Write(*ppIEncoderOptions, 1, opts, &v); VariantClear(&v); if (FAILED(hr)) @@ -1959,6 +2026,7 @@ static HRESULT WINAPI TiffEncoder_CreateNewFrame(IWICBitmapEncoder *iface, result->xres = 0.0; result->yres = 0.0; result->lines_written = 0; + result->colors = 0; IWICBitmapEncoder_AddRef(iface); *ppIFrameEncode = &result->IWICBitmapFrameEncode_iface; @@ -1971,7 +2039,7 @@ static HRESULT WINAPI TiffEncoder_CreateNewFrame(IWICBitmapEncoder *iface, else hr = E_OUTOFMEMORY; - if (FAILED(hr)) + if (FAILED(hr) && ppIEncoderOptions) { IPropertyBag2_Release(*ppIEncoderOptions); *ppIEncoderOptions = NULL; diff --git a/reactos/dll/win32/windowscodecs/typeof.h b/reactos/dll/win32/windowscodecs/typeof.h index 5bc66d4c937..384091cbd6d 100644 --- a/reactos/dll/win32/windowscodecs/typeof.h +++ b/reactos/dll/win32/windowscodecs/typeof.h @@ -76,7 +76,8 @@ typedef void (__cdecl typeof(png_write_info))(struct png_struct_def *, struct pn typedef void (__cdecl typeof(png_write_rows))(struct png_struct_def *, unsigned char **row, unsigned int); typedef unsigned int (__cdecl typeof(png_get_iCCP))(struct png_struct_def *, struct png_info_def *, char **, int *, char **, unsigned int *); typedef void (__cdecl typeof(png_set_crc_action))(struct png_struct_def *, int, int); - +typedef void (__stdcall typeof(png_set_PLTE))(struct png_struct_def *, struct png_info_def *, const struct png_color_struct *, int); +typedef void (__stdcall typeof(png_set_tRNS))(struct png_struct_def *, struct png_info_def *, const unsigned char *, int, const struct png_color_16_struct *); typedef void *thandle_t_1; typedef int (*TIFFReadWriteProc_1)(thandle_t_1, void *, long); typedef unsigned int (*TIFFSeekProc_1)(void *, unsigned int, int); diff --git a/reactos/dll/win32/windowscodecs/wincodecs_private.h b/reactos/dll/win32/windowscodecs/wincodecs_private.h index e31c5930dfb..6f9a6f5bf92 100644 --- a/reactos/dll/win32/windowscodecs/wincodecs_private.h +++ b/reactos/dll/win32/windowscodecs/wincodecs_private.h @@ -169,6 +169,7 @@ extern HRESULT PngEncoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN extern HRESULT BmpEncoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; extern HRESULT DibDecoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; extern HRESULT GifDecoder_CreateInstance(REFIID riid, void** ppv) DECLSPEC_HIDDEN; +extern HRESULT GifEncoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; extern HRESULT IcoDecoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; extern HRESULT JpegDecoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; extern HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv) DECLSPEC_HIDDEN; diff --git a/reactos/dll/win32/windowscodecs/windowscodecs_wincodec.idl b/reactos/dll/win32/windowscodecs/windowscodecs_wincodec.idl index fd7ff01067d..1681b1de19e 100644 --- a/reactos/dll/win32/windowscodecs/windowscodecs_wincodec.idl +++ b/reactos/dll/win32/windowscodecs/windowscodecs_wincodec.idl @@ -69,6 +69,13 @@ coclass WICBmpEncoder { interface IWICBitmapEncoder; } ] coclass WICGifDecoder { interface IWICBitmapDecoder; } +[ + helpstring("WIC GIF Encoder"), + threading(both), + uuid(114f5598-0b22-40a0-86a1-c83ea495adbd) +] +coclass WICGifEncoder { interface IWICBitmapEncoder; } + [ helpstring("WIC ICO Decoder"), threading(both), diff --git a/reactos/media/doc/README.WINE b/reactos/media/doc/README.WINE index 1873b3fcd0d..01cceedf887 100644 --- a/reactos/media/doc/README.WINE +++ b/reactos/media/doc/README.WINE @@ -196,7 +196,7 @@ reactos/dll/win32/version # Synced to WineStaging-1.9.11 reactos/dll/win32/vssapi # Synced to WineStaging-1.9.11 reactos/dll/win32/wbemdisp # Synced to WineStaging-1.9.16 reactos/dll/win32/wbemprox # Synced to WineStaging-1.9.23 -reactos/dll/win32/windowscodecs # Synced to WineStaging-1.9.16 +reactos/dll/win32/windowscodecs # Synced to WineStaging-1.9.23 reactos/dll/win32/windowscodecsext # Synced to WineStaging-1.9.11 reactos/dll/win32/winemp3.acm # Synced to WineStaging-1.9.11 reactos/dll/win32/wing32 # Synced to WineStaging-1.9.11