| /* | |
| * 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 | |
| } |