mirror of
https://github.com/openharmony/third_party_sane-airscan.git
synced 2026-07-01 21:14:00 -04:00
02f19226be
As "*unset*" is always implicitly supported, the never-executing code that marks option inactive if set of supported intents is empty, removed.
869 lines
24 KiB
C
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
|
|
*/
|