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