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/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(twain
);
35 static void *libjpeg_handle
;
36 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
37 MAKE_FUNCPTR(jpeg_std_error
);
38 MAKE_FUNCPTR(jpeg_CreateDecompress
);
39 MAKE_FUNCPTR(jpeg_read_header
);
40 MAKE_FUNCPTR(jpeg_start_decompress
);
41 MAKE_FUNCPTR(jpeg_read_scanlines
);
42 MAKE_FUNCPTR(jpeg_finish_decompress
);
43 MAKE_FUNCPTR(jpeg_destroy_decompress
);
46 static void *load_libjpeg(void)
48 if((libjpeg_handle
= dlopen(SONAME_LIBJPEG
, RTLD_NOW
)) != NULL
) {
50 #define LOAD_FUNCPTR(f) \
51 if((p##f = dlsym(libjpeg_handle, #f)) == NULL) { \
52 libjpeg_handle = NULL; \
56 LOAD_FUNCPTR(jpeg_std_error
);
57 LOAD_FUNCPTR(jpeg_CreateDecompress
);
58 LOAD_FUNCPTR(jpeg_read_header
);
59 LOAD_FUNCPTR(jpeg_start_decompress
);
60 LOAD_FUNCPTR(jpeg_read_scanlines
);
61 LOAD_FUNCPTR(jpeg_finish_decompress
);
62 LOAD_FUNCPTR(jpeg_destroy_decompress
);
65 return libjpeg_handle
;
69 /* for the jpeg decompressor source manager. */
70 static void _jpeg_init_source(j_decompress_ptr cinfo
) { }
72 static boolean
_jpeg_fill_input_buffer(j_decompress_ptr cinfo
) {
73 ERR("(), should not get here.\n");
77 static void _jpeg_skip_input_data(j_decompress_ptr cinfo
,long num_bytes
) {
78 TRACE("Skipping %ld bytes...\n", num_bytes
);
79 cinfo
->src
->next_input_byte
+= num_bytes
;
80 cinfo
->src
->bytes_in_buffer
-= num_bytes
;
83 static boolean
_jpeg_resync_to_restart(j_decompress_ptr cinfo
, int desired
) {
84 ERR("(desired=%d), should not get here.\n",desired
);
87 static void _jpeg_term_source(j_decompress_ptr cinfo
) { }
90 /* DG_IMAGE/DAT_CIECOLOR/MSG_GET */
91 TW_UINT16
GPHOTO2_CIEColorGet (pTW_IDENTITY pOrigin
,
99 /* DG_IMAGE/DAT_EXTIMAGEINFO/MSG_GET */
100 TW_UINT16
GPHOTO2_ExtImageInfoGet (pTW_IDENTITY pOrigin
,
108 /* DG_IMAGE/DAT_GRAYRESPONSE/MSG_RESET */
109 TW_UINT16
GPHOTO2_GrayResponseReset (pTW_IDENTITY pOrigin
,
117 /* DG_IMAGE/DAT_GRAYRESPONSE/MSG_SET */
118 TW_UINT16
GPHOTO2_GrayResponseSet (pTW_IDENTITY pOrigin
,
126 /* DG_IMAGE/DAT_IMAGEFILEXFER/MSG_GET */
127 TW_UINT16
GPHOTO2_ImageFileXferGet (pTW_IDENTITY pOrigin
,
136 static TW_UINT16
_get_image_and_startup_jpeg(void) {
137 const char *folder
= NULL
, *filename
= NULL
;
138 struct gphoto2_file
*file
;
139 const unsigned char *filedata
;
140 unsigned long filesize
;
143 if (activeDS
.file
) /* Already loaded. */
146 if(!libjpeg_handle
) {
147 if(!load_libjpeg()) {
148 FIXME("Failed reading JPEG because unable to find %s\n", SONAME_LIBJPEG
);
154 LIST_FOR_EACH_ENTRY( file
, &activeDS
.files
, struct gphoto2_file
, entry
) {
155 if (strstr(file
->filename
,".JPG") || strstr(file
->filename
,".jpg")) {
156 filename
= file
->filename
;
157 folder
= file
->folder
;
158 TRACE("downloading %s/%s\n", folder
, filename
);
159 if (file
->download
) {
160 file
->download
= FALSE
; /* mark as done */
165 gp_file_new (&activeDS
.file
);
166 ret
= gp_camera_file_get(activeDS
.camera
, folder
, filename
, GP_FILE_TYPE_NORMAL
,
167 activeDS
.file
, activeDS
.context
);
169 FIXME("Failed to get file?\n");
170 activeDS
.twCC
= TWCC_SEQERROR
;
173 ret
= gp_file_get_data_and_size (activeDS
.file
, (const char**)&filedata
, &filesize
);
175 FIXME("Failed to get file data?\n");
176 activeDS
.twCC
= TWCC_SEQERROR
;
180 /* This is basically so we can use in-memory data for jpeg decompression.
181 * We need to have all the functions.
183 activeDS
.xjsm
.next_input_byte
= filedata
;
184 activeDS
.xjsm
.bytes_in_buffer
= filesize
;
185 activeDS
.xjsm
.init_source
= _jpeg_init_source
;
186 activeDS
.xjsm
.fill_input_buffer
= _jpeg_fill_input_buffer
;
187 activeDS
.xjsm
.skip_input_data
= _jpeg_skip_input_data
;
188 activeDS
.xjsm
.resync_to_restart
= _jpeg_resync_to_restart
;
189 activeDS
.xjsm
.term_source
= _jpeg_term_source
;
191 activeDS
.jd
.err
= pjpeg_std_error(&activeDS
.jerr
);
192 /* jpeg_create_decompress is a macro that expands to jpeg_CreateDecompress - see jpeglib.h
193 * jpeg_create_decompress(&jd); */
194 pjpeg_CreateDecompress(&activeDS
.jd
, JPEG_LIB_VERSION
, sizeof(struct jpeg_decompress_struct
));
195 activeDS
.jd
.src
= &activeDS
.xjsm
;
196 ret
=pjpeg_read_header(&activeDS
.jd
,TRUE
);
197 activeDS
.jd
.out_color_space
= JCS_RGB
;
198 pjpeg_start_decompress(&activeDS
.jd
);
199 if (ret
!= JPEG_HEADER_OK
) {
200 ERR("Jpeg image in stream has bad format, read header returned %d.\n",ret
);
201 gp_file_unref (activeDS
.file
);
202 activeDS
.file
= NULL
;
209 /* DG_IMAGE/DAT_IMAGEINFO/MSG_GET */
210 TW_UINT16
GPHOTO2_ImageInfoGet (pTW_IDENTITY pOrigin
,
214 pTW_IMAGEINFO pImageInfo
= (pTW_IMAGEINFO
) pData
;
216 TRACE("DG_IMAGE/DAT_IMAGEINFO/MSG_GET\n");
218 if (activeDS
.currentState
!= 6 && activeDS
.currentState
!= 7) {
219 activeDS
.twCC
= TWCC_SEQERROR
;
222 if (TWRC_SUCCESS
!= _get_image_and_startup_jpeg()) {
223 FIXME("Failed to get an image\n");
224 activeDS
.twCC
= TWCC_SEQERROR
;
227 if (activeDS
.currentState
== 6)
229 /* return general image description information about the image about to be transferred */
230 TRACE("Getting parameters\n");
232 TRACE("activeDS.jd.output_width = %d\n", activeDS
.jd
.output_width
);
233 TRACE("activeDS.jd.output_height = %d\n", activeDS
.jd
.output_height
);
234 pImageInfo
->Compression
= TWCP_NONE
;
235 pImageInfo
->SamplesPerPixel
= 3;
236 pImageInfo
->BitsPerSample
[0]= 8;
237 pImageInfo
->BitsPerSample
[1]= 8;
238 pImageInfo
->BitsPerSample
[2]= 8;
239 pImageInfo
->PixelType
= TWPT_RGB
;
240 pImageInfo
->Planar
= FALSE
; /* R-G-B is chunky! */
241 pImageInfo
->XResolution
.Whole
= -1;
242 pImageInfo
->XResolution
.Frac
= 0;
243 pImageInfo
->YResolution
.Whole
= -1;
244 pImageInfo
->YResolution
.Frac
= 0;
245 pImageInfo
->ImageWidth
= activeDS
.jd
.output_width
;
246 pImageInfo
->ImageLength
= activeDS
.jd
.output_height
;
247 pImageInfo
->BitsPerPixel
= 24;
254 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET */
255 TW_UINT16
GPHOTO2_ImageLayoutGet (pTW_IDENTITY pOrigin
,
263 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GETDEFAULT */
264 TW_UINT16
GPHOTO2_ImageLayoutGetDefault (pTW_IDENTITY pOrigin
,
272 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_RESET */
273 TW_UINT16
GPHOTO2_ImageLayoutReset (pTW_IDENTITY pOrigin
,
281 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET */
282 TW_UINT16
GPHOTO2_ImageLayoutSet (pTW_IDENTITY pOrigin
,
290 /* DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET */
291 TW_UINT16
GPHOTO2_ImageMemXferGet (pTW_IDENTITY pOrigin
,
295 TW_UINT16 twRC
= TWRC_SUCCESS
;
296 pTW_IMAGEMEMXFER pImageMemXfer
= (pTW_IMAGEMEMXFER
) pData
;
301 TRACE ("DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET\n");
302 if (activeDS
.currentState
< 6 || activeDS
.currentState
> 7) {
303 activeDS
.twCC
= TWCC_SEQERROR
;
306 TRACE("pImageMemXfer.Compression is %d\n", pImageMemXfer
->Compression
);
307 if (activeDS
.currentState
== 6) {
308 if (TWRC_SUCCESS
!= _get_image_and_startup_jpeg()) {
309 FIXME("Failed to get an image\n");
310 activeDS
.twCC
= TWCC_SEQERROR
;
314 if (!activeDS
.progressWnd
)
315 activeDS
.progressWnd
= TransferringDialogBox(NULL
,0);
316 TransferringDialogBox(activeDS
.progressWnd
,0);
318 activeDS
.currentState
= 7;
320 if (!activeDS
.file
) {
321 activeDS
.twCC
= TWRC_SUCCESS
;
322 return TWRC_XFERDONE
;
326 if (pImageMemXfer
->Memory
.Flags
& TWMF_HANDLE
) {
327 FIXME("Memory Handle, may not be locked correctly\n");
328 buffer
= LocalLock(pImageMemXfer
->Memory
.TheMem
);
330 buffer
= pImageMemXfer
->Memory
.TheMem
;
332 memset(buffer
,0,pImageMemXfer
->Memory
.Length
);
333 curoff
= 0; readrows
= 0;
334 pImageMemXfer
->YOffset
= activeDS
.jd
.output_scanline
;
335 pImageMemXfer
->XOffset
= 0; /* we do whole strips */
336 while ((activeDS
.jd
.output_scanline
<activeDS
.jd
.output_height
) &&
337 ((pImageMemXfer
->Memory
.Length
- curoff
) > activeDS
.jd
.output_width
*activeDS
.jd
.output_components
)
339 JSAMPROW row
= buffer
+curoff
;
340 int x
= pjpeg_read_scanlines(&activeDS
.jd
,&row
,1);
342 FIXME("failed to read current scanline?\n");
346 curoff
+= activeDS
.jd
.output_width
*activeDS
.jd
.output_components
;
348 pImageMemXfer
->Compression
= TWCP_NONE
;
349 pImageMemXfer
->BytesPerRow
= activeDS
.jd
.output_components
* activeDS
.jd
.output_width
;
350 pImageMemXfer
->Rows
= readrows
;
351 pImageMemXfer
->Columns
= activeDS
.jd
.output_width
; /* we do whole strips */
352 pImageMemXfer
->BytesWritten
= curoff
;
354 TransferringDialogBox(activeDS
.progressWnd
,0);
356 if (activeDS
.jd
.output_scanline
== activeDS
.jd
.output_height
) {
357 pjpeg_finish_decompress(&activeDS
.jd
);
358 pjpeg_destroy_decompress(&activeDS
.jd
);
359 gp_file_unref (activeDS
.file
);
360 activeDS
.file
= NULL
;
361 TRACE("xfer is done!\n");
363 /*TransferringDialogBox(activeDS.progressWnd, -1);*/
364 twRC
= TWRC_XFERDONE
;
366 activeDS
.twCC
= TWRC_SUCCESS
;
367 if (pImageMemXfer
->Memory
.Flags
& TWMF_HANDLE
)
368 LocalUnlock(pImageMemXfer
->Memory
.TheMem
);
375 /* DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET */
376 TW_UINT16
GPHOTO2_ImageNativeXferGet (pTW_IDENTITY pOrigin
,
380 pTW_UINT32 pHandle
= (pTW_UINT32
) pData
;
384 JSAMPROW samprow
, oldsamprow
;
386 FIXME("DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET: implemented, but expect program crash due to DIB.\n");
388 /* NOTE NOTE NOTE NOTE NOTE NOTE NOTE
390 * While this is a mandatory transfer mode and this function
391 * is correctly implemented and fully works, the calling program
392 * will likely crash after calling.
394 * Reason is that there is a lot of example code that does:
395 * bmpinfo = GlobalLock(hBITMAP); ... pointer access to bmpinfo
397 * Our current HBITMAP handles do not support getting GlobalLocked -> App Crash
399 * This needs a GDI Handle rewrite, at least for DIB sections.
402 if (activeDS
.currentState
!= 6) {
403 activeDS
.twCC
= TWCC_SEQERROR
;
406 if (TWRC_SUCCESS
!= _get_image_and_startup_jpeg()) {
407 FIXME("Failed to get an image\n");
408 activeDS
.twCC
= TWCC_OPERATIONERROR
;
411 TRACE("Acquiring image %dx%dx%d bits from gphoto.\n",
412 activeDS
.jd
.output_width
, activeDS
.jd
.output_height
,
413 activeDS
.jd
.output_components
*8);
414 ZeroMemory (&bmpInfo
, sizeof (BITMAPINFO
));
415 bmpInfo
.bmiHeader
.biSize
= sizeof (BITMAPINFOHEADER
);
416 bmpInfo
.bmiHeader
.biWidth
= activeDS
.jd
.output_width
;
417 bmpInfo
.bmiHeader
.biHeight
= -activeDS
.jd
.output_height
;
418 bmpInfo
.bmiHeader
.biPlanes
= 1;
419 bmpInfo
.bmiHeader
.biBitCount
= activeDS
.jd
.output_components
*8;
420 bmpInfo
.bmiHeader
.biCompression
= BI_RGB
;
421 bmpInfo
.bmiHeader
.biSizeImage
= 0;
422 bmpInfo
.bmiHeader
.biXPelsPerMeter
= 0;
423 bmpInfo
.bmiHeader
.biYPelsPerMeter
= 0;
424 bmpInfo
.bmiHeader
.biClrUsed
= 0;
425 bmpInfo
.bmiHeader
.biClrImportant
= 0;
426 hDIB
= CreateDIBSection (0, &bmpInfo
, DIB_RGB_COLORS
, (LPVOID
)&bits
, 0, 0);
428 FIXME("Failed creating DIB.\n");
429 gp_file_unref (activeDS
.file
);
430 activeDS
.file
= NULL
;
431 activeDS
.twCC
= TWCC_LOWMEMORY
;
434 samprow
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,activeDS
.jd
.output_width
*activeDS
.jd
.output_components
);
435 oldsamprow
= samprow
;
436 while ( activeDS
.jd
.output_scanline
<activeDS
.jd
.output_height
) {
438 int 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
, 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
) {
645 int x
= pjpeg_read_scanlines(&jd
,&samprow
,1);
647 FIXME("failed to read current scanline?\n");
650 /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */
651 for(i
=0;i
<jd
.output_width
;i
++,samprow
+=jd
.output_components
) {
652 *(bits
++) = *(samprow
+2);
653 *(bits
++) = *(samprow
+1);
654 *(bits
++) = *(samprow
);
656 bits
= (LPBYTE
)(((UINT_PTR
)bits
+ 3) & ~3);
657 samprow
= oldsamprow
;
659 HeapFree (GetProcessHeap(), 0, samprow
);
660 gp_file_unref (file
);