windowscodecs: Implement JpegEncoder_Initialize.
[wine] / dlls / windowscodecs / jpegformat.c
1 /*
2  * Copyright 2009 Vincent Povirk for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "config.h"
20 #include "wine/port.h"
21
22 #ifdef HAVE_UNISTD_H
23 # include <unistd.h>
24 #endif
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <setjmp.h>
29
30 #ifdef SONAME_LIBJPEG
31 /* This is a hack, so jpeglib.h does not redefine INT32 and the like*/
32 #define XMD_H
33 #define UINT8 JPEG_UINT8
34 #define UINT16 JPEG_UINT16
35 #define boolean jpeg_boolean
36 #undef HAVE_STDLIB_H
37 # include <jpeglib.h>
38 #undef HAVE_STDLIB_H
39 #define HAVE_STDLIB_H 1
40 #undef UINT8
41 #undef UINT16
42 #undef boolean
43 #endif
44
45 #define COBJMACROS
46
47 #include "windef.h"
48 #include "winbase.h"
49 #include "objbase.h"
50 #include "wincodec.h"
51
52 #include "wincodecs_private.h"
53
54 #include "wine/debug.h"
55 #include "wine/library.h"
56
57 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
58
59 #ifdef SONAME_LIBJPEG
60 WINE_DECLARE_DEBUG_CHANNEL(jpeg);
61
62 static void *libjpeg_handle;
63
64 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
65 MAKE_FUNCPTR(jpeg_CreateCompress);
66 MAKE_FUNCPTR(jpeg_CreateDecompress);
67 MAKE_FUNCPTR(jpeg_destroy_compress);
68 MAKE_FUNCPTR(jpeg_destroy_decompress);
69 MAKE_FUNCPTR(jpeg_read_header);
70 MAKE_FUNCPTR(jpeg_read_scanlines);
71 MAKE_FUNCPTR(jpeg_resync_to_restart);
72 MAKE_FUNCPTR(jpeg_start_decompress);
73 MAKE_FUNCPTR(jpeg_std_error);
74 #undef MAKE_FUNCPTR
75
76 static void *load_libjpeg(void)
77 {
78     if((libjpeg_handle = wine_dlopen(SONAME_LIBJPEG, RTLD_NOW, NULL, 0)) != NULL) {
79
80 #define LOAD_FUNCPTR(f) \
81     if((p##f = wine_dlsym(libjpeg_handle, #f, NULL, 0)) == NULL) { \
82         libjpeg_handle = NULL; \
83         return NULL; \
84     }
85
86         LOAD_FUNCPTR(jpeg_CreateCompress);
87         LOAD_FUNCPTR(jpeg_CreateDecompress);
88         LOAD_FUNCPTR(jpeg_destroy_compress);
89         LOAD_FUNCPTR(jpeg_destroy_decompress);
90         LOAD_FUNCPTR(jpeg_read_header);
91         LOAD_FUNCPTR(jpeg_read_scanlines);
92         LOAD_FUNCPTR(jpeg_resync_to_restart);
93         LOAD_FUNCPTR(jpeg_start_decompress);
94         LOAD_FUNCPTR(jpeg_std_error);
95 #undef LOAD_FUNCPTR
96     }
97     return libjpeg_handle;
98 }
99
100 static void error_exit_fn(j_common_ptr cinfo)
101 {
102     char message[JMSG_LENGTH_MAX];
103     if (ERR_ON(jpeg))
104     {
105         cinfo->err->format_message(cinfo, message);
106         ERR_(jpeg)("%s\n", message);
107     }
108     longjmp(*(jmp_buf*)cinfo->client_data, 1);
109 }
110
111 static void emit_message_fn(j_common_ptr cinfo, int msg_level)
112 {
113     char message[JMSG_LENGTH_MAX];
114
115     if (msg_level < 0 && ERR_ON(jpeg))
116     {
117         cinfo->err->format_message(cinfo, message);
118         ERR_(jpeg)("%s\n", message);
119     }
120     else if (msg_level == 0 && WARN_ON(jpeg))
121     {
122         cinfo->err->format_message(cinfo, message);
123         WARN_(jpeg)("%s\n", message);
124     }
125     else if (msg_level > 0 && TRACE_ON(jpeg))
126     {
127         cinfo->err->format_message(cinfo, message);
128         TRACE_(jpeg)("%s\n", message);
129     }
130 }
131
132 typedef struct {
133     IWICBitmapDecoder IWICBitmapDecoder_iface;
134     IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
135     LONG ref;
136     BOOL initialized;
137     BOOL cinfo_initialized;
138     IStream *stream;
139     struct jpeg_decompress_struct cinfo;
140     struct jpeg_error_mgr jerr;
141     struct jpeg_source_mgr source_mgr;
142     BYTE source_buffer[1024];
143     BYTE *image_data;
144     CRITICAL_SECTION lock;
145 } JpegDecoder;
146
147 static inline JpegDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
148 {
149     return CONTAINING_RECORD(iface, JpegDecoder, IWICBitmapDecoder_iface);
150 }
151
152 static inline JpegDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
153 {
154     return CONTAINING_RECORD(iface, JpegDecoder, IWICBitmapFrameDecode_iface);
155 }
156
157 static inline JpegDecoder *decoder_from_decompress(j_decompress_ptr decompress)
158 {
159     return CONTAINING_RECORD(decompress, JpegDecoder, cinfo);
160 }
161
162 static HRESULT WINAPI JpegDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
163     void **ppv)
164 {
165     JpegDecoder *This = impl_from_IWICBitmapDecoder(iface);
166     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
167
168     if (!ppv) return E_INVALIDARG;
169
170     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IWICBitmapDecoder, iid))
171     {
172         *ppv = This;
173     }
174     else
175     {
176         *ppv = NULL;
177         return E_NOINTERFACE;
178     }
179
180     IUnknown_AddRef((IUnknown*)*ppv);
181     return S_OK;
182 }
183
184 static ULONG WINAPI JpegDecoder_AddRef(IWICBitmapDecoder *iface)
185 {
186     JpegDecoder *This = impl_from_IWICBitmapDecoder(iface);
187     ULONG ref = InterlockedIncrement(&This->ref);
188
189     TRACE("(%p) refcount=%u\n", iface, ref);
190
191     return ref;
192 }
193
194 static ULONG WINAPI JpegDecoder_Release(IWICBitmapDecoder *iface)
195 {
196     JpegDecoder *This = impl_from_IWICBitmapDecoder(iface);
197     ULONG ref = InterlockedDecrement(&This->ref);
198
199     TRACE("(%p) refcount=%u\n", iface, ref);
200
201     if (ref == 0)
202     {
203         This->lock.DebugInfo->Spare[0] = 0;
204         DeleteCriticalSection(&This->lock);
205         if (This->cinfo_initialized) pjpeg_destroy_decompress(&This->cinfo);
206         if (This->stream) IStream_Release(This->stream);
207         HeapFree(GetProcessHeap(), 0, This->image_data);
208         HeapFree(GetProcessHeap(), 0, This);
209     }
210
211     return ref;
212 }
213
214 static HRESULT WINAPI JpegDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *pIStream,
215     DWORD *pdwCapability)
216 {
217     FIXME("(%p,%p,%p): stub\n", iface, pIStream, pdwCapability);
218     return E_NOTIMPL;
219 }
220
221 static void source_mgr_init_source(j_decompress_ptr cinfo)
222 {
223 }
224
225 static jpeg_boolean source_mgr_fill_input_buffer(j_decompress_ptr cinfo)
226 {
227     JpegDecoder *This = decoder_from_decompress(cinfo);
228     HRESULT hr;
229     ULONG bytesread;
230
231     hr = IStream_Read(This->stream, This->source_buffer, 1024, &bytesread);
232
233     if (hr != S_OK || bytesread == 0)
234     {
235         return FALSE;
236     }
237     else
238     {
239         This->source_mgr.next_input_byte = This->source_buffer;
240         This->source_mgr.bytes_in_buffer = bytesread;
241         return TRUE;
242     }
243 }
244
245 static void source_mgr_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
246 {
247     JpegDecoder *This = decoder_from_decompress(cinfo);
248     LARGE_INTEGER seek;
249
250     if (num_bytes > This->source_mgr.bytes_in_buffer)
251     {
252         seek.QuadPart = num_bytes - This->source_mgr.bytes_in_buffer;
253         IStream_Seek(This->stream, seek, STREAM_SEEK_CUR, NULL);
254         This->source_mgr.bytes_in_buffer = 0;
255     }
256     else if (num_bytes > 0)
257     {
258         This->source_mgr.next_input_byte += num_bytes;
259         This->source_mgr.bytes_in_buffer -= num_bytes;
260     }
261 }
262
263 static void source_mgr_term_source(j_decompress_ptr cinfo)
264 {
265 }
266
267 static HRESULT WINAPI JpegDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
268     WICDecodeOptions cacheOptions)
269 {
270     JpegDecoder *This = impl_from_IWICBitmapDecoder(iface);
271     int ret;
272     LARGE_INTEGER seek;
273     jmp_buf jmpbuf;
274     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOptions);
275
276     EnterCriticalSection(&This->lock);
277
278     if (This->cinfo_initialized)
279     {
280         LeaveCriticalSection(&This->lock);
281         return WINCODEC_ERR_WRONGSTATE;
282     }
283
284     pjpeg_std_error(&This->jerr);
285
286     This->jerr.error_exit = error_exit_fn;
287     This->jerr.emit_message = emit_message_fn;
288
289     This->cinfo.err = &This->jerr;
290
291     This->cinfo.client_data = jmpbuf;
292
293     if (setjmp(jmpbuf))
294     {
295         LeaveCriticalSection(&This->lock);
296         return E_FAIL;
297     }
298
299     pjpeg_CreateDecompress(&This->cinfo, JPEG_LIB_VERSION, sizeof(struct jpeg_decompress_struct));
300
301     This->cinfo_initialized = TRUE;
302
303     This->stream = pIStream;
304     IStream_AddRef(pIStream);
305
306     seek.QuadPart = 0;
307     IStream_Seek(This->stream, seek, STREAM_SEEK_SET, NULL);
308
309     This->source_mgr.bytes_in_buffer = 0;
310     This->source_mgr.init_source = source_mgr_init_source;
311     This->source_mgr.fill_input_buffer = source_mgr_fill_input_buffer;
312     This->source_mgr.skip_input_data = source_mgr_skip_input_data;
313     This->source_mgr.resync_to_restart = pjpeg_resync_to_restart;
314     This->source_mgr.term_source = source_mgr_term_source;
315
316     This->cinfo.src = &This->source_mgr;
317
318     ret = pjpeg_read_header(&This->cinfo, TRUE);
319
320     if (ret != JPEG_HEADER_OK) {
321         WARN("Jpeg image in stream has bad format, read header returned %d.\n",ret);
322         LeaveCriticalSection(&This->lock);
323         return E_FAIL;
324     }
325
326     switch (This->cinfo.jpeg_color_space)
327     {
328     case JCS_GRAYSCALE:
329         This->cinfo.out_color_space = JCS_GRAYSCALE;
330         break;
331     case JCS_RGB:
332     case JCS_YCbCr:
333         This->cinfo.out_color_space = JCS_RGB;
334         break;
335     case JCS_CMYK:
336     case JCS_YCCK:
337         This->cinfo.out_color_space = JCS_CMYK;
338         break;
339     default:
340         ERR("Unknown JPEG color space %i\n", This->cinfo.jpeg_color_space);
341         LeaveCriticalSection(&This->lock);
342         return E_FAIL;
343     }
344
345     if (!pjpeg_start_decompress(&This->cinfo))
346     {
347         ERR("jpeg_start_decompress failed\n");
348         LeaveCriticalSection(&This->lock);
349         return E_FAIL;
350     }
351
352     This->initialized = TRUE;
353
354     LeaveCriticalSection(&This->lock);
355
356     return S_OK;
357 }
358
359 static HRESULT WINAPI JpegDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
360     GUID *pguidContainerFormat)
361 {
362     memcpy(pguidContainerFormat, &GUID_ContainerFormatJpeg, sizeof(GUID));
363     return S_OK;
364 }
365
366 static HRESULT WINAPI JpegDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
367     IWICBitmapDecoderInfo **ppIDecoderInfo)
368 {
369     HRESULT hr;
370     IWICComponentInfo *compinfo;
371
372     TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
373
374     hr = CreateComponentInfo(&CLSID_WICJpegDecoder, &compinfo);
375     if (FAILED(hr)) return hr;
376
377     hr = IWICComponentInfo_QueryInterface(compinfo, &IID_IWICBitmapDecoderInfo,
378         (void**)ppIDecoderInfo);
379
380     IWICComponentInfo_Release(compinfo);
381
382     return hr;
383 }
384
385 static HRESULT WINAPI JpegDecoder_CopyPalette(IWICBitmapDecoder *iface,
386     IWICPalette *pIPalette)
387 {
388     TRACE("(%p,%p)\n", iface, pIPalette);
389
390     return WINCODEC_ERR_PALETTEUNAVAILABLE;
391 }
392
393 static HRESULT WINAPI JpegDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
394     IWICMetadataQueryReader **ppIMetadataQueryReader)
395 {
396     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
397     return E_NOTIMPL;
398 }
399
400 static HRESULT WINAPI JpegDecoder_GetPreview(IWICBitmapDecoder *iface,
401     IWICBitmapSource **ppIBitmapSource)
402 {
403     FIXME("(%p,%p): stub\n", iface, ppIBitmapSource);
404     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
405 }
406
407 static HRESULT WINAPI JpegDecoder_GetColorContexts(IWICBitmapDecoder *iface,
408     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
409 {
410     FIXME("(%p,%u,%p,%p): stub\n", iface, cCount, ppIColorContexts, pcActualCount);
411     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
412 }
413
414 static HRESULT WINAPI JpegDecoder_GetThumbnail(IWICBitmapDecoder *iface,
415     IWICBitmapSource **ppIThumbnail)
416 {
417     FIXME("(%p,%p): stub\n", iface, ppIThumbnail);
418     return WINCODEC_ERR_CODECNOTHUMBNAIL;
419 }
420
421 static HRESULT WINAPI JpegDecoder_GetFrameCount(IWICBitmapDecoder *iface,
422     UINT *pCount)
423 {
424     *pCount = 1;
425     return S_OK;
426 }
427
428 static HRESULT WINAPI JpegDecoder_GetFrame(IWICBitmapDecoder *iface,
429     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
430 {
431     JpegDecoder *This = impl_from_IWICBitmapDecoder(iface);
432     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
433
434     if (!This->initialized) return WINCODEC_ERR_NOTINITIALIZED;
435
436     if (index != 0) return E_INVALIDARG;
437
438     IWICBitmapDecoder_AddRef(iface);
439     *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface;
440
441     return S_OK;
442 }
443
444 static const IWICBitmapDecoderVtbl JpegDecoder_Vtbl = {
445     JpegDecoder_QueryInterface,
446     JpegDecoder_AddRef,
447     JpegDecoder_Release,
448     JpegDecoder_QueryCapability,
449     JpegDecoder_Initialize,
450     JpegDecoder_GetContainerFormat,
451     JpegDecoder_GetDecoderInfo,
452     JpegDecoder_CopyPalette,
453     JpegDecoder_GetMetadataQueryReader,
454     JpegDecoder_GetPreview,
455     JpegDecoder_GetColorContexts,
456     JpegDecoder_GetThumbnail,
457     JpegDecoder_GetFrameCount,
458     JpegDecoder_GetFrame
459 };
460
461 static HRESULT WINAPI JpegDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
462     void **ppv)
463 {
464     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
465
466     if (!ppv) return E_INVALIDARG;
467
468     if (IsEqualIID(&IID_IUnknown, iid) ||
469         IsEqualIID(&IID_IWICBitmapSource, iid) ||
470         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
471     {
472         *ppv = iface;
473     }
474     else
475     {
476         *ppv = NULL;
477         return E_NOINTERFACE;
478     }
479
480     IUnknown_AddRef((IUnknown*)*ppv);
481     return S_OK;
482 }
483
484 static ULONG WINAPI JpegDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface)
485 {
486     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
487     return IUnknown_AddRef((IUnknown*)This);
488 }
489
490 static ULONG WINAPI JpegDecoder_Frame_Release(IWICBitmapFrameDecode *iface)
491 {
492     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
493     return IUnknown_Release((IUnknown*)This);
494 }
495
496 static HRESULT WINAPI JpegDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface,
497     UINT *puiWidth, UINT *puiHeight)
498 {
499     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
500     *puiWidth = This->cinfo.output_width;
501     *puiHeight = This->cinfo.output_height;
502     TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight);
503     return S_OK;
504 }
505
506 static HRESULT WINAPI JpegDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface,
507     WICPixelFormatGUID *pPixelFormat)
508 {
509     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
510     TRACE("(%p,%p)\n", iface, pPixelFormat);
511     if (This->cinfo.out_color_space == JCS_RGB)
512         memcpy(pPixelFormat, &GUID_WICPixelFormat24bppBGR, sizeof(GUID));
513     else if (This->cinfo.out_color_space == JCS_CMYK)
514         memcpy(pPixelFormat, &GUID_WICPixelFormat32bppCMYK, sizeof(GUID));
515     else /* This->cinfo.out_color_space == JCS_GRAYSCALE */
516         memcpy(pPixelFormat, &GUID_WICPixelFormat8bppGray, sizeof(GUID));
517     return S_OK;
518 }
519
520 static HRESULT WINAPI JpegDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface,
521     double *pDpiX, double *pDpiY)
522 {
523     FIXME("(%p,%p,%p): stub\n", iface, pDpiX, pDpiY);
524     return E_NOTIMPL;
525 }
526
527 static HRESULT WINAPI JpegDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface,
528     IWICPalette *pIPalette)
529 {
530     FIXME("(%p,%p): stub\n", iface, pIPalette);
531     return E_NOTIMPL;
532 }
533
534 static HRESULT WINAPI JpegDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface,
535     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
536 {
537     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
538     UINT bpp;
539     UINT stride;
540     UINT data_size;
541     UINT max_row_needed;
542     jmp_buf jmpbuf;
543     WICRect rect;
544     TRACE("(%p,%p,%u,%u,%p)\n", iface, prc, cbStride, cbBufferSize, pbBuffer);
545
546     if (!prc)
547     {
548         rect.X = 0;
549         rect.Y = 0;
550         rect.Width = This->cinfo.output_width;
551         rect.Height = This->cinfo.output_height;
552         prc = &rect;
553     }
554     else
555     {
556         if (prc->X < 0 || prc->Y < 0 || prc->X+prc->Width > This->cinfo.output_width ||
557             prc->Y+prc->Height > This->cinfo.output_height)
558             return E_INVALIDARG;
559     }
560
561     if (This->cinfo.out_color_space == JCS_GRAYSCALE) bpp = 8;
562     else if (This->cinfo.out_color_space == JCS_CMYK) bpp = 32;
563     else bpp = 24;
564
565     stride = bpp * This->cinfo.output_width;
566     data_size = stride * This->cinfo.output_height;
567
568     max_row_needed = prc->Y + prc->Height;
569     if (max_row_needed > This->cinfo.output_height) return E_INVALIDARG;
570
571     EnterCriticalSection(&This->lock);
572
573     if (!This->image_data)
574     {
575         This->image_data = HeapAlloc(GetProcessHeap(), 0, data_size);
576         if (!This->image_data)
577         {
578             LeaveCriticalSection(&This->lock);
579             return E_OUTOFMEMORY;
580         }
581     }
582
583     This->cinfo.client_data = jmpbuf;
584
585     if (setjmp(jmpbuf))
586     {
587         LeaveCriticalSection(&This->lock);
588         return E_FAIL;
589     }
590
591     while (max_row_needed > This->cinfo.output_scanline)
592     {
593         UINT first_scanline = This->cinfo.output_scanline;
594         UINT max_rows;
595         JSAMPROW out_rows[4];
596         UINT i;
597         JDIMENSION ret;
598
599         max_rows = min(This->cinfo.output_height-first_scanline, 4);
600         for (i=0; i<max_rows; i++)
601             out_rows[i] = This->image_data + stride * (first_scanline+i);
602
603         ret = pjpeg_read_scanlines(&This->cinfo, out_rows, max_rows);
604
605         if (ret == 0)
606         {
607             ERR("read_scanlines failed\n");
608             LeaveCriticalSection(&This->lock);
609             return E_FAIL;
610         }
611
612         if (bpp == 24)
613         {
614             /* libjpeg gives us RGB data and we want BGR, so byteswap the data */
615             reverse_bgr8(3, This->image_data + stride * first_scanline,
616                 This->cinfo.output_width, This->cinfo.output_scanline - first_scanline,
617                 stride);
618         }
619
620         if (This->cinfo.out_color_space == JCS_CMYK && This->cinfo.saw_Adobe_marker)
621             /* Adobe JPEG's have inverted CMYK data. */
622             for (i=0; i<data_size; i++)
623                 This->image_data[i] ^= 0xff;
624     }
625
626     LeaveCriticalSection(&This->lock);
627
628     return copy_pixels(bpp, This->image_data,
629         This->cinfo.output_width, This->cinfo.output_height, stride,
630         prc, cbStride, cbBufferSize, pbBuffer);
631 }
632
633 static HRESULT WINAPI JpegDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
634     IWICMetadataQueryReader **ppIMetadataQueryReader)
635 {
636     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryReader);
637     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
638 }
639
640 static HRESULT WINAPI JpegDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface,
641     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
642 {
643     FIXME("(%p,%u,%p,%p): stub\n", iface, cCount, ppIColorContexts, pcActualCount);
644     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
645 }
646
647 static HRESULT WINAPI JpegDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
648     IWICBitmapSource **ppIThumbnail)
649 {
650     FIXME("(%p,%p): stub\n", iface, ppIThumbnail);
651     return WINCODEC_ERR_CODECNOTHUMBNAIL;
652 }
653
654 static const IWICBitmapFrameDecodeVtbl JpegDecoder_Frame_Vtbl = {
655     JpegDecoder_Frame_QueryInterface,
656     JpegDecoder_Frame_AddRef,
657     JpegDecoder_Frame_Release,
658     JpegDecoder_Frame_GetSize,
659     JpegDecoder_Frame_GetPixelFormat,
660     JpegDecoder_Frame_GetResolution,
661     JpegDecoder_Frame_CopyPalette,
662     JpegDecoder_Frame_CopyPixels,
663     JpegDecoder_Frame_GetMetadataQueryReader,
664     JpegDecoder_Frame_GetColorContexts,
665     JpegDecoder_Frame_GetThumbnail
666 };
667
668 HRESULT JpegDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
669 {
670     JpegDecoder *This;
671     HRESULT ret;
672
673     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
674
675     if (!libjpeg_handle && !load_libjpeg())
676     {
677         ERR("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG);
678         return E_FAIL;
679     }
680
681     *ppv = NULL;
682
683     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
684
685     This = HeapAlloc(GetProcessHeap(), 0, sizeof(JpegDecoder));
686     if (!This) return E_OUTOFMEMORY;
687
688     This->IWICBitmapDecoder_iface.lpVtbl = &JpegDecoder_Vtbl;
689     This->IWICBitmapFrameDecode_iface.lpVtbl = &JpegDecoder_Frame_Vtbl;
690     This->ref = 1;
691     This->initialized = FALSE;
692     This->cinfo_initialized = FALSE;
693     This->stream = NULL;
694     This->image_data = NULL;
695     InitializeCriticalSection(&This->lock);
696     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JpegDecoder.lock");
697
698     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
699     IUnknown_Release((IUnknown*)This);
700
701     return ret;
702 }
703
704 typedef struct JpegEncoder {
705     IWICBitmapEncoder IWICBitmapEncoder_iface;
706     LONG ref;
707     struct jpeg_compress_struct cinfo;
708     struct jpeg_error_mgr jerr;
709     struct jpeg_destination_mgr dest_mgr;
710     int initialized;
711     IStream *stream;
712     CRITICAL_SECTION lock;
713     BYTE dest_buffer[1024];
714 } JpegEncoder;
715
716 static inline JpegEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
717 {
718     return CONTAINING_RECORD(iface, JpegEncoder, IWICBitmapEncoder_iface);
719 }
720
721 static inline JpegEncoder *encoder_from_compress(j_compress_ptr compress)
722 {
723     return CONTAINING_RECORD(compress, JpegEncoder, cinfo);
724 }
725
726 static void dest_mgr_init_destination(j_compress_ptr cinfo)
727 {
728     JpegEncoder *This = encoder_from_compress(cinfo);
729
730     This->dest_mgr.next_output_byte = This->dest_buffer;
731     This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer);
732 }
733
734 static jpeg_boolean dest_mgr_empty_output_buffer(j_compress_ptr cinfo)
735 {
736     JpegEncoder *This = encoder_from_compress(cinfo);
737     HRESULT hr;
738     ULONG byteswritten;
739
740     hr = IStream_Write(This->stream, This->dest_buffer,
741         sizeof(This->dest_buffer), &byteswritten);
742
743     if (hr != S_OK || byteswritten == 0)
744     {
745         ERR("Failed writing data, hr=%x\n", hr);
746         return FALSE;
747     }
748
749     This->dest_mgr.next_output_byte = This->dest_buffer;
750     This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer);
751     return TRUE;
752 }
753
754 static void dest_mgr_term_destination(j_compress_ptr cinfo)
755 {
756     JpegEncoder *This = encoder_from_compress(cinfo);
757     ULONG byteswritten;
758     HRESULT hr;
759
760     if (This->dest_mgr.free_in_buffer != sizeof(This->dest_buffer))
761     {
762         hr = IStream_Write(This->stream, This->dest_buffer,
763             sizeof(This->dest_buffer) - This->dest_mgr.free_in_buffer, &byteswritten);
764
765         if (hr != S_OK || byteswritten == 0)
766             ERR("Failed writing data, hr=%x\n", hr);
767     }
768 }
769
770 static HRESULT WINAPI JpegEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
771     void **ppv)
772 {
773     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
774     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
775
776     if (!ppv) return E_INVALIDARG;
777
778     if (IsEqualIID(&IID_IUnknown, iid) ||
779         IsEqualIID(&IID_IWICBitmapEncoder, iid))
780     {
781         *ppv = This;
782     }
783     else
784     {
785         *ppv = NULL;
786         return E_NOINTERFACE;
787     }
788
789     IUnknown_AddRef((IUnknown*)*ppv);
790     return S_OK;
791 }
792
793 static ULONG WINAPI JpegEncoder_AddRef(IWICBitmapEncoder *iface)
794 {
795     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
796     ULONG ref = InterlockedIncrement(&This->ref);
797
798     TRACE("(%p) refcount=%u\n", iface, ref);
799
800     return ref;
801 }
802
803 static ULONG WINAPI JpegEncoder_Release(IWICBitmapEncoder *iface)
804 {
805     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
806     ULONG ref = InterlockedDecrement(&This->ref);
807
808     TRACE("(%p) refcount=%u\n", iface, ref);
809
810     if (ref == 0)
811     {
812         This->lock.DebugInfo->Spare[0] = 0;
813         DeleteCriticalSection(&This->lock);
814         if (This->initialized) pjpeg_destroy_compress(&This->cinfo);
815         if (This->stream) IStream_Release(This->stream);
816         HeapFree(GetProcessHeap(), 0, This);
817     }
818
819     return ref;
820 }
821
822 static HRESULT WINAPI JpegEncoder_Initialize(IWICBitmapEncoder *iface,
823     IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
824 {
825     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
826     jmp_buf jmpbuf;
827
828     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
829
830     EnterCriticalSection(&This->lock);
831
832     if (This->initialized)
833     {
834         LeaveCriticalSection(&This->lock);
835         return WINCODEC_ERR_WRONGSTATE;
836     }
837
838     pjpeg_std_error(&This->jerr);
839
840     This->jerr.error_exit = error_exit_fn;
841     This->jerr.emit_message = emit_message_fn;
842
843     This->cinfo.err = &This->jerr;
844
845     This->cinfo.client_data = &jmpbuf;
846
847     if (setjmp(jmpbuf))
848     {
849         LeaveCriticalSection(&This->lock);
850         return E_FAIL;
851     }
852
853     pjpeg_CreateCompress(&This->cinfo, JPEG_LIB_VERSION, sizeof(struct jpeg_compress_struct));
854
855     This->stream = pIStream;
856     IStream_AddRef(pIStream);
857
858     This->dest_mgr.next_output_byte = This->dest_buffer;
859     This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer);
860
861     This->dest_mgr.init_destination = dest_mgr_init_destination;
862     This->dest_mgr.empty_output_buffer = dest_mgr_empty_output_buffer;
863     This->dest_mgr.term_destination = dest_mgr_term_destination;
864
865     This->cinfo.dest = &This->dest_mgr;
866
867     This->initialized = TRUE;
868
869     LeaveCriticalSection(&This->lock);
870
871     return S_OK;
872 }
873
874 static HRESULT WINAPI JpegEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
875     GUID *pguidContainerFormat)
876 {
877     FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
878     return E_NOTIMPL;
879 }
880
881 static HRESULT WINAPI JpegEncoder_GetEncoderInfo(IWICBitmapEncoder *iface,
882     IWICBitmapEncoderInfo **ppIEncoderInfo)
883 {
884     FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo);
885     return E_NOTIMPL;
886 }
887
888 static HRESULT WINAPI JpegEncoder_SetColorContexts(IWICBitmapEncoder *iface,
889     UINT cCount, IWICColorContext **ppIColorContext)
890 {
891     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
892     return E_NOTIMPL;
893 }
894
895 static HRESULT WINAPI JpegEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette)
896 {
897     TRACE("(%p,%p)\n", iface, pIPalette);
898     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
899 }
900
901 static HRESULT WINAPI JpegEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
902 {
903     TRACE("(%p,%p)\n", iface, pIThumbnail);
904     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
905 }
906
907 static HRESULT WINAPI JpegEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
908 {
909     TRACE("(%p,%p)\n", iface, pIPreview);
910     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
911 }
912
913 static HRESULT WINAPI JpegEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
914     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
915 {
916     FIXME("(%p,%p,%p): stub\n", iface, ppIFrameEncode, ppIEncoderOptions);
917     return E_NOTIMPL;
918 }
919
920 static HRESULT WINAPI JpegEncoder_Commit(IWICBitmapEncoder *iface)
921 {
922     FIXME("(%p): stub\n", iface);
923     return E_NOTIMPL;
924 }
925
926 static HRESULT WINAPI JpegEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
927     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
928 {
929     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
930     return E_NOTIMPL;
931 }
932
933 static const IWICBitmapEncoderVtbl JpegEncoder_Vtbl = {
934     JpegEncoder_QueryInterface,
935     JpegEncoder_AddRef,
936     JpegEncoder_Release,
937     JpegEncoder_Initialize,
938     JpegEncoder_GetContainerFormat,
939     JpegEncoder_GetEncoderInfo,
940     JpegEncoder_SetColorContexts,
941     JpegEncoder_SetPalette,
942     JpegEncoder_SetThumbnail,
943     JpegEncoder_SetPreview,
944     JpegEncoder_CreateNewFrame,
945     JpegEncoder_Commit,
946     JpegEncoder_GetMetadataQueryWriter
947 };
948
949 HRESULT JpegEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
950 {
951     JpegEncoder *This;
952     HRESULT ret;
953
954     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
955
956     *ppv = NULL;
957
958     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
959
960     if (!libjpeg_handle && !load_libjpeg())
961     {
962         ERR("Failed writing JPEG because unable to find %s\n",SONAME_LIBJPEG);
963         return E_FAIL;
964     }
965
966     This = HeapAlloc(GetProcessHeap(), 0, sizeof(JpegEncoder));
967     if (!This) return E_OUTOFMEMORY;
968
969     This->IWICBitmapEncoder_iface.lpVtbl = &JpegEncoder_Vtbl;
970     This->ref = 1;
971     This->initialized = 0;
972     This->stream = NULL;
973     InitializeCriticalSection(&This->lock);
974     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JpegEncoder.lock");
975
976     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
977     IUnknown_Release((IUnknown*)This);
978
979     return ret;
980 }
981
982 #else /* !defined(SONAME_LIBJPEG) */
983
984 HRESULT JpegDecoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
985 {
986     ERR("Trying to load JPEG picture, but JPEG support is not compiled in.\n");
987     return E_FAIL;
988 }
989
990 HRESULT JpegEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
991 {
992     ERR("Trying to save JPEG picture, but JPEG support is not compiled in.\n");
993     return E_FAIL;
994 }
995
996 #endif