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.
This MR provide access to the resolution in ICAP_xRESOLUTION and DG_IMAGE/DAT_IMAGEINFO/MSG_GET for both types of sane backends.
Since some backends have only one option "resolution" for both X and Y resolution, while others provide separate options for "x-resolution" and "y-resolution", there are a lot of possible combinations.
Some backends do not allow read access to to their options once scanning has started. This had the consequene that for these backends, DG_IMAGE/DAT_IMAGEINFO/MSG_GET specified a resolution of -1. For that reason the current X/Y Resolution is now cached in activeDS before starting the scan.
One backend that uses TYPE_FIXED is the SANE "test" backend. When setting a resolution, the test backend rounds to the nearest integer, so 300.4 DPI get 300.0 DPI and 300.6 DPI git 301 DPI.
The test backend is not the only backend that stores resolution as TYPE_FIXED.
This MR also contains code to display the resolution in DPI and the paper size calculated by pixel width/height and resolution in the progress dialog. If LOCALE_IMEASURE says that the locale applies the metric system, sizes are displayed in mm, otherwise (USA) sizes are displayed in inch.
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 );