#include "stdafx.h" #ifdef max #undef max #endif #ifdef min #undef min #endif #include #include "huagaods.hpp" #include "twglue.hpp" #include "resource.h" #include "CTwainUI.h" #include "CIndicatorDlg.h" #include "Device/PublicFunc.h" #include "Device/GScanO200.h" #include "Device/filetools.h" #include "Device/GScanVirtual.h" #include #include #include "Device/JsonConfig.h" //custom define caps enum enum class CapTypeEx : unsigned short { Base = 0x8000,//custom caps enum value must bigger than CapTypeEx::Base value,otherwise might make conflict TwEx_IMultiOutputRed = 0x8026, TwEx_IFillHole = 0x8018, TwEx_IFillHoleRatio = 0x8092, TwEx_IFillBackground = 0x8004, TwEx_IBackRotate180 = 0x8005, TwEx_IAutoDiscardBlankVince = 0x8091, TwEx_IEnhanceColor = 0x8007, TwEx_HardwareVersion = 0x8025, TwEx_ScrewDetectEnable = 0x8006, TwEx_ScrewLevel = 0x8021, TwEx_Sharpen = 0x8022, TwEx_DBAreaNum =0x8027, TwEx_DBDevnMax = 0x8028, TwEx_StableDetectEnable = 0x8090 }; using namespace Twpp; using namespace std::placeholders; #define TWPP_ENTRY_MFC(SourceClass)\ extern "C" TWPP_DETAIL_EXPORT Twpp::ReturnCode TWPP_DETAIL_CALLSTYLE \ DS_Entry(Twpp::Identity* origin, Twpp::DataGroup dg, Twpp::Dat dat, Twpp::Msg msg, void* data){\ AFX_MANAGE_STATE(AfxGetStaticModuleState()); \ static_assert(\ std::is_base_of, SourceClass>::value ||\ std::is_base_of, SourceClass>::value,\ "Class " #SourceClass " is not derived from SourceFromThis."\ );\ return SourceClass::entry(origin, dg, dat, msg, data);\ } TWPP_ENTRY_MFC(HuagaoDs) static constexpr const Identity srcIdent( Version(3, 3, Language::English, Country::CzechRepublic, "v3.3.1.6"), DataGroup::Image, "宁波华高信息科技有限公司", "G200 Series", "HUAGOSCAN TWAIN" #if defined(_MSC_VER) "" #elif defined(__GNUC__) " GCC" #elif defined(__clang__) " CLang" #endif ); // lets just simulate uniform resolution for both axes static constexpr UInt32 RESOLUTIONX = 85; static std::unique_ptr application(new CWinApp()); static list resList = { 100.0,150.0,200.0,240.0,300.0 }; static list paperSizeList = { (UInt16)PaperSize::A3,(UInt16)PaperSize::A4,(UInt16)PaperSize::A5,(UInt16)PaperSize::A6, (UInt16)PaperSize::IsoB4,(UInt16)PaperSize::IsoB5,(UInt16)PaperSize::IsoB6, (UInt16)PaperSize::UsLetter,(UInt16)PaperSize::UsLegal,(UInt16)PaperSize::UsLedger, (UInt16)PaperSize::MaxSize,(UInt16)PaperSize::None,(UInt16)PaperSize::UsStatement }; static list imageRotateList = {0.0,90.0,180.0,270.0}; static map noticeMsgMap = { {OPEN_COVER,_T("扫描仪开盖")}, {NO_FEED,_T("无纸!")}, {FEED_IN_ERROR,_T("搓纸失败!")}, {PAPER_JAM,_T("卡纸!")}, {DETECT_DOUBLE_FEED,_T("双张!")}, {DETECT_STAPLE,_T("订书针!")}, {PAPER_SKEW,_T("纸张歪斜!")}, {COUNT_MODE,_T("计数模式,请退出计数模式!")}, {HARDWARE_ERROR,_T("硬件错误")}, {FPGA_ERROR,_T("FPGA 异常")}, {USB_DISCONNECTED,_T("USB连接异常")} }; static void DeleteWnd(CDialog* pWnd) { if (pWnd && pWnd->GetSafeHwnd()) { pWnd->DestroyWindow(); delete pWnd; } } static std::unique_ptr guiTwain(nullptr, DeleteWnd); #if TWPP_DETAIL_OS_WIN static std::unique_ptr guiBridge(nullptr, DeleteWnd); #endif static std::unique_ptr guiIndicator(nullptr, DeleteWnd); #ifndef HG_VIRTUAL static std::unique_ptr scanner(new GScanO200()); #else static std::unique_ptr scanner(new GScanVirtual()); #endif HuagaoDs::HuagaoDs() : m_scanparam(new GScanCap) { /*string ss1= getOSInfo(); string ss2=getManufactureID(); string ss3=getCpuType(); string ss4= getMemoryInfo(); DWORD dwNum; CString aas[10]; GetDiskInfo(dwNum,aas);*/ initGScanCap(); } HuagaoDs::~HuagaoDs() { } const Identity& HuagaoDs::defaultIdentity() noexcept{ // remember, we return a reference, therefore the identity must not be placed on the stack of this method return srcIdent; } Result HuagaoDs::call(const Identity& origin, DataGroup dg, Dat dat, Msg msg, void* data){ try { // we can override almost anything from SourceFromThis, even the top-most source instance call return Base::call(origin, dg, dat, msg, data); } catch (const CapabilityException&){ return badValue(); } } // some helper functions to handle capability stuff template static Result oneValGet(Msg msg, Capability& data, const T& value){ switch (msg){ case Msg::Get: case Msg::GetCurrent: case Msg::GetDefault: data = Capability::createOneValue(data.type(), value); return {}; default: return {ReturnCode::Failure, ConditionCode::CapBadOperation}; } } static Result oneValGetString(Msg msg, Capability& data, std::string value) { Str255 str; str.setData(value.c_str(), value.size()); return oneValGet(msg, data, str); } template static Result enmGet(Msg msg, Capability& data, const T& value){ switch (msg){ case Msg::Get: data = Capability::createEnumeration(data.type(), {value}); return {}; case Msg::GetCurrent: case Msg::GetDefault: data = Capability::createOneValue(data.type(), value); return {}; default: return {ReturnCode::Failure, ConditionCode::CapBadOperation}; } } template static Result oneValGetSet(Msg msg, Capability& data, T& value, const T& def){ switch (msg){ case Msg::Reset: value = def; // fallthrough case Msg::Get: case Msg::GetCurrent: data = Capability::createOneValue(data.type(), value); return {}; case Msg::GetDefault: data = Capability::createOneValue(data.type(), def); return {}; case Msg::Set: value = data.currentItem(); return {}; default: return {ReturnCode::Failure, ConditionCode::CapBadOperation}; } } template static Result oneValGetSetConst(Msg msg, Capability& data, const T& def){ switch (msg){ case Msg::Get: case Msg::GetCurrent: case Msg::GetDefault: case Msg::Reset: data = Capability::createOneValue(data.type(), def); return {}; case Msg::Set: return data.currentItem() == def ? Result() : Result(ReturnCode::Failure, ConditionCode::BadValue); default: return {ReturnCode::Failure, ConditionCode::CapBadOperation}; } } template static Result enmGetSetConst(Msg msg, Capability& data, const T& def){ switch (msg){ case Msg::Get: data = Capability::createEnumeration(data.type(), {def}); return {}; case Msg::GetCurrent: case Msg::GetDefault: case Msg::Reset: data = Capability::createOneValue(data.type(), def); return {}; case Msg::Set: return data.currentItem() == def ? Result() : Result(ReturnCode::Failure, ConditionCode::BadValue); default: return {ReturnCode::Failure, ConditionCode::CapBadOperation}; } } /// Shortcut for Result(RC::Failure, CC::CheckDeviceOnline). static constexpr Result checkDeviceOnline() noexcept { return { ReturnCode::Failure,ConditionCode::CheckDeviceOnline }; } //void HuagaoDs::updateScanParam() //{ // //} Result HuagaoDs::capCommon(const Identity&, Msg msg, Capability& data){ auto it = m_caps.find(data.type()); if (it != m_caps.end()){ return (it->second)(msg, data); } return capUnsupported(); } Result HuagaoDs::capabilityGet(const Identity& origin, Capability& data){ return capCommon(origin, Msg::Get, data); } Result HuagaoDs::capabilityGetCurrent(const Identity& origin, Capability& data){ return capCommon(origin, Msg::GetCurrent, data); } Result HuagaoDs::capabilityGetDefault(const Identity& origin, Capability& data){ return capCommon(origin, Msg::GetDefault, data); } Result HuagaoDs::capabilityQuerySupport(const Identity&, Capability& data){ auto it = m_query.find(data.type()); MsgSupport sup = it != m_query.end() ? it->second : msgSupportEmpty; data = Capability::createOneValue(data.type(), sup); return success(); } Result HuagaoDs::capabilityReset(const Identity& origin, Capability& data){ return capCommon(origin, Msg::Reset, data); } Result HuagaoDs::capabilityResetAll(const Identity& origin){ for (auto& pair : m_query){ if ((pair.second & MsgSupport::Reset) != msgSupportEmpty){ Capability dummyCap(pair.first); capCommon(origin, Msg::Reset, dummyCap); } } return success(); } Result HuagaoDs::capabilitySet(const Identity& origin, Capability& data){ return capCommon(origin, Msg::Set, data); } Result HuagaoDs::eventProcess(const Identity&, Event& event){ // Qt needs to process its events, otherwise the GUI will appear frozen // this is Windows-only method, Linux and macOS behave differently if (guiTwain) { // // QApplication::processEvents(); - TODO: needs more investigation; results in freeze when attempting to scan using old DSM // QApplication::sendPostedEvents(); guiTwain->SendMessage((UINT)(event.message())); } event.setMessage(Msg::Null); return {ReturnCode::NotDsEvent, ConditionCode::Success}; } Result HuagaoDs::identityOpenDs(const Identity&){ bmpData.resize(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)); BITMAPINFOHEADER& bmInfo = *((BITMAPINFOHEADER*)header()); bmInfo.biHeight = 2000; bmInfo.biWidth = 2000; bmInfo.biBitCount = 24; updataGscanCap(); scanner->open(0x064B, 0x7823); if (!scanner->IsConnected()) { MessageBox(NULL, _T("未找到扫描仪!请检查电源或者USB连接线是否接通!"), _T("提示"), MB_SYSTEMMODAL | MB_OK | MB_ICONINFORMATION); return checkDeviceOnline(); } // init caps // there are caps a minimal source must support // query -> says which operations a cap supports // caps -> has handler for each specific cap m_query[CapType::SupportedCaps] = msgSupportGetAll; m_caps[CapType::SupportedCaps] = [this](Msg msg, Capability& data){ switch (msg){ case Msg::Get: case Msg::GetCurrent: case Msg::GetDefault: { data = Capability::createArray(m_caps.size()); auto arr = data.array(); UInt32 i = 0; for (const auto& kv : m_caps){ arr[i] = kv.first; i++; } return success(); } default: return capBadOperation(); } }; m_query[CapType::UiControllable] = msgSupportGetAll; m_caps[CapType::UiControllable] = std::bind(enmGet, _1, _2, Bool(true)); m_query[CapType::DeviceOnline] = msgSupportGetAll; m_caps[CapType::DeviceOnline] = std::bind(enmGet, _1, _2, Bool(scanner->IsConnected())); m_query[CapType::XferCount] = msgSupportGetAllSetReset; m_caps[CapType::XferCount] = [this](Msg msg, Capability& data) -> Result{ if (msg == Msg::Set){ auto item = data.currentItem(); if (item > 65535 || item < -1){ return badValue(); } m_capXferCount = item; return success(); } auto ret = oneValGetSet(msg, data, m_capXferCount, -1); if (Twpp::success(ret) && m_capXferCount == 0){ m_capXferCount = -1; m_scanparam->scannum = m_capXferCount; return {ReturnCode::CheckStatus, ConditionCode::BadValue}; } return ret; }; m_query[CapType::ICompression] = msgSupportGetAllSetReset; m_caps[CapType::ICompression] = std::bind(enmGetSetConst, _1, _2, Compression::None); m_query[CapType::IBitDepth] = msgSupportGetAllSetReset; m_caps[CapType::IBitDepth] = std::bind(enmGetSetConst, _1, _2, UInt16(header()->biBitCount)); m_query[CapType::IBitOrder] = msgSupportGetAllSetReset; m_caps[CapType::IBitOrder] = std::bind(enmGetSetConst, _1, _2, BitOrder::MsbFirst); m_query[CapType::IPlanarChunky] = msgSupportGetAllSetReset; m_caps[CapType::IPlanarChunky] = std::bind(enmGetSetConst, _1, _2, PlanarChunky::Chunky); m_query[CapType::IPhysicalWidth] = msgSupportGetAll; m_caps[CapType::IPhysicalWidth] = std::bind(oneValGet, _1, _2, Fix32(static_cast(header()->biWidth) / RESOLUTIONX)); m_query[CapType::IPhysicalHeight] = msgSupportGetAll; m_caps[CapType::IPhysicalHeight] = std::bind(oneValGet, _1, _2, Fix32(static_cast(header()->biHeight) / RESOLUTIONX)); m_query[CapType::IPixelFlavor] = msgSupportGetAllSetReset; m_caps[CapType::IPixelFlavor] = std::bind(enmGetSetConst, _1, _2, PixelFlavor::Chocolate); m_query[CapType::IPixelType] = msgSupportGetAllSetReset; m_caps[CapType::IPixelType] = [this](Msg msg, Capability& data) -> Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration( { PixelType::BlackWhite, PixelType::Gray, PixelType::Rgb}, (int)(m_scanparam->pixtype), (int)PixelType::Gray); return success(); case Msg::Reset: m_scanparam->pixtype = (byte)PixelType::Gray; case Msg::GetCurrent: data = Capability::createOneValue((PixelType)(m_scanparam->pixtype)); return success(); case Msg::GetDefault: data = Capability::createOneValue(PixelType::Gray); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->pixtype = (int)mech; return success(); } default: return capBadOperation(); } }; m_query[CapType::IUnits] = msgSupportGetAllSetReset; m_caps[CapType::IUnits] = std::bind(enmGetSetConst, _1, _2, Unit::Inches); m_query[CapType::IXferMech] = msgSupportGetAllSetReset; m_caps[CapType::IXferMech] = [this](Msg msg, Capability& data) -> Result{ switch (msg){ case Msg::Get: data = Capability::createEnumeration( {XferMech::Native, XferMech::Memory}, m_capXferMech == XferMech::Native ? 0 : 1, 0); return success(); case Msg::Reset: m_capXferMech = XferMech::Native; // fallthrough case Msg::GetCurrent: data = Capability::createOneValue(m_capXferMech); return success(); case Msg::GetDefault: data = Capability::createOneValue(XferMech::Native); return success(); case Msg::Set: { auto mech = data.currentItem(); if (mech == XferMech::Native || mech == XferMech::Memory){ m_capXferMech = mech; return success(); } else { return badValue(); } } default: return capBadOperation(); } }; m_query[CapType::IXResolution] = msgSupportGetAllSetReset; m_caps[CapType::IXResolution] = [this](Msg msg, Capability& data){ switch (msg){ case Msg::Get: { int index = -1; std::list::iterator it = resList.begin(); int i = 0; for (it,i; it != resList.end(); ++it, i++) { if (*it == m_scanparam->resolution_dst) { index = i; break; } } data = Capability::createEnumeration(data.type(), { Fix32(100.0),Fix32(150.0),Fix32(200.0),Fix32(240.0),Fix32(300.0) }, index==-1?2:index, 2); return success(); } case Msg::GetCurrent: { data = Capability::createOneValue(data.type(), Fix32(m_scanparam->resolution_dst)); return success(); } case Msg::GetDefault: case Msg::Reset:{ data = Capability::createOneValue(data.type(), Fix32(200.0)); return success(); } case Msg::Set:{ auto res = data.currentItem(); std::list::iterator resIter= resList.begin(); bool contains = false; for (resIter; resIter != resList.end(); resIter++) { if (*resIter == res) { contains = true; break; } } if (contains) { m_scanparam->resolution_dst = (float)res; return success(); } return badValue(); } default: return capBadOperation(); } }; m_query[CapType::IYResolution] = msgSupportGetAllSetReset; m_caps[CapType::IYResolution] = m_caps[CapType::IXResolution]; m_query[CapType::IXNativeResolution] = msgSupportGetAll; m_caps[CapType::IXNativeResolution] = std::bind(enmGet, _1, _2, Fix32(200.0)); m_query[CapType::IYNativeResolution] = msgSupportGetAll; m_caps[CapType::IYNativeResolution] = m_caps[CapType::IXNativeResolution]; m_query[CapType::FeederLoaded] = msgSupportGetAll; m_caps[CapType::FeederLoaded] = std::bind(enmGet, _1, _2, Bool(scanner->Get_Scanner_PaperOn())); m_query[CapType::ISupportedSizes] = msgSupportGetAllSetReset; m_caps[CapType::ISupportedSizes] = [this](Msg msg, Capability& data) { switch (msg) { case Msg::Get: { int index = -1; std::list::iterator it = paperSizeList.begin(); int i = 0; for (it, i; it != paperSizeList.end(); ++it, i++) { if (*it == m_scanparam->papertype) { index = i; break; } } data = Capability::createEnumeration(data.type(), { PaperSize::A3,PaperSize::A4,PaperSize::A5, PaperSize::A6,PaperSize::IsoB4,PaperSize::IsoB5, PaperSize::IsoB6,PaperSize::UsLetter,PaperSize::UsLegal, PaperSize::UsLedger,PaperSize::MaxSize,PaperSize::None}, index == -1 ? 0 : index, 0); return success(); } case Msg::GetCurrent: data = Capability::createOneValue(data.type(), UInt16(m_scanparam->papertype)); return success(); case Msg::GetDefault: case Msg::Reset: data = Capability::createOneValue(data.type(), UInt16(PaperSize::A3)); return success(); case Msg::Set: { auto res = data.currentItem(); std::list::iterator resIter = paperSizeList.begin(); bool contains = false; for (resIter; resIter != paperSizeList.end(); resIter++) { if (*resIter == res) { contains = true; break; } } if (contains) { m_scanparam->papertype = (byte)res; return success(); } return badValue(); } default: return capBadOperation(); } }; m_query[CapType::IOrientation] = msgSupportGetAllSetReset; m_caps[CapType::IOrientation] = [this](Msg msg, Capability& data) -> Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration( { Orientation::Portrait, Orientation::Landscape,Orientation::AutoText}, m_scanparam->paperAlign,0); return success(); case Msg::Reset: m_scanparam->paperAlign = PaperAlign::Rot0; case Msg::GetCurrent: data = Capability::createOneValue((Orientation)(m_scanparam->paperAlign)); return success(); case Msg::GetDefault: data = Capability::createOneValue(Orientation::Portrait); return success(); case Msg::Set: { auto mech = data.currentItem(); if(mech== Orientation::Landscape|| mech == Orientation::Portrait|| mech== Orientation::AutoText){ m_scanparam->paperAlign =(PaperAlign)mech; return success(); } return badValue(); } default: return capBadOperation(); } }; m_query[CapType::IRotation] = msgSupportGetAllSetReset; m_caps[CapType::IRotation] = [this](Msg msg, Capability& data) -> Result { switch (msg) { case Msg::Get:{ int index = -1; std::list::iterator it = imageRotateList.begin(); int i = 0; for (it, i; it != imageRotateList.end(); ++it, i++) { if (*it == m_scanparam->imageRotateDegree) { index = i; break; } } data = Capability::createEnumeration( {Fix32(0.0),Fix32(90.0),Fix32(180.0),Fix32(270.0)}, index==-1?0:index, 0); return success(); } case Msg::Reset: m_scanparam->imageRotateDegree = 0.0; case Msg::GetCurrent: data = Capability::createOneValue(Fix32(m_scanparam->imageRotateDegree)); return success(); case Msg::GetDefault: data = Capability::createOneValue(Fix32(0.0)); return success(); case Msg::Set: { auto res = data.currentItem(); std::list::iterator resIter = imageRotateList.begin(); bool contains = false; for (resIter; resIter != imageRotateList.end(); resIter++) { if (*resIter == res) { contains = true; break; } } if (contains) { m_scanparam->imageRotateDegree = (float)res; return success(); } return badValue(); } default: return capBadOperation(); } }; m_query[CapType::SerialNumber] = msgSupportGetAll; m_caps[CapType::SerialNumber] = std::bind(oneValGetString,_1,_2,scanner->GetSerialNum()); m_query[(CapType)(CapTypeEx::TwEx_HardwareVersion)] = msgSupportGetAll; m_caps[(CapType)(CapTypeEx::TwEx_HardwareVersion)] = std::bind(oneValGetString, _1, _2, scanner->GetFWVersion()); m_query[CapType::Indicators] = msgSupportGetAllSetReset; m_caps[CapType::Indicators] = [this](Msg msg, Capability& data) -> Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration( { Bool(), Bool(true)}, Bool(m_bIndicator)); return success(); case Msg::Reset: m_bIndicator = true; // fallthrough case Msg::GetCurrent: data = Capability::createOneValue(m_bIndicator); return success(); case Msg::GetDefault: data = Capability::createOneValue(false); return success(); case Msg::Set: { auto show = data.currentItem(); m_bIndicator = show; return success(); } default: return capBadOperation(); } }; m_query[CapType::EnableDsUiOnly] = msgSupportGetAll; m_caps[CapType::EnableDsUiOnly] = std::bind(enmGet, _1, _2, Bool(true)); m_query[CapType::PaperDetectable] = msgSupportGetAll; m_caps[CapType::PaperDetectable] = std::bind(enmGet, _1, _2, Bool(true)); m_query[CapType::FeederEnabled] = msgSupportGetAllSetReset; m_caps[CapType::FeederEnabled] = [this](Msg msg, Capability& data) -> Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration( { Bool(), Bool(true) }, Bool(m_bFeederEnabled)); return success(); case Msg::GetCurrent: data = Capability::createOneValue(m_bFeederEnabled); return success(); case Msg::Reset: case Msg::GetDefault: data = Capability::createOneValue(true); m_bFeederEnabled = true; return success(); case Msg::Set: { auto feederen = data.currentItem(); m_bFeederEnabled = feederen; return success(); } default: return capBadOperation(); } }; m_query[CapType::Duplex] = msgSupportGetAll; m_caps[CapType::Duplex] = std::bind(oneValGet, _1, _2, Duplex::OnePass); m_query[CapType::DuplexEnabled] = msgSupportGetAllSetReset; m_caps[CapType::DuplexEnabled] = [this](Msg msg, Capability& data) -> Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration( { Bool(), Bool(true) }, Bool(m_scanparam->is_duplex)); return success(); case Msg::Reset: m_scanparam->is_duplex = true; case Msg::GetCurrent: data = Capability::createOneValue(m_scanparam->is_duplex); return success(); case Msg::GetDefault: data = Capability::createOneValue(false); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->is_duplex=mech; return success(); } default: return capBadOperation(); } }; m_query[CapType::AutoFeed] = msgSupportGetAllSetReset; m_caps[CapType::AutoFeed] = [this](Msg msg, Capability& data) -> Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration( { Bool(), Bool(true) }, Bool(m_bAutoFeed)); return success(); case Msg::Reset: m_bAutoFeed = true; // fallthrough case Msg::GetCurrent: data = Capability::createOneValue(m_bAutoFeed); return success(); case Msg::GetDefault: data = Capability::createOneValue(true); return success(); case Msg::Set: { auto mech = data.currentItem(); m_bAutoFeed = mech; return success(); } default: return capBadOperation(); } }; m_query[CapType::IImageFileFormat] = msgSupportGetAllSetReset; m_caps[CapType::IImageFileFormat] = [this](Msg msg, Capability& data) -> Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration( { ImageFileFormat::Bmp, ImageFileFormat::Tiff}, m_capImageFileFormat == ImageFileFormat::Bmp? 0 : 1, 0); return success(); case Msg::Reset: m_capImageFileFormat =ImageFileFormat::Bmp; // fallthrough case Msg::GetCurrent: data = Capability::createOneValue(m_capImageFileFormat); return success(); case Msg::GetDefault: data = Capability::createOneValue(ImageFileFormat::Bmp); return success(); case Msg::Set: { auto mech = data.currentItem(); if (mech == ImageFileFormat::Bmp || mech == ImageFileFormat::Tiff) { m_capImageFileFormat =mech; return success(); } else { return badValue(); } } default: return capBadOperation(); } }; //custom define m_query[CapType::IAutomaticDeskew] = msgSupportGetAllSetReset; m_caps[CapType::IAutomaticDeskew] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration( { Bool(), Bool(true) }, Bool(m_scanparam->autodescrew)); return success(); case Msg::Reset: m_scanparam->autodescrew = true; case Msg::GetCurrent: data = Capability::createOneValue(m_scanparam->autodescrew); return success(); case Msg::GetDefault: data = Capability::createOneValue(true); return success(); case Msg::Set: { auto atuodsw = data.currentItem(); m_scanparam->autodescrew = (bool)atuodsw; return success(); } default: return capBadOperation(); } }; m_query[CapType::IAutomaticRotate] = msgSupportGetAllSetReset; m_caps[CapType::IAutomaticRotate] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration( { Bool(), Bool(true) }, Bool(m_scanparam->is_autotext)); case Msg::Reset: m_scanparam->is_autotext = false; // fallthrough case Msg::GetCurrent: data = Capability::createOneValue(m_scanparam->is_autotext); return success(); case Msg::GetDefault: data = Capability::createOneValue(false); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->is_autotext = (bool)mech; return success(); } default: return capBadOperation(); } }; m_query[CapType::IAutomaticCropUsesFrame] = msgSupportGetAllSetReset; m_caps[CapType::IAutomaticCropUsesFrame] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration( { Bool(), Bool(true) }, Bool(m_scanparam->is_autocrop)); return success(); case Msg::Reset: m_scanparam->is_autocrop = false; // fallthrough case Msg::GetCurrent: data = Capability::createOneValue(m_scanparam->is_autocrop); return success(); case Msg::GetDefault: data = Capability::createOneValue(false); return success(); case Msg::Set: { auto autocrop = data.currentItem(); m_scanparam->is_autocrop = (bool)autocrop; return success(); } default: return capBadOperation(); } }; m_query[CapType::IImageMerge] = msgSupportGetAllSetReset; m_caps[CapType::IImageMerge] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createOneValue(CapType::IImageMerge,(UInt16)m_scanparam->en_fold); return success(); case Msg::Reset: m_scanparam->en_fold = 0;//默认不对折 // fallthrough case Msg::GetCurrent: data = Capability::createOneValue(CapType::IImageMerge, (UInt16)m_scanparam->en_fold); return success(); case Msg::GetDefault: data = Capability::createOneValue(CapType::IImageMerge,0); return success(); case Msg::Set: { auto autocrop = data.currentItem(); m_scanparam->en_fold = (byte)autocrop; return success(); } default: return capBadOperation(); } }; m_query[CapType::IAutoDiscardBlankPages] = msgSupportGetAllSetReset; m_caps[CapType::IAutoDiscardBlankPages] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration({ DiscardBlankPages::Disabled,DiscardBlankPages::Auto}, m_scanparam->is_autodiscradblank_normal?1:0, 0); return success(); case Msg::Reset: m_scanparam->is_autodiscradblank_normal = false; case Msg::GetCurrent: data = Capability::createOneValue(m_scanparam->is_autodiscradblank_normal? DiscardBlankPages::Auto: DiscardBlankPages::Disabled); return success(); case Msg::GetDefault: data = Capability::createOneValue(DiscardBlankPages::Disabled); return success(); case Msg::Set:{ auto mech = data.currentItem(); m_scanparam->is_autodiscradblank_normal = mech == DiscardBlankPages::Auto; return success(); } default: return capBadOperation(); } }; /*costom caps*/ //跳过空白页发票 m_query[(CapType)(CapTypeEx::TwEx_IAutoDiscardBlankVince)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_IAutoDiscardBlankVince)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration((CapType)((CapType)(CapTypeEx::TwEx_IAutoDiscardBlankVince)), { Bool(),Bool(true) }, Bool(m_scanparam->is_autodiscradblank_vince), 0); return success(); case Msg::Reset: m_scanparam->is_autodiscradblank_vince = false; case Msg::GetCurrent: data = Capability::createOneValue((CapType)((CapType)(CapTypeEx::TwEx_IAutoDiscardBlankVince)), m_scanparam->is_autodiscradblank_vince); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)((CapType)(CapTypeEx::TwEx_IAutoDiscardBlankVince)), Bool(false)); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->is_autodiscradblank_vince = mech; return success(); } default: return capBadOperation(); } }; m_query[(CapType)(CapTypeEx::TwEx_IBackRotate180)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_IBackRotate180)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration((CapType)(CapTypeEx::TwEx_IBackRotate180), { Bool(),Bool(true) }, Bool(m_scanparam->is_backrotate180), 0); return success(); case Msg::Reset: m_scanparam->is_backrotate180 = false; case Msg::GetCurrent: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IBackRotate180), m_scanparam->is_backrotate180); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IBackRotate180), Bool(false)); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->is_backrotate180 = mech; return success(); } default: return capBadOperation(); } }; //填黑框 m_query[(CapType)(CapTypeEx::TwEx_IFillBackground)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_IFillBackground)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration((CapType)(CapTypeEx::TwEx_IFillBackground), { Bool(),Bool(true) }, Bool(m_scanparam->fillbackground), 0); return success(); case Msg::Reset: m_scanparam->fillbackground = false; case Msg::GetCurrent: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IFillBackground), m_scanparam->fillbackground); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IFillBackground), Bool(false)); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->fillbackground = mech; return success(); } default: return capBadOperation(); } }; //填穿孔 m_query[(CapType)(CapTypeEx::TwEx_IFillHole)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_IFillHole)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration((CapType)(CapTypeEx::TwEx_IFillHole), { Bool(),Bool(true) }, Bool(m_scanparam->fillhole.is_fillhole), 0); return success(); case Msg::Reset: m_scanparam->fillhole.is_fillhole = false; case Msg::GetCurrent: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IFillHole), m_scanparam->fillhole.is_fillhole); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IFillHole), Bool(false)); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->fillhole.is_fillhole = mech; return success(); } default: return capBadOperation(); } }; m_query[(CapType)(CapTypeEx::TwEx_IFillHoleRatio)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_IFillHoleRatio)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IFillHoleRatio), m_scanparam->fillhole.fillholeratio); return success(); case Msg::Reset: m_scanparam->fillhole.fillholeratio = 10; case Msg::GetCurrent: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IFillHoleRatio), m_scanparam->fillhole.fillholeratio); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IFillHoleRatio), Int32(10)); return success(); case Msg::Set: { auto mech = data.currentItem(); if (mech > 0 && mech < 50) { m_scanparam->fillhole.fillholeratio = (float)mech; return success(); } return badValue(); } default: return capBadOperation(); } }; //多流除红 m_query[(CapType)(CapTypeEx::TwEx_IMultiOutputRed)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_IMultiOutputRed)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration((CapType)(CapTypeEx::TwEx_IMultiOutputRed), { Bool(),Bool(true) }, Bool(m_scanparam->multi_output_red), 0); return success(); case Msg::Reset: m_scanparam->multi_output_red = false; case Msg::GetCurrent: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IMultiOutputRed), m_scanparam->multi_output_red); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IMultiOutputRed), Bool(false)); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->multi_output_red = mech; return success(); } default: return capBadOperation(); } }; m_query[CapType::IFilter] = msgSupportGetAllSetReset; m_caps[CapType::IFilter] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration({Filter::Red,Filter::Green,Filter::Blue,Filter::None }, m_scanparam->filter, 0); return success(); case Msg::Reset: m_scanparam->filter = (byte)Filter::None; case Msg::GetCurrent: data = Capability::createOneValue((Filter)m_scanparam->filter); return success(); case Msg::GetDefault: data = Capability::createOneValue(Filter::None); return success(); case Msg::Set: { auto mech = data.currentItem(); if(mech == Filter::None|| mech == Filter::Red||mech == Filter::Green ||mech ==Filter::Blue){ m_scanparam->filter =(byte) mech; return success(); } return badValue(); } default: return capBadOperation(); } }; //颜色增强 m_query[(CapType)(CapTypeEx::TwEx_IEnhanceColor)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_IEnhanceColor)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration((CapType)(CapTypeEx::TwEx_IEnhanceColor), { Enchace_Color::Enhance_None,Enchace_Color::Enhance_Red,Enchace_Color::Enhance_Green,Enchace_Color::Enhance_Blue},m_scanparam->enhance_color,0); return success(); case Msg::Reset: m_scanparam->enhance_color = Enchace_Color::Enhance_None; case Msg::GetCurrent: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IEnhanceColor), (Enchace_Color)(m_scanparam->enhance_color)); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_IEnhanceColor), Enchace_Color::Enhance_None); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->enhance_color =(byte)mech; return success(); } default: return capBadOperation(); } }; m_query[(CapType)(CapTypeEx::TwEx_Sharpen)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_Sharpen)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration((CapType)(CapTypeEx::TwEx_IEnhanceColor), { SharpenBlur::Sharpen_None,SharpenBlur::Sharpen_Normal,SharpenBlur::Sharpen_More,SharpenBlur::Sharpen_Blur,SharpenBlur::Sharpen_Blur_More }, m_scanparam->sharpen, 0); return success(); case Msg::Reset: m_scanparam->sharpen = SharpenBlur::Sharpen_None; case Msg::GetCurrent: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_Sharpen), (SharpenBlur)(m_scanparam->sharpen)); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_Sharpen), SharpenBlur::Sharpen_None); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->sharpen = (byte)mech; return success(); } default: return capBadOperation(); } }; /*亮度 对比度 gamma range类型 Range 类型*/ m_query[CapType::IBrightness] = msgSupportGetAllSetReset; m_caps[CapType::IBrightness] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data=Capability::createRange(Fix32(-1000.0f), Fix32(1000.0f), Fix32(333.3f), Fix32(m_scanparam->brightness), Fix32(0.0)); return success(); case Msg::Reset: m_scanparam->brightness =0.0f; case Msg::GetCurrent: data = Capability::createOneValue(Fix32(m_scanparam->brightness)); return success(); case Msg::GetDefault: data = Capability::createOneValue(Fix32(0.0f)); return success(); case Msg::Set: { auto mech = data.currentItem(); if (mech > 1000.0f || mech < -1000.0f) return badValue(); m_scanparam->brightness = (float)mech; return success(); } default: return capBadOperation(); } }; m_query[CapType::IContrast] = msgSupportGetAllSetReset; m_caps[CapType::IContrast] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createRange(Fix32(-1000.0f), Fix32(1000.0f), Fix32(333.3f), Fix32(m_scanparam->contrast), Fix32(0.0)); return success(); case Msg::Reset: m_scanparam->contrast = 0.0f; case Msg::GetCurrent: data = Capability::createOneValue(Fix32(m_scanparam->contrast)); return success(); case Msg::GetDefault: data = Capability::createOneValue(Fix32(0.0f)); return success(); case Msg::Set: { auto mech = data.currentItem(); if (mech > 1000.0f || mech < -1000.0f) return badValue(); m_scanparam->contrast = (float)mech; return success(); } default: return capBadOperation(); } }; m_query[CapType::IGamma] = msgSupportGetAllSetReset; m_caps[CapType::IGamma] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createRange(Fix32(0.0f), Fix32(5.0f), Fix32(1.0f), Fix32(m_scanparam->gamma), Fix32(1.0)); return success(); case Msg::Reset: m_scanparam->gamma = 1.0f; case Msg::GetCurrent: data = Capability::createOneValue(Fix32(m_scanparam->gamma)); return success(); case Msg::GetDefault: data = Capability::createOneValue(Fix32(0.0f)); return success(); case Msg::Set: { auto mech = data.currentItem(); if (mech > 5.0f || mech < 0.0f) return badValue(); m_scanparam->gamma = (float)mech; return success(); } default: return capBadOperation(); } }; //m_query[(CapType)(CapTypeEx::TwEx_DBAreaNum)] = msgSupportGetAllSetReset; //m_caps[(CapType)(CapTypeEx::TwEx_DBAreaNum)] = [this](Msg msg, Capability& data)->Result { // switch (msg) { // case Msg::Get: // data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_DBAreaNum), m_scanparam->areanum); // return success(); // case Msg::Reset: // m_scanparam->areanum = 8; // case Msg::GetCurrent: // data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_DBAreaNum), m_scanparam->areanum); // return success(); // case Msg::GetDefault: // data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_DBAreaNum), UInt16(8)); // return success(); // case Msg::Set: { // auto mech = data.currentItem(); // if (mech >= 5 && mech <= 40) { // m_scanparam->areanum = mech; // return success(); // } // return badValue(); // } // default: // return capBadOperation(); // } //}; //m_query[(CapType)(CapTypeEx::TwEx_DBDevnMax)] = msgSupportGetAllSetReset; //m_caps[(CapType)(CapTypeEx::TwEx_DBDevnMax)] = [this](Msg msg, Capability& data)->Result { // switch (msg) { // case Msg::Get: // data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_DBDevnMax), m_scanparam->devnmax); // return success(); // case Msg::Reset: // m_scanparam->devnmax = 200; // case Msg::GetCurrent: // data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_DBDevnMax), m_scanparam->devnmax); // return success(); // case Msg::GetDefault: // data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_DBDevnMax), UInt16(200)); // return success(); // case Msg::Set: { // auto mech = data.currentItem(); // if (mech >= 150 && mech <= 400) { // m_scanparam->devnmax = mech; // return success(); // } // return badValue(); // } // default: // return capBadOperation(); // } //}; /*以下为硬件协议*/ m_query[(CapType)(CapTypeEx::TwEx_ScrewDetectEnable)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_ScrewDetectEnable)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration((CapType)(CapTypeEx::TwEx_ScrewDetectEnable), { Bool(),Bool(true) }, Bool(m_scanparam->hardwarecaps.en_skrewdetect), 0); return success(); case Msg::Reset: m_scanparam->hardwarecaps.en_skrewdetect = false; case Msg::GetCurrent: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_ScrewDetectEnable), m_scanparam->hardwarecaps.en_skrewdetect); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_ScrewDetectEnable), Bool(false)); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->hardwarecaps.en_skrewdetect = mech; return success(); } default: return capBadOperation(); } }; m_query[(CapType)(CapTypeEx::TwEx_ScrewLevel)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_ScrewLevel)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_ScrewLevel), m_scanparam->hardwarecaps.skrewdetectlevel); return success(); case Msg::Reset: m_scanparam->hardwarecaps.skrewdetectlevel = 3; case Msg::GetCurrent: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_ScrewLevel), m_scanparam->hardwarecaps.skrewdetectlevel); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_ScrewLevel), UInt8(3)); return success(); case Msg::Set: { auto mech = data.currentItem(); if (mech >= 1 && mech <= 5) { m_scanparam->hardwarecaps.skrewdetectlevel = mech; return success(); } return badValue(); } default: return capBadOperation(); } }; //装订检测 m_query[(CapType)(CapTypeEx::TwEx_StableDetectEnable)] = msgSupportGetAllSetReset; m_caps[(CapType)(CapTypeEx::TwEx_StableDetectEnable)] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration((CapType)(CapTypeEx::TwEx_StableDetectEnable), { Bool(),Bool(true) }, Bool(m_scanparam->hardwarecaps.en_stapledetect), 0); return success(); case Msg::Reset: m_scanparam->hardwarecaps.en_stapledetect = false; case Msg::GetCurrent: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_StableDetectEnable), m_scanparam->hardwarecaps.en_stapledetect); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)(CapTypeEx::TwEx_StableDetectEnable), Bool(false)); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->hardwarecaps.en_stapledetect = mech; return success(); } default: return capBadOperation(); } }; //双张检测 与官方标准定义有所差异 此协议修改为bool型 m_query[CapType::DoubleFeedDetection] = msgSupportGetAllSetReset; m_caps[CapType::DoubleFeedDetection] = [this](Msg msg, Capability& data)->Result { switch (msg) { case Msg::Get: data = Capability::createEnumeration(CapType::DoubleFeedDetection, { Bool(),Bool(true) }, Bool(m_scanparam->hardwarecaps.en_doublefeed), 0); return success(); case Msg::Reset: m_scanparam->hardwarecaps.en_doublefeed = false; case Msg::GetCurrent: data = Capability::createOneValue(CapType::DoubleFeedDetection, m_scanparam->hardwarecaps.en_doublefeed); return success(); case Msg::GetDefault: data = Capability::createOneValue(CapType::DoubleFeedDetection, Bool(false)); return success(); case Msg::Set: { auto mech = data.currentItem(); m_scanparam->hardwarecaps.en_doublefeed = mech; return success(); } default: return capBadOperation(); } }; return success(); } Result HuagaoDs::identityCloseDs(const Identity&){ // no need to explicitly release any resources if using RAII // TWPP will free the whole source on its own after this method guiIndicator.reset(); guiTwain.reset(); guiBridge.reset(); scanner.reset(); return success(); } Result HuagaoDs::pendingXfersGet(const Identity&, PendingXfers& data){ data.setCount(m_pendingXfers); return success(); } Result HuagaoDs::pendingXfersEnd(const Identity&, PendingXfers& data){ //!< end xfer if set count 0 int ret= scanner->aquire_bmpdata(bmpData); if (ret !=0) { scanner->Set_ErrorCode(0); guiIndicator.reset(); if (ret != -1) { MessageBox(guiTwain ? guiTwain->m_hWnd : NULL, noticeMsgMap[ret], _T("提示"), MB_SYSTEMMODAL | MB_OK | MB_ICONINFORMATION); } m_pendingXfers = 0; if (guiTwain.get()) { ((CTwainUI*)(guiTwain.get()))->EnableID_OKorID_Cancel(true); } } else { m_pendingXfers = 1; } #ifdef LOG_NORMAL FileTools::write_log("out.txt", "m_pendingXfers " + std::to_string(m_pendingXfers)); #endif // LOG_NORMAL data.setCount(m_pendingXfers); return success(); } Result HuagaoDs::pendingXfersReset(const Identity&, PendingXfers& data){ data.setCount(0); return success(); } Result HuagaoDs::setupMemXferGet(const Identity&, SetupMemXfer& data){ auto bpl = bytesPerLine(); auto max = bpl * static_cast(header()->biHeight); data.setMinSize(bpl); data.setPreferredSize(max); data.setMaxSize(max); return success(); } Result HuagaoDs::userInterfaceDisable(const Identity&, UserInterface& ui){ guiTwain.reset(); #if TWPP_DETAIL_OS_WIN guiBridge.reset(); #endif return success(); } Result HuagaoDs::userInterfaceEnable(const Identity&, UserInterface& ui){ m_pendingXfers = 1; m_memXferYOff = 0; if (!ui.showUi()){ // this is an exception when we want to set state explicitly, notifyXferReady can be called only in enabled state // with hidden UI, the usual workflow DsState::Enabled -> notifyXferReady() -> DsState::XferReady is a single step updataGscanCap(); setState(DsState::Enabled); if(startScan() == success()){ m_pendingXfers = 1; auto notified = notifyXferReady(); return success(); } else { m_pendingXfers = 0; setState(DsState::Open); return seqError(); } } return showTwainUI(ui); } Result HuagaoDs::userInterfaceEnableUiOnly(const Identity&, UserInterface& ui){ // as a minimal source, we do not support GUI that just saves settings return showTwainUI(ui, true); } Result HuagaoDs::imageInfoGet(const Identity&, ImageInfo& data){ // our image does not change auto dib = header(); data.setBitsPerPixel(static_cast(dib->biBitCount)); data.setHeight(dib->biHeight); //dib->biClrUseddib->biClrUsed==2?T data.setPixelType(dib->biClrUsed == 2 ? PixelType::BlackWhite : (dib->biClrUsed == 256 ? PixelType::Gray : PixelType::Rgb)); data.setPlanar(false); data.setWidth(dib->biWidth); data.setXResolution(m_scanparam->resolution_dst); data.setYResolution(m_scanparam->resolution_dst); switch (dib->biClrUsed) { case 2: case 256: data.setSamplesPerPixel(1); data.bitsPerSample()[0] = 8; break; case 0: data.setSamplesPerPixel(3); data.bitsPerSample()[0] = 8; data.bitsPerSample()[1] = 8; data.bitsPerSample()[2] = 8; default: break; } return success(); } Result HuagaoDs::imageLayoutGet(const Identity&, ImageLayout& data){ // our image does not change auto dib = header(); data.setDocumentNumber(1); data.setFrameNumber(1); data.setPageNumber(1); data.setFrame(Frame(0, 0, static_cast(dib->biWidth) / RESOLUTIONX, static_cast(dib->biHeight) / RESOLUTIONX)); return success(); } Result HuagaoDs::imageLayoutGetDefault(const Identity& origin, ImageLayout& data){ return imageLayoutGet(origin, data); } Result HuagaoDs::imageLayoutSet(const Identity& origin, ImageLayout& lay){ // we dont support setting image frame ImageLayout def; imageLayoutGetDefault(origin, def); return lay.frame() == def.frame() ? success() : badValue(); } Result HuagaoDs::imageLayoutReset(const Identity& origin, ImageLayout& data){ return imageLayoutGet(origin, data); } Result HuagaoDs::imageMemXferGet(const Identity& origin, ImageMemXfer& data){ if (!m_pendingXfers){ return seqError(); } // we can call our TWPP methods, but be careful about states SetupMemXfer setup; setupMemXferGet(origin, setup); // just a simple stored BMP image auto dib = header(); auto bpl = bytesPerLine(); auto memSize = data.memory().size(); if (memSize > setup.maxSize() || memSize < setup.minSize()){ return badValue(); } auto maxRows = memSize / bpl; auto rows = std::min(maxRows, static_cast(dib->biHeight) - m_memXferYOff); if (rows == 0){ return seqError(); // image already transfered in this session } data.setBytesPerRow(bpl); data.setColumns(static_cast(dib->biWidth)); data.setRows(rows); data.setBytesWritten(rows * bpl); data.setXOffset(0); data.setYOffset(m_memXferYOff); data.setCompression(Compression::None); auto lock = data.memory().data(); char* out = lock.data(); // bottom-up BMP -> top-down memory transfer auto begin = bmpEnd() - (bpl * (m_memXferYOff + 1)); for (UInt32 i = 0; i < rows; i++){ // copy bytes std::copy(begin, begin + bpl, out); char* line = out; out += bpl; begin -= bpl; // BGR BMP -> RGB memory transfer for ( ; line + 3 < out; line += 3){ std::swap(line[0], line[2]); } } m_memXferYOff += rows; if (m_memXferYOff >= static_cast(std::abs(dib->biHeight))){ m_pendingXfers = 0; return {ReturnCode::XferDone, ConditionCode::Success}; } return success(); } #ifdef LOG_NORMAL static int xtfer = 0; #endif Result HuagaoDs::imageNativeXferGet(const Identity&, ImageNativeXfer& data){ if (!m_pendingXfers){ return seqError(); } if (data) data.release(); // it does not get easier than that if we already have BMP data = ImageNativeXfer(bmpSize()); std::copy(bmpBegin(), bmpEnd(), data.data().data()); #ifdef LOG_NORMAL FileTools::write_log("out.txt", "imageNativeXferGet "+ std::to_string(++xtfer)); #endif // LOG return {ReturnCode::XferDone, ConditionCode::Success}; } Twpp::Result HuagaoDs::pendingXfersStopFeeder(const Identity& origin, PendingXfers& data) { if (!scanner.get()) return seqError(); if (scanner->IsConnected()) { scanner->Stop_scan(); } data.setCount(scanner->Get_IsImageQueueEmpty() ? 0 : 1); return success(); } Twpp::Result HuagaoDs::showTwainUI(Twpp::UserInterface& ui, bool bUiOnly) { // as a minimal source, we do not support GUI that just saves settings if (ui.parent()) { guiBridge.reset(new CDialog()); HWND appWindow = static_cast(ui.parent().raw()); guiBridge->Create(IDD_BACK, CWnd::FromHandle(appWindow)); HWND bridgeWindow = guiBridge->GetSafeHwnd(); long bridgeFlags = GetWindowLong(bridgeWindow, GWL_STYLE); SetWindowLong(bridgeWindow, GWL_STYLE, bridgeFlags | WS_CHILD); SetParent(bridgeWindow, appWindow); //if (ui.modalUi()) { // long appFlags = GetWindowLong(appWindow, GWL_STYLE); // SetWindowLong(appWindow, GWL_STYLE, appFlags | WS_DISABLED); //} } //!< show ui to scan button push auto scanFunction = [this](const GScanCap& caps) { m_pendingXfers = 1; m_scanparam.reset(new GScanCap(caps)); //scanner->config_params(*m_scanparam); if (startScan() == success()) { notifyXferReady(); } else { m_pendingXfers = 0; } }; //!< ui only to confirm button push auto confirmFunction = [this](const GScanCap& caps) { m_scanparam.reset(new GScanCap(caps)); notifyCloseOk(); }; //!< cancel button push auto cancelFunction = [this]() { notifyCloseCancel(); }; CWnd* parent = guiBridge.get(); TwGlue glue = {scanFunction, cancelFunction }; TwGlue glueUiOnly = {confirmFunction, cancelFunction }; std::string serialnum = scanner->GetSerialNum(); std::string hardwareversion = scanner->GetFWVersion(); guiTwain.reset(new CTwainUI(bUiOnly ? glueUiOnly : glue, bUiOnly?"确定":"扫描", hardwareversion, serialnum)); guiTwain->Create(IDD_TWAINUI, parent); guiTwain->ShowWindow(SW_SHOWNORMAL); return success(); } const BITMAPINFOHEADER* HuagaoDs::header() const noexcept{ return reinterpret_cast(bmpData.data() + sizeof(BITMAPFILEHEADER)); } UInt32 HuagaoDs::bytesPerLine() const noexcept{ auto dib = header(); return static_cast(dib->biWidth * dib->biBitCount + 31) / 32 * 4; } UInt32 HuagaoDs::bmpSize() const noexcept{ return static_cast(bmpData.size()) - sizeof(BITMAPFILEHEADER); } const char* HuagaoDs::bmpBegin() const noexcept{ return (const char*)bmpData.cbegin()._Ptr + sizeof(BITMAPFILEHEADER); } const char* HuagaoDs::bmpEnd() const noexcept{ return (const char*)bmpData.cend()._Ptr; } void HuagaoDs::initGScanCap() { m_scanparam->autodescrew = 1; m_scanparam->brightness = 0.0f; m_scanparam->contrast = 0.0f; m_scanparam->fillbackground = 1; m_scanparam->fillhole.is_fillhole = 1; m_scanparam->fillhole.fillholeratio = 0.1f; m_scanparam->filter = (byte)Filter::None; m_scanparam->sharpen = (byte)SharpenBlur::Sharpen_None; m_scanparam->gamma = 1.0; m_scanparam->en_fold = 0; m_scanparam->hardwarecaps.capturepixtype = (int)(PixelType::Gray); m_scanparam->hardwarecaps.en_doublefeed = 1; m_scanparam->hardwarecaps.en_skrewdetect = 0; m_scanparam->hardwarecaps.en_stapledetect = 0; m_scanparam->hardwarecaps.skrewdetectlevel = 3; m_scanparam->is_autocontrast = 0; m_scanparam->is_autocrop = 0; m_scanparam->is_autodiscradblank_normal = 0; m_scanparam->is_autodiscradblank_vince = 0; //m_scanparam->areanum = 8; //m_scanparam->devnmax = 200; m_scanparam->is_backrotate180 = 0; m_scanparam->is_duplex = 1; m_scanparam->multi_output_red = 0; m_scanparam->papertype = 11;//A3 m_scanparam->paperAlign = PaperAlign::Rot0; m_scanparam->resolution_native = 200.0f; m_scanparam->resolution_dst = 200.0f; m_scanparam->scannum = -1; m_scanparam->is_autotext = 0; m_scanparam->enhance_color = 0; m_scanparam->pixtype= (int)(PixelType::Gray); ScanRect rect = { 0 };//to be define m_scanparam->scanrect = rect; m_scanparam->threshold = 128.0f; } void HuagaoDs::updataGscanCap() { JsonConfig js; CONFIGPARAMS cfs=js.ReadDefaultConfig(); m_scanparam->autodescrew = cfs.EnAutoDescrew?1:0; m_scanparam->brightness = cfs.Brightness; m_scanparam->contrast = cfs.Contrast; m_scanparam->fillbackground = cfs.EnFillBlack?1:0; m_scanparam->fillhole.is_fillhole = cfs.EnOutHole?1:0; m_scanparam->fillhole.fillholeratio = cfs.OutHoleRatio; m_scanparam->sharpen = (SharpenBlur)cfs.Sharpen; if (cfs.Filter) { if (cfs.Filter <= 3) { m_scanparam->filter = filterMaps[cfs.Filter]; m_scanparam->enhance_color = 0;//红色增强 none } else { m_scanparam->filter = (byte)Filter::None;//不除色 m_scanparam->enhance_color = filterMaps[cfs.Filter]; } } else { m_scanparam->filter = (byte)Filter::None; m_scanparam->enhance_color = EnchaceColor::Enhance_None; } m_scanparam->gamma =cfs.Gamma; m_scanparam->hardwarecaps.en_doublefeed = cfs.EnUltrasonicDetect?1:0; m_scanparam->hardwarecaps.en_skrewdetect = cfs.EnScrewDetect?1:0; m_scanparam->hardwarecaps.en_stapledetect = cfs.EnBindingDetect?1:0; m_scanparam->hardwarecaps.skrewdetectlevel = cfs.ScrewDetectLevel; m_scanparam->is_autocontrast = cfs.EnAutoContrast?1:0; m_scanparam->is_autocrop = cfs.EnAutoCrop?1:0; m_scanparam->is_autodiscradblank_normal = cfs.EnDiscardBlank?1:0; m_scanparam->is_autodiscradblank_vince = cfs.EnDiscardBlankVince?1:0; m_scanparam->is_backrotate180 = cfs.EnBackRotate180?1:0; m_scanparam->is_duplex = cfs.Duplex>=1?1:0; m_scanparam->en_fold = cfs.EnFlod?1:0; m_scanparam->multi_output_red = cfs.EnMultiOutPutR?1:0; PaperStatus ps = paperStatusMap[cfs.PaperSize]; m_scanparam->papertype = ps.Paper;//A3 m_scanparam->paperAlign =(PaperAlign)ps.Orentate; m_scanparam->resolution_dst = resolutions[cfs.Resolution]; #ifdef REAL300DPI m_scanparam->resolution_native = m_scanparam->resolution_dst > 240.0f ? 300.0f : 200.0f; #else // REAL300DPI m_scanparam->resolution_native = 200.0f; #endif m_scanparam->scannum = cfs.ScanCount; m_scanparam->is_autotext = cfs.Orentation==4?1:0; if (cfs.Orentation != 4) { m_scanparam->imageRotateDegree = rotateDegrees[cfs.Orentation]; } m_scanparam->pixtype = colorModes[cfs.Pixtype]; if (cfs.Filter || cfs.EnMultiOutPutR) { m_scanparam->hardwarecaps.capturepixtype = (byte)(PixelType::Rgb); } else { m_scanparam->hardwarecaps.capturepixtype = m_scanparam->pixtype; } ScanRect rect = { 0 };//to be define m_scanparam->scanrect = rect; m_scanparam->threshold = 128.0f; } Twpp::Result HuagaoDs::startScan() { if (!scanner->IsConnected()) return checkDeviceOnline(); scanner->ResetScanner(); scanner->config_params(*m_scanparam); if (m_bIndicator) { //!< cancel button push auto stopFunc = [this]() { scanner->Stop_scan(); guiIndicator.reset();//取消扫描 关闭进度指示器 }; guiIndicator.reset(new CIndicatorDlg(stopFunc)); guiIndicator->Create(IDD_INDICATOR, guiTwain ? guiTwain.get() : guiBridge.get()); guiIndicator->ShowWindow(SW_SHOWNORMAL); } scanner->Scanner_StartScan(m_scanparam->scannum); if (bmpData.size() > 0) bmpData.clear(); if(guiTwain.get()){ ((CTwainUI*)(guiTwain.get()))->EnableID_OKorID_Cancel(false); } int retCode = scanner->aquire_bmpdata(bmpData); if (retCode!=0) { scanner->Set_ErrorCode(0); guiIndicator.reset(); if(retCode!=-1) MessageBox(guiTwain? guiTwain->m_hWnd:NULL, noticeMsgMap[retCode], _T("提示"), MB_SYSTEMMODAL | MB_OK | MB_ICONINFORMATION); if (guiTwain.get()) { ((CTwainUI*)(guiTwain.get()))->EnableID_OKorID_Cancel(true); } return seqError(); } if (bmpData.size()>0) { return success(); } else { guiIndicator.reset(); if (guiTwain.get()) { ((CTwainUI*)(guiTwain.get()))->EnableID_OKorID_Cancel(true); } return seqError(); } }