#include "pch.h" #include "huagaotwain.h" #include "huagao/hgscanner_error.h" #include "./twain/twain_2.4.h" #include "../../code_device/sdk/hginclude/hg_log.h" #define STR(s) #s #define PASTE_STR(a, b) STR(a##b) #define SANE_API(api) PASTE_STR(sane_hgsane_, api) #define API_ELEM(api) {SANE_API(api), (FARPROC*)&real_sane_##api##_} #ifdef VLOG_OK #define vlog_debug_info sane_invoker::log_debug_info #else #define vlog_debug_info #endif ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // utilities ... namespace local_utility { std::wstring reg_read(HKEY root, const wchar_t* path, const wchar_t* name) { HKEY key = NULL; RegOpenKeyW(root, path, &key); if (!key) return L""; wchar_t* buf = NULL; DWORD len = 0; DWORD type = REG_SZ; std::wstring ret(L""); RegQueryValueExW(key, name, NULL, &type, (LPBYTE)buf, &len); if(len) { buf = new wchar_t[len + 4]; memset(buf, 0, (len + 4) * sizeof(*buf)); RegQueryValueExW(key, name, NULL, &type, (LPBYTE)buf, &len); ret = buf; delete[] buf; } RegCloseKey(key); return ret; } std::wstring reg_get_app_installing_path(void) { return reg_read(HKEY_LOCAL_MACHINE, L"SOFTWARE\\HuaGoScan", L"AppDirectory"); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // scanned image ... scanned_img::scanned_img(SANE_Image* img, SANE_FinalImgFormat* header) : param_(img->header) { // PBITMAPINFOHEADER size_t bytes = line_bytes() * height(); std::string h(file_header(header, bytes)); unsigned char *src = img->data + img->bytes - param_.bytes_per_line, *dst = NULL; data_.resize(bytes + h.length()); memcpy(&data_[0], h.c_str(), h.length()); dst = (unsigned char*)&data_[0] + h.length(); if (param_.format == SANE_FRAME_RGB) { for (int i = 0; i < height(); ++i) { for (int j = 0; j < param_.pixels_per_line; ++j) { dst[j * 3 + 0] = src[j * 3 + 2]; dst[j * 3 + 1] = src[j * 3 + 1]; dst[j * 3 + 2] = src[j * 3 + 0]; } src -= param_.bytes_per_line; dst += line_bytes(); } } else { for (int i = 0; i < height(); ++i, dst += line_bytes(), src -= param_.bytes_per_line) memcpy(dst, src, param_.bytes_per_line); } } scanned_img::~scanned_img() {} std::string scanned_img::file_header(SANE_FinalImgFormat* header, float resolution) { std::string h(""); if (header->img_format == SANE_IMAGE_TYPE_BMP) { BITMAPINFOHEADER bih = { 0 }; bih.biSize = sizeof(bih); bih.biWidth = width(); bih.biBitCount = depth(); bih.biSizeImage = line_bytes() * height(); bih.biPlanes = 1; bih.biHeight = height(); bih.biCompression = BI_RGB; bih.biXPelsPerMeter = bih.biYPelsPerMeter = resolution * 39.37f + .5f; h = std::string((char*)&bih, sizeof(bih)); } return h; } int scanned_img::width(void) { return param_.pixels_per_line; } int scanned_img::line_bytes(void) { return (param_.bytes_per_line + 3) / 4 * 4; } int scanned_img::height(void) { return param_.lines; } int scanned_img::depth(void) { if (param_.format == SANE_FRAME_RGB) return param_.depth * 3; else return param_.depth; } int scanned_img::channel(void) { return param_.format == SANE_FRAME_RGB ? 3 : 1; } SANE_Frame scanned_img::type(void) { return param_.format; } unsigned int scanned_img::bytes(void) { return data_.size(); } unsigned char* scanned_img::bits(void) { return &data_[0]; } void scanned_img::copy_header(SANE_Parameters* head) { *head = param_; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // sane_invoker ... sane_invoker* sane_invoker::inst_ = NULL; sane_invoker::sane_invoker(const wchar_t* path) : ok_(false), ver_(0), sane_(NULL), first_cb_(NULL) { memset(&sane_api_, 0, sizeof(sane_api_)); cfg_file_ = std::wstring(path) + L"config.txt"; if (load_sane()) { first_cb_ = CreateEventA(NULL, FALSE, FALSE, NULL); real_sane_init_ex_(&ver_, &sane_invoker::sane_callback_handler, this); char msg[128] = { 0 }; sprintf_s(msg, _countof(msg) - 1, "Wait sane first callback = %d\r\n", WaitForSingleObject(first_cb_, 1000)); sane_invoker::log_debug_info(msg); } } sane_invoker::~sane_invoker() { if (sane_) { if(real_sane_exit_) real_sane_exit_(); FreeLibrary(sane_); sane_ = NULL; } if (first_cb_) CloseHandle(first_cb_); } bool sane_invoker::load_sane() { //sane_hgsane_init //sane_hgsane_exit //sane_hgsane_get_devices //sane_hgsane_open //sane_hgsane_close //sane_hgsane_get_option_descriptor //sane_hgsane_control_option //sane_hgsane_get_parameters //sane_hgsane_start //sane_hgsane_read //sane_hgsane_cancel //sane_hgsane_set_io_mode //sane_hgsane_get_select_fd //sane_hgsane_strstatus //sane_hgsane_init_ex //sane_hgsane_io_control struct { std::string name; FARPROC* api; }apis[] = { API_ELEM(init), API_ELEM(exit), API_ELEM(get_devices), API_ELEM(open), API_ELEM(close), API_ELEM(get_option_descriptor), API_ELEM(control_option), API_ELEM(get_parameters), API_ELEM(start), API_ELEM(read), API_ELEM(cancel), API_ELEM(set_io_mode), API_ELEM(get_select_fd), API_ELEM(strstatus), API_ELEM(io_control), API_ELEM(init_ex) }; wchar_t sane_path[MAX_PATH] = { 0 }; DWORD size = _countof(sane_path); log_ = &sane_invoker::no_log; for (int i = 0; i < _countof(apis); ++i) *apis[i].api = NULL; ok_ = false; #ifdef USE_LOCAL_CONFIG if (GetPrivateProfileStringW(L"sane", L"pe", L"", sane_path, size - 1, cfg_file_.c_str())) #else std::wstring reg(local_utility::reg_get_app_installing_path()); wcscpy_s(sane_path, size - 1, reg.c_str()); if(reg.length()) #endif { wcscat(sane_path, L"\\hgsane.dll"); size = sane_invoker::load_dll(sane_path, &sane_); if (sane_) { ok_ = true; *((FARPROC*)&log_) = GetProcAddress(sane_, "hg_debug_log"); if (!log_) log_ = &sane_invoker::no_log; for (int i = 0; i < _countof(apis); ++i) { *apis[i].api = GetProcAddress(sane_, apis[i].name.c_str()); if (!(*apis[i].api)) { vlog_debug_info(1024, "GetProcAddress(\"sane.dll\", \"%s\") failed with error: %d\r\n", apis[i].name.c_str(), GetLastError()); ok_ = false; } } if (ok_) { vlog_debug_info(1024, "sane component load success, path = \"%s\"\r\n", sane_invoker::u2ansi(sane_path).c_str()); for (int i = 0; i < sizeof(sane_api_) / sizeof(sane_api_.sane_cancel_api); ++i) ((FARPROC*)&sane_api_)[i] = *apis[i + 2].api; } } else { vlog_debug_info(1024, "LoadLibraryW(\"%s\") failed with error: %d\r\n", sane_invoker::u2ansi(sane_path).c_str(), GetLastError()); } } else { vlog_debug_info(1024, "GetPrivateProfileStringW(\"sane\", \"pe\", \"%s\") failed\r\n", sane_invoker::u2ansi(cfg_file_.c_str()).c_str()); } return ok_; } int sane_invoker::handle_sane_event(SANE_Handle hdev, int code, void* data, unsigned int* len) { SetEvent(first_cb_); if (code == SANE_EVENT_DEVICE_ARRIVED) { SANE_Device* sdev = (SANE_Device*)data; SANEDEV dev; std::lock_guard lock(lock_dev_); std::vector::iterator it = std::find(devices_.begin(), devices_.end(), sdev->name); if (it == devices_.end()) { dev.name = sdev->name; dev.type = sdev->model; dev.product = sdev->type; dev.vendor = sdev->vendor; devices_.push_back(dev); } else if (it->scanner) it->scanner->set_online(true); } else if (code == SANE_EVENT_DEVICE_LEFT) { SANE_Device* sdev = (SANE_Device*)data; std::lock_guard lock(lock_dev_); std::vector::iterator it = std::find(devices_.begin(), devices_.end(), sdev->name); if (it != devices_.end()) { if (it->scanner) it->scanner->set_online(false); else devices_.erase(it); } } else if (code == SANE_EVENT_IMAGE_OK) { scanner* dev = find_scanner(hdev); if (dev) { dev->put_image((SANE_Image*)data, len); dev->release(); } } else if (code == SANE_EVENT_SCAN_FINISHED) { scanner* dev = find_scanner(hdev); if (dev) { dev->scan_finished((char*)data, *len); } } else if (code == SANE_EVENT_ERROR) { std::wstring msg(sane_invoker::utf82u((char*)data)); MessageBoxW(NULL, msg.c_str(), L"Error", MB_OK); } return SCANNER_ERR_OK; } void sane_invoker::get_online_devices(std::vector& devs) { std::lock_guard lock(lock_dev_); devs = devices_; } int sane_invoker::get_online_device_count(void) { std::lock_guard lock(lock_dev_); return devices_.size(); } scanner* sane_invoker::find_scanner(SANE_Handle hdev) { std::lock_guard lock(lock_dev_); std::vector::iterator it = std::find(devices_.begin(), devices_.end(), hdev); if (it == devices_.end()) return NULL; else { it->scanner->add_ref(); return it->scanner; } } scanner* sane_invoker::open(const char* name, int* err) { std::lock_guard lock(lock_dev_); std::vector::iterator it = std::find(devices_.begin(), devices_.end(), name); if (it == devices_.end()) { if (err) *err = SCANNER_ERR_DEVICE_NOT_FOUND; return NULL; } scanner *s = new scanner(this, *it); if (s->last_error() != SANE_STATUS_GOOD) { if (err) *err = s->last_error(); s->release(); s = NULL; } return s; } void sane_invoker::no_log(int, const char* info) { OutputDebugStringA(info); } int sane_invoker::sane_callback_handler(SANE_Handle hdev, int code, void* data, unsigned int* len, void* param) { return ((sane_invoker*)param)->handle_sane_event(hdev, code, data, len); } int sane_invoker::load_dll(const wchar_t* path_dll, HMODULE* dll) { HMODULE h = LoadLibraryW(path_dll); int ret = 0; if (!h && GetLastError() == ERROR_MOD_NOT_FOUND) { std::wstring dir(path_dll); size_t pos = dir.rfind(L'\\'); wchar_t path[MAX_PATH] = { 0 }; GetCurrentDirectoryW(_countof(path) - 1, path); if (pos != std::wstring::npos) dir.erase(pos); SetCurrentDirectoryW(dir.c_str()); h = LoadLibraryW(path_dll); ret = GetLastError(); SetCurrentDirectoryW(path); } else ret = GetLastError(); if (dll) *dll = h; return ret; } bool sane_invoker::initialize(HMODULE me) { if (sane_invoker::inst_) return false; else { wchar_t path[MAX_PATH] = { 0 }, * last = NULL; GetModuleFileNameW(me, path, _countof(path) - 1); last = wcsrchr(path, L'\\'); if (last++) *last = 0; sane_invoker::inst_ = new sane_invoker(path); return true; } } void sane_invoker::uninitialize(void) { if (sane_invoker::inst_) delete sane_invoker::inst_; sane_invoker::inst_ = NULL; } std::string sane_invoker::u2m(const wchar_t* u, int page) { char *ansi = NULL; int len = 0; std::string mb(""); len = WideCharToMultiByte(page, 0, u, lstrlenW(u), NULL, 0, NULL, NULL); ansi = new char[len + 2]; len = WideCharToMultiByte(page, 0, u, lstrlenW(u), ansi, len, NULL, NULL); ansi[len--] = 0; mb = ansi; delete[] ansi; return mb; } std::wstring sane_invoker::m2u(const char* m, int page) { wchar_t *unic = NULL; int len = 0; std::wstring u(L""); len = MultiByteToWideChar(page, 0, m, lstrlenA(m), NULL, 0); unic = new wchar_t[len + 2]; len = MultiByteToWideChar(page, 0, m, lstrlenA(m), unic, len); unic[len--] = 0; u = unic; delete[] unic; return u; } std::string sane_invoker::u2ansi(const wchar_t* u) { return sane_invoker::u2m(u, CP_ACP); } std::string sane_invoker::u2utf8(const wchar_t* u) { return sane_invoker::u2m(u, CP_UTF8); } std::string sane_invoker::utf82ansi(const char* utf8) { return sane_invoker::u2m(sane_invoker::m2u(utf8, CP_UTF8).c_str(), CP_ACP); } std::string sane_invoker::ansi2utf8(const char* ansi) { return sane_invoker::u2m(sane_invoker::m2u(ansi, CP_ACP).c_str(), CP_UTF8); } std::wstring sane_invoker::utf82u(const char* utf8) { return sane_invoker::m2u(utf8, CP_UTF8); } std::wstring sane_invoker::ansi2u(const char* ansi) { return sane_invoker::m2u(ansi, CP_ACP); } void sane_invoker::log_debug_info(const char* info) { if (sane_invoker::inst_) sane_invoker::inst_->log_(LOG_LEVEL_DEBUG_INFO, info); else OutputDebugStringA(info); } #ifdef VLOG_OK void __cdecl sane_invoker::log_debug_info(int bytes, const char* fmt, ...) { std::string str(bytes + 20, 0); va_list args; va_start(args, fmt); sprintf_s(&str[0], bytes, fmt, args); va_end(args); vlog_debug_info(str.c_str()); } #endif SANE_Status sane_invoker::invoke_sane_init(SANE_Int* version_code, SANE_Auth_Callback authorize) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_init_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_init_(version_code, authorize); } void sane_invoker::invoke_sane_exit(void) { if (sane_invoker::inst_ && sane_invoker::inst_->real_sane_exit_) sane_invoker::inst_->real_sane_exit_(); } SANE_Status sane_invoker::invoke_sane_get_devices(const SANE_Device*** device_list, SANE_Bool local_only) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_get_devices_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_get_devices_(device_list, local_only); } SANE_Status sane_invoker::invoke_sane_open(SANE_String_Const devicename, SANE_Handle* handle) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_open_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_open_(devicename, handle); } void sane_invoker::invoke_sane_close(SANE_Handle handle) { if (sane_invoker::inst_ && sane_invoker::inst_->real_sane_close_) sane_invoker::inst_->real_sane_close_(handle); } const SANE_Option_Descriptor* sane_invoker::invoke_sane_get_option_descriptor(SANE_Handle handle, SANE_Int option) { if (!sane_invoker::inst_) return NULL; if (!sane_invoker::inst_->real_sane_get_option_descriptor_) return NULL; return sane_invoker::inst_->real_sane_get_option_descriptor_(handle, option); } SANE_Status sane_invoker::invoke_sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void* value, SANE_Int* info) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_control_option_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_control_option_(handle, option, action, value, info); } SANE_Status sane_invoker::invoke_sane_get_parameters(SANE_Handle handle, SANE_Parameters* params) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_get_parameters_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_get_parameters_(handle, params); } SANE_Status sane_invoker::invoke_sane_start(SANE_Handle handle) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_start_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_start_(handle); } SANE_Status sane_invoker::invoke_sane_read(SANE_Handle handle, SANE_Byte* data, SANE_Int max_length, SANE_Int* length) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_read_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_read_(handle, data, max_length, length); } void sane_invoker::invoke_sane_cancel(SANE_Handle handle) { if (sane_invoker::inst_ && sane_invoker::inst_->real_sane_cancel_) sane_invoker::inst_->invoke_sane_cancel(handle); } SANE_Status sane_invoker::invoke_sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_set_io_mode_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_set_io_mode_(handle, non_blocking); } SANE_Status sane_invoker::invoke_sane_get_select_fd(SANE_Handle handle, SANE_Int* fd) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_get_select_fd_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_get_select_fd_(handle, fd); } SANE_String_Const sane_invoker::invoke_sane_strstatus(SANE_Status status) { if (sane_invoker::inst_ && sane_invoker::inst_->real_sane_strstatus_) return sane_invoker::inst_->real_sane_strstatus_(status); else return ""; } SANE_Status sane_invoker::invoke_sane_init_ex(SANE_Int* version_code, sane_callback cb, void* param) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_init_ex_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_init_ex_(version_code, cb, param); } SANE_Status sane_invoker::invoke_sane_io_control(SANE_Handle h, unsigned long code, void* data, unsigned* len) { if (!sane_invoker::inst_) return (SANE_Status)SCANNER_ERR_NOT_OPEN; if (!sane_invoker::inst_->real_sane_io_control_) return SANE_STATUS_UNSUPPORTED; return sane_invoker::inst_->real_sane_io_control_(h, code, data, len); } LPSANEAPI sane_invoker::get_api(void) { if (sane_invoker::inst_) return &sane_invoker::inst_->sane_api_; else return NULL; } bool sane_invoker::is_ok(void) { if (sane_invoker::inst_) return sane_invoker::inst_->ok_; else return false; } std::string sane_invoker::version(LPDWORD v) { char vs[40] = { 0 }; DWORD ver = 0; if (sane_invoker::inst_) ver = sane_invoker::inst_->ver_; sprintf_s(vs, _countof(vs) - 1, "%u.%u.%u", (ver >> 24) & 0x0ff, (ver >> 16) & 0x0ff, ver & 0x0ffff); if (v) *v = ver; return vs; } void sane_invoker::get_devices(std::vector& devs) { if (sane_invoker::inst_) sane_invoker::inst_->get_online_devices(devs); } scanner* sane_invoker::open_scanner(const char* name, int* err) { if (!sane_invoker::inst_) { if (err) *err = SCANNER_ERR_NOT_OPEN; return NULL; } return sane_invoker::inst_->open(name, err); } int sane_invoker::online_devices(void) { if (sane_invoker::inst_) sane_invoker::inst_->get_online_device_count(); else return 0; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // scanner ... #define FIND_OPTION(opt) \ std::vector::iterator it = std::find(options_.begin(), options_.end(), opt); scanner::scanner(sane_invoker* host, struct _dev& dev) : host_(host), hdev_(NULL), dpi_(200.0f), option_count_(0), event_cb_(NULL) , name_(dev.name), type_(dev.type), vendor_(dev.vendor), product_(dev.product), cb_param_(NULL) , scanning_(false), wait_img_(CreateEventA(NULL, TRUE, FALSE, NULL)), fmt_(NULL) , opt_ind_dpi_(-1), opt_ind_color_mode_(-1), opt_ind_paper_(-1), opt_ind_scann_count_(-1) , jpeg_quality_(80), opt_ind_text_direction_(-1), opt_ind_page_(-1), img_fmt_(SANE_IMAGE_TYPE_BMP) , opt_ind_auto_descrew_(-1), opt_ind_erase_black_frame_(-1), opt_ind_filter_(-1), opt_ind_bright_(-1) , opt_ind_contrast_(-1), opt_ind_gamma_(-1), opt_ind_ultrasonic_(-1), err_(SCANNER_ERR_OK), desc_("") , compression_(SANE_COMPRESSION_NONE), opt_ind_flip_(-1), auto_crop_(false), opt_ind_rotate_bkg_(-1) , opt_ind_fill_blank_bkg_(-1), opt_ind_edge_ident_(-1), opt_ind_threshold_(-1), opt_ind_bkg_filling_method_(-1) , opt_ind_fill_hole_(-1), opt_ind_fill_hole_ratio_(-1), opt_ind_noise_(-1), opt_ind_noise_threshold_(-1) , opt_ind_rid_red_hsv_(-1), opt_ind_rid_red_(-1), opt_ind_sharpen_(-1), opt_ind_screw_detect_(-1), opt_ind_screw_detect_level_(-1) , opt_ind_staple_(-1), opt_ind_dogear_(-1), opt_ind_dark_sample_(-1), opt_ind_split_(-1), opt_ind_fade_bkg_(-1) , opt_ind_fade_bkg_val_(-1), opt_ind_size_detect_(-1), opt_ind_multi_out_(-1) { dev.scanner = this; SANE_Status statu = sane_invoker::invoke_sane_open(name_.c_str(), &hdev_); online_ = statu == SANE_STATUS_GOOD; { vlog_debug_info(128, "scanner(0x%x) generated, open '%s' = %d\r\n", this, name_.c_str(), statu); } if (online_) load_options(); init_image_format(); err_ = statu; } scanner::~scanner() { if (fmt_) delete[](char*)fmt_; for (auto& v : options_) { if (v.desc->type == SANE_Value_Type::SANE_TYPE_STRING) delete v.val.sv; } vlog_debug_info(128, "scanner(0x%x) destroyed\r\n", this); } std::string scanner::type(SANE_Value_Type st) { switch (st) { case SANE_Value_Type::SANE_TYPE_BOOL: return "bool"; case SANE_Value_Type::SANE_TYPE_BUTTON: return "button"; case SANE_Value_Type::SANE_TYPE_FIXED: return "float"; case SANE_Value_Type::SANE_TYPE_INT: return "int"; case SANE_Value_Type::SANE_TYPE_STRING: return "string"; default: return ""; } } value_limit scanner::limit(SANE_Constraint_Type st) { switch (st) { case SANE_Constraint_Type::SANE_CONSTRAINT_RANGE: return VAL_LIMIT_RANGE; case SANE_Constraint_Type::SANE_CONSTRAINT_STRING_LIST: case SANE_Constraint_Type::SANE_CONSTRAINT_WORD_LIST: return VAL_LIMIT_ENUM; default: return VAL_LIMIT_NONE; } } void scanner::load_options(void) { SANE_Int count = 0, afterdo = 0; SANE_Status statu = sane_invoker::invoke_sane_control_option(hdev_, 0, SANE_ACTION_GET_VALUE, &count, &afterdo); option_count_ = 0; if (statu != SANE_STATUS_GOOD) { vlog_debug_info(128, "get option count failed with error: %d\r\n", statu); return; } else vlog_debug_info(128, "\"%s\" has %d options.\r\n", count); option_count_ = count; for (int i = 1; i < option_count_; ++i) { const SANE_Option_Descriptor* desc = sane_invoker::invoke_sane_get_option_descriptor(hdev_, i); SANEOPTION op; op.ind = i; op.desc = desc; if (desc->name == KNOWN_OPT_NAME_CUSTOM_GAMMA) { SANE_Int afterdo = 0; SANE_Int *v = new SANE_Int[3 * 256]; sane_invoker::invoke_sane_control_option(hdev_, i, SANE_ACTION_GET_VALUE, v, &afterdo); op.val.iv = 0; delete[] v; } else if (desc->type == SANE_Value_Type::SANE_TYPE_BOOL) op.val.bv = get_boolean(i); else if (desc->type == SANE_Value_Type::SANE_TYPE_FIXED) op.val.dv = get_double(i); else if (desc->type == SANE_Value_Type::SANE_TYPE_INT) op.val.iv = get_integer(i); else if (desc->type == SANE_Value_Type::SANE_TYPE_STRING) op.val.sv = new std::string(get_string(i, desc->size)); options_.push_back(op); if (color_mode::is_me(desc->title)) opt_ind_color_mode_ = i; else if (dpi::is_me(desc->title)) opt_ind_dpi_ = i; else if (paper::is_me(desc->title)) opt_ind_paper_ = i; else if (scan_count::is_me(desc->title)) opt_ind_scann_count_ = MAKELPARAM(i, HIWORD(opt_ind_scann_count_)); else if (scan_count::is_parent(desc->title)) opt_ind_scann_count_ = MAKELPARAM(LOWORD(opt_ind_scann_count_), i); else if (text_direction::is_me(desc->title)) opt_ind_text_direction_ = i; else if (page::is_me(desc->title)) opt_ind_page_ = i; else if (auto_descrew::is_me(desc->title)) opt_ind_auto_descrew_ = i; else if (erase_black_frame::is_me(desc->title)) opt_ind_erase_black_frame_ = i; else if (filter::is_me(desc->title)) opt_ind_filter_ = i; else if (bright::is_me(desc->title)) opt_ind_bright_ = i; else if (contrast::is_me(desc->title)) opt_ind_contrast_ = i; else if (gamma::is_me(desc->title)) opt_ind_gamma_ = i; else if (ultrasonic::is_me(desc->title)) opt_ind_ultrasonic_ = i; else if (flip::is_me(desc->title)) opt_ind_flip_ = i; else if (rotate_bg::is_me(desc->title)) opt_ind_rotate_bkg_ = i; else if (fill_black_border::is_me(desc->title)) opt_ind_fill_blank_bkg_ = i; else if (edge_ident::is_me(desc->title)) opt_ind_edge_ident_ = i; else if (threshold::is_me(desc->title)) opt_ind_threshold_ = i; else if (bkg_filling_method::is_me(desc->title)) opt_ind_bkg_filling_method_ = i; else if (fill_hole::is_me(desc->title)) opt_ind_fill_hole_ = i; else if (fill_hole::is_ratio(desc->title)) opt_ind_fill_hole_ratio_ = i; else if (noise::is_me(desc->title)) opt_ind_noise_ = i; else if (noise::is_threshold(desc->title)) opt_ind_noise_threshold_ = i; else if (rid_red::is_me(desc->title, false)) opt_ind_rid_red_ = i; else if (rid_red::is_me(desc->title, true)) opt_ind_rid_red_hsv_ = i; else if (sharpen::is_me(desc->title)) opt_ind_sharpen_ = i; else if (screw::is_me(desc->title)) opt_ind_screw_detect_ = i; else if (screw::is_level(desc->title)) opt_ind_screw_detect_level_ = i; else if (staple::is_me(desc->title)) opt_ind_staple_ = i; else if (dogear::is_me(desc->title)) opt_ind_dogear_ = i; else if (sample::is_me(desc->title)) opt_ind_dark_sample_ = i; else if (split::is_me(desc->title)) opt_ind_split_ = i; else if (fade_bkg::is_me(desc->title)) opt_ind_fade_bkg_ = i; else if (fade_bkg::is_value(desc->title)) opt_ind_fade_bkg_val_ = i; else if (size_detect::is_me(desc->title)) opt_ind_size_detect_ = i; else if (multi_out::is_me(desc->title)) opt_ind_multi_out_ = i; } } void scanner::init_image_format(void) { if (fmt_) delete[](char*)fmt_; int len = sizeof(SANE_FinalImgFormat) + sizeof(BITMAPINFOHEADER); PBITMAPINFOHEADER pbi = NULL; fmt_ = (SANE_FinalImgFormat*)new char[len]; memset(fmt_, 0, len); fmt_->img_format = SANE_IMAGE_TYPE_BMP; pbi = (PBITMAPINFOHEADER)fmt_->detail; } std::string scanner::get_string(int opt, int bytes) { std::string ret(""); char* v = new char[bytes + 4]; SANE_Int afterdo = 0; memset(v, 0, bytes + 4); sane_invoker::invoke_sane_control_option(hdev_, opt, SANE_ACTION_GET_VALUE, v, &afterdo); ret = v; delete[] v; return ret; } bool scanner::get_boolean(int opt) { SANE_Int afterdo = 0; SANE_Bool v = 0; sane_invoker::invoke_sane_control_option(hdev_, opt, SANE_ACTION_GET_VALUE, &v, &afterdo); return v; } int scanner::get_integer(int opt) { SANE_Int afterdo = 0; SANE_Int v = 0; sane_invoker::invoke_sane_control_option(hdev_, opt, SANE_ACTION_GET_VALUE, &v, &afterdo); return v; } double scanner::get_double(int opt) { SANE_Int afterdo = 0; SANE_Fixed f = 0; sane_invoker::invoke_sane_control_option(hdev_, opt, SANE_ACTION_GET_VALUE, &f, &afterdo); return SANE_UNFIX(f); } int scanner::set_string(int opt, std::string& val, int size, SANE_Int* afterdo) { SANE_Int after = 0; char* buf = new char[size + 20]; int ret = 0; memset(buf, 0, size + 20); strcpy(buf, val.c_str()); ret = sane_invoker::invoke_sane_control_option(hdev_, opt, SANE_ACTION_SET_VALUE, buf, &after); val = buf; delete[] buf; if (afterdo) *afterdo = after; return ret; } int scanner::close(void) { if (hdev_) { sane_invoker::invoke_sane_close(hdev_); hdev_ = NULL; } return SCANNER_ERR_OK; } int scanner::start(void) { int ret = SCANNER_ERR_NOT_OPEN; ResetEvent(wait_img_); if (hdev_) ret = sane_invoker::invoke_sane_start(hdev_); scanning_ = ret == SANE_STATUS_GOOD; return ret; } int scanner::stop(void) { scanning_ = false; if (hdev_) sane_invoker::invoke_sane_cancel(hdev_); return SCANNER_ERR_OK; } void scanner::set_event_callback(void(*cb)(int, void*, unsigned int*, void*), void* param) { cb_param_ = param; event_cb_ = cb; } bool scanner::wait_image(DWORD milliseconds) { { std::lock_guard lock(lock_img_); if (img_.size()) return true; } if (!scanning_) { if (err_) { std::wstring tips(sane_invoker::utf82u(desc_.c_str())); MessageBoxW(NULL, tips.c_str(), L"\u9519\u8bef", MB_OK); } return false; } WaitForSingleObject(wait_img_, milliseconds); return img_.size() > 0; } int scanner::get_scanned_images(DWORD milliseconds) { if (milliseconds && !wait_image(milliseconds)) return 0; else { std::lock_guard lock(lock_img_); return img_.size(); } } scanned_img* scanner::take_first_image(void) // delete returned value, plz { std::lock_guard lock(lock_img_); scanned_img* img = NULL; if (img_.size()) { img = img_[0]; img_.erase(img_.begin()); ResetEvent(wait_img_); } return img; } bool scanner::get_first_image_header(SANE_Parameters* header) { std::lock_guard lock(lock_img_); if (img_.size()) { img_[0]->copy_header(header); header->bytes_per_line = img_[0]->line_bytes(); if (header->format == SANE_FRAME_RGB) header->depth *= 3; return true; } return false; } int scanner::last_error(void) { return err_; } SANE_Handle scanner::handle(void) { return hdev_; } bool scanner::is_online(void) { return online_; } void scanner::set_online(bool online) { online_ = online; } void scanner::put_image(SANE_Image* img, unsigned int* len) { std::lock_guard lock(lock_img_); img_.push_back(new scanned_img(img, fmt_)); SetEvent(wait_img_); if (event_cb_) event_cb_(SANE_EVENT_IMAGE_OK, (void*)img, len, cb_param_); } void scanner::scan_finished(const char* desc, int err) { desc_ = desc ? desc : "OK"; err_ = err; scanning_ = false; SetEvent(wait_img_); if (event_cb_) event_cb_(SANE_EVENT_SCAN_FINISHED, (void*)desc, (unsigned int*)&err, cb_param_); } // up to sane, we set the CAP_xxx according to settings display in UI ... bool scanner::get_value_info(int sn, std::string& type, value_limit& limit) { if (sn <= 0 || sn >= option_count_) return false; FIND_OPTION(sn); if (it == options_.end()) return false; type = scanner::type(it->desc->type); limit = scanner::limit(it->desc->constraint_type); return true; } bool scanner::get_value(int sn, std::list& values, std::string& now, std::string& init) { if (sn <= 0 || sn >= option_count_) return false; FIND_OPTION(sn); if (it == options_.end()) return false; if (scanner::type(it->desc->type) != "string" || scanner::limit(it->desc->constraint_type) != VAL_LIMIT_ENUM) return false; sane_trans::get_value_list(it->desc, &values); init = *it->val.sv; now = get_string(sn, it->desc->size); return true; } bool scanner::get_value(int sn, std::list& values, float& now, float& init) { if (sn <= 0 || sn >= option_count_) return false; FIND_OPTION(sn); if (it == options_.end()) return false; if (scanner::type(it->desc->type) != "float" || scanner::limit(it->desc->constraint_type) != VAL_LIMIT_ENUM) return false; std::list vals; sane_trans::get_value_list(it->desc, &vals); for(const auto& v : vals) values.push_back(v); init = it->val.dv; now = get_double(sn); return true; } bool scanner::get_value(int sn, std::list& values, bool& now, bool& init) { if (sn <= 0 || sn >= option_count_) return false; FIND_OPTION(sn); if (it == options_.end()) return false; values.push_back(false); values.push_back(true); now = get_boolean(sn); init = it->val.bv; return true; } bool scanner::get_value(int sn, int& now, int& init, int* lower, int* upper, int* step) { if (sn <= 0 || sn >= option_count_) return false; FIND_OPTION(sn); if (it == options_.end()) return false; if (scanner::type(it->desc->type) != "int") return false; bool ret = true; now = get_integer(sn); init = it->val.iv; if (lower && upper && step) { *lower = *upper = *step = 0; if (scanner::limit(it->desc->constraint_type) == VAL_LIMIT_RANGE) { *lower = it->desc->constraint.range->min; *upper = it->desc->constraint.range->max; *step = it->desc->constraint.range->quant; } else ret = false; } return ret; } bool scanner::get_value(int sn, float& now, float& init, float* lower, float* upper, float* step) { if (sn <= 0 || sn >= option_count_) return false; std::vector::iterator it = std::find(options_.begin(), options_.end(), sn); if (it == options_.end()) return false; if (scanner::type(it->desc->type) != "float") return false; bool ret = true; double ratio = sn == opt_ind_fill_hole_ratio_ ? 100.0f : 1.0f; now = get_double(sn) * ratio; init = it->val.dv * ratio; if (lower && upper && step) { *lower = *upper = *step = 0; if (scanner::limit(it->desc->constraint_type) == VAL_LIMIT_RANGE) { *lower = SANE_UNFIX(it->desc->constraint.range->min) * ratio; *upper = SANE_UNFIX(it->desc->constraint.range->max) * ratio; *step = SANE_UNFIX(it->desc->constraint.range->quant) * ratio; } else ret = false; } return ret; } int scanner::set_value(int sn, std::string val) { if (sn <= 0 || sn >= option_count_) return SCANNER_ERR_OUT_OF_RANGE; std::vector::iterator it = std::find(options_.begin(), options_.end(), sn); if (it == options_.end()) return SCANNER_ERR_NO_DATA; if (scanner::type(it->desc->type) != "string" || val.length() > it->desc->size) return SCANNER_ERR_INVALID_PARAMETER; char* buf = new char[it->desc->size + 20]; int ret = SCANNER_ERR_OK; SANE_Int afterdo = 0; memset(buf, 0, it->desc->size + 20); strcpy(buf, val.c_str()); ret = sane_invoker::invoke_sane_control_option(hdev_, sn, SANE_ACTION_SET_VALUE, buf, &afterdo); if (sn == opt_ind_paper_ && (ret == SCANNER_ERR_OK || ret == SCANNER_ERR_NOT_EXACT)) { auto_crop_ = paper::is_auto_crop(buf); } delete[] buf; return ret; } int scanner::set_value(int sn, bool val) { if (sn <= 0 || sn >= option_count_) return SCANNER_ERR_OUT_OF_RANGE; std::vector::iterator it = std::find(options_.begin(), options_.end(), sn); if (it == options_.end()) return SCANNER_ERR_NO_DATA; if (scanner::type(it->desc->type) != "bool" && scanner::type(it->desc->type) != "button") return SCANNER_ERR_INVALID_PARAMETER; int ret = SCANNER_ERR_OK; SANE_Int afterdo = 0; SANE_Bool v = val; ret = sane_invoker::invoke_sane_control_option(hdev_, sn, SANE_ACTION_SET_VALUE, &v, &afterdo); return ret; } int scanner::set_value(int sn, int val) { if (sn <= 0 || sn >= option_count_) return SCANNER_ERR_OUT_OF_RANGE; std::vector::iterator it = std::find(options_.begin(), options_.end(), sn); if (it == options_.end()) return SCANNER_ERR_NO_DATA; if (scanner::type(it->desc->type) != "int") return SCANNER_ERR_INVALID_PARAMETER; int ret = SCANNER_ERR_OK; SANE_Int afterdo = 0; SANE_Int v = val; ret = sane_invoker::invoke_sane_control_option(hdev_, sn, SANE_ACTION_SET_VALUE, &v, &afterdo); return ret; } int scanner::set_value(int sn, double val) { if (sn <= 0 || sn >= option_count_) return SCANNER_ERR_OUT_OF_RANGE; std::vector::iterator it = std::find(options_.begin(), options_.end(), sn); if (it == options_.end()) return SCANNER_ERR_NO_DATA; if (scanner::type(it->desc->type) != "float") return SCANNER_ERR_INVALID_PARAMETER; if (sn == opt_ind_fill_hole_ratio_) val /= 100.0f; int ret = SCANNER_ERR_OK; SANE_Int afterdo = 0; SANE_Fixed v = SANE_FIX(val); ret = sane_invoker::invoke_sane_control_option(hdev_, sn, SANE_ACTION_SET_VALUE, &v, &afterdo); return ret; } int scanner::twain_set_resolution(float dpi) { float l = .0f, u = .0f, n = .0f, i = .0f; if(opt_ind_dpi_ == -1 || !get_value(opt_ind_dpi_, n, i, &l, &u)) return dpi >= 200.0f && dpi <= 600.0f ? SCANNER_ERR_OK : SCANNER_ERR_INVALID_PARAMETER; if (dpi <= l || dpi >= u) return SCANNER_ERR_INVALID_PARAMETER; std::vector::iterator it = std::find(options_.begin(), options_.end(), opt_ind_dpi_); SANE_Fixed v = it->desc->type == SANE_TYPE_FIXED ? SANE_FIX(dpi) : dpi; SANE_Int afterdo = 0; int ret = sane_invoker::invoke_sane_control_option(hdev_, opt_ind_dpi_, SANE_ACTION_SET_VALUE, &v, &afterdo); dpi_ = it->desc->type == SANE_TYPE_FIXED ? SANE_UNFIX(v) : v; return ret; } float scanner::twain_get_resolution(float* init, std::vector* values, value_limit* limit) { if (opt_ind_dpi_ != -1) { std::vector< SANEOPTION>::iterator it = std::find(options_.begin(), options_.end(), opt_ind_dpi_); if (it != options_.end()) { if(init) *init = it->val.dv; if (limit) *limit = scanner::limit(it->desc->constraint_type); if (values && (it->desc->type == SANE_TYPE_FIXED || it->desc->type == SANE_TYPE_INT)) { if (it->desc->constraint_type == SANE_CONSTRAINT_WORD_LIST) { const SANE_Word* val = it->desc->constraint.word_list; for (SANE_Word i = 1; i < val[0]; ++i) values->push_back(it->desc->type == SANE_TYPE_FIXED ? SANE_UNFIX(val[i]) : val[i]); } else if (it->desc->constraint_type == SANE_CONSTRAINT_RANGE) { const SANE_Range* r = it->desc->constraint.range; values->push_back(it->desc->type == SANE_TYPE_FIXED ? SANE_UNFIX(r->min) : r->min); values->push_back(it->desc->type == SANE_TYPE_FIXED ? SANE_UNFIX(r->max) : r->max); } } } } if (values && values->empty()) { values->push_back(100.0f); values->push_back(600.0f); if (limit) *limit = VAL_LIMIT_RANGE; } return dpi_; } int scanner::twain_set_color_mode(int twain_pixel_type) { int ret = SCANNER_ERR_DEVICE_NOT_SUPPORT; for (const auto& v : options_) { if (v.ind != opt_ind_color_mode_) continue; std::string val(color_mode::from_twain_pixel_type(twain_pixel_type)); char* buf = new char[v.desc->size + 8]; SANE_Int afterdo = 0; memset(buf, 0, v.desc->size + 8); strcpy(buf, val.c_str()); ret = sane_invoker::invoke_sane_control_option(hdev_, v.ind, SANE_ACTION_SET_VALUE, buf, &afterdo); delete buf; break; } return SCANNER_ERR_OK; } int scanner::twain_get_color_mode(int* init, std::vector* values, value_limit* limit) { int tw_pixel_type = TWPT_RGB; for(const auto& v : options_) { if (v.ind != opt_ind_color_mode_) continue; if (v.desc->type == SANE_Value_Type::SANE_TYPE_STRING) { SANE_Int afterdo = 0; char* now = new char[v.desc->size + 8]; memset(now, 0, v.desc->size + 8); sane_invoker::invoke_sane_control_option(hdev_, v.ind, SANE_ACTION_GET_VALUE, now, &afterdo); tw_pixel_type = color_mode::to_twain_pixel_type(now); if (init) *init = color_mode::to_twain_pixel_type(v.val.sv->c_str()); if (values && limit) { std::list vals; if (sane_trans::get_value_list(v.desc, &vals)) { *limit = VAL_LIMIT_ENUM; for (const auto& var : vals) values->push_back(color_mode::to_twain_pixel_type(var.c_str())); } } delete[] now; } break; } if (values && values->empty()) { values->push_back(TWPT_BW); values->push_back(TWPT_GRAY); values->push_back(TWPT_RGB); if (limit) *limit = VAL_LIMIT_ENUM; } return tw_pixel_type; } int scanner::twain_set_auto_color_type(int type) { unsigned int len = sizeof(type); return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_AUTO_COLOR_TYPE, &type, &len); } int scanner::twain_get_paper_ind(void) { return opt_ind_paper_; } int scanner::twain_set_paper_lateral(bool lateral) { FIND_OPTION(opt_ind_paper_); if (it == options_.end()) return SCANNER_ERR_INVALID_PARAMETER; std::list vals; std::string now(get_string(opt_ind_paper_, it->desc->size)); size_t pos = now.find(paper::lateral_title()); int ret = SCANNER_ERR_OK; if (lateral) { if (!paper::is_lateral(now.c_str())) { now += paper::lateral_title(); sane_trans::get_value_list(it->desc, &vals); if (std::find(vals.begin(), vals.end(), now) == vals.end()) ret = SCANNER_ERR_INVALID_PARAMETER; else { ret = set_string(opt_ind_paper_, now, it->desc->size); } } } else { if (paper::is_lateral(now.c_str())) { size_t pos = now.find(paper::lateral_title()); if (pos != std::string::npos) { now.erase(pos); ret = set_string(opt_ind_paper_, now, it->desc->size); } } } return ret; } bool scanner::twain_is_paper_lateral(void) { FIND_OPTION(opt_ind_paper_); if (it == options_.end()) return false; std::string now(get_string(opt_ind_paper_, it->desc->size)); return paper::is_lateral(now.c_str()); } int scanner::twain_set_paper_auto_match_size(bool match) { FIND_OPTION(opt_ind_paper_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; std::string val(match ? paper::auto_size_title() : *it->val.sv); return set_string(opt_ind_paper_, val, it->desc->size); } bool scanner::twain_is_paper_auto_match_size(void) { FIND_OPTION(opt_ind_paper_); if (it == options_.end()) return false; std::string now(get_string(opt_ind_paper_, it->desc->size)); return paper::is_auto_size(now.c_str()); } int scanner::twain_set_scan_count(int count) { int ret = SCANNER_ERR_DEVICE_NOT_SUPPORT; if (opt_ind_scann_count_ != -1) { SANE_Int afterdo = 0; char buf[80] = { 0 }; strcpy(buf, scan_count::scan_continous_val().c_str()); if (count == -1) { ret = sane_invoker::invoke_sane_control_option(hdev_, HIWORD(opt_ind_scann_count_), SANE_ACTION_SET_VALUE, buf, &afterdo); } else { std::list vals; std::string now(""), init(""); get_value(HIWORD(opt_ind_scann_count_), vals, now, init); for (const auto& v : vals) { if (v != buf) { init = v; break; } } strcpy(buf, init.c_str()); ret = sane_invoker::invoke_sane_control_option(hdev_, HIWORD(opt_ind_scann_count_), SANE_ACTION_SET_VALUE, buf, &afterdo); if (ret == SANE_STATUS_GOOD) { SANE_Int v = count; ret = sane_invoker::invoke_sane_control_option(hdev_, LOWORD(opt_ind_scann_count_), SANE_ACTION_SET_VALUE, &v, &afterdo); } } } return ret; } int scanner::twain_get_scan_count(void) { int ret = -1; if (opt_ind_scann_count_ != -1) { char buf[80] = { 0 }; SANE_Int afterdo = 0, count = -1; ret = sane_invoker::invoke_sane_control_option(hdev_, HIWORD(opt_ind_scann_count_), SANE_ACTION_GET_VALUE, buf, &afterdo); if (scan_count::scan_continous_val() == buf) ret = -1; else { sane_invoker::invoke_sane_control_option(hdev_, LOWORD(opt_ind_scann_count_), SANE_ACTION_GET_VALUE, &count, &afterdo); ret = count; } } return ret; } int scanner::twain_set_final_format(SANE_ImageType type, void* param) { SANE_FinalImgFormat fmt; unsigned int len = sizeof(fmt); int ret = SCANNER_ERR_OK; fmt.img_format = type; fmt.detail = param; ret = sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_FINAL_IMAGE_FORMAT, &fmt, &len); if (ret == SCANNER_ERR_OK) { if (type == SANE_IMAGE_TYPE_JPG) jpeg_quality_ = (int)param; img_fmt_ = type; } return ret; } int scanner::twain_get_jpeg_quality(void) { return jpeg_quality_; } SANE_ImageType scanner::get_final_format(void) { return img_fmt_; } int scanner::twain_set_final_compression(int compression) { SANE_Compression compr; unsigned int len = sizeof(compr); int ret = SCANNER_ERR_OK; compr.compression = (SANE_CompressionType)compression; compr.detail = NULL; ret = sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_FINAL_COMPRESSION, &compr, &len); if (ret == SCANNER_ERR_OK || ret == SCANNER_ERR_NOT_EXACT) compression_ = compr.compression; return ret; } int scanner::twain_get_final_compression(int* init, std::vector* values) { if (init || values) { SANE_Int vals[80] = { 0 }; unsigned int len = _countof(vals) - 4; int ret = sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_FINAL_COMPRESSION, vals, &len); if (ret == SCANNER_ERR_OK) { if (init) *init = vals[1]; if (values) { for (int i = 0; i < vals[0]; ++i) values->push_back(vals[3 + i]); } } compression_ = (SANE_CompressionType)vals[2]; } return compression_; } int scanner::twain_set_text_direction(double degree) { int ret = SCANNER_ERR_INVALID_PARAMETER; std::string val(text_direction::from_twain_angle(degree)); if (val.length()) { FIND_OPTION(opt_ind_text_direction_); if (it != options_.end()) { ret = set_string(opt_ind_text_direction_, val, it->desc->size); } } return ret; } double scanner::twain_get_text_direction(double* init, std::list* vals, value_limit* limit) { double dir = .0f; FIND_OPTION(opt_ind_text_direction_); if (it != options_.end()) { if (init) *init = text_direction::to_twain_angle(it->val.sv->c_str()); if (vals) { std::list values; sane_trans::get_value_list(it->desc, &values); for (const auto& v : values) vals->push_back(text_direction::to_twain_angle(v.c_str())); if (limit) *limit = scanner::limit(it->desc->constraint_type); } dir = text_direction::to_twain_angle(get_string(opt_ind_text_direction_, it->desc->size).c_str()); } return dir; } int scanner::twain_set_text_auto_matic(bool am) { FIND_OPTION(opt_ind_text_direction_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; std::string val(am ? text_direction::auto_val() : text_direction::from_twain_angle(0)); return set_string(opt_ind_text_direction_, val, it->desc->size); } bool scanner::twain_is_text_auto_matic(void) { FIND_OPTION(opt_ind_text_direction_); if (it == options_.end()) return false; return text_direction::is_auto(get_string(opt_ind_text_direction_, it->desc->size).c_str()); } int scanner::twain_set_page_duplex(bool dup) { std::string val(page::from_duplex(dup)); FIND_OPTION(opt_ind_page_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; return set_string(opt_ind_page_, val, it->desc->size); } bool scanner::twain_is_page_duplex(void) { FIND_OPTION(opt_ind_page_); if (it == options_.end()) return true; std::string now(get_string(opt_ind_page_, it->desc->size)); return page::is_duplex(now.c_str()); } int scanner::twain_set_page_discarding_blank_page(bool discard, bool receipt) { std::string val(discard ? page::discard_blank_page_title(receipt) : page::from_duplex(true)); FIND_OPTION(opt_ind_page_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; return set_string(opt_ind_page_, val, it->desc->size); } bool scanner::twain_is_page_discarding_blank_page(bool receipt) { FIND_OPTION(opt_ind_page_); if (it == options_.end()) return true; std::string now(get_string(opt_ind_page_, it->desc->size)); return page::is_discard_blank_page(now.c_str(), receipt); } int scanner::twain_set_page_fold(bool fold) { std::string val(page::fold_page_title()); FIND_OPTION(opt_ind_page_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; return set_string(opt_ind_page_, val, it->desc->size); } bool scanner::twain_is_page_fold(void) { FIND_OPTION(opt_ind_page_); if (it == options_.end()) return true; std::string now(get_string(opt_ind_page_, it->desc->size)); return page::is_fold(now.c_str()); } int scanner::twain_set_auto_descrew(bool enable) { FIND_OPTION(opt_ind_auto_descrew_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; SANE_Bool v = enable; SANE_Int after = 0; return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_auto_descrew_, SANE_ACTION_SET_VALUE, &v, &after); } bool scanner::twain_is_auto_descrew(void) { return get_boolean(opt_ind_auto_descrew_); } int scanner::twain_set_erase_black_frame(bool erase) { FIND_OPTION(opt_ind_erase_black_frame_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; SANE_Bool v = erase; SANE_Int after = 0; return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_erase_black_frame_, SANE_ACTION_SET_VALUE, &v, &after); } bool scanner::twain_is_erase_black_frame(bool* init) { FIND_OPTION(opt_ind_erase_black_frame_); if (it == options_.end()) return false; if (init) *init = it->val.bv; return get_boolean(opt_ind_erase_black_frame_); } int scanner::twain_set_filter(int tw_filter, bool enhance) { FIND_OPTION(opt_ind_filter_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; std::string val(filter::from_filter_type(tw_filter, enhance)); return set_string(opt_ind_filter_, val, it->desc->size); } int scanner::twain_get_filter(bool enhance) { FIND_OPTION(opt_ind_filter_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; std::string val(get_string(opt_ind_filter_, it->desc->size)); return filter::to_filter_type(val.c_str(), enhance); } int scanner::twain_set_bright(double bright) { FIND_OPTION(opt_ind_bright_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; SANE_Fixed v = SANE_FIX(bright); SANE_Int after = 0; return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_bright_, SANE_ACTION_SET_VALUE, &v, &after); } double scanner::twain_get_bright(double* init, double* lower, double* upper, double* step) { FIND_OPTION(opt_ind_bright_); if (it == options_.end()) return .0f; if(lower && upper) sane_trans::get_value_range(it->desc, lower, upper); if (init) *init = it->val.dv; if (step) *step = 1.0f; return get_double(opt_ind_bright_); } int scanner::twain_set_contrast(double bright) { FIND_OPTION(opt_ind_contrast_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; SANE_Fixed v = SANE_FIX(bright); SANE_Int after = 0; return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_contrast_, SANE_ACTION_SET_VALUE, &v, &after); } double scanner::twain_get_contrast(double* init, double* lower, double* upper, double* step) { FIND_OPTION(opt_ind_contrast_); if (it == options_.end()) return .0f; if (lower && upper) sane_trans::get_value_range(it->desc, lower, upper); if (init) *init = it->val.dv; if (step) *step = 1.0f; return get_double(opt_ind_contrast_); } int scanner::twain_set_gamma(double bright) { FIND_OPTION(opt_ind_gamma_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; SANE_Fixed v = SANE_FIX(bright); SANE_Int after = 0; return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_gamma_, SANE_ACTION_SET_VALUE, &v, &after); } double scanner::twain_get_gamma(double* init, double* lower, double* upper, double* step) { FIND_OPTION(opt_ind_gamma_); if (it == options_.end()) return .0f; if (lower && upper) sane_trans::get_value_range(it->desc, lower, upper); if (init) *init = it->val.dv; if (step) *step = 1.0f; return get_double(opt_ind_gamma_); } int scanner::twain_set_ultrasonic_check(bool check) { FIND_OPTION(opt_ind_ultrasonic_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; SANE_Bool v = check; SANE_Int after = 0; return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_ultrasonic_, SANE_ACTION_SET_VALUE, &v, &after); } bool scanner::twain_is_ultrasonic_check(bool* init) { FIND_OPTION(opt_ind_ultrasonic_); if (it == options_.end()) return false; if (init) *init = it->val.bv; return get_boolean(opt_ind_ultrasonic_); } bool scanner::twain_is_auto_crop(void) { return auto_crop_; } bool scanner::twain_is_paper_on(void) { SANE_Bool on = false; unsigned int len = sizeof(on); sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_PAPER_ON, &on, &len); return on; } int scanner::twain_get_device_code(char* buf, unsigned int len) { return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_DEVICE_CODE, buf, &len); } int scanner::twain_set_sharpen(int sharpen) { std::string val(sharpen::from_type(sharpen)); FIND_OPTION(opt_ind_sharpen_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; return set_string(opt_ind_sharpen_, val, it->desc->size); } int scanner::twain_get_sharpen(void) { FIND_OPTION(opt_ind_sharpen_); if (it == options_.end()) return SHARPEN_NONE; std::string now(get_string(opt_ind_sharpen_, it->desc->size)); return sharpen::to_type(now.c_str()); } int scanner::twain_get_serial_num(char buf[]) { unsigned int len = 240; return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_SERIAL, buf, &len); } int scanner::twain_get_hareware_version(char buf[]) { unsigned int len = 240; return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_HARDWARE_VERSION, buf, &len); } int scanner::twain_get_ip(char buf[]) { unsigned int len = 240; return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_IP, buf, &len); } int scanner::twain_get_dogear_distance(void) { SANE_Int v = 0; unsigned int len = sizeof(v); sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_DOGEAR_DISTANCE, &v, &len); return v; } int scanner::twain_set_dogear_distance(int dist) { SANE_Int v = dist; unsigned int len = sizeof(v); return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_DOGEAR_DISTANCE, &v, &len); } int scanner::twain_set_power_level(int level) { SANE_Power v = (SANE_Power)level; unsigned int len = sizeof(v); return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_POWER_LEVEL, &v, &len); } int scanner::twain_get_power_level(void) { SANE_Power v = SANE_POWER_NONE; unsigned int len = sizeof(v); sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_POWER_LEVEL, &v, &len); return v; } int scanner::twain_set_to_be_scan(bool yes) { SANE_Bool v = yes; unsigned int len = sizeof(v); return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_SCAN_WHEN_PAPER_ON, &v, &len); } bool scanner::twain_get_to_be_scan(void) { SANE_Bool v = 0; unsigned int len = sizeof(v); sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_SCAN_WHEN_PAPER_ON, &v, &len); return v; } int scanner::twain_set_scan_with_hole(bool yes) { SANE_Bool v = yes; unsigned int len = sizeof(v); return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_SCAN_WITH_HOLE, &v, &len); } bool scanner::twain_get_scan_with_hole(void) { SANE_Bool v = 0; unsigned int len = sizeof(v); sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_SCAN_WITH_HOLE, &v, &len); return v; } int scanner::twain_set_multioutput_type(int type) { FIND_OPTION(opt_ind_multi_out_); if (it == options_.end()) return SCANNER_ERR_NOT_OPEN; std::string val(multi_out::from_twain_type(type)); return set_string(opt_ind_multi_out_, val, it->desc->size); } int scanner::twain_get_multioutput_type(void) { FIND_OPTION(opt_ind_multi_out_); if (it == options_.end()) return MULTI_OUT_NONE; std::string now(get_string(opt_ind_multi_out_, it->desc->size)); return multi_out::to_twain_type(now.c_str()); } int scanner::twain_get_flip_ind(void) { return opt_ind_flip_; } int scanner::twain_get_rotate_bkg_ind(void) { return opt_ind_rotate_bkg_; } int scanner::twain_get_fill_black_bkg_ind(void) { return opt_ind_fill_blank_bkg_; } int scanner::twain_get_edge_ident_ind(void) { return opt_ind_edge_ident_; } int scanner::twain_get_threshold_ind(void) { return opt_ind_threshold_; } int scanner::twain_bkg_filling_method_ind(void) { return opt_ind_bkg_filling_method_; } int scanner::twain_fill_hole_ind(void) { return opt_ind_fill_hole_; } int scanner::twain_fill_hole_ratio_ind(void) { return opt_ind_fill_hole_ratio_; } int scanner::twain_detach_noise_ind(void) { return opt_ind_noise_; } int scanner::twain_detach_noise_threshold_ind(void) { return opt_ind_noise_threshold_; } int scanner::twain_rid_red_ind(void) { return opt_ind_rid_red_; } int scanner::twain_rid_red_hsv_ind(void) { return opt_ind_rid_red_hsv_; } int scanner::twain_screw_detect_ind(void) { return opt_ind_screw_detect_; } int scanner::twain_screw_detect_level_ind(void) { return opt_ind_screw_detect_level_; } int scanner::twain_staple_detect_ind(void) { return opt_ind_staple_; } int scanner::twain_dogear_detect_ind(void) { return opt_ind_dogear_; } int scanner::twain_dark_sample_ind(void) { return opt_ind_dark_sample_; } int scanner::twain_image_split_ind(void) { return opt_ind_split_; } int scanner::twain_fade_bkground_ind(void) { return opt_ind_fade_bkg_; } int scanner::twain_fade_bkground_val_ind(void) { return opt_ind_fade_bkg_val_; } int scanner::twain_size_detect_ind(void) { return opt_ind_size_detect_; }