In Twain the capability ICAP_BITDEPTH defines the number of bits to transfer per pixel during a scan. So for an RGB image with 8 bit per color channel, ICAP_BITDEPTH is 24.
In Sane the depth is defined as the number of bits per color channel, so for an RGB image with 8 bit per color channel, depth is 8.
See Page 10-132 (PDF Page 550) if the Twain 2.5 Spec. Obviously older spec versions where not as exact, so there were misunderstandings.
The existing code to handle ICAP_BITDEPTH did not consider this difference and returned the sane depth value.
This merge request fixes this and adds the ability to set the depth to 16 bit per color channel for those sane backends that support the "depth" option.
-- v2: dlls/sane.ds: Refuse native transfer mode with depth 16 bit dlls/sane.ds: Fix ICAP_BITDEPTH semantics, allow setting it
From: Bernd Herd codeberg@herdsoft.com
Twain ICAP_BITDEPTH is defined in bits per pixel, whereas in sane depth is defined per color channel. Fix it.
Also allows setting ICAP_BITDEPTH for those sane backends that allow it --- dlls/sane.ds/capability.c | 78 ++++++++++++++++++++++++++++++++++----- dlls/sane.ds/options.c | 2 +- dlls/sane.ds/sane_i.h | 1 + 3 files changed, 71 insertions(+), 10 deletions(-)
diff --git a/dlls/sane.ds/capability.c b/dlls/sane.ds/capability.c index 33b586b0824..52633931007 100644 --- a/dlls/sane.ds/capability.c +++ b/dlls/sane.ds/capability.c @@ -85,10 +85,10 @@ static TW_UINT16 msg_get_enum(pTW_CAPABILITY pCapability, const TW_UINT32 *value pCapability->hContainer = 0;
if (type == TWTY_INT16 || type == TWTY_UINT16) - pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ENUMERATION, ItemList[value_count * sizeof(TW_UINT16)])); + pCapability->hContainer = GlobalAlloc (GMEM_ZEROINIT, FIELD_OFFSET( TW_ENUMERATION, ItemList[value_count * sizeof(TW_UINT16)]));
if (type == TWTY_INT32 || type == TWTY_UINT32) - pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ENUMERATION, ItemList[value_count * sizeof(TW_UINT32)])); + pCapability->hContainer = GlobalAlloc (GMEM_ZEROINIT, FIELD_OFFSET( TW_ENUMERATION, ItemList[value_count * sizeof(TW_UINT32)]));
if (pCapability->hContainer) enumv = GlobalLock(pCapability->hContainer); @@ -476,30 +476,90 @@ static TW_UINT16 SANE_ICAPUnits (pTW_CAPABILITY pCapability, TW_UINT16 action) static TW_UINT16 SANE_ICAPBitDepth(pTW_CAPABILITY pCapability, TW_UINT16 action) { TW_UINT16 twCC = TWCC_BADCAP; - TW_UINT32 possible_values[1]; + TW_UINT32 val; + TW_UINT32 sane_depth, twain_depth; + BOOL have_option_depth; + int samples_per_pixel; + struct option_descriptor opt;
TRACE("ICAP_BITDEPTH\n");
- possible_values[0] = activeDS.frame_params.depth; + get_sane_params(&activeDS.frame_params); // Updates activeDS.frame_params.format + samples_per_pixel=(activeDS.frame_params.format == FMT_RGB) ? 3 : 1; + + sane_depth=activeDS.frame_params.depth; + have_option_depth = + sane_find_option( "depth", TYPE_INT, &opt ) == TWCC_SUCCESS + && (opt.size==sizeof(TW_UINT32)); + if (have_option_depth) + { + twCC = sane_option_get_value(opt.optno, &sane_depth); + } + twain_depth = sane_depth*samples_per_pixel;
switch (action) { case MSG_QUERYSUPPORT: twCC = set_onevalue(pCapability, TWTY_INT32, - TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT ); + TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT | (have_option_depth ? TWQC_SET : 0) ); break;
case MSG_GET: - twCC = msg_get_enum(pCapability, possible_values, ARRAY_SIZE(possible_values), - TWTY_UINT16, activeDS.frame_params.depth, activeDS.frame_params.depth); + if (have_option_depth && + opt.constraint_type == CONSTRAINT_WORD_LIST && + opt.constraint.word_list[0]<=32) + { + /* The constraint word ist is in bits per color channel, for TWAIN we need bits per pixel */ + TW_UINT32 enum_bitdepths[32]; + int i; + for (i=0; i<opt.constraint.word_list[0]; i++) + enum_bitdepths[i] = opt.constraint.word_list[i+1] * samples_per_pixel; + twCC = msg_get_enum(pCapability, enum_bitdepths, opt.constraint.word_list[0], + TWTY_UINT16, twain_depth, twain_depth); + } + else + { + twCC = msg_get_enum(pCapability, &twain_depth, 1, + TWTY_UINT16, twain_depth, twain_depth); + } + break; + + case MSG_SET: + if (have_option_depth) + { + twCC = msg_set(pCapability, &val); + if (twCC == TWCC_SUCCESS) + { + BOOL reload = FALSE; + TW_UINT16 val16 = (TW_UINT16) val; + + /* TWAIN Spec 2.4 says unambiguous that the depth is defined per pixel, + * not per color channel. However it also warns that there have been + * misunderstandings. So interpret it... */ + if (val16==8 && samples_per_pixel==3) + { + val16=24; + } else if (val16==16 && samples_per_pixel==3) + { + val16=48; + } + sane_depth = val16/samples_per_pixel; + twCC = sane_option_set_value(opt.optno, &sane_depth, &reload); + if (reload) twCC = TWCC_CHECKSTATUS; + } + } + else + { + twCC = TWCC_BADCAP; + } break;
case MSG_GETDEFAULT: /* .. Fall through intentional .. */
case MSG_GETCURRENT: - TRACE("Returning current bitdepth of %d\n", activeDS.frame_params.depth); - twCC = set_onevalue(pCapability, TWTY_UINT16, activeDS.frame_params.depth); + TRACE("Returning current bitdepth of %ld\n", twain_depth); + twCC = set_onevalue(pCapability, TWTY_UINT16, twain_depth); break; } return twCC; diff --git a/dlls/sane.ds/options.c b/dlls/sane.ds/options.c index 335e0306f38..f46ba0f8fc7 100644 --- a/dlls/sane.ds/options.c +++ b/dlls/sane.ds/options.c @@ -40,7 +40,7 @@ TW_UINT16 sane_option_set_value( int optno, void *val, BOOL *reload ) return SANE_CALL( option_set_value, ¶ms ); }
-static TW_UINT16 sane_find_option( const char *name, int type, struct option_descriptor *descr ) +TW_UINT16 sane_find_option( const char *name, int type, struct option_descriptor *descr ) { struct option_find_descriptor_params params = { name, type, descr }; return SANE_CALL( option_find_descriptor, ¶ms ) ? TWCC_CAPUNSUPPORTED : TWCC_SUCCESS; diff --git a/dlls/sane.ds/sane_i.h b/dlls/sane.ds/sane_i.h index 0691f02e22d..4ce7f42e1b1 100644 --- a/dlls/sane.ds/sane_i.h +++ b/dlls/sane.ds/sane_i.h @@ -207,6 +207,7 @@ BOOL DoScannerUI(void); HWND ScanningDialogBox(HWND dialog, LONG progress);
/* Option functions */ +TW_UINT16 sane_find_option( const char *name, int type, struct option_descriptor *descr ); TW_UINT16 sane_option_get_value( int optno, void *val ); TW_UINT16 sane_option_set_value( int optno, void *val, BOOL *reload ); TW_UINT16 sane_option_get_int( const char *option_name, int *val );
From: Bernd Herd codeberg@herdsoft.com
Native transfer mode on windows can only work with 8 bit per color component as biBitCount values of 48 for 16 bit per color component are no valid values for a DIB --- dlls/sane.ds/ds_image.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/dlls/sane.ds/ds_image.c b/dlls/sane.ds/ds_image.c index 37327bc5db6..4b4e77dbcad 100644 --- a/dlls/sane.ds/ds_image.c +++ b/dlls/sane.ds/ds_image.c @@ -386,6 +386,14 @@ TW_UINT16 SANE_ImageNativeXferGet (pTW_IDENTITY pOrigin, } break; case FMT_RGB: + if (activeDS.frame_params.depth != 8) + { + FIXME("For NATIVE, we support only 8 bit per color channel, not %d\n", activeDS.frame_params.depth); + SANE_Cancel(); + activeDS.twCC = TWCC_OPERATIONERROR; + activeDS.currentState = 6; + return TWRC_FAILURE; + } break; case FMT_OTHER: FIXME("For NATIVE, we support only GRAY and RGB\n");
Esme Povirk (@madewokherd) commented about dlls/sane.ds/capability.c:
int samples_per_pixel;
struct option_descriptor opt;
TRACE("ICAP_BITDEPTH\n");
- possible_values[0] = activeDS.frame_params.depth;
- get_sane_params(&activeDS.frame_params); // Updates activeDS.frame_params.format
- samples_per_pixel=(activeDS.frame_params.format == FMT_RGB) ? 3 : 1;
- sane_depth=activeDS.frame_params.depth;
- have_option_depth =
sane_find_option( "depth", TYPE_INT, &opt ) == TWCC_SUCCESS&& (opt.size==sizeof(TW_UINT32));- if (have_option_depth)
- {
twCC = sane_option_get_value(opt.optno, &sane_depth);
I think this is effectively a dead assignment, as every defined case in the following switch statement overwrites it.
Technically, this also potentially overwrites the TWCC_BADCAP in the case where we're given an unsupported action. We'll probably have to do that in a default case in the switch statement.