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 
51 #include "wincodecs_private.h"
52 
53 #include "wine/heap.h"
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 static const WCHAR wszImageQuality[] = {'I','m','a','g','e','Q','u','a','l','i','t','y',0};
65 static const WCHAR wszBitmapTransform[] = {'B','i','t','m','a','p','T','r','a','n','s','f','o','r','m',0};
66 static const WCHAR wszLuminance[] = {'L','u','m','i','n','a','n','c','e',0};
67 static const WCHAR wszChrominance[] = {'C','h','r','o','m','i','n','a','n','c','e',0};
68 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};
69 static const WCHAR wszSuppressApp0[] = {'S','u','p','p','r','e','s','s','A','p','p','0',0};
70 
71 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
72 MAKE_FUNCPTR(jpeg_CreateCompress);
73 MAKE_FUNCPTR(jpeg_CreateDecompress);
74 MAKE_FUNCPTR(jpeg_destroy_compress);
75 MAKE_FUNCPTR(jpeg_destroy_decompress);
76 MAKE_FUNCPTR(jpeg_finish_compress);
77 MAKE_FUNCPTR(jpeg_read_header);
78 MAKE_FUNCPTR(jpeg_read_scanlines);
79 MAKE_FUNCPTR(jpeg_resync_to_restart);
80 MAKE_FUNCPTR(jpeg_set_defaults);
81 MAKE_FUNCPTR(jpeg_start_compress);
82 MAKE_FUNCPTR(jpeg_start_decompress);
83 MAKE_FUNCPTR(jpeg_std_error);
84 MAKE_FUNCPTR(jpeg_write_scanlines);
85 #undef MAKE_FUNCPTR
86 
87 static void *load_libjpeg(void)
88 {
89     if((libjpeg_handle = wine_dlopen(SONAME_LIBJPEG, RTLD_NOW, NULL, 0)) != NULL) {
90 
91 #define LOAD_FUNCPTR(f) \
92     if((p##f = wine_dlsym(libjpeg_handle, #f, NULL, 0)) == NULL) { \
93         libjpeg_handle = NULL; \
94         return NULL; \
95     }
96 
97         LOAD_FUNCPTR(jpeg_CreateCompress);
98         LOAD_FUNCPTR(jpeg_CreateDecompress);
99         LOAD_FUNCPTR(jpeg_destroy_compress);
100         LOAD_FUNCPTR(jpeg_destroy_decompress);
101         LOAD_FUNCPTR(jpeg_finish_compress);
102         LOAD_FUNCPTR(jpeg_read_header);
103         LOAD_FUNCPTR(jpeg_read_scanlines);
104         LOAD_FUNCPTR(jpeg_resync_to_restart);
105         LOAD_FUNCPTR(jpeg_set_defaults);
106         LOAD_FUNCPTR(jpeg_start_compress);
107         LOAD_FUNCPTR(jpeg_start_decompress);
108         LOAD_FUNCPTR(jpeg_std_error);
109         LOAD_FUNCPTR(jpeg_write_scanlines);
110 #undef LOAD_FUNCPTR
111     }
112     return libjpeg_handle;
113 }
114 
115 static void error_exit_fn(j_common_ptr cinfo)
116 {
117     char message[JMSG_LENGTH_MAX];
118     if (ERR_ON(jpeg))
119     {
120         cinfo->err->format_message(cinfo, message);
121         ERR_(jpeg)("%s\n", message);
122     }
123     longjmp(*(jmp_buf*)cinfo->client_data, 1);
124 }
125 
126 static void emit_message_fn(j_common_ptr cinfo, int msg_level)
127 {
128     char message[JMSG_LENGTH_MAX];
129 
130     if (msg_level < 0 && ERR_ON(jpeg))
131     {
132         cinfo->err->format_message(cinfo, message);
133         ERR_(jpeg)("%s\n", message);
134     }
135     else if (msg_level == 0 && WARN_ON(jpeg))
136     {
137         cinfo->err->format_message(cinfo, message);
138         WARN_(jpeg)("%s\n", message);
139     }
140     else if (msg_level > 0 && TRACE_ON(jpeg))
141     {
142         cinfo->err->format_message(cinfo, message);
143         TRACE_(jpeg)("%s\n", message);
144     }
145 }
146 
147 typedef struct {
148     IWICBitmapDecoder IWICBitmapDecoder_iface;
149     IWICBitmapFrameDecode IWICBitmapFrameDecode_iface;
150     IWICMetadataBlockReader IWICMetadataBlockReader_iface;
151     LONG ref;
152     BOOL initialized;
153     BOOL cinfo_initialized;
154     IStream *stream;
155     struct jpeg_decompress_struct cinfo;
156     struct jpeg_error_mgr jerr;
157     struct jpeg_source_mgr source_mgr;
158     BYTE source_buffer[1024];
159     UINT bpp, stride;
160     BYTE *image_data;
161     CRITICAL_SECTION lock;
162 } JpegDecoder;
163 
164 static inline JpegDecoder *impl_from_IWICBitmapDecoder(IWICBitmapDecoder *iface)
165 {
166     return CONTAINING_RECORD(iface, JpegDecoder, IWICBitmapDecoder_iface);
167 }
168 
169 static inline JpegDecoder *impl_from_IWICBitmapFrameDecode(IWICBitmapFrameDecode *iface)
170 {
171     return CONTAINING_RECORD(iface, JpegDecoder, IWICBitmapFrameDecode_iface);
172 }
173 
174 static inline JpegDecoder *decoder_from_decompress(j_decompress_ptr decompress)
175 {
176     return CONTAINING_RECORD(decompress, JpegDecoder, cinfo);
177 }
178 
179 static inline JpegDecoder *impl_from_IWICMetadataBlockReader(IWICMetadataBlockReader *iface)
180 {
181     return CONTAINING_RECORD(iface, JpegDecoder, IWICMetadataBlockReader_iface);
182 }
183 
184 static HRESULT WINAPI JpegDecoder_QueryInterface(IWICBitmapDecoder *iface, REFIID iid,
185     void **ppv)
186 {
187     JpegDecoder *This = impl_from_IWICBitmapDecoder(iface);
188     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
189 
190     if (!ppv) return E_INVALIDARG;
191 
192     if (IsEqualIID(&IID_IUnknown, iid) ||
193         IsEqualIID(&IID_IWICBitmapDecoder, iid))
194     {
195         *ppv = &This->IWICBitmapDecoder_iface;
196     }
197     else
198     {
199         *ppv = NULL;
200         return E_NOINTERFACE;
201     }
202 
203     IUnknown_AddRef((IUnknown*)*ppv);
204     return S_OK;
205 }
206 
207 static ULONG WINAPI JpegDecoder_AddRef(IWICBitmapDecoder *iface)
208 {
209     JpegDecoder *This = impl_from_IWICBitmapDecoder(iface);
210     ULONG ref = InterlockedIncrement(&This->ref);
211 
212     TRACE("(%p) refcount=%u\n", iface, ref);
213 
214     return ref;
215 }
216 
217 static ULONG WINAPI JpegDecoder_Release(IWICBitmapDecoder *iface)
218 {
219     JpegDecoder *This = impl_from_IWICBitmapDecoder(iface);
220     ULONG ref = InterlockedDecrement(&This->ref);
221 
222     TRACE("(%p) refcount=%u\n", iface, ref);
223 
224     if (ref == 0)
225     {
226         This->lock.DebugInfo->Spare[0] = 0;
227         DeleteCriticalSection(&This->lock);
228         if (This->cinfo_initialized) pjpeg_destroy_decompress(&This->cinfo);
229         if (This->stream) IStream_Release(This->stream);
230         HeapFree(GetProcessHeap(), 0, This->image_data);
231         HeapFree(GetProcessHeap(), 0, This);
232     }
233 
234     return ref;
235 }
236 
237 static HRESULT WINAPI JpegDecoder_QueryCapability(IWICBitmapDecoder *iface, IStream *stream,
238     DWORD *capability)
239 {
240     HRESULT hr;
241 
242     TRACE("(%p,%p,%p)\n", iface, stream, capability);
243 
244     if (!stream || !capability) return E_INVALIDARG;
245 
246     hr = IWICBitmapDecoder_Initialize(iface, stream, WICDecodeMetadataCacheOnDemand);
247     if (hr != S_OK) return hr;
248 
249     *capability = WICBitmapDecoderCapabilityCanDecodeAllImages |
250                   WICBitmapDecoderCapabilityCanDecodeSomeImages;
251     /* FIXME: WICBitmapDecoderCapabilityCanEnumerateMetadata */
252     return S_OK;
253 }
254 
255 static void source_mgr_init_source(j_decompress_ptr cinfo)
256 {
257 }
258 
259 static jpeg_boolean source_mgr_fill_input_buffer(j_decompress_ptr cinfo)
260 {
261     JpegDecoder *This = decoder_from_decompress(cinfo);
262     HRESULT hr;
263     ULONG bytesread;
264 
265     hr = IStream_Read(This->stream, This->source_buffer, 1024, &bytesread);
266 
267     if (FAILED(hr) || bytesread == 0)
268     {
269         return FALSE;
270     }
271     else
272     {
273         This->source_mgr.next_input_byte = This->source_buffer;
274         This->source_mgr.bytes_in_buffer = bytesread;
275         return TRUE;
276     }
277 }
278 
279 static void source_mgr_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
280 {
281     JpegDecoder *This = decoder_from_decompress(cinfo);
282     LARGE_INTEGER seek;
283 
284     if (num_bytes > This->source_mgr.bytes_in_buffer)
285     {
286         seek.QuadPart = num_bytes - This->source_mgr.bytes_in_buffer;
287         IStream_Seek(This->stream, seek, STREAM_SEEK_CUR, NULL);
288         This->source_mgr.bytes_in_buffer = 0;
289     }
290     else if (num_bytes > 0)
291     {
292         This->source_mgr.next_input_byte += num_bytes;
293         This->source_mgr.bytes_in_buffer -= num_bytes;
294     }
295 }
296 
297 static void source_mgr_term_source(j_decompress_ptr cinfo)
298 {
299 }
300 
301 static HRESULT WINAPI JpegDecoder_Initialize(IWICBitmapDecoder *iface, IStream *pIStream,
302     WICDecodeOptions cacheOptions)
303 {
304     JpegDecoder *This = impl_from_IWICBitmapDecoder(iface);
305     int ret;
306     LARGE_INTEGER seek;
307     jmp_buf jmpbuf;
308     UINT data_size, i;
309 
310     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOptions);
311 
312     EnterCriticalSection(&This->lock);
313 
314     if (This->cinfo_initialized)
315     {
316         LeaveCriticalSection(&This->lock);
317         return WINCODEC_ERR_WRONGSTATE;
318     }
319 
320     pjpeg_std_error(&This->jerr);
321 
322     This->jerr.error_exit = error_exit_fn;
323     This->jerr.emit_message = emit_message_fn;
324 
325     This->cinfo.err = &This->jerr;
326 
327     This->cinfo.client_data = jmpbuf;
328 
329     if (setjmp(jmpbuf))
330     {
331         LeaveCriticalSection(&This->lock);
332         return E_FAIL;
333     }
334 
335     pjpeg_CreateDecompress(&This->cinfo, JPEG_LIB_VERSION, sizeof(struct jpeg_decompress_struct));
336 
337     This->cinfo_initialized = TRUE;
338 
339     This->stream = pIStream;
340     IStream_AddRef(pIStream);
341 
342     seek.QuadPart = 0;
343     IStream_Seek(This->stream, seek, STREAM_SEEK_SET, NULL);
344 
345     This->source_mgr.bytes_in_buffer = 0;
346     This->source_mgr.init_source = source_mgr_init_source;
347     This->source_mgr.fill_input_buffer = source_mgr_fill_input_buffer;
348     This->source_mgr.skip_input_data = source_mgr_skip_input_data;
349     This->source_mgr.resync_to_restart = pjpeg_resync_to_restart;
350     This->source_mgr.term_source = source_mgr_term_source;
351 
352     This->cinfo.src = &This->source_mgr;
353 
354     ret = pjpeg_read_header(&This->cinfo, TRUE);
355 
356     if (ret != JPEG_HEADER_OK) {
357         WARN("Jpeg image in stream has bad format, read header returned %d.\n",ret);
358         LeaveCriticalSection(&This->lock);
359         return E_FAIL;
360     }
361 
362     switch (This->cinfo.jpeg_color_space)
363     {
364     case JCS_GRAYSCALE:
365         This->cinfo.out_color_space = JCS_GRAYSCALE;
366         break;
367     case JCS_RGB:
368     case JCS_YCbCr:
369         This->cinfo.out_color_space = JCS_RGB;
370         break;
371     case JCS_CMYK:
372     case JCS_YCCK:
373         This->cinfo.out_color_space = JCS_CMYK;
374         break;
375     default:
376         ERR("Unknown JPEG color space %i\n", This->cinfo.jpeg_color_space);
377         LeaveCriticalSection(&This->lock);
378         return E_FAIL;
379     }
380 
381     if (!pjpeg_start_decompress(&This->cinfo))
382     {
383         ERR("jpeg_start_decompress failed\n");
384         LeaveCriticalSection(&This->lock);
385         return E_FAIL;
386     }
387 
388     if (This->cinfo.out_color_space == JCS_GRAYSCALE) This->bpp = 8;
389     else if (This->cinfo.out_color_space == JCS_CMYK) This->bpp = 32;
390     else This->bpp = 24;
391 
392     This->stride = (This->bpp * This->cinfo.output_width + 7) / 8;
393     data_size = This->stride * This->cinfo.output_height;
394 
395     This->image_data = heap_alloc(data_size);
396     if (!This->image_data)
397     {
398         LeaveCriticalSection(&This->lock);
399         return E_OUTOFMEMORY;
400     }
401 
402     while (This->cinfo.output_scanline < This->cinfo.output_height)
403     {
404         UINT first_scanline = This->cinfo.output_scanline;
405         UINT max_rows;
406         JSAMPROW out_rows[4];
407         JDIMENSION ret;
408 
409         max_rows = min(This->cinfo.output_height-first_scanline, 4);
410         for (i=0; i<max_rows; i++)
411             out_rows[i] = This->image_data + This->stride * (first_scanline+i);
412 
413         ret = pjpeg_read_scanlines(&This->cinfo, out_rows, max_rows);
414         if (ret == 0)
415         {
416             ERR("read_scanlines failed\n");
417             LeaveCriticalSection(&This->lock);
418             return E_FAIL;
419         }
420     }
421 
422     if (This->bpp == 24)
423     {
424         /* libjpeg gives us RGB data and we want BGR, so byteswap the data */
425         reverse_bgr8(3, This->image_data,
426             This->cinfo.output_width, This->cinfo.output_height,
427             This->stride);
428     }
429 
430     if (This->cinfo.out_color_space == JCS_CMYK && This->cinfo.saw_Adobe_marker)
431     {
432         /* Adobe JPEG's have inverted CMYK data. */
433         for (i=0; i<data_size; i++)
434             This->image_data[i] ^= 0xff;
435     }
436 
437     This->initialized = TRUE;
438 
439     LeaveCriticalSection(&This->lock);
440 
441     return S_OK;
442 }
443 
444 static HRESULT WINAPI JpegDecoder_GetContainerFormat(IWICBitmapDecoder *iface,
445     GUID *pguidContainerFormat)
446 {
447     memcpy(pguidContainerFormat, &GUID_ContainerFormatJpeg, sizeof(GUID));
448     return S_OK;
449 }
450 
451 static HRESULT WINAPI JpegDecoder_GetDecoderInfo(IWICBitmapDecoder *iface,
452     IWICBitmapDecoderInfo **ppIDecoderInfo)
453 {
454     TRACE("(%p,%p)\n", iface, ppIDecoderInfo);
455 
456     return get_decoder_info(&CLSID_WICJpegDecoder, ppIDecoderInfo);
457 }
458 
459 static HRESULT WINAPI JpegDecoder_CopyPalette(IWICBitmapDecoder *iface,
460     IWICPalette *pIPalette)
461 {
462     TRACE("(%p,%p)\n", iface, pIPalette);
463 
464     return WINCODEC_ERR_PALETTEUNAVAILABLE;
465 }
466 
467 static HRESULT WINAPI JpegDecoder_GetMetadataQueryReader(IWICBitmapDecoder *iface,
468     IWICMetadataQueryReader **reader)
469 {
470     TRACE("(%p,%p)\n", iface, reader);
471 
472     if (!reader) return E_INVALIDARG;
473 
474     *reader = NULL;
475     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
476 }
477 
478 static HRESULT WINAPI JpegDecoder_GetPreview(IWICBitmapDecoder *iface,
479     IWICBitmapSource **ppIBitmapSource)
480 {
481     FIXME("(%p,%p): stub\n", iface, ppIBitmapSource);
482     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
483 }
484 
485 static HRESULT WINAPI JpegDecoder_GetColorContexts(IWICBitmapDecoder *iface,
486     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
487 {
488     FIXME("(%p,%u,%p,%p): stub\n", iface, cCount, ppIColorContexts, pcActualCount);
489     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
490 }
491 
492 static HRESULT WINAPI JpegDecoder_GetThumbnail(IWICBitmapDecoder *iface,
493     IWICBitmapSource **ppIThumbnail)
494 {
495     FIXME("(%p,%p): stub\n", iface, ppIThumbnail);
496     return WINCODEC_ERR_CODECNOTHUMBNAIL;
497 }
498 
499 static HRESULT WINAPI JpegDecoder_GetFrameCount(IWICBitmapDecoder *iface,
500     UINT *pCount)
501 {
502     if (!pCount) return E_INVALIDARG;
503 
504     *pCount = 1;
505     return S_OK;
506 }
507 
508 static HRESULT WINAPI JpegDecoder_GetFrame(IWICBitmapDecoder *iface,
509     UINT index, IWICBitmapFrameDecode **ppIBitmapFrame)
510 {
511     JpegDecoder *This = impl_from_IWICBitmapDecoder(iface);
512     TRACE("(%p,%u,%p)\n", iface, index, ppIBitmapFrame);
513 
514     if (!This->initialized) return WINCODEC_ERR_FRAMEMISSING;
515 
516     if (index != 0) return E_INVALIDARG;
517 
518     IWICBitmapDecoder_AddRef(iface);
519     *ppIBitmapFrame = &This->IWICBitmapFrameDecode_iface;
520 
521     return S_OK;
522 }
523 
524 static const IWICBitmapDecoderVtbl JpegDecoder_Vtbl = {
525     JpegDecoder_QueryInterface,
526     JpegDecoder_AddRef,
527     JpegDecoder_Release,
528     JpegDecoder_QueryCapability,
529     JpegDecoder_Initialize,
530     JpegDecoder_GetContainerFormat,
531     JpegDecoder_GetDecoderInfo,
532     JpegDecoder_CopyPalette,
533     JpegDecoder_GetMetadataQueryReader,
534     JpegDecoder_GetPreview,
535     JpegDecoder_GetColorContexts,
536     JpegDecoder_GetThumbnail,
537     JpegDecoder_GetFrameCount,
538     JpegDecoder_GetFrame
539 };
540 
541 static HRESULT WINAPI JpegDecoder_Frame_QueryInterface(IWICBitmapFrameDecode *iface, REFIID iid,
542     void **ppv)
543 {
544     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
545 
546     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
547 
548     if (!ppv) return E_INVALIDARG;
549 
550     if (IsEqualIID(&IID_IUnknown, iid) ||
551         IsEqualIID(&IID_IWICBitmapSource, iid) ||
552         IsEqualIID(&IID_IWICBitmapFrameDecode, iid))
553     {
554         *ppv = &This->IWICBitmapFrameDecode_iface;
555     }
556     else
557     {
558         *ppv = NULL;
559         return E_NOINTERFACE;
560     }
561 
562     IUnknown_AddRef((IUnknown*)*ppv);
563     return S_OK;
564 }
565 
566 static ULONG WINAPI JpegDecoder_Frame_AddRef(IWICBitmapFrameDecode *iface)
567 {
568     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
569     return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
570 }
571 
572 static ULONG WINAPI JpegDecoder_Frame_Release(IWICBitmapFrameDecode *iface)
573 {
574     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
575     return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
576 }
577 
578 static HRESULT WINAPI JpegDecoder_Frame_GetSize(IWICBitmapFrameDecode *iface,
579     UINT *puiWidth, UINT *puiHeight)
580 {
581     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
582     *puiWidth = This->cinfo.output_width;
583     *puiHeight = This->cinfo.output_height;
584     TRACE("(%p)->(%u,%u)\n", iface, *puiWidth, *puiHeight);
585     return S_OK;
586 }
587 
588 static HRESULT WINAPI JpegDecoder_Frame_GetPixelFormat(IWICBitmapFrameDecode *iface,
589     WICPixelFormatGUID *pPixelFormat)
590 {
591     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
592     TRACE("(%p,%p)\n", iface, pPixelFormat);
593     if (This->cinfo.out_color_space == JCS_RGB)
594         memcpy(pPixelFormat, &GUID_WICPixelFormat24bppBGR, sizeof(GUID));
595     else if (This->cinfo.out_color_space == JCS_CMYK)
596         memcpy(pPixelFormat, &GUID_WICPixelFormat32bppCMYK, sizeof(GUID));
597     else /* This->cinfo.out_color_space == JCS_GRAYSCALE */
598         memcpy(pPixelFormat, &GUID_WICPixelFormat8bppGray, sizeof(GUID));
599     return S_OK;
600 }
601 
602 static HRESULT WINAPI JpegDecoder_Frame_GetResolution(IWICBitmapFrameDecode *iface,
603     double *pDpiX, double *pDpiY)
604 {
605     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
606 
607     EnterCriticalSection(&This->lock);
608 
609     switch (This->cinfo.density_unit)
610     {
611     case 2: /* pixels per centimeter */
612         *pDpiX = This->cinfo.X_density * 2.54;
613         *pDpiY = This->cinfo.Y_density * 2.54;
614         break;
615 
616     case 1: /* pixels per inch */
617         *pDpiX = This->cinfo.X_density;
618         *pDpiY = This->cinfo.Y_density;
619         break;
620 
621     case 0: /* unknown */
622     default:
623         *pDpiX = 96.0;
624         *pDpiY = 96.0;
625         break;
626     }
627 
628     LeaveCriticalSection(&This->lock);
629 
630     TRACE("(%p)->(%0.2f,%0.2f)\n", iface, *pDpiX, *pDpiY);
631 
632     return S_OK;
633 }
634 
635 static HRESULT WINAPI JpegDecoder_Frame_CopyPalette(IWICBitmapFrameDecode *iface,
636     IWICPalette *pIPalette)
637 {
638     FIXME("(%p,%p): stub\n", iface, pIPalette);
639     return E_NOTIMPL;
640 }
641 
642 static HRESULT WINAPI JpegDecoder_Frame_CopyPixels(IWICBitmapFrameDecode *iface,
643     const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer)
644 {
645     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
646 
647     TRACE("(%p,%s,%u,%u,%p)\n", iface, debug_wic_rect(prc), cbStride, cbBufferSize, pbBuffer);
648 
649     return copy_pixels(This->bpp, This->image_data,
650         This->cinfo.output_width, This->cinfo.output_height, This->stride,
651         prc, cbStride, cbBufferSize, pbBuffer);
652 }
653 
654 static HRESULT WINAPI JpegDecoder_Frame_GetMetadataQueryReader(IWICBitmapFrameDecode *iface,
655     IWICMetadataQueryReader **ppIMetadataQueryReader)
656 {
657     JpegDecoder *This = impl_from_IWICBitmapFrameDecode(iface);
658 
659     TRACE("(%p,%p)\n", iface, ppIMetadataQueryReader);
660 
661     if (!ppIMetadataQueryReader)
662         return E_INVALIDARG;
663 
664     return MetadataQueryReader_CreateInstance(&This->IWICMetadataBlockReader_iface, NULL, ppIMetadataQueryReader);
665 }
666 
667 static HRESULT WINAPI JpegDecoder_Frame_GetColorContexts(IWICBitmapFrameDecode *iface,
668     UINT cCount, IWICColorContext **ppIColorContexts, UINT *pcActualCount)
669 {
670     FIXME("(%p,%u,%p,%p): stub\n", iface, cCount, ppIColorContexts, pcActualCount);
671     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
672 }
673 
674 static HRESULT WINAPI JpegDecoder_Frame_GetThumbnail(IWICBitmapFrameDecode *iface,
675     IWICBitmapSource **ppIThumbnail)
676 {
677     FIXME("(%p,%p): stub\n", iface, ppIThumbnail);
678     return WINCODEC_ERR_CODECNOTHUMBNAIL;
679 }
680 
681 static const IWICBitmapFrameDecodeVtbl JpegDecoder_Frame_Vtbl = {
682     JpegDecoder_Frame_QueryInterface,
683     JpegDecoder_Frame_AddRef,
684     JpegDecoder_Frame_Release,
685     JpegDecoder_Frame_GetSize,
686     JpegDecoder_Frame_GetPixelFormat,
687     JpegDecoder_Frame_GetResolution,
688     JpegDecoder_Frame_CopyPalette,
689     JpegDecoder_Frame_CopyPixels,
690     JpegDecoder_Frame_GetMetadataQueryReader,
691     JpegDecoder_Frame_GetColorContexts,
692     JpegDecoder_Frame_GetThumbnail
693 };
694 
695 static HRESULT WINAPI JpegDecoder_Block_QueryInterface(IWICMetadataBlockReader *iface, REFIID iid,
696     void **ppv)
697 {
698     JpegDecoder *This = impl_from_IWICMetadataBlockReader(iface);
699     return IWICBitmapFrameDecode_QueryInterface(&This->IWICBitmapFrameDecode_iface, iid, ppv);
700 }
701 
702 static ULONG WINAPI JpegDecoder_Block_AddRef(IWICMetadataBlockReader *iface)
703 {
704     JpegDecoder *This = impl_from_IWICMetadataBlockReader(iface);
705     return IWICBitmapDecoder_AddRef(&This->IWICBitmapDecoder_iface);
706 }
707 
708 static ULONG WINAPI JpegDecoder_Block_Release(IWICMetadataBlockReader *iface)
709 {
710     JpegDecoder *This = impl_from_IWICMetadataBlockReader(iface);
711     return IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
712 }
713 
714 static HRESULT WINAPI JpegDecoder_Block_GetContainerFormat(IWICMetadataBlockReader *iface,
715     GUID *pguidContainerFormat)
716 {
717     TRACE("%p,%p\n", iface, pguidContainerFormat);
718 
719     if (!pguidContainerFormat) return E_INVALIDARG;
720 
721     memcpy(pguidContainerFormat, &GUID_ContainerFormatJpeg, sizeof(GUID));
722 
723     return S_OK;
724 }
725 
726 static HRESULT WINAPI JpegDecoder_Block_GetCount(IWICMetadataBlockReader *iface,
727     UINT *pcCount)
728 {
729     FIXME("%p,%p\n", iface, pcCount);
730 
731     if (!pcCount) return E_INVALIDARG;
732 
733     *pcCount = 0;
734 
735     return S_OK;
736 }
737 
738 static HRESULT WINAPI JpegDecoder_Block_GetReaderByIndex(IWICMetadataBlockReader *iface,
739     UINT nIndex, IWICMetadataReader **ppIMetadataReader)
740 {
741     FIXME("%p,%d,%p\n", iface, nIndex, ppIMetadataReader);
742     return E_INVALIDARG;
743 }
744 
745 static HRESULT WINAPI JpegDecoder_Block_GetEnumerator(IWICMetadataBlockReader *iface,
746     IEnumUnknown **ppIEnumMetadata)
747 {
748     FIXME("%p,%p\n", iface, ppIEnumMetadata);
749     return E_NOTIMPL;
750 }
751 
752 static const IWICMetadataBlockReaderVtbl JpegDecoder_Block_Vtbl = {
753     JpegDecoder_Block_QueryInterface,
754     JpegDecoder_Block_AddRef,
755     JpegDecoder_Block_Release,
756     JpegDecoder_Block_GetContainerFormat,
757     JpegDecoder_Block_GetCount,
758     JpegDecoder_Block_GetReaderByIndex,
759     JpegDecoder_Block_GetEnumerator,
760 };
761 
762 HRESULT JpegDecoder_CreateInstance(REFIID iid, void** ppv)
763 {
764     JpegDecoder *This;
765     HRESULT ret;
766 
767     TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
768 
769     if (!libjpeg_handle && !load_libjpeg())
770     {
771         ERR("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG);
772         return E_FAIL;
773     }
774 
775     *ppv = NULL;
776 
777     This = HeapAlloc(GetProcessHeap(), 0, sizeof(JpegDecoder));
778     if (!This) return E_OUTOFMEMORY;
779 
780     This->IWICBitmapDecoder_iface.lpVtbl = &JpegDecoder_Vtbl;
781     This->IWICBitmapFrameDecode_iface.lpVtbl = &JpegDecoder_Frame_Vtbl;
782     This->IWICMetadataBlockReader_iface.lpVtbl = &JpegDecoder_Block_Vtbl;
783     This->ref = 1;
784     This->initialized = FALSE;
785     This->cinfo_initialized = FALSE;
786     This->stream = NULL;
787     This->image_data = NULL;
788     InitializeCriticalSection(&This->lock);
789     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JpegDecoder.lock");
790 
791     ret = IWICBitmapDecoder_QueryInterface(&This->IWICBitmapDecoder_iface, iid, ppv);
792     IWICBitmapDecoder_Release(&This->IWICBitmapDecoder_iface);
793 
794     return ret;
795 }
796 
797 typedef struct jpeg_compress_format {
798     const WICPixelFormatGUID *guid;
799     int bpp;
800     int num_components;
801     J_COLOR_SPACE color_space;
802     int swap_rgb;
803 } jpeg_compress_format;
804 
805 static const jpeg_compress_format compress_formats[] = {
806     { &GUID_WICPixelFormat24bppBGR, 24, 3, JCS_RGB, 1 },
807     { &GUID_WICPixelFormat32bppCMYK, 32, 4, JCS_CMYK },
808     { &GUID_WICPixelFormat8bppGray, 8, 1, JCS_GRAYSCALE },
809     { 0 }
810 };
811 
812 typedef struct JpegEncoder {
813     IWICBitmapEncoder IWICBitmapEncoder_iface;
814     IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
815     LONG ref;
816     struct jpeg_compress_struct cinfo;
817     struct jpeg_error_mgr jerr;
818     struct jpeg_destination_mgr dest_mgr;
819     BOOL initialized;
820     int frame_count;
821     BOOL frame_initialized;
822     BOOL started_compress;
823     int lines_written;
824     BOOL frame_committed;
825     BOOL committed;
826     UINT width, height;
827     double xres, yres;
828     const jpeg_compress_format *format;
829     IStream *stream;
830     WICColor palette[256];
831     UINT colors;
832     CRITICAL_SECTION lock;
833     BYTE dest_buffer[1024];
834 } JpegEncoder;
835 
836 static inline JpegEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
837 {
838     return CONTAINING_RECORD(iface, JpegEncoder, IWICBitmapEncoder_iface);
839 }
840 
841 static inline JpegEncoder *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
842 {
843     return CONTAINING_RECORD(iface, JpegEncoder, IWICBitmapFrameEncode_iface);
844 }
845 
846 static inline JpegEncoder *encoder_from_compress(j_compress_ptr compress)
847 {
848     return CONTAINING_RECORD(compress, JpegEncoder, cinfo);
849 }
850 
851 static void dest_mgr_init_destination(j_compress_ptr cinfo)
852 {
853     JpegEncoder *This = encoder_from_compress(cinfo);
854 
855     This->dest_mgr.next_output_byte = This->dest_buffer;
856     This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer);
857 }
858 
859 static jpeg_boolean dest_mgr_empty_output_buffer(j_compress_ptr cinfo)
860 {
861     JpegEncoder *This = encoder_from_compress(cinfo);
862     HRESULT hr;
863     ULONG byteswritten;
864 
865     hr = IStream_Write(This->stream, This->dest_buffer,
866         sizeof(This->dest_buffer), &byteswritten);
867 
868     if (hr != S_OK || byteswritten == 0)
869     {
870         ERR("Failed writing data, hr=%x\n", hr);
871         return FALSE;
872     }
873 
874     This->dest_mgr.next_output_byte = This->dest_buffer;
875     This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer);
876     return TRUE;
877 }
878 
879 static void dest_mgr_term_destination(j_compress_ptr cinfo)
880 {
881     JpegEncoder *This = encoder_from_compress(cinfo);
882     ULONG byteswritten;
883     HRESULT hr;
884 
885     if (This->dest_mgr.free_in_buffer != sizeof(This->dest_buffer))
886     {
887         hr = IStream_Write(This->stream, This->dest_buffer,
888             sizeof(This->dest_buffer) - This->dest_mgr.free_in_buffer, &byteswritten);
889 
890         if (hr != S_OK || byteswritten == 0)
891             ERR("Failed writing data, hr=%x\n", hr);
892     }
893 }
894 
895 static HRESULT WINAPI JpegEncoder_Frame_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
896     void **ppv)
897 {
898     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
899     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
900 
901     if (!ppv) return E_INVALIDARG;
902 
903     if (IsEqualIID(&IID_IUnknown, iid) ||
904         IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
905     {
906         *ppv = &This->IWICBitmapFrameEncode_iface;
907     }
908     else
909     {
910         *ppv = NULL;
911         return E_NOINTERFACE;
912     }
913 
914     IUnknown_AddRef((IUnknown*)*ppv);
915     return S_OK;
916 }
917 
918 static ULONG WINAPI JpegEncoder_Frame_AddRef(IWICBitmapFrameEncode *iface)
919 {
920     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
921     return IWICBitmapEncoder_AddRef(&This->IWICBitmapEncoder_iface);
922 }
923 
924 static ULONG WINAPI JpegEncoder_Frame_Release(IWICBitmapFrameEncode *iface)
925 {
926     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
927     return IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
928 }
929 
930 static HRESULT WINAPI JpegEncoder_Frame_Initialize(IWICBitmapFrameEncode *iface,
931     IPropertyBag2 *pIEncoderOptions)
932 {
933     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
934     TRACE("(%p,%p)\n", iface, pIEncoderOptions);
935 
936     EnterCriticalSection(&This->lock);
937 
938     if (This->frame_initialized)
939     {
940         LeaveCriticalSection(&This->lock);
941         return WINCODEC_ERR_WRONGSTATE;
942     }
943 
944     This->frame_initialized = TRUE;
945 
946     LeaveCriticalSection(&This->lock);
947 
948     return S_OK;
949 }
950 
951 static HRESULT WINAPI JpegEncoder_Frame_SetSize(IWICBitmapFrameEncode *iface,
952     UINT uiWidth, UINT uiHeight)
953 {
954     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
955     TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
956 
957     EnterCriticalSection(&This->lock);
958 
959     if (!This->frame_initialized || This->started_compress)
960     {
961         LeaveCriticalSection(&This->lock);
962         return WINCODEC_ERR_WRONGSTATE;
963     }
964 
965     This->width = uiWidth;
966     This->height = uiHeight;
967 
968     LeaveCriticalSection(&This->lock);
969 
970     return S_OK;
971 }
972 
973 static HRESULT WINAPI JpegEncoder_Frame_SetResolution(IWICBitmapFrameEncode *iface,
974     double dpiX, double dpiY)
975 {
976     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
977     TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
978 
979     EnterCriticalSection(&This->lock);
980 
981     if (!This->frame_initialized || This->started_compress)
982     {
983         LeaveCriticalSection(&This->lock);
984         return WINCODEC_ERR_WRONGSTATE;
985     }
986 
987     This->xres = dpiX;
988     This->yres = dpiY;
989 
990     LeaveCriticalSection(&This->lock);
991 
992     return S_OK;
993 }
994 
995 static HRESULT WINAPI JpegEncoder_Frame_SetPixelFormat(IWICBitmapFrameEncode *iface,
996     WICPixelFormatGUID *pPixelFormat)
997 {
998     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
999     int i;
1000     TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
1001 
1002     EnterCriticalSection(&This->lock);
1003 
1004     if (!This->frame_initialized || This->started_compress)
1005     {
1006         LeaveCriticalSection(&This->lock);
1007         return WINCODEC_ERR_WRONGSTATE;
1008     }
1009 
1010     for (i=0; compress_formats[i].guid; i++)
1011     {
1012         if (memcmp(compress_formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
1013             break;
1014     }
1015 
1016     if (!compress_formats[i].guid) i = 0;
1017 
1018     This->format = &compress_formats[i];
1019     memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
1020 
1021     LeaveCriticalSection(&This->lock);
1022 
1023     return S_OK;
1024 }
1025 
1026 static HRESULT WINAPI JpegEncoder_Frame_SetColorContexts(IWICBitmapFrameEncode *iface,
1027     UINT cCount, IWICColorContext **ppIColorContext)
1028 {
1029     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1030     return E_NOTIMPL;
1031 }
1032 
1033 static HRESULT WINAPI JpegEncoder_Frame_SetPalette(IWICBitmapFrameEncode *iface,
1034     IWICPalette *palette)
1035 {
1036     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1037     HRESULT hr;
1038 
1039     TRACE("(%p,%p)\n", iface, palette);
1040 
1041     if (!palette) return E_INVALIDARG;
1042 
1043     EnterCriticalSection(&This->lock);
1044 
1045     if (This->frame_initialized)
1046         hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors);
1047     else
1048         hr = WINCODEC_ERR_NOTINITIALIZED;
1049 
1050     LeaveCriticalSection(&This->lock);
1051     return hr;
1052 }
1053 
1054 static HRESULT WINAPI JpegEncoder_Frame_SetThumbnail(IWICBitmapFrameEncode *iface,
1055     IWICBitmapSource *pIThumbnail)
1056 {
1057     FIXME("(%p,%p): stub\n", iface, pIThumbnail);
1058     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1059 }
1060 
1061 static HRESULT WINAPI JpegEncoder_Frame_WritePixels(IWICBitmapFrameEncode *iface,
1062     UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
1063 {
1064     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1065     jmp_buf jmpbuf;
1066     BYTE *swapped_data = NULL, *current_row;
1067     UINT line;
1068     int row_size;
1069     TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
1070 
1071     EnterCriticalSection(&This->lock);
1072 
1073     if (!This->frame_initialized || !This->width || !This->height || !This->format)
1074     {
1075         LeaveCriticalSection(&This->lock);
1076         return WINCODEC_ERR_WRONGSTATE;
1077     }
1078 
1079     if (lineCount == 0 || lineCount + This->lines_written > This->height)
1080     {
1081         LeaveCriticalSection(&This->lock);
1082         return E_INVALIDARG;
1083     }
1084 
1085     /* set up setjmp/longjmp error handling */
1086     if (setjmp(jmpbuf))
1087     {
1088         LeaveCriticalSection(&This->lock);
1089         HeapFree(GetProcessHeap(), 0, swapped_data);
1090         return E_FAIL;
1091     }
1092     This->cinfo.client_data = jmpbuf;
1093 
1094     if (!This->started_compress)
1095     {
1096         This->cinfo.image_width = This->width;
1097         This->cinfo.image_height = This->height;
1098         This->cinfo.input_components = This->format->num_components;
1099         This->cinfo.in_color_space = This->format->color_space;
1100 
1101         pjpeg_set_defaults(&This->cinfo);
1102 
1103         if (This->xres != 0.0 && This->yres != 0.0)
1104         {
1105             This->cinfo.density_unit = 1; /* dots per inch */
1106             This->cinfo.X_density = This->xres;
1107             This->cinfo.Y_density = This->yres;
1108         }
1109 
1110         pjpeg_start_compress(&This->cinfo, TRUE);
1111 
1112         This->started_compress = TRUE;
1113     }
1114 
1115     row_size = This->format->bpp / 8 * This->width;
1116 
1117     if (This->format->swap_rgb)
1118     {
1119         swapped_data = HeapAlloc(GetProcessHeap(), 0, row_size);
1120         if (!swapped_data)
1121         {
1122             LeaveCriticalSection(&This->lock);
1123             return E_OUTOFMEMORY;
1124         }
1125     }
1126 
1127     for (line=0; line < lineCount; line++)
1128     {
1129         if (This->format->swap_rgb)
1130         {
1131             UINT x;
1132 
1133             memcpy(swapped_data, pbPixels + (cbStride * line), row_size);
1134 
1135             for (x=0; x < This->width; x++)
1136             {
1137                 BYTE b;
1138 
1139                 b = swapped_data[x*3];
1140                 swapped_data[x*3] = swapped_data[x*3+2];
1141                 swapped_data[x*3+2] = b;
1142             }
1143 
1144             current_row = swapped_data;
1145         }
1146         else
1147             current_row = pbPixels + (cbStride * line);
1148 
1149         if (!pjpeg_write_scanlines(&This->cinfo, &current_row, 1))
1150         {
1151             ERR("failed writing scanlines\n");
1152             LeaveCriticalSection(&This->lock);
1153             HeapFree(GetProcessHeap(), 0, swapped_data);
1154             return E_FAIL;
1155         }
1156 
1157         This->lines_written++;
1158     }
1159 
1160     LeaveCriticalSection(&This->lock);
1161     HeapFree(GetProcessHeap(), 0, swapped_data);
1162 
1163     return S_OK;
1164 }
1165 
1166 static HRESULT WINAPI JpegEncoder_Frame_WriteSource(IWICBitmapFrameEncode *iface,
1167     IWICBitmapSource *pIBitmapSource, WICRect *prc)
1168 {
1169     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1170     HRESULT hr;
1171     TRACE("(%p,%p,%s)\n", iface, pIBitmapSource, debug_wic_rect(prc));
1172 
1173     if (!This->frame_initialized)
1174         return WINCODEC_ERR_WRONGSTATE;
1175 
1176     hr = configure_write_source(iface, pIBitmapSource, prc,
1177         This->format ? This->format->guid : NULL, This->width, This->height,
1178         This->xres, This->yres);
1179 
1180     if (SUCCEEDED(hr))
1181     {
1182         hr = write_source(iface, pIBitmapSource, prc,
1183             This->format->guid, This->format->bpp, This->width, This->height);
1184     }
1185 
1186     return hr;
1187 }
1188 
1189 static HRESULT WINAPI JpegEncoder_Frame_Commit(IWICBitmapFrameEncode *iface)
1190 {
1191     JpegEncoder *This = impl_from_IWICBitmapFrameEncode(iface);
1192     jmp_buf jmpbuf;
1193     TRACE("(%p)\n", iface);
1194 
1195     EnterCriticalSection(&This->lock);
1196 
1197     if (!This->started_compress || This->lines_written != This->height || This->frame_committed)
1198     {
1199         LeaveCriticalSection(&This->lock);
1200         return WINCODEC_ERR_WRONGSTATE;
1201     }
1202 
1203     /* set up setjmp/longjmp error handling */
1204     if (setjmp(jmpbuf))
1205     {
1206         LeaveCriticalSection(&This->lock);
1207         return E_FAIL;
1208     }
1209     This->cinfo.client_data = jmpbuf;
1210 
1211     pjpeg_finish_compress(&This->cinfo);
1212 
1213     This->frame_committed = TRUE;
1214 
1215     LeaveCriticalSection(&This->lock);
1216 
1217     return S_OK;
1218 }
1219 
1220 static HRESULT WINAPI JpegEncoder_Frame_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
1221     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1222 {
1223     FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
1224     return E_NOTIMPL;
1225 }
1226 
1227 static const IWICBitmapFrameEncodeVtbl JpegEncoder_FrameVtbl = {
1228     JpegEncoder_Frame_QueryInterface,
1229     JpegEncoder_Frame_AddRef,
1230     JpegEncoder_Frame_Release,
1231     JpegEncoder_Frame_Initialize,
1232     JpegEncoder_Frame_SetSize,
1233     JpegEncoder_Frame_SetResolution,
1234     JpegEncoder_Frame_SetPixelFormat,
1235     JpegEncoder_Frame_SetColorContexts,
1236     JpegEncoder_Frame_SetPalette,
1237     JpegEncoder_Frame_SetThumbnail,
1238     JpegEncoder_Frame_WritePixels,
1239     JpegEncoder_Frame_WriteSource,
1240     JpegEncoder_Frame_Commit,
1241     JpegEncoder_Frame_GetMetadataQueryWriter
1242 };
1243 
1244 static HRESULT WINAPI JpegEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
1245     void **ppv)
1246 {
1247     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
1248     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
1249 
1250     if (!ppv) return E_INVALIDARG;
1251 
1252     if (IsEqualIID(&IID_IUnknown, iid) ||
1253         IsEqualIID(&IID_IWICBitmapEncoder, iid))
1254     {
1255         *ppv = &This->IWICBitmapEncoder_iface;
1256     }
1257     else
1258     {
1259         *ppv = NULL;
1260         return E_NOINTERFACE;
1261     }
1262 
1263     IUnknown_AddRef((IUnknown*)*ppv);
1264     return S_OK;
1265 }
1266 
1267 static ULONG WINAPI JpegEncoder_AddRef(IWICBitmapEncoder *iface)
1268 {
1269     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
1270     ULONG ref = InterlockedIncrement(&This->ref);
1271 
1272     TRACE("(%p) refcount=%u\n", iface, ref);
1273 
1274     return ref;
1275 }
1276 
1277 static ULONG WINAPI JpegEncoder_Release(IWICBitmapEncoder *iface)
1278 {
1279     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
1280     ULONG ref = InterlockedDecrement(&This->ref);
1281 
1282     TRACE("(%p) refcount=%u\n", iface, ref);
1283 
1284     if (ref == 0)
1285     {
1286         This->lock.DebugInfo->Spare[0] = 0;
1287         DeleteCriticalSection(&This->lock);
1288         if (This->initialized) pjpeg_destroy_compress(&This->cinfo);
1289         if (This->stream) IStream_Release(This->stream);
1290         HeapFree(GetProcessHeap(), 0, This);
1291     }
1292 
1293     return ref;
1294 }
1295 
1296 static HRESULT WINAPI JpegEncoder_Initialize(IWICBitmapEncoder *iface,
1297     IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
1298 {
1299     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
1300     jmp_buf jmpbuf;
1301 
1302     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
1303 
1304     EnterCriticalSection(&This->lock);
1305 
1306     if (This->initialized)
1307     {
1308         LeaveCriticalSection(&This->lock);
1309         return WINCODEC_ERR_WRONGSTATE;
1310     }
1311 
1312     pjpeg_std_error(&This->jerr);
1313 
1314     This->jerr.error_exit = error_exit_fn;
1315     This->jerr.emit_message = emit_message_fn;
1316 
1317     This->cinfo.err = &This->jerr;
1318 
1319     This->cinfo.client_data = jmpbuf;
1320 
1321     if (setjmp(jmpbuf))
1322     {
1323         LeaveCriticalSection(&This->lock);
1324         return E_FAIL;
1325     }
1326 
1327     pjpeg_CreateCompress(&This->cinfo, JPEG_LIB_VERSION, sizeof(struct jpeg_compress_struct));
1328 
1329     This->stream = pIStream;
1330     IStream_AddRef(pIStream);
1331 
1332     This->dest_mgr.next_output_byte = This->dest_buffer;
1333     This->dest_mgr.free_in_buffer = sizeof(This->dest_buffer);
1334 
1335     This->dest_mgr.init_destination = dest_mgr_init_destination;
1336     This->dest_mgr.empty_output_buffer = dest_mgr_empty_output_buffer;
1337     This->dest_mgr.term_destination = dest_mgr_term_destination;
1338 
1339     This->cinfo.dest = &This->dest_mgr;
1340 
1341     This->initialized = TRUE;
1342 
1343     LeaveCriticalSection(&This->lock);
1344 
1345     return S_OK;
1346 }
1347 
1348 static HRESULT WINAPI JpegEncoder_GetContainerFormat(IWICBitmapEncoder *iface, GUID *format)
1349 {
1350     TRACE("(%p,%p)\n", iface, format);
1351 
1352     if (!format)
1353         return E_INVALIDARG;
1354 
1355     memcpy(format, &GUID_ContainerFormatJpeg, sizeof(*format));
1356     return S_OK;
1357 }
1358 
1359 static HRESULT WINAPI JpegEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info)
1360 {
1361     IWICComponentInfo *comp_info;
1362     HRESULT hr;
1363 
1364     TRACE("%p,%p\n", iface, info);
1365 
1366     if (!info) return E_INVALIDARG;
1367 
1368     hr = CreateComponentInfo(&CLSID_WICJpegEncoder, &comp_info);
1369     if (hr == S_OK)
1370     {
1371         hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info);
1372         IWICComponentInfo_Release(comp_info);
1373     }
1374     return hr;
1375 }
1376 
1377 static HRESULT WINAPI JpegEncoder_SetColorContexts(IWICBitmapEncoder *iface,
1378     UINT cCount, IWICColorContext **ppIColorContext)
1379 {
1380     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
1381     return E_NOTIMPL;
1382 }
1383 
1384 static HRESULT WINAPI JpegEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette)
1385 {
1386     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
1387     HRESULT hr;
1388 
1389     TRACE("(%p,%p)\n", iface, pIPalette);
1390 
1391     EnterCriticalSection(&This->lock);
1392 
1393     hr = This->initialized ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED;
1394 
1395     LeaveCriticalSection(&This->lock);
1396 
1397     return hr;
1398 }
1399 
1400 static HRESULT WINAPI JpegEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
1401 {
1402     TRACE("(%p,%p)\n", iface, pIThumbnail);
1403     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1404 }
1405 
1406 static HRESULT WINAPI JpegEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
1407 {
1408     TRACE("(%p,%p)\n", iface, pIPreview);
1409     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1410 }
1411 
1412 static HRESULT WINAPI JpegEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
1413     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
1414 {
1415     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
1416     HRESULT hr;
1417     static const PROPBAG2 opts[6] =
1418     {
1419         { PROPBAG2_TYPE_DATA, VT_R4,            0, 0, (LPOLESTR)wszImageQuality },
1420         { PROPBAG2_TYPE_DATA, VT_UI1,           0, 0, (LPOLESTR)wszBitmapTransform },
1421         { PROPBAG2_TYPE_DATA, VT_I4 | VT_ARRAY, 0, 0, (LPOLESTR)wszLuminance },
1422         { PROPBAG2_TYPE_DATA, VT_I4 | VT_ARRAY, 0, 0, (LPOLESTR)wszChrominance },
1423         { PROPBAG2_TYPE_DATA, VT_UI1,           0, 0, (LPOLESTR)wszJpegYCrCbSubsampling },
1424         { PROPBAG2_TYPE_DATA, VT_BOOL,          0, 0, (LPOLESTR)wszSuppressApp0 },
1425     };
1426 
1427     TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
1428 
1429     EnterCriticalSection(&This->lock);
1430 
1431     if (This->frame_count != 0)
1432     {
1433         LeaveCriticalSection(&This->lock);
1434         return WINCODEC_ERR_UNSUPPORTEDOPERATION;
1435     }
1436 
1437     if (!This->initialized)
1438     {
1439         LeaveCriticalSection(&This->lock);
1440         return WINCODEC_ERR_NOTINITIALIZED;
1441     }
1442 
1443     if (ppIEncoderOptions)
1444     {
1445         hr = CreatePropertyBag2(opts, ARRAY_SIZE(opts), ppIEncoderOptions);
1446         if (FAILED(hr))
1447         {
1448             LeaveCriticalSection(&This->lock);
1449             return hr;
1450         }
1451     }
1452 
1453     This->frame_count = 1;
1454 
1455     LeaveCriticalSection(&This->lock);
1456 
1457     IWICBitmapEncoder_AddRef(iface);
1458     *ppIFrameEncode = &This->IWICBitmapFrameEncode_iface;
1459 
1460     return S_OK;
1461 }
1462 
1463 static HRESULT WINAPI JpegEncoder_Commit(IWICBitmapEncoder *iface)
1464 {
1465     JpegEncoder *This = impl_from_IWICBitmapEncoder(iface);
1466     TRACE("(%p)\n", iface);
1467 
1468     EnterCriticalSection(&This->lock);
1469 
1470     if (!This->frame_committed || This->committed)
1471     {
1472         LeaveCriticalSection(&This->lock);
1473         return WINCODEC_ERR_WRONGSTATE;
1474     }
1475 
1476     This->committed = TRUE;
1477 
1478     LeaveCriticalSection(&This->lock);
1479 
1480     return S_OK;
1481 }
1482 
1483 static HRESULT WINAPI JpegEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
1484     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
1485 {
1486     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
1487     return E_NOTIMPL;
1488 }
1489 
1490 static const IWICBitmapEncoderVtbl JpegEncoder_Vtbl = {
1491     JpegEncoder_QueryInterface,
1492     JpegEncoder_AddRef,
1493     JpegEncoder_Release,
1494     JpegEncoder_Initialize,
1495     JpegEncoder_GetContainerFormat,
1496     JpegEncoder_GetEncoderInfo,
1497     JpegEncoder_SetColorContexts,
1498     JpegEncoder_SetPalette,
1499     JpegEncoder_SetThumbnail,
1500     JpegEncoder_SetPreview,
1501     JpegEncoder_CreateNewFrame,
1502     JpegEncoder_Commit,
1503     JpegEncoder_GetMetadataQueryWriter
1504 };
1505 
1506 HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv)
1507 {
1508     JpegEncoder *This;
1509     HRESULT ret;
1510 
1511     TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
1512 
1513     *ppv = NULL;
1514 
1515     if (!libjpeg_handle && !load_libjpeg())
1516     {
1517         ERR("Failed writing JPEG because unable to find %s\n",SONAME_LIBJPEG);
1518         return E_FAIL;
1519     }
1520 
1521     This = HeapAlloc(GetProcessHeap(), 0, sizeof(JpegEncoder));
1522     if (!This) return E_OUTOFMEMORY;
1523 
1524     This->IWICBitmapEncoder_iface.lpVtbl = &JpegEncoder_Vtbl;
1525     This->IWICBitmapFrameEncode_iface.lpVtbl = &JpegEncoder_FrameVtbl;
1526     This->ref = 1;
1527     This->initialized = FALSE;
1528     This->frame_count = 0;
1529     This->frame_initialized = FALSE;
1530     This->started_compress = FALSE;
1531     This->lines_written = 0;
1532     This->frame_committed = FALSE;
1533     This->committed = FALSE;
1534     This->width = This->height = 0;
1535     This->xres = This->yres = 0.0;
1536     This->format = NULL;
1537     This->stream = NULL;
1538     This->colors = 0;
1539     InitializeCriticalSection(&This->lock);
1540     This->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JpegEncoder.lock");
1541 
1542     ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
1543     IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
1544 
1545     return ret;
1546 }
1547 
1548 #else /* !defined(SONAME_LIBJPEG) */
1549 
1550 HRESULT JpegDecoder_CreateInstance(REFIID iid, void** ppv)
1551 {
1552     ERR("Trying to load JPEG picture, but JPEG support is not compiled in.\n");
1553     return E_FAIL;
1554 }
1555 
1556 HRESULT JpegEncoder_CreateInstance(REFIID iid, void** ppv)
1557 {
1558     ERR("Trying to save JPEG picture, but JPEG support is not compiled in.\n");
1559     return E_FAIL;
1560 }
1561 
1562 #endif
1563