Files
third_party_sane-airscan/airscan-devops.c
T
Alexander Pevzner 02f19226be The "sane-intent" option cannot be SANE_CAP_INACTIVE
As "*unset*" is always implicitly supported, the never-executing code
that marks option inactive if set of supported intents is empty, removed.
2024-11-19 11:47:27 +03:00

869 lines
24 KiB
C

/* AirScan (a.k.a. eSCL) backend for SANE
*
* Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Device options
*/
#include "airscan.h"
#include <stdlib.h>
#include <string.h>
/* Static variables */
static const SANE_Range devopt_percent_range = {
.min = SANE_FIX(-100.0),
.max = SANE_FIX(100.0),
.quant = SANE_FIX(1.0)
};
static const SANE_Range devopt_nonnegative_percent_range = {
.min = SANE_FIX(0.0),
.max = SANE_FIX(100.0),
.quant = SANE_FIX(1.0)
};
static const SANE_Range devopt_gamma_range = {
.min = SANE_FIX(0.1),
.max = SANE_FIX(4.0),
};
/* Initialize device options
*/
void
devopt_init (devopt *opt)
{
devcaps_init(&opt->caps);
opt->src = ID_SOURCE_UNKNOWN;
opt->colormode_emul = ID_COLORMODE_UNKNOWN;
opt->colormode_real = ID_COLORMODE_UNKNOWN;
opt->resolution = CONFIG_DEFAULT_RESOLUTION;
opt->sane_sources = sane_string_array_new();
opt->sane_colormodes = sane_string_array_new();
opt->sane_scanintents = sane_string_array_new();
}
/* Cleanup device options
*/
void
devopt_cleanup (devopt *opt)
{
sane_string_array_free(opt->sane_sources);
sane_string_array_free(opt->sane_colormodes);
sane_string_array_free(opt->sane_scanintents);
devcaps_cleanup(&opt->caps);
}
/* Choose default source
*/
static ID_SOURCE
devopt_choose_default_source (devopt *opt)
{
/* Choose initial source */
ID_SOURCE id_src = (ID_SOURCE) 0;
while (id_src < NUM_ID_SOURCE &&
(opt->caps.src[id_src]) == NULL) {
id_src ++;
}
log_assert(NULL, id_src != NUM_ID_SOURCE);
return id_src;
}
/* Get available color modes
*/
static unsigned int
devopt_available_colormodes (const devcaps_source *src)
{
unsigned int colormodes = src->colormodes;
if ((colormodes & (1 << ID_COLORMODE_COLOR)) != 0) {
colormodes |= 1 << ID_COLORMODE_GRAYSCALE; /* We can resample! */
}
return colormodes;
}
/* Chose "real" color mode that can be used for emulated color mode
*/
static ID_COLORMODE
devopt_real_colormode (ID_COLORMODE emulated, const devcaps_source *src)
{
if ((src->colormodes & (1 << emulated)) != 0) {
return emulated;
}
switch (emulated) {
case ID_COLORMODE_GRAYSCALE:
log_assert(NULL, (src->colormodes & (1 << ID_COLORMODE_COLOR)) != 0);
return ID_COLORMODE_COLOR;
default:
log_internal_error(NULL);
}
return ID_COLORMODE_UNKNOWN;
}
/* Choose appropriate color mode
*/
static ID_COLORMODE
devopt_choose_colormode (devopt *opt, ID_COLORMODE wanted)
{
devcaps_source *src = opt->caps.src[opt->src];
unsigned int colormodes = devopt_available_colormodes(src);
/* Prefer wanted mode if possible and if not, try to find
* a reasonable downgrade */
if (wanted != ID_COLORMODE_UNKNOWN) {
while (wanted < NUM_ID_COLORMODE) {
if ((colormodes & (1 << wanted)) != 0) {
return wanted;
}
wanted ++;
}
}
/* Nothing found in a previous step. Just choose the best mode
* supported by the scanner */
wanted = (ID_COLORMODE) 0;
while ((colormodes & (1 << wanted)) == 0) {
log_assert(NULL, wanted < NUM_ID_COLORMODE);
wanted ++;
}
return wanted;
}
/* Choose appropriate scanner resolution
*/
static SANE_Word
devopt_choose_resolution (devopt *opt, SANE_Word wanted)
{
devcaps_source *src = opt->caps.src[opt->src];
if (src->flags & DEVCAPS_SOURCE_RES_DISCRETE) {
SANE_Word res = src->resolutions[1];
SANE_Word delta = (SANE_Word) labs(wanted - res);
size_t i, end = sane_word_array_len(src->resolutions) + 1;
for (i = 2; i < end; i ++) {
SANE_Word res2 = src->resolutions[i];
SANE_Word delta2 = (SANE_Word) labs(wanted - res2);
if (delta2 <= delta) {
res = res2;
delta = delta2;
}
}
return res;
} else {
return math_range_fit(&src->res_range, wanted);
}
}
/* Rebuild option descriptors
*/
static void
devopt_rebuild_opt_desc (devopt *opt)
{
SANE_Option_Descriptor *desc;
devcaps_source *src = opt->caps.src[opt->src];
unsigned int colormodes = devopt_available_colormodes(src);
unsigned int scanintents = src->scanintents;
int i;
const char *s;
memset(opt->desc, 0, sizeof(opt->desc));
sane_string_array_reset(opt->sane_sources);
sane_string_array_reset(opt->sane_colormodes);
sane_string_array_reset(opt->sane_scanintents);
for (i = 0; i < NUM_ID_SOURCE; i ++) {
if (opt->caps.src[i] != NULL) {
opt->sane_sources = sane_string_array_append(
opt->sane_sources, (SANE_String) id_source_sane_name(i));
}
}
for (i = 0; i < NUM_ID_COLORMODE; i ++) {
if ((colormodes & (1 << i)) != 0) {
opt->sane_colormodes =
sane_string_array_append(
opt->sane_colormodes, (SANE_String) id_colormode_sane_name(i));
}
}
scanintents |= 1 << ID_SCANINTENT_UNSET; /* Always implicitly supported */
for (i = 0; i < NUM_ID_SCANINTENT; i ++) {
if ((scanintents & (1 << i)) != 0) {
opt->sane_scanintents =
sane_string_array_append(
opt->sane_scanintents, (SANE_String) id_scanintent_sane_name(i));
}
}
/* OPT_NUM_OPTIONS */
desc = &opt->desc[OPT_NUM_OPTIONS];
desc->name = SANE_NAME_NUM_OPTIONS;
desc->title = SANE_TITLE_NUM_OPTIONS;
desc->desc = SANE_DESC_NUM_OPTIONS;
desc->type = SANE_TYPE_INT;
desc->size = sizeof(SANE_Word);
desc->cap = SANE_CAP_SOFT_DETECT;
/* OPT_GROUP_STANDARD */
desc = &opt->desc[OPT_GROUP_STANDARD];
desc->name = SANE_NAME_STANDARD;
desc->title = SANE_TITLE_STANDARD;
desc->desc = SANE_DESC_STANDARD;
desc->type = SANE_TYPE_GROUP;
desc->cap = 0;
/* OPT_SCAN_RESOLUTION */
desc = &opt->desc[OPT_SCAN_RESOLUTION];
desc->name = SANE_NAME_SCAN_RESOLUTION;
desc->title = SANE_TITLE_SCAN_RESOLUTION;
desc->desc = SANE_DESC_SCAN_RESOLUTION;
desc->type = SANE_TYPE_INT;
desc->size = sizeof(SANE_Word);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
desc->unit = SANE_UNIT_DPI;
if ((src->flags & DEVCAPS_SOURCE_RES_DISCRETE) != 0) {
desc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
desc->constraint.word_list = src->resolutions;
} else {
desc->constraint_type = SANE_CONSTRAINT_RANGE;
desc->constraint.range = &src->res_range;
}
/* OPT_SCAN_MODE */
desc = &opt->desc[OPT_SCAN_COLORMODE];
desc->name = SANE_NAME_SCAN_MODE;
desc->title = SANE_TITLE_SCAN_MODE;
desc->desc = SANE_DESC_SCAN_MODE;
desc->type = SANE_TYPE_STRING;
desc->size = sane_string_array_max_strlen(opt->sane_colormodes) + 1;
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
desc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
desc->constraint.string_list = (SANE_String_Const*) opt->sane_colormodes;
/* OPT_SCAN_INTENT */
desc = &opt->desc[OPT_SCAN_INTENT];
desc->name = "scan-intent";
desc->title = "Scan intent";
desc->desc = "Optimize scan for Text/Photo/etc.";
desc->type = SANE_TYPE_STRING;
desc->size = sane_string_array_max_strlen(opt->sane_scanintents) + 1;
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
desc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
desc->constraint.string_list = (SANE_String_Const*) opt->sane_scanintents;
/* OPT_SCAN_SOURCE */
desc = &opt->desc[OPT_SCAN_SOURCE];
desc->name = SANE_NAME_SCAN_SOURCE;
desc->title = SANE_TITLE_SCAN_SOURCE;
desc->desc = SANE_DESC_SCAN_SOURCE;
desc->type = SANE_TYPE_STRING;
desc->size = sane_string_array_max_strlen(opt->sane_sources) + 1;
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
desc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
desc->constraint.string_list = (SANE_String_Const*) opt->sane_sources;
/* OPT_GROUP_GEOMETRY */
desc = &opt->desc[OPT_GROUP_GEOMETRY];
desc->name = SANE_NAME_GEOMETRY;
desc->title = SANE_TITLE_GEOMETRY;
desc->desc = SANE_DESC_GEOMETRY;
desc->type = SANE_TYPE_GROUP;
desc->cap = 0;
/* OPT_SCAN_TL_X */
desc = &opt->desc[OPT_SCAN_TL_X];
desc->name = SANE_NAME_SCAN_TL_X;
desc->title = SANE_TITLE_SCAN_TL_X;
desc->desc = SANE_DESC_SCAN_TL_X;
desc->type = SANE_TYPE_FIXED;
desc->size = sizeof(SANE_Fixed);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
desc->unit = SANE_UNIT_MM;
desc->constraint_type = SANE_CONSTRAINT_RANGE;
desc->constraint.range = &src->win_x_range_mm;
/* OPT_SCAN_TL_Y */
desc = &opt->desc[OPT_SCAN_TL_Y];
desc->name = SANE_NAME_SCAN_TL_Y;
desc->title = SANE_TITLE_SCAN_TL_Y;
desc->desc = SANE_DESC_SCAN_TL_Y;
desc->type = SANE_TYPE_FIXED;
desc->size = sizeof(SANE_Fixed);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
desc->unit = SANE_UNIT_MM;
desc->constraint_type = SANE_CONSTRAINT_RANGE;
desc->constraint.range = &src->win_y_range_mm;
/* OPT_SCAN_BR_X */
desc = &opt->desc[OPT_SCAN_BR_X];
desc->name = SANE_NAME_SCAN_BR_X;
desc->title = SANE_TITLE_SCAN_BR_X;
desc->desc = SANE_DESC_SCAN_BR_X;
desc->type = SANE_TYPE_FIXED;
desc->size = sizeof(SANE_Fixed);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
desc->unit = SANE_UNIT_MM;
desc->constraint_type = SANE_CONSTRAINT_RANGE;
desc->constraint.range = &src->win_x_range_mm;
/* OPT_SCAN_BR_Y */
desc = &opt->desc[OPT_SCAN_BR_Y];
desc->name = SANE_NAME_SCAN_BR_Y;
desc->title = SANE_TITLE_SCAN_BR_Y;
desc->desc = SANE_DESC_SCAN_BR_Y;
desc->type = SANE_TYPE_FIXED;
desc->size = sizeof(SANE_Fixed);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
desc->unit = SANE_UNIT_MM;
desc->constraint_type = SANE_CONSTRAINT_RANGE;
desc->constraint.range = &src->win_y_range_mm;
/* OPT_GROUP_ENHANCEMENT */
desc = &opt->desc[OPT_GROUP_ENHANCEMENT];
desc->name = SANE_NAME_ENHANCEMENT;
desc->title = SANE_TITLE_ENHANCEMENT;
desc->desc = SANE_DESC_ENHANCEMENT;
desc->type = SANE_TYPE_GROUP;
desc->cap = 0;
/* OPT_BRIGHTNESS */
desc = &opt->desc[OPT_BRIGHTNESS];
desc->name = SANE_NAME_BRIGHTNESS;
desc->title = SANE_TITLE_BRIGHTNESS;
desc->desc = SANE_DESC_BRIGHTNESS;
desc->type = SANE_TYPE_FIXED;
desc->size = sizeof(SANE_Fixed);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_EMULATED;
desc->unit = SANE_UNIT_PERCENT;
desc->constraint_type = SANE_CONSTRAINT_RANGE;
desc->constraint.range = &devopt_percent_range;
/* OPT_CONTRAST */
desc = &opt->desc[OPT_CONTRAST];
desc->name = SANE_NAME_CONTRAST;
desc->title = SANE_TITLE_CONTRAST;
desc->desc = SANE_DESC_CONTRAST;
desc->type = SANE_TYPE_FIXED;
desc->size = sizeof(SANE_Fixed);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_EMULATED;
desc->unit = SANE_UNIT_PERCENT;
desc->constraint_type = SANE_CONSTRAINT_RANGE;
desc->constraint.range = &devopt_percent_range;
/* OPT_SHADOW */
desc = &opt->desc[OPT_SHADOW];
desc->name = SANE_NAME_SHADOW;
desc->title = SANE_TITLE_SHADOW;
desc->desc = SANE_DESC_SHADOW;
desc->type = SANE_TYPE_FIXED;
desc->size = sizeof(SANE_Fixed);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_EMULATED;
desc->unit = SANE_UNIT_PERCENT;
desc->constraint_type = SANE_CONSTRAINT_RANGE;
desc->constraint.range = &devopt_nonnegative_percent_range;
/* OPT_HIGHLIGHT */
desc = &opt->desc[OPT_HIGHLIGHT];
desc->name = SANE_NAME_HIGHLIGHT;
desc->title = SANE_TITLE_HIGHLIGHT;
desc->desc = SANE_DESC_HIGHLIGHT;
desc->type = SANE_TYPE_FIXED;
desc->size = sizeof(SANE_Fixed);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_EMULATED;
desc->unit = SANE_UNIT_PERCENT;
desc->constraint_type = SANE_CONSTRAINT_RANGE;
desc->constraint.range = &devopt_nonnegative_percent_range;
/* OPT_GAMMA */
desc = &opt->desc[OPT_GAMMA];
desc->name = SANE_NAME_ANALOG_GAMMA;
desc->title = SANE_TITLE_ANALOG_GAMMA;
desc->desc = SANE_DESC_ANALOG_GAMMA;
desc->type = SANE_TYPE_FIXED;
desc->size = sizeof(SANE_Fixed);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_EMULATED;
desc->unit = SANE_UNIT_NONE;
desc->constraint_type = SANE_CONSTRAINT_RANGE;
desc->constraint.range = &devopt_gamma_range;
/* OPT_NEGATIVE */
desc = &opt->desc[OPT_NEGATIVE];
desc->name = SANE_NAME_NEGATIVE;
desc->title = SANE_TITLE_NEGATIVE;
desc->desc = SANE_DESC_NEGATIVE;
desc->type = SANE_TYPE_BOOL;
desc->size = sizeof(SANE_Bool);
desc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_EMULATED;
/* OPT_JUSTIFICATION_X */
desc = &opt->desc[OPT_JUSTIFICATION_X];
desc->name = SANE_NAME_ADF_JUSTIFICATION_X;
desc->title = SANE_TITLE_ADF_JUSTIFICATION_X;
desc->desc = SANE_DESC_ADF_JUSTIFICATION_X;
desc->type = SANE_TYPE_STRING;
desc->cap = SANE_CAP_SOFT_DETECT;
if (opt->caps.justification_x == ID_JUSTIFICATION_UNKNOWN) {
desc->cap |= SANE_CAP_INACTIVE;
}
s = id_justification_sane_name(opt->caps.justification_x);
desc->size = (s ? strlen(s) : 0) + 1;
/* OPT_JUSTIFICATION_Y */
desc = &opt->desc[OPT_JUSTIFICATION_Y];
desc->name = SANE_NAME_ADF_JUSTIFICATION_Y;
desc->title = SANE_TITLE_ADF_JUSTIFICATION_Y;
desc->desc = SANE_DESC_ADF_JUSTIFICATION_Y;
desc->type = SANE_TYPE_STRING;
desc->cap = SANE_CAP_SOFT_DETECT;
if (opt->caps.justification_y == ID_JUSTIFICATION_UNKNOWN) {
desc->cap |= SANE_CAP_INACTIVE;
}
s = id_justification_sane_name(opt->caps.justification_y);
desc->size = (s ? strlen(s) : 0) + 1;
}
/* Update scan parameters, according to the currently set
* scan options
*/
static void
devopt_update_params (devopt *opt)
{
SANE_Fixed wid = math_max(0, opt->br_x - opt->tl_x);
SANE_Fixed hei = math_max(0, opt->br_y - opt->tl_y);
opt->params.last_frame = SANE_TRUE;
opt->params.pixels_per_line = math_mm2px_res(wid, opt->resolution);
opt->params.lines = math_mm2px_res(hei, opt->resolution);
switch (opt->colormode_emul) {
case ID_COLORMODE_COLOR:
opt->params.format = SANE_FRAME_RGB;
opt->params.depth = 8;
opt->params.bytes_per_line = opt->params.pixels_per_line * 3;
break;
case ID_COLORMODE_GRAYSCALE:
opt->params.format = SANE_FRAME_GRAY;
opt->params.depth = 8;
opt->params.bytes_per_line = opt->params.pixels_per_line;
break;
case ID_COLORMODE_BW1:
opt->params.format = SANE_FRAME_GRAY;
opt->params.depth = 1;
opt->params.bytes_per_line =
((opt->params.pixels_per_line + 7) / 8) * 8;
break;
default:
log_assert(NULL, !"internal error");
}
}
/* Set current resolution
*/
static SANE_Status
devopt_set_resolution (devopt *opt, SANE_Word opt_resolution, SANE_Word *info)
{
if (opt->resolution == opt_resolution) {
return SANE_STATUS_GOOD;
}
opt->resolution = devopt_choose_resolution(opt, opt_resolution);
*info |= SANE_INFO_RELOAD_PARAMS;
if (opt->resolution != opt_resolution) {
*info |= SANE_INFO_INEXACT;
}
return SANE_STATUS_GOOD;
}
/* Set color mode
*/
static SANE_Status
devopt_set_colormode (devopt *opt, ID_COLORMODE id_colormode, SANE_Word *info)
{
devcaps_source *src = opt->caps.src[opt->src];
unsigned int colormodes = devopt_available_colormodes(src);
if (opt->colormode_emul == id_colormode) {
return SANE_STATUS_GOOD;
}
if ((colormodes & (1 << id_colormode)) == 0) {
return SANE_STATUS_INVAL;
}
opt->colormode_emul = id_colormode;
opt->colormode_real = devopt_real_colormode(id_colormode, src);
*info |= SANE_INFO_RELOAD_PARAMS;
return SANE_STATUS_GOOD;
}
/* Set current source. Affects many other options
*/
static SANE_Status
devopt_set_source (devopt *opt, ID_SOURCE id_src, SANE_Word *info)
{
devcaps_source *src = opt->caps.src[id_src];
if (src == NULL) {
return SANE_STATUS_INVAL;
}
if (opt->src == id_src) {
return SANE_STATUS_GOOD;
}
opt->src = id_src;
/* Try to preserve current color mode */
opt->colormode_emul = devopt_choose_colormode(opt, opt->colormode_emul);
/* Try to preserve resolution */
opt->resolution = devopt_choose_resolution(opt, opt->resolution);
/* Reset window to maximum size */
opt->tl_x = 0;
opt->tl_y = 0;
opt->br_x = src->win_x_range_mm.max;
opt->br_y = src->win_y_range_mm.max;
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
return SANE_STATUS_GOOD;
}
/* Set scan intent.
*/
static SANE_Status
devopt_set_scanintent (devopt *opt, ID_SCANINTENT intent)
{
devcaps_source *src = opt->caps.src[opt->src];
unsigned int scanintents = src->scanintents;
scanintents |= 1 << ID_SCANINTENT_UNSET; /* Always implicitly supported */
if ((scanintents & (1 << intent)) == 0) {
return SANE_STATUS_INVAL;
}
opt->scanintent = intent;
return SANE_STATUS_GOOD;
}
/* Set geometry option
*/
static SANE_Status
devopt_set_geom (devopt *opt, SANE_Int option, SANE_Fixed val, SANE_Word *info)
{
SANE_Fixed *out = NULL;
SANE_Range *range = NULL;
devcaps_source *src = opt->caps.src[opt->src];
/* Choose destination and range */
switch (option) {
case OPT_SCAN_TL_X:
out = &opt->tl_x;
range = &src->win_x_range_mm;
break;
case OPT_SCAN_TL_Y:
out = &opt->tl_y;
range = &src->win_y_range_mm;
break;
case OPT_SCAN_BR_X:
out = &opt->br_x;
range = &src->win_x_range_mm;
break;
case OPT_SCAN_BR_Y:
out = &opt->br_y;
range = &src->win_y_range_mm;
break;
default:
log_internal_error(NULL);
}
/* Update option */
if (*out != val) {
*out = math_range_fit(range, val);
if (*out == val) {
*info |= SANE_INFO_RELOAD_PARAMS;
} else {
*info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_INEXACT;
}
}
return SANE_STATUS_GOOD;
}
/* Set enhancement option
*/
static SANE_Status
devopt_set_enh (devopt *opt, SANE_Int option, SANE_Fixed val, SANE_Word *info)
{
SANE_Fixed *out = NULL;
SANE_Range range = *opt->desc[option].constraint.range;
SANE_Fixed val_adjusted;
switch (option) {
case OPT_BRIGHTNESS:
out = &opt->brightness;
break;
case OPT_CONTRAST:
out = &opt->contrast;
break;
case OPT_SHADOW:
out = &opt->shadow;
range.max = opt->highlight - range.quant;
break;
case OPT_HIGHLIGHT:
out = &opt->highlight;
range.min = opt->shadow + range.quant;
break;
case OPT_GAMMA:
out = &opt->gamma;
break;
default:
log_internal_error(NULL);
}
val_adjusted = math_range_fit(&range, val);
if (val_adjusted != val) {
*info |= SANE_INFO_INEXACT;
}
*out = val_adjusted;
return SANE_STATUS_GOOD;
}
/* Set default option values. Before call to this function,
* devopt.caps needs to be properly filled.
*/
void
devopt_set_defaults (devopt *opt)
{
devcaps_source *src;
opt->src = devopt_choose_default_source(opt);
src = opt->caps.src[opt->src];
opt->colormode_emul = devopt_choose_colormode(opt, ID_COLORMODE_UNKNOWN);
opt->colormode_real = devopt_real_colormode(opt->colormode_emul, src);
opt->scanintent = ID_SCANINTENT_UNSET;
opt->resolution = devopt_choose_resolution(opt, CONFIG_DEFAULT_RESOLUTION);
opt->tl_x = 0;
opt->tl_y = 0;
opt->br_x = src->win_x_range_mm.max;
opt->br_y = src->win_y_range_mm.max;
opt->brightness = SANE_FIX(0.0);
opt->contrast = SANE_FIX(0.0);
opt->shadow = SANE_FIX(0.0);
opt->highlight = SANE_FIX(100.0);
opt->gamma = SANE_FIX(1.0);
devopt_rebuild_opt_desc(opt);
devopt_update_params(opt);
}
/* Set device option
*/
SANE_Status
devopt_set_option (devopt *opt, SANE_Int option, void *value, SANE_Word *info)
{
SANE_Status status = SANE_STATUS_GOOD;
ID_SOURCE id_src;
ID_COLORMODE id_colormode;
ID_SCANINTENT id_scanintent;
/* Simplify life of options handlers by ensuring info != NULL */
if (info == NULL) {
static SANE_Word unused;
info = &unused;
}
*info = 0;
/* Switch by option */
switch (option) {
case OPT_SCAN_RESOLUTION:
status = devopt_set_resolution(opt, *(SANE_Word*)value, info);
break;
case OPT_SCAN_COLORMODE:
id_colormode = id_colormode_by_sane_name(value);
if (id_colormode == ID_COLORMODE_UNKNOWN) {
status = SANE_STATUS_INVAL;
} else {
status = devopt_set_colormode(opt, id_colormode, info);
}
break;
case OPT_SCAN_INTENT:
id_scanintent = id_scanintent_by_sane_name(value);
if (id_scanintent == ID_SCANINTENT_UNKNOWN) {
status = SANE_STATUS_INVAL;
} else {
status = devopt_set_scanintent(opt, id_scanintent);
}
break;
case OPT_SCAN_SOURCE:
id_src = id_source_by_sane_name(value);
if (id_src == ID_SOURCE_UNKNOWN) {
status = SANE_STATUS_INVAL;
} else {
status = devopt_set_source(opt, id_src, info);
}
break;
case OPT_SCAN_TL_X:
case OPT_SCAN_TL_Y:
case OPT_SCAN_BR_X:
case OPT_SCAN_BR_Y:
status = devopt_set_geom(opt, option, *(SANE_Fixed*)value, info);
break;
case OPT_BRIGHTNESS:
case OPT_CONTRAST:
case OPT_SHADOW:
case OPT_HIGHLIGHT:
case OPT_GAMMA:
status = devopt_set_enh(opt, option, *(SANE_Fixed*)value, info);
break;
case OPT_NEGATIVE:
opt->negative = *(SANE_Bool*)value != 0;
break;
default:
status = SANE_STATUS_INVAL;
}
/* Rebuild option descriptors and update scan parameters, if needed */
if ((*info & SANE_INFO_RELOAD_OPTIONS) != 0) {
devopt_rebuild_opt_desc(opt);
}
if ((*info & SANE_INFO_RELOAD_PARAMS) != 0) {
devopt_update_params(opt);
}
return status;
}
/* Get device option
*/
SANE_Status
devopt_get_option (devopt *opt, SANE_Int option, void *value)
{
SANE_Status status = SANE_STATUS_GOOD;
const char *s;
switch (option) {
case OPT_NUM_OPTIONS:
*(SANE_Word*) value = NUM_OPTIONS;
break;
case OPT_SCAN_RESOLUTION:
*(SANE_Word*) value = opt->resolution;
break;
case OPT_SCAN_COLORMODE:
strcpy(value, id_colormode_sane_name(opt->colormode_emul));
break;
case OPT_SCAN_INTENT:
strcpy(value, id_scanintent_sane_name(opt->scanintent));
break;
case OPT_SCAN_SOURCE:
strcpy(value, id_source_sane_name(opt->src));
break;
case OPT_SCAN_TL_X:
*(SANE_Fixed*) value = opt->tl_x;
break;
case OPT_SCAN_TL_Y:
*(SANE_Fixed*) value = opt->tl_y;
break;
case OPT_SCAN_BR_X:
*(SANE_Fixed*) value = opt->br_x;
break;
case OPT_SCAN_BR_Y:
*(SANE_Fixed*) value = opt->br_y;
break;
case OPT_BRIGHTNESS:
*(SANE_Fixed*) value = opt->brightness;
break;
case OPT_CONTRAST:
*(SANE_Fixed*) value = opt->contrast;
break;
case OPT_SHADOW:
*(SANE_Fixed*) value = opt->shadow;
break;
case OPT_HIGHLIGHT:
*(SANE_Fixed*) value = opt->highlight;
break;
case OPT_GAMMA:
*(SANE_Fixed*) value = opt->gamma;
break;
case OPT_NEGATIVE:
*(SANE_Bool*)value = opt->negative;
break;
case OPT_JUSTIFICATION_X:
s = id_justification_sane_name(opt->caps.justification_x);
strcpy(value, s ? s : "");
break;
case OPT_JUSTIFICATION_Y:
s = id_justification_sane_name(opt->caps.justification_y);
strcpy(value, s ? s : "");
break;
default:
status = SANE_STATUS_INVAL;
}
return status;
}
/* vim:ts=8:sw=4:et
*/