Signed-off-by: Esme Povirk esme@codeweavers.com --- dlls/windowscodecs/Makefile.in | 1 + dlls/windowscodecs/decoder.c | 76 +- dlls/windowscodecs/libtiff.c | 1187 ++++++++++++++++++++ dlls/windowscodecs/main.c | 32 +- dlls/windowscodecs/tiffformat.c | 1428 +----------------------- dlls/windowscodecs/unix_iface.c | 1 + dlls/windowscodecs/unix_lib.c | 8 + dlls/windowscodecs/wincodecs_common.h | 19 + dlls/windowscodecs/wincodecs_private.h | 7 + 9 files changed, 1312 insertions(+), 1447 deletions(-) create mode 100644 dlls/windowscodecs/libtiff.c
diff --git a/dlls/windowscodecs/Makefile.in b/dlls/windowscodecs/Makefile.in index 2472ef57d63..2dd6a78e147 100644 --- a/dlls/windowscodecs/Makefile.in +++ b/dlls/windowscodecs/Makefile.in @@ -23,6 +23,7 @@ C_SRCS = \ info.c \ jpegformat.c \ libpng.c \ + libtiff.c \ main.c \ metadatahandler.c \ metadataquery.c \ diff --git a/dlls/windowscodecs/decoder.c b/dlls/windowscodecs/decoder.c index 106aad4b92b..b51ca0a8def 100644 --- a/dlls/windowscodecs/decoder.c +++ b/dlls/windowscodecs/decoder.c @@ -216,9 +216,11 @@ static HRESULT WINAPI CommonDecoder_GetFrameCount(IWICBitmapDecoder *iface, CommonDecoder *This = impl_from_IWICBitmapDecoder(iface); if (!pCount) return E_INVALIDARG;
- if (!This->stream) return WINCODEC_ERR_WRONGSTATE; + if (This->stream) + *pCount = This->file_info.frame_count; + else + *pCount = 0;
- *pCount = This->file_info.frame_count; return S_OK; }
@@ -606,23 +608,69 @@ static HRESULT WINAPI CommonDecoderFrame_Block_GetReaderByIndex(IWICMetadataBloc
if (SUCCEEDED(hr)) { - ULARGE_INTEGER offset, length; + if (This->metadata_blocks[nIndex].options & DECODER_BLOCK_FULL_STREAM) + { + LARGE_INTEGER offset; + offset.QuadPart = This->metadata_blocks[nIndex].offset;
- offset.QuadPart = This->metadata_blocks[nIndex].offset; - length.QuadPart = This->metadata_blocks[nIndex].length; - hr = IWICStream_InitializeFromIStreamRegion(stream, This->parent->stream, - offset, length); + hr = IWICStream_InitializeFromIStream(stream, This->parent->stream);
- if (SUCCEEDED(hr)) - hr = ImagingFactory_CreateInstance(&IID_IWICComponentFactory, (void**)&factory); + if (SUCCEEDED(hr)) + hr = IWICStream_Seek(stream, offset, STREAM_SEEK_SET, NULL); + } + else + { + ULARGE_INTEGER offset, length;
- if (SUCCEEDED(hr)) + offset.QuadPart = This->metadata_blocks[nIndex].offset; + length.QuadPart = This->metadata_blocks[nIndex].length; + + hr = IWICStream_InitializeFromIStreamRegion(stream, This->parent->stream, + offset, length); + } + + if (This->metadata_blocks[nIndex].options & DECODER_BLOCK_READER_CLSID) { - hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory, - &This->parent->decoder_info.block_format, NULL, This->metadata_blocks[nIndex].options, - (IStream*)stream, ppIMetadataReader); + IWICMetadataReader *reader; + IWICPersistStream *persist; + if (SUCCEEDED(hr)) + { + hr = create_instance(&This->metadata_blocks[nIndex].reader_clsid, + &IID_IWICMetadataReader, (void**)&reader); + } + + if (SUCCEEDED(hr)) + { + hr = IWICMetadataReader_QueryInterface(reader, &IID_IWICPersistStream, (void**)&persist);
- IWICComponentFactory_Release(factory); + if (SUCCEEDED(hr)) + { + hr = IWICPersistStream_LoadEx(persist, (IStream*)stream, NULL, + This->metadata_blocks[nIndex].options & DECODER_BLOCK_OPTION_MASK); + + IWICPersistStream_Release(persist); + } + + if (SUCCEEDED(hr)) + *ppIMetadataReader = reader; + else + IWICMetadataReader_Release(reader); + } + } + else + { + if (SUCCEEDED(hr)) + hr = ImagingFactory_CreateInstance(&IID_IWICComponentFactory, (void**)&factory); + + if (SUCCEEDED(hr)) + { + hr = IWICComponentFactory_CreateMetadataReaderFromContainer(factory, + &This->parent->decoder_info.block_format, NULL, + This->metadata_blocks[nIndex].options & DECODER_BLOCK_OPTION_MASK, + (IStream*)stream, ppIMetadataReader); + + IWICComponentFactory_Release(factory); + } }
IWICStream_Release(stream); diff --git a/dlls/windowscodecs/libtiff.c b/dlls/windowscodecs/libtiff.c new file mode 100644 index 00000000000..27f89679766 --- /dev/null +++ b/dlls/windowscodecs/libtiff.c @@ -0,0 +1,1187 @@ +/* + * 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" +#include "wine/port.h" + +#include <stdarg.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_TIFFIO_H +#include <tiffio.h> +#endif + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#include "winbase.h" +#include "objbase.h" + +#include "wincodecs_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); + +#ifdef SONAME_LIBTIFF + +/* Workaround for broken libtiff 4.x headers on some 64-bit hosts which + * define TIFF_UINT64_T/toff_t as 32-bit for 32-bit builds, while they + * are supposed to be always 64-bit. + * TIFF_UINT64_T doesn't exist in libtiff 3.x, it was introduced in 4.x. + */ +#ifdef TIFF_UINT64_T +# undef toff_t +# define toff_t UINT64 +#endif + +static CRITICAL_SECTION init_tiff_cs; +static CRITICAL_SECTION_DEBUG init_tiff_cs_debug = +{ + 0, 0, &init_tiff_cs, + { &init_tiff_cs_debug.ProcessLocksList, + &init_tiff_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": init_tiff_cs") } +}; +static CRITICAL_SECTION init_tiff_cs = { &init_tiff_cs_debug, -1, 0, 0, 0, 0 }; + +static void *libtiff_handle; +#define MAKE_FUNCPTR(f) static typeof(f) * p##f +MAKE_FUNCPTR(TIFFClientOpen); +MAKE_FUNCPTR(TIFFClose); +MAKE_FUNCPTR(TIFFCurrentDirOffset); +MAKE_FUNCPTR(TIFFGetField); +MAKE_FUNCPTR(TIFFIsByteSwapped); +MAKE_FUNCPTR(TIFFNumberOfDirectories); +MAKE_FUNCPTR(TIFFReadDirectory); +MAKE_FUNCPTR(TIFFReadEncodedStrip); +MAKE_FUNCPTR(TIFFReadEncodedTile); +MAKE_FUNCPTR(TIFFSetDirectory); +MAKE_FUNCPTR(TIFFSetField); +MAKE_FUNCPTR(TIFFWriteDirectory); +MAKE_FUNCPTR(TIFFWriteScanline); +#undef MAKE_FUNCPTR + +static void *load_libtiff(void) +{ + void *result; + + RtlEnterCriticalSection(&init_tiff_cs); + + if (!libtiff_handle && + (libtiff_handle = dlopen(SONAME_LIBTIFF, RTLD_NOW)) != NULL) + { + void * (*pTIFFSetWarningHandler)(void *); + void * (*pTIFFSetWarningHandlerExt)(void *); + +#define LOAD_FUNCPTR(f) \ + if((p##f = dlsym(libtiff_handle, #f)) == NULL) { \ + ERR("failed to load symbol %s\n", #f); \ + libtiff_handle = NULL; \ + RtlLeaveCriticalSection(&init_tiff_cs); \ + return NULL; \ + } + LOAD_FUNCPTR(TIFFClientOpen); + LOAD_FUNCPTR(TIFFClose); + LOAD_FUNCPTR(TIFFCurrentDirOffset); + LOAD_FUNCPTR(TIFFGetField); + LOAD_FUNCPTR(TIFFIsByteSwapped); + LOAD_FUNCPTR(TIFFNumberOfDirectories); + LOAD_FUNCPTR(TIFFReadDirectory); + LOAD_FUNCPTR(TIFFReadEncodedStrip); + LOAD_FUNCPTR(TIFFReadEncodedTile); + LOAD_FUNCPTR(TIFFSetDirectory); + LOAD_FUNCPTR(TIFFSetField); + LOAD_FUNCPTR(TIFFWriteDirectory); + LOAD_FUNCPTR(TIFFWriteScanline); +#undef LOAD_FUNCPTR + + if ((pTIFFSetWarningHandler = dlsym(libtiff_handle, "TIFFSetWarningHandler"))) + pTIFFSetWarningHandler(NULL); + if ((pTIFFSetWarningHandlerExt = dlsym(libtiff_handle, "TIFFSetWarningHandlerExt"))) + pTIFFSetWarningHandlerExt(NULL); + } + + result = libtiff_handle; + + RtlLeaveCriticalSection(&init_tiff_cs); + return result; +} + +static tsize_t tiff_stream_read(thandle_t client_data, tdata_t data, tsize_t size) +{ + IStream *stream = (IStream*)client_data; + ULONG bytes_read; + HRESULT hr; + + hr = stream_read(stream, data, size, &bytes_read); + if (FAILED(hr)) bytes_read = 0; + return bytes_read; +} + +static tsize_t tiff_stream_write(thandle_t client_data, tdata_t data, tsize_t size) +{ + FIXME("stub\n"); + return 0; +} + +static toff_t tiff_stream_seek(thandle_t client_data, toff_t offset, int whence) +{ + IStream *stream = (IStream*)client_data; + DWORD origin; + ULONGLONG new_position; + HRESULT hr; + + switch (whence) + { + case SEEK_SET: + origin = STREAM_SEEK_SET; + break; + case SEEK_CUR: + origin = STREAM_SEEK_CUR; + break; + case SEEK_END: + origin = STREAM_SEEK_END; + break; + default: + ERR("unknown whence value %i\n", whence); + return -1; + } + + hr = stream_seek(stream, offset, origin, &new_position); + if (SUCCEEDED(hr)) return new_position; + else return -1; +} + +static int tiff_stream_close(thandle_t client_data) +{ + /* Caller is responsible for releasing the stream object. */ + return 0; +} + +static toff_t tiff_stream_size(thandle_t client_data) +{ + IStream *stream = (IStream*)client_data; + ULONGLONG size; + HRESULT hr; + + hr = stream_getsize(stream, &size); + + if (SUCCEEDED(hr)) return size; + else return -1; +} + +static int tiff_stream_map(thandle_t client_data, tdata_t *addr, toff_t *size) +{ + /* Cannot mmap streams */ + return 0; +} + +static void tiff_stream_unmap(thandle_t client_data, tdata_t addr, toff_t size) +{ + /* No need to ever do this, since we can't map things. */ +} + +static TIFF* tiff_open_stream(IStream *stream, const char *mode) +{ + stream_seek(stream, 0, STREAM_SEEK_SET, NULL); + + return pTIFFClientOpen("<IStream object>", mode, stream, tiff_stream_read, + tiff_stream_write, (void *)tiff_stream_seek, tiff_stream_close, + (void *)tiff_stream_size, (void *)tiff_stream_map, (void *)tiff_stream_unmap); +} + +typedef struct { + struct decoder_frame frame; + int bps; + int samples; + int source_bpp; + int planar; + int indexed; + int reverse_bgr; + int invert_grayscale; + UINT tile_width, tile_height; + UINT tile_stride; + UINT tile_size; + int tiled; + UINT tiles_across; +} tiff_decode_info; + +struct tiff_decoder +{ + struct decoder decoder; + IStream *stream; + TIFF *tiff; + DWORD frame_count; + DWORD cached_frame; + tiff_decode_info cached_decode_info; + INT cached_tile_x, cached_tile_y; + BYTE *cached_tile; +}; + +static inline struct tiff_decoder *impl_from_decoder(struct decoder* iface) +{ + return CONTAINING_RECORD(iface, struct tiff_decoder, decoder); +} + +static HRESULT tiff_get_decode_info(TIFF *tiff, tiff_decode_info *decode_info) +{ + uint16 photometric, bps, samples, planar; + uint16 extra_sample_count, extra_sample, *extra_samples; + uint16 *red, *green, *blue; + UINT resolution_unit; + float xres=0.0, yres=0.0; + int ret, i; + const BYTE *profile; + UINT len; + + decode_info->indexed = 0; + decode_info->reverse_bgr = 0; + decode_info->invert_grayscale = 0; + decode_info->tiled = 0; + decode_info->source_bpp = 0; + + ret = pTIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric); + if (!ret) + { + WARN("missing PhotometricInterpretation tag\n"); + return E_FAIL; + } + + ret = pTIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bps); + if (!ret) bps = 1; + decode_info->bps = bps; + + ret = pTIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samples); + if (!ret) samples = 1; + decode_info->samples = samples; + + if (samples == 1) + planar = 1; + else + { + ret = pTIFFGetField(tiff, TIFFTAG_PLANARCONFIG, &planar); + if (!ret) planar = 1; + if (planar != 1) + { + FIXME("unhandled planar configuration %u\n", planar); + return E_FAIL; + } + } + decode_info->planar = planar; + + TRACE("planar %u, photometric %u, samples %u, bps %u\n", planar, photometric, samples, bps); + + switch(photometric) + { + case 0: /* WhiteIsZero */ + decode_info->invert_grayscale = 1; + /* fall through */ + case 1: /* BlackIsZero */ + if (samples == 2) + { + ret = pTIFFGetField(tiff, TIFFTAG_EXTRASAMPLES, &extra_sample_count, &extra_samples); + if (!ret) + { + extra_sample_count = 1; + extra_sample = 0; + extra_samples = &extra_sample; + } + } + else if (samples != 1) + { + FIXME("unhandled %dbpp sample count %u\n", bps, samples); + return E_FAIL; + } + + decode_info->frame.bpp = bps * samples; + decode_info->source_bpp = decode_info->frame.bpp; + switch (bps) + { + case 1: + if (samples != 1) + { + FIXME("unhandled 1bpp sample count %u\n", samples); + return E_FAIL; + } + decode_info->frame.pixel_format = GUID_WICPixelFormatBlackWhite; + break; + case 4: + if (samples != 1) + { + FIXME("unhandled 4bpp grayscale sample count %u\n", samples); + return E_FAIL; + } + decode_info->frame.pixel_format = GUID_WICPixelFormat4bppGray; + break; + case 8: + if (samples == 1) + decode_info->frame.pixel_format = GUID_WICPixelFormat8bppGray; + else + { + decode_info->frame.bpp = 32; + + switch(extra_samples[0]) + { + case 1: /* Associated (pre-multiplied) alpha data */ + decode_info->frame.pixel_format = GUID_WICPixelFormat32bppPBGRA; + break; + case 0: /* Unspecified data */ + case 2: /* Unassociated alpha data */ + decode_info->frame.pixel_format = GUID_WICPixelFormat32bppBGRA; + break; + default: + FIXME("unhandled extra sample type %u\n", extra_samples[0]); + return E_FAIL; + } + } + break; + case 16: + if (samples != 1) + { + FIXME("unhandled 16bpp grayscale sample count %u\n", samples); + return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; + } + decode_info->frame.pixel_format = GUID_WICPixelFormat16bppGray; + break; + case 32: + if (samples != 1) + { + FIXME("unhandled 32bpp grayscale sample count %u\n", samples); + return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; + } + decode_info->frame.pixel_format = GUID_WICPixelFormat32bppGrayFloat; + break; + default: + WARN("unhandled greyscale bit count %u\n", bps); + return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; + } + break; + case 2: /* RGB */ + if (samples == 4) + { + ret = pTIFFGetField(tiff, TIFFTAG_EXTRASAMPLES, &extra_sample_count, &extra_samples); + if (!ret) + { + extra_sample_count = 1; + extra_sample = 0; + extra_samples = &extra_sample; + } + } + else if (samples != 3) + { + FIXME("unhandled RGB sample count %u\n", samples); + return E_FAIL; + } + + decode_info->frame.bpp = max(bps, 8) * samples; + decode_info->source_bpp = bps * samples; + switch(bps) + { + case 1: + case 4: + case 8: + decode_info->reverse_bgr = 1; + if (samples == 3) + decode_info->frame.pixel_format = GUID_WICPixelFormat24bppBGR; + else + switch(extra_samples[0]) + { + case 1: /* Associated (pre-multiplied) alpha data */ + decode_info->frame.pixel_format = GUID_WICPixelFormat32bppPBGRA; + break; + case 0: /* Unspecified data */ + case 2: /* Unassociated alpha data */ + decode_info->frame.pixel_format = GUID_WICPixelFormat32bppBGRA; + break; + default: + FIXME("unhandled extra sample type %i\n", extra_samples[0]); + return E_FAIL; + } + break; + case 16: + if (samples == 3) + decode_info->frame.pixel_format = GUID_WICPixelFormat48bppRGB; + else + switch(extra_samples[0]) + { + case 1: /* Associated (pre-multiplied) alpha data */ + decode_info->frame.pixel_format = GUID_WICPixelFormat64bppPRGBA; + break; + case 0: /* Unspecified data */ + case 2: /* Unassociated alpha data */ + decode_info->frame.pixel_format = GUID_WICPixelFormat64bppRGBA; + break; + default: + FIXME("unhandled extra sample type %i\n", extra_samples[0]); + return E_FAIL; + } + break; + case 32: + if (samples == 3) + decode_info->frame.pixel_format = GUID_WICPixelFormat96bppRGBFloat; + else + switch(extra_samples[0]) + { + case 1: /* Associated (pre-multiplied) alpha data */ + decode_info->frame.pixel_format = GUID_WICPixelFormat128bppPRGBAFloat; + break; + case 0: /* Unspecified data */ + case 2: /* Unassociated alpha data */ + decode_info->frame.pixel_format = GUID_WICPixelFormat128bppRGBAFloat; + break; + default: + FIXME("unhandled extra sample type %i\n", extra_samples[0]); + return E_FAIL; + } + break; + default: + WARN("unhandled RGB bit count %u\n", bps); + return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; + } + break; + case 3: /* RGB Palette */ + if (samples != 1) + { + FIXME("unhandled indexed sample count %u\n", samples); + return E_FAIL; + } + + decode_info->indexed = 1; + decode_info->frame.bpp = bps; + switch (bps) + { + case 1: + decode_info->frame.pixel_format = GUID_WICPixelFormat1bppIndexed; + break; + case 2: + decode_info->frame.pixel_format = GUID_WICPixelFormat2bppIndexed; + break; + case 4: + decode_info->frame.pixel_format = GUID_WICPixelFormat4bppIndexed; + break; + case 8: + decode_info->frame.pixel_format = GUID_WICPixelFormat8bppIndexed; + break; + default: + FIXME("unhandled indexed bit count %u\n", bps); + return E_NOTIMPL; + } + break; + + case 5: /* Separated */ + if (samples != 4) + { + FIXME("unhandled Separated sample count %u\n", samples); + return E_FAIL; + } + + decode_info->frame.bpp = bps * samples; + switch(bps) + { + case 8: + decode_info->frame.pixel_format = GUID_WICPixelFormat32bppCMYK; + break; + case 16: + decode_info->frame.pixel_format = GUID_WICPixelFormat64bppCMYK; + break; + + default: + WARN("unhandled Separated bit count %u\n", bps); + return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; + } + break; + + case 4: /* Transparency mask */ + case 6: /* YCbCr */ + case 8: /* CIELab */ + default: + FIXME("unhandled PhotometricInterpretation %u\n", photometric); + return E_FAIL; + } + + ret = pTIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &decode_info->frame.width); + if (!ret) + { + WARN("missing image width\n"); + return E_FAIL; + } + + ret = pTIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &decode_info->frame.height); + if (!ret) + { + WARN("missing image length\n"); + return E_FAIL; + } + + if ((ret = pTIFFGetField(tiff, TIFFTAG_TILEWIDTH, &decode_info->tile_width))) + { + decode_info->tiled = 1; + + ret = pTIFFGetField(tiff, TIFFTAG_TILELENGTH, &decode_info->tile_height); + if (!ret) + { + WARN("missing tile height\n"); + return E_FAIL; + } + + decode_info->tile_stride = ((decode_info->frame.bpp * decode_info->tile_width + 7)/8); + decode_info->tile_size = decode_info->tile_height * decode_info->tile_stride; + decode_info->tiles_across = (decode_info->frame.width + decode_info->tile_width - 1) / decode_info->tile_width; + } + else if ((ret = pTIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &decode_info->tile_height))) + { + if (decode_info->tile_height > decode_info->frame.height) + decode_info->tile_height = decode_info->frame.height; + decode_info->tile_width = decode_info->frame.width; + decode_info->tile_stride = ((decode_info->frame.bpp * decode_info->tile_width + 7)/8); + decode_info->tile_size = decode_info->tile_height * decode_info->tile_stride; + } + else + { + /* Some broken TIFF files have a single strip and lack the RowsPerStrip tag */ + decode_info->tile_height = decode_info->frame.height; + decode_info->tile_width = decode_info->frame.width; + decode_info->tile_stride = ((decode_info->frame.bpp * decode_info->tile_width + 7)/8); + decode_info->tile_size = decode_info->tile_height * decode_info->tile_stride; + } + + resolution_unit = 0; + pTIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &resolution_unit); + + ret = pTIFFGetField(tiff, TIFFTAG_XRESOLUTION, &xres); + if (!ret) + { + WARN("missing X resolution\n"); + } + /* Emulate the behavior of current libtiff versions (libtiff commit a39f6131) + * yielding 0 instead of INFINITY for IFD_RATIONAL fields with denominator 0. */ + if (!isfinite(xres)) + { + xres = 0.0; + } + + ret = pTIFFGetField(tiff, TIFFTAG_YRESOLUTION, &yres); + if (!ret) + { + WARN("missing Y resolution\n"); + } + if (!isfinite(yres)) + { + yres = 0.0; + } + + if (xres == 0.0 || yres == 0.0) + { + decode_info->frame.dpix = decode_info->frame.dpiy = 96.0; + } + else + { + switch (resolution_unit) + { + default: + FIXME("unknown resolution unit %i\n", resolution_unit); + /* fall through */ + case 0: /* Not set */ + case 1: /* Relative measurements */ + case 2: /* Inch */ + decode_info->frame.dpix = xres; + decode_info->frame.dpiy = yres; + break; + case 3: /* Centimeter */ + decode_info->frame.dpix = xres * 2.54; + decode_info->frame.dpiy = yres * 2.54; + break; + } + } + + if (decode_info->indexed && + pTIFFGetField(tiff, TIFFTAG_COLORMAP, &red, &green, &blue)) + { + decode_info->frame.num_colors = 1 << decode_info->bps; + for (i=0; i<decode_info->frame.num_colors; i++) + { + decode_info->frame.palette[i] = 0xff000000 | + ((red[i]<<8) & 0xff0000) | + (green[i] & 0xff00) | + ((blue[i]>>8) & 0xff); + } + } + else + { + decode_info->frame.num_colors = 0; + } + + if (pTIFFGetField(tiff, TIFFTAG_ICCPROFILE, &len, &profile)) + decode_info->frame.num_color_contexts = 1; + else + decode_info->frame.num_color_contexts = 0; + + return S_OK; +} + +static HRESULT CDECL tiff_decoder_initialize(struct decoder* iface, IStream *stream, struct decoder_stat *st) +{ + struct tiff_decoder *This = impl_from_decoder(iface); + HRESULT hr; + + This->tiff = tiff_open_stream(stream, "r"); + if (!This->tiff) + return E_FAIL; + + This->frame_count = pTIFFNumberOfDirectories(This->tiff); + This->cached_frame = 0; + hr = tiff_get_decode_info(This->tiff, &This->cached_decode_info); + if (FAILED(hr)) + goto fail; + + st->frame_count = This->frame_count; + st->flags = WICBitmapDecoderCapabilityCanDecodeAllImages | + WICBitmapDecoderCapabilityCanDecodeSomeImages | + WICBitmapDecoderCapabilityCanEnumerateMetadata; + return S_OK; + +fail: + pTIFFClose(This->tiff); + This->tiff = NULL; + return hr; +} + +static HRESULT tiff_decoder_select_frame(struct tiff_decoder* This, DWORD frame) +{ + HRESULT hr; + UINT prev_tile_size; + int res; + + if (frame >= This->frame_count) + return E_INVALIDARG; + + if (This->cached_frame == frame) + return S_OK; + + prev_tile_size = This->cached_tile ? This->cached_decode_info.tile_size : 0; + + res = pTIFFSetDirectory(This->tiff, frame); + if (!res) + return E_INVALIDARG; + + hr = tiff_get_decode_info(This->tiff, &This->cached_decode_info); + + This->cached_tile_x = -1; + + if (SUCCEEDED(hr)) + { + This->cached_frame = frame; + if (This->cached_decode_info.tile_size > prev_tile_size) + { + free(This->cached_tile); + This->cached_tile = NULL; + } + } + else + { + /* Set an invalid value to ensure we'll refresh cached_decode_info before using it. */ + This->cached_frame = This->frame_count; + free(This->cached_tile); + This->cached_tile = NULL; + } + + return hr; +} + +static HRESULT CDECL tiff_decoder_get_frame_info(struct decoder* iface, UINT frame, struct decoder_frame *info) +{ + struct tiff_decoder *This = impl_from_decoder(iface); + HRESULT hr; + + hr = tiff_decoder_select_frame(This, frame); + if (SUCCEEDED(hr)) + { + *info = This->cached_decode_info.frame; + } + + return hr; +} + +static HRESULT tiff_decoder_read_tile(struct tiff_decoder *This, UINT tile_x, UINT tile_y) +{ + tsize_t ret; + int swap_bytes; + tiff_decode_info *info = &This->cached_decode_info; + + swap_bytes = pTIFFIsByteSwapped(This->tiff); + + if (info->tiled) + ret = pTIFFReadEncodedTile(This->tiff, tile_x + tile_y * info->tiles_across, This->cached_tile, info->tile_size); + else + ret = pTIFFReadEncodedStrip(This->tiff, tile_y, This->cached_tile, info->tile_size); + + if (ret == -1) + return E_FAIL; + + /* 3bps RGB */ + if (info->source_bpp == 3 && info->samples == 3 && info->frame.bpp == 24) + { + BYTE *srcdata, *src, *dst; + DWORD x, y, count, width_bytes = (info->tile_width * 3 + 7) / 8; + + count = width_bytes * info->tile_height; + + srcdata = malloc(count); + if (!srcdata) return E_OUTOFMEMORY; + memcpy(srcdata, This->cached_tile, count); + + for (y = 0; y < info->tile_height; y++) + { + src = srcdata + y * width_bytes; + dst = This->cached_tile + y * info->tile_width * 3; + + for (x = 0; x < info->tile_width; x += 8) + { + dst[2] = (src[0] & 0x80) ? 0xff : 0; /* R */ + dst[1] = (src[0] & 0x40) ? 0xff : 0; /* G */ + dst[0] = (src[0] & 0x20) ? 0xff : 0; /* B */ + if (x + 1 < info->tile_width) + { + dst[5] = (src[0] & 0x10) ? 0xff : 0; /* R */ + dst[4] = (src[0] & 0x08) ? 0xff : 0; /* G */ + dst[3] = (src[0] & 0x04) ? 0xff : 0; /* B */ + } + if (x + 2 < info->tile_width) + { + dst[8] = (src[0] & 0x02) ? 0xff : 0; /* R */ + dst[7] = (src[0] & 0x01) ? 0xff : 0; /* G */ + dst[6] = (src[1] & 0x80) ? 0xff : 0; /* B */ + } + if (x + 3 < info->tile_width) + { + dst[11] = (src[1] & 0x40) ? 0xff : 0; /* R */ + dst[10] = (src[1] & 0x20) ? 0xff : 0; /* G */ + dst[9] = (src[1] & 0x10) ? 0xff : 0; /* B */ + } + if (x + 4 < info->tile_width) + { + dst[14] = (src[1] & 0x08) ? 0xff : 0; /* R */ + dst[13] = (src[1] & 0x04) ? 0xff : 0; /* G */ + dst[12] = (src[1] & 0x02) ? 0xff : 0; /* B */ + } + if (x + 5 < info->tile_width) + { + dst[17] = (src[1] & 0x01) ? 0xff : 0; /* R */ + dst[16] = (src[2] & 0x80) ? 0xff : 0; /* G */ + dst[15] = (src[2] & 0x40) ? 0xff : 0; /* B */ + } + if (x + 6 < info->tile_width) + { + dst[20] = (src[2] & 0x20) ? 0xff : 0; /* R */ + dst[19] = (src[2] & 0x10) ? 0xff : 0; /* G */ + dst[18] = (src[2] & 0x08) ? 0xff : 0; /* B */ + } + if (x + 7 < info->tile_width) + { + dst[23] = (src[2] & 0x04) ? 0xff : 0; /* R */ + dst[22] = (src[2] & 0x02) ? 0xff : 0; /* G */ + dst[21] = (src[2] & 0x01) ? 0xff : 0; /* B */ + } + src += 3; + dst += 24; + } + } + + free(srcdata); + } + /* 12bps RGB */ + else if (info->source_bpp == 12 && info->samples == 3 && info->frame.bpp == 24) + { + BYTE *srcdata, *src, *dst; + DWORD x, y, count, width_bytes = (info->tile_width * 12 + 7) / 8; + + count = width_bytes * info->tile_height; + + srcdata = malloc(count); + if (!srcdata) return E_OUTOFMEMORY; + memcpy(srcdata, This->cached_tile, count); + + for (y = 0; y < info->tile_height; y++) + { + src = srcdata + y * width_bytes; + dst = This->cached_tile + y * info->tile_width * 3; + + for (x = 0; x < info->tile_width; x += 2) + { + dst[0] = ((src[1] & 0xf0) >> 4) * 17; /* B */ + dst[1] = (src[0] & 0x0f) * 17; /* G */ + dst[2] = ((src[0] & 0xf0) >> 4) * 17; /* R */ + if (x + 1 < info->tile_width) + { + dst[5] = (src[1] & 0x0f) * 17; /* B */ + dst[4] = ((src[2] & 0xf0) >> 4) * 17; /* G */ + dst[3] = (src[2] & 0x0f) * 17; /* R */ + } + src += 3; + dst += 6; + } + } + + free(srcdata); + } + /* 4bps RGBA */ + else if (info->source_bpp == 4 && info->samples == 4 && info->frame.bpp == 32) + { + BYTE *srcdata, *src, *dst; + DWORD x, y, count, width_bytes = (info->tile_width * 3 + 7) / 8; + + count = width_bytes * info->tile_height; + + srcdata = malloc(count); + if (!srcdata) return E_OUTOFMEMORY; + memcpy(srcdata, This->cached_tile, count); + + for (y = 0; y < info->tile_height; y++) + { + src = srcdata + y * width_bytes; + dst = This->cached_tile + y * info->tile_width * 4; + + /* 1 source byte expands to 2 BGRA samples */ + + for (x = 0; x < info->tile_width; x += 2) + { + dst[0] = (src[0] & 0x20) ? 0xff : 0; /* B */ + dst[1] = (src[0] & 0x40) ? 0xff : 0; /* G */ + dst[2] = (src[0] & 0x80) ? 0xff : 0; /* R */ + dst[3] = (src[0] & 0x10) ? 0xff : 0; /* A */ + if (x + 1 < info->tile_width) + { + dst[4] = (src[0] & 0x02) ? 0xff : 0; /* B */ + dst[5] = (src[0] & 0x04) ? 0xff : 0; /* G */ + dst[6] = (src[0] & 0x08) ? 0xff : 0; /* R */ + dst[7] = (src[0] & 0x01) ? 0xff : 0; /* A */ + } + src++; + dst += 8; + } + } + + free(srcdata); + } + /* 16bps RGBA */ + else if (info->source_bpp == 16 && info->samples == 4 && info->frame.bpp == 32) + { + BYTE *srcdata, *src, *dst; + DWORD x, y, count, width_bytes = (info->tile_width * 12 + 7) / 8; + + count = width_bytes * info->tile_height; + + srcdata = malloc(count); + if (!srcdata) return E_OUTOFMEMORY; + memcpy(srcdata, This->cached_tile, count); + + for (y = 0; y < info->tile_height; y++) + { + src = srcdata + y * width_bytes; + dst = This->cached_tile + y * info->tile_width * 4; + + for (x = 0; x < info->tile_width; x++) + { + dst[0] = ((src[1] & 0xf0) >> 4) * 17; /* B */ + dst[1] = (src[0] & 0x0f) * 17; /* G */ + dst[2] = ((src[0] & 0xf0) >> 4) * 17; /* R */ + dst[3] = (src[1] & 0x0f) * 17; /* A */ + src += 2; + dst += 4; + } + } + + free(srcdata); + } + /* 8bpp grayscale with extra alpha */ + else if (info->source_bpp == 16 && info->samples == 2 && info->frame.bpp == 32) + { + BYTE *src; + DWORD *dst, count = info->tile_width * info->tile_height; + + src = This->cached_tile + info->tile_width * info->tile_height * 2 - 2; + dst = (DWORD *)(This->cached_tile + info->tile_size - 4); + + while (count--) + { + *dst-- = src[0] | (src[0] << 8) | (src[0] << 16) | (src[1] << 24); + src -= 2; + } + } + + if (info->reverse_bgr) + { + if (info->bps == 8) + { + UINT sample_count = info->samples; + + reverse_bgr8(sample_count, This->cached_tile, info->tile_width, + info->tile_height, info->tile_width * sample_count); + } + } + + if (swap_bytes && info->bps > 8) + { + UINT row, i, samples_per_row; + BYTE *sample, temp; + + samples_per_row = info->tile_width * info->samples; + + switch(info->bps) + { + case 16: + for (row=0; row<info->tile_height; row++) + { + sample = This->cached_tile + row * info->tile_stride; + for (i=0; i<samples_per_row; i++) + { + temp = sample[1]; + sample[1] = sample[0]; + sample[0] = temp; + sample += 2; + } + } + break; + default: + ERR("unhandled bps for byte swap %u\n", info->bps); + return E_FAIL; + } + } + + if (info->invert_grayscale) + { + BYTE *byte, *end; + + if (info->samples != 1) + { + ERR("cannot invert grayscale image with %u samples\n", info->samples); + return E_FAIL; + } + + end = This->cached_tile+info->tile_size; + + for (byte = This->cached_tile; byte != end; byte++) + *byte = ~(*byte); + } + + This->cached_tile_x = tile_x; + This->cached_tile_y = tile_y; + + return S_OK; +} + +static HRESULT CDECL tiff_decoder_copy_pixels(struct decoder* iface, UINT frame, + const WICRect *prc, UINT stride, UINT buffersize, BYTE *buffer) +{ + struct tiff_decoder *This = impl_from_decoder(iface); + HRESULT hr; + UINT min_tile_x, max_tile_x, min_tile_y, max_tile_y; + UINT tile_x, tile_y; + BYTE *dst_tilepos; + WICRect rc; + tiff_decode_info *info = &This->cached_decode_info; + + hr = tiff_decoder_select_frame(This, frame); + if (FAILED(hr)) + return hr; + + if (!This->cached_tile) + { + This->cached_tile = malloc(info->tile_size); + if (!This->cached_tile) + return E_OUTOFMEMORY; + } + + min_tile_x = prc->X / info->tile_width; + min_tile_y = prc->Y / info->tile_height; + max_tile_x = (prc->X+prc->Width-1) / info->tile_width; + max_tile_y = (prc->Y+prc->Height-1) / info->tile_height; + + for (tile_x=min_tile_x; tile_x <= max_tile_x; tile_x++) + { + for (tile_y=min_tile_y; tile_y <= max_tile_y; tile_y++) + { + if (tile_x != This->cached_tile_x || tile_y != This->cached_tile_y) + { + hr = tiff_decoder_read_tile(This, tile_x, tile_y); + } + + if (SUCCEEDED(hr)) + { + if (prc->X < tile_x * info->tile_width) + rc.X = 0; + else + rc.X = prc->X - tile_x * info->tile_width; + + if (prc->Y < tile_y * info->tile_height) + rc.Y = 0; + else + rc.Y = prc->Y - tile_y * info->tile_height; + + if (prc->X+prc->Width > (tile_x+1) * info->tile_width) + rc.Width = info->tile_width - rc.X; + else if (prc->X < tile_x * info->tile_width) + rc.Width = prc->Width + prc->X - tile_x * info->tile_width; + else + rc.Width = prc->Width; + + if (prc->Y+prc->Height > (tile_y+1) * info->tile_height) + rc.Height = info->tile_height - rc.Y; + else if (prc->Y < tile_y * info->tile_height) + rc.Height = prc->Height + prc->Y - tile_y * info->tile_height; + else + rc.Height = prc->Height; + + dst_tilepos = buffer + (stride * ((rc.Y + tile_y * info->tile_height) - prc->Y)) + + ((info->frame.bpp * ((rc.X + tile_x * info->tile_width) - prc->X) + 7) / 8); + + hr = copy_pixels(info->frame.bpp, This->cached_tile, + info->tile_width, info->tile_height, info->tile_stride, + &rc, stride, buffersize, dst_tilepos); + } + + if (FAILED(hr)) + { + TRACE("<-- 0x%x\n", hr); + return hr; + } + } + } + + return S_OK; +} + +static HRESULT CDECL tiff_decoder_get_color_context(struct decoder *iface, + UINT frame, UINT num, BYTE **data, DWORD *datasize) +{ + struct tiff_decoder *This = impl_from_decoder(iface); + const BYTE *profile; + UINT len; + HRESULT hr; + + hr = tiff_decoder_select_frame(This, frame); + if (FAILED(hr)) + return hr; + + if (!pTIFFGetField(This->tiff, TIFFTAG_ICCPROFILE, &len, &profile)) + { + return E_UNEXPECTED; + } + + *datasize = len; + *data = RtlAllocateHeap(GetProcessHeap(), 0, len); + if (!*data) + return E_OUTOFMEMORY; + + memcpy(*data, profile, len); + + return S_OK; +} + +static HRESULT CDECL tiff_decoder_get_metadata_blocks(struct decoder *iface, + UINT frame, UINT *count, struct decoder_block **blocks) +{ + struct tiff_decoder *This = impl_from_decoder(iface); + HRESULT hr; + BOOL byte_swapped; + struct decoder_block result; + + hr = tiff_decoder_select_frame(This, frame); + if (FAILED(hr)) + return hr; + + *count = 1; + + result.offset = pTIFFCurrentDirOffset(This->tiff); + result.length = 0; + + byte_swapped = pTIFFIsByteSwapped(This->tiff); +#ifdef WORDS_BIGENDIAN + result.options = byte_swapped ? WICPersistOptionLittleEndian : WICPersistOptionBigEndian; +#else + result.options = byte_swapped ? WICPersistOptionBigEndian : WICPersistOptionLittleEndian; +#endif + result.options |= WICPersistOptionNoCacheStream|DECODER_BLOCK_FULL_STREAM|DECODER_BLOCK_READER_CLSID; + result.reader_clsid = CLSID_WICIfdMetadataReader; + + *blocks = malloc(sizeof(**blocks)); + **blocks = result; + + return S_OK; +} + +static void CDECL tiff_decoder_destroy(struct decoder* iface) +{ + struct tiff_decoder *This = impl_from_decoder(iface); + if (This->tiff) pTIFFClose(This->tiff); + free(This->cached_tile); + RtlFreeHeap(GetProcessHeap(), 0, This); +} + +static const struct decoder_funcs tiff_decoder_vtable = { + tiff_decoder_initialize, + tiff_decoder_get_frame_info, + tiff_decoder_copy_pixels, + tiff_decoder_get_metadata_blocks, + tiff_decoder_get_color_context, + tiff_decoder_destroy +}; + +HRESULT CDECL tiff_decoder_create(struct decoder_info *info, struct decoder **result) +{ + struct tiff_decoder *This; + + if (!load_libtiff()) + { + ERR("Failed reading TIFF because unable to load %s\n",SONAME_LIBTIFF); + return E_FAIL; + } + + This = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(*This)); + if (!This) return E_OUTOFMEMORY; + + This->decoder.vtable = &tiff_decoder_vtable; + This->tiff = NULL; + This->cached_tile = NULL; + This->cached_tile_x = -1; + *result = &This->decoder; + + info->container_format = GUID_ContainerFormatTiff; + info->block_format = GUID_ContainerFormatTiff; + info->clsid = CLSID_WICTiffDecoder; + + return S_OK; +} + +#else /* !SONAME_LIBTIFF */ + +HRESULT CDECL tiff_decoder_create(struct decoder_info *info, struct decoder **result) +{ + ERR("Trying to load TIFF picture, but Wine was compiled without TIFF support.\n"); + return E_FAIL; +} + +#endif diff --git a/dlls/windowscodecs/main.c b/dlls/windowscodecs/main.c index 1b5057b1b95..7976b96a393 100644 --- a/dlls/windowscodecs/main.c +++ b/dlls/windowscodecs/main.c @@ -191,6 +191,19 @@ HRESULT write_source(IWICBitmapFrameEncode *iface, return hr; }
+HRESULT CDECL stream_getsize(IStream *stream, ULONGLONG *size) +{ + STATSTG statstg; + HRESULT hr; + + hr = IStream_Stat(stream, &statstg, STATFLAG_NONAME); + + if (SUCCEEDED(hr)) + *size = statstg.cbSize.QuadPart; + + return hr; +} + HRESULT CDECL stream_read(IStream *stream, void *buffer, ULONG read, ULONG *bytes_read) { return IStream_Read(stream, buffer, read, bytes_read); @@ -210,25 +223,6 @@ HRESULT CDECL stream_seek(IStream *stream, LONGLONG ofs, DWORD origin, ULONGLONG return hr; }
-void reverse_bgr8(UINT bytesperpixel, LPBYTE bits, UINT width, UINT height, INT stride) -{ - UINT x, y; - BYTE *pixel, temp; - - for (y=0; y<height; y++) - { - pixel = bits + stride * y; - - for (x=0; x<width; x++) - { - temp = pixel[2]; - pixel[2] = pixel[0]; - pixel[0] = temp; - pixel += bytesperpixel; - } - } -} - HRESULT get_pixelformat_bpp(const GUID *pixelformat, UINT *bpp) { HRESULT hr; diff --git a/dlls/windowscodecs/tiffformat.c b/dlls/windowscodecs/tiffformat.c index 19cb0cf9286..b2478380334 100644 --- a/dlls/windowscodecs/tiffformat.c +++ b/dlls/windowscodecs/tiffformat.c @@ -221,1414 +221,6 @@ static TIFF* tiff_open_stream(IStream *stream, const char *mode) (void *)tiff_stream_size, (void *)tiff_stream_map, (void *)tiff_stream_unmap); }
-typedef struct { - IWICBitmapDecoder IWICBitmapDecoder_iface; - LONG ref; - IStream *stream; - CRITICAL_SECTION lock; /* Must be held when tiff is used or initialized is set */ - TIFF *tiff; - BOOL initialized; -} TiffDecoder; - -typedef struct { - const WICPixelFormatGUID *format; - int bps; - int samples; - int bpp, source_bpp; - int planar; - int indexed; - int reverse_bgr; - int invert_grayscale; - UINT width, height; - UINT tile_width, tile_height; - UINT tile_stride; - UINT tile_size; - int tiled; - UINT tiles_across; - UINT resolution_unit; - float xres, yres; -} tiff_decode_info; - -typedef struct { - IWICBitmapFrameDecode IWICBitmapFrameDecode_iface; - IWICMetadataBlockReader IWICMetadataBlockReader_iface; - LONG ref; - TiffDecoder *parent; - UINT index; - tiff_decode_info decode_info; - INT cached_tile_x, cached_tile_y; - BYTE *cached_tile; -} TiffFrameDecode; - -static const IWICBitmapFrameDecodeVtbl TiffFrameDecode_Vtbl; -static const IWICMetadataBlockReaderVtbl TiffFrameDecode_BlockVtbl; - -static inline TiffDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface) -{ - return CONTAINING_RECORD(iface, TiffDecoder, IWICBitmapDecoder_iface); -} - -static inline TiffFrameDecode *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface) -{ - return CONTAINING_RECORD(iface, TiffFrameDecode, IWICBitmapFrameDecode_iface); -} - -static inline TiffFrameDecode *impl_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface) -{ - return CONTAINING_RECORD(iface, TiffFrameDecode, IWICMetadataBlockReader_iface); -} - -static HRESULT tiff_get_decode_info(TIFF *tiff, tiff_decode_info *decode_info) -{ - uint16 photometric, bps, samples, planar; - uint16 extra_sample_count, extra_sample, *extra_samples; - int ret; - - decode_info->indexed = 0; - decode_info->reverse_bgr = 0; - decode_info->invert_grayscale = 0; - decode_info->tiled = 0; - decode_info->source_bpp = 0; - - ret = pTIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric); - if (!ret) - { - WARN("missing PhotometricInterpretation tag\n"); - return E_FAIL; - } - - ret = pTIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bps); - if (!ret) bps = 1; - decode_info->bps = bps; - - ret = pTIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samples); - if (!ret) samples = 1; - decode_info->samples = samples; - - if (samples == 1) - planar = 1; - else - { - ret = pTIFFGetField(tiff, TIFFTAG_PLANARCONFIG, &planar); - if (!ret) planar = 1; - if (planar != 1) - { - FIXME("unhandled planar configuration %u\n", planar); - return E_FAIL; - } - } - decode_info->planar = planar; - - TRACE("planar %u, photometric %u, samples %u, bps %u\n", planar, photometric, samples, bps); - - switch(photometric) - { - case 0: /* WhiteIsZero */ - decode_info->invert_grayscale = 1; - /* fall through */ - case 1: /* BlackIsZero */ - if (samples == 2) - { - ret = pTIFFGetField(tiff, TIFFTAG_EXTRASAMPLES, &extra_sample_count, &extra_samples); - if (!ret) - { - extra_sample_count = 1; - extra_sample = 0; - extra_samples = &extra_sample; - } - } - else if (samples != 1) - { - FIXME("unhandled %dbpp sample count %u\n", bps, samples); - return E_FAIL; - } - - decode_info->bpp = bps * samples; - decode_info->source_bpp = decode_info->bpp; - switch (bps) - { - case 1: - if (samples != 1) - { - FIXME("unhandled 1bpp sample count %u\n", samples); - return E_FAIL; - } - decode_info->format = &GUID_WICPixelFormatBlackWhite; - break; - case 4: - if (samples != 1) - { - FIXME("unhandled 4bpp grayscale sample count %u\n", samples); - return E_FAIL; - } - decode_info->format = &GUID_WICPixelFormat4bppGray; - break; - case 8: - if (samples == 1) - decode_info->format = &GUID_WICPixelFormat8bppGray; - else - { - decode_info->bpp = 32; - - switch(extra_samples[0]) - { - case 1: /* Associated (pre-multiplied) alpha data */ - decode_info->format = &GUID_WICPixelFormat32bppPBGRA; - break; - case 0: /* Unspecified data */ - case 2: /* Unassociated alpha data */ - decode_info->format = &GUID_WICPixelFormat32bppBGRA; - break; - default: - FIXME("unhandled extra sample type %u\n", extra_samples[0]); - return E_FAIL; - } - } - break; - case 16: - if (samples != 1) - { - FIXME("unhandled 16bpp grayscale sample count %u\n", samples); - return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; - } - decode_info->format = &GUID_WICPixelFormat16bppGray; - break; - case 32: - if (samples != 1) - { - FIXME("unhandled 32bpp grayscale sample count %u\n", samples); - return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; - } - decode_info->format = &GUID_WICPixelFormat32bppGrayFloat; - break; - default: - WARN("unhandled greyscale bit count %u\n", bps); - return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; - } - break; - case 2: /* RGB */ - if (samples == 4) - { - ret = pTIFFGetField(tiff, TIFFTAG_EXTRASAMPLES, &extra_sample_count, &extra_samples); - if (!ret) - { - extra_sample_count = 1; - extra_sample = 0; - extra_samples = &extra_sample; - } - } - else if (samples != 3) - { - FIXME("unhandled RGB sample count %u\n", samples); - return E_FAIL; - } - - decode_info->bpp = max(bps, 8) * samples; - decode_info->source_bpp = bps * samples; - switch(bps) - { - case 1: - case 4: - case 8: - decode_info->reverse_bgr = 1; - if (samples == 3) - decode_info->format = &GUID_WICPixelFormat24bppBGR; - else - switch(extra_samples[0]) - { - case 1: /* Associated (pre-multiplied) alpha data */ - decode_info->format = &GUID_WICPixelFormat32bppPBGRA; - break; - case 0: /* Unspecified data */ - case 2: /* Unassociated alpha data */ - decode_info->format = &GUID_WICPixelFormat32bppBGRA; - break; - default: - FIXME("unhandled extra sample type %i\n", extra_samples[0]); - return E_FAIL; - } - break; - case 16: - if (samples == 3) - decode_info->format = &GUID_WICPixelFormat48bppRGB; - else - switch(extra_samples[0]) - { - case 1: /* Associated (pre-multiplied) alpha data */ - decode_info->format = &GUID_WICPixelFormat64bppPRGBA; - break; - case 0: /* Unspecified data */ - case 2: /* Unassociated alpha data */ - decode_info->format = &GUID_WICPixelFormat64bppRGBA; - break; - default: - FIXME("unhandled extra sample type %i\n", extra_samples[0]); - return E_FAIL; - } - break; - case 32: - if (samples == 3) - decode_info->format = &GUID_WICPixelFormat96bppRGBFloat; - else - switch(extra_samples[0]) - { - case 1: /* Associated (pre-multiplied) alpha data */ - decode_info->format = &GUID_WICPixelFormat128bppPRGBAFloat; - break; - case 0: /* Unspecified data */ - case 2: /* Unassociated alpha data */ - decode_info->format = &GUID_WICPixelFormat128bppRGBAFloat; - break; - default: - FIXME("unhandled extra sample type %i\n", extra_samples[0]); - return E_FAIL; - } - break; - default: - WARN("unhandled RGB bit count %u\n", bps); - return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; - } - break; - case 3: /* RGB Palette */ - if (samples != 1) - { - FIXME("unhandled indexed sample count %u\n", samples); - return E_FAIL; - } - - decode_info->indexed = 1; - 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; - case 8: - decode_info->format = &GUID_WICPixelFormat8bppIndexed; - break; - default: - FIXME("unhandled indexed bit count %u\n", bps); - return E_NOTIMPL; - } - break; - - case 5: /* Separated */ - if (samples != 4) - { - FIXME("unhandled Separated sample count %u\n", samples); - return E_FAIL; - } - - decode_info->bpp = bps * samples; - switch(bps) - { - case 8: - decode_info->format = &GUID_WICPixelFormat32bppCMYK; - break; - case 16: - decode_info->format = &GUID_WICPixelFormat64bppCMYK; - break; - - default: - WARN("unhandled Separated bit count %u\n", bps); - return WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT; - } - break; - - case 4: /* Transparency mask */ - case 6: /* YCbCr */ - case 8: /* CIELab */ - default: - FIXME("unhandled PhotometricInterpretation %u\n", photometric); - return E_FAIL; - } - - ret = pTIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &decode_info->width); - if (!ret) - { - WARN("missing image width\n"); - return E_FAIL; - } - - ret = pTIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &decode_info->height); - if (!ret) - { - WARN("missing image length\n"); - return E_FAIL; - } - - if ((ret = pTIFFGetField(tiff, TIFFTAG_TILEWIDTH, &decode_info->tile_width))) - { - decode_info->tiled = 1; - - ret = pTIFFGetField(tiff, TIFFTAG_TILELENGTH, &decode_info->tile_height); - if (!ret) - { - WARN("missing tile height\n"); - return E_FAIL; - } - - decode_info->tile_stride = ((decode_info->bpp * decode_info->tile_width + 7)/8); - decode_info->tile_size = decode_info->tile_height * decode_info->tile_stride; - decode_info->tiles_across = (decode_info->width + decode_info->tile_width - 1) / decode_info->tile_width; - } - else if ((ret = pTIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &decode_info->tile_height))) - { - if (decode_info->tile_height > decode_info->height) - decode_info->tile_height = decode_info->height; - decode_info->tile_width = decode_info->width; - decode_info->tile_stride = ((decode_info->bpp * decode_info->tile_width + 7)/8); - decode_info->tile_size = decode_info->tile_height * decode_info->tile_stride; - } - else - { - /* Some broken TIFF files have a single strip and lack the RowsPerStrip tag */ - decode_info->tile_height = decode_info->height; - decode_info->tile_width = decode_info->width; - decode_info->tile_stride = ((decode_info->bpp * decode_info->tile_width + 7)/8); - decode_info->tile_size = decode_info->tile_height * decode_info->tile_stride; - } - - decode_info->resolution_unit = 0; - pTIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &decode_info->resolution_unit); - - ret = pTIFFGetField(tiff, TIFFTAG_XRESOLUTION, &decode_info->xres); - if (!ret) - { - WARN("missing X resolution\n"); - } - /* Emulate the behavior of current libtiff versions (libtiff commit a39f6131) - * yielding 0 instead of INFINITY for IFD_RATIONAL fields with denominator 0. */ - if (!isfinite(decode_info->xres)) - { - decode_info->xres = 0.0; - } - - ret = pTIFFGetField(tiff, TIFFTAG_YRESOLUTION, &decode_info->yres); - if (!ret) - { - WARN("missing Y resolution\n"); - } - if (!isfinite(decode_info->yres)) - { - decode_info->yres = 0.0; - } - - return S_OK; -} - -static HRESULT WINAPI TiffDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid, - void **ppv) -{ - TiffDecoder *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)) - { - *ppv = &This->IWICBitmapDecoder_iface; - } - else - { - *ppv = NULL; - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown*)*ppv); - return S_OK; -} - -static ULONG WINAPI TiffDecoder_AddRef(IWICBitmapDecoder *iface) -{ - TiffDecoder *This = impl_from_IWICBitmapDecoder(iface); - ULONG ref = InterlockedIncrement(&This->ref); - - TRACE("(%p) refcount=%u\n", iface, ref); - - return ref; -} - -static ULONG WINAPI TiffDecoder_Release(IWICBitmapDecoder *iface) -{ - TiffDecoder *This = impl_from_IWICBitmapDecoder(iface); - ULONG ref = InterlockedDecrement(&This->ref); - - TRACE("(%p) refcount=%u\n", iface, ref); - - if (ref == 0) - { - if (This->tiff) pTIFFClose(This->tiff); - 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 TiffDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream, - DWORD *capability) -{ - HRESULT hr; - - TRACE("(%p,%p,%p)\n", iface, stream, capability); - - if (!stream || !capability) return E_INVALIDARG; - - hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand); - if (hr != S_OK) return hr; - - *capability = WICBitmapDecoderCapabilityCanDecodeAllImages | - WICBitmapDecoderCapabilityCanDecodeSomeImages | - WICBitmapDecoderCapabilityCanEnumerateMetadata; - return S_OK; -} - -static HRESULT WINAPI TiffDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream, - WICDecodeOptions cacheOptions) -{ - TiffDecoder *This = impl_from_IWICBitmapDecoder(iface); - TIFF *tiff; - tiff_decode_info decode_info; - HRESULT hr=S_OK; - - TRACE("(%p,%p,%x)\n", iface, pIStream, cacheOptions); - - EnterCriticalSection(&This->lock); - - if (This->initialized) - { - hr = WINCODEC_ERR_WRONGSTATE; - goto exit; - } - - tiff = tiff_open_stream(pIStream, "r"); - if (!tiff) - { - hr = E_FAIL; - goto exit; - } - - /* make sure that TIFF format is supported */ - hr = tiff_get_decode_info(tiff, &decode_info); - if (hr != S_OK) - { - pTIFFClose(tiff); - goto exit; - } - - This->tiff = tiff; - This->stream = pIStream; - IStream_AddRef(pIStream); - This->initialized = TRUE; - -exit: - LeaveCriticalSection(&This->lock); - return hr; -} - -static HRESULT WINAPI TiffDecoder_GetContainerFormat(IWICBitmapDecoder *iface, - GUID *pguidContainerFormat) -{ - if (!pguidContainerFormat) return E_INVALIDARG; - - memcpy(pguidContainerFormat, &GUID_ContainerFormatTiff, sizeof(GUID)); - return S_OK; -} - -static HRESULT WINAPI TiffDecoder_GetDecoderInfo(IWICBitmapDecoder *iface, - IWICBitmapDecoderInfo **ppIDecoderInfo) -{ - TRACE("(%p,%p)\n", iface, ppIDecoderInfo); - - return get_decoder_info(&CLSID_WICTiffDecoder, ppIDecoderInfo); -} - -static HRESULT WINAPI TiffDecoder_CopyPalette(IWICBitmapDecoder *iface, - IWICPalette *palette) -{ - TRACE("(%p,%p)\n", iface, palette); - return WINCODEC_ERR_PALETTEUNAVAILABLE; -} - -static HRESULT WINAPI TiffDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface, - IWICMetadataQueryReader **ppIMetadataQueryReader) -{ - TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader); - - if (!ppIMetadataQueryReader) return E_INVALIDARG; - - *ppIMetadataQueryReader = NULL; - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI TiffDecoder_GetPreview(IWICBitmapDecoder *iface, - IWICBitmapSource **ppIBitmapSource) -{ - TRACE("(%p,%p)\n", iface, ppIBitmapSource); - - if (!ppIBitmapSource) return E_INVALIDARG; - - *ppIBitmapSource = NULL; - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI TiffDecoder_GetColorContexts(IWICBitmapDecoder *iface, - UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount) -{ - FIXME("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI TiffDecoder_GetThumbnail(IWICBitmapDecoder *iface, - IWICBitmapSource **ppIThumbnail) -{ - TRACE("(%p,%p)\n", iface, ppIThumbnail); - - if (!ppIThumbnail) return E_INVALIDARG; - - *ppIThumbnail = NULL; - return WINCODEC_ERR_CODECNOTHUMBNAIL; -} - -static HRESULT WINAPI TiffDecoder_GetFrameCount(IWICBitmapDecoder *iface, - UINT *pCount) -{ - TiffDecoder *This = impl_from_IWICBitmapDecoder(iface); - - if (!pCount) return E_INVALIDARG; - - EnterCriticalSection(&This->lock); - *pCount = This->tiff ? pTIFFNumberOfDirectories(This->tiff) : 0; - LeaveCriticalSection(&This->lock); - - TRACE("(%p) <-- %i\n", iface, *pCount); - - return S_OK; -} - -static HRESULT WINAPI TiffDecoder_GetFrame(IWICBitmapDecoder *iface, - UINT index, IWICBitmapFrameDecode **ppIBitmapFrame) -{ - TiffDecoder *This = impl_from_IWICBitmapDecoder(iface); - TiffFrameDecode *result; - int res; - tiff_decode_info decode_info; - HRESULT hr; - - TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame); - - if (!This->tiff) - return WINCODEC_ERR_FRAMEMISSING; - - EnterCriticalSection(&This->lock); - res = pTIFFSetDirectory(This->tiff, index); - if (!res) hr = E_INVALIDARG; - else hr = tiff_get_decode_info(This->tiff, &decode_info); - LeaveCriticalSection(&This->lock); - - if (SUCCEEDED(hr)) - { - result = HeapAlloc(GetProcessHeap(), 0, sizeof(TiffFrameDecode)); - - if (result) - { - result->IWICBitmapFrameDecode_iface.lpVtbl = &TiffFrameDecode_Vtbl; - result->IWICMetadataBlockReader_iface.lpVtbl = &TiffFrameDecode_BlockVtbl; - result->ref = 1; - result->parent = This; - IWICBitmapDecoder_AddRef(iface); - result->index = index; - result->decode_info = decode_info; - result->cached_tile_x = -1; - result->cached_tile = HeapAlloc(GetProcessHeap(), 0, decode_info.tile_size); - - if (result->cached_tile) - *ppIBitmapFrame = &result->IWICBitmapFrameDecode_iface; - else - { - hr = E_OUTOFMEMORY; - IWICBitmapFrameDecode_Release(&result->IWICBitmapFrameDecode_iface); - } - } - else hr = E_OUTOFMEMORY; - } - - if (FAILED(hr)) *ppIBitmapFrame = NULL; - - return hr; -} - -static const IWICBitmapDecoderVtbl TiffDecoder_Vtbl = { - TiffDecoder_QueryInterface, - TiffDecoder_AddRef, - TiffDecoder_Release, - TiffDecoder_QueryCapability, - TiffDecoder_Initialize, - TiffDecoder_GetContainerFormat, - TiffDecoder_GetDecoderInfo, - TiffDecoder_CopyPalette, - TiffDecoder_GetMetadataQueryReader, - TiffDecoder_GetPreview, - TiffDecoder_GetColorContexts, - TiffDecoder_GetThumbnail, - TiffDecoder_GetFrameCount, - TiffDecoder_GetFrame -}; - -static HRESULT WINAPI TiffFrameDecode_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid, - void **ppv) -{ - TiffFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface); - TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); - - if (!ppv) return E_INVALIDARG; - - if (IsEqualIID(&IID_IUnknown, iid) || - IsEqualIID(&IID_IWICBitmapSource, iid) || - IsEqualIID(&IID_IWICBitmapFrameDecode, iid)) - { - *ppv = &This->IWICBitmapFrameDecode_iface; - } - else if (IsEqualIID(&IID_IWICMetadataBlockReader, iid)) - { - *ppv = &This->IWICMetadataBlockReader_iface; - } - else - { - *ppv = NULL; - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown*)*ppv); - return S_OK; -} - -static ULONG WINAPI TiffFrameDecode_AddRef(IWICBitmapFrameDecode *iface) -{ - TiffFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface); - ULONG ref = InterlockedIncrement(&This->ref); - - TRACE("(%p) refcount=%u\n", iface, ref); - - return ref; -} - -static ULONG WINAPI TiffFrameDecode_Release(IWICBitmapFrameDecode *iface) -{ - TiffFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface); - ULONG ref = InterlockedDecrement(&This->ref); - - TRACE("(%p) refcount=%u\n", iface, ref); - - if (ref == 0) - { - IWICBitmapDecoder_Release(&This->parent->IWICBitmapDecoder_iface); - HeapFree(GetProcessHeap(), 0, This->cached_tile); - HeapFree(GetProcessHeap(), 0, This); - } - - return ref; -} - -static HRESULT WINAPI TiffFrameDecode_GetSize(IWICBitmapFrameDecode *iface, - UINT *puiWidth, UINT *puiHeight) -{ - TiffFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface); - - *puiWidth = This->decode_info.width; - *puiHeight = This->decode_info.height; - - TRACE("(%p) <-- %ux%u\n", iface, *puiWidth, *puiHeight); - - return S_OK; -} - -static HRESULT WINAPI TiffFrameDecode_GetPixelFormat(IWICBitmapFrameDecode *iface, - WICPixelFormatGUID *pPixelFormat) -{ - TiffFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface); - - memcpy(pPixelFormat, This->decode_info.format, sizeof(GUID)); - - TRACE("(%p) <-- %s\n", This, debugstr_guid(This->decode_info.format)); - - return S_OK; -} - -static HRESULT WINAPI TiffFrameDecode_GetResolution(IWICBitmapFrameDecode *iface, - double *pDpiX, double *pDpiY) -{ - TiffFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface); - - if (This->decode_info.xres == 0 || This->decode_info.yres == 0) - { - *pDpiX = *pDpiY = 96.0; - } - else - { - switch (This->decode_info.resolution_unit) - { - default: - FIXME("unknown resolution unit %i\n", This->decode_info.resolution_unit); - /* fall through */ - case 0: /* Not set */ - case 1: /* Relative measurements */ - case 2: /* Inch */ - *pDpiX = This->decode_info.xres; - *pDpiY = This->decode_info.yres; - break; - case 3: /* Centimeter */ - *pDpiX = This->decode_info.xres * 2.54; - *pDpiY = This->decode_info.yres * 2.54; - break; - } - } - - TRACE("(%p) <-- %f,%f unit=%i\n", iface, *pDpiX, *pDpiY, This->decode_info.resolution_unit); - - return S_OK; -} - -static HRESULT WINAPI TiffFrameDecode_CopyPalette(IWICBitmapFrameDecode *iface, - IWICPalette *pIPalette) -{ - TiffFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface); - uint16 *red, *green, *blue; - WICColor colors[256]; - int color_count, ret, i; - - TRACE("(%p,%p)\n", iface, pIPalette); - - color_count = 1<<This->decode_info.bps; - - EnterCriticalSection(&This->parent->lock); - ret = pTIFFGetField(This->parent->tiff, TIFFTAG_COLORMAP, &red, &green, &blue); - LeaveCriticalSection(&This->parent->lock); - - if (!ret) - { - WARN("Couldn't read color map\n"); - return WINCODEC_ERR_PALETTEUNAVAILABLE; - } - - for (i=0; i<color_count; i++) - { - colors[i] = 0xff000000 | - ((red[i]<<8) & 0xff0000) | - (green[i] & 0xff00) | - ((blue[i]>>8) & 0xff); - } - - return IWICPalette_InitializeCustom(pIPalette, colors, color_count); -} - -static HRESULT TiffFrameDecode_ReadTile(TiffFrameDecode *This, UINT tile_x, UINT tile_y) -{ - tsize_t ret; - int swap_bytes; - - swap_bytes = pTIFFIsByteSwapped(This->parent->tiff); - - ret = pTIFFSetDirectory(This->parent->tiff, This->index); - if (ret == -1) - return E_FAIL; - - if (This->decode_info.tiled) - ret = pTIFFReadEncodedTile(This->parent->tiff, tile_x + tile_y * This->decode_info.tiles_across, This->cached_tile, This->decode_info.tile_size); - else - ret = pTIFFReadEncodedStrip(This->parent->tiff, tile_y, This->cached_tile, This->decode_info.tile_size); - - if (ret == -1) - return E_FAIL; - - /* 3bps RGB */ - if (This->decode_info.source_bpp == 3 && This->decode_info.samples == 3 && This->decode_info.bpp == 24) - { - BYTE *srcdata, *src, *dst; - DWORD x, y, count, width_bytes = (This->decode_info.tile_width * 3 + 7) / 8; - - count = width_bytes * This->decode_info.tile_height; - - srcdata = HeapAlloc(GetProcessHeap(), 0, count); - if (!srcdata) return E_OUTOFMEMORY; - memcpy(srcdata, This->cached_tile, count); - - for (y = 0; y < This->decode_info.tile_height; y++) - { - src = srcdata + y * width_bytes; - dst = This->cached_tile + y * This->decode_info.tile_width * 3; - - for (x = 0; x < This->decode_info.tile_width; x += 8) - { - dst[2] = (src[0] & 0x80) ? 0xff : 0; /* R */ - dst[1] = (src[0] & 0x40) ? 0xff : 0; /* G */ - dst[0] = (src[0] & 0x20) ? 0xff : 0; /* B */ - if (x + 1 < This->decode_info.tile_width) - { - dst[5] = (src[0] & 0x10) ? 0xff : 0; /* R */ - dst[4] = (src[0] & 0x08) ? 0xff : 0; /* G */ - dst[3] = (src[0] & 0x04) ? 0xff : 0; /* B */ - } - if (x + 2 < This->decode_info.tile_width) - { - dst[8] = (src[0] & 0x02) ? 0xff : 0; /* R */ - dst[7] = (src[0] & 0x01) ? 0xff : 0; /* G */ - dst[6] = (src[1] & 0x80) ? 0xff : 0; /* B */ - } - if (x + 3 < This->decode_info.tile_width) - { - dst[11] = (src[1] & 0x40) ? 0xff : 0; /* R */ - dst[10] = (src[1] & 0x20) ? 0xff : 0; /* G */ - dst[9] = (src[1] & 0x10) ? 0xff : 0; /* B */ - } - if (x + 4 < This->decode_info.tile_width) - { - dst[14] = (src[1] & 0x08) ? 0xff : 0; /* R */ - dst[13] = (src[1] & 0x04) ? 0xff : 0; /* G */ - dst[12] = (src[1] & 0x02) ? 0xff : 0; /* B */ - } - if (x + 5 < This->decode_info.tile_width) - { - dst[17] = (src[1] & 0x01) ? 0xff : 0; /* R */ - dst[16] = (src[2] & 0x80) ? 0xff : 0; /* G */ - dst[15] = (src[2] & 0x40) ? 0xff : 0; /* B */ - } - if (x + 6 < This->decode_info.tile_width) - { - dst[20] = (src[2] & 0x20) ? 0xff : 0; /* R */ - dst[19] = (src[2] & 0x10) ? 0xff : 0; /* G */ - dst[18] = (src[2] & 0x08) ? 0xff : 0; /* B */ - } - if (x + 7 < This->decode_info.tile_width) - { - dst[23] = (src[2] & 0x04) ? 0xff : 0; /* R */ - dst[22] = (src[2] & 0x02) ? 0xff : 0; /* G */ - dst[21] = (src[2] & 0x01) ? 0xff : 0; /* B */ - } - src += 3; - dst += 24; - } - } - - HeapFree(GetProcessHeap(), 0, srcdata); - } - /* 12bps RGB */ - else if (This->decode_info.source_bpp == 12 && This->decode_info.samples == 3 && This->decode_info.bpp == 24) - { - BYTE *srcdata, *src, *dst; - DWORD x, y, count, width_bytes = (This->decode_info.tile_width * 12 + 7) / 8; - - count = width_bytes * This->decode_info.tile_height; - - srcdata = HeapAlloc(GetProcessHeap(), 0, count); - if (!srcdata) return E_OUTOFMEMORY; - memcpy(srcdata, This->cached_tile, count); - - for (y = 0; y < This->decode_info.tile_height; y++) - { - src = srcdata + y * width_bytes; - dst = This->cached_tile + y * This->decode_info.tile_width * 3; - - for (x = 0; x < This->decode_info.tile_width; x += 2) - { - dst[0] = ((src[1] & 0xf0) >> 4) * 17; /* B */ - dst[1] = (src[0] & 0x0f) * 17; /* G */ - dst[2] = ((src[0] & 0xf0) >> 4) * 17; /* R */ - if (x + 1 < This->decode_info.tile_width) - { - dst[5] = (src[1] & 0x0f) * 17; /* B */ - dst[4] = ((src[2] & 0xf0) >> 4) * 17; /* G */ - dst[3] = (src[2] & 0x0f) * 17; /* R */ - } - src += 3; - dst += 6; - } - } - - HeapFree(GetProcessHeap(), 0, srcdata); - } - /* 4bps RGBA */ - else if (This->decode_info.source_bpp == 4 && This->decode_info.samples == 4 && This->decode_info.bpp == 32) - { - BYTE *srcdata, *src, *dst; - DWORD x, y, count, width_bytes = (This->decode_info.tile_width * 3 + 7) / 8; - - count = width_bytes * This->decode_info.tile_height; - - srcdata = HeapAlloc(GetProcessHeap(), 0, count); - if (!srcdata) return E_OUTOFMEMORY; - memcpy(srcdata, This->cached_tile, count); - - for (y = 0; y < This->decode_info.tile_height; y++) - { - src = srcdata + y * width_bytes; - dst = This->cached_tile + y * This->decode_info.tile_width * 4; - - /* 1 source byte expands to 2 BGRA samples */ - - for (x = 0; x < This->decode_info.tile_width; x += 2) - { - dst[0] = (src[0] & 0x20) ? 0xff : 0; /* B */ - dst[1] = (src[0] & 0x40) ? 0xff : 0; /* G */ - dst[2] = (src[0] & 0x80) ? 0xff : 0; /* R */ - dst[3] = (src[0] & 0x10) ? 0xff : 0; /* A */ - if (x + 1 < This->decode_info.tile_width) - { - dst[4] = (src[0] & 0x02) ? 0xff : 0; /* B */ - dst[5] = (src[0] & 0x04) ? 0xff : 0; /* G */ - dst[6] = (src[0] & 0x08) ? 0xff : 0; /* R */ - dst[7] = (src[0] & 0x01) ? 0xff : 0; /* A */ - } - src++; - dst += 8; - } - } - - HeapFree(GetProcessHeap(), 0, srcdata); - } - /* 16bps RGBA */ - else if (This->decode_info.source_bpp == 16 && This->decode_info.samples == 4 && This->decode_info.bpp == 32) - { - BYTE *srcdata, *src, *dst; - DWORD x, y, count, width_bytes = (This->decode_info.tile_width * 12 + 7) / 8; - - count = width_bytes * This->decode_info.tile_height; - - srcdata = HeapAlloc(GetProcessHeap(), 0, count); - if (!srcdata) return E_OUTOFMEMORY; - memcpy(srcdata, This->cached_tile, count); - - for (y = 0; y < This->decode_info.tile_height; y++) - { - src = srcdata + y * width_bytes; - dst = This->cached_tile + y * This->decode_info.tile_width * 4; - - for (x = 0; x < This->decode_info.tile_width; x++) - { - dst[0] = ((src[1] & 0xf0) >> 4) * 17; /* B */ - dst[1] = (src[0] & 0x0f) * 17; /* G */ - dst[2] = ((src[0] & 0xf0) >> 4) * 17; /* R */ - dst[3] = (src[1] & 0x0f) * 17; /* A */ - src += 2; - dst += 4; - } - } - - HeapFree(GetProcessHeap(), 0, srcdata); - } - /* 8bpp grayscale with extra alpha */ - else if (This->decode_info.source_bpp == 16 && This->decode_info.samples == 2 && This->decode_info.bpp == 32) - { - BYTE *src; - DWORD *dst, count = This->decode_info.tile_width * This->decode_info.tile_height; - - src = This->cached_tile + This->decode_info.tile_width * This->decode_info.tile_height * 2 - 2; - dst = (DWORD *)(This->cached_tile + This->decode_info.tile_size - 4); - - while (count--) - { - *dst-- = src[0] | (src[0] << 8) | (src[0] << 16) | (src[1] << 24); - src -= 2; - } - } - - if (This->decode_info.reverse_bgr) - { - if (This->decode_info.bps == 8) - { - UINT sample_count = This->decode_info.samples; - - reverse_bgr8(sample_count, This->cached_tile, This->decode_info.tile_width, - This->decode_info.tile_height, This->decode_info.tile_width * sample_count); - } - } - - if (swap_bytes && This->decode_info.bps > 8) - { - UINT row, i, samples_per_row; - BYTE *sample, temp; - - samples_per_row = This->decode_info.tile_width * This->decode_info.samples; - - switch(This->decode_info.bps) - { - case 16: - for (row=0; row<This->decode_info.tile_height; row++) - { - sample = This->cached_tile + row * This->decode_info.tile_stride; - for (i=0; i<samples_per_row; i++) - { - temp = sample[1]; - sample[1] = sample[0]; - sample[0] = temp; - sample += 2; - } - } - break; - default: - ERR("unhandled bps for byte swap %u\n", This->decode_info.bps); - return E_FAIL; - } - } - - if (This->decode_info.invert_grayscale) - { - BYTE *byte, *end; - - if (This->decode_info.samples != 1) - { - ERR("cannot invert grayscale image with %u samples\n", This->decode_info.samples); - return E_FAIL; - } - - end = This->cached_tile+This->decode_info.tile_size; - - for (byte = This->cached_tile; byte != end; byte++) - *byte = ~(*byte); - } - - This->cached_tile_x = tile_x; - This->cached_tile_y = tile_y; - - return S_OK; -} - -static HRESULT WINAPI TiffFrameDecode_CopyPixels(IWICBitmapFrameDecode *iface, - const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer) -{ - TiffFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface); - UINT min_tile_x, max_tile_x, min_tile_y, max_tile_y; - UINT tile_x, tile_y; - WICRect rc; - HRESULT hr=S_OK; - BYTE *dst_tilepos; - UINT bytesperrow; - WICRect rect; - - TRACE("(%p,%s,%u,%u,%p)\n", iface, debug_wic_rect(prc), cbStride, cbBufferSize, pbBuffer); - - if (!prc) - { - rect.X = 0; - rect.Y = 0; - rect.Width = This->decode_info.width; - rect.Height = This->decode_info.height; - prc = ▭ - } - else - { - if (prc->X < 0 || prc->Y < 0 || prc->X+prc->Width > This->decode_info.width || - prc->Y+prc->Height > This->decode_info.height) - return E_INVALIDARG; - } - - bytesperrow = ((This->decode_info.bpp * prc->Width)+7)/8; - - if (cbStride < bytesperrow) - return E_INVALIDARG; - - if ((cbStride * (prc->Height-1)) + bytesperrow > cbBufferSize) - return E_INVALIDARG; - - min_tile_x = prc->X / This->decode_info.tile_width; - min_tile_y = prc->Y / This->decode_info.tile_height; - max_tile_x = (prc->X+prc->Width-1) / This->decode_info.tile_width; - max_tile_y = (prc->Y+prc->Height-1) / This->decode_info.tile_height; - - EnterCriticalSection(&This->parent->lock); - - for (tile_x=min_tile_x; tile_x <= max_tile_x; tile_x++) - { - for (tile_y=min_tile_y; tile_y <= max_tile_y; tile_y++) - { - if (tile_x != This->cached_tile_x || tile_y != This->cached_tile_y) - { - hr = TiffFrameDecode_ReadTile(This, tile_x, tile_y); - } - - if (SUCCEEDED(hr)) - { - if (prc->X < tile_x * This->decode_info.tile_width) - rc.X = 0; - else - rc.X = prc->X - tile_x * This->decode_info.tile_width; - - if (prc->Y < tile_y * This->decode_info.tile_height) - rc.Y = 0; - else - rc.Y = prc->Y - tile_y * This->decode_info.tile_height; - - if (prc->X+prc->Width > (tile_x+1) * This->decode_info.tile_width) - rc.Width = This->decode_info.tile_width - rc.X; - else if (prc->X < tile_x * This->decode_info.tile_width) - rc.Width = prc->Width + prc->X - tile_x * This->decode_info.tile_width; - else - rc.Width = prc->Width; - - if (prc->Y+prc->Height > (tile_y+1) * This->decode_info.tile_height) - rc.Height = This->decode_info.tile_height - rc.Y; - else if (prc->Y < tile_y * This->decode_info.tile_height) - rc.Height = prc->Height + prc->Y - tile_y * This->decode_info.tile_height; - else - rc.Height = prc->Height; - - dst_tilepos = pbBuffer + (cbStride * ((rc.Y + tile_y * This->decode_info.tile_height) - prc->Y)) + - ((This->decode_info.bpp * ((rc.X + tile_x * This->decode_info.tile_width) - prc->X) + 7) / 8); - - hr = copy_pixels(This->decode_info.bpp, This->cached_tile, - This->decode_info.tile_width, This->decode_info.tile_height, This->decode_info.tile_stride, - &rc, cbStride, cbBufferSize, dst_tilepos); - } - - if (FAILED(hr)) - { - LeaveCriticalSection(&This->parent->lock); - TRACE("<-- 0x%x\n", hr); - return hr; - } - } - } - - LeaveCriticalSection(&This->parent->lock); - - return S_OK; -} - -static HRESULT WINAPI TiffFrameDecode_GetMetadataQueryReader(IWICBitmapFrameDecode *iface, - IWICMetadataQueryReader **ppIMetadataQueryReader) -{ - TiffFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface); - - TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader); - - if (!ppIMetadataQueryReader) - return E_INVALIDARG; - - return MetadataQueryReader_CreateInstance(&This->IWICMetadataBlockReader_iface, NULL, ppIMetadataQueryReader); -} - -static HRESULT WINAPI TiffFrameDecode_GetColorContexts(IWICBitmapFrameDecode *iface, - UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount) -{ - TiffFrameDecode *This = impl_from_IWICBitmapFrameDecode(iface); - const BYTE *profile; - UINT len; - HRESULT hr; - - TRACE("(%p,%u,%p,%p)\n", iface, cCount, ppIColorContexts, pcActualCount); - - EnterCriticalSection(&This->parent->lock); - - if (pTIFFGetField(This->parent->tiff, TIFFTAG_ICCPROFILE, &len, &profile)) - { - if (cCount && ppIColorContexts) - { - hr = IWICColorContext_InitializeFromMemory(*ppIColorContexts, profile, len); - if (FAILED(hr)) - { - LeaveCriticalSection(&This->parent->lock); - return hr; - } - } - *pcActualCount = 1; - } - else - *pcActualCount = 0; - - LeaveCriticalSection(&This->parent->lock); - - return S_OK; -} - -static HRESULT WINAPI TiffFrameDecode_GetThumbnail(IWICBitmapFrameDecode *iface, - IWICBitmapSource **ppIThumbnail) -{ - TRACE("(%p,%p)\n", iface, ppIThumbnail); - - if (!ppIThumbnail) return E_INVALIDARG; - - *ppIThumbnail = NULL; - return WINCODEC_ERR_CODECNOTHUMBNAIL; -} - -static const IWICBitmapFrameDecodeVtbl TiffFrameDecode_Vtbl = { - TiffFrameDecode_QueryInterface, - TiffFrameDecode_AddRef, - TiffFrameDecode_Release, - TiffFrameDecode_GetSize, - TiffFrameDecode_GetPixelFormat, - TiffFrameDecode_GetResolution, - TiffFrameDecode_CopyPalette, - TiffFrameDecode_CopyPixels, - TiffFrameDecode_GetMetadataQueryReader, - TiffFrameDecode_GetColorContexts, - TiffFrameDecode_GetThumbnail -}; - -static HRESULT WINAPI TiffFrameDecode_Block_QueryInterface(IWICMetadataBlockReader *iface, - REFIID iid, void **ppv) -{ - TiffFrameDecode *This = impl_from_IWICMetadataBlockReader(iface); - return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv); -} - -static ULONG WINAPI TiffFrameDecode_Block_AddRef(IWICMetadataBlockReader *iface) -{ - TiffFrameDecode *This = impl_from_IWICMetadataBlockReader(iface); - return IWICBitmapFrameDecode_AddRef(&This->IWICBitmapFrameDecode_iface); -} - -static ULONG WINAPI TiffFrameDecode_Block_Release(IWICMetadataBlockReader *iface) -{ - TiffFrameDecode *This = impl_from_IWICMetadataBlockReader(iface); - return IWICBitmapFrameDecode_Release(&This->IWICBitmapFrameDecode_iface); -} - -static HRESULT WINAPI TiffFrameDecode_Block_GetContainerFormat(IWICMetadataBlockReader *iface, - GUID *guid) -{ - TRACE("(%p,%p)\n", iface, guid); - - if (!guid) return E_INVALIDARG; - - *guid = GUID_ContainerFormatTiff; - return S_OK; -} - -static HRESULT WINAPI TiffFrameDecode_Block_GetCount(IWICMetadataBlockReader *iface, - UINT *count) -{ - TRACE("%p,%p\n", iface, count); - - if (!count) return E_INVALIDARG; - - *count = 1; - return S_OK; -} - -static HRESULT create_metadata_reader(TiffFrameDecode *This, IWICMetadataReader **reader) -{ - HRESULT hr; - LARGE_INTEGER dir_offset; - IWICMetadataReader *metadata_reader; - IWICPersistStream *persist; - - /* FIXME: Use IWICComponentFactory_CreateMetadataReader once it's implemented */ - - hr = IfdMetadataReader_CreateInstance(&IID_IWICMetadataReader, (void **)&metadata_reader); - if (FAILED(hr)) return hr; - - hr = IWICMetadataReader_QueryInterface(metadata_reader, &IID_IWICPersistStream, (void **)&persist); - if (FAILED(hr)) - { - IWICMetadataReader_Release(metadata_reader); - return hr; - } - - EnterCriticalSection(&This->parent->lock); - - dir_offset.QuadPart = pTIFFCurrentDirOffset(This->parent->tiff); - hr = IStream_Seek(This->parent->stream, dir_offset, STREAM_SEEK_SET, NULL); - if (SUCCEEDED(hr)) - { - BOOL byte_swapped = pTIFFIsByteSwapped(This->parent->tiff); -#ifdef WORDS_BIGENDIAN - DWORD persist_options = byte_swapped ? WICPersistOptionLittleEndian : WICPersistOptionBigEndian; -#else - DWORD persist_options = byte_swapped ? WICPersistOptionBigEndian : WICPersistOptionLittleEndian; -#endif - persist_options |= WICPersistOptionNoCacheStream; - hr = IWICPersistStream_LoadEx(persist, This->parent->stream, NULL, persist_options); - if (FAILED(hr)) - ERR("IWICPersistStream_LoadEx error %#x\n", hr); - } - - LeaveCriticalSection(&This->parent->lock); - - IWICPersistStream_Release(persist); - - if (FAILED(hr)) - { - IWICMetadataReader_Release(metadata_reader); - return hr; - } - - *reader = metadata_reader; - return S_OK; -} - -static HRESULT WINAPI TiffFrameDecode_Block_GetReaderByIndex(IWICMetadataBlockReader *iface, - UINT index, IWICMetadataReader **reader) -{ - TiffFrameDecode *This = impl_from_IWICMetadataBlockReader(iface); - - TRACE("(%p,%u,%p)\n", iface, index, reader); - - if (!reader || index != 0) return E_INVALIDARG; - - return create_metadata_reader(This, reader); -} - -static HRESULT WINAPI TiffFrameDecode_Block_GetEnumerator(IWICMetadataBlockReader *iface, - IEnumUnknown **enum_metadata) -{ - FIXME("(%p,%p): stub\n", iface, enum_metadata); - return E_NOTIMPL; -} - -static const IWICMetadataBlockReaderVtbl TiffFrameDecode_BlockVtbl = -{ - TiffFrameDecode_Block_QueryInterface, - TiffFrameDecode_Block_AddRef, - TiffFrameDecode_Block_Release, - TiffFrameDecode_Block_GetContainerFormat, - TiffFrameDecode_Block_GetCount, - TiffFrameDecode_Block_GetReaderByIndex, - TiffFrameDecode_Block_GetEnumerator -}; - -HRESULT TiffDecoder_CreateInstance(REFIID iid, void** ppv) -{ - HRESULT ret; - TiffDecoder *This; - - TRACE("(%s,%p)\n", debugstr_guid(iid), ppv); - - *ppv = NULL; - - if (!load_libtiff()) - { - ERR("Failed reading TIFF because unable to load %s\n",SONAME_LIBTIFF); - return E_FAIL; - } - - This = HeapAlloc(GetProcessHeap(), 0, sizeof(TiffDecoder)); - if (!This) return E_OUTOFMEMORY; - - This->IWICBitmapDecoder_iface.lpVtbl = &TiffDecoder_Vtbl; - This->ref = 1; - This->stream = NULL; - InitializeCriticalSection(&This->lock); - This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": TiffDecoder.lock"); - This->tiff = NULL; - This->initialized = FALSE; - - ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv); - IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface); - - return ret; -} - struct tiff_encode_format { const WICPixelFormatGUID *guid; int photometric; @@ -2386,12 +978,6 @@ HRESULT TiffEncoder_CreateInstance(REFIID iid, void** ppv)
#else /* !SONAME_LIBTIFF */
-HRESULT TiffDecoder_CreateInstance(REFIID iid, void** ppv) -{ - ERR("Trying to load TIFF picture, but Wine was compiled without TIFF support.\n"); - return E_FAIL; -} - HRESULT TiffEncoder_CreateInstance(REFIID iid, void** ppv) { ERR("Trying to save TIFF picture, but Wine was compiled without TIFF support.\n"); @@ -2399,3 +985,17 @@ HRESULT TiffEncoder_CreateInstance(REFIID iid, void** ppv) }
#endif + +HRESULT TiffDecoder_CreateInstance(REFIID iid, void** ppv) +{ + HRESULT hr; + struct decoder *decoder; + struct decoder_info decoder_info; + + hr = get_unix_decoder(&CLSID_WICTiffDecoder, &decoder_info, &decoder); + + if (SUCCEEDED(hr)) + hr = CommonDecoder_CreateInstance(decoder, &decoder_info, iid, ppv); + + return hr; +} diff --git a/dlls/windowscodecs/unix_iface.c b/dlls/windowscodecs/unix_iface.c index e376ea45841..5ed995e7642 100644 --- a/dlls/windowscodecs/unix_iface.c +++ b/dlls/windowscodecs/unix_iface.c @@ -40,6 +40,7 @@ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; static const struct unix_funcs *unix_funcs;
static const struct win32_funcs win32_funcs = { + stream_getsize, stream_read, stream_seek }; diff --git a/dlls/windowscodecs/unix_lib.c b/dlls/windowscodecs/unix_lib.c index 04cbe053a0e..ca2b38ef990 100644 --- a/dlls/windowscodecs/unix_lib.c +++ b/dlls/windowscodecs/unix_lib.c @@ -47,6 +47,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
static const struct win32_funcs *win32_funcs;
+HRESULT CDECL stream_getsize(IStream *stream, ULONGLONG *size) +{ + return win32_funcs->stream_getsize(stream, size); +} + HRESULT CDECL stream_read(IStream *stream, void *buffer, ULONG read, ULONG *bytes_read) { return win32_funcs->stream_read(stream, buffer, read, bytes_read); @@ -62,6 +67,9 @@ HRESULT CDECL decoder_create(const CLSID *decoder_clsid, struct decoder_info *in if (IsEqualGUID(decoder_clsid, &CLSID_WICPngDecoder)) return png_decoder_create(info, result);
+ if (IsEqualGUID(decoder_clsid, &CLSID_WICTiffDecoder)) + return tiff_decoder_create(info, result); + return E_NOTIMPL; }
diff --git a/dlls/windowscodecs/wincodecs_common.h b/dlls/windowscodecs/wincodecs_common.h index 07e6dcaf44a..b7572d69df4 100644 --- a/dlls/windowscodecs/wincodecs_common.h +++ b/dlls/windowscodecs/wincodecs_common.h @@ -158,3 +158,22 @@ HRESULT read_png_chunk(IStream *stream, BYTE *type, BYTE **data, ULONG *data_siz
return S_OK; } + +void reverse_bgr8(UINT bytesperpixel, LPBYTE bits, UINT width, UINT height, INT stride) +{ + UINT x, y; + BYTE *pixel, temp; + + for (y=0; y<height; y++) + { + pixel = bits + stride * y; + + for (x=0; x<width; x++) + { + temp = pixel[2]; + pixel[2] = pixel[0]; + pixel[0] = temp; + pixel += bytesperpixel; + } + } +} diff --git a/dlls/windowscodecs/wincodecs_private.h b/dlls/windowscodecs/wincodecs_private.h index 64c813329cf..3d7a46f0f59 100644 --- a/dlls/windowscodecs/wincodecs_private.h +++ b/dlls/windowscodecs/wincodecs_private.h @@ -280,11 +280,15 @@ struct decoder_frame WICColor palette[256]; };
+#define DECODER_BLOCK_OPTION_MASK 0x0001000F +#define DECODER_BLOCK_FULL_STREAM 0x80000000 +#define DECODER_BLOCK_READER_CLSID 0x40000000 struct decoder_block { ULONGLONG offset; ULONGLONG length; DWORD options; + GUID reader_clsid; };
struct decoder @@ -305,11 +309,13 @@ struct decoder_funcs void (CDECL *destroy)(struct decoder* This); };
+HRESULT CDECL stream_getsize(IStream *stream, ULONGLONG *size); HRESULT CDECL stream_read(IStream *stream, void *buffer, ULONG read, ULONG *bytes_read); HRESULT CDECL stream_seek(IStream *stream, LONGLONG ofs, DWORD origin, ULONGLONG *new_position);
struct win32_funcs { + HRESULT (CDECL *stream_getsize)(IStream *stream, ULONGLONG *size); HRESULT (CDECL *stream_read)(IStream *stream, void *buffer, ULONG read, ULONG *bytes_read); HRESULT (CDECL *stream_seek)(IStream *stream, LONGLONG ofs, DWORD origin, ULONGLONG *new_position); }; @@ -326,6 +332,7 @@ HRESULT CDECL decoder_get_color_context(struct decoder* This, UINT frame, UINT n void CDECL decoder_destroy(struct decoder *This);
HRESULT CDECL png_decoder_create(struct decoder_info *info, struct decoder **result); +HRESULT CDECL tiff_decoder_create(struct decoder_info *info, struct decoder **result);
struct unix_funcs {