From: Bernd Herd codeberg@herdsoft.com
Most SANE backends store resolution in DPI as an integer value TYPE_INT. Some store it as TYPE_FIXED. Twain always uses TW_FIX32 for resolution. Provide access in ICAP_xRESOLUTION and DG_IMAGE/DAT_IMAGEINFO/MSG_GET for both types.
Cache resolutions before SANE_Start as some backends do not allow option read access after scanning started any more. --- dlls/sane.ds/capability.c | 53 +++++++--------- dlls/sane.ds/ds_image.c | 36 ++++++++--- dlls/sane.ds/options.c | 129 +++++++++++++++++++++++++++++++++++++- dlls/sane.ds/sane_i.h | 10 ++- dlls/sane.ds/sane_main.c | 9 +++ dlls/sane.ds/unixlib.c | 3 +- 6 files changed, 197 insertions(+), 43 deletions(-)
diff --git a/dlls/sane.ds/capability.c b/dlls/sane.ds/capability.c index 7ae48071580..670c0eb9c41 100644 --- a/dlls/sane.ds/capability.c +++ b/dlls/sane.ds/capability.c @@ -44,7 +44,7 @@ static TW_UINT16 get_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 *type, TW_UI }
-static TW_UINT16 set_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 type, TW_UINT32 value) +static TW_UINT16 set_onevaluep(pTW_CAPABILITY pCapability, TW_UINT16 type, void *value) { pCapability->hContainer = GlobalAlloc (0, sizeof(TW_ONEVALUE));
@@ -55,7 +55,7 @@ static TW_UINT16 set_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 type, TW_UIN { pCapability->ConType = TWON_ONEVALUE; pVal->ItemType = type; - pVal->Item = value; + memcpy(&pVal->Item, value, sizeof(pVal->Item)); GlobalUnlock (pCapability->hContainer); return TWCC_SUCCESS; } @@ -63,6 +63,12 @@ static TW_UINT16 set_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 type, TW_UIN return TWCC_LOWMEMORY; }
+ +static TW_UINT16 set_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 type, TW_UINT32 value) +{ + return set_onevaluep(pCapability, type, &value); +} + static TW_UINT16 msg_set(pTW_CAPABILITY pCapability, TW_UINT32 *val) { if (pCapability->ConType == TWON_ONEVALUE) @@ -573,7 +579,7 @@ static TW_UINT16 SANE_ICAPResolution (pTW_CAPABILITY pCapability, TW_UINT16 acti { TW_UINT16 twCC = TWCC_BADCAP; TW_UINT32 val; - int current_resolution; + TW_FIX32 current_resolution; TW_FIX32 *default_res; const char *best_option_name; struct option_descriptor opt; @@ -591,28 +597,9 @@ static TW_UINT16 SANE_ICAPResolution (pTW_CAPABILITY pCapability, TW_UINT16 acti best_option_name = "y-resolution"; default_res = &activeDS.defaultYResolution; } - if (sane_option_get_int(best_option_name, ¤t_resolution) != TWCC_SUCCESS) - { - best_option_name = "resolution"; - if (sane_option_get_int(best_option_name, ¤t_resolution) != TWCC_SUCCESS) - return TWCC_BADCAP; - } - - /* Sane does not support a concept of 'default' resolution, so we have to - * cache the resolution the very first time we load the scanner, and use that - * as the default */ - if (cap == ICAP_XRESOLUTION && ! activeDS.XResolutionSet) + if (sane_option_get_resolution(best_option_name, ¤t_resolution) != TWCC_SUCCESS) { - default_res->Whole = current_resolution; - default_res->Frac = 0; - activeDS.XResolutionSet = TRUE; - } - - if (cap == ICAP_YRESOLUTION && ! activeDS.YResolutionSet) - { - default_res->Whole = current_resolution; - default_res->Frac = 0; - activeDS.YResolutionSet = TRUE; + return TWCC_BADCAP; }
switch (action) @@ -623,20 +610,24 @@ static TW_UINT16 SANE_ICAPResolution (pTW_CAPABILITY pCapability, TW_UINT16 acti break;
case MSG_GET: - twCC = sane_option_probe_resolution(best_option_name, &opt); - if (twCC == TWCC_SUCCESS) + if (sane_option_probe_resolution(best_option_name, &opt)==TWCC_SUCCESS || + sane_option_probe_resolution("resolution", &opt)==TWCC_SUCCESS) { if (opt.constraint_type == CONSTRAINT_RANGE) twCC = msg_get_range(pCapability, TWTY_FIX32, opt.constraint.range.min, opt.constraint.range.max, opt.constraint.range.quant == 0 ? 1 : opt.constraint.range.quant, - default_res->Whole, current_resolution); + default_res->Whole, current_resolution.Whole); else if (opt.constraint_type == CONSTRAINT_WORD_LIST) twCC = msg_get_array(pCapability, TWTY_UINT32, &(opt.constraint.word_list[1]), opt.constraint.word_list[0]); else twCC = TWCC_CAPUNSUPPORTED; } + else + { + twCC = TWCC_BADCAP; + } break;
case MSG_SET: @@ -646,23 +637,23 @@ static TW_UINT16 SANE_ICAPResolution (pTW_CAPABILITY pCapability, TW_UINT16 acti TW_FIX32 f32; BOOL reload = FALSE; memcpy(&f32, &val, sizeof(f32)); - twCC = sane_option_set_int(best_option_name, f32.Whole, &reload); + twCC = sane_option_set_resolution(best_option_name, &f32, &reload); if (reload) twCC = TWCC_CHECKSTATUS; } break;
case MSG_GETDEFAULT: - twCC = set_onevalue(pCapability, TWTY_FIX32, default_res->Whole); + twCC = set_onevaluep(pCapability, TWTY_FIX32, default_res); break;
case MSG_RESET: - twCC = sane_option_set_int(best_option_name, default_res->Whole, NULL); + twCC = sane_option_set_resolution(best_option_name, default_res, NULL); if (twCC != TWCC_SUCCESS) return twCC;
/* .. fall through intentional .. */
case MSG_GETCURRENT: - twCC = set_onevalue(pCapability, TWTY_FIX32, current_resolution); + twCC = set_onevaluep(pCapability, TWTY_FIX32, ¤t_resolution); break; } return twCC; diff --git a/dlls/sane.ds/ds_image.c b/dlls/sane.ds/ds_image.c index f2b0a294531..81730024cf0 100644 --- a/dlls/sane.ds/ds_image.c +++ b/dlls/sane.ds/ds_image.c @@ -25,6 +25,8 @@ #include "wine/debug.h" #include "resource.h"
+#include <winnls.h> + WINE_DEFAULT_DEBUG_CHANNEL(twain);
/* Sane result codes from sane.h */ @@ -141,6 +143,8 @@ TW_UINT16 SANE_Start(void)
if (activeDS.progressWnd) { + WCHAR szLocaleBuffer[4]; + WCHAR szResolution[200]; WCHAR szFormat[20]; int sid_format;
@@ -153,7 +157,28 @@ TW_UINT16 SANE_Start(void)
LoadStringW( SANE_instance, sid_format, szFormat, ARRAY_SIZE( szFormat ) );
- SetDlgItemTextW(activeDS.progressWnd, IDC_RESOLUTION, szFormat); + if (activeDS.XResolution.Whole && activeDS.YResolution.Whole) + { + const double xresolution = activeDS.XResolution.Whole + activeDS.XResolution.Frac / 65536.0; + const double yresolution = activeDS.YResolution.Whole + activeDS.YResolution.Frac / 65536.0; + + GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_IMEASURE, szLocaleBuffer, ARRAY_SIZE(szLocaleBuffer)); + if (szLocaleBuffer[0]=='1') + { + swprintf(szResolution, ARRAY_SIZE(szResolution), L"%d DPI %s %.2f x %.2f "", + activeDS.XResolution.Whole, szFormat, + activeDS.frame_params.pixels_per_line / xresolution , + activeDS.frame_params.lines / yresolution ); + } + else + { + swprintf(szResolution, ARRAY_SIZE(szResolution), L"%d DPI %s %.1f x %.1f mm", + activeDS.XResolution.Whole, szFormat, + activeDS.frame_params.pixels_per_line * 25.4 / xresolution , + activeDS.frame_params.lines * 25.4 / yresolution ); + } + SetDlgItemTextW(activeDS.progressWnd, IDC_RESOLUTION, szResolution); + } }
TRACE("Acquiring image %dx%dx%d bits (format=%d last=%d) from sane...\n" @@ -189,7 +214,6 @@ TW_UINT16 SANE_ImageInfoGet (pTW_IDENTITY pOrigin, { TW_UINT16 twRC = TWRC_SUCCESS; pTW_IMAGEINFO pImageInfo = (pTW_IMAGEINFO) pData; - int resolution;
TRACE("DG_IMAGE/DAT_IMAGEINFO/MSG_GET\n");
@@ -210,12 +234,8 @@ TW_UINT16 SANE_ImageInfoGet (pTW_IDENTITY pOrigin, } }
- if (sane_option_get_int("resolution", &resolution) == TWCC_SUCCESS) - pImageInfo->XResolution.Whole = pImageInfo->YResolution.Whole = resolution; - else - pImageInfo->XResolution.Whole = pImageInfo->YResolution.Whole = -1; - pImageInfo->XResolution.Frac = 0; - pImageInfo->YResolution.Frac = 0; + pImageInfo->XResolution = activeDS.XResolution; + pImageInfo->YResolution = activeDS.YResolution; pImageInfo->ImageWidth = activeDS.frame_params.pixels_per_line; pImageInfo->ImageLength = activeDS.frame_params.lines;
diff --git a/dlls/sane.ds/options.c b/dlls/sane.ds/options.c index 335e0306f38..f485fb04bb3 100644 --- a/dlls/sane.ds/options.c +++ b/dlls/sane.ds/options.c @@ -65,6 +65,130 @@ TW_UINT16 sane_option_set_int(const char *option_name, int val, BOOL *needs_relo return rc; }
+ +/** @brief Read an option from SANE into a TW_FIX32 structure + * + * If the sane option is stored as TYPE_INT, then the value is + * set into the "Whole" portion. + * + * @param option_name Name of the SANE option, like "resolution" + * @param val OUT: The value of the option + * @return TWCC_SUCCESS on success + */ +TW_UINT16 sane_option_get_fix32(const char *option_name, TW_FIX32 *val) +{ + struct option_descriptor opt; + int resolution; + TW_UINT16 rc = sane_find_option(option_name, -1, &opt); + if (rc == TWCC_SUCCESS) + { + if (opt.size == sizeof(int) && + (opt.type == TYPE_INT || opt.type == TYPE_FIXED)) + { + rc = sane_option_get_value(opt.optno, &resolution); + if (opt.type == TYPE_INT) + { + val->Whole = resolution; + val->Frac = 0; + } + else + { + val->Whole= resolution >> 16; + val->Frac = resolution & 0xffff; + } + } + else + { + rc = TWCC_BADCAP; + } + } + return rc; +} + + +/** @brief Set an option from SANE from a TW_FIX32 structure + * + * If the sane option is stored as TYPE_INT, then the value is + * set into the "Whole" portion. + * + * @param option_name Name of the SANE option, like "resolution" + * @param val The new value of the option + * @param needs_reload Forwarded to sane_option_set_value + * + * @return TWCC_SUCCESS on success + */ +TW_UINT16 sane_option_set_fix32(const char *option_name, const TW_FIX32 *val, BOOL *needs_reload ) +{ + struct option_descriptor opt; + int resolution; + TW_UINT16 rc = sane_find_option(option_name, -1, &opt); + if (rc == TWCC_SUCCESS) + { + if (opt.is_settable && + opt.size == sizeof(int) && + (opt.type == TYPE_INT || opt.type == TYPE_FIXED)) + { + resolution = + (opt.type == TYPE_INT) + ? val->Whole + : ( (val->Whole << 16) | val->Frac); + + rc = sane_option_set_value( opt.optno, &resolution, needs_reload ); + } + else + { + rc = TWCC_BADCAP; + } + } + + return rc; +} + + +/** @brief Get X or Y-Resolution from SANE and store as TW_FIX32 + * @param best_option_name Either "x-resolution" or "y-resolution" + * @param val OUT: Resolution value read or -1 + * @return TWCC_SUCCESS on success + */ +TW_UINT16 +sane_option_get_resolution(const char *best_option_name, TW_FIX32 *val) +{ + TW_UINT16 rc; + if (sane_option_get_fix32(best_option_name, val)!=TWCC_SUCCESS && + sane_option_get_fix32("resolution" , val)!=TWCC_SUCCESS) + { + val->Whole = -1; + val->Frac = 0; + rc = TWCC_BADCAP; + } + else + { + rc = TWCC_SUCCESS; + } + return rc; +} + + + +/** @brief Set X or Y-Resolution in SANE and from a TW_FIX32 + * @param best_option_name Either "x-resolution" or "y-resolution" + * @param val New resolution to set + * @param needs_reload Forwarded to sane_option_set_value + * @return TWCC_SUCCESS on success + */ +TW_UINT16 +sane_option_set_resolution(const char *best_option_name, const TW_FIX32 *val, BOOL *needs_reload) +{ + return + sane_option_set_fix32(best_option_name, val, needs_reload)==TWCC_SUCCESS || + sane_option_set_fix32("resolution" , val, needs_reload)==TWCC_SUCCESS + ? TWCC_SUCCESS + : TWCC_BADCAP; +} + + + + TW_UINT16 sane_option_get_bool(const char *option_name, int *val) { struct option_descriptor opt; @@ -112,7 +236,10 @@ TW_UINT16 sane_option_set_str(const char *option_name, char *val, BOOL *needs_re
TW_UINT16 sane_option_probe_resolution(const char *option_name, struct option_descriptor *opt) { - return sane_find_option(option_name, TYPE_INT, opt); + TW_UINT16 rc = sane_find_option(option_name, -1, opt); + return rc==TWCC_SUCCESS + ? (opt->type == TYPE_INT || opt->type == TYPE_FIXED) ? TWCC_SUCCESS : TWCC_BADCAP + : rc; }
static void sane_categorize_value(const WCHAR* value, const WCHAR* const* filter[], char* categories, int buf_len) diff --git a/dlls/sane.ds/sane_i.h b/dlls/sane.ds/sane_i.h index 8a1a742605e..9a77ce3b301 100644 --- a/dlls/sane.ds/sane_i.h +++ b/dlls/sane.ds/sane_i.h @@ -49,9 +49,7 @@ struct tagActiveDS TW_BOOL capIndicators; /* CAP_INDICATORS */ BOOL PixelTypeSet; TW_UINT16 defaultPixelType; /* ICAP_PIXELTYPE */ - BOOL XResolutionSet; TW_FIX32 defaultXResolution; - BOOL YResolutionSet; TW_FIX32 defaultYResolution;
/* Number of scan lines already transfered in DG_IMAGE / DAT_IMAGEMEMXFER / MSG_GET. */ @@ -68,6 +66,10 @@ struct tagActiveDS
/* TRUE if user interface dialog is shown in DG_CONTROL/DAT_USERINTERFACE/MSG_ENABLEDS */ BOOL ShowUI; + + /* Resolution used during scan */ + TW_FIX32 XResolution; + TW_FIX32 YResolution; };
extern struct tagActiveDS activeDS; @@ -221,6 +223,8 @@ TW_UINT16 sane_option_get_int( const char *option_name, int *val ); TW_UINT16 sane_option_set_int( const char *option_name, int val, BOOL *reload ); TW_UINT16 sane_option_get_str( const char *option_name, char *val, int len ); TW_UINT16 sane_option_set_str( const char *option_name, char *val, BOOL *reload ); +TW_UINT16 sane_option_get_fix32( const char *option_name, TW_FIX32 *val); +TW_UINT16 sane_option_set_fix32( const char *option_name, const TW_FIX32 *val, BOOL *reload); TW_UINT16 sane_option_probe_resolution( const char *option_name, struct option_descriptor *opt ); TW_UINT16 sane_option_probe_str( const char* option_name, const WCHAR* const* filter[], char* opt_vals, int buf_len ); TW_UINT16 sane_option_get_bool( const char *option_name, BOOL *val ); @@ -228,6 +232,8 @@ TW_UINT16 sane_option_set_bool( const char *option_name, BOOL val ); TW_UINT16 sane_option_get_scan_area( int *tlx, int *tly, int *brx, int *bry ); TW_UINT16 sane_option_get_max_scan_area( int *tlx, int *tly, int *brx, int *bry ); TW_UINT16 sane_option_set_scan_area( int tlx, int tly, int brx, int bry, BOOL *reload ); +TW_UINT16 sane_option_get_resolution(const char *best_option_name, TW_FIX32 *val); +TW_UINT16 sane_option_set_resolution(const char *best_option_name, const TW_FIX32 *val, BOOL *reload); TW_FIX32 convert_sane_res_to_twain( int res ); int convert_twain_res_to_sane( TW_FIX32 res ); TW_UINT16 get_sane_params( struct frame_parameters *params ); diff --git a/dlls/sane.ds/sane_main.c b/dlls/sane.ds/sane_main.c index 6b8f1e23f89..1d3f62862e1 100644 --- a/dlls/sane.ds/sane_main.c +++ b/dlls/sane.ds/sane_main.c @@ -85,6 +85,12 @@ static TW_UINT16 SANE_OpenDS( pTW_IDENTITY pOrigin, pTW_IDENTITY self) activeDS.capIndicators = TRUE; activeDS.ShowUI = FALSE;
+ /* Sane does not support a concept of 'default' resolution, so we have to + * cache the resolution the very first time we load the scanner, and use that + * as the default */ + sane_option_get_resolution("x-resolution", &activeDS.defaultXResolution); + sane_option_get_resolution("y-resolution", &activeDS.defaultYResolution); + SANE_LoadOptions();
return TWRC_SUCCESS; @@ -394,6 +400,9 @@ SANE_XferReady(void) (current_source[0]=='A' || current_source[0]=='a'); activeDS.userCancelled = FALSE;
+ sane_option_get_resolution("x-resolution", &activeDS.XResolution); + sane_option_get_resolution("y-resolution", &activeDS.YResolution); + SANE_Notify(MSG_XFERREADY); }
diff --git a/dlls/sane.ds/unixlib.c b/dlls/sane.ds/unixlib.c index 011a00d07b7..f7955706c24 100644 --- a/dlls/sane.ds/unixlib.c +++ b/dlls/sane.ds/unixlib.c @@ -399,7 +399,8 @@ static NTSTATUS option_find_descriptor( void *args )
for (i = 1; (opt = sane_get_option_descriptor( device_handle, i )) != NULL; i++) { - if (params->type != map_type( opt->type )) continue; + if (params->type >= 0 && + params->type != map_type( opt->type )) continue; if (strcmp( params->name, opt->name )) continue; descr->optno = i; map_descr( descr, opt );