| /* |
| * Copyright 2000 Corel Corporation |
| * Copyright 2006 CodeWeavers, Aric Stewart |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| |
| #include <stdarg.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wingdi.h" |
| #include "winuser.h" |
| #include "twain.h" |
| #include "sane_i.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(twain); |
| |
| /* DG_IMAGE/DAT_IMAGEINFO/MSG_GET */ |
| TW_UINT16 SANE_ImageInfoGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| #ifndef SONAME_LIBSANE |
| return TWRC_FAILURE; |
| #else |
| TW_UINT16 twRC = TWRC_SUCCESS; |
| pTW_IMAGEINFO pImageInfo = (pTW_IMAGEINFO) pData; |
| SANE_Status status; |
| SANE_Int resolution; |
| |
| TRACE("DG_IMAGE/DAT_IMAGEINFO/MSG_GET\n"); |
| |
| if (activeDS.currentState != 6 && activeDS.currentState != 7) |
| { |
| twRC = TWRC_FAILURE; |
| activeDS.twCC = TWCC_SEQERROR; |
| } |
| else |
| { |
| if (activeDS.currentState == 6) |
| { |
| /* return general image description information about the image about to be transferred */ |
| status = psane_get_parameters (activeDS.deviceHandle, &activeDS.sane_param); |
| TRACE("Getting parameters\n"); |
| if (status != SANE_STATUS_GOOD) |
| { |
| WARN("psane_get_parameters: %s\n", psane_strstatus (status)); |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| activeDS.twCC = TWCC_OPERATIONERROR; |
| return TWRC_FAILURE; |
| } |
| activeDS.sane_param_valid = TRUE; |
| } |
| |
| if (sane_option_get_int(activeDS.deviceHandle, "resolution", &resolution) == SANE_STATUS_GOOD) |
| pImageInfo->XResolution.Whole = pImageInfo->YResolution.Whole = resolution; |
| else |
| pImageInfo->XResolution.Whole = pImageInfo->YResolution.Whole = -1; |
| pImageInfo->XResolution.Frac = 0; |
| pImageInfo->YResolution.Frac = 0; |
| pImageInfo->ImageWidth = activeDS.sane_param.pixels_per_line; |
| pImageInfo->ImageLength = activeDS.sane_param.lines; |
| |
| TRACE("Bits per Sample %i\n",activeDS.sane_param.depth); |
| TRACE("Frame Format %i\n",activeDS.sane_param.format); |
| |
| if (activeDS.sane_param.format == SANE_FRAME_RGB ) |
| { |
| pImageInfo->BitsPerPixel = activeDS.sane_param.depth * 3; |
| pImageInfo->Compression = TWCP_NONE; |
| pImageInfo->Planar = TRUE; |
| pImageInfo->SamplesPerPixel = 3; |
| pImageInfo->BitsPerSample[0] = activeDS.sane_param.depth; |
| pImageInfo->BitsPerSample[1] = activeDS.sane_param.depth; |
| pImageInfo->BitsPerSample[2] = activeDS.sane_param.depth; |
| pImageInfo->PixelType = TWPT_RGB; |
| } |
| else if (activeDS.sane_param.format == SANE_FRAME_GRAY) |
| { |
| pImageInfo->BitsPerPixel = activeDS.sane_param.depth; |
| pImageInfo->Compression = TWCP_NONE; |
| pImageInfo->Planar = TRUE; |
| pImageInfo->SamplesPerPixel = 1; |
| pImageInfo->BitsPerSample[0] = activeDS.sane_param.depth; |
| if (activeDS.sane_param.depth == 1) |
| pImageInfo->PixelType = TWPT_BW; |
| else |
| pImageInfo->PixelType = TWPT_GRAY; |
| } |
| else |
| { |
| ERR("Unhandled source frame type %i\n",activeDS.sane_param.format); |
| twRC = TWRC_FAILURE; |
| activeDS.twCC = TWCC_SEQERROR; |
| } |
| } |
| |
| return twRC; |
| #endif |
| } |
| |
| /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET */ |
| TW_UINT16 SANE_ImageLayoutGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| #ifndef SONAME_LIBSANE |
| return TWRC_FAILURE; |
| #else |
| TW_IMAGELAYOUT *img = (TW_IMAGELAYOUT *) pData; |
| SANE_Fixed tlx_current; |
| SANE_Fixed tly_current; |
| SANE_Fixed brx_current; |
| SANE_Fixed bry_current; |
| SANE_Status status; |
| |
| TRACE("DG_IMAGE/DAT_IMAGELAYOUT/MSG_GET\n"); |
| |
| status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-x", &tlx_current, NULL, NULL, NULL, NULL); |
| if (status == SANE_STATUS_GOOD) |
| status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-y", &tly_current, NULL, NULL, NULL, NULL); |
| |
| if (status == SANE_STATUS_GOOD) |
| status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-x", &brx_current, NULL, NULL, NULL, NULL); |
| |
| if (status == SANE_STATUS_GOOD) |
| status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-y", &bry_current, NULL, NULL, NULL, NULL); |
| |
| if (status != SANE_STATUS_GOOD) |
| { |
| activeDS.twCC = sane_status_to_twcc(status); |
| return TWRC_FAILURE; |
| } |
| |
| convert_sane_res_to_twain(SANE_UNFIX(tlx_current), SANE_UNIT_MM, &img->Frame.Left, TWUN_INCHES); |
| convert_sane_res_to_twain(SANE_UNFIX(tly_current), SANE_UNIT_MM, &img->Frame.Top, TWUN_INCHES); |
| convert_sane_res_to_twain(SANE_UNFIX(brx_current), SANE_UNIT_MM, &img->Frame.Right, TWUN_INCHES); |
| convert_sane_res_to_twain(SANE_UNFIX(bry_current), SANE_UNIT_MM, &img->Frame.Bottom, TWUN_INCHES); |
| |
| img->DocumentNumber = 1; |
| img->PageNumber = 1; |
| img->FrameNumber = 1; |
| |
| activeDS.twCC = TWCC_SUCCESS; |
| return TWRC_SUCCESS; |
| #endif |
| } |
| |
| /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_GETDEFAULT */ |
| TW_UINT16 SANE_ImageLayoutGetDefault (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_RESET */ |
| TW_UINT16 SANE_ImageLayoutReset (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| FIXME ("stub!\n"); |
| |
| return TWRC_FAILURE; |
| } |
| |
| #ifdef SONAME_LIBSANE |
| static TW_UINT16 set_one_imagecoord(const char *option_name, TW_FIX32 val, BOOL *changed) |
| { |
| double d = val.Whole + ((double) val.Frac / 65536.0); |
| int set_status = 0; |
| SANE_Status status; |
| status = sane_option_set_fixed(activeDS.deviceHandle, option_name, |
| SANE_FIX((d * 254) / 10), &set_status); |
| if (status != SANE_STATUS_GOOD) |
| { |
| activeDS.twCC = sane_status_to_twcc(status); |
| return TWRC_FAILURE; |
| } |
| if (set_status & SANE_INFO_INEXACT) |
| *changed = TRUE; |
| return TWRC_SUCCESS; |
| } |
| #endif |
| |
| /* DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET */ |
| TW_UINT16 SANE_ImageLayoutSet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| #ifndef SONAME_LIBSANE |
| return TWRC_FAILURE; |
| #else |
| TW_IMAGELAYOUT *img = (TW_IMAGELAYOUT *) pData; |
| BOOL changed = FALSE; |
| TW_UINT16 twrc; |
| |
| TRACE("DG_IMAGE/DAT_IMAGELAYOUT/MSG_SET\n"); |
| TRACE("Frame: [Left %x.%x|Top %x.%x|Right %x.%x|Bottom %x.%x]\n", |
| img->Frame.Left.Whole, img->Frame.Left.Frac, |
| img->Frame.Top.Whole, img->Frame.Top.Frac, |
| img->Frame.Right.Whole, img->Frame.Right.Frac, |
| img->Frame.Bottom.Whole, img->Frame.Bottom.Frac); |
| |
| twrc = set_one_imagecoord("tl-x", img->Frame.Left, &changed); |
| if (twrc != TWRC_SUCCESS) |
| return (twrc); |
| |
| twrc = set_one_imagecoord("tl-y", img->Frame.Top, &changed); |
| if (twrc != TWRC_SUCCESS) |
| return (twrc); |
| |
| twrc = set_one_imagecoord("br-x", img->Frame.Right, &changed); |
| if (twrc != TWRC_SUCCESS) |
| return (twrc); |
| |
| twrc = set_one_imagecoord("br-y", img->Frame.Bottom, &changed); |
| if (twrc != TWRC_SUCCESS) |
| return (twrc); |
| |
| activeDS.twCC = TWCC_SUCCESS; |
| return changed ? TWRC_CHECKSTATUS : TWRC_SUCCESS; |
| #endif |
| } |
| |
| /* DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET */ |
| TW_UINT16 SANE_ImageMemXferGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| #ifndef SONAME_LIBSANE |
| return TWRC_FAILURE; |
| #else |
| TW_UINT16 twRC = TWRC_SUCCESS; |
| pTW_IMAGEMEMXFER pImageMemXfer = (pTW_IMAGEMEMXFER) pData; |
| SANE_Status status = SANE_STATUS_GOOD; |
| |
| TRACE ("DG_IMAGE/DAT_IMAGEMEMXFER/MSG_GET\n"); |
| |
| if (activeDS.currentState < 6 || activeDS.currentState > 7) |
| { |
| twRC = TWRC_FAILURE; |
| activeDS.twCC = TWCC_SEQERROR; |
| } |
| else |
| { |
| LPBYTE buffer; |
| int buff_len = 0; |
| int consumed_len = 0; |
| LPBYTE ptr; |
| int rows; |
| |
| /* Transfer an image from the source to the application */ |
| if (activeDS.currentState == 6) |
| { |
| |
| /* trigger scanning dialog */ |
| activeDS.progressWnd = ScanningDialogBox(NULL,0); |
| |
| ScanningDialogBox(activeDS.progressWnd,0); |
| |
| if (! activeDS.sane_started) |
| { |
| status = psane_start (activeDS.deviceHandle); |
| if (status != SANE_STATUS_GOOD) |
| { |
| WARN("psane_start: %s\n", psane_strstatus (status)); |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.twCC = TWCC_OPERATIONERROR; |
| return TWRC_FAILURE; |
| } |
| activeDS.sane_started = TRUE; |
| } |
| |
| status = psane_get_parameters (activeDS.deviceHandle, |
| &activeDS.sane_param); |
| activeDS.sane_param_valid = TRUE; |
| |
| if (status != SANE_STATUS_GOOD) |
| { |
| WARN("psane_get_parameters: %s\n", psane_strstatus (status)); |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| activeDS.twCC = TWCC_OPERATIONERROR; |
| return TWRC_FAILURE; |
| } |
| |
| TRACE("Acquiring image %dx%dx%d bits (format=%d last=%d) from sane...\n" |
| , activeDS.sane_param.pixels_per_line, activeDS.sane_param.lines, |
| activeDS.sane_param.depth, activeDS.sane_param.format, |
| activeDS.sane_param.last_frame); |
| |
| activeDS.currentState = 7; |
| } |
| |
| /* access memory buffer */ |
| if (pImageMemXfer->Memory.Length < activeDS.sane_param.bytes_per_line) |
| { |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| activeDS.twCC = TWCC_BADVALUE; |
| return TWRC_FAILURE; |
| } |
| |
| if (pImageMemXfer->Memory.Flags & TWMF_HANDLE) |
| { |
| FIXME("Memory Handle, may not be locked correctly\n"); |
| buffer = LocalLock(pImageMemXfer->Memory.TheMem); |
| } |
| else |
| buffer = pImageMemXfer->Memory.TheMem; |
| |
| memset(buffer,0,pImageMemXfer->Memory.Length); |
| |
| ptr = buffer; |
| consumed_len = 0; |
| rows = pImageMemXfer->Memory.Length / activeDS.sane_param.bytes_per_line; |
| |
| /* must fill full lines */ |
| while (consumed_len < (activeDS.sane_param.bytes_per_line*rows) && |
| status == SANE_STATUS_GOOD) |
| { |
| status = psane_read (activeDS.deviceHandle, ptr, |
| (activeDS.sane_param.bytes_per_line*rows) - consumed_len , |
| &buff_len); |
| consumed_len += buff_len; |
| ptr += buff_len; |
| } |
| |
| if (status == SANE_STATUS_GOOD || status == SANE_STATUS_EOF) |
| { |
| pImageMemXfer->Compression = TWCP_NONE; |
| pImageMemXfer->BytesPerRow = activeDS.sane_param.bytes_per_line; |
| pImageMemXfer->Columns = activeDS.sane_param.pixels_per_line; |
| pImageMemXfer->Rows = rows; |
| pImageMemXfer->XOffset = 0; |
| pImageMemXfer->YOffset = 0; |
| pImageMemXfer->BytesWritten = consumed_len; |
| |
| ScanningDialogBox(activeDS.progressWnd, consumed_len); |
| |
| if (status == SANE_STATUS_EOF) |
| { |
| ScanningDialogBox(activeDS.progressWnd, -1); |
| TRACE("psane_read: %s\n", psane_strstatus (status)); |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| twRC = TWRC_XFERDONE; |
| } |
| activeDS.twCC = TWRC_SUCCESS; |
| } |
| else if (status != SANE_STATUS_EOF) |
| { |
| ScanningDialogBox(activeDS.progressWnd, -1); |
| WARN("psane_read: %s\n", psane_strstatus (status)); |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| activeDS.twCC = TWCC_OPERATIONERROR; |
| twRC = TWRC_FAILURE; |
| } |
| } |
| |
| if (pImageMemXfer->Memory.Flags & TWMF_HANDLE) |
| LocalUnlock(pImageMemXfer->Memory.TheMem); |
| |
| return twRC; |
| #endif |
| } |
| |
| #ifdef SONAME_LIBSANE |
| static SANE_Status read_one_line(SANE_Handle h, BYTE *line, int len) |
| { |
| int read_len; |
| SANE_Status status; |
| |
| for (;;) |
| { |
| read_len = 0; |
| status = psane_read (activeDS.deviceHandle, line, len, &read_len); |
| if (status != SANE_STATUS_GOOD) |
| break; |
| |
| if (read_len == len) |
| break; |
| |
| line += read_len; |
| len -= read_len; |
| } |
| |
| return status; |
| } |
| #endif |
| |
| /* DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET */ |
| TW_UINT16 SANE_ImageNativeXferGet (pTW_IDENTITY pOrigin, |
| TW_MEMREF pData) |
| { |
| #ifndef SONAME_LIBSANE |
| return TWRC_FAILURE; |
| #else |
| TW_UINT16 twRC = TWRC_SUCCESS; |
| pTW_UINT32 pHandle = (pTW_UINT32) pData; |
| SANE_Status status; |
| HANDLE hDIB; |
| BITMAPINFOHEADER *header = NULL; |
| int dib_bytes; |
| int dib_bytes_per_line; |
| BYTE *line; |
| RGBQUAD *colors; |
| int color_size = 0; |
| int i; |
| BYTE *p; |
| |
| TRACE("DG_IMAGE/DAT_IMAGENATIVEXFER/MSG_GET\n"); |
| |
| if (activeDS.currentState != 6) |
| { |
| twRC = TWRC_FAILURE; |
| activeDS.twCC = TWCC_SEQERROR; |
| } |
| else |
| { |
| /* Transfer an image from the source to the application */ |
| if (! activeDS.sane_started) |
| { |
| status = psane_start (activeDS.deviceHandle); |
| if (status != SANE_STATUS_GOOD) |
| { |
| WARN("psane_start: %s\n", psane_strstatus (status)); |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.twCC = TWCC_OPERATIONERROR; |
| return TWRC_FAILURE; |
| } |
| activeDS.sane_started = TRUE; |
| } |
| |
| status = psane_get_parameters (activeDS.deviceHandle, &activeDS.sane_param); |
| activeDS.sane_param_valid = TRUE; |
| if (status != SANE_STATUS_GOOD) |
| { |
| WARN("psane_get_parameters: %s\n", psane_strstatus (status)); |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| activeDS.twCC = TWCC_OPERATIONERROR; |
| return TWRC_FAILURE; |
| } |
| |
| if (activeDS.sane_param.format == SANE_FRAME_GRAY) |
| { |
| if (activeDS.sane_param.depth == 8) |
| color_size = (1 << 8) * sizeof(*colors); |
| else if (activeDS.sane_param.depth == 1) |
| ; |
| else |
| { |
| FIXME("For NATIVE, we support only 1 bit monochrome and 8 bit Grayscale, not %d\n", activeDS.sane_param.depth); |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| activeDS.twCC = TWCC_OPERATIONERROR; |
| return TWRC_FAILURE; |
| } |
| } |
| else if (activeDS.sane_param.format != SANE_FRAME_RGB) |
| { |
| FIXME("For NATIVE, we support only GRAY and RGB, not %d\n", activeDS.sane_param.format); |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| activeDS.twCC = TWCC_OPERATIONERROR; |
| return TWRC_FAILURE; |
| } |
| |
| TRACE("Acquiring image %dx%dx%d bits (format=%d last=%d bpl=%d) from sane...\n" |
| , activeDS.sane_param.pixels_per_line, activeDS.sane_param.lines, |
| activeDS.sane_param.depth, activeDS.sane_param.format, |
| activeDS.sane_param.last_frame, activeDS.sane_param.bytes_per_line); |
| |
| dib_bytes_per_line = ((activeDS.sane_param.bytes_per_line + 3) / 4) * 4; |
| dib_bytes = activeDS.sane_param.lines * dib_bytes_per_line; |
| |
| hDIB = GlobalAlloc(GMEM_ZEROINIT, dib_bytes + sizeof(*header) + color_size); |
| if (hDIB) |
| header = GlobalLock(hDIB); |
| |
| if (!header) |
| { |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| activeDS.twCC = TWCC_LOWMEMORY; |
| if (hDIB) |
| GlobalFree(hDIB); |
| return TWRC_FAILURE; |
| } |
| |
| header->biSize = sizeof (*header); |
| header->biWidth = activeDS.sane_param.pixels_per_line; |
| header->biHeight = activeDS.sane_param.lines; |
| header->biPlanes = 1; |
| header->biCompression = BI_RGB; |
| if (activeDS.sane_param.format == SANE_FRAME_RGB) |
| header->biBitCount = activeDS.sane_param.depth * 3; |
| if (activeDS.sane_param.format == SANE_FRAME_GRAY) |
| header->biBitCount = activeDS.sane_param.depth; |
| header->biSizeImage = dib_bytes; |
| header->biXPelsPerMeter = 0; |
| header->biYPelsPerMeter = 0; |
| header->biClrUsed = 0; |
| header->biClrImportant = 0; |
| |
| p = (BYTE *)(header + 1); |
| |
| if (color_size > 0) |
| { |
| colors = (RGBQUAD *) p; |
| p += color_size; |
| for (i = 0; i < (color_size / sizeof(*colors)); i++) |
| colors[i].rgbBlue = colors[i].rgbRed = colors[i].rgbGreen = i; |
| } |
| |
| |
| /* Sane returns data in top down order. Acrobat does best with |
| a bottom up DIB being returned. */ |
| line = p + (activeDS.sane_param.lines - 1) * dib_bytes_per_line; |
| for (i = activeDS.sane_param.lines - 1; i >= 0; i--) |
| { |
| activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd, |
| ((activeDS.sane_param.lines - 1 - i) * 100) |
| / |
| (activeDS.sane_param.lines - 1)); |
| |
| status = read_one_line(activeDS.deviceHandle, line, |
| activeDS.sane_param.bytes_per_line); |
| if (status != SANE_STATUS_GOOD) |
| break; |
| |
| line -= dib_bytes_per_line; |
| } |
| activeDS.progressWnd = ScanningDialogBox(activeDS.progressWnd, -1); |
| |
| GlobalUnlock(hDIB); |
| |
| if (status != SANE_STATUS_GOOD && status != SANE_STATUS_EOF) |
| { |
| WARN("psane_read: %s, reading line %d\n", psane_strstatus(status), i); |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| activeDS.twCC = TWCC_OPERATIONERROR; |
| GlobalFree(hDIB); |
| return TWRC_FAILURE; |
| } |
| |
| psane_cancel (activeDS.deviceHandle); |
| activeDS.sane_started = FALSE; |
| *pHandle = (UINT_PTR)hDIB; |
| twRC = TWRC_XFERDONE; |
| activeDS.twCC = TWCC_SUCCESS; |
| activeDS.currentState = 7; |
| } |
| return twRC; |
| #endif |
| } |