| /* |
| * Copyright 2010 Vincent Povirk for CodeWeavers |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "objbase.h" |
| |
| #include "wincodecs_private.h" |
| |
| #include "wine/debug.h" |
| #include "wine/library.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); |
| |
| #include "pshpack1.h" |
| |
| typedef struct { |
| BYTE id_length; |
| BYTE colormap_type; |
| BYTE image_type; |
| /* Colormap Specification */ |
| WORD colormap_firstentry; |
| WORD colormap_length; |
| BYTE colormap_entrysize; |
| /* Image Specification */ |
| WORD xorigin; |
| WORD yorigin; |
| WORD width; |
| WORD height; |
| BYTE depth; |
| BYTE image_descriptor; |
| } tga_header; |
| |
| #define IMAGETYPE_COLORMAPPED 1 |
| #define IMAGETYPE_TRUECOLOR 2 |
| #define IMAGETYPE_GRAYSCALE 3 |
| #define IMAGETYPE_RLE 8 |
| |
| #define IMAGE_ATTRIBUTE_BITCOUNT_MASK 0xf |
| #define IMAGE_RIGHTTOLEFT 0x10 |
| #define IMAGE_TOPTOBOTTOM 0x20 |
| |
| typedef struct { |
| DWORD extension_area_offset; |
| DWORD developer_directory_offset; |
| char magic[18]; |
| } tga_footer; |
| |
| static const BYTE tga_footer_magic[18] = "TRUEVISION-XFILE."; |
| |
| typedef struct { |
| WORD size; |
| char author_name[41]; |
| char author_comments[324]; |
| WORD timestamp[6]; |
| char job_name[41]; |
| WORD job_timestamp[6]; |
| char software_id[41]; |
| WORD software_version; |
| char software_version_letter; |
| DWORD key_color; |
| WORD pixel_width; |
| WORD pixel_height; |
| WORD gamma_numerator; |
| WORD gamma_denominator; |
| DWORD color_correction_offset; |
| DWORD thumbnail_offset; |
| DWORD scanline_offset; |
| BYTE attributes_type; |
| } tga_extension_area; |
| |
| #define ATTRIBUTE_NO_ALPHA 0 |
| #define ATTRIBUTE_UNDEFINED 1 |
| #define ATTRIBUTE_UNDEFINED_PRESERVE 2 |
| #define ATTRIBUTE_ALPHA 3 |
| #define ATTRIBUTE_PALPHA 4 |
| |
| #include "poppack.h" |
| |
| typedef struct { |
| IWICBitmapDecoder IWICBitmapDecoder_iface; |
| IWICBitmapFrameDecode IWICBitmapFrameDecode_iface; |
| LONG ref; |
| BOOL initialized; |
| IStream *stream; |
| tga_header header; |
| tga_extension_area extension_area; |
| BYTE *imagebits; |
| BYTE *origin; |
| int stride; |
| ULONG id_offset; |
| ULONG colormap_length; |
| ULONG colormap_offset; |
| ULONG image_offset; |
| ULONG extension_area_offset; |
| ULONG developer_directory_offset; |
| CRITICAL_SECTION lock; |
| } TgaDecoder; |
| |
| static inline TgaDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface) |
| { |
| return CONTAINING_RECORD(iface, TgaDecoder, IWICBitmapDecoder_iface); |
| } |
| |
| static inline TgaDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface) |
| { |
| return CONTAINING_RECORD(iface, TgaDecoder, IWICBitmapFrameDecode_iface); |
| } |
| |
| static HRESULT WINAPI TgaDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid, |
| void **ppv) |
| { |
| TgaDecoder *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 TgaDecoder_AddRef(IWICBitmapDecoder *iface) |
| { |
| TgaDecoder *This = impl_from_IWICBitmapDecoder(iface); |
| ULONG ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p) refcount=%u\n", iface, ref); |
| |
| return ref; |
| } |
| |
| static ULONG WINAPI TgaDecoder_Release(IWICBitmapDecoder *iface) |
| { |
| TgaDecoder *This = impl_from_IWICBitmapDecoder(iface); |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| TRACE("(%p) refcount=%u\n", iface, ref); |
| |
| if (ref == 0) |
| { |
| This->lock.DebugInfo->Spare[0] = 0; |
| DeleteCriticalSection(&This->lock); |
| if (This->stream) |
| IStream_Release(This->stream); |
| HeapFree(GetProcessHeap(), 0, This->imagebits); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_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; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream, |
| WICDecodeOptions cacheOptions) |
| { |
| TgaDecoder *This = impl_from_IWICBitmapDecoder(iface); |
| HRESULT hr=S_OK; |
| DWORD bytesread; |
| LARGE_INTEGER seek; |
| tga_footer footer; |
| int attribute_bitcount; |
| int mapped_depth=0; |
| |
| TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOptions); |
| |
| EnterCriticalSection(&This->lock); |
| |
| if (This->initialized) |
| { |
| hr = WINCODEC_ERR_WRONGSTATE; |
| goto end; |
| } |
| |
| seek.QuadPart = 0; |
| hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL); |
| if (FAILED(hr)) goto end; |
| |
| hr = IStream_Read(pIStream, &This->header, sizeof(tga_header), &bytesread); |
| if (SUCCEEDED(hr) && bytesread != sizeof(tga_header)) |
| { |
| TRACE("got only %u bytes\n", bytesread); |
| hr = E_FAIL; |
| } |
| if (FAILED(hr)) goto end; |
| |
| TRACE("imagetype=%u, colormap type=%u, depth=%u, image descriptor=0x%x\n", |
| This->header.image_type, This->header.colormap_type, |
| This->header.depth, This->header.image_descriptor); |
| |
| /* Sanity checking. Since TGA has no clear identifying markers, we need |
| * to be careful to not load a non-TGA image. */ |
| switch (This->header.image_type) |
| { |
| case IMAGETYPE_COLORMAPPED: |
| case IMAGETYPE_COLORMAPPED|IMAGETYPE_RLE: |
| if (This->header.colormap_type != 1) |
| hr = E_FAIL; |
| mapped_depth = This->header.colormap_entrysize; |
| break; |
| case IMAGETYPE_TRUECOLOR: |
| case IMAGETYPE_TRUECOLOR|IMAGETYPE_RLE: |
| if (This->header.colormap_type != 0 && This->header.colormap_type != 1) |
| hr = E_FAIL; |
| mapped_depth = This->header.depth; |
| break; |
| case IMAGETYPE_GRAYSCALE: |
| case IMAGETYPE_GRAYSCALE|IMAGETYPE_RLE: |
| if (This->header.colormap_type != 0) |
| hr = E_FAIL; |
| mapped_depth = 0; |
| break; |
| default: |
| hr = E_FAIL; |
| } |
| |
| if (This->header.depth != 8 && This->header.depth != 16 && |
| This->header.depth != 24 && This->header.depth != 32) |
| hr = E_FAIL; |
| |
| if ((This->header.image_descriptor & 0xc0) != 0) |
| hr = E_FAIL; |
| |
| attribute_bitcount = This->header.image_descriptor & IMAGE_ATTRIBUTE_BITCOUNT_MASK; |
| |
| if (attribute_bitcount && |
| !((mapped_depth == 32 && attribute_bitcount == 8) || |
| (mapped_depth == 16 && attribute_bitcount == 1))) |
| hr = E_FAIL; |
| |
| if (FAILED(hr)) |
| { |
| WARN("bad tga header\n"); |
| goto end; |
| } |
| |
| /* Locate data in the file based on the header. */ |
| This->id_offset = sizeof(tga_header); |
| This->colormap_offset = This->id_offset + This->header.id_length; |
| if (This->header.colormap_type == 1) |
| This->colormap_length = ((This->header.colormap_entrysize+7)/8) * This->header.colormap_length; |
| else |
| This->colormap_length = 0; |
| This->image_offset = This->colormap_offset + This->colormap_length; |
| |
| /* Read footer if there is one */ |
| seek.QuadPart = -(LONGLONG)sizeof(tga_footer); |
| hr = IStream_Seek(pIStream, seek, STREAM_SEEK_END, NULL); |
| |
| if (SUCCEEDED(hr)) { |
| hr = IStream_Read(pIStream, &footer, sizeof(tga_footer), &bytesread); |
| if (SUCCEEDED(hr) && bytesread != sizeof(tga_footer)) |
| { |
| TRACE("got only %u footer bytes\n", bytesread); |
| hr = E_FAIL; |
| } |
| |
| if (memcmp(footer.magic, tga_footer_magic, sizeof(tga_footer_magic)) == 0) |
| { |
| This->extension_area_offset = footer.extension_area_offset; |
| This->developer_directory_offset = footer.developer_directory_offset; |
| } |
| else |
| { |
| This->extension_area_offset = 0; |
| This->developer_directory_offset = 0; |
| } |
| } |
| else |
| { |
| /* File is too small to have a footer. */ |
| This->extension_area_offset = 0; |
| This->developer_directory_offset = 0; |
| hr = S_OK; |
| } |
| |
| if (This->extension_area_offset) |
| { |
| seek.QuadPart = This->extension_area_offset; |
| hr = IStream_Seek(pIStream, seek, STREAM_SEEK_SET, NULL); |
| if (FAILED(hr)) goto end; |
| |
| hr = IStream_Read(pIStream, &This->extension_area, sizeof(tga_extension_area), &bytesread); |
| if (SUCCEEDED(hr) && bytesread != sizeof(tga_extension_area)) |
| { |
| TRACE("got only %u extension area bytes\n", bytesread); |
| hr = E_FAIL; |
| } |
| if (SUCCEEDED(hr) && This->extension_area.size < 495) |
| { |
| TRACE("extension area is only %u bytes long\n", This->extension_area.size); |
| hr = E_FAIL; |
| } |
| if (FAILED(hr)) goto end; |
| } |
| |
| IStream_AddRef(pIStream); |
| This->stream = pIStream; |
| This->initialized = TRUE; |
| |
| end: |
| LeaveCriticalSection(&This->lock); |
| return hr; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_GetContainerFormat(IWICBitmapDecoder *iface, |
| GUID *pguidContainerFormat) |
| { |
| memcpy(pguidContainerFormat, &GUID_WineContainerFormatTga, sizeof(GUID)); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_GetDecoderInfo(IWICBitmapDecoder *iface, |
| IWICBitmapDecoderInfo **ppIDecoderInfo) |
| { |
| HRESULT hr; |
| IWICComponentInfo *compinfo; |
| |
| TRACE("(%p,%p)\n", iface, ppIDecoderInfo); |
| |
| hr = CreateComponentInfo(&CLSID_WineTgaDecoder, &compinfo); |
| if (FAILED(hr)) return hr; |
| |
| hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo, |
| (void**)ppIDecoderInfo); |
| |
| IWICComponentInfo_Release(compinfo); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_CopyPalette(IWICBitmapDecoder *iface, |
| IWICPalette *pIPalette) |
| { |
| FIXME("(%p,%p): stub\n", iface, pIPalette); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface, |
| IWICMetadataQueryReader **ppIMetadataQueryReader) |
| { |
| FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_GetPreview(IWICBitmapDecoder *iface, |
| IWICBitmapSource **ppIBitmapSource) |
| { |
| FIXME("(%p,%p): stub\n", iface, ppIBitmapSource); |
| return WINCODEC_ERR_UNSUPPORTEDOPERATION; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_GetColorContexts(IWICBitmapDecoder *iface, |
| UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount) |
| { |
| FIXME("(%p,%u,%p,%p): stub\n", iface, cCount, ppIColorContexts, pcActualCount); |
| return WINCODEC_ERR_UNSUPPORTEDOPERATION; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_GetThumbnail(IWICBitmapDecoder *iface, |
| IWICBitmapSource **ppIThumbnail) |
| { |
| FIXME("(%p,%p): stub\n", iface, ppIThumbnail); |
| return WINCODEC_ERR_CODECNOTHUMBNAIL; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_GetFrameCount(IWICBitmapDecoder *iface, |
| UINT *pCount) |
| { |
| if (!pCount) return E_INVALIDARG; |
| |
| *pCount = 1; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_GetFrame(IWICBitmapDecoder *iface, |
| UINT index, IWICBitmapFrameDecode **ppIBitmapFrame) |
| { |
| TgaDecoder *This = impl_from_IWICBitmapDecoder(iface); |
| TRACE("(%p,%p)\n", iface, ppIBitmapFrame); |
| |
| if (!This->initialized) return WINCODEC_ERR_FRAMEMISSING; |
| |
| if (index != 0) return E_INVALIDARG; |
| |
| IWICBitmapDecoder_AddRef(iface); |
| *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface; |
| |
| return S_OK; |
| } |
| |
| static const IWICBitmapDecoderVtbl TgaDecoder_Vtbl = { |
| TgaDecoder_QueryInterface, |
| TgaDecoder_AddRef, |
| TgaDecoder_Release, |
| TgaDecoder_QueryCapability, |
| TgaDecoder_Initialize, |
| TgaDecoder_GetContainerFormat, |
| TgaDecoder_GetDecoderInfo, |
| TgaDecoder_CopyPalette, |
| TgaDecoder_GetMetadataQueryReader, |
| TgaDecoder_GetPreview, |
| TgaDecoder_GetColorContexts, |
| TgaDecoder_GetThumbnail, |
| TgaDecoder_GetFrameCount, |
| TgaDecoder_GetFrame |
| }; |
| |
| static HRESULT WINAPI TgaDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *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_IWICBitmapSource, iid) || |
| IsEqualIID(&IID_IWICBitmapFrameDecode, iid)) |
| { |
| *ppv = iface; |
| } |
| else |
| { |
| *ppv = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| IUnknown_AddRef((IUnknown*)*ppv); |
| return S_OK; |
| } |
| |
| static ULONG WINAPI TgaDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface) |
| { |
| TgaDecoder *This = impl_from_IWICBitmapFrameDecode(iface); |
| return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface); |
| } |
| |
| static ULONG WINAPI TgaDecoder_Frame_Release(IWICBitmapFrameDecode *iface) |
| { |
| TgaDecoder *This = impl_from_IWICBitmapFrameDecode(iface); |
| return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface); |
| } |
| |
| static HRESULT WINAPI TgaDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface, |
| UINT *puiWidth, UINT *puiHeight) |
| { |
| TgaDecoder *This = impl_from_IWICBitmapFrameDecode(iface); |
| |
| *puiWidth = This->header.width; |
| *puiHeight = This->header.height; |
| |
| TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight); |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface, |
| WICPixelFormatGUID *pPixelFormat) |
| { |
| TgaDecoder *This = impl_from_IWICBitmapFrameDecode(iface); |
| int attribute_bitcount; |
| byte attribute_type; |
| |
| TRACE("(%p,%p)\n", iface, pPixelFormat); |
| |
| attribute_bitcount = This->header.image_descriptor & IMAGE_ATTRIBUTE_BITCOUNT_MASK; |
| |
| if (attribute_bitcount && This->extension_area_offset) |
| attribute_type = This->extension_area.attributes_type; |
| else if (attribute_bitcount) |
| attribute_type = ATTRIBUTE_ALPHA; |
| else |
| attribute_type = ATTRIBUTE_NO_ALPHA; |
| |
| switch (This->header.image_type & ~IMAGETYPE_RLE) |
| { |
| case IMAGETYPE_COLORMAPPED: |
| switch (This->header.depth) |
| { |
| case 8: |
| memcpy(pPixelFormat, &GUID_WICPixelFormat8bppIndexed, sizeof(GUID)); |
| break; |
| default: |
| FIXME("Unhandled indexed color depth %u\n", This->header.depth); |
| return E_NOTIMPL; |
| } |
| break; |
| case IMAGETYPE_TRUECOLOR: |
| switch (This->header.depth) |
| { |
| case 16: |
| switch (attribute_type) |
| { |
| case ATTRIBUTE_NO_ALPHA: |
| case ATTRIBUTE_UNDEFINED: |
| case ATTRIBUTE_UNDEFINED_PRESERVE: |
| memcpy(pPixelFormat, &GUID_WICPixelFormat16bppBGR555, sizeof(GUID)); |
| break; |
| case ATTRIBUTE_ALPHA: |
| case ATTRIBUTE_PALPHA: |
| memcpy(pPixelFormat, &GUID_WICPixelFormat16bppBGRA5551, sizeof(GUID)); |
| break; |
| default: |
| FIXME("Unhandled 16-bit attribute type %u\n", attribute_type); |
| return E_NOTIMPL; |
| } |
| break; |
| case 24: |
| memcpy(pPixelFormat, &GUID_WICPixelFormat24bppBGR, sizeof(GUID)); |
| break; |
| case 32: |
| switch (attribute_type) |
| { |
| case ATTRIBUTE_NO_ALPHA: |
| case ATTRIBUTE_UNDEFINED: |
| case ATTRIBUTE_UNDEFINED_PRESERVE: |
| memcpy(pPixelFormat, &GUID_WICPixelFormat32bppBGR, sizeof(GUID)); |
| break; |
| case ATTRIBUTE_ALPHA: |
| memcpy(pPixelFormat, &GUID_WICPixelFormat32bppBGRA, sizeof(GUID)); |
| break; |
| case ATTRIBUTE_PALPHA: |
| memcpy(pPixelFormat, &GUID_WICPixelFormat32bppPBGRA, sizeof(GUID)); |
| break; |
| default: |
| FIXME("Unhandled 32-bit attribute type %u\n", attribute_type); |
| return E_NOTIMPL; |
| } |
| break; |
| default: |
| FIXME("Unhandled truecolor depth %u\n", This->header.depth); |
| return E_NOTIMPL; |
| } |
| break; |
| case IMAGETYPE_GRAYSCALE: |
| switch (This->header.depth) |
| { |
| case 8: |
| memcpy(pPixelFormat, &GUID_WICPixelFormat8bppGray, sizeof(GUID)); |
| break; |
| case 16: |
| memcpy(pPixelFormat, &GUID_WICPixelFormat16bppGray, sizeof(GUID)); |
| break; |
| default: |
| FIXME("Unhandled grayscale depth %u\n", This->header.depth); |
| return E_NOTIMPL; |
| } |
| break; |
| default: |
| ERR("Unknown image type %u\n", This->header.image_type); |
| return E_FAIL; |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface, |
| double *pDpiX, double *pDpiY) |
| { |
| FIXME("(%p,%p,%p): stub\n", iface, pDpiX, pDpiY); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface, |
| IWICPalette *pIPalette) |
| { |
| TgaDecoder *This = impl_from_IWICBitmapFrameDecode(iface); |
| HRESULT hr=S_OK; |
| WICColor colors[256], *color; |
| BYTE *colormap_data; |
| WORD *wcolormap_data; |
| DWORD *dwcolormap_data; |
| LARGE_INTEGER seek; |
| ULONG bytesread; |
| int depth, attribute_bitcount, attribute_type; |
| int i; |
| |
| TRACE("(%p,%p)\n", iface, pIPalette); |
| |
| if (!This->colormap_length) |
| { |
| WARN("no colormap present in this file\n"); |
| return WINCODEC_ERR_PALETTEUNAVAILABLE; |
| } |
| |
| if (This->header.colormap_firstentry + This->header.colormap_length > 256) |
| { |
| FIXME("cannot read colormap with %i entries starting at %i\n", |
| This->header.colormap_firstentry + This->header.colormap_length, |
| This->header.colormap_firstentry); |
| return E_FAIL; |
| } |
| |
| colormap_data = HeapAlloc(GetProcessHeap(), 0, This->colormap_length); |
| if (!colormap_data) return E_OUTOFMEMORY; |
| |
| wcolormap_data = (WORD*)colormap_data; |
| dwcolormap_data = (DWORD*)colormap_data; |
| |
| EnterCriticalSection(&This->lock); |
| |
| seek.QuadPart = This->colormap_offset; |
| hr = IStream_Seek(This->stream, seek, STREAM_SEEK_SET, NULL); |
| |
| if (SUCCEEDED(hr)) |
| { |
| hr = IStream_Read(This->stream, colormap_data, This->colormap_length, &bytesread); |
| if (SUCCEEDED(hr) && bytesread != This->colormap_length) |
| { |
| WARN("expected %i bytes in colormap, got %i\n", This->colormap_length, bytesread); |
| hr = E_FAIL; |
| } |
| } |
| |
| LeaveCriticalSection(&This->lock); |
| |
| if (SUCCEEDED(hr)) |
| { |
| attribute_bitcount = This->header.image_descriptor & IMAGE_ATTRIBUTE_BITCOUNT_MASK; |
| |
| if (attribute_bitcount && This->extension_area_offset) |
| attribute_type = This->extension_area.attributes_type; |
| else if (attribute_bitcount) |
| attribute_type = ATTRIBUTE_ALPHA; |
| else |
| attribute_type = ATTRIBUTE_NO_ALPHA; |
| |
| depth = This->header.colormap_entrysize; |
| if (depth == 15) |
| { |
| depth = 16; |
| attribute_type = ATTRIBUTE_NO_ALPHA; |
| } |
| |
| memset(colors, 0, sizeof(colors)); |
| |
| color = &colors[This->header.colormap_firstentry]; |
| |
| /* Colormap entries can be in any truecolor format, and we have to convert them. */ |
| switch (depth) |
| { |
| case 16: |
| switch (attribute_type) |
| { |
| case ATTRIBUTE_NO_ALPHA: |
| case ATTRIBUTE_UNDEFINED: |
| case ATTRIBUTE_UNDEFINED_PRESERVE: |
| for (i=0; i<This->header.colormap_length; i++) |
| { |
| WORD srcval = wcolormap_data[i]; |
| *color++=0xff000000 | /* constant 255 alpha */ |
| ((srcval << 9) & 0xf80000) | /* r */ |
| ((srcval << 4) & 0x070000) | /* r - 3 bits */ |
| ((srcval << 6) & 0x00f800) | /* g */ |
| ((srcval << 1) & 0x000700) | /* g - 3 bits */ |
| ((srcval << 3) & 0x0000f8) | /* b */ |
| ((srcval >> 2) & 0x000007); /* b - 3 bits */ |
| } |
| break; |
| case ATTRIBUTE_ALPHA: |
| case ATTRIBUTE_PALPHA: |
| for (i=0; i<This->header.colormap_length; i++) |
| { |
| WORD srcval = wcolormap_data[i]; |
| *color++=((srcval & 0x8000) ? 0xff000000 : 0) | /* alpha */ |
| ((srcval << 9) & 0xf80000) | /* r */ |
| ((srcval << 4) & 0x070000) | /* r - 3 bits */ |
| ((srcval << 6) & 0x00f800) | /* g */ |
| ((srcval << 1) & 0x000700) | /* g - 3 bits */ |
| ((srcval << 3) & 0x0000f8) | /* b */ |
| ((srcval >> 2) & 0x000007); /* b - 3 bits */ |
| } |
| break; |
| default: |
| FIXME("Unhandled 16-bit attribute type %u\n", attribute_type); |
| hr = E_NOTIMPL; |
| } |
| break; |
| case 24: |
| for (i=0; i<This->header.colormap_length; i++) |
| { |
| *color++=0xff000000 | /* alpha */ |
| colormap_data[i*3+2] | /* red */ |
| colormap_data[i*3+1] | /* green */ |
| colormap_data[i*3]; /* blue */ |
| } |
| break; |
| case 32: |
| switch (attribute_type) |
| { |
| case ATTRIBUTE_NO_ALPHA: |
| case ATTRIBUTE_UNDEFINED: |
| case ATTRIBUTE_UNDEFINED_PRESERVE: |
| for (i=0; i<This->header.colormap_length; i++) |
| *color++=dwcolormap_data[i]|0xff000000; |
| break; |
| case ATTRIBUTE_ALPHA: |
| for (i=0; i<This->header.colormap_length; i++) |
| *color++=dwcolormap_data[i]; |
| break; |
| case ATTRIBUTE_PALPHA: |
| /* FIXME: Unpremultiply alpha */ |
| default: |
| FIXME("Unhandled 16-bit attribute type %u\n", attribute_type); |
| hr = E_NOTIMPL; |
| } |
| break; |
| default: |
| FIXME("Unhandled truecolor depth %u\n", This->header.depth); |
| hr = E_NOTIMPL; |
| } |
| } |
| |
| HeapFree(GetProcessHeap(), 0, colormap_data); |
| |
| if (SUCCEEDED(hr)) |
| hr = IWICPalette_InitializeCustom(pIPalette, colors, 256); |
| |
| return hr; |
| } |
| |
| static HRESULT TgaDecoder_ReadRLE(TgaDecoder *This, BYTE *imagebits, int datasize) |
| { |
| int i=0, j, bytesperpixel; |
| ULONG bytesread; |
| HRESULT hr=S_OK; |
| |
| bytesperpixel = This->header.depth / 8; |
| |
| while (i<datasize) |
| { |
| BYTE rc; |
| int count, size; |
| BYTE pixeldata[4]; |
| |
| hr = IStream_Read(This->stream, &rc, 1, &bytesread); |
| if (bytesread != 1) hr = E_FAIL; |
| if (FAILED(hr)) break; |
| |
| count = (rc&0x7f)+1; |
| size = count * bytesperpixel; |
| |
| if (size + i > datasize) |
| { |
| WARN("RLE packet too large\n"); |
| hr = E_FAIL; |
| break; |
| } |
| |
| if (rc&0x80) |
| { |
| /* Run-length packet */ |
| hr = IStream_Read(This->stream, pixeldata, bytesperpixel, &bytesread); |
| if (bytesread != bytesperpixel) hr = E_FAIL; |
| if (FAILED(hr)) break; |
| |
| if (bytesperpixel == 1) |
| memset(&imagebits[i], pixeldata[0], count); |
| else |
| { |
| for (j=0; j<count; j++) |
| memcpy(&imagebits[i+j*bytesperpixel], pixeldata, bytesperpixel); |
| } |
| } |
| else |
| { |
| /* Raw packet */ |
| hr = IStream_Read(This->stream, &imagebits[i], size, &bytesread); |
| if (bytesread != size) hr = E_FAIL; |
| if (FAILED(hr)) break; |
| } |
| |
| i += size; |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT TgaDecoder_ReadImage(TgaDecoder *This) |
| { |
| HRESULT hr=S_OK; |
| int datasize; |
| LARGE_INTEGER seek; |
| ULONG bytesread; |
| |
| if (This->imagebits) |
| return S_OK; |
| |
| EnterCriticalSection(&This->lock); |
| |
| if (!This->imagebits) |
| { |
| if (This->header.image_descriptor & IMAGE_RIGHTTOLEFT) |
| { |
| FIXME("Right to left image reading not implemented\n"); |
| hr = E_NOTIMPL; |
| } |
| |
| if (SUCCEEDED(hr)) |
| { |
| datasize = This->header.width * This->header.height * (This->header.depth / 8); |
| This->imagebits = HeapAlloc(GetProcessHeap(), 0, datasize); |
| if (!This->imagebits) hr = E_OUTOFMEMORY; |
| } |
| |
| if (SUCCEEDED(hr)) |
| { |
| seek.QuadPart = This->image_offset; |
| hr = IStream_Seek(This->stream, seek, STREAM_SEEK_SET, NULL); |
| } |
| |
| if (SUCCEEDED(hr)) |
| { |
| if (This->header.image_type & IMAGETYPE_RLE) |
| { |
| hr = TgaDecoder_ReadRLE(This, This->imagebits, datasize); |
| } |
| else |
| { |
| hr = IStream_Read(This->stream, This->imagebits, datasize, &bytesread); |
| if (SUCCEEDED(hr) && bytesread != datasize) |
| hr = E_FAIL; |
| } |
| } |
| |
| if (SUCCEEDED(hr)) |
| { |
| if (This->header.image_descriptor & IMAGE_TOPTOBOTTOM) |
| { |
| This->origin = This->imagebits; |
| This->stride = This->header.width * (This->header.depth / 8); |
| } |
| else |
| { |
| This->stride = -This->header.width * (This->header.depth / 8); |
| This->origin = This->imagebits + This->header.width * (This->header.height - 1) * (This->header.depth / 8); |
| } |
| } |
| else |
| { |
| HeapFree(GetProcessHeap(), 0, This->imagebits); |
| This->imagebits = NULL; |
| } |
| } |
| |
| LeaveCriticalSection(&This->lock); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface, |
| const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer) |
| { |
| TgaDecoder *This = impl_from_IWICBitmapFrameDecode(iface); |
| HRESULT hr; |
| |
| TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer); |
| |
| hr = TgaDecoder_ReadImage(This); |
| |
| if (SUCCEEDED(hr)) |
| { |
| hr = copy_pixels(This->header.depth, This->origin, |
| This->header.width, This->header.height, This->stride, |
| prc, cbStride, cbBufferSize, pbBuffer); |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface, |
| IWICMetadataQueryReader **ppIMetadataQueryReader) |
| { |
| FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader); |
| return WINCODEC_ERR_UNSUPPORTEDOPERATION; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface, |
| UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount) |
| { |
| FIXME("(%p,%u,%p,%p): stub\n", iface, cCount, ppIColorContexts, pcActualCount); |
| return WINCODEC_ERR_UNSUPPORTEDOPERATION; |
| } |
| |
| static HRESULT WINAPI TgaDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface, |
| IWICBitmapSource **ppIThumbnail) |
| { |
| FIXME("(%p,%p): stub\n", iface, ppIThumbnail); |
| return WINCODEC_ERR_CODECNOTHUMBNAIL; |
| } |
| |
| static const IWICBitmapFrameDecodeVtbl TgaDecoder_Frame_Vtbl = { |
| TgaDecoder_Frame_QueryInterface, |
| TgaDecoder_Frame_AddRef, |
| TgaDecoder_Frame_Release, |
| TgaDecoder_Frame_GetSize, |
| TgaDecoder_Frame_GetPixelFormat, |
| TgaDecoder_Frame_GetResolution, |
| TgaDecoder_Frame_CopyPalette, |
| TgaDecoder_Frame_CopyPixels, |
| TgaDecoder_Frame_GetMetadataQueryReader, |
| TgaDecoder_Frame_GetColorContexts, |
| TgaDecoder_Frame_GetThumbnail |
| }; |
| |
| HRESULT TgaDecoder_CreateInstance(REFIID iid, void** ppv) |
| { |
| TgaDecoder *This; |
| HRESULT ret; |
| |
| TRACE("(%s,%p)\n", debugstr_guid(iid), ppv); |
| |
| *ppv = NULL; |
| |
| This = HeapAlloc(GetProcessHeap(), 0, sizeof(TgaDecoder)); |
| if (!This) return E_OUTOFMEMORY; |
| |
| This->IWICBitmapDecoder_iface.lpVtbl = &TgaDecoder_Vtbl; |
| This->IWICBitmapFrameDecode_iface.lpVtbl = &TgaDecoder_Frame_Vtbl; |
| This->ref = 1; |
| This->initialized = FALSE; |
| This->stream = NULL; |
| This->imagebits = NULL; |
| InitializeCriticalSection(&This->lock); |
| This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": TgaDecoder.lock"); |
| |
| ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv); |
| IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface); |
| |
| return ret; |
| } |