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
) {
439 int x
= pjpeg_read_scanlines(&activeDS
.jd
,&samprow
,1);
441 FIXME("failed to read current scanline?\n");
444 /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */
445 for(i
=0;i
<activeDS
.jd
.output_width
;i
++,samprow
+=activeDS
.jd
.output_components
) {
446 *(bits
++) = *(samprow
+2);
447 *(bits
++) = *(samprow
+1);
448 *(bits
++) = *(samprow
);
450 bits
= (LPBYTE
)(((UINT_PTR
)bits
+ 3) & ~3);
451 samprow
= oldsamprow
;
453 HeapFree (GetProcessHeap(), 0, samprow
);
454 gp_file_unref (activeDS
.file
);
455 activeDS
.file
= NULL
;
456 *pHandle
= (UINT_PTR
)hDIB
;
457 activeDS
.twCC
= TWCC_SUCCESS
;
458 activeDS
.currentState
= 7;
459 return TWRC_XFERDONE
;
465 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_GET */
466 TW_UINT16
GPHOTO2_JPEGCompressionGet (pTW_IDENTITY pOrigin
,
474 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_GETDEFAULT */
475 TW_UINT16
GPHOTO2_JPEGCompressionGetDefault (pTW_IDENTITY pOrigin
,
484 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_RESET */
485 TW_UINT16
GPHOTO2_JPEGCompressionReset (pTW_IDENTITY pOrigin
,
493 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_SET */
494 TW_UINT16
GPHOTO2_JPEGCompressionSet (pTW_IDENTITY pOrigin
,
502 /* DG_IMAGE/DAT_PALETTE8/MSG_GET */
503 TW_UINT16
GPHOTO2_Palette8Get (pTW_IDENTITY pOrigin
,
511 /* DG_IMAGE/DAT_PALETTE8/MSG_GETDEFAULT */
512 TW_UINT16
GPHOTO2_Palette8GetDefault (pTW_IDENTITY pOrigin
,
520 /* DG_IMAGE/DAT_PALETTE8/MSG_RESET */
521 TW_UINT16
GPHOTO2_Palette8Reset (pTW_IDENTITY pOrigin
,
529 /* DG_IMAGE/DAT_PALETTE8/MSG_SET */
530 TW_UINT16
GPHOTO2_Palette8Set (pTW_IDENTITY pOrigin
,
538 /* DG_IMAGE/DAT_RGBRESPONSE/MSG_RESET */
539 TW_UINT16
GPHOTO2_RGBResponseReset (pTW_IDENTITY pOrigin
,
547 /* DG_IMAGE/DAT_RGBRESPONSE/MSG_SET */
548 TW_UINT16
GPHOTO2_RGBResponseSet (pTW_IDENTITY pOrigin
,
558 _get_gphoto2_file_as_DIB(
559 const char *folder
, const char *filename
, CameraFileType type
,
560 HWND hwnd
, HBITMAP
*hDIB
562 const unsigned char *filedata
;
563 unsigned long filesize
;
566 struct jpeg_source_mgr xjsm
;
567 struct jpeg_decompress_struct jd
;
568 struct jpeg_error_mgr jerr
;
571 JSAMPROW samprow
, oldsamprow
;
573 if(!libjpeg_handle
) {
574 if(!load_libjpeg()) {
575 FIXME("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG
);
582 ret
= gp_camera_file_get(activeDS
.camera
, folder
, filename
, type
, file
, activeDS
.context
);
584 FIXME("Failed to get file?\n");
585 gp_file_unref (file
);
588 ret
= gp_file_get_data_and_size (file
, (const char**)&filedata
, &filesize
);
590 FIXME("Failed to get file data?\n");
594 /* FIXME: Actually we might get other types than JPEG ... But only handle JPEG for now */
595 if (filedata
[0] != 0xff) {
596 ERR("File %s/%s might not be JPEG, cannot decode!\n", folder
, filename
);
599 /* This is basically so we can use in-memory data for jpeg decompression.
600 * We need to have all the functions.
602 xjsm
.next_input_byte
= filedata
;
603 xjsm
.bytes_in_buffer
= filesize
;
604 xjsm
.init_source
= _jpeg_init_source
;
605 xjsm
.fill_input_buffer
= _jpeg_fill_input_buffer
;
606 xjsm
.skip_input_data
= _jpeg_skip_input_data
;
607 xjsm
.resync_to_restart
= _jpeg_resync_to_restart
;
608 xjsm
.term_source
= _jpeg_term_source
;
610 jd
.err
= pjpeg_std_error(&jerr
);
611 /* jpeg_create_decompress is a macro that expands to jpeg_CreateDecompress - see jpeglib.h
612 * jpeg_create_decompress(&jd); */
613 pjpeg_CreateDecompress(&jd
, JPEG_LIB_VERSION
, (size_t) sizeof(struct jpeg_decompress_struct
));
615 ret
=pjpeg_read_header(&jd
,TRUE
);
616 jd
.out_color_space
= JCS_RGB
;
617 pjpeg_start_decompress(&jd
);
618 if (ret
!= JPEG_HEADER_OK
) {
619 ERR("Jpeg image in stream has bad format, read header returned %d.\n",ret
);
620 gp_file_unref (file
);
624 ZeroMemory (&bmpInfo
, sizeof (BITMAPINFO
));
625 bmpInfo
.bmiHeader
.biSize
= sizeof (BITMAPINFOHEADER
);
626 bmpInfo
.bmiHeader
.biWidth
= jd
.output_width
;
627 bmpInfo
.bmiHeader
.biHeight
= -jd
.output_height
;
628 bmpInfo
.bmiHeader
.biPlanes
= 1;
629 bmpInfo
.bmiHeader
.biBitCount
= jd
.output_components
*8;
630 bmpInfo
.bmiHeader
.biCompression
= BI_RGB
;
631 bmpInfo
.bmiHeader
.biSizeImage
= 0;
632 bmpInfo
.bmiHeader
.biXPelsPerMeter
= 0;
633 bmpInfo
.bmiHeader
.biYPelsPerMeter
= 0;
634 bmpInfo
.bmiHeader
.biClrUsed
= 0;
635 bmpInfo
.bmiHeader
.biClrImportant
= 0;
636 *hDIB
= CreateDIBSection(0, &bmpInfo
, DIB_RGB_COLORS
, (LPVOID
)&bits
, 0, 0);
638 FIXME("Failed creating DIB.\n");
639 gp_file_unref (file
);
642 samprow
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,jd
.output_width
*jd
.output_components
);
643 oldsamprow
= samprow
;
644 while ( jd
.output_scanline
<jd
.output_height
) {
646 int x
= pjpeg_read_scanlines(&jd
,&samprow
,1);
648 FIXME("failed to read current scanline?\n");
651 /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */
652 for(i
=0;i
<jd
.output_width
;i
++,samprow
+=jd
.output_components
) {
653 *(bits
++) = *(samprow
+2);
654 *(bits
++) = *(samprow
+1);
655 *(bits
++) = *(samprow
);
657 bits
= (LPBYTE
)(((UINT_PTR
)bits
+ 3) & ~3);
658 samprow
= oldsamprow
;
660 HeapFree (GetProcessHeap(), 0, samprow
);
661 gp_file_unref (file
);