wineps.drv: Ignore requested resolutions not supported by device.
[wine] / dlls / sane.ds / ds_image.c
1 /*
2  * Copyright 2000 Corel Corporation
3  * Copyright 2006 CodeWeavers, Aric Stewart
4  *
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.
9  *
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.
14  *
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
18  */
19
20 #include "config.h"
21
22 #include <stdarg.h>
23
24 #include "sane_i.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "wine/debug.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(twain);
30
31 /* DG_IMAGE/DAT_IMAGEINFO/MSG_GET */
32 TW_UINT16 SANE_ImageInfoGet (pTW_IDENTITY pOrigin, 
33                               TW_MEMREF pData)
34 {
35 #ifndef SONAME_LIBSANE
36     return TWRC_FAILURE;
37 #else
38     TW_UINT16 twRC = TWRC_SUCCESS;
39     pTW_IMAGEINFO pImageInfo = (pTW_IMAGEINFO) pData;
40     SANE_Status status;
41     SANE_Int resolution;
42
43     TRACE("DG_IMAGE/DAT_IMAGEINFO/MSG_GET\n");
44
45     if (activeDS.currentState != 6 && activeDS.currentState != 7)
46     {
47         twRC = TWRC_FAILURE;
48         activeDS.twCC = TWCC_SEQERROR;
49     }
50     else
51     {
52         if (activeDS.currentState == 6)
53         {
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)
58             {
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;
63                 return TWRC_FAILURE;
64             }
65             activeDS.sane_param_valid = TRUE;
66         }
67
68         if (sane_option_get_int(activeDS.deviceHandle, "resolution", &resolution) == SANE_STATUS_GOOD)
69             pImageInfo->XResolution.Whole = pImageInfo->YResolution.Whole = resolution;
70         else
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;
76
77         TRACE("Bits per Sample %i\n",activeDS.sane_param.depth);
78         TRACE("Frame Format %i\n",activeDS.sane_param.format);
79
80         if (activeDS.sane_param.format == SANE_FRAME_RGB )
81         {
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;
90         }
91         else if (activeDS.sane_param.format == SANE_FRAME_GRAY)
92         {
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;
100             else
101                 pImageInfo->PixelType = TWPT_GRAY;
102         }
103         else
104         {
105             ERR("Unhandled source frame type %i\n",activeDS.sane_param.format);
106             twRC = TWRC_FAILURE;
107             activeDS.twCC = TWCC_SEQERROR;
108         }
109     }
110
111     return twRC;
112 #endif
113 }
114
115 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET */
116 TW_UINT16 SANE_ImageLayoutGet (pTW_IDENTITY pOrigin,
117                                 TW_MEMREF pData)
118 {
119 #ifndef SONAME_LIBSANE
120     return TWRC_FAILURE;
121 #else
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;
127     SANE_Status status;
128
129     TRACE("DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET\n");
130
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);
134
135     if (status == SANE_STATUS_GOOD)
136         status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-x", &brx_current, NULL, NULL, NULL, NULL);
137
138     if (status == SANE_STATUS_GOOD)
139         status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-y", &bry_current, NULL, NULL, NULL, NULL);
140
141     if (status != SANE_STATUS_GOOD)
142     {
143         activeDS.twCC = sane_status_to_twcc(status);
144         return TWRC_FAILURE;
145     }
146
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);
151
152     img->DocumentNumber = 1;
153     img->PageNumber = 1;
154     img->FrameNumber = 1;
155
156     activeDS.twCC = TWCC_SUCCESS;
157     return TWRC_SUCCESS;
158 #endif
159 }
160
161 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GETDEFAULT */
162 TW_UINT16 SANE_ImageLayoutGetDefault (pTW_IDENTITY pOrigin, 
163                                        TW_MEMREF pData)
164 {
165     FIXME ("stub!\n");
166
167     return TWRC_FAILURE;
168 }
169
170 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_RESET */
171 TW_UINT16 SANE_ImageLayoutReset (pTW_IDENTITY pOrigin, 
172                                   TW_MEMREF pData)
173 {
174     FIXME ("stub!\n");
175
176     return TWRC_FAILURE;
177 }
178
179 #ifdef SONAME_LIBSANE
180 static TW_UINT16 set_one_imagecoord(const char *option_name, TW_FIX32 val, BOOL *changed)
181 {
182     double d = val.Whole + ((double) val.Frac / 65536.0);
183     int set_status = 0;
184     SANE_Status status;
185     status = sane_option_set_fixed(activeDS.deviceHandle, option_name,
186              SANE_FIX((d * 254) / 10), &set_status);
187     if (status != SANE_STATUS_GOOD)
188     {
189         activeDS.twCC = sane_status_to_twcc(status);
190         return TWRC_FAILURE;
191     }
192     if (set_status & SANE_INFO_INEXACT)
193         *changed = TRUE;
194     return TWRC_SUCCESS;
195 }
196 #endif
197
198 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET */
199 TW_UINT16 SANE_ImageLayoutSet (pTW_IDENTITY pOrigin,
200                                 TW_MEMREF pData)
201 {
202 #ifndef SONAME_LIBSANE
203     return TWRC_FAILURE;
204 #else
205     TW_IMAGELAYOUT *img = (TW_IMAGELAYOUT *) pData;
206     BOOL changed = FALSE;
207     TW_UINT16 twrc;
208
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);
215
216     twrc = set_one_imagecoord("tl-x", img->Frame.Left, &changed);
217     if (twrc != TWRC_SUCCESS)
218         return (twrc);
219
220     twrc = set_one_imagecoord("tl-y", img->Frame.Top, &changed);
221     if (twrc != TWRC_SUCCESS)
222         return (twrc);
223
224     twrc = set_one_imagecoord("br-x", img->Frame.Right, &changed);
225     if (twrc != TWRC_SUCCESS)
226         return (twrc);
227
228     twrc = set_one_imagecoord("br-y", img->Frame.Bottom, &changed);
229     if (twrc != TWRC_SUCCESS)
230         return (twrc);
231
232     activeDS.twCC = TWCC_SUCCESS;
233     return changed ? TWRC_CHECKSTATUS : TWRC_SUCCESS;
234 #endif
235 }
236
237 /* DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET */
238 TW_UINT16 SANE_ImageMemXferGet (pTW_IDENTITY pOrigin, 
239                                  TW_MEMREF pData)
240 {
241 #ifndef SONAME_LIBSANE
242     return TWRC_FAILURE;
243 #else
244     TW_UINT16 twRC = TWRC_SUCCESS;
245     pTW_IMAGEMEMXFER pImageMemXfer = (pTW_IMAGEMEMXFER) pData;
246     SANE_Status status = SANE_STATUS_GOOD;
247
248     TRACE ("DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET\n");
249
250     if (activeDS.currentState < 6 || activeDS.currentState > 7)
251     {
252         twRC = TWRC_FAILURE;
253         activeDS.twCC = TWCC_SEQERROR;
254     }
255     else
256     {
257         LPBYTE buffer;
258         int buff_len = 0;
259         int consumed_len = 0;
260         LPBYTE ptr;
261         int rows;
262
263         /* Transfer an image from the source to the application */
264         if (activeDS.currentState == 6)
265         {
266
267             /* trigger scanning dialog */
268             activeDS.progressWnd = ScanningDialogBox(NULL,0);
269
270             ScanningDialogBox(activeDS.progressWnd,0);
271
272             if (! activeDS.sane_started)
273             {
274                 status = psane_start (activeDS.deviceHandle);
275                 if (status != SANE_STATUS_GOOD)
276                 {
277                     WARN("psane_start: %s\n", psane_strstatus (status));
278                     psane_cancel (activeDS.deviceHandle);
279                     activeDS.twCC = TWCC_OPERATIONERROR;
280                     return TWRC_FAILURE;
281                 }
282                 activeDS.sane_started = TRUE;
283             }
284
285             status = psane_get_parameters (activeDS.deviceHandle,
286                     &activeDS.sane_param);
287             activeDS.sane_param_valid = TRUE;
288
289             if (status != SANE_STATUS_GOOD)
290             {
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;
295                 return TWRC_FAILURE;
296             }
297
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);
302
303             activeDS.currentState = 7;
304         }
305
306         /* access memory buffer */
307         if (pImageMemXfer->Memory.Length < activeDS.sane_param.bytes_per_line)
308         {
309             psane_cancel (activeDS.deviceHandle);
310             activeDS.sane_started = FALSE;
311             activeDS.twCC = TWCC_BADVALUE;
312             return TWRC_FAILURE;
313         }
314
315         if (pImageMemXfer->Memory.Flags & TWMF_HANDLE)
316         {
317             FIXME("Memory Handle, may not be locked correctly\n");
318             buffer = LocalLock(pImageMemXfer->Memory.TheMem);
319         }
320         else
321             buffer = pImageMemXfer->Memory.TheMem;
322        
323         memset(buffer,0,pImageMemXfer->Memory.Length);
324
325         ptr = buffer;
326         consumed_len = 0;
327         rows = pImageMemXfer->Memory.Length / activeDS.sane_param.bytes_per_line;
328
329         /* must fill full lines */
330         while (consumed_len < (activeDS.sane_param.bytes_per_line*rows) && 
331                 status == SANE_STATUS_GOOD)
332         {
333             status = psane_read (activeDS.deviceHandle, ptr,
334                     (activeDS.sane_param.bytes_per_line*rows) - consumed_len ,
335                     &buff_len);
336             consumed_len += buff_len;
337             ptr += buff_len;
338         }
339
340         if (status == SANE_STATUS_GOOD || status == SANE_STATUS_EOF)
341         {
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;
349
350             ScanningDialogBox(activeDS.progressWnd, consumed_len);
351
352             if (status == SANE_STATUS_EOF)
353             {
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;
359             }
360             activeDS.twCC = TWRC_SUCCESS;
361         }
362         else if (status != SANE_STATUS_EOF)
363         {
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;
369             twRC = TWRC_FAILURE;
370         }
371     }
372
373     if (pImageMemXfer->Memory.Flags & TWMF_HANDLE)
374         LocalUnlock(pImageMemXfer->Memory.TheMem);
375     
376     return twRC;
377 #endif
378 }
379
380 #ifdef SONAME_LIBSANE
381 static SANE_Status read_one_line(SANE_Handle h, BYTE *line, int len)
382 {
383     int read_len;
384     SANE_Status status;
385
386     for (;;)
387     {
388         read_len = 0;
389         status = psane_read (activeDS.deviceHandle, line, len, &read_len);
390         if (status != SANE_STATUS_GOOD)
391             break;
392
393         if (read_len == len)
394             break;
395
396         line += read_len;
397         len -= read_len;
398     }
399
400     return status;
401 }
402 #endif
403
404 /* DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET */
405 TW_UINT16 SANE_ImageNativeXferGet (pTW_IDENTITY pOrigin, 
406                                     TW_MEMREF pData)
407 {
408 #ifndef SONAME_LIBSANE
409     return TWRC_FAILURE;
410 #else
411     TW_UINT16 twRC = TWRC_SUCCESS;
412     pTW_UINT32 pHandle = (pTW_UINT32) pData;
413     SANE_Status status;
414     HANDLE hDIB;
415     BITMAPINFOHEADER *header = NULL;
416     int dib_bytes;
417     int dib_bytes_per_line;
418     BYTE *line;
419     RGBQUAD *colors;
420     int color_size = 0;
421     int i;
422     BYTE *p;
423
424     TRACE("DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET\n");
425
426     if (activeDS.currentState != 6)
427     {
428         twRC = TWRC_FAILURE;
429         activeDS.twCC = TWCC_SEQERROR;
430     }
431     else
432     {
433         /* Transfer an image from the source to the application */
434         if (! activeDS.sane_started)
435         {
436             status = psane_start (activeDS.deviceHandle);
437             if (status != SANE_STATUS_GOOD)
438             {
439                 WARN("psane_start: %s\n", psane_strstatus (status));
440                 psane_cancel (activeDS.deviceHandle);
441                 activeDS.twCC = TWCC_OPERATIONERROR;
442                 return TWRC_FAILURE;
443             }
444             activeDS.sane_started = TRUE;
445         }
446
447         status = psane_get_parameters (activeDS.deviceHandle, &activeDS.sane_param);
448         activeDS.sane_param_valid = TRUE;
449         if (status != SANE_STATUS_GOOD)
450         {
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;
455             return TWRC_FAILURE;
456         }
457
458         if (activeDS.sane_param.format == SANE_FRAME_GRAY)
459         {
460             if (activeDS.sane_param.depth == 8)
461                 color_size = (1 << 8) * sizeof(*colors);
462             else if (activeDS.sane_param.depth == 1)
463                 ;
464             else
465             {
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;
470                 return TWRC_FAILURE;
471             }
472         }
473         else if (activeDS.sane_param.format != SANE_FRAME_RGB)
474         {
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;
479             return TWRC_FAILURE;
480         }
481
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);
486
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;
489
490         hDIB = GlobalAlloc(GMEM_ZEROINIT, dib_bytes + sizeof(*header) + color_size);
491         if (hDIB)
492            header = GlobalLock(hDIB);
493
494         if (!header)
495         {
496             psane_cancel (activeDS.deviceHandle);
497             activeDS.sane_started = FALSE;
498             activeDS.twCC = TWCC_LOWMEMORY;
499             if (hDIB)
500                 GlobalFree(hDIB);
501             return TWRC_FAILURE;
502         }
503
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;
518
519         p = (BYTE *)(header + 1);
520
521         if (color_size > 0)
522         {
523             colors = (RGBQUAD *) p;
524             p += color_size;
525             for (i = 0; i < (color_size / sizeof(*colors)); i++)
526                 colors[i].rgbBlue = colors[i].rgbRed = colors[i].rgbGreen = i;
527         }
528
529
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--)
534         {
535             activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd,
536                     ((activeDS.sane_param.lines - 1 - i) * 100)
537                             /
538                     (activeDS.sane_param.lines - 1));
539
540             status = read_one_line(activeDS.deviceHandle, line,
541                             activeDS.sane_param.bytes_per_line);
542             if (status != SANE_STATUS_GOOD)
543                 break;
544
545             line -= dib_bytes_per_line;
546         }
547         activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd, -1);
548
549         GlobalUnlock(hDIB);
550
551         if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF)
552         {
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;
557             GlobalFree(hDIB);
558             return TWRC_FAILURE;
559         }
560
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;
567     }
568     return twRC;
569 #endif
570 }