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