From c837215382c3c42ea38cb931319e46a5acfe288d Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Sun, 8 Nov 2020 11:10:02 -0600 Subject: [PATCH] windowscodecs: Move jpeg decoding to the unix lib. Signed-off-by: Esme Povirk Signed-off-by: Alexandre Julliard --- dlls/windowscodecs/Makefile.in | 1 + dlls/windowscodecs/decoder.c | 7 + dlls/windowscodecs/jpegformat.c | 671 +------------------------ dlls/windowscodecs/libjpeg.c | 464 +++++++++++++++++ dlls/windowscodecs/unix_lib.c | 3 + dlls/windowscodecs/wincodecs_private.h | 2 + 6 files changed, 491 insertions(+), 657 deletions(-) create mode 100644 dlls/windowscodecs/libjpeg.c diff --git a/dlls/windowscodecs/Makefile.in b/dlls/windowscodecs/Makefile.in index 2dd6a78e147..94b75b62596 100644 --- a/dlls/windowscodecs/Makefile.in +++ b/dlls/windowscodecs/Makefile.in @@ -22,6 +22,7 @@ C_SRCS = \ imgfactory.c \ info.c \ jpegformat.c \ + libjpeg.c \ libpng.c \ libtiff.c \ main.c \ diff --git a/dlls/windowscodecs/decoder.c b/dlls/windowscodecs/decoder.c index b51ca0a8def..ad5127cd258 100644 --- a/dlls/windowscodecs/decoder.c +++ b/dlls/windowscodecs/decoder.c @@ -460,6 +460,13 @@ static HRESULT WINAPI CommonDecoderFrame_GetColorContexts(IWICBitmapFrameDecode if (!pcActualCount) return E_INVALIDARG; + if (This->parent->file_info.flags & DECODER_FLAGS_UNSUPPORTED_COLOR_CONTEXT) + { + FIXME("not supported for %s\n", wine_dbgstr_guid(&This->parent->decoder_info.clsid)); + *pcActualCount = 0; + return WINCODEC_ERR_UNSUPPORTEDOPERATION; + } + *pcActualCount = This->decoder_frame.num_color_contexts; if (This->decoder_frame.num_color_contexts && cCount && ppIColorContexts) diff --git a/dlls/windowscodecs/jpegformat.c b/dlls/windowscodecs/jpegformat.c index e04e914cda1..5be35a1ccb4 100644 --- a/dlls/windowscodecs/jpegformat.c +++ b/dlls/windowscodecs/jpegformat.c @@ -143,657 +143,6 @@ static void emit_message_fn(j_common_ptr cinfo, int msg_level) } } -typedef struct { - IWICBitmapDecoder IWICBitmapDecoder_iface; - IWICBitmapFrameDecode IWICBitmapFrameDecode_iface; - IWICMetadataBlockReader IWICMetadataBlockReader_iface; - LONG ref; - BOOL initialized; - BOOL cinfo_initialized; - IStream *stream; - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - struct jpeg_source_mgr source_mgr; - BYTE source_buffer[1024]; - UINT bpp, stride; - BYTE *image_data; - CRITICAL_SECTION lock; -} JpegDecoder; - -static inline JpegDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface) -{ - return CONTAINING_RECORD(iface, JpegDecoder, IWICBitmapDecoder_iface); -} - -static inline JpegDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface) -{ - return CONTAINING_RECORD(iface, JpegDecoder, IWICBitmapFrameDecode_iface); -} - -static inline JpegDecoder *decoder_from_decompress(j_decompress_ptr decompress) -{ - return CONTAINING_RECORD(decompress, JpegDecoder, cinfo); -} - -static inline JpegDecoder *impl_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface) -{ - return CONTAINING_RECORD(iface, JpegDecoder, IWICMetadataBlockReader_iface); -} - -static HRESULT WINAPI JpegDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid, - void **ppv) -{ - JpegDecoder *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 JpegDecoder_AddRef(IWICBitmapDecoder *iface) -{ - JpegDecoder *This = impl_from_IWICBitmapDecoder(iface); - ULONG ref = InterlockedIncrement(&This->ref); - - TRACE("(%p) refcount=%u\n", iface, ref); - - return ref; -} - -static ULONG WINAPI JpegDecoder_Release(IWICBitmapDecoder *iface) -{ - JpegDecoder *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->cinfo_initialized) pjpeg_destroy_decompress(&This->cinfo); - if (This->stream) IStream_Release(This->stream); - HeapFree(GetProcessHeap(), 0, This->image_data); - HeapFree(GetProcessHeap(), 0, This); - } - - return ref; -} - -static HRESULT WINAPI JpegDecoder_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; - /* FIXME: WICBitmapDecoderCapabilityCanEnumerateMetadata */ - return S_OK; -} - -static void source_mgr_init_source(j_decompress_ptr cinfo) -{ -} - -static jpeg_boolean source_mgr_fill_input_buffer(j_decompress_ptr cinfo) -{ - JpegDecoder *This = decoder_from_decompress(cinfo); - HRESULT hr; - ULONG bytesread; - - hr = IStream_Read(This->stream, This->source_buffer, 1024, &bytesread); - - if (FAILED(hr) || bytesread == 0) - { - return FALSE; - } - else - { - This->source_mgr.next_input_byte = This->source_buffer; - This->source_mgr.bytes_in_buffer = bytesread; - return TRUE; - } -} - -static void source_mgr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) -{ - JpegDecoder *This = decoder_from_decompress(cinfo); - LARGE_INTEGER seek; - - if (num_bytes > This->source_mgr.bytes_in_buffer) - { - seek.QuadPart = num_bytes - This->source_mgr.bytes_in_buffer; - IStream_Seek(This->stream, seek, STREAM_SEEK_CUR, NULL); - This->source_mgr.bytes_in_buffer = 0; - } - else if (num_bytes > 0) - { - This->source_mgr.next_input_byte += num_bytes; - This->source_mgr.bytes_in_buffer -= num_bytes; - } -} - -static void source_mgr_term_source(j_decompress_ptr cinfo) -{ -} - -static HRESULT WINAPI JpegDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream, - WICDecodeOptions cacheOptions) -{ - JpegDecoder *This = impl_from_IWICBitmapDecoder(iface); - int ret; - LARGE_INTEGER seek; - jmp_buf jmpbuf; - UINT data_size, i; - - TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOptions); - - EnterCriticalSection(&This->lock); - - if (This->cinfo_initialized) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - pjpeg_std_error(&This->jerr); - - This->jerr.error_exit = error_exit_fn; - This->jerr.emit_message = emit_message_fn; - - This->cinfo.err = &This->jerr; - - This->cinfo.client_data = jmpbuf; - - if (setjmp(jmpbuf)) - { - LeaveCriticalSection(&This->lock); - return E_FAIL; - } - - pjpeg_CreateDecompress(&This->cinfo, JPEG_LIB_VERSION, sizeof(struct jpeg_decompress_struct)); - - This->cinfo_initialized = TRUE; - - This->stream = pIStream; - IStream_AddRef(pIStream); - - seek.QuadPart = 0; - IStream_Seek(This->stream, seek, STREAM_SEEK_SET, NULL); - - This->source_mgr.bytes_in_buffer = 0; - This->source_mgr.init_source = source_mgr_init_source; - This->source_mgr.fill_input_buffer = source_mgr_fill_input_buffer; - This->source_mgr.skip_input_data = source_mgr_skip_input_data; - This->source_mgr.resync_to_restart = pjpeg_resync_to_restart; - This->source_mgr.term_source = source_mgr_term_source; - - This->cinfo.src = &This->source_mgr; - - ret = pjpeg_read_header(&This->cinfo, TRUE); - - if (ret != JPEG_HEADER_OK) { - WARN("Jpeg image in stream has bad format, read header returned %d.\n",ret); - LeaveCriticalSection(&This->lock); - return E_FAIL; - } - - switch (This->cinfo.jpeg_color_space) - { - case JCS_GRAYSCALE: - This->cinfo.out_color_space = JCS_GRAYSCALE; - break; - case JCS_RGB: - case JCS_YCbCr: - This->cinfo.out_color_space = JCS_RGB; - break; - case JCS_CMYK: - case JCS_YCCK: - This->cinfo.out_color_space = JCS_CMYK; - break; - default: - ERR("Unknown JPEG color space %i\n", This->cinfo.jpeg_color_space); - LeaveCriticalSection(&This->lock); - return E_FAIL; - } - - if (!pjpeg_start_decompress(&This->cinfo)) - { - ERR("jpeg_start_decompress failed\n"); - LeaveCriticalSection(&This->lock); - return E_FAIL; - } - - if (This->cinfo.out_color_space == JCS_GRAYSCALE) This->bpp = 8; - else if (This->cinfo.out_color_space == JCS_CMYK) This->bpp = 32; - else This->bpp = 24; - - This->stride = (This->bpp * This->cinfo.output_width + 7) / 8; - data_size = This->stride * This->cinfo.output_height; - - This->image_data = heap_alloc(data_size); - if (!This->image_data) - { - LeaveCriticalSection(&This->lock); - return E_OUTOFMEMORY; - } - - while (This->cinfo.output_scanline < This->cinfo.output_height) - { - UINT first_scanline = This->cinfo.output_scanline; - UINT max_rows; - JSAMPROW out_rows[4]; - JDIMENSION ret; - - max_rows = min(This->cinfo.output_height-first_scanline, 4); - for (i=0; iimage_data + This->stride * (first_scanline+i); - - ret = pjpeg_read_scanlines(&This->cinfo, out_rows, max_rows); - if (ret == 0) - { - ERR("read_scanlines failed\n"); - LeaveCriticalSection(&This->lock); - return E_FAIL; - } - } - - if (This->bpp == 24) - { - /* libjpeg gives us RGB data and we want BGR, so byteswap the data */ - reverse_bgr8(3, This->image_data, - This->cinfo.output_width, This->cinfo.output_height, - This->stride); - } - - if (This->cinfo.out_color_space == JCS_CMYK && This->cinfo.saw_Adobe_marker) - { - /* Adobe JPEG's have inverted CMYK data. */ - for (i=0; iimage_data[i] ^= 0xff; - } - - This->initialized = TRUE; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI JpegDecoder_GetContainerFormat(IWICBitmapDecoder *iface, - GUID *pguidContainerFormat) -{ - memcpy(pguidContainerFormat, &GUID_ContainerFormatJpeg, sizeof(GUID)); - return S_OK; -} - -static HRESULT WINAPI JpegDecoder_GetDecoderInfo(IWICBitmapDecoder *iface, - IWICBitmapDecoderInfo **ppIDecoderInfo) -{ - TRACE("(%p,%p)\n", iface, ppIDecoderInfo); - - return get_decoder_info(&CLSID_WICJpegDecoder, ppIDecoderInfo); -} - -static HRESULT WINAPI JpegDecoder_CopyPalette(IWICBitmapDecoder *iface, - IWICPalette *pIPalette) -{ - TRACE("(%p,%p)\n", iface, pIPalette); - - return WINCODEC_ERR_PALETTEUNAVAILABLE; -} - -static HRESULT WINAPI JpegDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface, - IWICMetadataQueryReader **reader) -{ - TRACE("(%p,%p)\n", iface, reader); - - if (!reader) return E_INVALIDARG; - - *reader = NULL; - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI JpegDecoder_GetPreview(IWICBitmapDecoder *iface, - IWICBitmapSource **ppIBitmapSource) -{ - FIXME("(%p,%p): stub\n", iface, ppIBitmapSource); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI JpegDecoder_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 JpegDecoder_GetThumbnail(IWICBitmapDecoder *iface, - IWICBitmapSource **ppIThumbnail) -{ - FIXME("(%p,%p): stub\n", iface, ppIThumbnail); - return WINCODEC_ERR_CODECNOTHUMBNAIL; -} - -static HRESULT WINAPI JpegDecoder_GetFrameCount(IWICBitmapDecoder *iface, - UINT *pCount) -{ - if (!pCount) return E_INVALIDARG; - - *pCount = 1; - return S_OK; -} - -static HRESULT WINAPI JpegDecoder_GetFrame(IWICBitmapDecoder *iface, - UINT index, IWICBitmapFrameDecode **ppIBitmapFrame) -{ - JpegDecoder *This = impl_from_IWICBitmapDecoder(iface); - TRACE("(%p,%u,%p)\n", iface, index, 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 JpegDecoder_Vtbl = { - JpegDecoder_QueryInterface, - JpegDecoder_AddRef, - JpegDecoder_Release, - JpegDecoder_QueryCapability, - JpegDecoder_Initialize, - JpegDecoder_GetContainerFormat, - JpegDecoder_GetDecoderInfo, - JpegDecoder_CopyPalette, - JpegDecoder_GetMetadataQueryReader, - JpegDecoder_GetPreview, - JpegDecoder_GetColorContexts, - JpegDecoder_GetThumbnail, - JpegDecoder_GetFrameCount, - JpegDecoder_GetFrame -}; - -static HRESULT WINAPI JpegDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid, - void **ppv) -{ - JpegDecoder *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 - { - *ppv = NULL; - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown*)*ppv); - return S_OK; -} - -static ULONG WINAPI JpegDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface) -{ - JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface); - return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface); -} - -static ULONG WINAPI JpegDecoder_Frame_Release(IWICBitmapFrameDecode *iface) -{ - JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface); - return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface); -} - -static HRESULT WINAPI JpegDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface, - UINT *puiWidth, UINT *puiHeight) -{ - JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface); - *puiWidth = This->cinfo.output_width; - *puiHeight = This->cinfo.output_height; - TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight); - return S_OK; -} - -static HRESULT WINAPI JpegDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface, - WICPixelFormatGUID *pPixelFormat) -{ - JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface); - TRACE("(%p,%p)\n", iface, pPixelFormat); - if (This->cinfo.out_color_space == JCS_RGB) - memcpy(pPixelFormat, &GUID_WICPixelFormat24bppBGR, sizeof(GUID)); - else if (This->cinfo.out_color_space == JCS_CMYK) - memcpy(pPixelFormat, &GUID_WICPixelFormat32bppCMYK, sizeof(GUID)); - else /* This->cinfo.out_color_space == JCS_GRAYSCALE */ - memcpy(pPixelFormat, &GUID_WICPixelFormat8bppGray, sizeof(GUID)); - return S_OK; -} - -static HRESULT WINAPI JpegDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface, - double *pDpiX, double *pDpiY) -{ - JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface); - - EnterCriticalSection(&This->lock); - - switch (This->cinfo.density_unit) - { - case 2: /* pixels per centimeter */ - *pDpiX = This->cinfo.X_density * 2.54; - *pDpiY = This->cinfo.Y_density * 2.54; - break; - - case 1: /* pixels per inch */ - *pDpiX = This->cinfo.X_density; - *pDpiY = This->cinfo.Y_density; - break; - - case 0: /* unknown */ - default: - *pDpiX = 96.0; - *pDpiY = 96.0; - break; - } - - LeaveCriticalSection(&This->lock); - - TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY); - - return S_OK; -} - -static HRESULT WINAPI JpegDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface, - IWICPalette *pIPalette) -{ - TRACE("(%p,%p)\n", iface, pIPalette); - - return WINCODEC_ERR_PALETTEUNAVAILABLE; -} - -static HRESULT WINAPI JpegDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface, - const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer) -{ - JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface); - - TRACE("(%p,%s,%u,%u,%p)\n", iface, debug_wic_rect(prc), cbStride, cbBufferSize, pbBuffer); - - return copy_pixels(This->bpp, This->image_data, - This->cinfo.output_width, This->cinfo.output_height, This->stride, - prc, cbStride, cbBufferSize, pbBuffer); -} - -static HRESULT WINAPI JpegDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface, - IWICMetadataQueryReader **ppIMetadataQueryReader) -{ - JpegDecoder *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 JpegDecoder_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 JpegDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface, - IWICBitmapSource **ppIThumbnail) -{ - FIXME("(%p,%p): stub\n", iface, ppIThumbnail); - return WINCODEC_ERR_CODECNOTHUMBNAIL; -} - -static const IWICBitmapFrameDecodeVtbl JpegDecoder_Frame_Vtbl = { - JpegDecoder_Frame_QueryInterface, - JpegDecoder_Frame_AddRef, - JpegDecoder_Frame_Release, - JpegDecoder_Frame_GetSize, - JpegDecoder_Frame_GetPixelFormat, - JpegDecoder_Frame_GetResolution, - JpegDecoder_Frame_CopyPalette, - JpegDecoder_Frame_CopyPixels, - JpegDecoder_Frame_GetMetadataQueryReader, - JpegDecoder_Frame_GetColorContexts, - JpegDecoder_Frame_GetThumbnail -}; - -static HRESULT WINAPI JpegDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid, - void **ppv) -{ - JpegDecoder *This = impl_from_IWICMetadataBlockReader(iface); - return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv); -} - -static ULONG WINAPI JpegDecoder_Block_AddRef(IWICMetadataBlockReader *iface) -{ - JpegDecoder *This = impl_from_IWICMetadataBlockReader(iface); - return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface); -} - -static ULONG WINAPI JpegDecoder_Block_Release(IWICMetadataBlockReader *iface) -{ - JpegDecoder *This = impl_from_IWICMetadataBlockReader(iface); - return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface); -} - -static HRESULT WINAPI JpegDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface, - GUID *pguidContainerFormat) -{ - TRACE("%p,%p\n", iface, pguidContainerFormat); - - if (!pguidContainerFormat) return E_INVALIDARG; - - memcpy(pguidContainerFormat, &GUID_ContainerFormatJpeg, sizeof(GUID)); - - return S_OK; -} - -static HRESULT WINAPI JpegDecoder_Block_GetCount(IWICMetadataBlockReader *iface, - UINT *pcCount) -{ - FIXME("%p,%p\n", iface, pcCount); - - if (!pcCount) return E_INVALIDARG; - - *pcCount = 0; - - return S_OK; -} - -static HRESULT WINAPI JpegDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface, - UINT nIndex, IWICMetadataReader **ppIMetadataReader) -{ - FIXME("%p,%d,%p\n", iface, nIndex, ppIMetadataReader); - return E_INVALIDARG; -} - -static HRESULT WINAPI JpegDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface, - IEnumUnknown **ppIEnumMetadata) -{ - FIXME("%p,%p\n", iface, ppIEnumMetadata); - return E_NOTIMPL; -} - -static const IWICMetadataBlockReaderVtbl JpegDecoder_Block_Vtbl = { - JpegDecoder_Block_QueryInterface, - JpegDecoder_Block_AddRef, - JpegDecoder_Block_Release, - JpegDecoder_Block_GetContainerFormat, - JpegDecoder_Block_GetCount, - JpegDecoder_Block_GetReaderByIndex, - JpegDecoder_Block_GetEnumerator, -}; - -HRESULT JpegDecoder_CreateInstance(REFIID iid, void** ppv) -{ - JpegDecoder *This; - HRESULT ret; - - TRACE("(%s,%p)\n", debugstr_guid(iid), ppv); - - if (!libjpeg_handle && !load_libjpeg()) - { - ERR("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG); - return E_FAIL; - } - - *ppv = NULL; - - This = HeapAlloc(GetProcessHeap(), 0, sizeof(JpegDecoder)); - if (!This) return E_OUTOFMEMORY; - - This->IWICBitmapDecoder_iface.lpVtbl = &JpegDecoder_Vtbl; - This->IWICBitmapFrameDecode_iface.lpVtbl = &JpegDecoder_Frame_Vtbl; - This->IWICMetadataBlockReader_iface.lpVtbl = &JpegDecoder_Block_Vtbl; - This->ref = 1; - This->initialized = FALSE; - This->cinfo_initialized = FALSE; - This->stream = NULL; - This->image_data = NULL; - InitializeCriticalSection(&This->lock); - This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JpegDecoder.lock"); - - ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv); - IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface); - - return ret; -} - typedef struct jpeg_compress_format { const WICPixelFormatGUID *guid; int bpp; @@ -1548,12 +897,6 @@ HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv) #else /* !defined(SONAME_LIBJPEG) */ -HRESULT JpegDecoder_CreateInstance(REFIID iid, void** ppv) -{ - ERR("Trying to load JPEG picture, but JPEG support is not compiled in.\n"); - return E_FAIL; -} - HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv) { ERR("Trying to save JPEG picture, but JPEG support is not compiled in.\n"); @@ -1561,3 +904,17 @@ HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv) } #endif + +HRESULT JpegDecoder_CreateInstance(REFIID iid, void** ppv) +{ + HRESULT hr; + struct decoder *decoder; + struct decoder_info decoder_info; + + hr = get_unix_decoder(&CLSID_WICJpegDecoder, &decoder_info, &decoder); + + if (SUCCEEDED(hr)) + hr = CommonDecoder_CreateInstance(decoder, &decoder_info, iid, ppv); + + return hr; +} diff --git a/dlls/windowscodecs/libjpeg.c b/dlls/windowscodecs/libjpeg.c new file mode 100644 index 00000000000..58ca58e93b4 --- /dev/null +++ b/dlls/windowscodecs/libjpeg.c @@ -0,0 +1,464 @@ +/* + * Copyright 2009 Vincent Povirk for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" +#include "wine/port.h" + +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include +#include +#include + +#ifdef SONAME_LIBJPEG +/* This is a hack, so jpeglib.h does not redefine INT32 and the like*/ +#define XMD_H +#define UINT8 JPEG_UINT8 +#define UINT16 JPEG_UINT16 +#define boolean jpeg_boolean +#undef HAVE_STDLIB_H +# include +#undef HAVE_STDLIB_H +#define HAVE_STDLIB_H 1 +#undef UINT8 +#undef UINT16 +#undef boolean +#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_LIBJPEG +WINE_DECLARE_DEBUG_CHANNEL(jpeg); + +static CRITICAL_SECTION init_jpeg_cs; +static CRITICAL_SECTION_DEBUG init_jpeg_cs_debug = +{ + 0, 0, &init_jpeg_cs, + { &init_jpeg_cs_debug.ProcessLocksList, + &init_jpeg_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": init_jpeg_cs") } +}; +static CRITICAL_SECTION init_jpeg_cs = { &init_jpeg_cs_debug, -1, 0, 0, 0, 0 }; + +static void *libjpeg_handle; + +#define MAKE_FUNCPTR(f) static typeof(f) * p##f +MAKE_FUNCPTR(jpeg_CreateCompress); +MAKE_FUNCPTR(jpeg_CreateDecompress); +MAKE_FUNCPTR(jpeg_destroy_compress); +MAKE_FUNCPTR(jpeg_destroy_decompress); +MAKE_FUNCPTR(jpeg_finish_compress); +MAKE_FUNCPTR(jpeg_read_header); +MAKE_FUNCPTR(jpeg_read_scanlines); +MAKE_FUNCPTR(jpeg_resync_to_restart); +MAKE_FUNCPTR(jpeg_set_defaults); +MAKE_FUNCPTR(jpeg_start_compress); +MAKE_FUNCPTR(jpeg_start_decompress); +MAKE_FUNCPTR(jpeg_std_error); +MAKE_FUNCPTR(jpeg_write_scanlines); +#undef MAKE_FUNCPTR + +static void *load_libjpeg(void) +{ + void *result; + + RtlEnterCriticalSection(&init_jpeg_cs); + + if((libjpeg_handle = dlopen(SONAME_LIBJPEG, RTLD_NOW)) != NULL) { + +#define LOAD_FUNCPTR(f) \ + if((p##f = dlsym(libjpeg_handle, #f)) == NULL) { \ + ERR("failed to load symbol %s\n", #f); \ + libjpeg_handle = NULL; \ + RtlLeaveCriticalSection(&init_jpeg_cs); \ + return NULL; \ + } + + LOAD_FUNCPTR(jpeg_CreateCompress); + LOAD_FUNCPTR(jpeg_CreateDecompress); + LOAD_FUNCPTR(jpeg_destroy_compress); + LOAD_FUNCPTR(jpeg_destroy_decompress); + LOAD_FUNCPTR(jpeg_finish_compress); + LOAD_FUNCPTR(jpeg_read_header); + LOAD_FUNCPTR(jpeg_read_scanlines); + LOAD_FUNCPTR(jpeg_resync_to_restart); + LOAD_FUNCPTR(jpeg_set_defaults); + LOAD_FUNCPTR(jpeg_start_compress); + LOAD_FUNCPTR(jpeg_start_decompress); + LOAD_FUNCPTR(jpeg_std_error); + LOAD_FUNCPTR(jpeg_write_scanlines); +#undef LOAD_FUNCPTR + } + result = libjpeg_handle; + + RtlLeaveCriticalSection(&init_jpeg_cs); + + return result; +} + +static void error_exit_fn(j_common_ptr cinfo) +{ + char message[JMSG_LENGTH_MAX]; + if (ERR_ON(jpeg)) + { + cinfo->err->format_message(cinfo, message); + ERR_(jpeg)("%s\n", message); + } + longjmp(*(jmp_buf*)cinfo->client_data, 1); +} + +static void emit_message_fn(j_common_ptr cinfo, int msg_level) +{ + char message[JMSG_LENGTH_MAX]; + + if (msg_level < 0 && ERR_ON(jpeg)) + { + cinfo->err->format_message(cinfo, message); + ERR_(jpeg)("%s\n", message); + } + else if (msg_level == 0 && WARN_ON(jpeg)) + { + cinfo->err->format_message(cinfo, message); + WARN_(jpeg)("%s\n", message); + } + else if (msg_level > 0 && TRACE_ON(jpeg)) + { + cinfo->err->format_message(cinfo, message); + TRACE_(jpeg)("%s\n", message); + } +} + +struct jpeg_decoder { + struct decoder decoder; + struct decoder_frame frame; + BOOL cinfo_initialized; + IStream *stream; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + struct jpeg_source_mgr source_mgr; + BYTE source_buffer[1024]; + UINT stride; + BYTE *image_data; +}; + +static inline struct jpeg_decoder *impl_from_decoder(struct decoder* iface) +{ + return CONTAINING_RECORD(iface, struct jpeg_decoder, decoder); +} + +static inline struct jpeg_decoder *decoder_from_decompress(j_decompress_ptr decompress) +{ + return CONTAINING_RECORD(decompress, struct jpeg_decoder, cinfo); +} + +static void CDECL jpeg_decoder_destroy(struct decoder* iface) +{ + struct jpeg_decoder *This = impl_from_decoder(iface); + + if (This->cinfo_initialized) pjpeg_destroy_decompress(&This->cinfo); + free(This->image_data); + RtlFreeHeap(GetProcessHeap(), 0, This); +} + +static void source_mgr_init_source(j_decompress_ptr cinfo) +{ +} + +static jpeg_boolean source_mgr_fill_input_buffer(j_decompress_ptr cinfo) +{ + struct jpeg_decoder *This = decoder_from_decompress(cinfo); + HRESULT hr; + ULONG bytesread; + + hr = stream_read(This->stream, This->source_buffer, 1024, &bytesread); + + if (FAILED(hr) || bytesread == 0) + { + return FALSE; + } + else + { + This->source_mgr.next_input_byte = This->source_buffer; + This->source_mgr.bytes_in_buffer = bytesread; + return TRUE; + } +} + +static void source_mgr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + struct jpeg_decoder *This = decoder_from_decompress(cinfo); + + if (num_bytes > This->source_mgr.bytes_in_buffer) + { + stream_seek(This->stream, num_bytes - This->source_mgr.bytes_in_buffer, STREAM_SEEK_CUR, NULL); + This->source_mgr.bytes_in_buffer = 0; + } + else if (num_bytes > 0) + { + This->source_mgr.next_input_byte += num_bytes; + This->source_mgr.bytes_in_buffer -= num_bytes; + } +} + +static void source_mgr_term_source(j_decompress_ptr cinfo) +{ +} + +static HRESULT CDECL jpeg_decoder_initialize(struct decoder* iface, IStream *stream, struct decoder_stat *st) +{ + struct jpeg_decoder *This = impl_from_decoder(iface); + int ret; + jmp_buf jmpbuf; + UINT data_size, i; + + if (This->cinfo_initialized) + return WINCODEC_ERR_WRONGSTATE; + + pjpeg_std_error(&This->jerr); + + This->jerr.error_exit = error_exit_fn; + This->jerr.emit_message = emit_message_fn; + + This->cinfo.err = &This->jerr; + + This->cinfo.client_data = jmpbuf; + + if (setjmp(jmpbuf)) + return E_FAIL; + + pjpeg_CreateDecompress(&This->cinfo, JPEG_LIB_VERSION, sizeof(struct jpeg_decompress_struct)); + + This->cinfo_initialized = TRUE; + + This->stream = stream; + + stream_seek(This->stream, 0, STREAM_SEEK_SET, NULL); + + This->source_mgr.bytes_in_buffer = 0; + This->source_mgr.init_source = source_mgr_init_source; + This->source_mgr.fill_input_buffer = source_mgr_fill_input_buffer; + This->source_mgr.skip_input_data = source_mgr_skip_input_data; + This->source_mgr.resync_to_restart = pjpeg_resync_to_restart; + This->source_mgr.term_source = source_mgr_term_source; + + This->cinfo.src = &This->source_mgr; + + ret = pjpeg_read_header(&This->cinfo, TRUE); + + if (ret != JPEG_HEADER_OK) { + WARN("Jpeg image in stream has bad format, read header returned %d.\n",ret); + return E_FAIL; + } + + switch (This->cinfo.jpeg_color_space) + { + case JCS_GRAYSCALE: + This->cinfo.out_color_space = JCS_GRAYSCALE; + This->frame.bpp = 8; + This->frame.pixel_format = GUID_WICPixelFormat8bppGray; + break; + case JCS_RGB: + case JCS_YCbCr: + This->cinfo.out_color_space = JCS_RGB; + This->frame.bpp = 24; + This->frame.pixel_format = GUID_WICPixelFormat24bppBGR; + break; + case JCS_CMYK: + case JCS_YCCK: + This->cinfo.out_color_space = JCS_CMYK; + This->frame.bpp = 32; + This->frame.pixel_format = GUID_WICPixelFormat32bppCMYK; + break; + default: + ERR("Unknown JPEG color space %i\n", This->cinfo.jpeg_color_space); + return E_FAIL; + } + + if (!pjpeg_start_decompress(&This->cinfo)) + { + ERR("jpeg_start_decompress failed\n"); + return E_FAIL; + } + + This->frame.width = This->cinfo.output_width; + This->frame.height = This->cinfo.output_height; + + switch (This->cinfo.density_unit) + { + case 2: /* pixels per centimeter */ + This->frame.dpix = This->cinfo.X_density * 2.54; + This->frame.dpiy = This->cinfo.Y_density * 2.54; + break; + + case 1: /* pixels per inch */ + This->frame.dpix = This->cinfo.X_density; + This->frame.dpiy = This->cinfo.Y_density; + break; + + case 0: /* unknown */ + default: + This->frame.dpix = This->frame.dpiy = 96.0; + break; + } + + This->frame.num_color_contexts = 0; + This->frame.num_colors = 0; + + This->stride = (This->frame.bpp * This->cinfo.output_width + 7) / 8; + data_size = This->stride * This->cinfo.output_height; + + This->image_data = malloc(data_size); + if (!This->image_data) + return E_OUTOFMEMORY; + + while (This->cinfo.output_scanline < This->cinfo.output_height) + { + UINT first_scanline = This->cinfo.output_scanline; + UINT max_rows; + JSAMPROW out_rows[4]; + JDIMENSION ret; + + max_rows = min(This->cinfo.output_height-first_scanline, 4); + for (i=0; iimage_data + This->stride * (first_scanline+i); + + ret = pjpeg_read_scanlines(&This->cinfo, out_rows, max_rows); + if (ret == 0) + { + ERR("read_scanlines failed\n"); + return E_FAIL; + } + } + + if (This->frame.bpp == 24) + { + /* libjpeg gives us RGB data and we want BGR, so byteswap the data */ + reverse_bgr8(3, This->image_data, + This->cinfo.output_width, This->cinfo.output_height, + This->stride); + } + + if (This->cinfo.out_color_space == JCS_CMYK && This->cinfo.saw_Adobe_marker) + { + /* Adobe JPEG's have inverted CMYK data. */ + for (i=0; iimage_data[i] ^= 0xff; + } + + st->frame_count = 1; + st->flags = WICBitmapDecoderCapabilityCanDecodeAllImages | + WICBitmapDecoderCapabilityCanDecodeSomeImages | + WICBitmapDecoderCapabilityCanEnumerateMetadata | + DECODER_FLAGS_UNSUPPORTED_COLOR_CONTEXT; + return S_OK; +} + +static HRESULT CDECL jpeg_decoder_get_frame_info(struct decoder* iface, UINT frame, struct decoder_frame *info) +{ + struct jpeg_decoder *This = impl_from_decoder(iface); + *info = This->frame; + return S_OK; +} + +static HRESULT CDECL jpeg_decoder_copy_pixels(struct decoder* iface, UINT frame, + const WICRect *prc, UINT stride, UINT buffersize, BYTE *buffer) +{ + struct jpeg_decoder *This = impl_from_decoder(iface); + return copy_pixels(This->frame.bpp, This->image_data, + This->frame.width, This->frame.height, This->stride, + prc, stride, buffersize, buffer); +} + +static HRESULT CDECL jpeg_decoder_get_metadata_blocks(struct decoder* iface, UINT frame, + UINT *count, struct decoder_block **blocks) +{ + FIXME("stub\n"); + *count = 0; + *blocks = NULL; + return S_OK; +} + +static HRESULT CDECL jpeg_decoder_get_color_context(struct decoder* This, UINT frame, UINT num, + BYTE **data, DWORD *datasize) +{ + /* This should never be called because we report 0 color contexts and the unsupported flag. */ + FIXME("stub\n"); + return E_NOTIMPL; +} + +static const struct decoder_funcs jpeg_decoder_vtable = { + jpeg_decoder_initialize, + jpeg_decoder_get_frame_info, + jpeg_decoder_copy_pixels, + jpeg_decoder_get_metadata_blocks, + jpeg_decoder_get_color_context, + jpeg_decoder_destroy +}; + +HRESULT CDECL jpeg_decoder_create(struct decoder_info *info, struct decoder **result) +{ + struct jpeg_decoder *This; + + if (!load_libjpeg()) + { + ERR("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG); + return E_FAIL; + } + + This = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(struct jpeg_decoder)); + if (!This) return E_OUTOFMEMORY; + + This->decoder.vtable = &jpeg_decoder_vtable; + This->cinfo_initialized = FALSE; + This->stream = NULL; + This->image_data = NULL; + *result = &This->decoder; + + info->container_format = GUID_ContainerFormatJpeg; + info->block_format = GUID_ContainerFormatJpeg; + info->clsid = CLSID_WICJpegDecoder; + + return S_OK; +} + +#else /* !defined(SONAME_LIBJPEG) */ + +HRESULT CDECL jpeg_decoder_create(struct decoder_info *info, struct decoder **result) +{ + ERR("Trying to load JPEG picture, but JPEG support is not compiled in.\n"); + return E_FAIL; +} + +#endif diff --git a/dlls/windowscodecs/unix_lib.c b/dlls/windowscodecs/unix_lib.c index ca2b38ef990..dc8549e1eb8 100644 --- a/dlls/windowscodecs/unix_lib.c +++ b/dlls/windowscodecs/unix_lib.c @@ -70,6 +70,9 @@ HRESULT CDECL decoder_create(const CLSID *decoder_clsid, struct decoder_info *in if (IsEqualGUID(decoder_clsid, &CLSID_WICTiffDecoder)) return tiff_decoder_create(info, result); + if (IsEqualGUID(decoder_clsid, &CLSID_WICJpegDecoder)) + return jpeg_decoder_create(info, result); + return E_NOTIMPL; } diff --git a/dlls/windowscodecs/wincodecs_private.h b/dlls/windowscodecs/wincodecs_private.h index 3d7a46f0f59..d60b281faef 100644 --- a/dlls/windowscodecs/wincodecs_private.h +++ b/dlls/windowscodecs/wincodecs_private.h @@ -262,6 +262,7 @@ struct decoder_info }; #define DECODER_FLAGS_CAPABILITY_MASK 0x1f +#define DECODER_FLAGS_UNSUPPORTED_COLOR_CONTEXT 0x80000000 struct decoder_stat { @@ -333,6 +334,7 @@ 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); +HRESULT CDECL jpeg_decoder_create(struct decoder_info *info, struct decoder **result); struct unix_funcs {