2 * Copyright 2000 Corel Corporation
3 * Copyright 2006 CodeWeavers, Aric Stewart
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(twain);
31 /* DG_IMAGE/DAT_IMAGEINFO/MSG_GET */
32 TW_UINT16 SANE_ImageInfoGet (pTW_IDENTITY pOrigin,
35 #ifndef SONAME_LIBSANE
38 TW_UINT16 twRC = TWRC_SUCCESS;
39 pTW_IMAGEINFO pImageInfo = (pTW_IMAGEINFO) pData;
43 TRACE("DG_IMAGE/DAT_IMAGEINFO/MSG_GET\n");
45 if (activeDS.currentState != 6 && activeDS.currentState != 7)
48 activeDS.twCC = TWCC_SEQERROR;
52 if (activeDS.currentState == 6)
54 /* return general image description information about the image about to be transferred */
55 status = psane_get_parameters (activeDS.deviceHandle, &activeDS.sane_param);
56 TRACE("Getting parameters\n");
57 if (status != SANE_STATUS_GOOD)
59 WARN("psane_get_parameters: %s\n", psane_strstatus (status));
60 psane_cancel (activeDS.deviceHandle);
61 activeDS.sane_started = FALSE;
62 activeDS.twCC = TWCC_OPERATIONERROR;
65 activeDS.sane_param_valid = TRUE;
68 if (sane_option_get_int(activeDS.deviceHandle, "resolution", &resolution) == SANE_STATUS_GOOD)
69 pImageInfo->XResolution.Whole = pImageInfo->YResolution.Whole = resolution;
71 pImageInfo->XResolution.Whole = pImageInfo->YResolution.Whole = -1;
72 pImageInfo->XResolution.Frac = 0;
73 pImageInfo->YResolution.Frac = 0;
74 pImageInfo->ImageWidth = activeDS.sane_param.pixels_per_line;
75 pImageInfo->ImageLength = activeDS.sane_param.lines;
77 TRACE("Bits per Sample %i\n",activeDS.sane_param.depth);
78 TRACE("Frame Format %i\n",activeDS.sane_param.format);
80 if (activeDS.sane_param.format == SANE_FRAME_RGB )
82 pImageInfo->BitsPerPixel = activeDS.sane_param.depth * 3;
83 pImageInfo->Compression = TWCP_NONE;
84 pImageInfo->Planar = TRUE;
85 pImageInfo->SamplesPerPixel = 3;
86 pImageInfo->BitsPerSample[0] = activeDS.sane_param.depth;
87 pImageInfo->BitsPerSample[1] = activeDS.sane_param.depth;
88 pImageInfo->BitsPerSample[2] = activeDS.sane_param.depth;
89 pImageInfo->PixelType = TWPT_RGB;
91 else if (activeDS.sane_param.format == SANE_FRAME_GRAY)
93 pImageInfo->BitsPerPixel = activeDS.sane_param.depth;
94 pImageInfo->Compression = TWCP_NONE;
95 pImageInfo->Planar = TRUE;
96 pImageInfo->SamplesPerPixel = 1;
97 pImageInfo->BitsPerSample[0] = activeDS.sane_param.depth;
98 if (activeDS.sane_param.depth == 1)
99 pImageInfo->PixelType = TWPT_BW;
101 pImageInfo->PixelType = TWPT_GRAY;
105 ERR("Unhandled source frame type %i\n",activeDS.sane_param.format);
107 activeDS.twCC = TWCC_SEQERROR;
115 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET */
116 TW_UINT16 SANE_ImageLayoutGet (pTW_IDENTITY pOrigin,
119 #ifndef SONAME_LIBSANE
122 TW_IMAGELAYOUT *img = (TW_IMAGELAYOUT *) pData;
123 SANE_Fixed tlx_current;
124 SANE_Fixed tly_current;
125 SANE_Fixed brx_current;
126 SANE_Fixed bry_current;
129 TRACE("DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET\n");
131 status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-x", &tlx_current, NULL, NULL, NULL, NULL);
132 if (status == SANE_STATUS_GOOD)
133 status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-y", &tly_current, NULL, NULL, NULL, NULL);
135 if (status == SANE_STATUS_GOOD)
136 status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-x", &brx_current, NULL, NULL, NULL, NULL);
138 if (status == SANE_STATUS_GOOD)
139 status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-y", &bry_current, NULL, NULL, NULL, NULL);
141 if (status != SANE_STATUS_GOOD)
143 activeDS.twCC = sane_status_to_twcc(status);
147 convert_sane_res_to_twain(SANE_UNFIX(tlx_current), SANE_UNIT_MM, &img->Frame.Left, TWUN_INCHES);
148 convert_sane_res_to_twain(SANE_UNFIX(tly_current), SANE_UNIT_MM, &img->Frame.Top, TWUN_INCHES);
149 convert_sane_res_to_twain(SANE_UNFIX(brx_current), SANE_UNIT_MM, &img->Frame.Right, TWUN_INCHES);
150 convert_sane_res_to_twain(SANE_UNFIX(bry_current), SANE_UNIT_MM, &img->Frame.Bottom, TWUN_INCHES);
152 img->DocumentNumber = 1;
154 img->FrameNumber = 1;
156 activeDS.twCC = TWCC_SUCCESS;
161 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GETDEFAULT */
162 TW_UINT16 SANE_ImageLayoutGetDefault (pTW_IDENTITY pOrigin,
170 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_RESET */
171 TW_UINT16 SANE_ImageLayoutReset (pTW_IDENTITY pOrigin,
179 #ifdef SONAME_LIBSANE
180 static TW_UINT16 set_one_imagecoord(const char *option_name, TW_FIX32 val, BOOL *changed)
182 double d = val.Whole + ((double) val.Frac / 65536.0);
185 status = sane_option_set_fixed(activeDS.deviceHandle, option_name,
186 SANE_FIX((d * 254) / 10), &set_status);
187 if (status != SANE_STATUS_GOOD)
189 activeDS.twCC = sane_status_to_twcc(status);
192 if (set_status & SANE_INFO_INEXACT)
198 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET */
199 TW_UINT16 SANE_ImageLayoutSet (pTW_IDENTITY pOrigin,
202 #ifndef SONAME_LIBSANE
205 TW_IMAGELAYOUT *img = (TW_IMAGELAYOUT *) pData;
206 BOOL changed = FALSE;
209 TRACE("DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET\n");
210 TRACE("Frame: [Left %x.%x|Top %x.%x|Right %x.%x|Bottom %x.%x]\n",
211 img->Frame.Left.Whole, img->Frame.Left.Frac,
212 img->Frame.Top.Whole, img->Frame.Top.Frac,
213 img->Frame.Right.Whole, img->Frame.Right.Frac,
214 img->Frame.Bottom.Whole, img->Frame.Bottom.Frac);
216 twrc = set_one_imagecoord("tl-x", img->Frame.Left, &changed);
217 if (twrc != TWRC_SUCCESS)
220 twrc = set_one_imagecoord("tl-y", img->Frame.Top, &changed);
221 if (twrc != TWRC_SUCCESS)
224 twrc = set_one_imagecoord("br-x", img->Frame.Right, &changed);
225 if (twrc != TWRC_SUCCESS)
228 twrc = set_one_imagecoord("br-y", img->Frame.Bottom, &changed);
229 if (twrc != TWRC_SUCCESS)
232 activeDS.twCC = TWCC_SUCCESS;
233 return changed ? TWRC_CHECKSTATUS : TWRC_SUCCESS;
237 /* DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET */
238 TW_UINT16 SANE_ImageMemXferGet (pTW_IDENTITY pOrigin,
241 #ifndef SONAME_LIBSANE
244 TW_UINT16 twRC = TWRC_SUCCESS;
245 pTW_IMAGEMEMXFER pImageMemXfer = (pTW_IMAGEMEMXFER) pData;
246 SANE_Status status = SANE_STATUS_GOOD;
248 TRACE ("DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET\n");
250 if (activeDS.currentState < 6 || activeDS.currentState > 7)
253 activeDS.twCC = TWCC_SEQERROR;
259 int consumed_len = 0;
263 /* Transfer an image from the source to the application */
264 if (activeDS.currentState == 6)
267 /* trigger scanning dialog */
268 activeDS.progressWnd = ScanningDialogBox(NULL,0);
270 ScanningDialogBox(activeDS.progressWnd,0);
272 if (! activeDS.sane_started)
274 status = psane_start (activeDS.deviceHandle);
275 if (status != SANE_STATUS_GOOD)
277 WARN("psane_start: %s\n", psane_strstatus (status));
278 psane_cancel (activeDS.deviceHandle);
279 activeDS.twCC = TWCC_OPERATIONERROR;
282 activeDS.sane_started = TRUE;
285 status = psane_get_parameters (activeDS.deviceHandle,
286 &activeDS.sane_param);
287 activeDS.sane_param_valid = TRUE;
289 if (status != SANE_STATUS_GOOD)
291 WARN("psane_get_parameters: %s\n", psane_strstatus (status));
292 psane_cancel (activeDS.deviceHandle);
293 activeDS.sane_started = FALSE;
294 activeDS.twCC = TWCC_OPERATIONERROR;
298 TRACE("Acquiring image %dx%dx%d bits (format=%d last=%d) from sane...\n"
299 , activeDS.sane_param.pixels_per_line, activeDS.sane_param.lines,
300 activeDS.sane_param.depth, activeDS.sane_param.format,
301 activeDS.sane_param.last_frame);
303 activeDS.currentState = 7;
306 /* access memory buffer */
307 if (pImageMemXfer->Memory.Length < activeDS.sane_param.bytes_per_line)
309 psane_cancel (activeDS.deviceHandle);
310 activeDS.sane_started = FALSE;
311 activeDS.twCC = TWCC_BADVALUE;
315 if (pImageMemXfer->Memory.Flags & TWMF_HANDLE)
317 FIXME("Memory Handle, may not be locked correctly\n");
318 buffer = LocalLock(pImageMemXfer->Memory.TheMem);
321 buffer = pImageMemXfer->Memory.TheMem;
323 memset(buffer,0,pImageMemXfer->Memory.Length);
327 rows = pImageMemXfer->Memory.Length / activeDS.sane_param.bytes_per_line;
329 /* must fill full lines */
330 while (consumed_len < (activeDS.sane_param.bytes_per_line*rows) &&
331 status == SANE_STATUS_GOOD)
333 status = psane_read (activeDS.deviceHandle, ptr,
334 (activeDS.sane_param.bytes_per_line*rows) - consumed_len ,
336 consumed_len += buff_len;
340 if (status == SANE_STATUS_GOOD || status == SANE_STATUS_EOF)
342 pImageMemXfer->Compression = TWCP_NONE;
343 pImageMemXfer->BytesPerRow = activeDS.sane_param.bytes_per_line;
344 pImageMemXfer->Columns = activeDS.sane_param.pixels_per_line;
345 pImageMemXfer->Rows = rows;
346 pImageMemXfer->XOffset = 0;
347 pImageMemXfer->YOffset = 0;
348 pImageMemXfer->BytesWritten = consumed_len;
350 ScanningDialogBox(activeDS.progressWnd, consumed_len);
352 if (status == SANE_STATUS_EOF)
354 ScanningDialogBox(activeDS.progressWnd, -1);
355 TRACE("psane_read: %s\n", psane_strstatus (status));
356 psane_cancel (activeDS.deviceHandle);
357 activeDS.sane_started = FALSE;
358 twRC = TWRC_XFERDONE;
360 activeDS.twCC = TWRC_SUCCESS;
362 else if (status != SANE_STATUS_EOF)
364 ScanningDialogBox(activeDS.progressWnd, -1);
365 WARN("psane_read: %s\n", psane_strstatus (status));
366 psane_cancel (activeDS.deviceHandle);
367 activeDS.sane_started = FALSE;
368 activeDS.twCC = TWCC_OPERATIONERROR;
373 if (pImageMemXfer->Memory.Flags & TWMF_HANDLE)
374 LocalUnlock(pImageMemXfer->Memory.TheMem);
380 #ifdef SONAME_LIBSANE
381 static SANE_Status read_one_line(SANE_Handle h, BYTE *line, int len)
389 status = psane_read (activeDS.deviceHandle, line, len, &read_len);
390 if (status != SANE_STATUS_GOOD)
404 /* DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET */
405 TW_UINT16 SANE_ImageNativeXferGet (pTW_IDENTITY pOrigin,
408 #ifndef SONAME_LIBSANE
411 TW_UINT16 twRC = TWRC_SUCCESS;
412 pTW_UINT32 pHandle = (pTW_UINT32) pData;
415 BITMAPINFOHEADER *header = NULL;
417 int dib_bytes_per_line;
424 TRACE("DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET\n");
426 if (activeDS.currentState != 6)
429 activeDS.twCC = TWCC_SEQERROR;
433 /* Transfer an image from the source to the application */
434 if (! activeDS.sane_started)
436 status = psane_start (activeDS.deviceHandle);
437 if (status != SANE_STATUS_GOOD)
439 WARN("psane_start: %s\n", psane_strstatus (status));
440 psane_cancel (activeDS.deviceHandle);
441 activeDS.twCC = TWCC_OPERATIONERROR;
444 activeDS.sane_started = TRUE;
447 status = psane_get_parameters (activeDS.deviceHandle, &activeDS.sane_param);
448 activeDS.sane_param_valid = TRUE;
449 if (status != SANE_STATUS_GOOD)
451 WARN("psane_get_parameters: %s\n", psane_strstatus (status));
452 psane_cancel (activeDS.deviceHandle);
453 activeDS.sane_started = FALSE;
454 activeDS.twCC = TWCC_OPERATIONERROR;
458 if (activeDS.sane_param.format == SANE_FRAME_GRAY)
460 if (activeDS.sane_param.depth == 8)
461 color_size = (1 << 8) * sizeof(*colors);
462 else if (activeDS.sane_param.depth == 1)
466 FIXME("For NATIVE, we support only 1 bit monochrome and 8 bit Grayscale, not %d\n", activeDS.sane_param.depth);
467 psane_cancel (activeDS.deviceHandle);
468 activeDS.sane_started = FALSE;
469 activeDS.twCC = TWCC_OPERATIONERROR;
473 else if (activeDS.sane_param.format != SANE_FRAME_RGB)
475 FIXME("For NATIVE, we support only GRAY and RGB, not %d\n", activeDS.sane_param.format);
476 psane_cancel (activeDS.deviceHandle);
477 activeDS.sane_started = FALSE;
478 activeDS.twCC = TWCC_OPERATIONERROR;
482 TRACE("Acquiring image %dx%dx%d bits (format=%d last=%d bpl=%d) from sane...\n"
483 , activeDS.sane_param.pixels_per_line, activeDS.sane_param.lines,
484 activeDS.sane_param.depth, activeDS.sane_param.format,
485 activeDS.sane_param.last_frame, activeDS.sane_param.bytes_per_line);
487 dib_bytes_per_line = ((activeDS.sane_param.bytes_per_line + 3) / 4) * 4;
488 dib_bytes = activeDS.sane_param.lines * dib_bytes_per_line;
490 hDIB = GlobalAlloc(GMEM_ZEROINIT, dib_bytes + sizeof(*header) + color_size);
492 header = GlobalLock(hDIB);
496 psane_cancel (activeDS.deviceHandle);
497 activeDS.sane_started = FALSE;
498 activeDS.twCC = TWCC_LOWMEMORY;
504 header->biSize = sizeof (*header);
505 header->biWidth = activeDS.sane_param.pixels_per_line;
506 header->biHeight = activeDS.sane_param.lines;
507 header->biPlanes = 1;
508 header->biCompression = BI_RGB;
509 if (activeDS.sane_param.format == SANE_FRAME_RGB)
510 header->biBitCount = activeDS.sane_param.depth * 3;
511 if (activeDS.sane_param.format == SANE_FRAME_GRAY)
512 header->biBitCount = activeDS.sane_param.depth;
513 header->biSizeImage = dib_bytes;
514 header->biXPelsPerMeter = 0;
515 header->biYPelsPerMeter = 0;
516 header->biClrUsed = 0;
517 header->biClrImportant = 0;
519 p = (BYTE *)(header + 1);
523 colors = (RGBQUAD *) p;
525 for (i = 0; i < (color_size / sizeof(*colors)); i++)
526 colors[i].rgbBlue = colors[i].rgbRed = colors[i].rgbGreen = i;
530 /* Sane returns data in top down order. Acrobat does best with
531 a bottom up DIB being returned. */
532 line = p + (activeDS.sane_param.lines - 1) * dib_bytes_per_line;
533 for (i = activeDS.sane_param.lines - 1; i >= 0; i--)
535 activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd,
536 ((activeDS.sane_param.lines - 1 - i) * 100)
538 (activeDS.sane_param.lines - 1));
540 status = read_one_line(activeDS.deviceHandle, line,
541 activeDS.sane_param.bytes_per_line);
542 if (status != SANE_STATUS_GOOD)
545 line -= dib_bytes_per_line;
547 activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd, -1);
551 if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF)
553 WARN("psane_read: %s, reading line %d\n", psane_strstatus(status), i);
554 psane_cancel (activeDS.deviceHandle);
555 activeDS.sane_started = FALSE;
556 activeDS.twCC = TWCC_OPERATIONERROR;
561 psane_cancel (activeDS.deviceHandle);
562 activeDS.sane_started = FALSE;
563 *pHandle = (UINT_PTR)hDIB;
564 twRC = TWRC_XFERDONE;
565 activeDS.twCC = TWCC_SUCCESS;
566 activeDS.currentState = 7;