sane.ds: Implement support for ICAP_XRESOLUTION and ICAP_YRESOLUTION.

This commit is contained in:
Jeremy White 2009-02-23 16:25:26 -06:00 committed by Alexandre Julliard
parent f728c19a73
commit 6ace799f5a
5 changed files with 336 additions and 7 deletions

View File

@ -122,11 +122,40 @@ static TW_UINT16 msg_get_enum(pTW_CAPABILITY pCapability, const TW_UINT32 *value
return TWCC_SUCCESS;
}
#ifdef SONAME_LIBSANE
static TW_UINT16 msg_get_range(pTW_CAPABILITY pCapability, TW_UINT16 type,
TW_UINT32 minval, TW_UINT32 maxval, TW_UINT32 step, TW_UINT32 def, TW_UINT32 current)
{
TW_RANGE *range = NULL;
pCapability->ConType = TWON_RANGE;
pCapability->hContainer = 0;
pCapability->hContainer = GlobalAlloc (0, sizeof(*range));
if (pCapability->hContainer)
range = GlobalLock(pCapability->hContainer);
if (! range)
return TWCC_LOWMEMORY;
range->ItemType = type;
range->MinValue = minval;
range->MaxValue = maxval;
range->StepSize = step;
range->DefaultValue = def;
range->CurrentValue = current;
GlobalUnlock(pCapability->hContainer);
return TWCC_SUCCESS;
}
#endif
static TW_UINT16 TWAIN_GetSupportedCaps(pTW_CAPABILITY pCapability)
{
TW_ARRAY *a;
static const UINT16 supported_caps[] = { CAP_SUPPORTEDCAPS, CAP_XFERCOUNT, CAP_UICONTROLLABLE,
ICAP_XFERMECH, ICAP_PIXELTYPE, ICAP_COMPRESSION, ICAP_PIXELFLAVOR };
ICAP_XFERMECH, ICAP_PIXELTYPE, ICAP_COMPRESSION, ICAP_PIXELFLAVOR,
ICAP_XRESOLUTION, ICAP_YRESOLUTION };
pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ARRAY, ItemList[sizeof(supported_caps)] ));
pCapability->ConType = TWON_ARRAY;
@ -345,6 +374,108 @@ static TW_UINT16 SANE_ICAPCompression (pTW_CAPABILITY pCapability, TW_UINT16 act
return twCC;
}
/* ICAP_XRESOLUTION, ICAP_YRESOLUTION */
static TW_UINT16 SANE_ICAPResolution (pTW_CAPABILITY pCapability, TW_UINT16 action, TW_UINT16 cap)
{
TW_UINT16 twCC = TWCC_BADCAP;
#ifdef SONAME_LIBSANE
TW_UINT32 val;
SANE_Int current_resolution;
TW_FIX32 *default_res;
const char *best_option_name;
SANE_Int minval, maxval, quantval;
SANE_Status sane_rc;
SANE_Int set_status;
TRACE("ICAP_%cRESOLUTION\n", cap == ICAP_XRESOLUTION ? 'X' : 'Y');
/* Some scanners support 'x-resolution', most seem to just support 'resolution' */
if (cap == ICAP_XRESOLUTION)
{
best_option_name = "x-resolution";
default_res = &activeDS.defaultXResolution;
}
else
{
best_option_name = "y-resolution";
default_res = &activeDS.defaultYResolution;
}
if (sane_option_get_int(activeDS.deviceHandle, best_option_name, &current_resolution) != SANE_STATUS_GOOD)
{
best_option_name = "resolution";
if (sane_option_get_int(activeDS.deviceHandle, best_option_name, &current_resolution) != SANE_STATUS_GOOD)
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)
{
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;
}
switch (action)
{
case MSG_QUERYSUPPORT:
twCC = set_onevalue(pCapability, TWTY_INT32,
TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
break;
case MSG_GET:
sane_rc = sane_option_probe_resolution(activeDS.deviceHandle, best_option_name, &minval, &maxval, &quantval);
if (sane_rc != SANE_STATUS_GOOD)
twCC = TWCC_BADCAP;
else
twCC = msg_get_range(pCapability, TWTY_FIX32,
minval, maxval, quantval == 0 ? 1 : quantval, default_res->Whole, current_resolution);
break;
case MSG_SET:
twCC = msg_set(pCapability, &val);
if (twCC == TWCC_SUCCESS)
{
TW_FIX32 f32;
memcpy(&f32, &val, sizeof(f32));
sane_rc = sane_option_set_int(activeDS.deviceHandle, best_option_name, f32.Whole, &set_status);
if (sane_rc != SANE_STATUS_GOOD)
{
FIXME("Status of %d not expected or handled\n", sane_rc);
twCC = TWCC_BADCAP;
}
else if (set_status == SANE_INFO_INEXACT)
twCC = TWCC_CHECKSTATUS;
}
break;
case MSG_GETDEFAULT:
twCC = set_onevalue(pCapability, TWTY_FIX32, default_res->Whole);
break;
case MSG_RESET:
sane_rc = sane_option_set_int(activeDS.deviceHandle, best_option_name, default_res->Whole, NULL);
if (sane_rc != SANE_STATUS_GOOD)
return TWCC_BADCAP;
/* .. fall through intentional .. */
case MSG_GETCURRENT:
twCC = set_onevalue(pCapability, TWTY_FIX32, current_resolution);
break;
}
#endif
return twCC;
}
/* ICAP_PIXELFLAVOR */
static TW_UINT16 SANE_ICAPPixelFlavor (pTW_CAPABILITY pCapability, TW_UINT16 action)
{
@ -427,6 +558,14 @@ TW_UINT16 SANE_SaneCapability (pTW_CAPABILITY pCapability, TW_UINT16 action)
case ICAP_COMPRESSION:
twCC = SANE_ICAPCompression(pCapability, action);
break;
case ICAP_XRESOLUTION:
twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap);
break;
case ICAP_YRESOLUTION:
twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap);
break;
}
/* Twain specifies that you should return a 0 in response to QUERYSUPPORT,

View File

@ -162,7 +162,13 @@ TW_UINT16 SANE_CapabilitySet (pTW_IDENTITY pOrigin,
else
{
twCC = SANE_SaneCapability (pCapability, MSG_SET);
twRC = (twCC == TWCC_SUCCESS)?TWRC_SUCCESS:TWRC_FAILURE;
if (twCC == TWCC_CHECKSTATUS)
{
twCC = TWCC_SUCCESS;
twRC = TWRC_CHECKSTATUS;
}
else
twRC = (twCC == TWCC_SUCCESS)?TWRC_SUCCESS:TWRC_FAILURE;
activeDS.twCC = twCC;
}
return twRC;

View File

@ -26,7 +26,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(twain);
#ifdef SONAME_LIBSANE
SANE_Status sane_option_get_int(SANE_Handle h, const char *option_name, SANE_Int *val)
static SANE_Status sane_find_option(SANE_Handle h, const char *option_name,
const SANE_Option_Descriptor **opt_p, int *optno, SANE_Value_Type type)
{
SANE_Status rc;
SANE_Int optcount;
@ -41,9 +42,59 @@ SANE_Status sane_option_get_int(SANE_Handle h, const char *option_name, SANE_Int
{
opt = psane_get_option_descriptor(h, i);
if (opt && (opt->name && strcmp(opt->name, option_name) == 0) &&
opt->type == SANE_TYPE_INT )
return psane_control_option(h, i, SANE_ACTION_GET_VALUE, val, NULL);
opt->type == type)
{
*opt_p = opt;
*optno = i;
return SANE_STATUS_GOOD;
}
}
return SANE_STATUS_EOF;
}
SANE_Status sane_option_get_int(SANE_Handle h, const char *option_name, SANE_Int *val)
{
SANE_Status rc;
int optno;
const SANE_Option_Descriptor *opt;
rc = sane_find_option(h, option_name, &opt, &optno, SANE_TYPE_INT);
if (rc != SANE_STATUS_GOOD)
return rc;
return psane_control_option(h, optno, SANE_ACTION_GET_VALUE, val, NULL);
}
SANE_Status sane_option_set_int(SANE_Handle h, const char *option_name, SANE_Int val, SANE_Int *status)
{
SANE_Status rc;
int optno;
const SANE_Option_Descriptor *opt;
rc = sane_find_option(h, option_name, &opt, &optno, SANE_TYPE_INT);
if (rc != SANE_STATUS_GOOD)
return rc;
return psane_control_option(h, optno, SANE_ACTION_SET_VALUE, (void *) &val, status);
}
SANE_Status sane_option_probe_resolution(SANE_Handle h, const char *option_name, SANE_Int *minval, SANE_Int *maxval, SANE_Int *quant)
{
SANE_Status rc;
int optno;
const SANE_Option_Descriptor *opt;
rc = sane_find_option(h, option_name, &opt, &optno, SANE_TYPE_INT);
if (rc != SANE_STATUS_GOOD)
return rc;
if (opt->constraint_type != SANE_CONSTRAINT_RANGE)
return SANE_STATUS_UNSUPPORTED;
*minval = opt->constraint.range->min;
*maxval = opt->constraint.range->max;
*quant = opt->constraint.range->quant;
return rc;
}
#endif

View File

@ -53,6 +53,8 @@ MAKE_FUNCPTR(sane_strstatus)
extern HINSTANCE SANE_instance;
#define TWCC_CHECKSTATUS (TWCC_CUSTOMBASE + 1)
/* internal information about an active data source */
struct tagActiveDS
{
@ -73,6 +75,10 @@ struct tagActiveDS
/* Capabilities */
TW_UINT16 capXferMech; /* ICAP_XFERMECH */
TW_UINT16 capPixelType; /* ICAP_PIXELTYPE */
BOOL XResolutionSet;
TW_FIX32 defaultXResolution;
BOOL YResolutionSet;
TW_FIX32 defaultYResolution;
} activeDS;
/* Helper functions */
@ -214,6 +220,8 @@ HWND ScanningDialogBox(HWND dialog, LONG progress);
/* Option functions */
#ifdef SONAME_LIBSANE
SANE_Status sane_option_get_int(SANE_Handle h, const char *option_name, SANE_Int *val);
SANE_Status sane_option_set_int(SANE_Handle h, const char *option_name, SANE_Int val, SANE_Int *status);
SANE_Status sane_option_probe_resolution(SANE_Handle h, const char *option_name, SANE_Int *minval, SANE_Int *maxval, SANE_Int *quant);
#endif

