2 * Copyright 2000 Corel Corporation
3 * Copyright 2006 Marcus Meissner
4 * Copyright 2006 CodeWeavers, Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
23 #include "wine/library.h"
33 #include "gphoto2_i.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(twain);
39 static void *libjpeg_handle;
40 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
41 MAKE_FUNCPTR(jpeg_std_error);
42 MAKE_FUNCPTR(jpeg_CreateDecompress);
43 MAKE_FUNCPTR(jpeg_read_header);
44 MAKE_FUNCPTR(jpeg_start_decompress);
45 MAKE_FUNCPTR(jpeg_read_scanlines);
46 MAKE_FUNCPTR(jpeg_finish_decompress);
47 MAKE_FUNCPTR(jpeg_destroy_decompress);
50 static void *load_libjpeg(void)
52 if((libjpeg_handle = wine_dlopen(SONAME_LIBJPEG, RTLD_NOW, NULL, 0)) != NULL) {
54 #define LOAD_FUNCPTR(f) \
55 if((p##f = wine_dlsym(libjpeg_handle, #f, NULL, 0)) == NULL) { \
56 libjpeg_handle = NULL; \
60 LOAD_FUNCPTR(jpeg_std_error);
61 LOAD_FUNCPTR(jpeg_CreateDecompress);
62 LOAD_FUNCPTR(jpeg_read_header);
63 LOAD_FUNCPTR(jpeg_start_decompress);
64 LOAD_FUNCPTR(jpeg_read_scanlines);
65 LOAD_FUNCPTR(jpeg_finish_decompress);
66 LOAD_FUNCPTR(jpeg_destroy_decompress);
69 return libjpeg_handle;
73 /* for the jpeg decompressor source manager. */
74 static void _jpeg_init_source(j_decompress_ptr cinfo) { }
76 static boolean _jpeg_fill_input_buffer(j_decompress_ptr cinfo) {
77 ERR("(), should not get here.\n");
81 static void _jpeg_skip_input_data(j_decompress_ptr cinfo,long num_bytes) {
82 TRACE("Skipping %ld bytes...\n", num_bytes);
83 cinfo->src->next_input_byte += num_bytes;
84 cinfo->src->bytes_in_buffer -= num_bytes;
87 static boolean _jpeg_resync_to_restart(j_decompress_ptr cinfo, int desired) {
88 ERR("(desired=%d), should not get here.\n",desired);
91 static void _jpeg_term_source(j_decompress_ptr cinfo) { }
94 /* DG_IMAGE/DAT_CIECOLOR/MSG_GET */
95 TW_UINT16 GPHOTO2_CIEColorGet (pTW_IDENTITY pOrigin,
103 /* DG_IMAGE/DAT_EXTIMAGEINFO/MSG_GET */
104 TW_UINT16 GPHOTO2_ExtImageInfoGet (pTW_IDENTITY pOrigin,
112 /* DG_IMAGE/DAT_GRAYRESPONSE/MSG_RESET */
113 TW_UINT16 GPHOTO2_GrayResponseReset (pTW_IDENTITY pOrigin,
121 /* DG_IMAGE/DAT_GRAYRESPONSE/MSG_SET */
122 TW_UINT16 GPHOTO2_GrayResponseSet (pTW_IDENTITY pOrigin,
130 /* DG_IMAGE/DAT_IMAGEFILEXFER/MSG_GET */
131 TW_UINT16 GPHOTO2_ImageFileXferGet (pTW_IDENTITY pOrigin,
140 static TW_UINT16 _get_image_and_startup_jpeg(void) {
141 const char *folder = NULL, *filename = NULL;
142 struct gphoto2_file *file;
143 const unsigned char *filedata;
144 unsigned long filesize;
147 if (activeDS.file) /* Already loaded. */
150 if(!libjpeg_handle) {
151 if(!load_libjpeg()) {
152 FIXME("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG);
158 LIST_FOR_EACH_ENTRY( file, &activeDS.files, struct gphoto2_file, entry ) {
159 if (strstr(file->filename,".JPG") || strstr(file->filename,".jpg")) {
160 filename = file->filename;
161 folder = file->folder;
162 TRACE("downloading %s/%s\n", folder, filename);
163 if (file->download) {
164 file->download = FALSE; /* mark as done */
169 gp_file_new (&activeDS.file);
170 ret = gp_camera_file_get(activeDS.camera, folder, filename, GP_FILE_TYPE_NORMAL,
171 activeDS.file, activeDS.context);
173 FIXME("Failed to get file?\n");
174 activeDS.twCC = TWCC_SEQERROR;
177 ret = gp_file_get_data_and_size (activeDS.file, (const char**)&filedata, &filesize);
179 FIXME("Failed to get file data?\n");
180 activeDS.twCC = TWCC_SEQERROR;
184 /* This is basically so we can use in-memory data for jpeg decompression.
185 * We need to have all the functions.
187 activeDS.xjsm.next_input_byte = filedata;
188 activeDS.xjsm.bytes_in_buffer = filesize;
189 activeDS.xjsm.init_source = _jpeg_init_source;
190 activeDS.xjsm.fill_input_buffer = _jpeg_fill_input_buffer;
191 activeDS.xjsm.skip_input_data = _jpeg_skip_input_data;
192 activeDS.xjsm.resync_to_restart = _jpeg_resync_to_restart;
193 activeDS.xjsm.term_source = _jpeg_term_source;
195 activeDS.jd.err = pjpeg_std_error(&activeDS.jerr);
196 /* jpeg_create_decompress is a macro that expands to jpeg_CreateDecompress - see jpeglib.h
197 * jpeg_create_decompress(&jd); */
198 pjpeg_CreateDecompress(&activeDS.jd, JPEG_LIB_VERSION, (size_t) sizeof(struct jpeg_decompress_struct));
199 activeDS.jd.src = &activeDS.xjsm;
200 ret=pjpeg_read_header(&activeDS.jd,TRUE);
201 activeDS.jd.out_color_space = JCS_RGB;
202 pjpeg_start_decompress(&activeDS.jd);
203 if (ret != JPEG_HEADER_OK) {
204 ERR("Jpeg image in stream has bad format, read header returned %d.\n",ret);
205 gp_file_unref (activeDS.file);
206 activeDS.file = NULL;
213 /* DG_IMAGE/DAT_IMAGEINFO/MSG_GET */
214 TW_UINT16 GPHOTO2_ImageInfoGet (pTW_IDENTITY pOrigin,
218 pTW_IMAGEINFO pImageInfo = (pTW_IMAGEINFO) pData;
220 TRACE("DG_IMAGE/DAT_IMAGEINFO/MSG_GET\n");
222 if (activeDS.currentState != 6 && activeDS.currentState != 7) {
223 activeDS.twCC = TWCC_SEQERROR;
226 if (TWRC_SUCCESS != _get_image_and_startup_jpeg()) {
227 FIXME("Failed to get an image\n");
228 activeDS.twCC = TWCC_SEQERROR;
231 if (activeDS.currentState == 6)
233 /* return general image description information about the image about to be transferred */
234 TRACE("Getting parameters\n");
236 TRACE("activeDS.jd.output_width = %d\n", activeDS.jd.output_width);
237 TRACE("activeDS.jd.output_height = %d\n", activeDS.jd.output_height);
238 pImageInfo->Compression = TWCP_NONE;
239 pImageInfo->SamplesPerPixel = 3;
240 pImageInfo->BitsPerSample[0]= 8;
241 pImageInfo->BitsPerSample[1]= 8;
242 pImageInfo->BitsPerSample[2]= 8;
243 pImageInfo->PixelType = TWPT_RGB;
244 pImageInfo->Planar = FALSE; /* R-G-B is chunky! */
245 pImageInfo->XResolution.Whole = -1;
246 pImageInfo->XResolution.Frac = 0;
247 pImageInfo->YResolution.Whole = -1;
248 pImageInfo->YResolution.Frac = 0;
249 pImageInfo->ImageWidth = activeDS.jd.output_width;
250 pImageInfo->ImageLength = activeDS.jd.output_height;
251 pImageInfo->BitsPerPixel = 24;
258 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET */
259 TW_UINT16 GPHOTO2_ImageLayoutGet (pTW_IDENTITY pOrigin,
267 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GETDEFAULT */
268 TW_UINT16 GPHOTO2_ImageLayoutGetDefault (pTW_IDENTITY pOrigin,
276 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_RESET */
277 TW_UINT16 GPHOTO2_ImageLayoutReset (pTW_IDENTITY pOrigin,
285 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET */
286 TW_UINT16 GPHOTO2_ImageLayoutSet (pTW_IDENTITY pOrigin,
294 /* DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET */
295 TW_UINT16 GPHOTO2_ImageMemXferGet (pTW_IDENTITY pOrigin,
299 TW_UINT16 twRC = TWRC_SUCCESS;
300 pTW_IMAGEMEMXFER pImageMemXfer = (pTW_IMAGEMEMXFER) pData;
305 TRACE ("DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET\n");
306 if (activeDS.currentState < 6 || activeDS.currentState > 7) {
307 activeDS.twCC = TWCC_SEQERROR;
310 TRACE("pImageMemXfer.Compression is %d\n", pImageMemXfer->Compression);
311 if (activeDS.currentState == 6) {
312 if (TWRC_SUCCESS != _get_image_and_startup_jpeg()) {
313 FIXME("Failed to get an image\n");
314 activeDS.twCC = TWCC_SEQERROR;
318 if (!activeDS.progressWnd)
319 activeDS.progressWnd = TransferringDialogBox(NULL,0);
320 TransferringDialogBox(activeDS.progressWnd,0);
322 activeDS.currentState = 7;
324 if (!activeDS.file) {
325 activeDS.twCC = TWRC_SUCCESS;
326 return TWRC_XFERDONE;
330 if (pImageMemXfer->Memory.Flags & TWMF_HANDLE) {
331 FIXME("Memory Handle, may not be locked correctly\n");
332 buffer = LocalLock(pImageMemXfer->Memory.TheMem);
334 buffer = pImageMemXfer->Memory.TheMem;
336 memset(buffer,0,pImageMemXfer->Memory.Length);
337 curoff = 0; readrows = 0;
338 pImageMemXfer->YOffset = activeDS.jd.output_scanline;
339 pImageMemXfer->XOffset = 0; /* we do whole strips */
340 while ((activeDS.jd.output_scanline<activeDS.jd.output_height) &&
341 ((pImageMemXfer->Memory.Length - curoff) > activeDS.jd.output_width*activeDS.jd.output_components)
343 JSAMPROW row = buffer+curoff;
344 int x = pjpeg_read_scanlines(&activeDS.jd,&row,1);
346 FIXME("failed to read current scanline?\n");
350 curoff += activeDS.jd.output_width*activeDS.jd.output_components;
352 pImageMemXfer->Compression = TWCP_NONE;
353 pImageMemXfer->BytesPerRow = activeDS.jd.output_components * activeDS.jd.output_width;
354 pImageMemXfer->Rows = readrows;
355 pImageMemXfer->Columns = activeDS.jd.output_width; /* we do whole strips */
356 pImageMemXfer->BytesWritten = curoff;
358 TransferringDialogBox(activeDS.progressWnd,0);
360 if (activeDS.jd.output_scanline == activeDS.jd.output_height) {
361 pjpeg_finish_decompress(&activeDS.jd);
362 pjpeg_destroy_decompress(&activeDS.jd);
363 gp_file_unref (activeDS.file);
364 activeDS.file = NULL;
365 TRACE("xfer is done!\n");
367 /*TransferringDialogBox(activeDS.progressWnd, -1);*/
368 twRC = TWRC_XFERDONE;
370 activeDS.twCC = TWRC_SUCCESS;
371 if (pImageMemXfer->Memory.Flags & TWMF_HANDLE)
372 LocalUnlock(pImageMemXfer->Memory.TheMem);
379 /* DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET */
380 TW_UINT16 GPHOTO2_ImageNativeXferGet (pTW_IDENTITY pOrigin,
384 pTW_UINT32 pHandle = (pTW_UINT32) pData;
387 LPBYTE bits, oldbits;
388 JSAMPROW samprow, oldsamprow;
391 FIXME("DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET: implemented, but expect program crash due to DIB.\n");
393 /* NOTE NOTE NOTE NOTE NOTE NOTE NOTE
395 * While this is a mandatory transfer mode and this function
396 * is correctly implemented and fully works, the calling program
397 * will likely crash after calling.
399 * Reason is that there is a lot of example code that does:
400 * bmpinfo = GlobalLock(hBITMAP); ... pointer access to bmpinfo
402 * Our current HBITMAP handles do not support getting GlobalLocked -> App Crash
404 * This needs a GDI Handle rewrite, at least for DIB sections.
407 if (activeDS.currentState != 6) {
408 activeDS.twCC = TWCC_SEQERROR;
411 if (TWRC_SUCCESS != _get_image_and_startup_jpeg()) {
412 FIXME("Failed to get an image\n");
413 activeDS.twCC = TWCC_OPERATIONERROR;
416 TRACE("Acquiring image %dx%dx%d bits from gphoto.\n",
417 activeDS.jd.output_width, activeDS.jd.output_height,
418 activeDS.jd.output_components*8);
419 ZeroMemory (&bmpInfo, sizeof (BITMAPINFO));
420 bmpInfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
421 bmpInfo.bmiHeader.biWidth = activeDS.jd.output_width;
422 bmpInfo.bmiHeader.biHeight = -activeDS.jd.output_height;
423 bmpInfo.bmiHeader.biPlanes = 1;
424 bmpInfo.bmiHeader.biBitCount = activeDS.jd.output_components*8;
425 bmpInfo.bmiHeader.biCompression = BI_RGB;
426 bmpInfo.bmiHeader.biSizeImage = 0;
427 bmpInfo.bmiHeader.biXPelsPerMeter = 0;
428 bmpInfo.bmiHeader.biYPelsPerMeter = 0;
429 bmpInfo.bmiHeader.biClrUsed = 0;
430 bmpInfo.bmiHeader.biClrImportant = 0;
431 hDIB = CreateDIBSection ((dc = GetDC(activeDS.hwndOwner)), &bmpInfo,
432 DIB_RGB_COLORS, (LPVOID)&bits, 0, 0);
434 FIXME("Failed creating DIB.\n");
435 gp_file_unref (activeDS.file);
436 activeDS.file = NULL;
437 activeDS.twCC = TWCC_LOWMEMORY;
440 samprow = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,activeDS.jd.output_width*activeDS.jd.output_components);
442 oldsamprow = samprow;
443 while ( activeDS.jd.output_scanline<activeDS.jd.output_height ) {
444 int i, x = pjpeg_read_scanlines(&activeDS.jd,&samprow,1);
446 FIXME("failed to read current scanline?\n");
449 /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */
450 for(i=0;i<activeDS.jd.output_width;i++,samprow+=activeDS.jd.output_components) {
451 *(bits++) = *(samprow+2);
452 *(bits++) = *(samprow+1);
453 *(bits++) = *(samprow);
455 bits = (LPBYTE)(((UINT_PTR)bits + 3) & ~3);
456 samprow = oldsamprow;
459 HeapFree (GetProcessHeap(), 0, samprow);
460 gp_file_unref (activeDS.file);
461 activeDS.file = NULL;
462 ReleaseDC (activeDS.hwndOwner, dc);
463 *pHandle = (TW_UINT32)hDIB;
464 activeDS.twCC = TWCC_SUCCESS;
465 activeDS.currentState = 7;
466 return TWRC_XFERDONE;
472 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_GET */
473 TW_UINT16 GPHOTO2_JPEGCompressionGet (pTW_IDENTITY pOrigin,
481 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_GETDEFAULT */
482 TW_UINT16 GPHOTO2_JPEGCompressionGetDefault (pTW_IDENTITY pOrigin,
491 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_RESET */
492 TW_UINT16 GPHOTO2_JPEGCompressionReset (pTW_IDENTITY pOrigin,
500 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_SET */
501 TW_UINT16 GPHOTO2_JPEGCompressionSet (pTW_IDENTITY pOrigin,
509 /* DG_IMAGE/DAT_PALETTE8/MSG_GET */
510 TW_UINT16 GPHOTO2_Palette8Get (pTW_IDENTITY pOrigin,
518 /* DG_IMAGE/DAT_PALETTE8/MSG_GETDEFAULT */
519 TW_UINT16 GPHOTO2_Palette8GetDefault (pTW_IDENTITY pOrigin,
527 /* DG_IMAGE/DAT_PALETTE8/MSG_RESET */
528 TW_UINT16 GPHOTO2_Palette8Reset (pTW_IDENTITY pOrigin,
536 /* DG_IMAGE/DAT_PALETTE8/MSG_SET */
537 TW_UINT16 GPHOTO2_Palette8Set (pTW_IDENTITY pOrigin,
545 /* DG_IMAGE/DAT_RGBRESPONSE/MSG_RESET */
546 TW_UINT16 GPHOTO2_RGBResponseReset (pTW_IDENTITY pOrigin,
554 /* DG_IMAGE/DAT_RGBRESPONSE/MSG_SET */
555 TW_UINT16 GPHOTO2_RGBResponseSet (pTW_IDENTITY pOrigin,
565 _get_gphoto2_file_as_DIB(
566 const char *folder, const char *filename, CameraFileType type,
567 HWND hwnd, HBITMAP *hDIB
569 const unsigned char *filedata;
570 unsigned long filesize;
573 struct jpeg_source_mgr xjsm;
574 struct jpeg_decompress_struct jd;
575 struct jpeg_error_mgr jerr;
578 LPBYTE bits, oldbits;
579 JSAMPROW samprow, oldsamprow;
581 if(!libjpeg_handle) {
582 if(!load_libjpeg()) {
583 FIXME("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG);
590 ret = gp_camera_file_get(activeDS.camera, folder, filename, type, file, activeDS.context);
592 FIXME("Failed to get file?\n");
593 gp_file_unref (file);
596 ret = gp_file_get_data_and_size (file, (const char**)&filedata, &filesize);
598 FIXME("Failed to get file data?\n");
602 /* FIXME: Actually we might get other types than JPEG ... But only handle JPEG for now */
603 if (filedata[0] != 0xff) {
604 ERR("File %s/%s might not be JPEG, cannot decode!\n", folder, filename);
607 /* This is basically so we can use in-memory data for jpeg decompression.
608 * We need to have all the functions.
610 xjsm.next_input_byte = filedata;
611 xjsm.bytes_in_buffer = filesize;
612 xjsm.init_source = _jpeg_init_source;
613 xjsm.fill_input_buffer = _jpeg_fill_input_buffer;
614 xjsm.skip_input_data = _jpeg_skip_input_data;
615 xjsm.resync_to_restart = _jpeg_resync_to_restart;
616 xjsm.term_source = _jpeg_term_source;
618 jd.err = pjpeg_std_error(&jerr);
619 /* jpeg_create_decompress is a macro that expands to jpeg_CreateDecompress - see jpeglib.h
620 * jpeg_create_decompress(&jd); */
621 pjpeg_CreateDecompress(&jd, JPEG_LIB_VERSION, (size_t) sizeof(struct jpeg_decompress_struct));
623 ret=pjpeg_read_header(&jd,TRUE);
624 jd.out_color_space = JCS_RGB;
625 pjpeg_start_decompress(&jd);
626 if (ret != JPEG_HEADER_OK) {
627 ERR("Jpeg image in stream has bad format, read header returned %d.\n",ret);
628 gp_file_unref (file);
632 ZeroMemory (&bmpInfo, sizeof (BITMAPINFO));
633 bmpInfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
634 bmpInfo.bmiHeader.biWidth = jd.output_width;
635 bmpInfo.bmiHeader.biHeight = -jd.output_height;
636 bmpInfo.bmiHeader.biPlanes = 1;
637 bmpInfo.bmiHeader.biBitCount = jd.output_components*8;
638 bmpInfo.bmiHeader.biCompression = BI_RGB;
639 bmpInfo.bmiHeader.biSizeImage = 0;
640 bmpInfo.bmiHeader.biXPelsPerMeter = 0;
641 bmpInfo.bmiHeader.biYPelsPerMeter = 0;
642 bmpInfo.bmiHeader.biClrUsed = 0;
643 bmpInfo.bmiHeader.biClrImportant = 0;
644 *hDIB = CreateDIBSection ((dc = GetDC(hwnd)), &bmpInfo, DIB_RGB_COLORS, (LPVOID)&bits, 0, 0);
646 FIXME("Failed creating DIB.\n");
647 gp_file_unref (file);
650 samprow = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,jd.output_width*jd.output_components);
652 oldsamprow = samprow;
653 while ( jd.output_scanline<jd.output_height ) {
654 int i, x = pjpeg_read_scanlines(&jd,&samprow,1);
656 FIXME("failed to read current scanline?\n");
659 /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */
660 for(i=0;i<jd.output_width;i++,samprow+=jd.output_components) {
661 *(bits++) = *(samprow+2);
662 *(bits++) = *(samprow+1);
663 *(bits++) = *(samprow);
665 bits = (LPBYTE)(((UINT_PTR)bits + 3) & ~3);
666 samprow = oldsamprow;
668 if (hwnd) ReleaseDC (hwnd, dc);
669 HeapFree (GetProcessHeap(), 0, samprow);
670 gp_file_unref (file);