From acc4c2e8e95699868763efcabddff6f81b6efb4f Mon Sep 17 00:00:00 2001 From: gb <741021719@qq.com> Date: Wed, 3 May 2023 11:21:41 +0800 Subject: [PATCH] add mode, paper, ... options --- .../packages/common.pkg/include/commondef.h | 2 + device/gxx-linux/scanner/scannerregs.cpp | 18 +- device/gxx-linux/usb/inc/usbdevice.h | 2 + .../usb/src/async_model/common/log_util.cpp | 2 + .../usb/src/async_model/common/log_util.h | 2 + .../usb/src/async_model/common/packet.h | 9 + .../usb/src/async_model/common/paper.cpp | 122 ++++++ .../usb/src/async_model/common/paper.h | 32 ++ .../usb/src/async_model/common/sane_cfg.cpp | 82 +--- .../usb/src/async_model/common/sane_cfg.h | 65 ++- .../usb/src/async_model/hardware/hardware.cpp | 381 +++++------------- .../usb/src/async_model/hardware/hardware.h | 381 +++++++++++++++++- .../img_process/algs/auto_crop.cpp | 185 +++++++++ .../async_model/img_process/algs/auto_crop.h | 53 +++ .../img_process/algs/discard_blank.cpp | 191 +++++++++ .../img_process/algs/discard_blank.h | 199 +++++++++ .../async_model/img_process/algs/dogear.cpp | 6 +- .../src/async_model/img_process/algs/dogear.h | 4 +- .../img_process/algs/img_algorithm.cpp | 198 +++++++-- .../img_process/algs/img_algorithm.h | 41 +- .../img_process/algs/remove_hole.cpp | 199 +++++++++ .../img_process/algs/remove_hole.h | 253 ++++++++++++ .../async_model/img_process/cis_preproc.cpp | 6 +- .../src/async_model/img_process/cis_preproc.h | 4 +- .../async_model/img_process/img_process.cpp | 15 +- .../src/async_model/img_process/img_process.h | 4 +- .../src/async_model/scanner/async_scanner.cpp | 4 +- .../src/async_model/scanner/async_scanner.h | 2 +- .../src/async_model/scanner/readonly_opts.cpp | 6 +- .../src/async_model/scanner/readonly_opts.h | 4 +- device/gxx-linux/usb/src/usbdevice.cpp | 20 + .../sln/usb_tools/Debug/usb_tools.exe | Bin 1767936 -> 1767936 bytes .../sln/usb_tools/scanner/opt_ui/DlgPage.cpp | 61 ++- 33 files changed, 2094 insertions(+), 459 deletions(-) create mode 100644 device/gxx-linux/usb/src/async_model/common/paper.cpp create mode 100644 device/gxx-linux/usb/src/async_model/common/paper.h create mode 100644 device/gxx-linux/usb/src/async_model/img_process/algs/auto_crop.cpp create mode 100644 device/gxx-linux/usb/src/async_model/img_process/algs/auto_crop.h create mode 100644 device/gxx-linux/usb/src/async_model/img_process/algs/discard_blank.cpp create mode 100644 device/gxx-linux/usb/src/async_model/img_process/algs/discard_blank.h create mode 100644 device/gxx-linux/usb/src/async_model/img_process/algs/remove_hole.cpp create mode 100644 device/gxx-linux/usb/src/async_model/img_process/algs/remove_hole.h diff --git a/device/gxx-linux/packages/common.pkg/include/commondef.h b/device/gxx-linux/packages/common.pkg/include/commondef.h index 671b0b5..9b90e35 100644 --- a/device/gxx-linux/packages/common.pkg/include/commondef.h +++ b/device/gxx-linux/packages/common.pkg/include/commondef.h @@ -509,6 +509,8 @@ enum Scanner_Reg_Defs SR_SCAN_DPI = 0x600, SR_SCAN_CNT, + SR_SCAN_PAPER, + SR_SCAN_PIXEL, }; typedef union Ctrol_Description{ diff --git a/device/gxx-linux/scanner/scannerregs.cpp b/device/gxx-linux/scanner/scannerregs.cpp index 1abe04b..57dfb0c 100644 --- a/device/gxx-linux/scanner/scannerregs.cpp +++ b/device/gxx-linux/scanner/scannerregs.cpp @@ -100,7 +100,7 @@ ScannerRegAccess::~ScannerRegAccess() bool ScannerRegAccess::write(unsigned int addr, const unsigned int val) { static uint64_t img_cb = 0, img_param = 0; - static uint32_t scan_dpi = 200, scan_cnt = -1; + static uint32_t scan_dpi = 200, scan_cnt = -1, paper_type = 0, pixel_type = 0; if(addr == (unsigned int)-1) { @@ -192,6 +192,12 @@ bool ScannerRegAccess::write(unsigned int addr, const unsigned int val) case SR_SCAN_CNT: scan_cnt = val; return true; + case SR_SCAN_PAPER: + paper_type = val; + return true; + case SR_SCAN_PIXEL: + pixel_type = val; + return true; case SR_CONFIF_IMGPROCPARAM: { printf("\nSR_CONFIF_IMGPROCPARAM size =%d\n",val); @@ -200,9 +206,15 @@ bool ScannerRegAccess::write(unsigned int addr, const unsigned int val) GScanCap cap = *(GScanCap*)&cfg[0]; cap.resolution_dst = scan_dpi; - cap.resolution_native = 200; + if(scan_dpi < 300.0f) + cap.resolution_native = 200.0f; + else if(scan_dpi < 500.0f) + cap.resolution_native = 300.0f; + else + cap.resolution_native = 600.0f; cap.scannum = scan_cnt; - cap.pixtype = 0; + cap.pixtype = pixel_type; + cap.papertype = paper_type; usbimages->set_dpi(scan_dpi, scan_dpi); printf("DPI: %d, Count: %d\n", scan_dpi, scan_cnt); #else diff --git a/device/gxx-linux/usb/inc/usbdevice.h b/device/gxx-linux/usb/inc/usbdevice.h index 9baeb2e..379f6e7 100644 --- a/device/gxx-linux/usb/inc/usbdevice.h +++ b/device/gxx-linux/usb/inc/usbdevice.h @@ -31,6 +31,8 @@ public: int set_dpi(int *dpix, int* dpiy); int set_scan_num(int num); int set_img_receiver(void* api, void* param); + int set_paper_type(int type); + int set_pixel_type(int *type); #endif public: diff --git a/device/gxx-linux/usb/src/async_model/common/log_util.cpp b/device/gxx-linux/usb/src/async_model/common/log_util.cpp index e8036a5..962f113 100644 --- a/device/gxx-linux/usb/src/async_model/common/log_util.cpp +++ b/device/gxx-linux/usb/src/async_model/common/log_util.cpp @@ -160,6 +160,7 @@ int32_t log_cls::log_when_err(int32_t err, const char* oper_desc, log_level leve return err; } +#if !defined(WIN32) && !defined(_WIN64) void log_cls::log_memory_usage(const char* tag, bool print_screen, const char* prog_name) { std::string memu("--Memory usage of "); @@ -172,6 +173,7 @@ void log_cls::log_memory_usage(const char* tag, bool print_screen, const char* p else if (log_cls::inst_) log_cls::inst_->log(LOG_LEVEL_DEBUG, "%s\n", memu.c_str()); } +#endif log_level log_cls::get_log_level(void) { if (log_cls::inst_) diff --git a/device/gxx-linux/usb/src/async_model/common/log_util.h b/device/gxx-linux/usb/src/async_model/common/log_util.h index b8e5bd2..a4395e9 100644 --- a/device/gxx-linux/usb/src/async_model/common/log_util.h +++ b/device/gxx-linux/usb/src/async_model/common/log_util.h @@ -60,7 +60,9 @@ public: } } static int32_t log_when_err(int32_t err, const char* oper_desc, log_level level = LOG_LEVEL_WARNING); // log as: oper_desc = strerror(errno)\n. return real error number errno +#if !defined(WIN32) && !defined(_WIN64) static void log_memory_usage(const char* tag, bool print_screen = false/*call printf if this was true, or write down to file*/, const char* prog_name = "scan"); +#endif static log_level get_log_level(void); static std::string get_log_file(void); diff --git a/device/gxx-linux/usb/src/async_model/common/packet.h b/device/gxx-linux/usb/src/async_model/common/packet.h index 00d1948..f632a69 100644 --- a/device/gxx-linux/usb/src/async_model/common/packet.h +++ b/device/gxx-linux/usb/src/async_model/common/packet.h @@ -17,6 +17,9 @@ #define FLOAT_PRECISION .000001f #define IS_FLOAT_EQUAL(x, y) (-FLOAT_PRECISION <= (x) - (y) && (x) - (y) <= FLOAT_PRECISION) #define MAKE_WORD(b0, b1) (((b0) & 0xff) | (((b1) << 8) & 0x0ff00)) +#define MAKE_STR(str) #str +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + #define ROGER(cmd) cmd##_ROGER #define PAIR_COMMAND(cmd) \ cmd, \ @@ -224,6 +227,12 @@ enum clr_channel COLOR_CHANNEL_BLUE, COLOR_CHANNEL_ALPHA, }; +enum color_mode +{ + COLOR_MODE_BW = 0, + COLOR_MODE_GRAY, + COLOR_MODE_RGB, +}; #pragma pack(push) #pragma pack(1) diff --git a/device/gxx-linux/usb/src/async_model/common/paper.cpp b/device/gxx-linux/usb/src/async_model/common/paper.cpp new file mode 100644 index 0000000..a6236ea --- /dev/null +++ b/device/gxx-linux/usb/src/async_model/common/paper.cpp @@ -0,0 +1,122 @@ +#include "paper.h" + +#include "common/packet.h" +#include "../packages/common.pkg/include/commondef.h" + + + + + + + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// exporting api: +#define CM_PER_INCH 2.54f + +static struct paper_size +{ + const char* name; + TwSS type; + double w; + double h; +}g_paper_size[] = { + {"A3", TwSS::A3, 29.7f, 42.0f}, + {"A4", TwSS::A4, 21.0f, 29.7f}, + {"A5", TwSS::A5, 14.8f, 21.0f}, + {"A6", TwSS::A6, 10.5f, 14.8f}, + {"B4", TwSS::B4, 25.7f, 36.4f}, + {"B5", TwSS::B5, 17.6f, 25.0f}, // {PAPER_B5, {182, 257}}, + {"B6", TwSS::B6, 12.5f, 17.6f}, + {"8\xE5\xBC\x80", TwSS::K8, 29.7f, 42.0f}, + {"16\xE5\xBC\x80", TwSS::K16, 21.0f, 28.5f}, + {"Letter", TwSS::A4Letter, 21.6f, 27.9f}, + {"Legal", TwSS::USLegal, 21.6f, 35.6f}, + {"Double-Letter", TwSS::USLedger, 43.2f, 27.9f}, + {"\xE4\xB8\x89\xE8\x81\x94\xE8\xAF\x95\xE5\x8D\xB7", TwSS::Trigeminy, 29.7f, 42.0f}, // fixed me ... + {"\xE5\x8C\xB9\xE9\x85\x8D\xE5\x8E\x9F\xE5\xA7\x8B\xE5\xB0\xBA\xE5\xAF\xB8", TwSS::None, 29.7f, 42.0f}, //\xfixed\xme\x... + {"\xE6\x9C\x80\xE5\xA4\xA7\xE6\x89\xAB\xE6\x8F\x8F\xE5\xB0\xBA\xE5\xAF\xB8\xE8\x87\xAA\xE5\x8A\xA8\xE8\xA3\x81\xE5\x88\x87", TwSS::USStatement, 29.7f, 42.0f}, //\xfixed\xme\x... + {"\xE6\x9C\x80\xE5\xA4\xA7\xE6\x89\xAB\xE6\x8F\x8F\xE5\xB0\xBA\xE5\xAF\xB8", TwSS::MaxSize, 29.7f, 42.0f}, //\xfixed\xme\x... + }; + +namespace paper +{ + double inch_2_cm(double inch) + { + return inch * CM_PER_INCH; + } + double cm_2_inch(double cm) + { + return cm / CM_PER_INCH; + } + int inch_2_pixel(double inch, double dpi) + { + return inch * dpi + .5f; + } + double pixel_2_inch(int pixel, double dpi) + { + return pixel / dpi; + } + int cm_2_pixel(double cm, double dpi) + { + return inch_2_pixel(cm_2_inch(cm), dpi); + } + double pixel_2_cm(int pixel, double dpi) + { + return inch_2_cm(pixel_2_inch(pixel, dpi)); + } + + // Function: get known paper-type size in cm + // + // Parameter: name - paper type name, such as "A3", "A4", ... + // + // w - to receive width in cm + // + // h - to receive height in cm + // + // Return: true if 'name' was supported, or else false + bool paper_size(const char* name, double* w, double* h, int* type) + { + bool fit = false; + + for(auto& v: g_paper_size) + { + if(strcmp(v.name, name) == 0) + { + if(w) + *w = v.w; + if(h) + *h = v.h; + if(type) + *type = v.type; + fit = true; + break; + } + } + + if(!fit) + { + // A3 - 2338(29.7), 3307(42.0) + if(w) + *w = g_paper_size[0].w; + if(h) + *h = g_paper_size[0].h; + if(type) + *type = g_paper_size[0].type; + } + + return fit; + } +}; diff --git a/device/gxx-linux/usb/src/async_model/common/paper.h b/device/gxx-linux/usb/src/async_model/common/paper.h new file mode 100644 index 0000000..b791a21 --- /dev/null +++ b/device/gxx-linux/usb/src/async_model/common/paper.h @@ -0,0 +1,32 @@ +#pragma once + +// Objects for paper size ... +// +// created on 2023-05-02 + + + + +namespace paper +{ + double inch_2_cm(double inch); + double cm_2_inch(double cm); + int inch_2_pixel(double inch, double dpi = 200.0f); + double pixel_2_inch(int pixel, double dpi = 200.0f); + int cm_2_pixel(double cm, double dpi = 200.0f); + double pixel_2_cm(int pixel, double dpi = 200.0f); + + // Function: get known paper-type size in cm + // + // Parameter: name - paper type name, such as "A3", "A4", ... + // + // w - to receive width in cm + // + // h - to receive height in cm + // + // type - to receive paper type of TwSS + // + // Return: true if 'name' was supported, or else false + bool paper_size(const char* name, double* w, double* h, int* type = nullptr); +}; + diff --git a/device/gxx-linux/usb/src/async_model/common/sane_cfg.cpp b/device/gxx-linux/usb/src/async_model/common/sane_cfg.cpp index 67e4fb2..c954cd3 100644 --- a/device/gxx-linux/usb/src/async_model/common/sane_cfg.cpp +++ b/device/gxx-linux/usb/src/async_model/common/sane_cfg.cpp @@ -288,7 +288,7 @@ bool sane_cfg_provider::sane_refine_range(gb_json* jsn, void* data, size_t* len) return ok; } -void sane_cfg_provider::update_option_enable_status(gb_json* opt, std::function get_opt) +void sane_cfg_provider::update_option_enable_status(gb_json* opt, std::function get_opt) { gb_json* depends = nullptr, *item = nullptr; bool able = true, oper_and = false; @@ -322,6 +322,7 @@ void sane_cfg_provider::update_option_enable_status(gb_json* opt, std::function< } depends->release(); opt->set_value("enabled", able); + log_cls::log(LOG_LEVEL_DEBUG, "%s enabled: %s. (%s = %s)\n", opt->key().c_str(), able ? "true" : "false", name.c_str(), val.c_str()); } } data_type sane_cfg_provider::type_from_string(const char* type_desc) @@ -337,7 +338,7 @@ data_type sane_cfg_provider::type_from_string(const char* type_desc) return DATA_TYPE_CUSTOM; } - bool sane_cfg_provider::raw_value_in_json(gb_json* root, const char* key, std::string& val) + bool sane_cfg_provider::raw_value_in_json(gb_json* root, const char* key, std::string& val, uint32_t* type) { bool ret = true, bv = false; int nv = 0; @@ -346,24 +347,32 @@ data_type sane_cfg_provider::type_from_string(const char* type_desc) if(root->get_value(key, bv)) { val = std::string((char*)&bv, sizeof(bv)); + if(*type) + *type = DATA_TYPE_BOOL; } else if(root->get_value(key, nv)) { val = std::string((char*)&nv, sizeof(nv)); + if(*type) + *type = DATA_TYPE_INT4; } else if(root->get_value(key, fv)) { val = std::string((char*)&fv, sizeof(fv)); + if(*type) + *type = DATA_TYPE_FLOAT; } else { ret = root->get_value(key, val); + if(type) + *type = ret ? DATA_TYPE_STRING : DATA_TYPE_CUSTOM; } return ret; } -bool sane_cfg_provider::try_equal(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result) +bool sane_cfg_provider::try_equal(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result) { bool oper_not = false, calc = true, handled = false; size_t pos = exp.find("=="); @@ -381,20 +390,8 @@ bool sane_cfg_provider::try_equal(std::string& exp, std::string* name, int* type handled = true; if(pos) { - std::string types(""); - char buf[40] = {0}, *mem = nullptr; - size_t l = sizeof(buf) - 1; - *name = exp.substr(0, pos); - get_opt(name->c_str(), buf, &l, "type"); - *type = sane_cfg_provider::type_from_string(buf); - l = 0; - get_opt(name->c_str(), mem, &l, "cur"); - mem = new char[l + 4]; - l += 4; - get_opt(name->c_str(), mem, &l, "cur"); - *val = std::string(mem, l); - delete[] mem; + get_opt(name->c_str(), "cur", *val, (uint32_t*)type); } exp.erase(0, pos + 2); @@ -450,7 +447,7 @@ bool sane_cfg_provider::try_equal(std::string& exp, std::string* name, int* type return handled; } -bool sane_cfg_provider::try_great(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result) +bool sane_cfg_provider::try_great(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result) { bool oper_not = false, calc = true, handled = false; size_t pos = exp.find(">"); @@ -468,20 +465,8 @@ bool sane_cfg_provider::try_great(std::string& exp, std::string* name, int* type handled = true; if(pos) { - std::string types(""); - char buf[40] = {0}, *mem = nullptr; - size_t l = sizeof(buf) - 1; - *name = exp.substr(0, pos); - get_opt(name->c_str(), buf, &l, "type"); - *type = sane_cfg_provider::type_from_string(buf); - l = 0; - get_opt(name->c_str(), mem, &l, "cur"); - mem = new char[l + 4]; - l += 4; - get_opt(name->c_str(), mem, &l, "cur"); - *val = std::string(mem, l); - delete[] mem; + get_opt(name->c_str(), "cur", *val, (uint32_t*)type); } exp.erase(0, pos + 1 + oper_not); @@ -503,7 +488,7 @@ bool sane_cfg_provider::try_great(std::string& exp, std::string* name, int* type return handled; } -bool sane_cfg_provider::try_less(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result) +bool sane_cfg_provider::try_less(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result) { bool oper_not = false, calc = true, handled = false; size_t pos = exp.find("<"); @@ -521,20 +506,8 @@ bool sane_cfg_provider::try_less(std::string& exp, std::string* name, int* type, handled = true; if(pos) { - std::string types(""); - char buf[40] = {0}, *mem = nullptr; - size_t l = sizeof(buf) - 1; - *name = exp.substr(0, pos); - get_opt(name->c_str(), buf, &l, "type"); - *type = sane_cfg_provider::type_from_string(buf); - l = 0; - get_opt(name->c_str(), mem, &l, "cur"); - mem = new char[l + 4]; - l += 4; - get_opt(name->c_str(), mem, &l, "cur"); - *val = std::string(mem, l); - delete[] mem; + get_opt(name->c_str(), "cur", *val, (uint32_t*)type); } exp.erase(0, pos + 1 + oper_not); @@ -556,7 +529,7 @@ bool sane_cfg_provider::try_less(std::string& exp, std::string* name, int* type, return handled; } -bool sane_cfg_provider::is_depends_item_ok(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt) +bool sane_cfg_provider::is_depends_item_ok(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt) { // ==, >, < // !=, <=, >= @@ -790,32 +763,17 @@ std::string sane_cfg_mgr::get_all_configurations(void) } void sane_cfg_mgr::update_enable_status(void) { - auto get_opt = [&](const char* cfg_name, void* buf, size_t* len, const char* key) -> int32_t + auto get_opt = [&](const char* cfg_name, const char* key, std::string& val, uint32_t* type) -> int32_t { - std::string val(""); int32_t ret = 0; for(auto& v: sane_waiters_) { - ret = v->get_value(cfg_name, key, val); + ret = v->get_raw_value(cfg_name, key, val, type); if(ret != ENOENT) break; } - if(ret == 0) - { - if(*len < val.length()) - { - *len = val.length(); - ret = ENOMEM; - } - else - { - memcpy(buf, val.c_str(), val.length()); - *len = val.length(); - } - } - return ret; }; diff --git a/device/gxx-linux/usb/src/async_model/common/sane_cfg.h b/device/gxx-linux/usb/src/async_model/common/sane_cfg.h index 25d571e..3140acf 100644 --- a/device/gxx-linux/usb/src/async_model/common/sane_cfg.h +++ b/device/gxx-linux/usb/src/async_model/common/sane_cfg.h @@ -14,40 +14,27 @@ #include -//{ -// "paper": { -// "category": "base", -// "readonly": false, -// "affect": 0, -// "group": "base", -// "visible": true, -// "field": "Common", -// "ver": 1, -// "pos": 0, -// "unit": "None", -// "title": "纸张尺寸", -// "desc": "设置出图大小", -// "type": "string", -// "cur": "匹配原始尺寸", -// "default": "匹配原始尺寸", -// "size": 48, -// "range": ["A3", "8开", "A4", "A4横向", "16开", "16开横向", "A5", "A5横向", "A6", "A6横向", "B4", "B5", "B5横向", "B6", "B6横向", "Letter", "Letter横向", "Double Letter", "LEGAL", "匹配原始尺寸", "最大扫描尺寸自动裁切", "最大扫描尺寸", "三联试卷"] -// }, -//} -enum sane_after_do -{ - SANE_AFTER_DO_NOTHING = 0, - SANE_AFTER_DO_RELOAD_PARAM, - SANE_AFTER_DO_RELOAD_OPTION, -}; +// proto of getting raw value of sane-option +// +// cfg_name - option name +// +// key - the value key in JSON, e.g.: current value key is "cur" +// +// val - to receive the raw value +// +// type - to receive the value type +// +#define GET_SANE_OPT_PROTO int32_t(const char* cfg_name, const char* key, std::string& val, uint32_t* type) + + class gb_json; class sane_cfg_provider : public refer { - static bool try_equal(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result); - static bool try_great(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result); - static bool try_less(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result); - static bool is_depends_item_ok(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt); + static bool try_equal(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result); + static bool try_great(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result); + static bool try_less(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt, bool* result); + static bool is_depends_item_ok(std::string& exp, std::string* name, int* type, std::string* val, std::function get_opt); public: sane_cfg_provider(); @@ -60,9 +47,9 @@ public: static std::string sane_option_value_get(gb_json* jsn, const char* key = "cur"/*cur, default*/, std::string* strval = nullptr/*convert value into string*/); static bool sane_option_value_set(gb_json* jsn, void* data, const char* key = "cur"/*cur, default*/); static bool sane_refine_range(gb_json* jsn, void* data, size_t* len); // if 'data' is accept then return true, or else return false and new data set in 'data' and 'len' - static void update_option_enable_status(gb_json* opt, std::function get_opt); + static void update_option_enable_status(gb_json* opt, std::function get_opt); static data_type type_from_string(const char* type_desc); - static bool raw_value_in_json(gb_json* root, const char* key, std::string& val); + static bool raw_value_in_json(gb_json* root, const char* key, std::string& val, uint32_t* type = nullptr/*data_type*/); protected: int32_t inner_get_config(gb_json* root, void* buf, size_t* len, const char* cfg_name, std::string* strval); @@ -106,16 +93,8 @@ public: // // Parameters: get_opt - function to get option value, return value is the same as get_config // - // cfg_name - option name - // - // buf - buffer to receive the option value - // - // len - [in]: length of 'buf'; [out]: real size of value - // - // key - the value key in JSON, e.g.: current value key is "cur" - // // Return: none - virtual void update_enabled(std::function get_opt) = 0; + virtual void update_enabled(std::function get_opt) = 0; // Function: get value of given key // @@ -125,9 +104,11 @@ public: // // val = to receive the raw value. e.g. std::string(&bool, sizeof(bool)) // + // type - to receive the value type (data_type) + // // Return: 0 - success, // ENOENT - not found - virtual int32_t get_value(const char* name, const char* key, std::string& val) = 0; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) = 0; }; class sane_cfg_mgr : public refer diff --git a/device/gxx-linux/usb/src/async_model/hardware/hardware.cpp b/device/gxx-linux/usb/src/async_model/hardware/hardware.cpp index 8df685f..8337b44 100644 --- a/device/gxx-linux/usb/src/async_model/hardware/hardware.cpp +++ b/device/gxx-linux/usb/src/async_model/hardware/hardware.cpp @@ -1,272 +1,15 @@ #include "hardware.h" #include "common/json/gb_json.h" -//{ -// "cis-mode": { -// "category": "base", -// "readonly" : false, -// "affect" : 2, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "CIS����ģʽ", -// "desc" : "����CIS��ɫ���߻ҶȵĹ���ģʽ", -// "type" : "string", -// "cur" : "��ɫ", -// "default" : "��ɫ", -// "size" : 12, -// "range": ["��ɫ", "�Ҷ�"] -// }, -// "cis-dpi": { -// "category": "base", -// "readonly" : false, -// "affect" : 2, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "CIS�ֱ���", -// "desc" : "����CIS�ɼ��ķֱ���", -// "type" : "int", -// "cur" : 200, -// "default" : 200, -// "size" : 4, -// "range": [200, 300] -// }, -// "cis-sample": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "CIS����Ƶ��", -// "desc" : "����CIS��ͷ�����Ĺ���Ƶ��", -// "type" : "int", -// "cur" : 256, -// "default" : 256, -// "size" : 4, -// "range": [128, 256, 512] -// }, -// "frame-h": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "CIS֡�߶�", -// "desc" : "����CISÿһ֡�ĸ߶�", -// "type" : "int", -// "cur" : 12, -// "default" : 12, -// "size" : 4, -// "range": [4, 8, 12, 16] -// }, -// "gain-front": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "��������", -// "desc" : "����CIS���澵ͷ������", -// "type" : "int", -// "cur" : 256, -// "default" : 256, -// "size" : 4, -// "range": [100, 200, 300, 600] -// }, -// "gain-back": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "��������", -// "desc" : "����CIS���澵ͷ������", -// "type" : "int", -// "cur" : 256, -// "default" : 256, -// "size" : 4, -// "range": [100, 200, 300, 600] -// }, -// "offset-front": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "����ƫ��", -// "desc" : "����CIS�����ƫ�ƾ���", -// "type" : "int", -// "cur" : 150, -// "default" : 150, -// "size" : 4, -// "range": [0, 50, 100, 150, 200] -// }, -// "offset-back": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "����ƫ��", -// "desc" : "����CIS�����ƫ�ƾ���", -// "type" : "int", -// "cur" : 150, -// "default" : 150, -// "size" : 4, -// "range": [0, 50, 100, 150, 200] -// }, -// "exposure-f-r": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "�����ɫ�����ع��", -// "desc" : "���������ɫ�������ع�ǿ��", -// "type" : "int", -// "cur" : 0, -// "default" : 0, -// "size" : 4, -// "range": { -// "min": -1000, -// "max": 1000, -// "step": 200 -// } -// }, -// "exposure-f-g": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "������ɫ�����ع��", -// "desc" : "����������ɫ�������ع�ǿ��", -// "type" : "int", -// "cur" : 0, -// "default" : 0, -// "size" : 4, -// "range": { -// "min": -1000, -// "max": 1000, -// "step": 200 -// } -// }, -// "exposure-f-b": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "������ɫ�����ع��", -// "desc" : "����������ɫ�������ع�ǿ��", -// "type" : "int", -// "cur" : 0, -// "default" : 0, -// "size" : 4, -// "range": { -// "min": -1000, -// "max": 1000, -// "step": 200 -// } -// }, -// "exposure-b-r": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "�����ɫ�����ع��", -// "desc" : "�����������ɫ�������ع�ǿ��", -// "type" : "int", -// "cur" : 0, -// "default" : 0, -// "size" : 4, -// "range": { -// "min": -1000, -// "max": 1000, -// "step": 200 -// } -// }, -// "exposure-b-g": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "������ɫ�����ع��", -// "desc" : "���ñ�����ɫ�������ع�ǿ��", -// "type" : "int", -// "cur" : 0, -// "default" : 0, -// "size" : 4, -// "range": { -// "min": -1000, -// "max": 1000, -// "step": 200 -// } -// }, -// "exposure-b-b": { -// "category": "base", -// "readonly" : false, -// "affect" : 0, -// "group" : "base", -// "visible" : false, -// "field" : "cis", -// "pos" : 0, -// "unit" : "None", -// "title" : "������ɫ�����ع��", -// "desc" : "���ñ�����ɫ�������ع�ǿ��", -// "type" : "int", -// "cur" : 0, -// "default" : 0, -// "size" : 4, -// "range": { -// "min": -1000, -// "max": 1000, -// "step": 200 -// } -// }, -//} -static std::string json_text = - "{\"cis-mode\":{\"category\":\"base\",\"readonly\":false,\"affect\":2,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"CIS\\u5de5\\u4f5c\\u6a21\\u5f0f\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u5f69\\u8272\\u6216\\u8005\\u7070\\u5ea6\\u7684\\u5de5\\u4f5c\\u6a21\\u5f0f\",\"type\":\"string\",\"cur\":\"\\u5f69\\u8272\",\"default\":\"\\u5f69\\u8272\",\"size\":12,\"range\":[\"\\u5f69\\u8272\",\"\\u7070\\u5ea6\"]},\"cis-dpi\":{\"category\":\"base\",\"readonly\":false,\"affect\":2,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"CIS\\u5206\\u8fa8\\u7387\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u91c7\\u96c6\\u7684\\u5206\\u8fa8\\u7387\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":[200,300]},\"cis-sample\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"CIS\\u91c7\\u6837\\u9891\\u7387\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u955c\\u5934\\u91c7\\u6837\\u7684\\u5de5\\u4f5c\\u9891\\u7387\",\"type\":\"int\",\"cur\":256,\"default\":256,\"size\":4,\"range\":[128,256,512]},\"frame-h\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"CIS\\u5e27\\u9ad8\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u6bcf\\u4e00\\u5e27\\u7684\\u9ad8\\u5ea6\",\"type\":\"int\",\"cur\":12,\"default\":12,\"size\":4,\"range\":[4,8,12,16]},\"gain-front\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u6b63\\u9762\\u589e\\u76ca\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u6b63\\u9762\\u955c\\u5934\\u7684\\u589e\\u76ca\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":[100,200,300,600]},\"gain-back\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u80cc\\u9762\\u589e\\u76ca\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u80cc\\u9762\\u955c\\u5934\\u7684\\u589e\\u76ca\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":[100,200,300,600]},\"offset-front\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u6b63\\u9762\\u504f\\u79fb\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u6b63\\u9762\\u7684\\u504f\\u79fb\\u8ddd\\u79bb\",\"type\":\"int\",\"cur\":150,\"default\":150,\"size\":4,\"range\":[0,50,100,150,200]},\"offset-back\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u80cc\\u9762\\u504f\\u79fb\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u80cc\\u9762\\u7684\\u504f\\u79fb\\u8ddd\\u79bb\",\"type\":\"int\",\"cur\":150,\"default\":150,\"size\":4,\"range\":[0,50,100,150,200]},\"exposure-f-r\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u6b63\\u9762\\u7ea2\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u6b63\\u9762\\u7ea2\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"exposure-f-g\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u6b63\\u9762\\u7eff\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u6b63\\u9762\\u7eff\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"exposure-f-b\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u6b63\\u9762\\u84dd\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u6b63\\u9762\\u84dd\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"exposure-b-r\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u80cc\\u9762\\u7ea2\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u6b63\\u80cc\\u9762\\u7ea2\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"exposure-b-g\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u80cc\\u9762\\u7eff\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u80cc\\u9762\\u7eff\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"exposure-b-b\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u80cc\\u9762\\u84dd\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u80cc\\u9762\\u84dd\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"scan-count\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":true,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u626b\\u63cf\\u5f20\\u6570\",\"desc\":\"\\u8bbe\\u7f6e\\u626b\\u63cf\\u7684\\u7eb8\\u5f20\\u6570\\u91cf\uFF0C\\u201C-1\\u201D\\u4e3a\\u8fde\\u7eed\\u626b\\u63cf\",\"type\":\"int\",\"cur\":-1,\"default\":-1,\"size\":4,\"range\":[-1,1,2,3,4,5,6,7,8,9,10]}}"; +#include "common/paper.h" +#include "common/log_util.h" + + + + + +static std::string hardware_json = + "{\"cis-mode\":{\"category\":\"base\",\"readonly\":false,\"affect\":2,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"CIS\\u5de5\\u4f5c\\u6a21\\u5f0f\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u5f69\\u8272\\u6216\\u8005\\u7070\\u5ea6\\u7684\\u5de5\\u4f5c\\u6a21\\u5f0f\",\"type\":\"string\",\"cur\":\"\\u5f69\\u8272\",\"default\":\"\\u5f69\\u8272\",\"size\":12,\"range\":[\"\\u5f69\\u8272\",\"\\u7070\\u5ea6\"]},\"cis-dpi\":{\"category\":\"base\",\"readonly\":false,\"affect\":2,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"CIS\\u5206\\u8fa8\\u7387\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u91c7\\u96c6\\u7684\\u5206\\u8fa8\\u7387\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":[200,300]},\"cis-sample\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"CIS\\u91c7\\u6837\\u9891\\u7387\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u955c\\u5934\\u91c7\\u6837\\u7684\\u5de5\\u4f5c\\u9891\\u7387\",\"type\":\"int\",\"cur\":256,\"default\":256,\"size\":4,\"range\":[128,256,512]},\"frame-h\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"CIS\\u5e27\\u9ad8\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u6bcf\\u4e00\\u5e27\\u7684\\u9ad8\\u5ea6\",\"type\":\"int\",\"cur\":12,\"default\":12,\"size\":4,\"range\":[4,8,12,16]},\"gain-front\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u6b63\\u9762\\u589e\\u76ca\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u6b63\\u9762\\u955c\\u5934\\u7684\\u589e\\u76ca\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":[100,200,300,600]},\"gain-back\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u80cc\\u9762\\u589e\\u76ca\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u80cc\\u9762\\u955c\\u5934\\u7684\\u589e\\u76ca\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":[100,200,300,600]},\"offset-front\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u6b63\\u9762\\u504f\\u79fb\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u6b63\\u9762\\u7684\\u504f\\u79fb\\u8ddd\\u79bb\",\"type\":\"int\",\"cur\":150,\"default\":150,\"size\":4,\"range\":[0,50,100,150,200]},\"offset-back\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u80cc\\u9762\\u504f\\u79fb\",\"desc\":\"\\u8bbe\\u7f6eCIS\\u80cc\\u9762\\u7684\\u504f\\u79fb\\u8ddd\\u79bb\",\"type\":\"int\",\"cur\":150,\"default\":150,\"size\":4,\"range\":[0,50,100,150,200]},\"exposure-f-r\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u6b63\\u9762\\u7ea2\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u6b63\\u9762\\u7ea2\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"exposure-f-g\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u6b63\\u9762\\u7eff\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u6b63\\u9762\\u7eff\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"exposure-f-b\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u6b63\\u9762\\u84dd\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u6b63\\u9762\\u84dd\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"exposure-b-r\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u80cc\\u9762\\u7ea2\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u6b63\\u80cc\\u9762\\u7ea2\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"exposure-b-g\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u80cc\\u9762\\u7eff\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u80cc\\u9762\\u7eff\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"exposure-b-b\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"CIS\",\"visible\":false,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u80cc\\u9762\\u84dd\\u8272\\u5206\\u91cf\\u66dd\\u5149\\u5ea6\",\"desc\":\"\\u8bbe\\u7f6e\\u80cc\\u9762\\u84dd\\u8272\\u5206\\u91cf\\u7684\\u66dd\\u5149\\u5f3a\\u5ea6\",\"type\":\"int\",\"cur\":0,\"default\":0,\"size\":4,\"range\":{\"min\":-1000,\"max\":1000,\"step\":200}},\"paper\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"common\",\"group\":\"base\",\"pos\":0,\"affect\":6,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u7eb8\\u5f20\\u5c3a\\u5bf8\",\"desc\":\"\\u8bbe\\u7f6e\\u51fa\\u56fe\\u5927\\u5c0f\",\"type\":\"string\",\"cur\":\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"default\":\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"size\":48,\"range\":[\"A3\",\"8\\u5f00\",\"A4\",\"16\\u5f00\",\"A5\",\"A6\",\"B4\",\"B5\",\"B6\",\"Letter\",\"Double Letter\",\"LEGAL\",\"\\u5339\\u914d\\u539f\\u59cb\\u5c3a\\u5bf8\",\"\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\\u81ea\\u52a8\\u88c1\\u5207\",\"\\u6700\\u5927\\u626b\\u63cf\\u5c3a\\u5bf8\",\"\\u4e09\\u8054\\u8bd5\\u5377\"]},\"lateral\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"common\",\"group\":\"base\",\"pos\":0,\"enable\":false,\"affect\":4,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u6a2a\\u5411\",\"desc\":\"\\u6a2a\\u5411\\u653e\\u7eb8\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4,\"depend_or\":[\"paper==A4\",\"==A5\",\"==A6\",\"==B5\",\"==B6\",\"==Letter\"]},\"paper-w\":{\"category\":\"base\",\"readonly\":true,\"visible\":false,\"field\":\"common\",\"group\":\"base\",\"pos\":0,\"unit\":\"pixel\",\"ver\":1,\"title\":\"\\u7eb8\\u5f20\\u5bbd\\u5ea6\",\"desc\":\"\\u9009\\u62e9\\u7684\\u56fa\\u5b9a\\u5927\\u5c0f\\u7eb8\\u5f20\\u7684\\u5bbd\\u5ea6\",\"type\":\"int\",\"cur\":1654,\"default\":1654,\"size\":4},\"paper-h\":{\"category\":\"base\",\"readonly\":true,\"visible\":false,\"field\":\"common\",\"group\":\"base\",\"pos\":0,\"unit\":\"pixel\",\"ver\":1,\"title\":\"\\u7eb8\\u5f20\\u5bbd\\u5ea6\",\"desc\":\"\\u9009\\u62e9\\u7684\\u56fa\\u5b9a\\u5927\\u5c0f\\u7eb8\\u5f20\\u7684\\u5bbd\\u5ea6\",\"type\":\"int\",\"cur\":2339,\"default\":2339,\"size\":4},\"scan-count\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"base\",\"visible\":true,\"field\":\"cis\",\"pos\":0,\"ver\":1,\"unit\":\"None\",\"title\":\"\\u626b\\u63cf\\u5f20\\u6570\",\"desc\":\"\\u8bbe\\u7f6e\\u626b\\u63cf\\u7684\\u7eb8\\u5f20\\u6570\\u91cf\\uFF0C\\u201C-1\\u201D\\u4e3a\\u8fde\\u7eed\\u626b\\u63cf\",\"type\":\"int\",\"cur\":-1,\"default\":-1,\"size\":4,\"range\":[-1,1,2,3,4,5,6,7,8,9,10]}}"; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // class image_capture @@ -275,12 +18,14 @@ extern int32_t (*scan_start)(void); extern int32_t (*scan_stop)(void); extern int32_t (*set_dpi)(int*, int*); extern int32_t (*set_scan_num)(int); +extern int32_t (*set_paper_type)(int); +extern int32_t (*set_pixel_type)(int*); #endif image_capture::image_capture(std::function receiver) - : img_keeper_(receiver), cfg_(new gb_json()) + : img_keeper_(receiver), cfg_(new gb_json()), dpi_x_(200.0f), dpi_y_(200.0f) { - cfg_->attach_text(&json_text[0]); + cfg_->attach_text(&hardware_json[0]); memset(&api_, 0, sizeof(api_)); } image_capture::~image_capture() @@ -298,7 +43,7 @@ int32_t image_capture::set(const char* name, void* data, size_t* len) if(cfg_->get_value(name, child) && child) { - sane_cfg_provider::sane_refine_range(child, data, len); + bool exact = sane_cfg_provider::sane_refine_range(child, data, len); if (n == "cis-mode") { @@ -398,6 +143,19 @@ int32_t image_capture::set(const char* name, void* data, size_t* len) { set_scan_num(*(int*)data); child->set_value("cur", *(int*)data); + ret = exact ? 0 : EUCLEAN; + } + else if(n == "paper") + { + child->set_value("cur", (char*)data); + refresh_paper_size(true); + ret = exact ? 0 : EUCLEAN; + } + else if(n == "lateral") + { + child->set_value("cur", *(bool*)data); + refresh_paper_size(); + ret = 0; } child->release(); } @@ -415,6 +173,55 @@ int32_t image_capture::set(const char* name, void* data, size_t* len) return ret; } +void image_capture::refresh_paper_size(bool from_paper_type) +{ + gb_json* child = nullptr; + int w = 0, + h = 0, + type = 0; + double width = .0f, + height = .0f; + std::string pp(""); + + if(cfg_->get_value("paper", child) && child) + { + child->get_value("cur", pp); + child->release(); + } + + paper::paper_size(pp.c_str(), &width, &height, &type); + if(from_paper_type) + set_paper_type(type); + if(cfg_->get_value("lateral", child) && child) + { + bool enable = false; + if(child->get_value("enable", enable) && enable) + { + if(child->get_value("cur", enable) && enable) + { + double v = width; + width = height; + height = v; + pp += " Lateral"; + } + } + child->release(); + } + w = paper::cm_2_pixel(width, dpi_x_); + h = paper::cm_2_pixel(height, dpi_y_); + log_cls::log(LOG_LEVEL_ALL, "'%s' in DPI(%f, %f) size is: (%u, %u)\n", pp.c_str(), dpi_x_, dpi_y_, w, h); + + if(cfg_->get_value("paper-w", child) && child) + { + child->set_value("cur", w); + child->release(); + } + if(cfg_->get_value("paper-h", child) && child) + { + child->set_value("cur", h); + child->release(); + } +} int32_t image_capture::get_config(void* buf, size_t* len, const char* cfg_name, std::string* strval) { @@ -434,8 +241,9 @@ int32_t image_capture::set_config(const char* cfg_name, void* data, size_t* len, sane_option_value_set(child, data); if (afterdo) { - child->get_value("affect", ret); - *afterdo = ret; + int v = 0; + child->get_value("affect", v); + *afterdo = v; } if(!exact) ret = EUCLEAN; @@ -445,19 +253,27 @@ int32_t image_capture::set_config(const char* cfg_name, void* data, size_t* len, return ret; } -void image_capture::update_enabled(std::function get_opt) +void image_capture::update_enabled(std::function get_opt) { - sane_cfg_provider::update_option_enable_status(cfg_, get_opt); + gb_json* child = cfg_->first_child(); + + while(child) + { + sane_cfg_provider::update_option_enable_status(child, get_opt); + child->release(); + child = cfg_->next_child(); + } } -int32_t image_capture::get_value(const char* name, const char* key, std::string& val) +int32_t image_capture::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) { gb_json* child = nullptr; int32_t ret = ENOENT; if(cfg_->get_value(name, child) && child) { - if(sane_cfg_provider::raw_value_in_json(child, key, val)) + if(sane_cfg_provider::raw_value_in_json(child, key, val, type)) ret = 0; + child->release(); } return ret; @@ -481,5 +297,20 @@ int32_t image_capture::stop(void) } int image_capture::set_dpi(int* dpi_x, int* dpi_y) { + if(dpi_x) + dpi_x_ = *dpi_x; + if(dpi_y) + dpi_y_ = *dpi_y; + refresh_paper_size(); + return ::set_dpi(dpi_x, dpi_y); +} +int image_capture::set_color_mode(color_mode* mode) +{ + int val = *mode, + ret = 0; + + ret = set_pixel_type(&val); + + return ret; } \ No newline at end of file diff --git a/device/gxx-linux/usb/src/async_model/hardware/hardware.h b/device/gxx-linux/usb/src/async_model/hardware/hardware.h index 4ee6361..a044dbb 100644 --- a/device/gxx-linux/usb/src/async_model/hardware/hardware.h +++ b/device/gxx-linux/usb/src/async_model/hardware/hardware.h @@ -33,13 +33,383 @@ typedef struct _cis_api CIS_API set_exposure_back_blue; }CISAPI, *LPCISAPI; + +// { +// "cis-mode": { +// "category": "base", +// "readonly": false, +// "affect": 2, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "CIS\u5de5\u4f5c\u6a21\u5f0f", +// "desc": "\u8bbe\u7f6eCIS\u5f69\u8272\u6216\u8005\u7070\u5ea6\u7684\u5de5\u4f5c\u6a21\u5f0f", +// "type": "string", +// "cur": "\u5f69\u8272", +// "default": "\u5f69\u8272", +// "size": 12, +// "range": ["\u5f69\u8272", "\u7070\u5ea6"] +// }, +// "cis-dpi": { +// "category": "base", +// "readonly": false, +// "affect": 2, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "CIS\u5206\u8fa8\u7387", +// "desc": "\u8bbe\u7f6eCIS\u91c7\u96c6\u7684\u5206\u8fa8\u7387", +// "type": "int", +// "cur": 200, +// "default": 200, +// "size": 4, +// "range": [200, 300] +// }, +// "cis-sample": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "CIS\u91c7\u6837\u9891\u7387", +// "desc": "\u8bbe\u7f6eCIS\u955c\u5934\u91c7\u6837\u7684\u5de5\u4f5c\u9891\u7387", +// "type": "int", +// "cur": 256, +// "default": 256, +// "size": 4, +// "range": [128, 256, 512] +// }, +// "frame-h": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "CIS\u5e27\u9ad8\u5ea6", +// "desc": "\u8bbe\u7f6eCIS\u6bcf\u4e00\u5e27\u7684\u9ad8\u5ea6", +// "type": "int", +// "cur": 12, +// "default": 12, +// "size": 4, +// "range": [4, 8, 12, 16] +// }, +// "gain-front": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u6b63\u9762\u589e\u76ca", +// "desc": "\u8bbe\u7f6eCIS\u6b63\u9762\u955c\u5934\u7684\u589e\u76ca", +// "type": "int", +// "cur": 200, +// "default": 200, +// "size": 4, +// "range": [100, 200, 300, 600] +// }, +// "gain-back": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u80cc\u9762\u589e\u76ca", +// "desc": "\u8bbe\u7f6eCIS\u80cc\u9762\u955c\u5934\u7684\u589e\u76ca", +// "type": "int", +// "cur": 200, +// "default": 200, +// "size": 4, +// "range": [100, 200, 300, 600] +// }, +// "offset-front": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u6b63\u9762\u504f\u79fb", +// "desc": "\u8bbe\u7f6eCIS\u6b63\u9762\u7684\u504f\u79fb\u8ddd\u79bb", +// "type": "int", +// "cur": 150, +// "default": 150, +// "size": 4, +// "range": [0, 50, 100, 150, 200] +// }, +// "offset-back": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u80cc\u9762\u504f\u79fb", +// "desc": "\u8bbe\u7f6eCIS\u80cc\u9762\u7684\u504f\u79fb\u8ddd\u79bb", +// "type": "int", +// "cur": 150, +// "default": 150, +// "size": 4, +// "range": [0, 50, 100, 150, 200] +// }, +// "exposure-f-r": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u6b63\u9762\u7ea2\u8272\u5206\u91cf\u66dd\u5149\u5ea6", +// "desc": "\u8bbe\u7f6e\u6b63\u9762\u7ea2\u8272\u5206\u91cf\u7684\u66dd\u5149\u5f3a\u5ea6", +// "type": "int", +// "cur": 0, +// "default": 0, +// "size": 4, +// "range": { +// "min": -1000, +// "max": 1000, +// "step": 200 +// } +// }, +// "exposure-f-g": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u6b63\u9762\u7eff\u8272\u5206\u91cf\u66dd\u5149\u5ea6", +// "desc": "\u8bbe\u7f6e\u6b63\u9762\u7eff\u8272\u5206\u91cf\u7684\u66dd\u5149\u5f3a\u5ea6", +// "type": "int", +// "cur": 0, +// "default": 0, +// "size": 4, +// "range": { +// "min": -1000, +// "max": 1000, +// "step": 200 +// } +// }, +// "exposure-f-b": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u6b63\u9762\u84dd\u8272\u5206\u91cf\u66dd\u5149\u5ea6", +// "desc": "\u8bbe\u7f6e\u6b63\u9762\u84dd\u8272\u5206\u91cf\u7684\u66dd\u5149\u5f3a\u5ea6", +// "type": "int", +// "cur": 0, +// "default": 0, +// "size": 4, +// "range": { +// "min": -1000, +// "max": 1000, +// "step": 200 +// } +// }, +// "exposure-b-r": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u80cc\u9762\u7ea2\u8272\u5206\u91cf\u66dd\u5149\u5ea6", +// "desc": "\u8bbe\u7f6e\u6b63\u80cc\u9762\u7ea2\u8272\u5206\u91cf\u7684\u66dd\u5149\u5f3a\u5ea6", +// "type": "int", +// "cur": 0, +// "default": 0, +// "size": 4, +// "range": { +// "min": -1000, +// "max": 1000, +// "step": 200 +// } +// }, +// "exposure-b-g": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u80cc\u9762\u7eff\u8272\u5206\u91cf\u66dd\u5149\u5ea6", +// "desc": "\u8bbe\u7f6e\u80cc\u9762\u7eff\u8272\u5206\u91cf\u7684\u66dd\u5149\u5f3a\u5ea6", +// "type": "int", +// "cur": 0, +// "default": 0, +// "size": 4, +// "range": { +// "min": -1000, +// "max": 1000, +// "step": 200 +// } +// }, +// "exposure-b-b": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": false, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u80cc\u9762\u84dd\u8272\u5206\u91cf\u66dd\u5149\u5ea6", +// "desc": "\u8bbe\u7f6e\u80cc\u9762\u84dd\u8272\u5206\u91cf\u7684\u66dd\u5149\u5f3a\u5ea6", +// "type": "int", +// "cur": 0, +// "default": 0, +// "size": 4, +// "range": { +// "min": -1000, +// "max": 1000, +// "step": 200 +// } +// }, +// "paper": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "field": "common", +// "group": "base", +// "pos": 0, +// "affect": 6, +// "unit": "none", +// "ver": 1, +// "title": "\u7eb8\u5f20\u5c3a\u5bf8", +// "desc": "\u8bbe\u7f6e\u51fa\u56fe\u5927\u5c0f", +// "type": "string", +// "cur": "\u5339\u914d\u539f\u59cb\u5c3a\u5bf8", +// "default": "\u5339\u914d\u539f\u59cb\u5c3a\u5bf8", +// "size": 48, +// "range": ["A3", "8\u5f00", "A4", "16\u5f00", "A5", "A6", "B4", "B5", "B6", "Letter", "Double Letter", "LEGAL", "\u5339\u914d\u539f\u59cb\u5c3a\u5bf8", "\u6700\u5927\u626b\u63cf\u5c3a\u5bf8\u81ea\u52a8\u88c1\u5207", "\u6700\u5927\u626b\u63cf\u5c3a\u5bf8", "\u4e09\u8054\u8bd5\u5377"] +// }, +// "lateral": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "field": "common", +// "group": "base", +// "pos": 0, +// "affect": 4, +// "unit": "none", +// "ver": 1, +// "title": "\u6a2a\u5411", +// "desc": "\u6a2a\u5411\u653e\u7eb8", +// "type": "bool", +// "cur": false, +// "default": false, +// "size": 4, +// "depend_or": ["paper==A4", "==A5", "==A6", "==B5", "==B6", "==Letter"] +// }, +// "paper-w": { +// "category": "base", +// "readonly": true, +// "visible": false, +// "field": "common", +// "group": "base", +// "pos": 0, +// "unit": "pixel", +// "ver": 1, +// "title": "\u7eb8\u5f20\u5bbd\u5ea6", +// "desc": "\u9009\u62e9\u7684\u56fa\u5b9a\u5927\u5c0f\u7eb8\u5f20\u7684\u5bbd\u5ea6", +// "type": "int", +// "cur": 1654, +// "default": 1654, +// "size": 4 +// }, +// "paper-h": { +// "category": "base", +// "readonly": true, +// "visible": false, +// "field": "common", +// "group": "base", +// "pos": 0, +// "unit": "pixel", +// "ver": 1, +// "title": "\u7eb8\u5f20\u5bbd\u5ea6", +// "desc": "\u9009\u62e9\u7684\u56fa\u5b9a\u5927\u5c0f\u7eb8\u5f20\u7684\u5bbd\u5ea6", +// "type": "int", +// "cur": 2339, +// "default": 2339, +// "size": 4 +// }, +// "scan-count": { +// "category": "base", +// "readonly": false, +// "affect": 0, +// "group": "CIS", +// "visible": true, +// "field": "cis", +// "pos": 0, +// "ver": 1, +// "unit": "None", +// "title": "\u626b\u63cf\u5f20\u6570", +// "desc": "\u8bbe\u7f6e\u626b\u63cf\u7684\u7eb8\u5f20\u6570\u91cf\uFF0C\u201C-1\u201D\u4e3a\u8fde\u7eed\u626b\u63cf", +// "type": "int", +// "cur": -1, +// "default": -1, +// "size": 4, +// "range": [-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +// } +// } + + class image_capture : public sane_cfg_provider { gb_json* cfg_; CISAPI api_; + double dpi_x_; + double dpi_y_; std::function img_keeper_; int32_t set(const char* name, void* data, size_t* len); + void refresh_paper_size(bool from_paper_type = false); public: image_capture(std::function receiver); @@ -51,8 +421,8 @@ protected: public: virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; - virtual void update_enabled(std::function get_opt) override; - virtual int32_t get_value(const char* name, const char* key, std::string& val) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; public: void set_api(LPCISAPI api); @@ -69,5 +439,12 @@ public: // // Return: always 0. if input value is not accessible, the exact value is stored into the var int set_dpi(int* dpi_x, int* dpi_y); + + // Function: set resolution of hardware/CIS + // + // Parameters: mode - [in]color to be set; [out]applied value + // + // Return: always 0. if input value is not accessible, the exact value is stored into the var + int set_color_mode(color_mode* mode); }; diff --git a/device/gxx-linux/usb/src/async_model/img_process/algs/auto_crop.cpp b/device/gxx-linux/usb/src/async_model/img_process/algs/auto_crop.cpp new file mode 100644 index 0000000..7e785ad --- /dev/null +++ b/device/gxx-linux/usb/src/async_model/img_process/algs/auto_crop.cpp @@ -0,0 +1,185 @@ +#include "auto_crop.h" + + +#include "common/json/gb_json.h" +#include "imageprocess/ImageApplyAutoCrop.h" + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// + + + +static std::string auto_crop_jsn = + ""; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// class img_algs +auto_crop::auto_crop() : root_(new gb_json()) + , name_("is-discard-blank"), pos_(0), ver_(0), enable_(false) + , threshold_(40), indent_(30), stroke_(50), bg_clr_(200), noise_(11), universal_(true) +{ + root_->attach_text(&auto_crop_jsn[0]); + init_value_from_json(); +} +auto_crop::~auto_crop() +{ + root_->release(); +} + +void auto_crop::init_value_from_json(void) +{ + gb_json* child = nullptr; + + if(root_->get_value(name_.c_str(), child) && child) + { + int val = 0; + child->get_value("cur", enable_); + child->get_value("ver", val); + ver_ = val; + child->get_value("pos", val); + pos_ = val; + + child->release(); + } + if(root_->get_value("discard-blank", child) && child) + { + std::string val(""); + child->get_value("cur", val); + universal_ = val != "\xE5\x8F\x91\xE7\xA5\xA8\xE7\xBA\xB8"; + child->release(); + } + if(root_->get_value("blank-sensitivity", child) && child) + { + child->get_value("cur", stroke_); + child->release(); + } + + if(root_->get_value("blank-threshold", child) && child) + { + child->get_value("cur", threshold_); + child->release(); + } + if(root_->get_value("blank-indent", child) && child) + { + child->get_value("cur", indent_); + child->release(); + } + if(root_->get_value("blank-bg-clr", child) && child) + { + child->get_value("cur", bg_clr_); + child->release(); + } + if(root_->get_value("blank-noise", child) && child) + { + child->get_value("cur", noise_); + child->release(); + } +} + +int32_t auto_crop::get_config(void* buf, size_t* len, const char* cfg_name, std::string* strval) +{ + if(cfg_name) + { + return inner_get_config(root_, buf, len, cfg_name, strval); + } + else + { + if(!len) + return EINVAL; + + std::string val(root_->to_string()); + + if(*len < val.length()) + { + *len = val.length() + 4; + + return ENOMEM; + } + + strcpy((char*)buf, val.c_str()); + *len = val.length(); + } + + return 0; +} +int32_t auto_crop::set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) +{ + gb_json* child = nullptr; + int32_t ret = ENOENT; + + if(root_->get_value(cfg_name, child) && child) + { + ret = sane_cfg_provider::sane_refine_range(child, data, len) ? 0 : EUCLEAN; + sane_cfg_provider::sane_option_value_set(child, data); + init_value_from_json(); + if(strcmp(cfg_name, "discard-blank") == 0) + { + noise_ = universal_ ? 200 : 150; + } + + if(afterdo) + { + int val = 0; + child->get_value("affect", val); + *afterdo = val; + } + + child->release(); + } + + return ret; +} +void auto_crop::update_enabled(std::function get_opt) +{ + gb_json* child = root_->first_child(); + while(child) + { + sane_cfg_provider::update_option_enable_status(child, get_opt); + child->release(); + child = root_->next_child(); + } +} +int32_t auto_crop::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) +{ + int32_t ret = ENOENT; + gb_json* child = nullptr; + + if(root_->get_value(name, child) && child) + { + ret = sane_cfg_provider::raw_value_in_json(child, key, val, type) ? 0 : ENOENT; + child->release(); + } + + return ret; +} + +img_one_paper* auto_crop::execute(img_one_paper* img) +{ + for(auto& v : img->images_queue()) + { + // CImageApplyAutoCrop::autoCrop_desaskew_fillBlank(v.img, threshold_, indent_, stroke_, bg_clr_, noise_); + } + img->add_ref(); + + return img; +} +bool auto_crop::is_enabled(std::function get_opt_value) +{ + return enable_; +} + +uint32_t auto_crop::position(void) +{ + return pos_; +} +uint32_t auto_crop::version(void) +{ + return ver_; +} +const char* auto_crop::option_name(void) +{ + return name_.c_str(); +} + + diff --git a/device/gxx-linux/usb/src/async_model/img_process/algs/auto_crop.h b/device/gxx-linux/usb/src/async_model/img_process/algs/auto_crop.h new file mode 100644 index 0000000..bbaea51 --- /dev/null +++ b/device/gxx-linux/usb/src/async_model/img_process/algs/auto_crop.h @@ -0,0 +1,53 @@ +#pragma once + +// auto_crop algorithm +// +// created on 2023-04-23 +// + +#include "img_algorithm.h" + + + + + + +class gb_json; + +class auto_crop : public img_alg +{ + gb_json* root_; + + std::string name_; + uint32_t pos_; + uint32_t ver_; + bool enable_; + + int threshold_; + int indent_; + int stroke_; // sensitivity + int bg_clr_; + int noise_; + bool universal_; + + void init_value_from_json(void); + +public: + auto_crop(void); +protected: + ~auto_crop(); + +public: + virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; + virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; + +public: + img_one_paper* execute(img_one_paper* img) override; + bool is_enabled(std::function get_opt_value) override; + + uint32_t position(void) override; + uint32_t version(void) override; + const char* option_name(void) override; +}; diff --git a/device/gxx-linux/usb/src/async_model/img_process/algs/discard_blank.cpp b/device/gxx-linux/usb/src/async_model/img_process/algs/discard_blank.cpp new file mode 100644 index 0000000..e0b2718 --- /dev/null +++ b/device/gxx-linux/usb/src/async_model/img_process/algs/discard_blank.cpp @@ -0,0 +1,191 @@ +#include "discard_blank.h" + + +#include "common/json/gb_json.h" +#include "imageprocess/ImageApplyDiscardBlank.h" + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// + + + +static std::string discard_blank_jsn = + "{\"is-discard-blank\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"imgproc\",\"group\":\"imgproc\",\"affect\":2,\"pos\":200,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\",\"desc\":\"\\u5982\\u679c\\u68c0\\u6d4b\\u5230\\u56fe\\u50cf\\u4e3a\\u7a7a\\u767d\\uFF0C\\u5219\\u4e22\\u5f03\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"discard-blank\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":201,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\u7c7b\\u578b\",\"desc\":\"\\u901a\\u7528\\u548c\\u53d1\\u7968\\uFF0C\\u5176\\u4e2d\\u4e4b\\u4e00\",\"type\":\"string\",\"cur\":\"\\u901a\\u7528\",\"default\":\"\\u901a\\u7528\",\"size\":40,\"range\":[\"\\u901a\\u7528\",\"\\u53d1\\u7968\\u7eb8\"],\"depend_or\":[\"is-discard-blank==true\"]},\"blank-sensitivity\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":202,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u8df3\\u8fc7\\u7a7a\\u767d\\u9875\\u7075\\u654f\\u5ea6\",\"desc\":\"\\u6570\\u503c\\u8d8a\\u5927\\uFF0C\\u5219\\u8d8a\\u5bb9\\u6613\\u8df3\\u8fc7\",\"type\":\"int\",\"cur\":50,\"default\":50,\"size\":4,\"range\":{\"min\":1,\"max\":100,\"step\":1},\"depend_or\":[\"is-discard-blank==true\"]},\"blank-threshold\":{\"category\":\"base\",\"readonly\":false,\"visible\":false,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":210,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u8f6e\\u5ed3\\u9608\\u503c\",\"desc\":\"\\u8f6e\\u5ed3\\u9608\\u503c\",\"type\":\"int\",\"cur\":40,\"default\":40,\"size\":4,\"range\":{\"min\":1,\"max\":100,\"step\":1},\"depend_or\":[\"is-discard-blank==true\"]},\"blank-indent\":{\"category\":\"base\",\"readonly\":false,\"visible\":false,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":211,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u7a7a\\u767d\\u9875\\u8fb9\\u7f18\\u7f29\\u8fdb\",\"desc\":\"\\u7a7a\\u767d\\u9875\\u8fb9\\u7f18\\u7f29\\u8fdb\",\"type\":\"int\",\"cur\":30,\"default\":30,\"size\":4,\"range\":{\"min\":1,\"max\":100,\"step\":1},\"depend_or\":[\"is-discard-blank==true\"]},\"blank-bg-clr\":{\"category\":\"base\",\"readonly\":false,\"visible\":false,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":212,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u80cc\\u666f\\u8272\\u9608\\u503c\",\"desc\":\"\\u4f4e\\u4e8e\\u8be5\\u503c\\uFF0C\\u88ab\\u89c6\\u4f5c\\u80cc\\u666f\\u989c\\u8272\",\"type\":\"int\",\"cur\":200,\"default\":200,\"size\":4,\"range\":{\"min\":1,\"max\":255,\"step\":1},\"depend_or\":[\"is-discard-blank==true\"]},\"blank-noise\":{\"category\":\"base\",\"readonly\":false,\"visible\":false,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":213,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u7eb8\\u5f20\\u6742\\u70b9\",\"desc\":\"\\u5ffd\\u7565\\u7eb8\\u5f20\\u6742\\u70b9\\u3002\\u22641\\u65f6\\u4e0d\\u751f\\u6548\\uFF0C\\u503c\\u8d8a\\u5927\\u8d8a\\u5bb9\\u6613\\u5ffd\\u7565\\u6742\\u70b9\",\"type\":\"int\",\"cur\":11,\"default\":11,\"size\":4,\"range\":{\"min\":1,\"max\":100,\"step\":1},\"depend_or\":[\"is-discard-blank==true\"]}}"; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// class img_algs +discard_blank::discard_blank() : root_(new gb_json()) + , name_("is-discard-blank"), pos_(0), ver_(0), enable_(false) + , threshold_(40), indent_(30), stroke_(50), bg_clr_(200), noise_(11), universal_(true) +{ + root_->attach_text(&discard_blank_jsn[0]); + init_value_from_json(); +} +discard_blank::~discard_blank() +{ + root_->release(); +} + +void discard_blank::init_value_from_json(void) +{ + gb_json* child = nullptr; + + if(root_->get_value(name_.c_str(), child) && child) + { + int val = 0; + child->get_value("cur", enable_); + child->get_value("ver", val); + ver_ = val; + child->get_value("pos", val); + pos_ = val; + + child->release(); + } + if(root_->get_value("discard-blank", child) && child) + { + std::string val(""); + child->get_value("cur", val); + universal_ = val != "\xE5\x8F\x91\xE7\xA5\xA8\xE7\xBA\xB8"; + child->release(); + } + if(root_->get_value("blank-sensitivity", child) && child) + { + child->get_value("cur", stroke_); + child->release(); + } + + if(root_->get_value("blank-threshold", child) && child) + { + child->get_value("cur", threshold_); + child->release(); + } + if(root_->get_value("blank-indent", child) && child) + { + child->get_value("cur", indent_); + child->release(); + } + if(root_->get_value("blank-bg-clr", child) && child) + { + child->get_value("cur", bg_clr_); + child->release(); + } + if(root_->get_value("blank-noise", child) && child) + { + child->get_value("cur", noise_); + child->release(); + } +} + +int32_t discard_blank::get_config(void* buf, size_t* len, const char* cfg_name, std::string* strval) +{ + if(cfg_name) + { + return inner_get_config(root_, buf, len, cfg_name, strval); + } + else + { + if(!len) + return EINVAL; + + std::string val(root_->to_string()); + + if(*len < val.length()) + { + *len = val.length() + 4; + + return ENOMEM; + } + + strcpy((char*)buf, val.c_str()); + *len = val.length(); + } + + return 0; +} +int32_t discard_blank::set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) +{ + gb_json* child = nullptr; + int32_t ret = ENOENT; + + if(root_->get_value(cfg_name, child) && child) + { + ret = sane_cfg_provider::sane_refine_range(child, data, len) ? 0 : EUCLEAN; + sane_cfg_provider::sane_option_value_set(child, data); + init_value_from_json(); + if(strcmp(cfg_name, "discard-blank") == 0) + { + noise_ = universal_ ? 200 : 150; + } + + if(afterdo) + { + int val = 0; + child->get_value("affect", val); + *afterdo = val; + } + + child->release(); + } + + return ret; +} +void discard_blank::update_enabled(std::function get_opt) +{ + gb_json* child = root_->first_child(); + while(child) + { + sane_cfg_provider::update_option_enable_status(child, get_opt); + child->release(); + child = root_->next_child(); + } +} +int32_t discard_blank::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) +{ + int32_t ret = ENOENT; + gb_json* child = nullptr; + + if(root_->get_value(name, child) && child) + { + ret = sane_cfg_provider::raw_value_in_json(child, key, val, type) ? 0 : ENOENT; + child->release(); + } + + return ret; +} + +img_one_paper* discard_blank::execute(img_one_paper* img) +{ + int val = universal_ ? stroke_ : stroke_ * 1.5f + .5f; + + if(enable_) + { + for(auto& v : img->images_queue()) + { + if(CImageApplyDiscardBlank::apply(v.img, threshold_, indent_, val, bg_clr_, noise_)) + v.head.pos.status |= IMG_STATUS_BLANK; + } + } + img->add_ref(); + + return img; +} +bool discard_blank::is_enabled(std::function get_opt_value) +{ + return enable_; +} + +uint32_t discard_blank::position(void) +{ + return pos_; +} +uint32_t discard_blank::version(void) +{ + return ver_; +} +const char* discard_blank::option_name(void) +{ + return name_.c_str(); +} + + diff --git a/device/gxx-linux/usb/src/async_model/img_process/algs/discard_blank.h b/device/gxx-linux/usb/src/async_model/img_process/algs/discard_blank.h new file mode 100644 index 0000000..5200a35 --- /dev/null +++ b/device/gxx-linux/usb/src/async_model/img_process/algs/discard_blank.h @@ -0,0 +1,199 @@ +#pragma once + +// discard_blank algorithm +// +// created on 2023-04-23 +// + +#include "img_algorithm.h" + + + +// { +// "is-discard-blank": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "field": "imgproc", +// "group": "imgproc", +// "affect": 2, +// "pos": 200, +// "unit": "none", +// "ver": 1, +// "title": "跳过空白页", +// "desc": "如果检测到图像为空白,则丢弃", +// "type": "bool", +// "cur": false, +// "default": false, +// "size": 4 +// }, +// "discard-blank": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 201, +// "unit": "none", +// "ver": 1, +// "title": "跳过空白页类型", +// "desc": "通用和发票,其中之一", +// "type": "string", +// "cur": "通用", +// "default": "通用", +// "size": 40, +// "range": ["通用", "发票纸"], +// "depend_or": ["is-discard-blank==true"] +// }, +// "blank-sensitivity": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 202, +// "unit": "none", +// "ver": 1, +// "title": "跳过空白页灵敏度", +// "desc": "数值越大,则越容易跳过", +// "type": "int", +// "cur": 50, +// "default": 50, +// "size": 4, +// "range": { +// "min": 1, +// "max": 100, +// "step": 1 +// }, +// "depend_or": ["is-discard-blank==true"] +// }, +// "blank-threshold": { +// "category": "base", +// "readonly": false, +// "visible": false, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 210, +// "unit": "none", +// "ver": 1, +// "title": "轮廓阈值", +// "desc": "轮廓阈值", +// "type": "int", +// "cur": 40, +// "default": 40, +// "size": 4, +// "range": { +// "min": 1, +// "max": 100, +// "step": 1 +// }, +// "depend_or": ["is-discard-blank==true"] +// }, +// "blank-indent": { +// "category": "base", +// "readonly": false, +// "visible": false, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 211, +// "unit": "none", +// "ver": 1, +// "title": "空白页边缘缩进", +// "desc": "空白页边缘缩进", +// "type": "int", +// "cur": 30, +// "default": 30, +// "size": 4, +// "range": { +// "min": 1, +// "max": 100, +// "step": 1 +// }, +// "depend_or": ["is-discard-blank==true"] +// }, +// "blank-bg-clr": { +// "category": "base", +// "readonly": false, +// "visible": false, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 212, +// "unit": "none", +// "ver": 1, +// "title": "背景色阈值", +// "desc": "低于该值,被视作背景颜色", +// "type": "int", +// "cur": 200, +// "default": 200, +// "size": 4, +// "range": { +// "min": 1, +// "max": 255, +// "step": 1 +// }, +// "depend_or": ["is-discard-blank==true"] +// }, +// "blank-noise": { +// "category": "base", +// "readonly": false, +// "visible": false, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 213, +// "unit": "none", +// "ver": 1, +// "title": "纸张杂点", +// "desc": "忽略纸张杂点。≤1时不生效,值越大越容易忽略杂点", +// "type": "int", +// "cur": 11, +// "default": 11, +// "size": 4, +// "range": { +// "min": 1, +// "max": 100, +// "step": 1 +// }, +// "depend_or": ["is-discard-blank==true"] +// } +// } + + +class gb_json; + +class discard_blank : public img_alg +{ + gb_json* root_; + + std::string name_; + uint32_t pos_; + uint32_t ver_; + bool enable_; + + int threshold_; + int indent_; + int stroke_; // sensitivity + int bg_clr_; + int noise_; + bool universal_; + + void init_value_from_json(void); + +public: + discard_blank(void); +protected: + ~discard_blank(); + +public: + virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; + virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; + +public: + img_one_paper* execute(img_one_paper* img) override; + bool is_enabled(std::function get_opt_value) override; + + uint32_t position(void) override; + uint32_t version(void) override; + const char* option_name(void) override; +}; diff --git a/device/gxx-linux/usb/src/async_model/img_process/algs/dogear.cpp b/device/gxx-linux/usb/src/async_model/img_process/algs/dogear.cpp index 3ce2341..65fb172 100644 --- a/device/gxx-linux/usb/src/async_model/img_process/algs/dogear.cpp +++ b/device/gxx-linux/usb/src/async_model/img_process/algs/dogear.cpp @@ -140,7 +140,7 @@ int32_t dogear::set_config(const char* cfg_name, void* data, size_t* len, uint32 return ret; } -void dogear::update_enabled(std::function get_opt) +void dogear::update_enabled(std::function get_opt) { gb_json* child = root_->first_child(); while(child) @@ -150,14 +150,14 @@ void dogear::update_enabled(std::functionnext_child(); } } -int32_t dogear::get_value(const char* name, const char* key, std::string& val) +int32_t dogear::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) { int32_t ret = ENOENT; gb_json* child = nullptr; if(root_->get_value(name, child) && child) { - ret = sane_cfg_provider::raw_value_in_json(child, key, val) ? 0 : ENOENT; + ret = sane_cfg_provider::raw_value_in_json(child, key, val, type) ? 0 : ENOENT; child->release(); } diff --git a/device/gxx-linux/usb/src/async_model/img_process/algs/dogear.h b/device/gxx-linux/usb/src/async_model/img_process/algs/dogear.h index 0d5692e..968655f 100644 --- a/device/gxx-linux/usb/src/async_model/img_process/algs/dogear.h +++ b/device/gxx-linux/usb/src/async_model/img_process/algs/dogear.h @@ -176,8 +176,8 @@ protected: public: virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; - virtual void update_enabled(std::function get_opt) override; - virtual int32_t get_value(const char* name, const char* key, std::string& val) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; public: img_one_paper* execute(img_one_paper* img) override; diff --git a/device/gxx-linux/usb/src/async_model/img_process/algs/img_algorithm.cpp b/device/gxx-linux/usb/src/async_model/img_process/algs/img_algorithm.cpp index df10937..0f9e594 100644 --- a/device/gxx-linux/usb/src/async_model/img_process/algs/img_algorithm.cpp +++ b/device/gxx-linux/usb/src/async_model/img_process/algs/img_algorithm.cpp @@ -1,12 +1,16 @@ #include "img_algorithm.h" - -#include "dogear.h" #include "common/json/gb_json.h" #include "../capimage/hgutils.h" +#include "dogear.h" +#include "remove_hole.h" +#include "discard_blank.h" +#include "auto_crop.h" + #ifdef TEMPORARY_API extern int32_t (*set_dpi)(int*, int*); +extern int32_t (*set_pixel_type)(int*); #endif ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -95,6 +99,15 @@ img_alg* img_alg::create_image_algorithm(img_alg_type type) return dynamic_cast(new img_resizer()); if(type == IMG_ALG_ADJUST_COLOR) return dynamic_cast(new color_correct()); + if(type == IMG_ALG_REMOVE_HOLE) + return dynamic_cast(new rm_hole()); + if(type == IMG_ALG_DISCARD_BLANK) + return dynamic_cast(new discard_blank()); + if(type == IMG_ALG_COLOR_TRANSFER) + return dynamic_cast(new img_color_transfer()); + // if(type == IMG_ALG_AUTO_CROP) + // return dynamic_cast(new auto_crop()); + return nullptr; } @@ -117,7 +130,7 @@ const char* img_alg::option_name(void) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// class img_resizer +// class color_correct // { // "color-correct": { // "category": "base", @@ -125,7 +138,7 @@ const char* img_alg::option_name(void) // "visible": true, // "field": "imgproc", // "group": "imgproc", -// "pos": 100, +// "pos": 400, // "unit": "none", // "ver": 1, // "title": "颜色校正", @@ -138,7 +151,7 @@ const char* img_alg::option_name(void) // } // } static std::string color_correct_json = - "{\"color-correct\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":100,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u989c\\u8272\\u6821\\u6b63\",\"desc\":\"\\u6821\\u6b63\\u7531\\u4e8e\\u786c\\u4ef6\\u7279\\u6027\\u4ea7\\u751f\\u7684\\u8272\\u504f\\uFF0C\\u4f7f\\u56fe\\u7247\\u989c\\u8272\\u66f4\\u63a5\\u8fd1\\u771f\\u5b9e\",\"type\":\"string\",\"cur\":\"\\u666e\\u901a\\u6a21\\u5f0f\",\"default\":\"\\u666e\\u901a\\u6a21\\u5f0f\",\"size\":20,\"range\":[\"\\u4e0d\\u6821\\u6b63\",\"\\u666e\\u901a\\u6a21\\u5f0f\",\"\\u589e\\u5f3a\\u6a21\\u5f0f\"]}}"; + "{\"color-correct\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":400,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u989c\\u8272\\u6821\\u6b63\",\"desc\":\"\\u6821\\u6b63\\u7531\\u4e8e\\u786c\\u4ef6\\u7279\\u6027\\u4ea7\\u751f\\u7684\\u8272\\u504f\\uFF0C\\u4f7f\\u56fe\\u7247\\u989c\\u8272\\u66f4\\u63a5\\u8fd1\\u771f\\u5b9e\",\"type\":\"string\",\"cur\":\"\\u666e\\u901a\\u6a21\\u5f0f\",\"default\":\"\\u666e\\u901a\\u6a21\\u5f0f\",\"size\":20,\"range\":[\"\\u4e0d\\u6821\\u6b63\",\"\\u666e\\u901a\\u6a21\\u5f0f\",\"\\u589e\\u5f3a\\u6a21\\u5f0f\"]}}"; color_correct::color_correct() : enable_(true), enhance_(false) { @@ -201,28 +214,22 @@ int32_t color_correct::set_config(const char* cfg_name, void* data, size_t* len, if(cfg_->get_value(cfg_name, child) && child) { sane_cfg_provider::sane_refine_range(child, data, len); - if(name_ == cfg_name) - { - std::string val((char*)data); - - on_cur_val_changed(val); - child->set_value("cur", val.c_str()); - child->release(); - } + sane_cfg_provider::sane_option_value_set(child, data); + child->release(); } return ret; } -void color_correct::update_enabled(std::function get_opt) +void color_correct::update_enabled(std::function get_opt) {} -int32_t color_correct::get_value(const char* name, const char* key, std::string& val) +int32_t color_correct::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) { gb_json *child = nullptr; int32_t ret = ENOENT; if(cfg_->get_value(name, child) && child) { - sane_cfg_provider::raw_value_in_json(child, key, val); + ret = sane_cfg_provider::raw_value_in_json(child, key, val, type) ? 0 : ENOENT; child->release(); } @@ -276,7 +283,7 @@ bool color_correct::is_enabled(std::functionset_value("cur", dpi_); - child->release(); int x = *(int*)data, y = x; set_dpi(&x, &y); enable_ = x != dpi_ || y != dpi_; } + child->release(); } return ret; } -void img_resizer::update_enabled(std::function get_opt) +void img_resizer::update_enabled(std::function get_opt) {} -int32_t img_resizer::get_value(const char* name, const char* key, std::string& val) +int32_t img_resizer::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) { gb_json *child = nullptr; int32_t ret = ENOENT; if(cfg_->get_value(name, child) && child) { - sane_cfg_provider::raw_value_in_json(child, key, val); + if(sane_cfg_provider::raw_value_in_json(child, key, val, type)) + ret = 0; child->release(); } @@ -384,13 +392,147 @@ bool img_resizer::is_enabled(std::function +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// class img_color_transfer +// { +// "mode": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "field": "imgproc", +// "group": "base", +// "pos": 500, +// "unit": "none", +// "affect": 6, +// "ver": 1, +// "title": "颜色模式", +// "desc": "选择色彩模式", +// "type": "string", +// "cur": "24位彩色", +// "default": "24位彩色", +// "size": 32, +// "range": ["24位彩色", "256级灰度", "黑白", "颜色自动识别"] +// } +// } +static std::string color_mode_json = + "{\"mode\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"imgproc\",\"group\":\"base\",\"pos\":500,\"unit\":\"none\",\"affect\":6,\"ver\":1,\"title\":\"\\u989c\\u8272\\u6a21\\u5f0f\",\"desc\":\"\\u9009\\u62e9\\u8272\\u5f69\\u6a21\\u5f0f\",\"type\":\"string\",\"cur\":\"24\\u4f4d\\u5f69\\u8272\",\"default\":\"24\\u4f4d\\u5f69\\u8272\",\"size\":32,\"range\":[\"24\\u4f4d\\u5f69\\u8272\",\"256\\u7ea7\\u7070\\u5ea6\",\"\\u9ed1\\u767d\",\"\\u989c\\u8272\\u81ea\\u52a8\\u8bc6\\u522b\"]}}"; + +img_color_transfer::img_color_transfer() : enable_(false) +{ + name_ = "mode"; + cfg_ = new gb_json(); + cfg_->attach_text(&color_mode_json[0]); + + gb_json* child = nullptr; + + if(cfg_->get_value(name_.c_str(), child) && child) + { + std::string val(""); + + child->get_value("cur", val); + child->get_value("ver", ver_); + child->get_value("pos", pos_); + check_enable(val.c_str()); + child->release(); + } +} +img_color_transfer::~img_color_transfer() +{ + cfg_->release(); +} + +void img_color_transfer::check_enable(const char* val) +{ + enable_ = strcmp(val, "\xE9\xA2\x9C\xE8\x89\xB2\xE8\x87\xAA\xE5\x8A\xA8\xE8\xAF\x86\xE5\x88\xAB") == 0; +} + +int32_t img_color_transfer::get_config(void* buf, size_t* len, const char* cfg_name, std::string* strval) +{ + if(cfg_name) + return inner_get_config(cfg_, buf, len, cfg_name, strval); + else + { + std::string val(cfg_->to_string()); + + if(strval) + *strval = val; + if(*len < val.length()) + { + *len = val.length() + 4; + + return ENOMEM; + } + + memcpy(buf, val.c_str(), val.length()); + *len = val.length(); + + return 0; + } +} +int32_t img_color_transfer::set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) +{ + gb_json *child = nullptr; + int32_t ret = ENOENT; + + if(cfg_->get_value(cfg_name, child) && child) + { + int mode = COLOR_MODE_RGB; + + sane_cfg_provider::sane_refine_range(child, data, len); + sane_cfg_provider::sane_option_value_set(child, data); + if(strcmp((char*)data, "\xE9\xBB\x91\xE7\x99\xBD") == 0) + mode = COLOR_MODE_BW; + else if(strcmp((char*)data, "256\xE7\xBA\xA7\xE7\x81\xB0\xE5\xBA\xA6") == 0) + mode = COLOR_MODE_GRAY; + set_pixel_type(&mode); + check_enable((char*)data); + child->release(); + } + + return ret; +} +void img_color_transfer::update_enabled(std::function get_opt) +{} +int32_t img_color_transfer::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) +{ + gb_json *child = nullptr; + int32_t ret = ENOENT; + + if(cfg_->get_value(name, child) && child) + { + ret = sane_cfg_provider::raw_value_in_json(child, key, val, type) ? 0 : ENOENT; + child->release(); + } + + return ret; +} +img_one_paper* img_color_transfer::execute(img_one_paper* img) +{ + if(enable_) + { + // for(auto& v: img->images_queue()) + // { + // } + } + img->add_ref(); + + return img; +} +bool img_color_transfer::is_enabled(std::function get_opt_value) +{ + return enable_; +} + + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// class img_alg +// class img_encoder // { // "img-format": { // "category": "base", -// "readonly": true, +// "readonly": false, // "affect": 0, // "group": "output", // "visible": true, @@ -408,7 +550,7 @@ bool img_resizer::is_enabled(std::function // } // } static std::string fmt_json = - "{\"img-format\":{\"category\":\"base\",\"readonly\":true,\"affect\":0,\"group\":\"output\",\"visible\":true,\"field\":\"Common\",\"pos\":0,\"unit\":\"None\",\"title\":\"\\u56fe\\u7247\\u683c\\u5f0f\",\"desc\":\"\\u8bbe\\u5907\\u8f93\\u51fa\\u7684\\u56fe\\u7247\\u6587\\u4ef6\\u683c\\u5f0f\",\"type\":\"string\",\"cur\":\"JPEG\",\"default\":\"JPEG\",\"size\":20,\"ver\":1,\"range\":[\"JPEG\",\"PNG\",\"TIFF\"]}}"; + "{\"img-format\":{\"category\":\"base\",\"readonly\":false,\"affect\":0,\"group\":\"output\",\"visible\":true,\"field\":\"Common\",\"pos\":0,\"unit\":\"None\",\"title\":\"\\u56fe\\u7247\\u683c\\u5f0f\",\"desc\":\"\\u8bbe\\u5907\\u8f93\\u51fa\\u7684\\u56fe\\u7247\\u6587\\u4ef6\\u683c\\u5f0f\",\"type\":\"string\",\"cur\":\"JPEG\",\"default\":\"JPEG\",\"size\":20,\"ver\":1,\"range\":[\"JPEG\",\"PNG\",\"TIFF\"]}}"; img_encoder::img_encoder() : fmt_("JPEG") { @@ -477,18 +619,18 @@ int32_t img_encoder::set_config(const char* cfg_name, void* data, size_t* len, u return ret; } -void img_encoder::update_enabled(std::function get_opt) +void img_encoder::update_enabled(std::function get_opt) {} -int32_t img_encoder::get_value(const char* name, const char* key, std::string& val) +int32_t img_encoder::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) { gb_json* child = nullptr; int32_t ret = ENOENT; if(cfg_->get_value(name, child) && child) { - val = std::move(sane_cfg_provider::sane_option_value_get(child, key)); + if(sane_cfg_provider::raw_value_in_json(child, key, val, type)) + ret = 0; child->release(); - ret = val.empty() ? ENOENT : 0; } return ret; diff --git a/device/gxx-linux/usb/src/async_model/img_process/algs/img_algorithm.h b/device/gxx-linux/usb/src/async_model/img_process/algs/img_algorithm.h index 5d74563..1fb866c 100644 --- a/device/gxx-linux/usb/src/async_model/img_process/algs/img_algorithm.h +++ b/device/gxx-linux/usb/src/async_model/img_process/algs/img_algorithm.h @@ -64,9 +64,10 @@ enum img_alg_type IMG_ALG_NONE = 0, IMG_ALG_DOGEAR, IMG_ALG_SIZE_CHECK, - IMG_ALG_FILL_HOLE, + IMG_ALG_REMOVE_HOLE, + IMG_ALG_DISCARD_BLANK, IMG_ALG_AUTO_CROP, - IMG_ALG_BLANK, + IMG_ALG_COLOR_TRANSFER, IMG_ALG_CUSTOM_AREA, IMG_ALG_FADE_BACK, IMG_ALG_FILTER, @@ -126,8 +127,8 @@ protected: public: virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; - virtual void update_enabled(std::function get_opt) override; - virtual int32_t get_value(const char* name, const char* key, std::string& val) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; public: virtual img_one_paper* execute(img_one_paper* img) override; @@ -149,8 +150,32 @@ protected: public: virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; - virtual void update_enabled(std::function get_opt) override; - virtual int32_t get_value(const char* name, const char* key, std::string& val) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; + +public: + virtual img_one_paper* execute(img_one_paper* img) override; + virtual bool is_enabled(std::function get_opt_value) override; +}; + + +class img_color_transfer : public img_alg +{ + gb_json* cfg_; + bool enable_; + + void check_enable(const char* val); + +public: + img_color_transfer(); +protected: + ~img_color_transfer(); + +public: + virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; + virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; public: virtual img_one_paper* execute(img_one_paper* img) override; @@ -171,8 +196,8 @@ protected: public: virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; - virtual void update_enabled(std::function get_opt) override; - virtual int32_t get_value(const char* name, const char* key, std::string& val) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; public: MemoryPtr encode(PROCIMG* img); diff --git a/device/gxx-linux/usb/src/async_model/img_process/algs/remove_hole.cpp b/device/gxx-linux/usb/src/async_model/img_process/algs/remove_hole.cpp new file mode 100644 index 0000000..d25ed1f --- /dev/null +++ b/device/gxx-linux/usb/src/async_model/img_process/algs/remove_hole.cpp @@ -0,0 +1,199 @@ +#include "remove_hole.h" + + +#include "common/json/gb_json.h" +#include "imageprocess/ImageApplyOutHole.h" + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// + + + +static std::string remove_hole_jsn = + "{\"is-rid-hole-l\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"affect\":2,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":100,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u7a7f\\u5b54\\u79fb\\u9664\\u2014\\u5de6\\u4fa7\",\"desc\":\"\\u7a7f\\u5b54\\u5728\\u7eb8\\u5f20\\u4e0a\\u7684\\u5de6\\u4fa7\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"search-hole-range-l\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":101,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u5de6\\u4fa7\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"desc\":\"\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"type\":\"float\",\"cur\":0.100000,\"default\":0.100000,\"size\":4,\"range\":{\"min\":0.000000,\"max\":0.500000,\"step\":0.050000},\"depend_and\":[\"is-rid-hole-l==true\"]},\"is-rid-hole-r\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"affect\":2,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":110,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u7a7f\\u5b54\\u79fb\\u9664\\u2014\\u53f3\\u4fa7\",\"desc\":\"\\u7a7f\\u5b54\\u5728\\u7eb8\\u5f20\\u4e0a\\u7684\\u53f3\\u4fa7\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"search-hole-range-r\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":111,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u53f3\\u4fa7\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"desc\":\"\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"type\":\"float\",\"cur\":0.100000,\"default\":0.100000,\"size\":4,\"range\":{\"min\":0.000000,\"max\":0.500000,\"step\":0.050000},\"depend_and\":[\"is-rid-hole-r==true\"]},\"is-rid-hole-t\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"affect\":2,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":120,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u7a7f\\u5b54\\u79fb\\u9664\\u2014\\u4e0a\\u4fa7\",\"desc\":\"\\u7a7f\\u5b54\\u5728\\u7eb8\\u5f20\\u7684\\u4e0a\\u90e8\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"search-hole-range-t\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":121,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u4e0a\\u4fa7\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"desc\":\"\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"type\":\"float\",\"cur\":0.100000,\"default\":0.100000,\"size\":4,\"range\":{\"min\":0.000000,\"max\":0.500000,\"step\":0.050000},\"depend_and\":[\"is-rid-hole-t==true\"]},\"is-rid-hole-b\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"affect\":2,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":130,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u7a7f\\u5b54\\u79fb\\u9664\\u2014\\u4e0b\\u4fa7\",\"desc\":\"\\u7a7f\\u5b54\\u5728\\u7eb8\\u5f20\\u7684\\u4e0b\\u90e8\",\"type\":\"bool\",\"cur\":false,\"default\":false,\"size\":4},\"search-hole-range-b\":{\"category\":\"base\",\"readonly\":false,\"visible\":true,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":131,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u4e0b\\u4fa7\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"desc\":\"\\u7a7f\\u5b54\\u641c\\u7d22\\u8303\\u56f4\\u5360\\u5e45\\u9762\\u6bd4\\u4f8b\",\"type\":\"float\",\"cur\":0.100000,\"default\":0.100000,\"size\":4,\"range\":{\"min\":0.000000,\"max\":0.500000,\"step\":0.050000},\"depend_and\":[\"is-rid-hole-b==true\"]},\"hole-side-len\":{\"category\":\"base\",\"readonly\":false,\"visible\":false,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":140,\"unit\":\"pixel\",\"ver\":1,\"title\":\"\\u7a7f\\u5b54\\u8fb9\\u957f\\u9608\\u503c\",\"desc\":\"\\u7a7f\\u5b54\\u8fb9\\u957f\\u8d85\\u51fa\\u8be5\\u9608\\u503c\\u65f6\\uFF0C\\u5c06\\u88ab\\u79fb\\u9664\",\"type\":\"float\",\"cur\":25.0,\"default\":25.0,\"size\":4,\"range\":{\"min\":10.0,\"max\":50.0,\"step\":5}},\"hole-bin-threshold\":{\"category\":\"base\",\"readonly\":false,\"visible\":false,\"field\":\"imgproc\",\"group\":\"imgproc\",\"pos\":141,\"unit\":\"none\",\"ver\":1,\"title\":\"\\u7a7f\\u5b54\\u79fb\\u9664\\u4e2d\\uFF0C\\u4e8c\\u503c\\u5316\\u56fe\\u50cf\\u7684\\u9608\\u503c\",\"desc\":\"\\u7a7f\\u5b54\\u79fb\\u9664\\u7b97\\u6cd5\\u4e2d\\uFF0C\\u4e8c\\u503c\\u5316\\u56fe\\u50cf\\u65f6\\uFF0C\\u91c7\\u7528\\u7684\\u9608\\u503c\",\"type\":\"int\",\"cur\":50,\"default\":50,\"size\":4,\"range\":{\"min\":0,\"max\":255,\"step\":1}}}"; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// class img_algs +rm_hole::rm_hole() : holer_(nullptr), root_(new gb_json()) + , name_("is-rid-hole-l"), pos_(0), ver_(0), enable_(true) + , hole_side_len_(25.0f), percent_l_(.0f), percent_r_(.0f), percent_t_(.0f), percent_b_(.0f), bin_threshold_(50) +{ + gb_json* child = nullptr; + root_->attach_text(&remove_hole_jsn[0]); + + init_value_from_json(); + if(root_->get_value(name_.c_str(), child) && child) + { + int val = 0; + child->get_value("ver", val); + ver_ = val; + child->get_value("pos", val); + pos_ = val; + child->release(); + } + holer_ = new CImageApplyOutHole(hole_side_len_, {(float)percent_t_, (float)percent_b_, (float)percent_l_, (float)percent_r_}, bin_threshold_); +} +rm_hole::~rm_hole() +{ + root_->release(); + if(holer_) + delete holer_; +} + +void rm_hole::init_value_from_json(void) +{ + gb_json* child = nullptr, *childv = nullptr; + bool enable = true; + +#define GET_SIDE(s) \ + if(root_->get_value(MAKE_STR(is-rid-hole\x2d##s), child) && child) \ + { \ + if(child->get_value("cur", enable)) \ + { \ + if(enable) \ + { \ + if(root_->get_value(MAKE_STR(search-hole-range\x2d##s), childv) && childv) \ + { \ + childv->get_value("cur", percent_##s##_); \ + childv->release(); \ + } \ + } \ + else \ + percent_##s##_ = .0f; \ + } \ + child->release(); \ + } \ + enable_ |= enable; + + GET_SIDE(l); + GET_SIDE(r); + GET_SIDE(t); + GET_SIDE(b); + + if(root_->get_value("hole-side-len", child) && child) + { + child->get_value("cur", hole_side_len_); + child->release(); + } + + if(root_->get_value("hole-bin-threshold", child) && child) + { + child->get_value("cur", bin_threshold_); + child->release(); + } +} + +int32_t rm_hole::get_config(void* buf, size_t* len, const char* cfg_name, std::string* strval) +{ + if(cfg_name) + { + return inner_get_config(root_, buf, len, cfg_name, strval); + } + else + { + if(!len) + return EINVAL; + + std::string val(root_->to_string()); + + if(*len < val.length()) + { + *len = val.length() + 4; + + return ENOMEM; + } + + strcpy((char*)buf, val.c_str()); + *len = val.length(); + } + + return 0; +} +int32_t rm_hole::set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) +{ + gb_json* child = nullptr; + int32_t ret = ENOENT; + + if(root_->get_value(cfg_name, child) && child) + { + ret = sane_cfg_provider::sane_refine_range(child, data, len) ? 0 : EUCLEAN; + sane_cfg_provider::sane_option_value_set(child, data); + + if(afterdo) + { + int val = 0; + + child->get_value("affect", val); + *afterdo = val; + } + + child->release(); + init_value_from_json(); + } + + return ret; +} +void rm_hole::update_enabled(std::function get_opt) +{ + gb_json* child = root_->first_child(); + while(child) + { + sane_cfg_provider::update_option_enable_status(child, get_opt); + child->release(); + child = root_->next_child(); + } +} +int32_t rm_hole::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) +{ + int32_t ret = ENOENT; + gb_json* child = nullptr; + + if(root_->get_value(name, child) && child) + { + ret = sane_cfg_provider::raw_value_in_json(child, key, val, type) ? 0 : ENOENT; + child->release(); + } + + return ret; +} + +img_one_paper* rm_hole::execute(img_one_paper* img) +{ + if(enable_) + { + std::vector imgs; + + for(auto& v: img->images_queue()) + imgs.push_back(v.img); + + holer_->set_parameters(hole_side_len_, {(float)percent_t_, (float)percent_b_, (float)percent_l_, (float)percent_r_}, bin_threshold_); + holer_->apply(imgs, true); + + for(size_t i = 0; i < img->images_queue().size() && i < imgs.size(); ++i) + img->images_queue()[i].img = imgs[i]; + } + + img->add_ref(); + + return img; +} +bool rm_hole::is_enabled(std::function get_opt_value) +{ + return enable_; +} + +uint32_t rm_hole::position(void) +{ + return pos_; +} +uint32_t rm_hole::version(void) +{ + return ver_; +} +const char* rm_hole::option_name(void) +{ + return name_.c_str(); +} + + diff --git a/device/gxx-linux/usb/src/async_model/img_process/algs/remove_hole.h b/device/gxx-linux/usb/src/async_model/img_process/algs/remove_hole.h new file mode 100644 index 0000000..e188610 --- /dev/null +++ b/device/gxx-linux/usb/src/async_model/img_process/algs/remove_hole.h @@ -0,0 +1,253 @@ +#pragma once + +// remove hole algorithm +// +// created on 2023-05-02 +// + +#include "img_algorithm.h" + + + +// { +// "is-rid-hole-l": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "affect": 2, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 100, +// "unit": "none", +// "ver": 1, +// "title": "穿孔移除—左侧", +// "desc": "穿孔在纸张上的左侧", +// "type": "bool", +// "cur": false, +// "default": false, +// "size": 4 +// }, +// "search-hole-range-l": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 101, +// "unit": "none", +// "ver": 1, +// "title": "左侧穿孔搜索范围占幅面比例", +// "desc": "穿孔搜索范围占幅面比例", +// "type": "float", +// "cur": 0.100000, +// "default": 0.100000, +// "size": 4, +// "range": { +// "min": 0.000000, +// "max": 0.500000, +// "step": 0.050000 +// }, +// "depend_and": ["is-rid-hole-l==true"] +// }, +// "is-rid-hole-r": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "affect": 2, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 110, +// "unit": "none", +// "ver": 1, +// "title": "穿孔移除—右侧", +// "desc": "穿孔在纸张上的右侧", +// "type": "bool", +// "cur": false, +// "default": false, +// "size": 4 +// }, +// "search-hole-range-r": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 111, +// "unit": "none", +// "ver": 1, +// "title": "右侧穿孔搜索范围占幅面比例", +// "desc": "穿孔搜索范围占幅面比例", +// "type": "float", +// "cur": 0.100000, +// "default": 0.100000, +// "size": 4, +// "range": { +// "min": 0.000000, +// "max": 0.500000, +// "step": 0.050000 +// }, +// "depend_and": ["is-rid-hole-r==true"] +// }, +// "is-rid-hole-t": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "affect": 2, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 120, +// "unit": "none", +// "ver": 1, +// "title": "穿孔移除—上侧", +// "desc": "穿孔在纸张的上部", +// "type": "bool", +// "cur": false, +// "default": false, +// "size": 4 +// }, +// "search-hole-range-t": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 121, +// "unit": "none", +// "ver": 1, +// "title": "上侧穿孔搜索范围占幅面比例", +// "desc": "穿孔搜索范围占幅面比例", +// "type": "float", +// "cur": 0.100000, +// "default": 0.100000, +// "size": 4, +// "range": { +// "min": 0.000000, +// "max": 0.500000, +// "step": 0.050000 +// }, +// "depend_and": ["is-rid-hole-t==true"] +// }, +// "is-rid-hole-b": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "affect": 2, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 130, +// "unit": "none", +// "ver": 1, +// "title": "穿孔移除—下侧", +// "desc": "穿孔在纸张的下部", +// "type": "bool", +// "cur": false, +// "default": false, +// "size": 4 +// }, +// "search-hole-range-b": { +// "category": "base", +// "readonly": false, +// "visible": true, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 131, +// "unit": "none", +// "ver": 1, +// "title": "下侧穿孔搜索范围占幅面比例", +// "desc": "穿孔搜索范围占幅面比例", +// "type": "float", +// "cur": 0.100000, +// "default": 0.100000, +// "size": 4, +// "range": { +// "min": 0.000000, +// "max": 0.500000, +// "step": 0.050000 +// }, +// "depend_and": ["is-rid-hole-b==true"] +// }, +// "hole-side-len": { +// "category": "base", +// "readonly": false, +// "visible": false, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 140, +// "unit": "pixel", +// "ver": 1, +// "title": "穿孔边长阈值", +// "desc": "穿孔边长超出该阈值时,将被移除", +// "type": "float", +// "cur": 25.0, +// "default": 25.0, +// "size": 4, +// "range": { +// "min": 10.0, +// "max": 50.0, +// "step": 5 +// } +// }, +// "hole-bin-threshold": { +// "category": "base", +// "readonly": false, +// "visible": false, +// "field": "imgproc", +// "group": "imgproc", +// "pos": 141, +// "unit": "none", +// "ver": 1, +// "title": "穿孔移除中,二值化图像的阈值", +// "desc": "穿孔移除算法中,二值化图像时,采用的阈值", +// "type": "int", +// "cur": 50, +// "default": 50, +// "size": 4, +// "range": { +// "min": 0, +// "max": 255, +// "step": 1 +// } +// } +// } + +class CImageApplyOutHole; +class gb_json; + +class rm_hole : public img_alg +{ + CImageApplyOutHole* holer_; + gb_json* root_; + + std::string name_; + uint32_t pos_; + uint32_t ver_; + bool enable_; + + double hole_side_len_; // side length threshold, [10.0, 50.0], default 25.0 + double percent_l_; // percentage of the left side, [0.0, 0.5], default 0.0 + double percent_r_; // percentage of the right side, [0.0, 0.5], default 0.0 + double percent_t_; // percentage of the top side, [0.0, 0.5], default 0.0 + double percent_b_; // percentage of the bottom side, [0.0, 0.5], default 0.0 + int bin_threshold_; // binary threshold, [0, 255], default 50 + + void init_value_from_json(void); + +public: + rm_hole(void); +protected: + ~rm_hole(); + +public: + virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; + virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; + +public: + img_one_paper* execute(img_one_paper* img) override; + bool is_enabled(std::function get_opt_value) override; + + uint32_t position(void) override; + uint32_t version(void) override; + const char* option_name(void) override; +}; diff --git a/device/gxx-linux/usb/src/async_model/img_process/cis_preproc.cpp b/device/gxx-linux/usb/src/async_model/img_process/cis_preproc.cpp index 82ec64a..57425ba 100644 --- a/device/gxx-linux/usb/src/async_model/img_process/cis_preproc.cpp +++ b/device/gxx-linux/usb/src/async_model/img_process/cis_preproc.cpp @@ -195,7 +195,7 @@ int32_t cis_pre_do::set_config(const char* cfg_name, void* data, size_t* len, ui return ret; } -void cis_pre_do::update_enabled(std::function get_opt) +void cis_pre_do::update_enabled(std::function get_opt) { gb_json* root = new gb_json(); @@ -207,7 +207,7 @@ void cis_pre_do::update_enabled(std::functionrelease(); } -int32_t cis_pre_do::get_value(const char* name, const char* key, std::string& val) +int32_t cis_pre_do::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) { gb_json* root = new gb_json(), *child = nullptr; int32_t ret = ENOENT; @@ -216,7 +216,7 @@ int32_t cis_pre_do::get_value(const char* name, const char* key, std::string& va { if(root->get_value(name, child) && child) { - if(sane_cfg_provider::raw_value_in_json(child, key, val)) + if(sane_cfg_provider::raw_value_in_json(child, key, val, type)) ret = 0; child->release(); } diff --git a/device/gxx-linux/usb/src/async_model/img_process/cis_preproc.h b/device/gxx-linux/usb/src/async_model/img_process/cis_preproc.h index 5d6cb0f..6689978 100644 --- a/device/gxx-linux/usb/src/async_model/img_process/cis_preproc.h +++ b/device/gxx-linux/usb/src/async_model/img_process/cis_preproc.h @@ -32,8 +32,8 @@ protected: public: virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; - virtual void update_enabled(std::function get_opt) override; - virtual int32_t get_value(const char* name, const char* key, std::string& val) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; public: img_one_paper* execute(img_one_paper* img); diff --git a/device/gxx-linux/usb/src/async_model/img_process/img_process.cpp b/device/gxx-linux/usb/src/async_model/img_process/img_process.cpp index 0e570e3..dec958d 100644 --- a/device/gxx-linux/usb/src/async_model/img_process/img_process.cpp +++ b/device/gxx-linux/usb/src/async_model/img_process/img_process.cpp @@ -102,10 +102,11 @@ void img_processor::load_processors(void) CREATE_ALG(IMG_ALG_DOGEAR); CREATE_ALG(IMG_ALG_SIZE_CHECK); - CREATE_ALG(IMG_ALG_FILL_HOLE); + CREATE_ALG(IMG_ALG_REMOVE_HOLE); CREATE_ALG(IMG_ALG_AUTO_CROP); - CREATE_ALG(IMG_ALG_BLANK); + CREATE_ALG(IMG_ALG_DISCARD_BLANK); CREATE_ALG(IMG_ALG_CUSTOM_AREA); + CREATE_ALG(IMG_ALG_COLOR_TRANSFER); CREATE_ALG(IMG_ALG_FADE_BACK); CREATE_ALG(IMG_ALG_FILTER); CREATE_ALG(IMG_ALG_ADJUST_COLOR); @@ -314,7 +315,7 @@ int32_t img_processor::set_config(const char* cfg_name, void* data, size_t* len, return ret; } -void img_processor::update_enabled(std::function get_opt) +void img_processor::update_enabled(std::function get_opt) { // sane_cfg_provider::update_option_enable_status(cfg_, get_opt); cis_pre_->update_enabled(get_opt); @@ -325,18 +326,18 @@ void img_processor::update_enabled(std::functionupdate_enabled(get_opt); } -int32_t img_processor::get_value(const char* name, const char* key, std::string& val) +int32_t img_processor::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) { - int32_t ret = cis_pre_->get_value(name, key, val); + int32_t ret = cis_pre_->get_raw_value(name, key, val, type); if(ret == ENOENT) { - ret = encoder_->get_value(name, key, val); + ret = encoder_->get_raw_value(name, key, val, type); if(ret == ENOENT) { for(auto& v: all_proc_) { - ret = v->get_value(name, key, val); + ret = v->get_raw_value(name, key, val, type); if(ret != ENOENT) break; } diff --git a/device/gxx-linux/usb/src/async_model/img_process/img_process.h b/device/gxx-linux/usb/src/async_model/img_process/img_process.h index e4f42a1..d4fcd78 100644 --- a/device/gxx-linux/usb/src/async_model/img_process/img_process.h +++ b/device/gxx-linux/usb/src/async_model/img_process/img_process.h @@ -62,8 +62,8 @@ protected: public: virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; - virtual void update_enabled(std::function get_opt) override; - virtual int32_t get_value(const char* name, const char* key, std::string& val) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; public: int32_t push_image(img_one_paper* img/*scan status if 'over' was true*/, bool over = false); diff --git a/device/gxx-linux/usb/src/async_model/scanner/async_scanner.cpp b/device/gxx-linux/usb/src/async_model/scanner/async_scanner.cpp index d075ae2..d3f2398 100644 --- a/device/gxx-linux/usb/src/async_model/scanner/async_scanner.cpp +++ b/device/gxx-linux/usb/src/async_model/scanner/async_scanner.cpp @@ -324,7 +324,7 @@ dyn_mem_ptr async_scanner::handle_get_opt_value(LPPACK_BASE pack, uint32_t* used uint32_t err = cfg_mgr_->get_config(val, pack->payload, &str); LPCFGVAL cfg = nullptr; - log_cls::log(LOG_LEVEL_ALL, "Option '%s' value: %s\n", pack->payload, str.c_str()); + // log_cls::log(LOG_LEVEL_ALL, "Option '%s' value: %s\n", pack->payload, str.c_str()); reply = dyn_mem::memory(base_head_size + sizeof(CFGVAL) + strlen(pack->payload) + 1 + val.length() + 1); pk = (LPPACK_BASE)reply->ptr(); BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, err); @@ -807,7 +807,7 @@ uint32_t async_scanner::stop(void) // return ret; // } -// void async_scanner::update_enabled(std::function get_opt) +// void async_scanner::update_enabled(std::function get_opt) // { // gb_json *jsn = new gb_json(); // int32_t ret = EINVAL; diff --git a/device/gxx-linux/usb/src/async_model/scanner/async_scanner.h b/device/gxx-linux/usb/src/async_model/scanner/async_scanner.h index b11bd41..303610f 100644 --- a/device/gxx-linux/usb/src/async_model/scanner/async_scanner.h +++ b/device/gxx-linux/usb/src/async_model/scanner/async_scanner.h @@ -56,7 +56,7 @@ public: // virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name = nullptr, std::string* strval = nullptr) override; // virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; - // virtual void update_enabled(std::function get_opt) override; + // virtual void update_enabled(std::function get_opt) override; // virtual int32_t get_value(const char* name, const char* key, std::string& val) override; void push_image(int data_type, void* data, size_t w, size_t h, int dpi_x, int dpi_y, size_t paper_ind, paper_side side, clr_channel clr, img_status status, bool img_new, bool img_over); diff --git a/device/gxx-linux/usb/src/async_model/scanner/readonly_opts.cpp b/device/gxx-linux/usb/src/async_model/scanner/readonly_opts.cpp index b95ebca..d358f13 100644 --- a/device/gxx-linux/usb/src/async_model/scanner/readonly_opts.cpp +++ b/device/gxx-linux/usb/src/async_model/scanner/readonly_opts.cpp @@ -96,16 +96,16 @@ int32_t readonly_cfg::set_config(const char* cfg_name, void* data, size_t* len, // read-only attributes not support this operation !!! return EINVAL; } -void readonly_cfg::update_enabled(std::function get_opt) +void readonly_cfg::update_enabled(std::function get_opt) {} -int32_t readonly_cfg::get_value(const char* name, const char* key, std::string& val) +int32_t readonly_cfg::get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type) { int32_t ret = ENOENT; gb_json* child = nullptr; if(jsn_->get_value(name, child) && child) { - if(sane_cfg_provider::raw_value_in_json(child, key, val)) + if(sane_cfg_provider::raw_value_in_json(child, key, val, type)) ret = 0; child->release(); diff --git a/device/gxx-linux/usb/src/async_model/scanner/readonly_opts.h b/device/gxx-linux/usb/src/async_model/scanner/readonly_opts.h index a32a5c6..e033ba9 100644 --- a/device/gxx-linux/usb/src/async_model/scanner/readonly_opts.h +++ b/device/gxx-linux/usb/src/async_model/scanner/readonly_opts.h @@ -26,6 +26,6 @@ protected: public: virtual int32_t get_config(void* buf, size_t* len, const char* cfg_name, std::string* strval) override; virtual int32_t set_config(const char* cfg_name, void* data, size_t* len, uint32_t* afterdo) override; - virtual void update_enabled(std::function get_opt) override; - virtual int32_t get_value(const char* name, const char* key, std::string& val) override; + virtual void update_enabled(std::function get_opt) override; + virtual int32_t get_raw_value(const char* name, const char* key, std::string& val, uint32_t* type = nullptr) override; }; diff --git a/device/gxx-linux/usb/src/usbdevice.cpp b/device/gxx-linux/usb/src/usbdevice.cpp index 83cf57e..a6ec362 100644 --- a/device/gxx-linux/usb/src/usbdevice.cpp +++ b/device/gxx-linux/usb/src/usbdevice.cpp @@ -22,6 +22,8 @@ int32_t (*scan_start)(void) = nullptr; int32_t (*scan_stop)(void) = nullptr; int32_t (*set_dpi)(int*, int*) = nullptr; int32_t (*set_scan_num)(int) = nullptr; +int32_t (*set_paper_type)(int) = nullptr; +int32_t (*set_pixel_type)(int*) = nullptr; int32_t (*set_image_receiver)(void(*rcv)(int data_type, void* data, size_t w, size_t h, int, int, size_t paper_ind, paper_side side, clr_channel clr, img_status status, bool img_new, bool img_over, void* param), void* param) = nullptr; #endif @@ -91,6 +93,14 @@ int32_t call_set_scan_num(int num) { return inst->set_scan_num(num); } +int32_t call_set_paper_type(int type) +{ + return inst->set_paper_type(type); +} +int32_t call_set_pixel_type(int *type) +{ + return inst->set_pixel_type(type); +} int32_t call_set_image_receiver(void(*rcv)(int data_type, void* data, size_t w, size_t h, int dpi_x, int dpi_y, size_t paper_ind, paper_side side, clr_channel clr, img_status status, bool img_new, bool img_over, void* param), void* param) { return inst->set_img_receiver((void*)rcv, param); @@ -364,6 +374,8 @@ UsbDevice::UsbDevice(std::function *t = new thread_pool(this); t->thread_new(&UsbDevice::do_system_command, R"(echo linaro | sudo -S sh -c "echo fe900000.dwc3 > /opt/cfg/usb_gadget/g1/UDC")"); @@ -1442,4 +1454,12 @@ int UsbDevice::set_img_receiver(void* api, void* param) return ctrl_handler(-1, (usb_ctrlrequest*)-1, (unsigned char*)cb) ? 0 : EBADF; } +int UsbDevice::set_paper_type(int type) +{ + return ctrl_handler(-1, (usb_ctrlrequest*)SR_SCAN_PAPER, (unsigned char*)type) ? 0 : EBADF; +} +int UsbDevice::set_pixel_type(int *type) +{ + return ctrl_handler(-1, (usb_ctrlrequest*)SR_SCAN_PIXEL, (unsigned char*)*type) ? 0 : EBADF; +} #endif diff --git a/pc/code_twain/sln/usb_tools/Debug/usb_tools.exe b/pc/code_twain/sln/usb_tools/Debug/usb_tools.exe index 39cc9beae38756d8eb942c97acad67d633bb0a68..466f7057938210ff14ec9dd16db203e82ad9a664 100644 GIT binary patch delta 51272 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 V9dn~$2@kt_YyrEc`bQ@G{{zJoLmU7A delta 49426 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 Olexe)JVCH{H~k;|3I$gH diff --git a/pc/code_twain/sln/usb_tools/scanner/opt_ui/DlgPage.cpp b/pc/code_twain/sln/usb_tools/scanner/opt_ui/DlgPage.cpp index 6cb6040..3ce41b3 100644 --- a/pc/code_twain/sln/usb_tools/scanner/opt_ui/DlgPage.cpp +++ b/pc/code_twain/sln/usb_tools/scanner/opt_ui/DlgPage.cpp @@ -885,6 +885,12 @@ HWND dlg_page::create_control_bool(int sn, const SANE_Option_Descriptor* desc, v if (now) SendMessage(wnd, BM_SETCHECK, (WPARAM)BST_CHECKED, 0); + if (IS_CAP_READONLY(desc->cap)) + { + if (IsWindow(wnd)) + EnableWindow(wnd, FALSE); + } + return wnd; } HWND dlg_page::create_control_int(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size) @@ -910,7 +916,7 @@ HWND dlg_page::create_control_int(int sn, const SANE_Option_Descriptor* desc, vo vals.push_back(text); } swprintf_s(text, _countof(text) - 1, L"%d", now); - wnd = create_combox(sn, x, pos_.y, vals, text, &size); + wnd = create_combox(sn, x, pos_.y - 1, vals, text, &size); x += size.cx; } else @@ -934,6 +940,18 @@ HWND dlg_page::create_control_int(int sn, const SANE_Option_Descriptor* desc, vo } text_size->cx = x + dlg_page::gap_x; + if (IS_CAP_READONLY(desc->cap)) + { + if (IsWindow(label)) + EnableWindow(label, FALSE); + if (IsWindow(slider)) + EnableWindow(slider, FALSE); + if (IsWindow(wnd)) + EnableWindow(wnd, FALSE); + if (IsWindow(spin)) + EnableWindow(spin, FALSE); + } + return wnd; } HWND dlg_page::create_control_float(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size) @@ -961,7 +979,7 @@ HWND dlg_page::create_control_float(int sn, const SANE_Option_Descriptor* desc, if (v[i + 1] == *(SANE_Word*)cur_val) wcscpy_s(cur, _countof(cur) - 1, text); } - wnd = create_combox(sn, x, pos_.y, vals, cur, &size); + wnd = create_combox(sn, x, pos_.y - 1, vals, cur, &size); x += size.cx; } else @@ -985,15 +1003,27 @@ HWND dlg_page::create_control_float(int sn, const SANE_Option_Descriptor* desc, } text_size->cx = x + dlg_page::gap_x; + if (IS_CAP_READONLY(desc->cap)) + { + if (IsWindow(label)) + EnableWindow(label, FALSE); + if (IsWindow(slider)) + EnableWindow(slider, FALSE); + if (IsWindow(wnd)) + EnableWindow(wnd, FALSE); + if (IsWindow(spin)) + EnableWindow(spin, FALSE); + } + return wnd; } HWND dlg_page::create_control_string(int sn, const SANE_Option_Descriptor* desc, void* cur_val, const wchar_t* title, LPSIZE text_size) { - HWND wnd = NULL; + HWND wnd = NULL, lable = NULL; int x = pos_.x; std::wstring now(local_trans::a2u((char*)cur_val, CP_UTF8)); - create_label(sn, title, x, pos_.y, *text_size); + lable = create_label(sn, title, x, pos_.y, *text_size); x += text_size->cx + dlg_page::gap_x; if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST) @@ -1007,7 +1037,7 @@ HWND dlg_page::create_control_string(int sn, const SANE_Option_Descriptor* desc, std::wstring text(local_trans::a2u(str[i], CP_UTF8)); vals.push_back(text); } - wnd = create_combox(sn, x, pos_.y, vals, now.c_str(), &size); + wnd = create_combox(sn, x, pos_.y - 1, vals, now.c_str(), &size); x += size.cx; } else @@ -1015,8 +1045,13 @@ HWND dlg_page::create_control_string(int sn, const SANE_Option_Descriptor* desc, wnd = create_edit(sn, x, pos_.y, text_size->cy, 200); SetWindowTextW(wnd, now.c_str()); x += 200; - if (IS_CAP_READONLY(desc->cap)) - SendMessage(wnd, EM_SETREADONLY, 1, 0); + } + + if (IS_CAP_READONLY(desc->cap)) + { + //SendMessage(wnd, EM_SETREADONLY, 1, 0); + EnableWindow(lable, FALSE); + EnableWindow(wnd, FALSE); } text_size->cx = x + dlg_page::gap_x; @@ -1115,9 +1150,9 @@ BOOL dlg_page::on_notify(int ctrl_id, LPNMHDR pnmh) { if (pnmh->code != NM_RELEASEDCAPTURE) { - if (pnmh->code == NM_CUSTOMDRAW && (GetAsyncKeyState(VK_LBUTTON) & 0x8000) && GetFocus() == pnmh->hwndFrom) // drag track ... - ; - else + //if (pnmh->code == NM_CUSTOMDRAW && (GetAsyncKeyState(VK_LBUTTON) & 0x8000) && GetFocus() == pnmh->hwndFrom) // drag track ... + // ; + //else return FALSE; } } @@ -1627,13 +1662,15 @@ bool dlg_page::refresh(int sn, const SANE_Option_Descriptor* desc, void* cur_val { if (GetWindowLong(ctrls_[ind], GWL_ID) != sn) break; + + BOOL en = ((desc->cap & SANE_CAP_INACTIVE) != SANE_CAP_INACTIVE) && !(IS_CAP_READONLY(desc->cap)); set_ctrl_value(ctrls_[ind], desc->type, cur_val, true); - EnableWindow(ctrls_[ind], (desc->cap & SANE_CAP_INACTIVE) != SANE_CAP_INACTIVE); + EnableWindow(ctrls_[ind], en); HWND host = (HWND)GetPropW(ctrls_[ind], dlg_page::property_host.c_str()); if (IsWindow(host)) { BOOL checked = SendMessage(host, BM_GETCHECK, 0, 0) == BST_CHECKED; - checked &= (desc->cap & SANE_CAP_INACTIVE) != SANE_CAP_INACTIVE; + checked &= en; if (sn - dlg_page::dyn_id_base == id_custom_area_) { EnableWindow(ctrls_[ind], checked);