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
25 #include "gphoto2_i.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(twain
);
33 /* for the jpeg decompressor source manager. */
34 static void _jpeg_init_source(j_decompress_ptr cinfo
) { }
36 static boolean
_jpeg_fill_input_buffer(j_decompress_ptr cinfo
) {
37 ERR("(), should not get here.\n");
41 static void _jpeg_skip_input_data(j_decompress_ptr cinfo
,long num_bytes
) {
42 TRACE("Skipping %ld bytes...\n", num_bytes
);
43 cinfo
->src
->next_input_byte
+= num_bytes
;
44 cinfo
->src
->bytes_in_buffer
-= num_bytes
;
47 static boolean
_jpeg_resync_to_restart(j_decompress_ptr cinfo
, int desired
) {
48 ERR("(desired=%d), should not get here.\n",desired
);
51 static void _jpeg_term_source(j_decompress_ptr cinfo
) { }
53 static void close_file( UINT64 handle
)
55 struct close_file_params params
= { handle
};
56 GPHOTO2_CALL( close_file
, ¶ms
);
59 static void close_current_file(void)
61 close_file( activeDS
.file_handle
);
62 activeDS
.file_handle
= 0;
63 free( activeDS
.file_data
);
66 /* DG_IMAGE/DAT_CIECOLOR/MSG_GET */
67 TW_UINT16
GPHOTO2_CIEColorGet (pTW_IDENTITY pOrigin
,
75 /* DG_IMAGE/DAT_EXTIMAGEINFO/MSG_GET */
76 TW_UINT16
GPHOTO2_ExtImageInfoGet (pTW_IDENTITY pOrigin
,
84 /* DG_IMAGE/DAT_GRAYRESPONSE/MSG_RESET */
85 TW_UINT16
GPHOTO2_GrayResponseReset (pTW_IDENTITY pOrigin
,
93 /* DG_IMAGE/DAT_GRAYRESPONSE/MSG_SET */
94 TW_UINT16
GPHOTO2_GrayResponseSet (pTW_IDENTITY pOrigin
,
102 /* DG_IMAGE/DAT_IMAGEFILEXFER/MSG_GET */
103 TW_UINT16
GPHOTO2_ImageFileXferGet (pTW_IDENTITY pOrigin
,
111 static TW_UINT16
_get_image_and_startup_jpeg(void) {
114 struct open_file_params open_params
;
115 struct get_file_data_params get_data_params
;
117 if (activeDS
.file_handle
) /* Already loaded. */
120 for (i
= 0; i
< activeDS
.file_count
; i
++)
122 if (activeDS
.download_flags
[i
])
124 activeDS
.download_flags
[i
] = FALSE
; /* mark as done */
128 if (i
== activeDS
.file_count
)
130 activeDS
.twCC
= TWCC_SEQERROR
;
135 open_params
.preview
= FALSE
;
136 open_params
.handle
= &activeDS
.file_handle
;
137 open_params
.size
= &activeDS
.file_size
;
138 if (GPHOTO2_CALL( open_file
, &open_params
))
140 activeDS
.twCC
= TWCC_SEQERROR
;
144 activeDS
.file_data
= malloc( activeDS
.file_size
);
145 get_data_params
.handle
= activeDS
.file_handle
;
146 get_data_params
.data
= activeDS
.file_data
;
147 get_data_params
.size
= activeDS
.file_size
;
148 if (GPHOTO2_CALL( get_file_data
, &get_data_params
))
150 activeDS
.twCC
= TWCC_SEQERROR
;
154 /* This is basically so we can use in-memory data for jpeg decompression.
155 * We need to have all the functions.
157 activeDS
.xjsm
.next_input_byte
= activeDS
.file_data
;
158 activeDS
.xjsm
.bytes_in_buffer
= activeDS
.file_size
;
159 activeDS
.xjsm
.init_source
= _jpeg_init_source
;
160 activeDS
.xjsm
.fill_input_buffer
= _jpeg_fill_input_buffer
;
161 activeDS
.xjsm
.skip_input_data
= _jpeg_skip_input_data
;
162 activeDS
.xjsm
.resync_to_restart
= _jpeg_resync_to_restart
;
163 activeDS
.xjsm
.term_source
= _jpeg_term_source
;
165 activeDS
.jd
.err
= jpeg_std_error(&activeDS
.jerr
);
166 /* jpeg_create_decompress is a macro that expands to jpeg_CreateDecompress - see jpeglib.h
167 * jpeg_create_decompress(&jd); */
168 jpeg_CreateDecompress(&activeDS
.jd
, JPEG_LIB_VERSION
, sizeof(struct jpeg_decompress_struct
));
169 activeDS
.jd
.src
= &activeDS
.xjsm
;
170 ret
=jpeg_read_header(&activeDS
.jd
,TRUE
);
171 activeDS
.jd
.out_color_space
= JCS_RGB
;
172 jpeg_start_decompress(&activeDS
.jd
);
173 if (ret
!= JPEG_HEADER_OK
) {
174 ERR("Jpeg image in stream has bad format, read header returned %d.\n",ret
);
175 close_current_file();
181 /* DG_IMAGE/DAT_IMAGEINFO/MSG_GET */
182 TW_UINT16
GPHOTO2_ImageInfoGet (pTW_IDENTITY pOrigin
,
185 pTW_IMAGEINFO pImageInfo
= (pTW_IMAGEINFO
) pData
;
187 TRACE("DG_IMAGE/DAT_IMAGEINFO/MSG_GET\n");
189 if (activeDS
.currentState
!= 6 && activeDS
.currentState
!= 7) {
190 activeDS
.twCC
= TWCC_SEQERROR
;
193 if (TWRC_SUCCESS
!= _get_image_and_startup_jpeg()) {
194 FIXME("Failed to get an image\n");
195 activeDS
.twCC
= TWCC_SEQERROR
;
198 if (activeDS
.currentState
== 6)
200 /* return general image description information about the image about to be transferred */
201 TRACE("Getting parameters\n");
203 TRACE("activeDS.jd.output_width = %d\n", activeDS
.jd
.output_width
);
204 TRACE("activeDS.jd.output_height = %d\n", activeDS
.jd
.output_height
);
205 pImageInfo
->Compression
= TWCP_NONE
;
206 pImageInfo
->SamplesPerPixel
= 3;
207 pImageInfo
->BitsPerSample
[0]= 8;
208 pImageInfo
->BitsPerSample
[1]= 8;
209 pImageInfo
->BitsPerSample
[2]= 8;
210 pImageInfo
->PixelType
= TWPT_RGB
;
211 pImageInfo
->Planar
= FALSE
; /* R-G-B is chunky! */
212 pImageInfo
->XResolution
.Whole
= -1;
213 pImageInfo
->XResolution
.Frac
= 0;
214 pImageInfo
->YResolution
.Whole
= -1;
215 pImageInfo
->YResolution
.Frac
= 0;
216 pImageInfo
->ImageWidth
= activeDS
.jd
.output_width
;
217 pImageInfo
->ImageLength
= activeDS
.jd
.output_height
;
218 pImageInfo
->BitsPerPixel
= 24;
222 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET */
223 TW_UINT16
GPHOTO2_ImageLayoutGet (pTW_IDENTITY pOrigin
,
231 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GETDEFAULT */
232 TW_UINT16
GPHOTO2_ImageLayoutGetDefault (pTW_IDENTITY pOrigin
,
240 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_RESET */
241 TW_UINT16
GPHOTO2_ImageLayoutReset (pTW_IDENTITY pOrigin
,
249 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET */
250 TW_UINT16
GPHOTO2_ImageLayoutSet (pTW_IDENTITY pOrigin
,
258 /* DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET */
259 TW_UINT16
GPHOTO2_ImageMemXferGet (pTW_IDENTITY pOrigin
,
262 TW_UINT16 twRC
= TWRC_SUCCESS
;
263 pTW_IMAGEMEMXFER pImageMemXfer
= (pTW_IMAGEMEMXFER
) pData
;
268 TRACE ("DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET\n");
269 if (activeDS
.currentState
< 6 || activeDS
.currentState
> 7) {
270 activeDS
.twCC
= TWCC_SEQERROR
;
273 TRACE("pImageMemXfer.Compression is %d\n", pImageMemXfer
->Compression
);
274 if (activeDS
.currentState
== 6) {
275 if (TWRC_SUCCESS
!= _get_image_and_startup_jpeg()) {
276 FIXME("Failed to get an image\n");
277 activeDS
.twCC
= TWCC_SEQERROR
;
281 if (!activeDS
.progressWnd
)
282 activeDS
.progressWnd
= TransferringDialogBox(NULL
,0);
283 TransferringDialogBox(activeDS
.progressWnd
,0);
285 activeDS
.currentState
= 7;
287 if (!activeDS
.file_handle
) {
288 activeDS
.twCC
= TWRC_SUCCESS
;
289 return TWRC_XFERDONE
;
293 if (pImageMemXfer
->Memory
.Flags
& TWMF_HANDLE
) {
294 FIXME("Memory Handle, may not be locked correctly\n");
295 buffer
= LocalLock(pImageMemXfer
->Memory
.TheMem
);
297 buffer
= pImageMemXfer
->Memory
.TheMem
;
299 memset(buffer
,0,pImageMemXfer
->Memory
.Length
);
300 curoff
= 0; readrows
= 0;
301 pImageMemXfer
->YOffset
= activeDS
.jd
.output_scanline
;
302 pImageMemXfer
->XOffset
= 0; /* we do whole strips */
303 while ((activeDS
.jd
.output_scanline
<activeDS
.jd
.output_height
) &&
304 ((pImageMemXfer
->Memory
.Length
- curoff
) > activeDS
.jd
.output_width
*activeDS
.jd
.output_components
)
306 JSAMPROW row
= buffer
+curoff
;
307 int x
= jpeg_read_scanlines(&activeDS
.jd
,&row
,1);
309 FIXME("failed to read current scanline?\n");
313 curoff
+= activeDS
.jd
.output_width
*activeDS
.jd
.output_components
;
315 pImageMemXfer
->Compression
= TWCP_NONE
;
316 pImageMemXfer
->BytesPerRow
= activeDS
.jd
.output_components
* activeDS
.jd
.output_width
;
317 pImageMemXfer
->Rows
= readrows
;
318 pImageMemXfer
->Columns
= activeDS
.jd
.output_width
; /* we do whole strips */
319 pImageMemXfer
->BytesWritten
= curoff
;
321 TransferringDialogBox(activeDS
.progressWnd
,0);
323 if (activeDS
.jd
.output_scanline
== activeDS
.jd
.output_height
) {
324 jpeg_finish_decompress(&activeDS
.jd
);
325 jpeg_destroy_decompress(&activeDS
.jd
);
326 close_current_file();
327 TRACE("xfer is done!\n");
329 /*TransferringDialogBox(activeDS.progressWnd, -1);*/
330 twRC
= TWRC_XFERDONE
;
332 activeDS
.twCC
= TWRC_SUCCESS
;
333 if (pImageMemXfer
->Memory
.Flags
& TWMF_HANDLE
)
334 LocalUnlock(pImageMemXfer
->Memory
.TheMem
);
338 /* DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET */
339 TW_UINT16
GPHOTO2_ImageNativeXferGet (pTW_IDENTITY pOrigin
,
342 pTW_UINT32 pHandle
= (pTW_UINT32
) pData
;
346 JSAMPROW samprow
, oldsamprow
;
348 FIXME("DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET: implemented, but expect program crash due to DIB.\n");
350 /* NOTE NOTE NOTE NOTE NOTE NOTE NOTE
352 * While this is a mandatory transfer mode and this function
353 * is correctly implemented and fully works, the calling program
354 * will likely crash after calling.
356 * Reason is that there is a lot of example code that does:
357 * bmpinfo = GlobalLock(hBITMAP); ... pointer access to bmpinfo
359 * Our current HBITMAP handles do not support getting GlobalLocked -> App Crash
361 * This needs a GDI Handle rewrite, at least for DIB sections.
364 if (activeDS
.currentState
!= 6) {
365 activeDS
.twCC
= TWCC_SEQERROR
;
368 if (TWRC_SUCCESS
!= _get_image_and_startup_jpeg()) {
369 FIXME("Failed to get an image\n");
370 activeDS
.twCC
= TWCC_OPERATIONERROR
;
373 TRACE("Acquiring image %dx%dx%d bits from gphoto.\n",
374 activeDS
.jd
.output_width
, activeDS
.jd
.output_height
,
375 activeDS
.jd
.output_components
*8);
376 ZeroMemory (&bmpInfo
, sizeof (BITMAPINFO
));
377 bmpInfo
.bmiHeader
.biSize
= sizeof (BITMAPINFOHEADER
);
378 bmpInfo
.bmiHeader
.biWidth
= activeDS
.jd
.output_width
;
379 bmpInfo
.bmiHeader
.biHeight
= -activeDS
.jd
.output_height
;
380 bmpInfo
.bmiHeader
.biPlanes
= 1;
381 bmpInfo
.bmiHeader
.biBitCount
= activeDS
.jd
.output_components
*8;
382 bmpInfo
.bmiHeader
.biCompression
= BI_RGB
;
383 bmpInfo
.bmiHeader
.biSizeImage
= 0;
384 bmpInfo
.bmiHeader
.biXPelsPerMeter
= 0;
385 bmpInfo
.bmiHeader
.biYPelsPerMeter
= 0;
386 bmpInfo
.bmiHeader
.biClrUsed
= 0;
387 bmpInfo
.bmiHeader
.biClrImportant
= 0;
388 hDIB
= CreateDIBSection (0, &bmpInfo
, DIB_RGB_COLORS
, (LPVOID
)&bits
, 0, 0);
390 FIXME("Failed creating DIB.\n");
391 close_current_file();
392 activeDS
.twCC
= TWCC_LOWMEMORY
;
395 samprow
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,activeDS
.jd
.output_width
*activeDS
.jd
.output_components
);
396 oldsamprow
= samprow
;
397 while ( activeDS
.jd
.output_scanline
<activeDS
.jd
.output_height
) {
399 int x
= jpeg_read_scanlines(&activeDS
.jd
,&samprow
,1);
401 FIXME("failed to read current scanline?\n");
404 /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */
405 for(i
=0;i
<activeDS
.jd
.output_width
;i
++,samprow
+=activeDS
.jd
.output_components
) {
406 *(bits
++) = *(samprow
+2);
407 *(bits
++) = *(samprow
+1);
408 *(bits
++) = *(samprow
);
410 bits
= (LPBYTE
)(((UINT_PTR
)bits
+ 3) & ~3);
411 samprow
= oldsamprow
;
413 HeapFree (GetProcessHeap(), 0, samprow
);
414 close_current_file();
415 *pHandle
= (UINT_PTR
)hDIB
;
416 activeDS
.twCC
= TWCC_SUCCESS
;
417 activeDS
.currentState
= 7;
418 return TWRC_XFERDONE
;
421 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_GET */
422 TW_UINT16
GPHOTO2_JPEGCompressionGet (pTW_IDENTITY pOrigin
,
430 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_GETDEFAULT */
431 TW_UINT16
GPHOTO2_JPEGCompressionGetDefault (pTW_IDENTITY pOrigin
,
440 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_RESET */
441 TW_UINT16
GPHOTO2_JPEGCompressionReset (pTW_IDENTITY pOrigin
,
449 /* DG_IMAGE/DAT_JPEGCOMPRESSION/MSG_SET */
450 TW_UINT16
GPHOTO2_JPEGCompressionSet (pTW_IDENTITY pOrigin
,
458 /* DG_IMAGE/DAT_PALETTE8/MSG_GET */
459 TW_UINT16
GPHOTO2_Palette8Get (pTW_IDENTITY pOrigin
,
467 /* DG_IMAGE/DAT_PALETTE8/MSG_GETDEFAULT */
468 TW_UINT16
GPHOTO2_Palette8GetDefault (pTW_IDENTITY pOrigin
,
476 /* DG_IMAGE/DAT_PALETTE8/MSG_RESET */
477 TW_UINT16
GPHOTO2_Palette8Reset (pTW_IDENTITY pOrigin
,
485 /* DG_IMAGE/DAT_PALETTE8/MSG_SET */
486 TW_UINT16
GPHOTO2_Palette8Set (pTW_IDENTITY pOrigin
,
494 /* DG_IMAGE/DAT_RGBRESPONSE/MSG_RESET */
495 TW_UINT16
GPHOTO2_RGBResponseReset (pTW_IDENTITY pOrigin
,
503 /* DG_IMAGE/DAT_RGBRESPONSE/MSG_SET */
504 TW_UINT16
GPHOTO2_RGBResponseSet (pTW_IDENTITY pOrigin
,
513 _get_gphoto2_file_as_DIB( unsigned int idx
, BOOL preview
, HWND hwnd
, HBITMAP
*hDIB
)
515 unsigned char *filedata
;
517 struct jpeg_source_mgr xjsm
;
518 struct jpeg_decompress_struct jd
;
519 struct jpeg_error_mgr jerr
;
522 JSAMPROW samprow
, oldsamprow
;
523 struct open_file_params open_params
;
524 struct get_file_data_params get_data_params
;
526 unsigned int filesize
;
528 open_params
.idx
= idx
;
529 open_params
.preview
= preview
;
530 open_params
.handle
= &file_handle
;
531 open_params
.size
= &filesize
;
532 if (GPHOTO2_CALL( open_file
, &open_params
))
534 FIXME( "Failed to get file %u\n", idx
);
537 filedata
= malloc( filesize
);
538 get_data_params
.handle
= file_handle
;
539 get_data_params
.data
= filedata
;
540 get_data_params
.size
= filesize
;
541 if (GPHOTO2_CALL( get_file_data
, &get_data_params
))
543 close_file( file_handle
);
548 /* FIXME: Actually we might get other types than JPEG ... But only handle JPEG for now */
549 if (filedata
[0] != 0xff) {
550 ERR("File %u might not be JPEG, cannot decode!\n", idx
);
553 /* This is basically so we can use in-memory data for jpeg decompression.
554 * We need to have all the functions.
556 xjsm
.next_input_byte
= filedata
;
557 xjsm
.bytes_in_buffer
= filesize
;
558 xjsm
.init_source
= _jpeg_init_source
;
559 xjsm
.fill_input_buffer
= _jpeg_fill_input_buffer
;
560 xjsm
.skip_input_data
= _jpeg_skip_input_data
;
561 xjsm
.resync_to_restart
= _jpeg_resync_to_restart
;
562 xjsm
.term_source
= _jpeg_term_source
;
564 jd
.err
= jpeg_std_error(&jerr
);
565 /* jpeg_create_decompress is a macro that expands to jpeg_CreateDecompress - see jpeglib.h
566 * jpeg_create_decompress(&jd); */
567 jpeg_CreateDecompress(&jd
, JPEG_LIB_VERSION
, sizeof(struct jpeg_decompress_struct
));
569 ret
=jpeg_read_header(&jd
,TRUE
);
570 jd
.out_color_space
= JCS_RGB
;
571 jpeg_start_decompress(&jd
);
572 if (ret
!= JPEG_HEADER_OK
) {
573 ERR("Jpeg image in stream has bad format, read header returned %d.\n",ret
);
574 close_file( file_handle
);
579 ZeroMemory (&bmpInfo
, sizeof (BITMAPINFO
));
580 bmpInfo
.bmiHeader
.biSize
= sizeof (BITMAPINFOHEADER
);
581 bmpInfo
.bmiHeader
.biWidth
= jd
.output_width
;
582 bmpInfo
.bmiHeader
.biHeight
= -jd
.output_height
;
583 bmpInfo
.bmiHeader
.biPlanes
= 1;
584 bmpInfo
.bmiHeader
.biBitCount
= jd
.output_components
*8;
585 bmpInfo
.bmiHeader
.biCompression
= BI_RGB
;
586 bmpInfo
.bmiHeader
.biSizeImage
= 0;
587 bmpInfo
.bmiHeader
.biXPelsPerMeter
= 0;
588 bmpInfo
.bmiHeader
.biYPelsPerMeter
= 0;
589 bmpInfo
.bmiHeader
.biClrUsed
= 0;
590 bmpInfo
.bmiHeader
.biClrImportant
= 0;
591 *hDIB
= CreateDIBSection(0, &bmpInfo
, DIB_RGB_COLORS
, (LPVOID
)&bits
, 0, 0);
593 FIXME("Failed creating DIB.\n");
594 close_file( file_handle
);
598 samprow
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,jd
.output_width
*jd
.output_components
);
599 oldsamprow
= samprow
;
600 while ( jd
.output_scanline
<jd
.output_height
) {
602 int x
= jpeg_read_scanlines(&jd
,&samprow
,1);
604 FIXME("failed to read current scanline?\n");
607 /* We have to convert from RGB to BGR, see MSDN/ BITMAPINFOHEADER */
608 for(i
=0;i
<jd
.output_width
;i
++,samprow
+=jd
.output_components
) {
609 *(bits
++) = *(samprow
+2);
610 *(bits
++) = *(samprow
+1);
611 *(bits
++) = *(samprow
);
613 bits
= (LPBYTE
)(((UINT_PTR
)bits
+ 3) & ~3);
614 samprow
= oldsamprow
;
616 HeapFree (GetProcessHeap(), 0, samprow
);
617 close_file( file_handle
);