msxml3: Don't crash on null pointer when doing ::Next() on a last child.
[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 "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "twain.h"
29 #include "sane_i.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(twain);
33
34 /* DG_IMAGE/DAT_IMAGEINFO/MSG_GET */
35 TW_UINT16 SANE_ImageInfoGet (pTW_IDENTITY pOrigin, 
36                               TW_MEMREF pData)
37 {
38 #ifndef SONAME_LIBSANE
39     return TWRC_FAILURE;
40 #else
41     TW_UINT16 twRC = TWRC_SUCCESS;
42     pTW_IMAGEINFO pImageInfo = (pTW_IMAGEINFO) pData;
43     SANE_Status status;
44     SANE_Int resolution;
45
46     TRACE("DG_IMAGE/DAT_IMAGEINFO/MSG_GET\n");
47
48     if (activeDS.currentState != 6 && activeDS.currentState != 7)
49     {
50         twRC = TWRC_FAILURE;
51         activeDS.twCC = TWCC_SEQERROR;
52     }
53     else
54     {
55         if (activeDS.currentState == 6)
56         {
57             /* return general image description information about the image about to be transferred */
58             status = psane_get_parameters (activeDS.deviceHandle, &activeDS.sane_param);
59             TRACE("Getting parameters\n");
60             if (status != SANE_STATUS_GOOD)
61             {
62                 WARN("psane_get_parameters: %s\n", psane_strstatus (status));
63                 psane_cancel (activeDS.deviceHandle);
64                 activeDS.sane_started = FALSE;
65                 activeDS.twCC = TWCC_OPERATIONERROR;
66                 return TWRC_FAILURE;
67             }
68             activeDS.sane_param_valid = TRUE;
69         }
70
71         if (sane_option_get_int(activeDS.deviceHandle, "resolution", &resolution) == SANE_STATUS_GOOD)
72             pImageInfo->XResolution.Whole = pImageInfo->YResolution.Whole = resolution;
73         else
74             pImageInfo->XResolution.Whole = pImageInfo->YResolution.Whole = -1;
75         pImageInfo->XResolution.Frac = 0;
76         pImageInfo->YResolution.Frac = 0;
77         pImageInfo->ImageWidth = activeDS.sane_param.pixels_per_line;
78         pImageInfo->ImageLength = activeDS.sane_param.lines;
79
80         TRACE("Bits per Sample %i\n",activeDS.sane_param.depth);
81         TRACE("Frame Format %i\n",activeDS.sane_param.format);
82
83         if (activeDS.sane_param.format == SANE_FRAME_RGB )
84         {
85             pImageInfo->BitsPerPixel = activeDS.sane_param.depth * 3;
86             pImageInfo->Compression = TWCP_NONE;
87             pImageInfo->Planar = TRUE;
88             pImageInfo->SamplesPerPixel = 3;
89             pImageInfo->BitsPerSample[0] = activeDS.sane_param.depth;
90             pImageInfo->BitsPerSample[1] = activeDS.sane_param.depth;
91             pImageInfo->BitsPerSample[2] = activeDS.sane_param.depth;
92             pImageInfo->PixelType = TWPT_RGB;
93         }
94         else if (activeDS.sane_param.format == SANE_FRAME_GRAY)
95         {
96             pImageInfo->BitsPerPixel = activeDS.sane_param.depth;
97             pImageInfo->Compression = TWCP_NONE;
98             pImageInfo->Planar = TRUE;
99             pImageInfo->SamplesPerPixel = 1;
100             pImageInfo->BitsPerSample[0] = activeDS.sane_param.depth;
101             if (activeDS.sane_param.depth == 1)
102                 pImageInfo->PixelType = TWPT_BW;
103             else
104                 pImageInfo->PixelType = TWPT_GRAY;
105         }
106         else
107         {
108             ERR("Unhandled source frame type %i\n",activeDS.sane_param.format);
109             twRC = TWRC_FAILURE;
110             activeDS.twCC = TWCC_SEQERROR;
111         }
112     }
113
114     return twRC;
115 #endif
116 }
117
118 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET */
119 TW_UINT16 SANE_ImageLayoutGet (pTW_IDENTITY pOrigin,
120                                 TW_MEMREF pData)
121 {
122 #ifndef SONAME_LIBSANE
123     return TWRC_FAILURE;
124 #else
125     TW_IMAGELAYOUT *img = (TW_IMAGELAYOUT *) pData;
126     SANE_Fixed tlx_current;
127     SANE_Fixed tly_current;
128     SANE_Fixed brx_current;
129     SANE_Fixed bry_current;
130     SANE_Status status;
131
132     TRACE("DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET\n");
133
134     status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-x", &tlx_current, NULL, NULL, NULL, NULL);
135     if (status == SANE_STATUS_GOOD)
136         status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-y", &tly_current, NULL, NULL, NULL, NULL);
137
138     if (status == SANE_STATUS_GOOD)
139         status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-x", &brx_current, NULL, NULL, NULL, NULL);
140
141     if (status == SANE_STATUS_GOOD)
142         status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-y", &bry_current, NULL, NULL, NULL, NULL);
143
144     if (status != SANE_STATUS_GOOD)
145     {
146         activeDS.twCC = sane_status_to_twcc(status);
147         return TWRC_FAILURE;
148     }
149
150     convert_sane_res_to_twain(SANE_UNFIX(tlx_current), SANE_UNIT_MM, &img->Frame.Left, TWUN_INCHES);
151     convert_sane_res_to_twain(SANE_UNFIX(tly_current), SANE_UNIT_MM, &img->Frame.Top, TWUN_INCHES);
152     convert_sane_res_to_twain(SANE_UNFIX(brx_current), SANE_UNIT_MM, &img->Frame.Right, TWUN_INCHES);
153     convert_sane_res_to_twain(SANE_UNFIX(bry_current), SANE_UNIT_MM, &img->Frame.Bottom, TWUN_INCHES);
154
155     img->DocumentNumber = 1;
156     img->PageNumber = 1;
157     img->FrameNumber = 1;
158
159     activeDS.twCC = TWCC_SUCCESS;
160     return TWRC_SUCCESS;
161 #endif
162 }
163
164 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GETDEFAULT */
165 TW_UINT16 SANE_ImageLayoutGetDefault (pTW_IDENTITY pOrigin, 
166                                        TW_MEMREF pData)
167 {
168     FIXME ("stub!\n");
169
170     return TWRC_FAILURE;
171 }
172
173 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_RESET */
174 TW_UINT16 SANE_ImageLayoutReset (pTW_IDENTITY pOrigin, 
175                                   TW_MEMREF pData)
176 {
177     FIXME ("stub!\n");
178
179     return TWRC_FAILURE;
180 }
181
182 #ifdef SONAME_LIBSANE
183 static TW_UINT16 set_one_imagecoord(const char *option_name, TW_FIX32 val, BOOL *changed)
184 {
185     double d = val.Whole + ((double) val.Frac / 65536.0);
186     int set_status = 0;
187     SANE_Status status;
188     status = sane_option_set_fixed(activeDS.deviceHandle, option_name,
189              SANE_FIX((d * 254) / 10), &set_status);
190     if (status != SANE_STATUS_GOOD)
191     {
192         activeDS.twCC = sane_status_to_twcc(status);
193         return TWRC_FAILURE;
194     }
195     if (set_status & SANE_INFO_INEXACT)
196         *changed = TRUE;
197     return TWRC_SUCCESS;
198 }
199 #endif
200
201 /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET */
202 TW_UINT16 SANE_ImageLayoutSet (pTW_IDENTITY pOrigin,
203                                 TW_MEMREF pData)
204 {
205 #ifndef SONAME_LIBSANE
206     return TWRC_FAILURE;
207 #else
208     TW_IMAGELAYOUT *img = (TW_IMAGELAYOUT *) pData;
209     BOOL changed = FALSE;
210     TW_UINT16 twrc;
211
212     TRACE("DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET\n");
213     TRACE("Frame: [Left %x.%x|Top %x.%x|Right %x.%x|Bottom %x.%x]\n",
214             img->Frame.Left.Whole, img->Frame.Left.Frac,
215             img->Frame.Top.Whole, img->Frame.Top.Frac,
216             img->Frame.Right.Whole, img->Frame.Right.Frac,
217             img->Frame.Bottom.Whole, img->Frame.Bottom.Frac);
218
219     twrc = set_one_imagecoord("tl-x", img->Frame.Left, &changed);
220     if (twrc != TWRC_SUCCESS)
221         return (twrc);
222
223     twrc = set_one_imagecoord("tl-y", img->Frame.Top, &changed);
224     if (twrc != TWRC_SUCCESS)
225         return (twrc);
226
227     twrc = set_one_imagecoord("br-x", img->Frame.Right, &changed);
228     if (twrc != TWRC_SUCCESS)
229         return (twrc);
230
231     twrc = set_one_imagecoord("br-y", img->Frame.Bottom, &changed);
232     if (twrc != TWRC_SUCCESS)
233         return (twrc);
234
235     activeDS.twCC = TWCC_SUCCESS;
236     return changed ? TWRC_CHECKSTATUS : TWRC_SUCCESS;
237 #endif
238 }
239
240 /* DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET */
241 TW_UINT16 SANE_ImageMemXferGet (pTW_IDENTITY pOrigin, 
242                                  TW_MEMREF pData)
243 {
244 #ifndef SONAME_LIBSANE
245     return TWRC_FAILURE;
246 #else
247     TW_UINT16 twRC = TWRC_SUCCESS;
248     pTW_IMAGEMEMXFER pImageMemXfer = (pTW_IMAGEMEMXFER) pData;
249     SANE_Status status = SANE_STATUS_GOOD;
250
251     TRACE ("DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET\n");
252
253     if (activeDS.currentState < 6 || activeDS.currentState > 7)
254     {
255         twRC = TWRC_FAILURE;
256         activeDS.twCC = TWCC_SEQERROR;
257     }
258     else
259     {
260         LPBYTE buffer;
261         int buff_len = 0;
262         int consumed_len = 0;
263         LPBYTE ptr;
264         int rows;
265
266         /* Transfer an image from the source to the application */
267         if (activeDS.currentState == 6)
268         {
269
270             /* trigger scanning dialog */
271             activeDS.progressWnd = ScanningDialogBox(NULL,0);
272
273             ScanningDialogBox(activeDS.progressWnd,0);
274
275             if (! activeDS.sane_started)
276             {
277                 status = psane_start (activeDS.deviceHandle);
278                 if (status != SANE_STATUS_GOOD)
279                 {
280                     WARN("psane_start: %s\n", psane_strstatus (status));
281                     psane_cancel (activeDS.deviceHandle);
282                     activeDS.twCC = TWCC_OPERATIONERROR;
283                     return TWRC_FAILURE;
284                 }
285                 activeDS.sane_started = TRUE;
286             }
287
288             status = psane_get_parameters (activeDS.deviceHandle,
289                     &activeDS.sane_param);
290             activeDS.sane_param_valid = TRUE;
291
292             if (status != SANE_STATUS_GOOD)
293             {
294                 WARN("psane_get_parameters: %s\n", psane_strstatus (status));
295                 psane_cancel (activeDS.deviceHandle);
296                 activeDS.sane_started = FALSE;
297                 activeDS.twCC = TWCC_OPERATIONERROR;
298                 return TWRC_FAILURE;
299             }
300
301             TRACE("Acquiring image %dx%dx%d bits (format=%d last=%d) from sane...\n"
302               , activeDS.sane_param.pixels_per_line, activeDS.sane_param.lines,
303               activeDS.sane_param.depth, activeDS.sane_param.format,
304               activeDS.sane_param.last_frame);
305
306             activeDS.currentState = 7;
307         }
308
309         /* access memory buffer */
310         if (pImageMemXfer->Memory.Length < activeDS.sane_param.bytes_per_line)
311         {
312             psane_cancel (activeDS.deviceHandle);
313             activeDS.sane_started = FALSE;
314             activeDS.twCC = TWCC_BADVALUE;
315             return TWRC_FAILURE;
316         }
317
318         if (pImageMemXfer->Memory.Flags & TWMF_HANDLE)
319         {
320             FIXME("Memory Handle, may not be locked correctly\n");
321             buffer = LocalLock(pImageMemXfer->Memory.TheMem);
322         }
323         else
324             buffer = pImageMemXfer->Memory.TheMem;
325        
326         memset(buffer,0,pImageMemXfer->Memory.Length);
327
328         ptr = buffer;
329         consumed_len = 0;
330         rows = pImageMemXfer->Memory.Length / activeDS.sane_param.bytes_per_line;
331
332         /* must fill full lines */
333         while (consumed_len < (activeDS.sane_param.bytes_per_line*rows) && 
334                 status == SANE_STATUS_GOOD)
335         {
336             status = psane_read (activeDS.deviceHandle, ptr,
337                     (activeDS.sane_param.bytes_per_line*rows) - consumed_len ,
338                     &buff_len);
339             consumed_len += buff_len;
340             ptr += buff_len;
341         }
342
343         if (status == SANE_STATUS_GOOD || status == SANE_STATUS_EOF)
344         {
345             pImageMemXfer->Compression = TWCP_NONE;
346             pImageMemXfer->BytesPerRow = activeDS.sane_param.bytes_per_line;
347             pImageMemXfer->Columns = activeDS.sane_param.pixels_per_line;
348             pImageMemXfer->Rows = rows;
349             pImageMemXfer->XOffset = 0;
350             pImageMemXfer->YOffset = 0;
351             pImageMemXfer->BytesWritten = consumed_len;
352
353             ScanningDialogBox(activeDS.progressWnd, consumed_len);
354
355             if (status == SANE_STATUS_EOF)
356             {
357                 ScanningDialogBox(activeDS.progressWnd, -1);
358                 TRACE("psane_read: %s\n", psane_strstatus (status));
359                 psane_cancel (activeDS.deviceHandle);
360                 activeDS.sane_started = FALSE;
361                 twRC = TWRC_XFERDONE;
362             }
363             activeDS.twCC = TWRC_SUCCESS;
364         }
365         else if (status != SANE_STATUS_EOF)
366         {
367             ScanningDialogBox(activeDS.progressWnd, -1);
368             WARN("psane_read: %s\n", psane_strstatus (status));
369             psane_cancel (activeDS.deviceHandle);
370             activeDS.sane_started = FALSE;
371             activeDS.twCC = TWCC_OPERATIONERROR;
372             twRC = TWRC_FAILURE;
373         }
374     }
375
376     if (pImageMemXfer->Memory.Flags & TWMF_HANDLE)
377         LocalUnlock(pImageMemXfer->Memory.TheMem);
378     
379     return twRC;
380 #endif
381 }
382
383 #ifdef SONAME_LIBSANE
384 static SANE_Status read_one_line(SANE_Handle h, BYTE *line, int len)
385 {
386     int read_len;
387     SANE_Status status;
388
389     for (;;)
390     {
391         read_len = 0;
392         status = psane_read (activeDS.deviceHandle, line, len, &read_len);
393         if (status != SANE_STATUS_GOOD)
394             break;
395
396         if (read_len == len)
397             break;
398
399         line += read_len;
400         len -= read_len;
401     }
402
403     return status;
404 }
405 #endif
406
407 /* DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET */
408 TW_UINT16 SANE_ImageNativeXferGet (pTW_IDENTITY pOrigin, 
409                                     TW_MEMREF pData)
410 {
411 #ifndef SONAME_LIBSANE
412     return TWRC_FAILURE;
413 #else
414     TW_UINT16 twRC = TWRC_SUCCESS;
415     pTW_UINT32 pHandle = (pTW_UINT32) pData;
416     SANE_Status status;
417     HANDLE hDIB;
418     BITMAPINFOHEADER *header = NULL;
419     int dib_bytes;
420     int dib_bytes_per_line;
421     BYTE *line;
422     RGBQUAD *colors;
423     int color_size = 0;
424     int i;
425     BYTE *p;
426
427     TRACE("DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET\n");
428
429     if (activeDS.currentState != 6)
430     {
431         twRC = TWRC_FAILURE;
432         activeDS.twCC = TWCC_SEQERROR;
433     }
434     else
435     {
436         /* Transfer an image from the source to the application */
437         if (! activeDS.sane_started)
438         {
439             status = psane_start (activeDS.deviceHandle);
440             if (status != SANE_STATUS_GOOD)
441             {
442                 WARN("psane_start: %s\n", psane_strstatus (status));
443                 psane_cancel (activeDS.deviceHandle);
444                 activeDS.twCC = TWCC_OPERATIONERROR;
445                 return TWRC_FAILURE;
446             }
447             activeDS.sane_started = TRUE;
448         }
449
450         status = psane_get_parameters (activeDS.deviceHandle, &activeDS.sane_param);
451         activeDS.sane_param_valid = TRUE;
452         if (status != SANE_STATUS_GOOD)
453         {
454             WARN("psane_get_parameters: %s\n", psane_strstatus (status));
455             psane_cancel (activeDS.deviceHandle);
456             activeDS.sane_started = FALSE;
457             activeDS.twCC = TWCC_OPERATIONERROR;
458             return TWRC_FAILURE;
459         }
460
461         if (activeDS.sane_param.format == SANE_FRAME_GRAY)
462         {
463             if (activeDS.sane_param.depth == 8)
464                 color_size = (1 << 8) * sizeof(*colors);
465             else if (activeDS.sane_param.depth == 1)
466                 ;
467             else
468             {
469                 FIXME("For NATIVE, we support only 1 bit monochrome and 8 bit Grayscale, not %d\n", activeDS.sane_param.depth);
470                 psane_cancel (activeDS.deviceHandle);
471                 activeDS.sane_started = FALSE;
472                 activeDS.twCC = TWCC_OPERATIONERROR;
473                 return TWRC_FAILURE;
474             }
475         }
476         else if (activeDS.sane_param.format != SANE_FRAME_RGB)
477         {
478             FIXME("For NATIVE, we support only GRAY and RGB, not %d\n", activeDS.sane_param.format);
479             psane_cancel (activeDS.deviceHandle);
480             activeDS.sane_started = FALSE;
481             activeDS.twCC = TWCC_OPERATIONERROR;
482             return TWRC_FAILURE;
483         }
484
485         TRACE("Acquiring image %dx%dx%d bits (format=%d last=%d bpl=%d) from sane...\n"
486               , activeDS.sane_param.pixels_per_line, activeDS.sane_param.lines,
487               activeDS.sane_param.depth, activeDS.sane_param.format,
488               activeDS.sane_param.last_frame, activeDS.sane_param.bytes_per_line);
489
490         dib_bytes_per_line = ((activeDS.sane_param.bytes_per_line + 3) / 4) * 4;
491         dib_bytes = activeDS.sane_param.lines * dib_bytes_per_line;
492
493         hDIB = GlobalAlloc(GMEM_ZEROINIT, dib_bytes + sizeof(*header) + color_size);
494         if (hDIB)
495            header = GlobalLock(hDIB);
496
497         if (!header)
498         {
499             psane_cancel (activeDS.deviceHandle);
500             activeDS.sane_started = FALSE;
501             activeDS.twCC = TWCC_LOWMEMORY;
502             if (hDIB)
503                 GlobalFree(hDIB);
504             return TWRC_FAILURE;
505         }
506
507         header->biSize = sizeof (*header);
508         header->biWidth = activeDS.sane_param.pixels_per_line;
509         header->biHeight = activeDS.sane_param.lines;
510         header->biPlanes = 1;
511         header->biCompression = BI_RGB;
512         if (activeDS.sane_param.format == SANE_FRAME_RGB)
513             header->biBitCount = activeDS.sane_param.depth * 3;
514         if (activeDS.sane_param.format == SANE_FRAME_GRAY)
515             header->biBitCount = activeDS.sane_param.depth;
516         header->biSizeImage = dib_bytes;
517         header->biXPelsPerMeter = 0;
518         header->biYPelsPerMeter = 0;
519         header->biClrUsed = 0;
520         header->biClrImportant = 0;
521
522         p = (BYTE *)(header + 1);
523
524         if (color_size > 0)
525         {
526             colors = (RGBQUAD *) p;
527             p += color_size;
528             for (i = 0; i < (color_size / sizeof(*colors)); i++)
529                 colors[i].rgbBlue = colors[i].rgbRed = colors[i].rgbGreen = i;
530         }
531
532
533         /* Sane returns data in top down order.  Acrobat does best with
534            a bottom up DIB being returned.  */
535         line = p + (activeDS.sane_param.lines - 1) * dib_bytes_per_line;
536         for (i = activeDS.sane_param.lines - 1; i >= 0; i--)
537         {
538             activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd,
539                     ((activeDS.sane_param.lines - 1 - i) * 100)
540                             /
541                     (activeDS.sane_param.lines - 1));
542
543             status = read_one_line(activeDS.deviceHandle, line,
544                             activeDS.sane_param.bytes_per_line);
545             if (status != SANE_STATUS_GOOD)
546                 break;
547
548             line -= dib_bytes_per_line;
549         }
550         activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd, -1);
551
552         GlobalUnlock(hDIB);
553
554         if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF)
555         {
556             WARN("psane_read: %s, reading line %d\n", psane_strstatus(status), i);
557             psane_cancel (activeDS.deviceHandle);
558             activeDS.sane_started = FALSE;
559             activeDS.twCC = TWCC_OPERATIONERROR;
560             GlobalFree(hDIB);
561             return TWRC_FAILURE;
562         }
563
564         psane_cancel (activeDS.deviceHandle);
565         activeDS.sane_started = FALSE;
566         *pHandle = (UINT_PTR)hDIB;
567         twRC = TWRC_XFERDONE;
568         activeDS.twCC = TWCC_SUCCESS;
569         activeDS.currentState = 7;
570     }
571     return twRC;
572 #endif
573 }