View File

@ -267,6 +267,127 @@ static void test_onevalue_cap(TW_IDENTITY *appid, TW_IDENTITY *source, TW_UINT16
}
}
static void test_resolution(TW_IDENTITY *appid, TW_IDENTITY *source, TW_UINT16 captype, TW_INT32 minimum_support)
{
TW_UINT16 rc;
TW_STATUS status;
TW_CAPABILITY cap;
TW_UINT32 val;
TW_UINT16 type;
TW_INT32 actual_support;
TW_FIX32 orig_value = { 0, 0 };
TW_UINT32 new_value = 0;
TW_FIX32 default_value = { 0, 0 };
memset(&cap, 0, sizeof(cap));
cap.Cap = captype;
cap.ConType = TWON_DONTCARE16;
rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_QUERYSUPPORT, &cap);
get_condition_code(appid, source, &status);
ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
"Error [rc %d|cc %d] doing MSG_QUERYSUPPORT for type 0x%x\n", rc, status.ConditionCode, captype);
if (rc != TWRC_SUCCESS)
return;
ok(get_onevalue(cap.hContainer, (TW_UINT32 *) &actual_support, NULL), "Returned cap.hContainer invalid for QuerySupport on type 0x%x\n", captype);
ok((actual_support & minimum_support) == minimum_support,
"Error: minimum support 0x%x for type 0x%x, got 0x%x\n", minimum_support,
captype, actual_support);
if (actual_support & TWQC_GETCURRENT)
{
memset(&cap, 0, sizeof(cap));
cap.Cap = captype;
cap.ConType = TWON_DONTCARE16;
rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETCURRENT, &cap);
get_condition_code(appid, source, &status);
ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
"Error [rc %d|cc %d] doing MSG_GETCURRENT for type 0x%x\n", rc, status.ConditionCode, captype);
if (rc == TWRC_SUCCESS)
{
get_onevalue(cap.hContainer, &val, &type);
ok(type == TWTY_FIX32, "GETCURRENT for RESOLUTION is not type FIX32, is type %d\n", type);
memcpy(&orig_value, &val, sizeof(orig_value));
GlobalFree(cap.hContainer);
}
}
if (actual_support & TWQC_GETDEFAULT)
{
memset(&cap, 0, sizeof(cap));
cap.Cap = captype;
cap.ConType = TWON_DONTCARE16;
rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETDEFAULT, &cap);
get_condition_code(appid, source, &status);
ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
"Error [rc %d|cc %d] doing MSG_GETDEFAULT for type 0x%x\n", rc, status.ConditionCode, captype);
if (rc == TWRC_SUCCESS)
{
ok(type == TWTY_FIX32, "GETDEFAULT for RESOLUTION is not type FIX32, is type %d\n", type);
memcpy(&default_value, &val, sizeof(default_value));
GlobalFree(cap.hContainer);
}
}
if (actual_support & TWQC_GET)
{
memset(&cap, 0, sizeof(cap));
cap.Cap = captype;
cap.ConType = TWON_DONTCARE16;
rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GET, &cap);
get_condition_code(appid, source, &status);
ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
"Error [rc %d|cc %d] doing MSG_GET for type 0x%x\n", rc, status.ConditionCode, captype);
if (rc == TWRC_SUCCESS)
{
TW_RANGE *range;
ok(cap.ConType == TWON_RANGE, "MSG_GET for ICAP_[XY]RESOLUTION did not return TWON_RANGE, but %d\n", cap.ConType);
range = GlobalLock(cap.hContainer);
trace("MSG_GET of 0x%x returned [ItemType %d|MinValue %d|MaxValue %d|StepSize %d|DefaultValue %d|CurrentValue %d]:\n",
cap.Cap, range->ItemType, range->MinValue, range->MaxValue, range->StepSize,
range->DefaultValue, range->CurrentValue);
for (new_value = range->MinValue; new_value < range->MaxValue; new_value += range->StepSize)
if (new_value != range->CurrentValue)
break;
GlobalUnlock(cap.hContainer);
GlobalFree(cap.hContainer);
}
}
if (actual_support & TWQC_SET)
{
memset(&cap, 0, sizeof(cap));
cap.Cap = captype;
cap.ConType = TWON_ONEVALUE;
cap.hContainer = alloc_and_set_onevalue(new_value, TWTY_FIX32);
rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &cap);
get_condition_code(appid, source, &status);
ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
"Error [rc %d|cc %d] doing MSG_SET for type 0x%x\n", rc, status.ConditionCode, captype);
GlobalFree(cap.hContainer);
}
if (actual_support & TWQC_RESET)
{
memset(&cap, 0, sizeof(cap));
cap.Cap = captype;
cap.ConType = TWON_DONTCARE16;
rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_RESET, &cap);
get_condition_code(appid, source, &status);
ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
"Error [rc %d|cc %d] doing MSG_RESET for type 0x%x\n", rc, status.ConditionCode, captype);
if (rc == TWRC_SUCCESS)
GlobalFree(cap.hContainer);
}
}
static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source)
{
@ -356,10 +477,14 @@ static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source)
if (capabilities[ICAP_XFERMECH])
test_onevalue_cap(appid, source, ICAP_XFERMECH, TWTY_UINT16,
TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
todo_wine
ok(capabilities[ICAP_XRESOLUTION], "ICAP_XRESOLUTION not supported\n");
todo_wine
if (capabilities[ICAP_XRESOLUTION])
test_resolution(appid, source, ICAP_XRESOLUTION,
TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
ok(capabilities[ICAP_YRESOLUTION], "ICAP_YRESOLUTION not supported\n");
if (capabilities[ICAP_YRESOLUTION])
test_resolution(appid, source, ICAP_YRESOLUTION,
TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
}
}