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"
27 #include "gphoto2_i.h"
30 #include "wine/library.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(twain);
36 static void *libjpeg_handle;
37 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
38 MAKE_FUNCPTR(jpeg_std_error);
39 MAKE_FUNCPTR(jpeg_CreateDecompress);
40 MAKE_FUNCPTR(jpeg_read_header);
41 MAKE_FUNCPTR(jpeg_start_decompress);
42 MAKE_FUNCPTR(jpeg_read_scanlines);
43 MAKE_FUNCPTR(jpeg_finish_decompress);
44 MAKE_FUNCPTR(jpeg_destroy_decompress);
47 static void *load_libjpeg(void)
49 if((libjpeg_handle = wine_dlopen(SONAME_LIBJPEG, RTLD_NOW, NULL, 0)) != NULL) {
51 #define LOAD_FUNCPTR(f) \
52 if((p##f = wine_dlsym(libjpeg_handle, #f, NULL, 0)) == NULL) { \
53 libjpeg_handle = NULL; \
57 LOAD_FUNCPTR(jpeg_std_error);
58 LOAD_FUNCPTR(jpeg_CreateDecompress);
59 LOAD_FUNCPTR(jpeg_read_header);
60 LOAD_FUNCPTR(jpeg_start_decompress);
61 LOAD_FUNCPTR(jpeg_read_scanlines);
62 LOAD_FUNCPTR(jpeg_finish_decompress);
63 LOAD_FUNCPTR(jpeg_destroy_decompress);
66 return libjpeg_handle;
70 /* for the jpeg decompressor source manager. */
71 static void _jpeg_init_source(j_decompress_ptr cinfo) { }
73 static boolean _jpeg_fill_input_buffer(j_decompress_ptr cinfo) {
74 ERR("(), should not get here.\n");
78 static void _jpeg_skip_input_data(j_decompress_ptr cinfo,long num_bytes) {
79 TRACE("Skipping %ld bytes...\n", num_bytes);
80 cinfo->src->next_input_byte += num_bytes;
81 cinfo->src->bytes_in_buffer -= num_bytes;
84 static boolean _jpeg_resync_to_restart(j_decompress_ptr cinfo, int desired) {
85 ERR("(desired=%d), should not get here.\n",desired);
88 static void _jpeg_term_source(j_decompress_ptr cinfo) { }
91 /* DG_IMAGE/DAT_CIECOLOR/MSG_GET */
92 TW_UINT16 GPHOTO2_CIEColorGet (pTW_IDENTITY pOrigin,
100 /* DG_IMAGE/DAT_EXTIMAGEINFO/MSG_GET */
101 TW_UINT16 GPHOTO2_ExtImageInfoGet (pTW_IDENTITY pOrigin,
109 /* DG_IMAGE/DAT_GRAYRESPONSE/MSG_RESET */
110 TW_UINT16 GPHOTO2_GrayResponseReset (pTW_IDENTITY pOrigin,
118 /* DG_IMAGE/DAT_GRAYRESPONSE/MSG_SET */
119 TW_UINT16 GPHOTO2_GrayResponseSet (pTW_IDENTITY pOrigin,
127 /* DG_IMAGE/DAT_IMAGEFILEXFER/MSG_GET */
128 TW_UINT16 GPHOTO2_ImageFileXferGet (pTW_IDENTITY pOrigin,
137 static TW_UINT16 _get_image_and_startup_jpeg(void) {
138 const char *folder = NULL, *filename = NULL;
139 struct gphoto2_file *file;
140 const unsigned char *filedata;
141 unsigned long filesize;
144 if (activeDS.file) /* Already loaded. */
147 if(!libjpeg_handle) {
148 if(!load_libjpeg()) {
149 FIXME("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG);
155 LIST_FOR_EACH_ENTRY( file, &activeDS.files, struct gphoto2_file, entry ) {
156 if (strstr(file->filename,".JPG") || strstr(file->filename,".jpg")) {
157 filename = file->filename;
158 folder = file->folder;
159 TRACE("downloading %s/%s\n", folder, filename);
160 if (file->download) {
161 file->download = FALSE; /* mark as done */
166 gp_file_new (&activeDS.file);
167 ret = gp_camera_file_get(activeDS.camera, folder, filename, GP_FILE_TYPE_NORMAL,
168 activeDS.file, activeDS.context);
170 FIXME("Failed to get file?\n");
171 activeDS.twCC = TWCC_SEQERROR;
174 ret = gp_file_get_data_and_size (activeDS.file, (const char**)&filedata, &filesize);
176 FIXME("Failed to get file data?\n");
177 activeDS.twCC = TWCC_SEQERROR;
181 /* This is basically so we can use in-memory data for jpeg decompression.
182 * We need to have all the functions.
184 activeDS.xjsm.next_input_byte = filedata;
185 activeDS.xjsm.bytes_in_buffer = filesize;
186 activeDS.xjsm.init_source = _jpeg_init_source;
187 activeDS.xjsm.fill_input_buffer = _jpeg_fill_input_buffer;
188 activeDS.xjsm.skip_input_data = _jpeg_skip_input_data;
189 activeDS.xjsm.resync_to_restart = _jpeg_resync_to_restart;
190 activeDS.xjsm.term_source = _jpeg_term_source;
192 activeDS.jd.err = pjpeg_std_error(&activeDS.jerr);
193 /* jpeg_create_decompress is a macro that expands to jpeg_CreateDecompress - see jpeglib.h
194 * jpeg_create_decompress(&jd); */
195 pjpeg_CreateDecompress(&activeDS.jd, JPEG_LIB_VERSION, (size_t) sizeof(struct jpeg_decompress_struct));
196 activeDS.jd.src = &activeDS.xjsm;
197 ret=pjpeg_read_header(&activeDS.jd,TRUE);
198 activeDS.jd.out_color_space = JCS_RGB;
199 pjpeg_start_decompress(&activeDS.jd);
200 if (ret != JPEG_HEADER_OK) {
201 ERR("Jpeg image in stream has bad format, read header returned %d.\n",ret);
202 gp_file_unref (activeDS.file);
203 activeDS.file = NULL;
210 /* DG_IMAGE/DAT_IMAGEINFO/MSG_GET */
211 TW_UINT16 GPHOTO2_ImageInfoGet (pTW_IDENTITY pOrigin,
215 pTW_IMAGEINFO pImageInfo = (pTW_IMAGEINFO) pData;
217 TRACE("DG_IMAGE/DAT_IMAGEINFO/MSG_GET\n");
219 if (activeDS.currentState != 6 && activeDS.currentState != 7) {
220 activeDS.twCC = TWCC_SEQERROR;
223 if (TWRC_SUCCESS != _get_image_and_startup_jpeg()) {
224 FIXME("Failed to get an image\n");
225 activeDS.twCC = TWCC_SEQERROR;
228 if (activeDS.currentState == 6)
230 /* return general image description information about the image about to be transferred */
231 TRACE("Getting parameters\n");
233 TRACE("activeDS.jd.output_width = %d\n", activeDS.jd.output_width);
234 TRACE("activeDS.jd.output_height = %d\n", activeDS.jd.output_height);
235 pImageInfo->Compression = TWCP_NONE;
236 pImageInfo->SamplesPerPixel = 3;
237 pImageInfo->BitsPerSample[0]= 8;
238 pImageInfo->BitsPerSample[1]= 8;
239 pImageInfo->BitsPerSample[2]= 8;
240 pImageInfo->PixelType = TWPT_RGB;
241 pImageInfo->Planar = FALSE; /* R-G-B is chunky! */
242 pImageInfo->XResolution.Whole = -1;
243 pImageInfo->XResolution.Frac = 0;
244 pImageInfo->YResolution.Whole = -1;
245 pImageInfo->YResolution.Frac = 0;
246 pImageInfo->ImageWidth = activeDS.jd.output_width;
247 pImageInfo->ImageLength = activeDS.jd.output_height;
248 pImageInfo->BitsPerPixel = 24;
255 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET */
256 TW_UINT16 GPHOTO2_ImageLayoutGet (pTW_IDENTITY pOrigin,
264 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GETDEFAULT */
265 TW_UINT16 GPHOTO2_ImageLayoutGetDefault (pTW_IDENTITY pOrigin,
273 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_RESET */
274 TW_UINT16 GPHOTO2_ImageLayoutReset (pTW_IDENTITY pOrigin,
282 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET */
283 TW_UINT16 GPHOTO2_ImageLayoutSet (pTW_IDENTITY pOrigin,
291 /* DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET */
292 TW_UINT16 GPHOTO2_ImageMemXferGet (pTW_IDENTITY pOrigin,
296 TW_UINT16 twRC = TWRC_SUCCESS;
297 pTW_IMAGEMEMXFER pImageMemXfer = (pTW_IMAGEMEMXFER) pData;
302 TRACE ("DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET\n");
303 if (activeDS.currentState < 6 || activeDS.currentState > 7) {
304 activeDS.twCC = TWCC_SEQERROR;
307 TRACE("pImageMemXfer.Compression is %d\n", pImageMemXfer->Compression);
308 if (activeDS.currentState == 6) {
309 if (TWRC_SUCCESS != _get_image_and_startup_jpeg()) {
310 FIXME("Failed to get an image\n");
311 activeDS.twCC = TWCC_SEQERROR;
315 if (!activeDS.progressWnd)
316 activeDS.progressWnd = TransferringDialogBox(NULL,0);
317 TransferringDialogBox(activeDS.progressWnd,0);
319 activeDS.currentState = 7;
321 if (!activeDS.file) {
322 activeDS.twCC = TWRC_SUCCESS;
323 return TWRC_XFERDONE;
327 if (pImageMemXfer->Memory.Flags & TWMF_HANDLE) {
328 FIXME("Memory Handle, may not be locked correctly\n");
329 buffer = LocalLock(pImageMemXfer->Memory.TheMem);
331 buffer = pImageMemXfer->Memory.TheMem;
333 memset(buffer,0,pImageMemXfer->Memory.Length);
334 curoff = 0; readrows = 0;
335 pImageMemXfer->YOffset = activeDS.jd.output_scanline;
336 pImageMemXfer->XOffset = 0; /* we do whole strips */
337 while ((activeDS.jd.output_scanline<activeDS.jd.output_height) &&
338 ((pImageMemXfer->Memory.Length - curoff) > activeDS.jd.output_width*activeDS.jd.output_components)
340 JSAMPROW row = buffer+curoff;
341 int x = pjpeg_read_scanlines(&activeDS.jd,&row,1);
343 FIXME("failed to read current scanline?\n");
347 curoff += activeDS.jd.output_width*activeDS.jd.output_components;
349 pImageMemXfer->Compression = TWCP_NONE;
350 pImageMemXfer->BytesPerRow = activeDS.jd.output_components * activeDS.jd.output_width;
351 pImageMemXfer->Rows = readrows;
352 pImageMemXfer->Columns = activeDS.jd.output_width; /* we do whole strips */
353 pImageMemXfer->BytesWritten = curoff;
355 TransferringDialogBox(activeDS.progressWnd,0);
357 if (activeDS.jd.output_scanline == activeDS.jd.output_height) {
358 pjpeg_finish_decompress(&activeDS.jd);
359 pjpeg_destroy_decompress(&activeDS.jd);
360 gp_file_unref (activeDS.file);
361 activeDS.file = NULL;
362 TRACE("xfer is done!\n");
364 /*TransferringDialogBox(activeDS.progressWnd, -1);*/
365 twRC = TWRC_XFERDONE;
367 activeDS.twCC = TWRC_SUCCESS;
368 if (pImageMemXfer->Memory.Flags & TWMF_HANDLE)
369 LocalUnlock(pImageMemXfer->Memory.TheMem);
376 /* DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET */
377 TW_UINT16 GPHOTO2_ImageNativeXferGet (pTW_IDENTITY pOrigin,
381 pTW_UINT32 pHandle = (pTW_UINT32) pData;
385 JSAMPROW samprow, oldsamprow;
387 FIXME("DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET: implemented, but expect program crash due to DIB.\n");
389 /* NOTE NOTE NOTE NOTE NOTE NOTE NOTE
391 * While this is a mandatory transfer mode and this function
392 * is correctly implemented and fully works, the calling program
393 * will likely crash after calling.
395 * Reason is that there is a lot of example code that does:
396 * bmpinfo = GlobalLock(hBITMAP); ... pointer access to bmpinfo
398 * Our current HBITMAP handles do not support getting GlobalLocked -> App Crash
400 * This needs a GDI Handle rewrite, at least for DIB sections.
403 if (activeDS.currentState != 6) {
404 activeDS.twCC = TWCC_SEQERROR;
407 if (TWRC_SUCCESS != _get_image_and_startup_jpeg()) {
408 FIXME("Failed to get an image\n");
409 activeDS.twCC = TWCC_OPERATIONERROR;
412 TRACE("Acquiring image %dx%dx%d bits from gphoto.\n",
413 activeDS.jd.output_width, activeDS.jd.output_height,
414 activeDS.jd.output_components*8);
415 ZeroMemory (&bmpInfo, sizeof (BITMAPINFO));
416 bmpInfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
417 bmpInfo.bmiHeader.biWidth = activeDS.jd.output_width;
418 bmpInfo.bmiHeader.biHeight = -activeDS.jd.output_height;
419 bmpInfo.bmiHeader.biPlanes = 1;
420 bmpInfo.bmiHeader.biBitCount = activeDS.jd.output_components*8;
421 bmpInfo.bmiHeader.biCompression = BI_RGB;
422 bmpInfo.bmiHeader.biSizeImage = 0;
423 bmpInfo.bmiHeader.biXPelsPerMeter = 0;
424 bmpInfo.bmiHeader.biYPelsPerMeter = 0;
425 bmpInfo.bmiHeader.biClrUsed = 0;
426 bmpInfo.bmiHeader.biClrImportant = 0;
427 hDIB = CreateDIBSection (0, &bmpInfo, DIB_RGB_COLORS, (LPVOID)&bits, 0, 0);
429 FIXME("Failed creating DIB.\n");
430 gp_file_unref (activeDS.file);
431 activeDS.file = NULL;
432 activeDS.twCC = TWCC_LOWMEMORY;
435 samprow = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,activeDS.jd.output_width*activeDS.jd.output_components);
436 oldsamprow = samprow;
437 while ( activeDS.jd.output_scanline<activeDS.jd.output_height ) {
438 int i, x = pjpeg_read_scanlines(&activeDS.jd,&samprow,1);
440 FIXME("failed to read current scanline?\n");
443 /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */
444 for(i=0;i<activeDS.jd.output_width;i++,samprow+=activeDS.jd.output_components) {
445 *(bits++) = *(samprow+2);
446 *(bits++) = *(samprow+1);
447 *(bits++) = *(samprow);
449 bits = (LPBYTE)(((UINT_PTR)bits + 3) & ~3);
450 samprow = oldsamprow;
452 HeapFree (GetProcessHeap(), 0, samprow);
453 gp_file_unref (activeDS.file);
454 activeDS.file = NULL;
455 *pHandle = (UINT_PTR)hDIB;
456 activeDS.twCC = TWCC_SUCCESS;
457 activeDS.currentState = 7;
458 return TWRC_XFERDONE;
464 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_GET */
465 TW_UINT16 GPHOTO2_JPEGCompressionGet (pTW_IDENTITY pOrigin,
473 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_GETDEFAULT */
474 TW_UINT16 GPHOTO2_JPEGCompressionGetDefault (pTW_IDENTITY pOrigin,
483 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_RESET */
484 TW_UINT16 GPHOTO2_JPEGCompressionReset (pTW_IDENTITY pOrigin,
492 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_SET */
493 TW_UINT16 GPHOTO2_JPEGCompressionSet (pTW_IDENTITY pOrigin,
501 /* DG_IMAGE/DAT_PALETTE8/MSG_GET */
502 TW_UINT16 GPHOTO2_Palette8Get (pTW_IDENTITY pOrigin,
510 /* DG_IMAGE/DAT_PALETTE8/MSG_GETDEFAULT */
511 TW_UINT16 GPHOTO2_Palette8GetDefault (pTW_IDENTITY pOrigin,
519 /* DG_IMAGE/DAT_PALETTE8/MSG_RESET */
520 TW_UINT16 GPHOTO2_Palette8Reset (pTW_IDENTITY pOrigin,
528 /* DG_IMAGE/DAT_PALETTE8/MSG_SET */
529 TW_UINT16 GPHOTO2_Palette8Set (pTW_IDENTITY pOrigin,
537 /* DG_IMAGE/DAT_RGBRESPONSE/MSG_RESET */
538 TW_UINT16 GPHOTO2_RGBResponseReset (pTW_IDENTITY pOrigin,
546 /* DG_IMAGE/DAT_RGBRESPONSE/MSG_SET */
547 TW_UINT16 GPHOTO2_RGBResponseSet (pTW_IDENTITY pOrigin,
557 _get_gphoto2_file_as_DIB(
558 const char *folder, const char *filename, CameraFileType type,
559 HWND hwnd, HBITMAP *hDIB
561 const unsigned char *filedata;
562 unsigned long filesize;
565 struct jpeg_source_mgr xjsm;
566 struct jpeg_decompress_struct jd;
567 struct jpeg_error_mgr jerr;
570 JSAMPROW samprow, oldsamprow;
572 if(!libjpeg_handle) {
573 if(!load_libjpeg()) {
574 FIXME("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG);
581 ret = gp_camera_file_get(activeDS.camera, folder, filename, type, file, activeDS.context);
583 FIXME("Failed to get file?\n");
584 gp_file_unref (file);
587 ret = gp_file_get_data_and_size (file, (const char**)&filedata, &filesize);
589 FIXME("Failed to get file data?\n");
593 /* FIXME: Actually we might get other types than JPEG ... But only handle JPEG for now */
594 if (filedata[0] != 0xff) {
595 ERR("File %s/%s might not be JPEG, cannot decode!\n", folder, filename);
598 /* This is basically so we can use in-memory data for jpeg decompression.
599 * We need to have all the functions.
601 xjsm.next_input_byte = filedata;
602 xjsm.bytes_in_buffer = filesize;
603 xjsm.init_source = _jpeg_init_source;
604 xjsm.fill_input_buffer = _jpeg_fill_input_buffer;
605 xjsm.skip_input_data = _jpeg_skip_input_data;
606 xjsm.resync_to_restart = _jpeg_resync_to_restart;
607 xjsm.term_source = _jpeg_term_source;
609 jd.err = pjpeg_std_error(&jerr);
610 /* jpeg_create_decompress is a macro that expands to jpeg_CreateDecompress - see jpeglib.h
611 * jpeg_create_decompress(&jd); */
612 pjpeg_CreateDecompress(&jd, JPEG_LIB_VERSION, (size_t) sizeof(struct jpeg_decompress_struct));
614 ret=pjpeg_read_header(&jd,TRUE);
615 jd.out_color_space = JCS_RGB;
616 pjpeg_start_decompress(&jd);
617 if (ret != JPEG_HEADER_OK) {
618 ERR("Jpeg image in stream has bad format, read header returned %d.\n",ret);
619 gp_file_unref (file);
623 ZeroMemory (&bmpInfo, sizeof (BITMAPINFO));
624 bmpInfo.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
625 bmpInfo.bmiHeader.biWidth = jd.output_width;
626 bmpInfo.bmiHeader.biHeight = -jd.output_height;
627 bmpInfo.bmiHeader.biPlanes = 1;
628 bmpInfo.bmiHeader.biBitCount = jd.output_components*8;
629 bmpInfo.bmiHeader.biCompression = BI_RGB;
630 bmpInfo.bmiHeader.biSizeImage = 0;
631 bmpInfo.bmiHeader.biXPelsPerMeter = 0;
632 bmpInfo.bmiHeader.biYPelsPerMeter = 0;
633 bmpInfo.bmiHeader.biClrUsed = 0;
634 bmpInfo.bmiHeader.biClrImportant = 0;
635 *hDIB = CreateDIBSection(0, &bmpInfo, DIB_RGB_COLORS, (LPVOID)&bits, 0, 0);
637 FIXME("Failed creating DIB.\n");
638 gp_file_unref (file);
641 samprow = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,jd.output_width*jd.output_components);
642 oldsamprow = samprow;
643 while ( jd.output_scanline<jd.output_height ) {
644 int i, x = pjpeg_read_scanlines(&jd,&samprow,1);
646 FIXME("failed to read current scanline?\n");
649 /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */
650 for(i=0;i<jd.output_width;i++,samprow+=jd.output_components) {
651 *(bits++) = *(samprow+2);
652 *(bits++) = *(samprow+1);
653 *(bits++) = *(samprow);
655 bits = (LPBYTE)(((UINT_PTR)bits + 3) & ~3);
656 samprow = oldsamprow;
658 HeapFree (GetProcessHeap(), 0, samprow);
659 gp_file_unref (file);