Signed-off-by: Esme Povirk esme@codeweavers.com --- dlls/windowscodecs/Makefile.in | 1 - dlls/windowscodecs/encoder.c | 14 +- dlls/windowscodecs/jpegformat.c | 920 ------------------------- dlls/windowscodecs/libjpeg.c | 313 +++++++++ dlls/windowscodecs/main.c | 28 + dlls/windowscodecs/unix_lib.c | 3 + dlls/windowscodecs/wincodecs_private.h | 7 + 7 files changed, 364 insertions(+), 922 deletions(-) delete mode 100644 dlls/windowscodecs/jpegformat.c
diff --git a/dlls/windowscodecs/Makefile.in b/dlls/windowscodecs/Makefile.in index d7f9336916b..2ab89eac84c 100644 --- a/dlls/windowscodecs/Makefile.in +++ b/dlls/windowscodecs/Makefile.in @@ -22,7 +22,6 @@ C_SRCS = \ icoformat.c \ imgfactory.c \ info.c \ - jpegformat.c \ libjpeg.c \ libpng.c \ libtiff.c \ diff --git a/dlls/windowscodecs/encoder.c b/dlls/windowscodecs/encoder.c index bb673b13076..b17895a6c9c 100644 --- a/dlls/windowscodecs/encoder.c +++ b/dlls/windowscodecs/encoder.c @@ -38,12 +38,24 @@ static const WCHAR wszPngInterlaceOption[] = {'I','n','t','e','r','l','a','c','e static const WCHAR wszPngFilterOption[] = {'F','i','l','t','e','r','O','p','t','i','o','n',0}; static const WCHAR wszTiffCompressionMethod[] = {'T','i','f','f','C','o','m','p','r','e','s','s','i','o','n','M','e','t','h','o','d',0}; static const WCHAR wszCompressionQuality[] = {'C','o','m','p','r','e','s','s','i','o','n','Q','u','a','l','i','t','y',0}; +static const WCHAR wszImageQuality[] = {'I','m','a','g','e','Q','u','a','l','i','t','y',0}; +static const WCHAR wszBitmapTransform[] = {'B','i','t','m','a','p','T','r','a','n','s','f','o','r','m',0}; +static const WCHAR wszLuminance[] = {'L','u','m','i','n','a','n','c','e',0}; +static const WCHAR wszChrominance[] = {'C','h','r','o','m','i','n','a','n','c','e',0}; +static const WCHAR wszJpegYCrCbSubsampling[] = {'J','p','e','g','Y','C','r','C','b','S','u','b','s','a','m','p','l','i','n','g',0}; +static const WCHAR wszSuppressApp0[] = {'S','u','p','p','r','e','s','s','A','p','p','0',0};
static const PROPBAG2 encoder_option_properties[ENCODER_OPTION_END] = { { PROPBAG2_TYPE_DATA, VT_BOOL, 0, 0, (LPOLESTR)wszPngInterlaceOption }, { PROPBAG2_TYPE_DATA, VT_UI1, 0, 0, (LPOLESTR)wszPngFilterOption }, { PROPBAG2_TYPE_DATA, VT_UI1, 0, 0, (LPOLESTR)wszTiffCompressionMethod }, - { PROPBAG2_TYPE_DATA, VT_R4, 0, 0, (LPOLESTR)wszCompressionQuality } + { PROPBAG2_TYPE_DATA, VT_R4, 0, 0, (LPOLESTR)wszCompressionQuality }, + { PROPBAG2_TYPE_DATA, VT_R4, 0, 0, (LPOLESTR)wszImageQuality }, + { PROPBAG2_TYPE_DATA, VT_UI1, 0, 0, (LPOLESTR)wszBitmapTransform }, + { PROPBAG2_TYPE_DATA, VT_I4 | VT_ARRAY, 0, 0, (LPOLESTR)wszLuminance }, + { PROPBAG2_TYPE_DATA, VT_I4 | VT_ARRAY, 0, 0, (LPOLESTR)wszChrominance }, + { PROPBAG2_TYPE_DATA, VT_UI1, 0, 0, (LPOLESTR)wszJpegYCrCbSubsampling }, + { PROPBAG2_TYPE_DATA, VT_BOOL, 0, 0, (LPOLESTR)wszSuppressApp0 } };
typedef struct CommonEncoder { diff --git a/dlls/windowscodecs/jpegformat.c b/dlls/windowscodecs/jpegformat.c deleted file mode 100644 index 5be35a1ccb4..00000000000 --- a/dlls/windowscodecs/jpegformat.c +++ /dev/null @@ -1,920 +0,0 @@ -/* - * 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 - */ - -#include "config.h" -#include "wine/port.h" - -#ifdef HAVE_UNISTD_H -# include <unistd.h> -#endif -#include <stdarg.h> -#include <stdio.h> -#include <string.h> -#include <setjmp.h> - -#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 <jpeglib.h> -#undef HAVE_STDLIB_H -#define HAVE_STDLIB_H 1 -#undef UINT8 -#undef UINT16 -#undef boolean -#endif - -#define COBJMACROS - -#include "windef.h" -#include "winbase.h" -#include "objbase.h" - -#include "wincodecs_private.h" - -#include "wine/heap.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(wincodecs); - -#ifdef SONAME_LIBJPEG -WINE_DECLARE_DEBUG_CHANNEL(jpeg); - -static void *libjpeg_handle; - -static const WCHAR wszImageQuality[] = {'I','m','a','g','e','Q','u','a','l','i','t','y',0}; -static const WCHAR wszBitmapTransform[] = {'B','i','t','m','a','p','T','r','a','n','s','f','o','r','m',0}; -static const WCHAR wszLuminance[] = {'L','u','m','i','n','a','n','c','e',0}; -static const WCHAR wszChrominance[] = {'C','h','r','o','m','i','n','a','n','c','e',0}; -static const WCHAR wszJpegYCrCbSubsampling[] = {'J','p','e','g','Y','C','r','C','b','S','u','b','s','a','m','p','l','i','n','g',0}; -static const WCHAR wszSuppressApp0[] = {'S','u','p','p','r','e','s','s','A','p','p','0',0}; - -#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) -{ - if((libjpeg_handle = dlopen(SONAME_LIBJPEG, RTLD_NOW)) != NULL) { - -#define LOAD_FUNCPTR(f) \ - if((p##f = dlsym(libjpeg_handle, #f)) == NULL) { \ - libjpeg_handle = NULL; \ - 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 - } - return libjpeg_handle; -} - -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); - } -} - -typedef struct jpeg_compress_format { - const WICPixelFormatGUID *guid; - int bpp; - int num_components; - J_COLOR_SPACE color_space; - int swap_rgb; -} jpeg_compress_format; - -static const jpeg_compress_format compress_formats[] = { - { &GUID_WICPixelFormat24bppBGR, 24, 3, JCS_RGB, 1 }, - { &GUID_WICPixelFormat32bppCMYK, 32, 4, JCS_CMYK }, - { &GUID_WICPixelFormat8bppGray, 8, 1, JCS_GRAYSCALE }, - { 0 } -}; - -typedef struct JpegEncoder { - IWICBitmapEncoder IWICBitmapEncoder_iface; - IWICBitmapFrameEncode IWICBitmapFrameEncode_iface; - LONG ref; - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - struct jpeg_destination_mgr dest_mgr; - BOOL initialized; - int frame_count; - BOOL frame_initialized; - BOOL started_compress; - int lines_written; - BOOL frame_committed; - BOOL committed; - UINT width, height; - double xres, yres; - const jpeg_compress_format *format; - IStream *stream; - WICColor palette[256]; - UINT colors; - CRITICAL_SECTION lock; - BYTE dest_buffer[1024]; -} JpegEncoder; - -static inline JpegEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface) -{ - return CONTAINING_RECORD(iface, JpegEncoder, IWICBitmapEncoder_iface); -} - -static inline JpegEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface) -{ - return CONTAINING_RECORD(iface, JpegEncoder, IWICBitmapFrameEncode_iface); -} - -static inline JpegEncoder *encoder_from_compress(j_compress_ptr compress) -{ - return CONTAINING_RECORD(compress, JpegEncoder, cinfo); -} - -static void dest_mgr_init_destination(j_compress_ptr cinfo) -{ - JpegEncoder *This = encoder_from_compress(cinfo); - - This->dest_mgr.next_output_byte = This->dest_buffer; - This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer); -} - -static jpeg_boolean dest_mgr_empty_output_buffer(j_compress_ptr cinfo) -{ - JpegEncoder *This = encoder_from_compress(cinfo); - HRESULT hr; - ULONG byteswritten; - - hr = IStream_Write(This->stream, This->dest_buffer, - sizeof(This->dest_buffer), &byteswritten); - - if (hr != S_OK || byteswritten == 0) - { - ERR("Failed writing data, hr=%x\n", hr); - return FALSE; - } - - This->dest_mgr.next_output_byte = This->dest_buffer; - This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer); - return TRUE; -} - -static void dest_mgr_term_destination(j_compress_ptr cinfo) -{ - JpegEncoder *This = encoder_from_compress(cinfo); - ULONG byteswritten; - HRESULT hr; - - if (This->dest_mgr.free_in_buffer != sizeof(This->dest_buffer)) - { - hr = IStream_Write(This->stream, This->dest_buffer, - sizeof(This->dest_buffer) - This->dest_mgr.free_in_buffer, &byteswritten); - - if (hr != S_OK || byteswritten == 0) - ERR("Failed writing data, hr=%x\n", hr); - } -} - -static HRESULT WINAPI JpegEncoder_Frame_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid, - void **ppv) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); - - if (!ppv) return E_INVALIDARG; - - if (IsEqualIID(&IID_IUnknown, iid) || - IsEqualIID(&IID_IWICBitmapFrameEncode, iid)) - { - *ppv = &This->IWICBitmapFrameEncode_iface; - } - else - { - *ppv = NULL; - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown*)*ppv); - return S_OK; -} - -static ULONG WINAPI JpegEncoder_Frame_AddRef(IWICBitmapFrameEncode *iface) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface); -} - -static ULONG WINAPI JpegEncoder_Frame_Release(IWICBitmapFrameEncode *iface) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface); -} - -static HRESULT WINAPI JpegEncoder_Frame_Initialize(IWICBitmapFrameEncode *iface, - IPropertyBag2 *pIEncoderOptions) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - TRACE("(%p,%p)\n", iface, pIEncoderOptions); - - EnterCriticalSection(&This->lock); - - if (This->frame_initialized) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - This->frame_initialized = TRUE; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI JpegEncoder_Frame_SetSize(IWICBitmapFrameEncode *iface, - UINT uiWidth, UINT uiHeight) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight); - - EnterCriticalSection(&This->lock); - - if (!This->frame_initialized || This->started_compress) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - This->width = uiWidth; - This->height = uiHeight; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI JpegEncoder_Frame_SetResolution(IWICBitmapFrameEncode *iface, - double dpiX, double dpiY) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY); - - EnterCriticalSection(&This->lock); - - if (!This->frame_initialized || This->started_compress) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - This->xres = dpiX; - This->yres = dpiY; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI JpegEncoder_Frame_SetPixelFormat(IWICBitmapFrameEncode *iface, - WICPixelFormatGUID *pPixelFormat) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - int i; - TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat)); - - EnterCriticalSection(&This->lock); - - if (!This->frame_initialized || This->started_compress) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - for (i=0; compress_formats[i].guid; i++) - { - if (memcmp(compress_formats[i].guid, pPixelFormat, sizeof(GUID)) == 0) - break; - } - - if (!compress_formats[i].guid) i = 0; - - This->format = &compress_formats[i]; - memcpy(pPixelFormat, This->format->guid, sizeof(GUID)); - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI JpegEncoder_Frame_SetColorContexts(IWICBitmapFrameEncode *iface, - UINT cCount, IWICColorContext **ppIColorContext) -{ - FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext); - return E_NOTIMPL; -} - -static HRESULT WINAPI JpegEncoder_Frame_SetPalette(IWICBitmapFrameEncode *iface, - IWICPalette *palette) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - HRESULT hr; - - TRACE("(%p,%p)\n", iface, palette); - - if (!palette) return E_INVALIDARG; - - EnterCriticalSection(&This->lock); - - if (This->frame_initialized) - hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors); - else - hr = WINCODEC_ERR_NOTINITIALIZED; - - LeaveCriticalSection(&This->lock); - return hr; -} - -static HRESULT WINAPI JpegEncoder_Frame_SetThumbnail(IWICBitmapFrameEncode *iface, - IWICBitmapSource *pIThumbnail) -{ - FIXME("(%p,%p): stub\n", iface, pIThumbnail); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI JpegEncoder_Frame_WritePixels(IWICBitmapFrameEncode *iface, - UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - jmp_buf jmpbuf; - BYTE *swapped_data = NULL, *current_row; - UINT line; - int row_size; - TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels); - - EnterCriticalSection(&This->lock); - - if (!This->frame_initialized || !This->width || !This->height || !This->format) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - if (lineCount == 0 || lineCount + This->lines_written > This->height) - { - LeaveCriticalSection(&This->lock); - return E_INVALIDARG; - } - - /* set up setjmp/longjmp error handling */ - if (setjmp(jmpbuf)) - { - LeaveCriticalSection(&This->lock); - HeapFree(GetProcessHeap(), 0, swapped_data); - return E_FAIL; - } - This->cinfo.client_data = jmpbuf; - - if (!This->started_compress) - { - This->cinfo.image_width = This->width; - This->cinfo.image_height = This->height; - This->cinfo.input_components = This->format->num_components; - This->cinfo.in_color_space = This->format->color_space; - - pjpeg_set_defaults(&This->cinfo); - - if (This->xres != 0.0 && This->yres != 0.0) - { - This->cinfo.density_unit = 1; /* dots per inch */ - This->cinfo.X_density = This->xres; - This->cinfo.Y_density = This->yres; - } - - pjpeg_start_compress(&This->cinfo, TRUE); - - This->started_compress = TRUE; - } - - row_size = This->format->bpp / 8 * This->width; - - if (This->format->swap_rgb) - { - swapped_data = HeapAlloc(GetProcessHeap(), 0, row_size); - if (!swapped_data) - { - LeaveCriticalSection(&This->lock); - return E_OUTOFMEMORY; - } - } - - for (line=0; line < lineCount; line++) - { - if (This->format->swap_rgb) - { - UINT x; - - memcpy(swapped_data, pbPixels + (cbStride * line), row_size); - - for (x=0; x < This->width; x++) - { - BYTE b; - - b = swapped_data[x*3]; - swapped_data[x*3] = swapped_data[x*3+2]; - swapped_data[x*3+2] = b; - } - - current_row = swapped_data; - } - else - current_row = pbPixels + (cbStride * line); - - if (!pjpeg_write_scanlines(&This->cinfo, ¤t_row, 1)) - { - ERR("failed writing scanlines\n"); - LeaveCriticalSection(&This->lock); - HeapFree(GetProcessHeap(), 0, swapped_data); - return E_FAIL; - } - - This->lines_written++; - } - - LeaveCriticalSection(&This->lock); - HeapFree(GetProcessHeap(), 0, swapped_data); - - return S_OK; -} - -static HRESULT WINAPI JpegEncoder_Frame_WriteSource(IWICBitmapFrameEncode *iface, - IWICBitmapSource *pIBitmapSource, WICRect *prc) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - HRESULT hr; - TRACE("(%p,%p,%s)\n", iface, pIBitmapSource, debug_wic_rect(prc)); - - if (!This->frame_initialized) - return WINCODEC_ERR_WRONGSTATE; - - hr = configure_write_source(iface, pIBitmapSource, prc, - This->format ? This->format->guid : NULL, This->width, This->height, - This->xres, This->yres); - - if (SUCCEEDED(hr)) - { - hr = write_source(iface, pIBitmapSource, prc, - This->format->guid, This->format->bpp, FALSE, - This->width, This->height); - } - - return hr; -} - -static HRESULT WINAPI JpegEncoder_Frame_Commit(IWICBitmapFrameEncode *iface) -{ - JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface); - jmp_buf jmpbuf; - TRACE("(%p)\n", iface); - - EnterCriticalSection(&This->lock); - - if (!This->started_compress || This->lines_written != This->height || This->frame_committed) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - /* set up setjmp/longjmp error handling */ - if (setjmp(jmpbuf)) - { - LeaveCriticalSection(&This->lock); - return E_FAIL; - } - This->cinfo.client_data = jmpbuf; - - pjpeg_finish_compress(&This->cinfo); - - This->frame_committed = TRUE; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI JpegEncoder_Frame_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface, - IWICMetadataQueryWriter **ppIMetadataQueryWriter) -{ - FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter); - return E_NOTIMPL; -} - -static const IWICBitmapFrameEncodeVtbl JpegEncoder_FrameVtbl = { - JpegEncoder_Frame_QueryInterface, - JpegEncoder_Frame_AddRef, - JpegEncoder_Frame_Release, - JpegEncoder_Frame_Initialize, - JpegEncoder_Frame_SetSize, - JpegEncoder_Frame_SetResolution, - JpegEncoder_Frame_SetPixelFormat, - JpegEncoder_Frame_SetColorContexts, - JpegEncoder_Frame_SetPalette, - JpegEncoder_Frame_SetThumbnail, - JpegEncoder_Frame_WritePixels, - JpegEncoder_Frame_WriteSource, - JpegEncoder_Frame_Commit, - JpegEncoder_Frame_GetMetadataQueryWriter -}; - -static HRESULT WINAPI JpegEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid, - void **ppv) -{ - JpegEncoder *This = impl_from_IWICBitmapEncoder(iface); - TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv); - - if (!ppv) return E_INVALIDARG; - - if (IsEqualIID(&IID_IUnknown, iid) || - IsEqualIID(&IID_IWICBitmapEncoder, iid)) - { - *ppv = &This->IWICBitmapEncoder_iface; - } - else - { - *ppv = NULL; - return E_NOINTERFACE; - } - - IUnknown_AddRef((IUnknown*)*ppv); - return S_OK; -} - -static ULONG WINAPI JpegEncoder_AddRef(IWICBitmapEncoder *iface) -{ - JpegEncoder *This = impl_from_IWICBitmapEncoder(iface); - ULONG ref = InterlockedIncrement(&This->ref); - - TRACE("(%p) refcount=%u\n", iface, ref); - - return ref; -} - -static ULONG WINAPI JpegEncoder_Release(IWICBitmapEncoder *iface) -{ - JpegEncoder *This = impl_from_IWICBitmapEncoder(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->initialized) pjpeg_destroy_compress(&This->cinfo); - if (This->stream) IStream_Release(This->stream); - HeapFree(GetProcessHeap(), 0, This); - } - - return ref; -} - -static HRESULT WINAPI JpegEncoder_Initialize(IWICBitmapEncoder *iface, - IStream *pIStream, WICBitmapEncoderCacheOption cacheOption) -{ - JpegEncoder *This = impl_from_IWICBitmapEncoder(iface); - jmp_buf jmpbuf; - - TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption); - - EnterCriticalSection(&This->lock); - - if (This->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_CreateCompress(&This->cinfo, JPEG_LIB_VERSION, sizeof(struct jpeg_compress_struct)); - - This->stream = pIStream; - IStream_AddRef(pIStream); - - This->dest_mgr.next_output_byte = This->dest_buffer; - This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer); - - This->dest_mgr.init_destination = dest_mgr_init_destination; - This->dest_mgr.empty_output_buffer = dest_mgr_empty_output_buffer; - This->dest_mgr.term_destination = dest_mgr_term_destination; - - This->cinfo.dest = &This->dest_mgr; - - This->initialized = TRUE; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI JpegEncoder_GetContainerFormat(IWICBitmapEncoder *iface, GUID *format) -{ - TRACE("(%p,%p)\n", iface, format); - - if (!format) - return E_INVALIDARG; - - memcpy(format, &GUID_ContainerFormatJpeg, sizeof(*format)); - return S_OK; -} - -static HRESULT WINAPI JpegEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info) -{ - IWICComponentInfo *comp_info; - HRESULT hr; - - TRACE("%p,%p\n", iface, info); - - if (!info) return E_INVALIDARG; - - hr = CreateComponentInfo(&CLSID_WICJpegEncoder, &comp_info); - if (hr == S_OK) - { - hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info); - IWICComponentInfo_Release(comp_info); - } - return hr; -} - -static HRESULT WINAPI JpegEncoder_SetColorContexts(IWICBitmapEncoder *iface, - UINT cCount, IWICColorContext **ppIColorContext) -{ - FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext); - return E_NOTIMPL; -} - -static HRESULT WINAPI JpegEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette) -{ - JpegEncoder *This = impl_from_IWICBitmapEncoder(iface); - HRESULT hr; - - TRACE("(%p,%p)\n", iface, pIPalette); - - EnterCriticalSection(&This->lock); - - hr = This->initialized ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED; - - LeaveCriticalSection(&This->lock); - - return hr; -} - -static HRESULT WINAPI JpegEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail) -{ - TRACE("(%p,%p)\n", iface, pIThumbnail); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI JpegEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview) -{ - TRACE("(%p,%p)\n", iface, pIPreview); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; -} - -static HRESULT WINAPI JpegEncoder_CreateNewFrame(IWICBitmapEncoder *iface, - IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions) -{ - JpegEncoder *This = impl_from_IWICBitmapEncoder(iface); - HRESULT hr; - static const PROPBAG2 opts[6] = - { - { PROPBAG2_TYPE_DATA, VT_R4, 0, 0, (LPOLESTR)wszImageQuality }, - { PROPBAG2_TYPE_DATA, VT_UI1, 0, 0, (LPOLESTR)wszBitmapTransform }, - { PROPBAG2_TYPE_DATA, VT_I4 | VT_ARRAY, 0, 0, (LPOLESTR)wszLuminance }, - { PROPBAG2_TYPE_DATA, VT_I4 | VT_ARRAY, 0, 0, (LPOLESTR)wszChrominance }, - { PROPBAG2_TYPE_DATA, VT_UI1, 0, 0, (LPOLESTR)wszJpegYCrCbSubsampling }, - { PROPBAG2_TYPE_DATA, VT_BOOL, 0, 0, (LPOLESTR)wszSuppressApp0 }, - }; - - TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions); - - EnterCriticalSection(&This->lock); - - if (This->frame_count != 0) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_UNSUPPORTEDOPERATION; - } - - if (!This->initialized) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_NOTINITIALIZED; - } - - if (ppIEncoderOptions) - { - hr = CreatePropertyBag2(opts, ARRAY_SIZE(opts), ppIEncoderOptions); - if (FAILED(hr)) - { - LeaveCriticalSection(&This->lock); - return hr; - } - } - - This->frame_count = 1; - - LeaveCriticalSection(&This->lock); - - IWICBitmapEncoder_AddRef(iface); - *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface; - - return S_OK; -} - -static HRESULT WINAPI JpegEncoder_Commit(IWICBitmapEncoder *iface) -{ - JpegEncoder *This = impl_from_IWICBitmapEncoder(iface); - TRACE("(%p)\n", iface); - - EnterCriticalSection(&This->lock); - - if (!This->frame_committed || This->committed) - { - LeaveCriticalSection(&This->lock); - return WINCODEC_ERR_WRONGSTATE; - } - - This->committed = TRUE; - - LeaveCriticalSection(&This->lock); - - return S_OK; -} - -static HRESULT WINAPI JpegEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface, - IWICMetadataQueryWriter **ppIMetadataQueryWriter) -{ - FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter); - return E_NOTIMPL; -} - -static const IWICBitmapEncoderVtbl JpegEncoder_Vtbl = { - JpegEncoder_QueryInterface, - JpegEncoder_AddRef, - JpegEncoder_Release, - JpegEncoder_Initialize, - JpegEncoder_GetContainerFormat, - JpegEncoder_GetEncoderInfo, - JpegEncoder_SetColorContexts, - JpegEncoder_SetPalette, - JpegEncoder_SetThumbnail, - JpegEncoder_SetPreview, - JpegEncoder_CreateNewFrame, - JpegEncoder_Commit, - JpegEncoder_GetMetadataQueryWriter -}; - -HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv) -{ - JpegEncoder *This; - HRESULT ret; - - TRACE("(%s,%p)\n", debugstr_guid(iid), ppv); - - *ppv = NULL; - - if (!libjpeg_handle && !load_libjpeg()) - { - ERR("Failed writing JPEG because unable to find %s\n",SONAME_LIBJPEG); - return E_FAIL; - } - - This = HeapAlloc(GetProcessHeap(), 0, sizeof(JpegEncoder)); - if (!This) return E_OUTOFMEMORY; - - This->IWICBitmapEncoder_iface.lpVtbl = &JpegEncoder_Vtbl; - This->IWICBitmapFrameEncode_iface.lpVtbl = &JpegEncoder_FrameVtbl; - This->ref = 1; - This->initialized = FALSE; - This->frame_count = 0; - This->frame_initialized = FALSE; - This->started_compress = FALSE; - This->lines_written = 0; - This->frame_committed = FALSE; - This->committed = FALSE; - This->width = This->height = 0; - This->xres = This->yres = 0.0; - This->format = NULL; - This->stream = NULL; - This->colors = 0; - InitializeCriticalSection(&This->lock); - This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JpegEncoder.lock"); - - ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv); - IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface); - - return ret; -} - -#else /* !defined(SONAME_LIBJPEG) */ - -HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv) -{ - ERR("Trying to save JPEG picture, but JPEG support is not compiled in.\n"); - return E_FAIL; -} - -#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 index 58ca58e93b4..84e4df5e007 100644 --- a/dlls/windowscodecs/libjpeg.c +++ b/dlls/windowscodecs/libjpeg.c @@ -453,6 +453,313 @@ HRESULT CDECL jpeg_decoder_create(struct decoder_info *info, struct decoder **re return S_OK; }
+typedef struct jpeg_compress_format { + const WICPixelFormatGUID *guid; + int bpp; + int num_components; + J_COLOR_SPACE color_space; + int swap_rgb; +} jpeg_compress_format; + +static const jpeg_compress_format compress_formats[] = { + { &GUID_WICPixelFormat24bppBGR, 24, 3, JCS_RGB, 1 }, + { &GUID_WICPixelFormat32bppCMYK, 32, 4, JCS_CMYK }, + { &GUID_WICPixelFormat8bppGray, 8, 1, JCS_GRAYSCALE }, + { 0 } +}; + +struct jpeg_encoder +{ + struct encoder encoder; + IStream *stream; + BOOL cinfo_initialized; + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + struct jpeg_destination_mgr dest_mgr; + struct encoder_frame encoder_frame; + const jpeg_compress_format *format; + BYTE dest_buffer[1024]; +}; + +static inline struct jpeg_encoder *impl_from_encoder(struct encoder* iface) +{ + return CONTAINING_RECORD(iface, struct jpeg_encoder, encoder); +} + +static inline struct jpeg_encoder *encoder_from_compress(j_compress_ptr compress) +{ + return CONTAINING_RECORD(compress, struct jpeg_encoder, cinfo); +} + +static void dest_mgr_init_destination(j_compress_ptr cinfo) +{ + struct jpeg_encoder *This = encoder_from_compress(cinfo); + + This->dest_mgr.next_output_byte = This->dest_buffer; + This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer); +} + +static jpeg_boolean dest_mgr_empty_output_buffer(j_compress_ptr cinfo) +{ + struct jpeg_encoder *This = encoder_from_compress(cinfo); + HRESULT hr; + ULONG byteswritten; + + hr = stream_write(This->stream, This->dest_buffer, + sizeof(This->dest_buffer), &byteswritten); + + if (hr != S_OK || byteswritten == 0) + { + ERR("Failed writing data, hr=%x\n", hr); + return FALSE; + } + + This->dest_mgr.next_output_byte = This->dest_buffer; + This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer); + return TRUE; +} + +static void dest_mgr_term_destination(j_compress_ptr cinfo) +{ + struct jpeg_encoder *This = encoder_from_compress(cinfo); + ULONG byteswritten; + HRESULT hr; + + if (This->dest_mgr.free_in_buffer != sizeof(This->dest_buffer)) + { + hr = stream_write(This->stream, This->dest_buffer, + sizeof(This->dest_buffer) - This->dest_mgr.free_in_buffer, &byteswritten); + + if (hr != S_OK || byteswritten == 0) + ERR("Failed writing data, hr=%x\n", hr); + } +} + +HRESULT CDECL jpeg_encoder_initialize(struct encoder* iface, IStream *stream) +{ + struct jpeg_encoder *This = impl_from_encoder(iface); + jmp_buf jmpbuf; + + 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_CreateCompress(&This->cinfo, JPEG_LIB_VERSION, sizeof(struct jpeg_compress_struct)); + + This->stream = stream; + + This->dest_mgr.next_output_byte = This->dest_buffer; + This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer); + + This->dest_mgr.init_destination = dest_mgr_init_destination; + This->dest_mgr.empty_output_buffer = dest_mgr_empty_output_buffer; + This->dest_mgr.term_destination = dest_mgr_term_destination; + + This->cinfo.dest = &This->dest_mgr; + + This->cinfo_initialized = TRUE; + + return S_OK; +} + +HRESULT CDECL jpeg_encoder_get_supported_format(struct encoder* iface, GUID *pixel_format, + DWORD *bpp, BOOL *indexed) +{ + int i; + + for (i=0; compress_formats[i].guid; i++) + { + if (memcmp(compress_formats[i].guid, pixel_format, sizeof(GUID)) == 0) + break; + } + + if (!compress_formats[i].guid) i = 0; + + *pixel_format = *compress_formats[i].guid; + *bpp = compress_formats[i].bpp; + *indexed = FALSE; + + return S_OK; +} + +HRESULT CDECL jpeg_encoder_create_frame(struct encoder* iface, const struct encoder_frame *frame) +{ + struct jpeg_encoder *This = impl_from_encoder(iface); + jmp_buf jmpbuf; + int i; + + This->encoder_frame = *frame; + + if (setjmp(jmpbuf)) + return E_FAIL; + + This->cinfo.client_data = jmpbuf; + + for (i=0; compress_formats[i].guid; i++) + { + if (memcmp(compress_formats[i].guid, &frame->pixel_format, sizeof(GUID)) == 0) + break; + } + This->format = &compress_formats[i]; + + This->cinfo.image_width = frame->width; + This->cinfo.image_height = frame->height; + This->cinfo.input_components = This->format->num_components; + This->cinfo.in_color_space = This->format->color_space; + + pjpeg_set_defaults(&This->cinfo); + + if (frame->dpix != 0.0 && frame->dpiy != 0.0) + { + This->cinfo.density_unit = 1; /* dots per inch */ + This->cinfo.X_density = frame->dpix; + This->cinfo.Y_density = frame->dpiy; + } + + pjpeg_start_compress(&This->cinfo, TRUE); + + return S_OK; +} + +HRESULT CDECL jpeg_encoder_write_lines(struct encoder* iface, BYTE *data, + DWORD line_count, DWORD stride) +{ + struct jpeg_encoder *This = impl_from_encoder(iface); + jmp_buf jmpbuf; + BYTE *swapped_data = NULL, *current_row; + UINT line; + int row_size; + + if (setjmp(jmpbuf)) + { + free(swapped_data); + return E_FAIL; + } + + This->cinfo.client_data = jmpbuf; + + row_size = This->format->bpp / 8 * This->encoder_frame.width; + + if (This->format->swap_rgb) + { + swapped_data = malloc(row_size); + if (!swapped_data) + return E_OUTOFMEMORY; + } + + for (line=0; line < line_count; line++) + { + if (This->format->swap_rgb) + { + UINT x; + + memcpy(swapped_data, data + (stride * line), row_size); + + for (x=0; x < This->encoder_frame.width; x++) + { + BYTE b; + + b = swapped_data[x*3]; + swapped_data[x*3] = swapped_data[x*3+2]; + swapped_data[x*3+2] = b; + } + + current_row = swapped_data; + } + else + current_row = data + (stride * line); + + if (!pjpeg_write_scanlines(&This->cinfo, ¤t_row, 1)) + { + ERR("failed writing scanlines\n"); + free(swapped_data); + return E_FAIL; + } + } + + free(swapped_data); + + return S_OK; +} + +HRESULT CDECL jpeg_encoder_commit_frame(struct encoder* iface) +{ + struct jpeg_encoder *This = impl_from_encoder(iface); + jmp_buf jmpbuf; + + if (setjmp(jmpbuf)) + return E_FAIL; + + This->cinfo.client_data = jmpbuf; + + pjpeg_finish_compress(&This->cinfo); + + return S_OK; +} + +HRESULT CDECL jpeg_encoder_commit_file(struct encoder* iface) +{ + return S_OK; +} + +void CDECL jpeg_encoder_destroy(struct encoder* iface) +{ + struct jpeg_encoder *This = impl_from_encoder(iface); + if (This->cinfo_initialized) + pjpeg_destroy_compress(&This->cinfo); + RtlFreeHeap(GetProcessHeap(), 0, This); +}; + +static const struct encoder_funcs jpeg_encoder_vtable = { + jpeg_encoder_initialize, + jpeg_encoder_get_supported_format, + jpeg_encoder_create_frame, + jpeg_encoder_write_lines, + jpeg_encoder_commit_frame, + jpeg_encoder_commit_file, + jpeg_encoder_destroy +}; + +HRESULT CDECL jpeg_encoder_create(struct encoder_info *info, struct encoder **result) +{ + struct jpeg_encoder *This; + + if (!load_libjpeg()) + { + ERR("Failed writing JPEG because unable to find %s\n", SONAME_LIBJPEG); + return E_FAIL; + } + + This = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(struct jpeg_encoder)); + if (!This) return E_OUTOFMEMORY; + + This->encoder.vtable = &jpeg_encoder_vtable; + This->stream = NULL; + This->cinfo_initialized = FALSE; + *result = &This->encoder; + + info->flags = 0; + info->container_format = GUID_ContainerFormatJpeg; + info->clsid = CLSID_WICJpegEncoder; + info->encoder_options[0] = ENCODER_OPTION_IMAGE_QUALITY; + info->encoder_options[1] = ENCODER_OPTION_BITMAP_TRANSFORM; + info->encoder_options[2] = ENCODER_OPTION_LUMINANCE; + info->encoder_options[3] = ENCODER_OPTION_CHROMINANCE; + info->encoder_options[4] = ENCODER_OPTION_YCRCB_SUBSAMPLING; + info->encoder_options[5] = ENCODER_OPTION_SUPPRESS_APP0; + info->encoder_options[6] = ENCODER_OPTION_END; + + return S_OK; +} + #else /* !defined(SONAME_LIBJPEG) */
HRESULT CDECL jpeg_decoder_create(struct decoder_info *info, struct decoder **result) @@ -461,4 +768,10 @@ HRESULT CDECL jpeg_decoder_create(struct decoder_info *info, struct decoder **re return E_FAIL; }
+HRESULT CDECL jpeg_encoder_create(struct encoder_info *info, struct encoder **result) +{ + ERR("Trying to save JPEG picture, but JPEG support is not compiled in.\n"); + return E_FAIL; +} + #endif diff --git a/dlls/windowscodecs/main.c b/dlls/windowscodecs/main.c index bc12eb2fda1..b8ffb216186 100644 --- a/dlls/windowscodecs/main.c +++ b/dlls/windowscodecs/main.c @@ -279,3 +279,31 @@ HRESULT TiffEncoder_CreateInstance(REFIID iid, void** ppv)
return hr; } + +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; +} + +HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv) +{ + HRESULT hr; + struct encoder *encoder; + struct encoder_info encoder_info; + + hr = get_unix_encoder(&CLSID_WICJpegEncoder, &encoder_info, &encoder); + + if (SUCCEEDED(hr)) + hr = CommonEncoder_CreateInstance(encoder, &encoder_info, iid, ppv); + + return hr; +} diff --git a/dlls/windowscodecs/unix_lib.c b/dlls/windowscodecs/unix_lib.c index 7101880b93c..f94340fc971 100644 --- a/dlls/windowscodecs/unix_lib.c +++ b/dlls/windowscodecs/unix_lib.c @@ -89,6 +89,9 @@ HRESULT CDECL encoder_create(const CLSID *encoder_clsid, struct encoder_info *in if (IsEqualGUID(encoder_clsid, &CLSID_WICTiffEncoder)) return tiff_encoder_create(info, result);
+ if (IsEqualGUID(encoder_clsid, &CLSID_WICJpegEncoder)) + return jpeg_encoder_create(info, result); + return E_NOTIMPL; }
diff --git a/dlls/windowscodecs/wincodecs_private.h b/dlls/windowscodecs/wincodecs_private.h index ab0329b8bb3..3814edb32bb 100644 --- a/dlls/windowscodecs/wincodecs_private.h +++ b/dlls/windowscodecs/wincodecs_private.h @@ -343,6 +343,12 @@ enum encoder_option ENCODER_OPTION_FILTER, ENCODER_OPTION_COMPRESSION_METHOD, ENCODER_OPTION_COMPRESSION_QUALITY, + ENCODER_OPTION_IMAGE_QUALITY, + ENCODER_OPTION_BITMAP_TRANSFORM, + ENCODER_OPTION_LUMINANCE, + ENCODER_OPTION_CHROMINANCE, + ENCODER_OPTION_YCRCB_SUBSAMPLING, + ENCODER_OPTION_SUPPRESS_APP0, ENCODER_OPTION_END };
@@ -400,6 +406,7 @@ HRESULT CDECL jpeg_decoder_create(struct decoder_info *info, struct decoder **re
HRESULT CDECL png_encoder_create(struct encoder_info *info, struct encoder **result); HRESULT CDECL tiff_encoder_create(struct encoder_info *info, struct encoder **result); +HRESULT CDECL jpeg_encoder_create(struct encoder_info *info, struct encoder **result);
struct unix_funcs {