sat23 преди 4 години
родител
ревизия
2f04979389

+ 26 - 7
GameAssist/GameAssist/Assist.vcxproj

@@ -133,6 +133,7 @@
       <WarningLevel>Level3</WarningLevel>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <AdditionalIncludeDirectories>..\opencv\;..\IMGProc</AdditionalIncludeDirectories>
+      <LanguageStandard>stdcpp17</LanguageStandard>
     </ClCompile>
     <ResourceCompile>
       <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -198,6 +199,7 @@
       <WarningLevel>Level3</WarningLevel>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <AdditionalIncludeDirectories>..\opencv\;..\IMGProc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <LanguageStandard>stdcpp17</LanguageStandard>
     </ClCompile>
     <ResourceCompile>
       <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -213,14 +215,31 @@
       <AdditionalLibraryDirectories>..\opencv\lib</AdditionalLibraryDirectories>
       <AdditionalDependencies>opencv_core420.lib;opencv_imgproc420.lib;opencv_highgui420.lib;opencv_ml420.lib;opencv_video420.lib;opencv_features2d420.lib;opencv_calib3d420.lib;opencv_objdetect420.lib;opencv_flann420.lib;opencv_dnn420.lib;opencv_imgcodecs420.lib;opencv_photo420.lib;opencv_stitching420.lib;opencv_videoio420.lib;opencv_gapi420.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
+    <PostBuildEvent>
+      <Command>xcopy $(SolutionDir)img $(OutputPath)img\ /s/y/a</Command>
+    </PostBuildEvent>
   </ItemDefinitionGroup>
   <ItemGroup>
-    <ClCompile Include="..\IMGProc\globalVar.cpp" />
-    <ClCompile Include="..\IMGProc\helpfunc.cpp" />
-    <ClCompile Include="..\IMGProc\ImageLoc.cpp" />
-    <ClCompile Include="..\IMGProc\ImageProc.cpp" />
-    <ClCompile Include="..\IMGProc\opEnv.cpp" />
-    <ClCompile Include="..\IMGProc\ThreadPool.cpp" />
+    <ClCompile Include="..\IMGProc\globalVar.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="..\IMGProc\helpfunc.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="..\IMGProc\ImageLoc.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="..\IMGProc\ImageProc.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="..\IMGProc\opEnv.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
+    </ClCompile>
     <ClCompile Include="Assist.cpp" />
     <ClCompile Include="CAction.cpp" />
     <ClCompile Include="CTask.cpp" />
@@ -240,13 +259,13 @@
   <ItemGroup>
     <ClInclude Include="..\IMGProc\bitfunc.h" />
     <ClInclude Include="..\IMGProc\color.h" />
-    <ClInclude Include="..\IMGProc\Dict.h" />
     <ClInclude Include="..\IMGProc\frameInfo.h" />
     <ClInclude Include="..\IMGProc\globalVar.h" />
     <ClInclude Include="..\IMGProc\helpfunc.h" />
     <ClInclude Include="..\IMGProc\Image.hpp" />
     <ClInclude Include="..\IMGProc\ImageLoc.h" />
     <ClInclude Include="..\IMGProc\ImageProc.h" />
+    <ClInclude Include="..\IMGProc\imageView.hpp" />
     <ClInclude Include="..\IMGProc\opEnv.h" />
     <ClInclude Include="..\IMGProc\optype.h" />
     <ClInclude Include="..\IMGProc\ThreadPool.h" />

+ 3 - 6
GameAssist/GameAssist/Assist.vcxproj.filters

@@ -69,9 +69,6 @@
     <ClCompile Include="..\IMGProc\opEnv.cpp">
       <Filter>IMGProc</Filter>
     </ClCompile>
-    <ClCompile Include="..\IMGProc\ThreadPool.cpp">
-      <Filter>IMGProc</Filter>
-    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="GameAssist.h">
@@ -131,9 +128,6 @@
     <ClInclude Include="..\IMGProc\color.h">
       <Filter>IMGProc</Filter>
     </ClInclude>
-    <ClInclude Include="..\IMGProc\Dict.h">
-      <Filter>IMGProc</Filter>
-    </ClInclude>
     <ClInclude Include="..\IMGProc\globalVar.h">
       <Filter>IMGProc</Filter>
     </ClInclude>
@@ -152,6 +146,9 @@
     <ClInclude Include="..\IMGProc\ThreadPool.h">
       <Filter>IMGProc</Filter>
     </ClInclude>
+    <ClInclude Include="..\IMGProc\imageView.hpp">
+      <Filter>IMGProc</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="Assist.ini">

+ 416 - 0
GameAssist/IMGProc/Dict.h

@@ -0,0 +1,416 @@
+#pragma once
+#ifndef __DICT_H_
+#define __DICT_H_
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <fstream>
+#include <sstream>
+#include "bitfunc.h"
+#include "Image.hpp"
+#include "helpfunc.h"
+
+#ifdef UNICODE 
+using TString = std::wstring;
+#else
+using TString = std::string;
+#endif
+//#define SET_BIT(x, idx) x |= 1u << (idx)
+
+//#define GET_BIT(x, idx) (((x )>> (idx)) & 1u)
+const int op_dict_version = 2;
+
+#ifndef max
+#define max(a,b)            (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b)            (((a) < (b)) ? (a) : (b))
+#endif
+/*
+第 0 代字库
+*/
+struct word_info_t 
+{
+	//char of word
+	TCHAR _char[4];
+	//char height
+	__int16 width, height;
+	//char bit ct
+	__int32 bit_count;
+	word_info_t() :width(0), height(0), bit_count(0) 
+	{ 
+		memset(_char, 0, sizeof(_char)); 
+	}
+
+	bool operator==(const word_info_t& rhs) {
+		return width == rhs.width && height == rhs.height;
+	}
+
+	bool operator!=(const word_info_t& rhs) {
+		return width != rhs.width && height == rhs.height;
+	}
+};
+
+struct word_t 
+{
+	//32 bit a col
+	using cline_t = unsigned __int32;
+	word_info_t info;
+	//char col line
+	cline_t clines[32];
+	bool operator==(const word_t& rhs) 
+	{
+		if (info != rhs.info)
+			return false;
+		for (int i = 0; i < info.width; ++i)
+			if (clines[i] != rhs.clines[i])
+				return false;
+		return true;
+	}
+
+	void set_chars(const TString& s)
+	{
+		memcpy(info._char, s.c_str(), min(sizeof(info._char), (s.length() + 1) * sizeof(TCHAR)));
+	}
+
+	void fromDm(const TCHAR* str, int ct, const TString& w)
+	{
+		int bin[50] = { 0 };
+		const int DM_DICT_HEIGTH = 11;
+		ct = min(ct, 88);
+		int i = 0;
+		auto hex2bin = [](TCHAR c) 
+		{
+			return c <= _T('9') ? c - _T('0') : c - _T('A') + 10;
+		};
+
+		while (i < ct) 
+		{
+			bin[i / 2] = (hex2bin(str[i]) << 4) | (hex2bin(str[i + 1]));
+			i += 2;
+		}
+		//
+		int cols = (ct * 4) / DM_DICT_HEIGTH;
+		memset(this, 0x0, sizeof(*this));
+		for (int j = 0; j < cols; ++j) {
+			for (int i = 0; i < 11; ++i) {
+				int idx = j * 11 + i;
+				if (GET_BIT(bin[idx >> 3], 7 - (idx & 7))) {
+					SET_BIT(clines[j], 31 - i);
+					++info.bit_count;
+				}
+
+			}
+		}
+
+		info.height = DM_DICT_HEIGTH;
+		info.width = cols;
+		set_chars(w);
+	}
+};
+
+/*
+第 1 代字库
+*/
+struct word1_info {
+	uint8_t w, h;//max is 255 2B
+	uint16_t bit_cnt;//max is 255*255=65025<65536 4B
+	TCHAR name[8];//name 12B
+	word1_info() :w(0), h(0), bit_cnt(0) {}
+};
+
+struct word1_t 
+{
+	word1_info info;
+	vector<uint8_t> data;//size is (w*h+7)/8
+
+	bool operator==(const word1_t& rhs) {
+		if (info.w != rhs.info.w || info.h != rhs.info.h || info.bit_cnt != rhs.info.bit_cnt)
+			return false;
+		for (size_t i = 0;i < data.size();i++)
+			if (data[i] != rhs.data[i])
+				return false;
+		return true;
+	}
+
+	void set_chars(const TString& s) {
+		int nlen = s.length() < 8 ? s.length() : 7;
+		memcpy(info.name, s.c_str(), nlen * 2);
+		info.name[nlen] = L'\0';
+	}
+
+	void from_word(word_t& wd) {
+		info.w = (uint8_t)wd.info.width;
+		info.h = wd.info.height;
+		init();
+		info.bit_cnt = wd.info.bit_count;
+		memcpy(info.name, wd.info._char, 4 * sizeof(wchar_t));
+		info.name[3] = 0;
+		int idx = 0;
+
+		for (int x = 0; x < info.w; x++) {
+			for (int y = 0; y < info.h; y++) {
+				if (GET_BIT(wd.clines[x], 31 - y))
+					SET_BIT(data[idx / 8], idx & 7);
+				idx++;
+			}
+		}
+	}
+
+	void init() 
+	{
+		data.resize((info.w * info.h + 7) / 8);
+		std::fill(data.begin(), data.end(), 0);
+	}
+};
+
+
+struct Dict 
+{
+	//v0 v1
+	struct dict_info_t {
+		__int16 _this_ver;//0 1
+		__int16 _word_count;
+		//check code=_this_ver^_word_count
+		__int32 _check_code;
+		dict_info_t() :_this_ver(1), _word_count(0) { _check_code = _word_count ^ _this_ver; }
+	};
+	dict_info_t info;
+	Dict() {}
+	std::vector<word1_t>words;
+
+	void read_dict(const TString& s)
+	{
+		if (s.empty())
+			return;
+		if (s.find(_T(".txt")) != -1)
+			return read_dict_dm(s);
+		clear();
+		std::fstream file;
+		file.open(s, std::ios::in | std::ios::binary);
+		if (!file.is_open())
+			return;
+		//读取头信息
+		file.read((char*)&info, sizeof(info));
+
+		//校验
+		if (info._this_ver == 0 && info._check_code == (info._this_ver ^ info._word_count)) {
+			//old dict format
+			words.resize(info._word_count);
+			info._this_ver = 1;
+			word_t tmp;
+			for (size_t i = 0; i < words.size(); i++) {
+				file.read((char*)&tmp, sizeof(tmp));
+				words[i].from_word(tmp);
+			}
+			//file.read((char*)&words[0], sizeof(word_t)*info._word_count);
+		}
+		else if (info._this_ver == 1 && info._check_code == (info._this_ver ^ info._word_count)) {
+			//new dict format
+			words.resize(info._word_count);
+			word1_info head;
+			for (size_t i = 0; i < words.size(); i++) {
+				file.read((char*)&head, sizeof(head));
+
+				words[i].info = head;
+				int nlen = (head.w * head.h + 7) / 8;
+				words[i].data.resize(nlen);
+				file.read((char*)words[i].data.data(), nlen);
+			}
+		}
+		file.close();
+		sort_dict();
+	}
+
+	void read_dict_dm(const std::string& s) 
+	{
+		clear();
+		std::fstream file;
+		file.open(s, std::ios::in);
+		if (!file.is_open())
+			return;
+		//读取信息
+		std::wstring ss;
+		std::string str;
+		while (std::getline(file, str)) {
+			std::string strLocale = setlocale(LC_ALL, "");
+			const char* chSrc = str.c_str();
+			size_t nDestSize = mbstowcs(NULL, chSrc, 0) + 1;
+			wchar_t* wchDest = new wchar_t[nDestSize];
+			wmemset(wchDest, 0, nDestSize);
+			mbstowcs(wchDest, chSrc, nDestSize);
+			std::wstring wstrResult = wchDest;
+			delete[]wchDest;
+			setlocale(LC_ALL, strLocale.c_str());
+			ss = wstrResult;
+			size_t idx1 = ss.find(L'$');
+			auto idx2 = ss.find(L'$', idx1 + 1);
+			word_t wd;
+			word1_t wd1;
+			std::wstring name;
+			if (idx1 != -1 && idx2 != -1) {
+				ss[idx1] = L'0';
+				name = ss.substr(idx1 + 1, idx2 - idx1 - 1);
+				wd.fromDm(ss.data(), idx1, name);
+				wd1.from_word(wd);
+				wd1.set_chars(name);
+				add_word(wd1);
+
+			}
+		}
+		file.close();
+		sort_dict();
+	}
+
+	void read_memory_dict_dm(const char* buf, size_t size) 
+	{
+		clear();
+		std::stringstream file;
+		file.write(buf, size);
+
+		//读取信息
+		std::wstring ss;
+		std::string str;
+		while (std::getline(file, str)) {
+			std::string strLocale = setlocale(LC_ALL, "");
+			const char* chSrc = str.c_str();
+			size_t nDestSize = mbstowcs(NULL, chSrc, 0) + 1;
+			wchar_t* wchDest = new wchar_t[nDestSize];
+			wmemset(wchDest, 0, nDestSize);
+			mbstowcs(wchDest, chSrc, nDestSize);
+			std::wstring wstrResult = wchDest;
+			delete[]wchDest;
+			setlocale(LC_ALL, strLocale.c_str());
+			ss = wstrResult;
+			size_t idx1 = ss.find(L'$');
+			auto idx2 = ss.find(L'$', idx1 + 1);
+			word_t wd;
+			word1_t wd1;
+			std::wstring name;
+			if (idx1 != -1 && idx2 != -1) {
+				ss[idx1] = L'0';
+				name = ss.substr(idx1 + 1, idx2 - idx1 - 1);
+				wd.fromDm(ss.data(), idx1, name);
+				wd1.from_word(wd);
+				wd1.set_chars(name);
+				add_word(wd1);
+
+			}
+		}
+		sort_dict();
+	}
+
+	void write_dict(const std::string& s) 
+	{
+		std::fstream file;
+		file.open(s, std::ios::out | std::ios::binary);
+		if (!file.is_open())
+			return;
+		// 删除所有空白字符;
+		auto it = words.begin();
+		while (it != words.end()) 
+		{
+			if (it->info.name[0] == L'\0')
+				it = words.erase(it);
+			else
+				++it;
+		}
+
+		info._word_count = words.size();
+		//设置校验
+		info._check_code = info._this_ver ^ info._word_count;
+		//写入信息
+		file.write((char*)&info, sizeof(info));
+		//写入数据
+		for (int i = 0; i < words.size(); i++) {
+			file.write((char*)&words[i].info, sizeof(word1_info));
+			file.write((char*)words[i].data.data(), words[i].data.size());
+		}
+		file.close();
+	}
+
+	void add_word(const ImageBin& binary, const rect_t& rc) {
+		int x2 = min(rc.x1 + 255, rc.x2);
+		int y2 = min(rc.y1 + 255, rc.y2);
+		word1_t word;
+		word.info.w = x2 - rc.x1;
+		word.info.h = y2 - rc.y1;
+		word.info.bit_cnt = 0;
+		word.init();
+		//word.data.resize((word.info.w * word.info.h + 7) / 8);
+		int idx = 0;
+		for (int j = rc.x1; j < x2; ++j) {
+			for (int i = rc.y1; i < y2; ++i) {
+				auto val = binary.at(i, j);
+				if (val == 1) {
+					SET_BIT(word.data[idx / 8], idx & 7);
+
+					++word.info.bit_cnt;
+				}
+				++idx;
+
+			}
+		}
+		auto it = find(word);
+		if (words.empty() || it == words.end()) {
+			word.set_chars(L"");
+
+			words.push_back(word);
+			info._word_count = words.size();
+		}
+		else {//only change char
+			//word.set_chars(c);
+		}
+	}
+
+	void sort_dict() {
+		//sort dict(size: big --> small ,cnt: small -->big)
+		std::stable_sort(words.begin(), words.end(),
+			[](const word1_t& lhs, const word1_t& rhs) {
+				int dh = lhs.info.h - rhs.info.h;
+				int dw = lhs.info.w - rhs.info.w;
+				return dh > 0 || (dh == 0 && dw > 0) ||
+					(dh == 0 && dw == 0 && lhs.info.bit_cnt < rhs.info.bit_cnt);
+			});
+	}
+
+	void add_word(const word1_t& word) {
+		auto it = find(word);
+		if (words.empty() || it == words.end()) {
+			words.push_back(word);
+		}
+		else {
+			it->set_chars(word.info.name);
+		}
+		info._word_count = words.size();
+	}
+
+	void clear() {
+		info._word_count = 0;
+		words.clear();
+	}
+
+	std::vector<word1_t>::iterator find(const word1_t& word) {
+		for (auto it = words.begin(); it != words.end(); ++it)
+			if (*it == word)return it;
+		return words.end();
+	}
+
+	void erase(const word1_t& word) {
+		auto it = find(word);
+		if (!words.empty() && it != words.end())
+			words.erase(it);
+		info._word_count = words.size();
+	}
+
+	int size() const {
+		return info._word_count;
+	}
+
+	bool empty()const {
+		return size() == 0;
+	}
+};
+
+#endif

+ 342 - 0
GameAssist/IMGProc/Image.hpp

@@ -0,0 +1,342 @@
+#pragma once
+#ifndef __IMAGE_H_
+#define __IMAGE_H_
+#include <vector>
+#include <atlimage.h>
+
+struct Image
+{
+	using iterator = unsigned __int32*;
+	Image() :width(0), height(0), pdata(nullptr) {
+	}
+
+	Image(int w, int h) :pdata(nullptr) {
+		create(w, h);
+	}
+
+	//copy ctr
+	Image(const Image& rhs) :pdata(nullptr) {
+		if (rhs.empty()) {
+			this->clear();
+		}
+		else {
+			this->create(rhs.width, rhs.height);
+			memcpy(this->pdata, rhs.pdata, width * height * 4);
+		}
+
+	}
+
+	~Image() {
+		release();
+	}
+
+	void create(int w, int h) {
+		width = w, height = h;
+		if (!pdata) {
+			pdata = (unsigned char*)malloc(w * h * 4);
+		}
+		else {
+			pdata = (unsigned char*)realloc(pdata, w * h * 4);
+		}
+		if (pdata == nullptr)throw("memory not enough");
+	}
+
+	void release() {
+		width = height = 0;
+		if (pdata)free(pdata);
+		pdata = nullptr;
+	}
+
+	int size() {
+		return width * height;
+	}
+
+	void clear() {
+		width = height = 0;
+	}
+
+	bool empty()const {
+		return width == 0;
+	}
+
+	Image& operator=(const Image& rhs) {
+		if (rhs.empty()) {
+			this->clear();
+		}
+		else if (this != &rhs) {
+			this->create(rhs.width, rhs.height);
+			memcpy(this->pdata, rhs.pdata, width * height * 4);
+		}
+		return *this;
+	}
+
+	bool read(LPCTSTR file) {
+		clear();
+		ATL::CImage img;
+		HRESULT hr = img.Load(file);
+		if (hr == S_OK) {
+			create(img.GetWidth(), img.GetHeight());
+			translate((unsigned char*)img.GetBits(), img.GetBPP() / 8, img.GetPitch());
+			return true;
+		}
+		else {
+			return false;
+		}
+	}
+
+	bool read(void* pMemData, long  len) {
+		clear();
+		ATL::CImage img;
+		auto hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, len);
+		if (hGlobal)
+		{
+			void* pData = GlobalLock(hGlobal);
+			memcpy_s(pData, len, pMemData, len);
+			GlobalUnlock(hGlobal);
+		}
+		else {
+			return false;
+		}
+
+		IStream* pStream = NULL;
+		if (CreateStreamOnHGlobal(hGlobal, TRUE, &pStream) != S_OK) {
+			GlobalFree(hGlobal);
+			return false;
+		}
+		HRESULT hr = img.Load(pStream);
+		if (hr == S_OK) {
+			pStream->Release();
+			GlobalFree(hGlobal);
+			create(img.GetWidth(), img.GetHeight());
+			translate((unsigned char*)img.GetBits(), img.GetBPP() / 8, img.GetPitch());
+			return true;
+		}
+		else {
+			GlobalFree(hGlobal);
+			return false;
+		}
+	}
+
+	bool read(ATL::CImage* img) {
+		translate((unsigned char*)img->GetBits(), img->GetBPP() / 8, img->GetPitch());
+		return true;
+	}
+
+	bool write(LPCTSTR file) {
+		if (empty())
+			return false;
+		ATL::CImage img;
+
+		img.Create(width, height, 32);
+		auto pdst = (unsigned char*)img.GetBits();
+		auto psrc = pdata;
+		int pitch = img.GetPitch();
+		for (int i = 0; i < height; ++i) {
+			for (int j = 0; j < width; ++j) {
+				((int*)pdst)[j] = ((int*)psrc)[j];
+			}
+			pdst += pitch;
+			psrc += width * 4;
+		}
+		return img.Save(file) == S_OK;
+	}
+
+	void translate(unsigned char* psrc, int pixSize, int pitch) {
+		auto pdst = pdata;
+		//gray
+		if (pixSize == 1) {
+			for (int i = 0; i < height; ++i) {
+				for (int j = 0; j < width; ++j) {
+					*pdst++ = psrc[j];
+					*pdst++ = psrc[j];
+					*pdst++ = psrc[j];
+					*pdst++ = 0xff;
+				}
+				psrc += pitch;
+
+			}
+		}//bgr
+		else if (pixSize == 3) {
+			for (int i = 0; i < height; ++i) {
+				for (int j = 0; j < width; ++j) {
+					*pdst++ = psrc[j * 3 + 0];
+					*pdst++ = psrc[j * 3 + 1];
+					*pdst++ = psrc[j * 3 + 2];
+					*pdst++ = 0xff;
+				}
+				psrc += pitch;
+
+			}
+		}
+		else if (pixSize == 4) {
+			for (int i = 0; i < height; ++i) {
+				for (int j = 0; j < width; ++j) {
+					*pdst++ = psrc[j * 4 + 0];
+					*pdst++ = psrc[j * 4 + 1];
+					*pdst++ = psrc[j * 4 + 2];
+					*pdst++ = psrc[j * 4 + 3];
+				}
+				psrc += pitch;
+
+			}
+		}
+	}
+
+	template<typename Tp>
+	Tp at(int y, int x)const {
+		return ((Tp*)pdata)[y * width + x];
+	}
+
+	template<typename Tp>
+	Tp& at(int y, int x) {
+		return ((Tp*)pdata)[y * width + x];
+	}
+
+	template<typename Tp>
+	Tp* ptr(int y) {
+		return (Tp*)(pdata + y * width * 4);
+	}
+
+	template<typename Tp>
+	const Tp* ptr(int y)const {
+		return (Tp*)(pdata + y * width * 4);
+	}
+
+	iterator begin() {
+		return (iterator)pdata;
+	}
+
+	iterator end() {
+		return (iterator)pdata + width * height;
+	}
+
+	iterator begin()const {
+		return (iterator)pdata;
+	}
+
+	iterator end()const {
+		return (iterator)pdata + width * height;
+	}
+
+	void fill(unsigned int val) {
+		std::fill(begin(), end(), val);
+	}
+
+	void fill(int row, int col, int h, int w, unsigned int val) {
+		for (int i = 0; i < h; ++i) {
+			auto p = ptr<unsigned int>(row + i) + col;
+			std::fill(p, p + w, val);
+		}
+	}
+
+	int width, height;
+	unsigned char* pdata;
+};
+
+//µ¥Í¨µÀͼÏñ
+struct ImageBin {
+	using iterator = unsigned char*;
+	ImageBin() :width(0), height(0) {}
+	ImageBin(const ImageBin& rhs) {
+		this->width = rhs.width;
+		this->height = rhs.height;
+		this->pixels = rhs.pixels;
+	}
+
+	void create(int w, int h) {
+		width = w, height = h;
+		pixels.resize(w * h);
+	}
+
+	void clear() {
+		width = height = 0;
+	}
+
+	int size()const {
+		return width * height;
+	}
+
+	bool empty()const {
+		return width == 0;
+	}
+
+	unsigned char* data() {
+		return pixels.data();
+	}
+
+	ImageBin& operator=(const ImageBin& rhs) {
+		this->width = rhs.width;
+		this->height = rhs.height;
+		this->pixels = rhs.pixels;
+		return *this;
+	}
+
+	unsigned char at(int y, int x)const {
+		return pixels[y * width + x];
+	}
+
+	unsigned char& at(int y, int x) {
+		return pixels[y * width + x];
+	}
+
+	unsigned char* ptr(int y) {
+		return pixels.data() + y * width;
+	}
+
+	unsigned char const* ptr(int y) const {
+		return pixels.data() + y * width;
+	}
+
+	void fromImage4(const Image& img4) {
+		create(img4.width, img4.height);
+		auto psrc = img4.pdata;
+		for (size_t i = 0; i < pixels.size(); ++i) {
+			//pixels[i] = (psrc[0] + psrc[1] + psrc[2]) / 3;
+			// Gray = (R*299 + G*587 + B*114 + 500) / 1000
+			pixels[i] = (psrc[2] * 299 + psrc[1] * 587 + psrc[0] * 114 + 500) / 1000;
+			psrc += 4;
+		}
+	}
+
+	bool write(LPCTSTR file) {
+		if (empty())
+			return false;
+		ATL::CImage img;
+
+		img.Create(width, height, 32);
+		auto pdst = (unsigned char*)img.GetBits();
+		auto psrc = pixels.data();
+		int pitch = img.GetPitch();
+		for (int i = 0; i < height; ++i) {
+			for (int j = 0; j < width; ++j) {
+				//((int*)pdst)[j] = ((int*)psrc)[j];
+				uchar v = psrc[j] == 1 ? 0xff : 0;
+				pdst[j * 4] = pdst[j * 4 + 1] = pdst[j * 4 + 2] = v;
+				pdst[j * 4 + 3] = 0xff;
+
+			}
+			pdst += pitch;
+			psrc += width;
+		}
+		return img.Save(file) == S_OK;
+	}
+
+	iterator begin() {
+		return pixels.data();
+	}
+
+	iterator end() {
+		return pixels.data() + pixels.size();
+	}
+
+	int width, height;
+	std::vector<unsigned char> pixels;
+};
+
+using inputimg = const Image&;
+using outputimg = Image&;
+
+using inputbin = const ImageBin&;
+using outputbin = ImageBin&;
+
+#endif

+ 1074 - 0
GameAssist/IMGProc/ImageLoc.cpp

@@ -0,0 +1,1074 @@
+//#include "stdafx.h"
+#include "ImageLoc.h"
+#include <assert.h>
+#include <time.h>
+#include <numeric>
+#include "helpfunc.h"
+#include "imageView.hpp"
+
+
+using std::to_wstring;
+
+int check_transparent(Image* img)
+{
+	if (img->width < 2 || img->height < 2) return 0;
+	uint c0 = *img->begin();
+	bool x = c0 == img->at<uint>(0, img->width - 1) &&
+		c0 == img->at<uint>(img->height - 1, 0) &&
+		c0 == img->at<uint>(img->height - 1, img->width - 1);
+	if (!x) return 0;
+
+	int ct = 0;
+	for (auto it : *img)
+		if (it == c0) ++ct;
+	int total = img->height * img->width;
+	return total * 0.5 <= ct && ct < total ? ct : 0;
+}
+
+void get_match_points(const Image& img, vector<uint>& points)
+{
+	points.clear();
+	uint cbk = *img.begin();
+	for (int i = 0; i < img.height; ++i) {
+		for (int j = 0; j < img.width; ++j)
+			if (cbk != img.at<uint>(i, j)) points.push_back((i << 16) | j);
+	}
+}
+
+void gen_next(const Image& img, vector<int>& next)
+{
+	next.resize(img.width * img.height);
+
+	auto t = img.ptr<int>(0);
+	auto p = next.data();
+	p[0] = -1;
+	int k = -1, j = 0;
+	while (j < next.size() - 1) {
+		if (k == -1 || t[k] == t[j]) {
+			k++;
+			j++;
+			p[j] = k;
+		}
+		else {
+			k = p[k];
+		}
+	}
+}
+
+void Connectivity(const ImageBin& bin, ImageBin& rec) {}
+
+void extractConnectivity(const ImageBin& src, int threshold, std::vector<ImageBin>& out)
+{
+	ImageBin bin = src;
+	for (auto& it : bin) {
+		it = it > threshold ? 0xffu : 0;
+	}
+	ImageBin rec;
+	rec.create(bin.width, bin.height);
+	for (auto& it : rec) {
+		it = 0;
+	}
+}
+
+struct MatchContext {
+	imageView view;
+	Image* pic;
+	ImageBin* gray;
+	color_t dfcolor;
+	int tnorm;
+	vector<uint>& points;
+	int use_ts_match;
+};
+
+enum PicMatchType { PicMatchRGB = 0, PicMatchGray = 1, PicMatchTrans = 2 };
+
+// template <int picMatchType>
+// point_t PicViewMatch(MatchContext context, double sim, bool *stop) {
+//   return point_t();
+// }
+// template <>
+// point_t PicViewMatch<PicMatchGray>(MatchContext context, double sim,
+//                                    bool *stop) {
+//   // 计算最大误差
+//   int max_err_ct =
+//       (context.pic->height * context.pic->width - context.use_ts_match) *
+//       (1.0 - sim);
+//   rect_t &block = context.view._block;
+//   for (int i = block.y1; i < block.y2; ++i) {
+//     for (int j = block.x1; j < block.x2; ++j) {
+//       if (*stop) return point_t(-1, -1);
+//       // 开始匹配
+//       // quick check
+//       if ((double)abs(context.tnorm -
+//                       region_sum(x, y, x + timg->width, y + timg->height)) /
+//               (double)context.tnorm >
+//           1.0 - sim)
+//         return 0;
+//       int err = 0;
+//       int maxErr = (1.0 - sim) * tnorm;
+//       for (int i = 0; i < context.gray->height; i++) {
+//         auto ptr = context.view._src.ptr(y + i) + x;
+//         auto ptr2 = timg->ptr(i);
+//         for (int j = 0; j < timg->width; j++) {
+//           err += abs(*ptr - *ptr2);
+//           ptr++;
+//           ptr2++;
+//         }
+//         if (err > maxErr) return 0;
+//       }
+
+//       return 1;
+//       // simple_match<false>(j, i, pic, dfcolor,tnorm, sim));
+//       if (match_ret) {
+//         *stop = true;
+//         return point_t(j + _x1 + _dx, i + _y1 + _dy);
+//       }
+
+//     }  // end for j
+//   }    // end for i
+//   return point_t(-1, -1);
+//}
+
+ImageBase::ImageBase() : m_threadPool(std::thread::hardware_concurrency()) {
+	_x1 = _y1 = 0;
+	_dx = _dy = 0;
+}
+
+ImageBase::~ImageBase() {}
+
+void ImageBase::set_offset(int x1, int y1) {
+	_x1 = x1;
+	_y1 = y1;
+}
+
+long ImageBase::GetPixel(long x, long y, color_t& cr) {
+	auto p = _src.ptr<color_t>(0);
+	// setlog("%d", _src.width);
+	// static_assert(sizeof(color_t) == 4);
+	cr = *p;
+	return 1;
+}
+
+long ImageBase::CmpColor(color_t color, std::vector<color_df_t>& colors,
+	double sim) {
+	for (auto& it : colors) {
+		if (IN_RANGE(color, it.color, it.df)) return 1;
+	}
+
+	return 0;
+}
+
+long ImageBase::FindColor(vector<color_df_t>& colors, int dir, long& x, long& y)
+{
+	for (auto& it : colors) {  //对每个颜色描述
+
+		for (int i = 0; i < _src.height; ++i) {
+			auto p = _src.ptr<color_t>(i);
+			for (int j = 0; j < _src.width; ++j) {
+				if (IN_RANGE(*(p + j), it.color, it.df)) {
+					x = j + _x1 + _dx;
+					y = i + _y1 + _dy;
+					return 1;
+				}
+				p++;
+			}
+		}
+	}
+
+	x = y = -1;
+	return 0;
+}
+
+long ImageBase::FindColorEx(vector<color_df_t>& colors, TString& retstr) {
+	retstr.clear();
+	int find_ct = 0;
+	for (int i = 0; i < _src.height; ++i)
+	{
+		auto p = _src.ptr<color_t>(i);
+		for (int j = 0; j < _src.width; ++j)
+		{
+			for (auto& it : colors)
+			{  //对每个颜色描述
+				if (IN_RANGE(*p, it.color, it.df))
+				{
+#ifdef UNICODE
+					retstr += std::to_wstring(j + _x1 + _dx) + _T(",") + std::to_wstring(i + _y1 + _dy);
+#else
+					retstr += std::to_string(j + _x1 + _dx) + _T(",") + std::to_string(i + _y1 + _dy);
+#endif
+					retstr += _T("|");
+					++find_ct;
+					// return 1;
+					if (find_ct > _max_return_obj_ct) goto _quick_break;
+					break;
+				}
+			}
+			p++;
+		}
+	}
+_quick_break:
+	if (!retstr.empty() && retstr.back() == _T('|')) retstr.pop_back();
+	return find_ct;
+}
+
+long ImageBase::FindMultiColor(std::vector<color_df_t>& first_color, std::vector<pt_cr_df_t>& offset_color, double sim, long dir, long& x, long& y)
+{
+	int max_err_ct = offset_color.size() * (1. - sim);
+	int err_ct;
+	for (int i = 0; i < _src.height; ++i)
+	{
+		auto p = _src.ptr<color_t>(i);
+		for (int j = 0; j < _src.width; ++j)
+		{
+			// step 1. find first color
+			for (auto& it : first_color)
+			{  //对每个颜色描述
+				if (IN_RANGE(*p, it.color, it.df))
+				{
+					//匹配其他坐标
+					err_ct = 0;
+					for (auto& off_cr : offset_color)
+					{
+						int ptX = j + off_cr.x;
+						int ptY = i + off_cr.y;
+						if (ptX >= 0 && ptX < _src.width && ptY >= 0 && ptY < _src.height)
+						{
+							color_t currentColor = _src.at<color_t>(ptY, ptX);
+							if (!CmpColor(currentColor, off_cr.crdfs, sim))
+								++err_ct;
+						}
+						else
+						{
+							++err_ct;
+						}
+						if (err_ct > max_err_ct)
+							goto _quick_break;
+					}
+					// ok
+					x = j + _x1 + _dx, y = i + _y1 + _dy;
+					return 1;
+				}
+			}
+		_quick_break:
+			p++;
+		}
+	}
+	x = y = -1;
+	return 0;
+}
+
+long ImageBase::FindMultiColorEx(std::vector<color_df_t>& first_color, std::vector<pt_cr_df_t>& offset_color, double sim, long dir, TString& retstr)
+{
+	int max_err_ct = offset_color.size() * (1. - sim);
+	int err_ct;
+	int find_ct = 0;
+	for (int i = 0; i < _src.height; ++i)
+	{
+		auto p = _src.ptr<color_t>(i);
+		for (int j = 0; j < _src.width; ++j)
+		{
+			// step 1. find first color
+			for (auto& it : first_color)
+			{  //对每个颜色描述
+				if (IN_RANGE(*p, it.color, it.df))
+				{
+					//匹配其他坐标
+					err_ct = 0;
+					for (auto& off_cr : offset_color)
+					{
+						/* color_t currentColor = _src.at<color_t>(j + off_cr.x, i + off_cr.y);
+						 if (!CmpColor(currentColor, off_cr.crdfs, sim)) ++err_ct;
+						 if (err_ct > max_err_ct) goto _quick_break;*/
+
+						 //
+						int ptX = j + off_cr.x;
+						int ptY = i + off_cr.y;
+						if (ptX >= 0 && ptX < _src.width && ptY >= 0 && ptY < _src.height)
+						{
+							color_t currentColor = _src.at<color_t>(ptY, ptX);
+							if (!CmpColor(currentColor, off_cr.crdfs, sim))
+								++err_ct;
+						}
+						else {
+							++err_ct;
+						}
+						if (err_ct > max_err_ct) goto _quick_break;
+					}
+
+					// ok
+#ifdef UNICODE
+					retstr += std::to_wstring(j + _x1 + _dx) + _T(",") + std::to_wstring(i + _y1 + _dy);
+#else
+					retstr += std::to_string(j + _x1 + _dx) + _T(",") + std::to_string(i + _y1 + _dy);
+#endif
+					retstr += _T("|");
+					++find_ct;
+					if (find_ct > _max_return_obj_ct)
+						goto _quick_return;
+					else
+						goto _quick_break;
+				}
+			}
+		_quick_break:
+			p++;
+		}
+	}
+_quick_return:
+	if (!retstr.empty() && retstr.back() == _T('|')) retstr.pop_back();
+	return find_ct;
+	// x = y = -1;
+}
+
+long ImageBase::FindPic(std::vector<Image*>& pics, color_t dfcolor, double sim, long& x, long& y)
+{
+	x = y = -1;
+	vector<uint> points;
+	int match_ret = 0;
+	ImageBin gimg;
+	_gray.fromImage4(_src);
+	record_sum(_gray);
+	int tnorm;
+	//将小循环放在最外面,提高处理速度
+	for (int pic_id = 0; pic_id < pics.size(); ++pic_id) {
+		auto pic = pics[pic_id];
+		int use_ts_match = check_transparent(pic);
+		if (use_ts_match)
+			get_match_points(*pic, points);
+		else {
+			gimg.fromImage4(*pic);
+			tnorm = sum(gimg.begin(), gimg.end());
+		}
+
+		for (int i = 0; i < _src.height; ++i) {
+			for (int j = 0; j < _src.width; ++j) {
+				// step 1. 边界检查
+				if (i + pic->height > _src.height || j + pic->width > _src.width)
+					continue;
+				// step 2. 计算最大误差
+				int max_err_ct =
+					(pic->height * pic->width - use_ts_match) * (1.0 - sim);
+				// step 3. 开始匹配
+
+				/*match_ret = (use_ts_match ? trans_match<false>(j, i, pic, dfcolor,
+				   points, max_err_ct) : simple_match<false>(j, i, pic, dfcolor,
+				   max_err_ct));*/
+				match_ret = (use_ts_match ? trans_match<false>(j, i, pic, dfcolor,
+					points, max_err_ct)
+					: real_match(j, i, &gimg, tnorm, sim));
+				// simple_match<false>(j, i, pic, dfcolor,tnorm, sim));
+				if (match_ret) {
+					x = j + _x1 + _dx;
+					y = i + _y1 + _dy;
+					return pic_id;
+				}
+
+			}  // end for j
+		}    // end for i
+	}      // end for pics
+	return -1;
+}
+
+long ImageBase::FindPicTh(std::vector<Image*>& pics, color_t dfcolor, double sim, long& x, long& y)
+{
+	x = y = -1;
+	vector<uint> points;
+	int match_ret = 0;
+	ImageBin gimg;
+	_gray.fromImage4(_src);
+	record_sum(_gray);
+	int tnorm;
+	std::vector<rect_t> blocks;
+	//将小循环放在最外面,提高处理速度
+	for (int pic_id = 0; pic_id < pics.size(); ++pic_id)
+	{
+		auto pic = pics[pic_id];
+		int use_ts_match = check_transparent(pic);
+		if (use_ts_match)
+			get_match_points(*pic, points);
+		else {
+			gimg.fromImage4(*pic);
+			tnorm = sum(gimg.begin(), gimg.end());
+		}
+		auto pgimg = &gimg;
+		rect_t matchRect(0, 0, _src.width, _src.height);
+		matchRect.shrinkRect(pic->width, pic->height);
+		if (!matchRect.valid()) continue;
+		matchRect.divideBlock(m_threadPool.getThreadNum(),
+			matchRect.width() > matchRect.height(), blocks);
+		std::vector<std::future<point_t>> results;
+		bool stop = false;
+		for (size_t i = 0; i < m_threadPool.getThreadNum(); ++i)
+		{
+			results.push_back(m_threadPool.enqueue(
+				[this, dfcolor, points, pgimg, tnorm](rect_t& block, Image* pic,
+					int use_ts_match, double sim,
+					bool* stop) {
+						// 计算最大误差
+						int max_err_ct =
+							(pic->height * pic->width - use_ts_match) * (1.0 - sim);
+						for (int i = block.y1; i < block.y2; ++i) {
+							for (int j = block.x1; j < block.x2; ++j) {
+								if (*stop) return point_t(-1, -1);
+								// 开始匹配
+								int match_ret =
+									(use_ts_match ? trans_match<false>(j, i, pic, dfcolor,
+										points, max_err_ct)
+										: real_match(j, i, pgimg, tnorm, sim));
+								// simple_match<false>(j, i, pic, dfcolor,tnorm, sim));
+								if (match_ret) {
+									*stop = true;
+									return point_t(j + _x1 + _dx, i + _y1 + _dy);
+								}
+
+							}  // end for j
+						}    // end for i
+						return point_t(-1, -1);
+				},
+				blocks[i], pic, use_ts_match, sim, &stop));
+			// results.push_back(r);
+		}
+
+		// wait all
+		for (auto&& f : results) {
+			point_t p = f.get();
+			if (p.x != -1) {
+				x = p.x;
+				y = p.y;
+				// return pic_id;
+			}
+		}
+		if (x != -1) {
+			return pic_id;
+		}
+
+	}  // end for pics
+	return -1;
+}
+
+long ImageBase::FindPicEx(std::vector<Image*>& pics, color_t dfcolor, double sim, vpoint_desc_t& vpd)
+{
+	int obj_ct = 0;
+	vpd.clear();
+	vector<uint> points;
+	bool nodfcolor = color2uint(dfcolor) == 0;
+	int match_ret = 0;
+	ImageBin gimg;
+	_gray.fromImage4(_src);
+	record_sum(_gray);
+	int tnorm;
+	for (int pic_id = 0; pic_id < pics.size(); ++pic_id)
+	{
+		auto pic = pics[pic_id];
+		int use_ts_match = check_transparent(pic);
+
+		if (use_ts_match)
+			get_match_points(*pic, points);
+		else {
+			gimg.fromImage4(*pic);
+			tnorm = sum(gimg.begin(), gimg.end());
+		}
+		for (int i = 0; i < _src.height; ++i)
+		{
+			for (int j = 0; j < _src.width; ++j) {
+				// step 1. 边界检查
+				if (i + pic->height > _src.height || j + pic->width > _src.width)
+					continue;
+				// step 2. 计算最大误差
+				int max_err_ct =
+					(pic->height * pic->width - use_ts_match) * (1.0 - sim);
+				// step 3. 开始匹配
+
+				match_ret = (use_ts_match ? trans_match<false>(j, i, pic, dfcolor,
+					points, max_err_ct)
+					: real_match(j, i, &gimg, tnorm, sim));
+				if (match_ret) {
+					point_desc_t pd = { pic_id, point_t(j + _x1 + _dx, i + _y1 + _dy) };
+
+					vpd.push_back(pd);
+					++obj_ct;
+					if (obj_ct > _max_return_obj_ct) goto _quick_return;
+				}
+
+			}  // end for j
+		}    // end for i
+	}      // end for pics
+_quick_return:
+
+	return obj_ct;
+}
+
+long ImageBase::FindPicExTh(std::vector<Image*>& pics, color_t dfcolor, double sim, vpoint_desc_t& vpd)
+{
+	vpd.clear();
+	int obj_ct = 0;
+	vector<uint> points;
+	int match_ret = 0;
+	ImageBin gimg;
+	_gray.fromImage4(_src);
+	record_sum(_gray);
+	int tnorm;
+	std::vector<rect_t> blocks;
+	//将小循环放在最外面,提高处理速度
+	for (int pic_id = 0; pic_id < pics.size(); ++pic_id) {
+		auto pic = pics[pic_id];
+		int use_ts_match = check_transparent(pic);
+		if (use_ts_match)
+			get_match_points(*pic, points);
+		else {
+			gimg.fromImage4(*pic);
+			tnorm = sum(gimg.begin(), gimg.end());
+		}
+		auto pgimg = &gimg;
+		rect_t matchRect(0, 0, _src.width, _src.height);
+		matchRect.shrinkRect(pic->width, pic->height);
+		if (!matchRect.valid()) continue;
+		matchRect.divideBlock(m_threadPool.getThreadNum(),
+			matchRect.width() > matchRect.height(), blocks);
+		std::vector<std::future<vpoint_t>> results;
+		for (size_t i = 0; i < m_threadPool.getThreadNum(); ++i)
+		{
+			results.push_back(m_threadPool.enqueue(
+				[this, dfcolor, points, pgimg, tnorm](rect_t& block, Image* pic,
+					int use_ts_match, double sim)->vpoint_t {
+						vpoint_t vp;
+						// 计算最大误差
+						int max_err_ct =
+							(pic->height * pic->width - use_ts_match) * (1.0 - sim);
+						for (int i = block.y1; i < block.y2; ++i) {
+							for (int j = block.x1; j < block.x2; ++j) {
+								// 开始匹配
+								int match_ret =
+									(use_ts_match ? trans_match<false>(j, i, pic, dfcolor,
+										points, max_err_ct)
+										: real_match(j, i, pgimg, tnorm, sim));
+								// simple_match<false>(j, i, pic, dfcolor,tnorm, sim));
+								if (match_ret) {
+									vp.push_back(point_t(j + _x1 + _dx, i + _y1 + _dy));
+								}
+
+							}  // end for j
+						}    // end for i
+						return vp;
+				},
+				blocks[i], pic, use_ts_match, sim));
+			// results.push_back(r);
+		}
+		// wait all
+		for (auto&& f : results) {
+			vpoint_t vp = f.get();
+			if (vp.size() > 0) {
+
+				for (auto& p : vp) {
+					point_desc_t pd = { pic_id,p };
+
+					vpd.push_back(pd);
+					++obj_ct;
+					if (obj_ct > _max_return_obj_ct) goto _quick_return;
+				}
+
+				// return pic_id;
+			}
+		}
+
+	}
+_quick_return:
+
+	return obj_ct;
+}
+
+long ImageBase::FindColorBlock(double sim, long count, long height, long width, long& x, long& y)
+{
+	x = y = -1;
+	record_sum(_binary);
+	for (int i = 0; i <= _binary.height - height; ++i) {
+		for (int j = 0; j < _binary.width - width; ++j) {
+			if (region_sum(j, i, j + width, i + height) >= count) {
+				x = j + _x1 + _dx;
+				y = i + _y1 + _dy;
+				return 1;
+			}
+		}
+	}
+	return -1;
+}
+
+long ImageBase::FindColorBlockEx(double sim, long count, long height, long width, TString& retstr)
+{
+	record_sum(_binary);
+	int cnt = 0;
+	for (int i = 0; i <= _binary.height - height; ++i) {
+		for (int j = 0; j < _binary.width - width; ++j) {
+			if (region_sum(j, i, j + width, i + height) >= count) {
+				TCHAR buff[20];
+				wsprintf(buff, _T("%d,%d|"), j + _x1 + _dx, i + _y1 + _dy);
+				retstr += buff;
+				++cnt;
+				if (cnt > _max_return_obj_ct) goto _quick_return;
+			}
+		}
+	}
+_quick_return:
+	if (cnt) {
+		retstr.pop_back();
+	}
+	return cnt;
+}
+
+long ImageBase::FindLine(double sim, TString& outStr)
+{
+	outStr.clear();
+	int h = sqrt(_binary.width * _binary.width + _binary.height * _binary.height) + 2;
+	_sum.create(360, h);
+	//行:距离,列:角度
+	_sum.fill(0);
+	for (int i = 0; i < _binary.height; i++)
+	{
+		for (int j = 0; j < _binary.width; j++) {
+			if (_binary.at(i, j) == WORD_COLOR) {
+				for (int t = 0; t < 360; t++) {
+					int d =
+						j * cos(t * 0.0174532925) + i * sin(t * 0.0174532925);  //可以优化
+					assert(d <= h);
+					if (d >= 0) _sum.at<int>(d, t)++;
+				}
+			}
+		}
+	}
+	int maxRow = 0, maxCol = 0;
+	int maxval = -1;
+	for (int i = 0; i < _sum.height; i++)
+	{
+		for (int j = 0; j < _sum.width; j++) {
+			if (_sum.at<int>(i, j) > maxval) {
+				maxRow = i;
+				maxCol = j;
+				maxval = _sum.at<int>(i, j);
+			}
+		}
+	}
+	// setlog("degree=%d,dis=%d,val=%d", maxCol, maxRow, maxval);
+	TCHAR buffer[256];
+	wsprintf(buffer, _T("%d,%d"), maxCol, maxRow);
+	outStr = buffer;
+	return maxval;
+}
+
+template <bool nodfcolor>
+long ImageBase::simple_match(long x, long y, Image* timg, color_t dfcolor, int tnorm, double sim)
+{
+	int err_ct = 0;
+	// quick check
+	if ((double)abs(tnorm - region_sum(x, y, x + timg->width, y + timg->height)) >
+		(double)tnorm * (1.0 - sim))
+		return 0;
+	int max_error = (1.0 - sim) * timg->size();
+	uint* pscreen_top, * pscreen_bottom, * pimg_top, * pimg_bottom;
+	pscreen_top = _src.ptr<uint>(y) + x;
+	pscreen_bottom = _src.ptr<uint>(y + timg->height - 1) + x;
+	pimg_top = timg->ptr<uint>(0);
+	pimg_bottom = timg->ptr<uint>(timg->height - 1);
+	while (pscreen_top <= pscreen_bottom)
+	{
+		auto ps1 = pscreen_top, ps2 = pscreen_top + timg->width - 1;
+		auto ps3 = pscreen_bottom, ps4 = pscreen_bottom + timg->width - 1;
+		auto pt1 = pimg_top, pt2 = pimg_top + timg->width - 1;
+		auto pt3 = pimg_bottom, pt4 = pimg_bottom + timg->width - 1;
+		while (ps1 <= ps2) {
+			if (nodfcolor) {
+				if (*ps1++ != *pt1++) ++err_ct;  // top left
+				if (*ps2-- != *pt2--) ++err_ct;  // top right
+				if (*ps3++ != *pt3++) ++err_ct;  // bottom left
+				if (*ps4-- != *pt4--) ++err_ct;  // bottom right
+			}
+			else {
+				if (!IN_RANGE(*(color_t*)ps1++, *(color_t*)pt1++, dfcolor)) ++err_ct;
+				if (!IN_RANGE(*(color_t*)ps2--, *(color_t*)pt2--, dfcolor)) ++err_ct;
+				if (!IN_RANGE(*(color_t*)ps3++, *(color_t*)pt3++, dfcolor)) ++err_ct;
+				if (!IN_RANGE(*(color_t*)ps4--, *(color_t*)pt4--, dfcolor)) ++err_ct;
+			}
+
+			if (err_ct > max_error) return 0;
+		}
+		pscreen_top += _src.width;
+		pscreen_bottom -= _src.width;
+	}
+
+	return 1;
+}
+
+template <bool nodfcolor>
+long ImageBase::trans_match(long x, long y, Image* timg, color_t dfcolor, vector<uint> pts, int max_error)
+{
+	int err_ct = 0, k, dx, dy;
+	int left, right;
+	left = 0;
+	right = pts.size() - 1;
+	while (left <= right)
+	{
+		auto it = pts[left];
+		if (nodfcolor) {
+			if (_src.at<uint>(y + PTY(pts[left]), x + PTX(pts[left])) !=
+				timg->at<uint>(PTY(pts[left]), PTX(pts[left])))
+				++err_ct;
+			if (_src.at<uint>(y + PTY(pts[right]), x + PTX(pts[right])) !=
+				timg->at<uint>(PTY(pts[right]), PTX(pts[right])))
+				++err_ct;
+		}
+		else {
+			color_t cr1, cr2;
+			cr1 = _src.at<color_t>(y + PTY(pts[left]), x + PTX(pts[left]));
+			cr2 = timg->at<color_t>(PTY(pts[left]), PTX(pts[left]));
+			if (!IN_RANGE(cr1, cr2, dfcolor)) ++err_ct;
+			cr1 = _src.at<color_t>(y + PTY(pts[right]), x + PTX(pts[right]));
+			cr2 = timg->at<color_t>(PTY(pts[right]), PTX(pts[right]));
+			if (!IN_RANGE(cr1, cr2, dfcolor)) ++err_ct;
+		}
+
+		++left;
+		--right;
+		if (err_ct > max_error) return 0;
+	}
+	return 1;
+}
+
+long ImageBase::real_match(long x, long y, ImageBin* timg, int tnorm, double sim)
+{
+	// quick check
+	if ((double)abs(tnorm - region_sum(x, y, x + timg->width, y + timg->height)) / (double)tnorm > 1.0 - sim)
+		return 0;
+	int err = 0;
+	int maxErr = (1.0 - sim) * tnorm;
+	for (int i = 0; i < timg->height; i++) {
+		auto ptr = _gray.ptr(y + i) + x;
+		auto ptr2 = timg->ptr(i);
+		for (int j = 0; j < timg->width; j++) {
+			err += abs(*ptr - *ptr2);
+			ptr++;
+			ptr2++;
+		}
+		if (err > maxErr) return 0;
+	}
+
+	return 1;
+}
+
+void ImageBase::record_sum(const ImageBin& gray)
+{
+	//为了减少边界判断,这里多多加一行一列
+	_sum.create(gray.width + 1, gray.height + 1);
+	_sum.fill(0);
+	int m = _sum.height;
+	int n = _sum.width;
+	for (int i = 1; i < m; i++) {
+		for (int j = 1; j < n; j++) {
+			int s = 0;
+
+			s += _sum.at<int>(i - 1, j);
+
+			s += _sum.at<int>(i, j - 1);
+
+			s -= _sum.at<int>(i - 1, j - 1);
+			s += (int)gray.at(i - 1, j - 1);
+			_sum.at<int>(i, j) = s;
+		}
+	}
+}
+
+int ImageBase::region_sum(int x1, int y1, int x2, int y2) {
+	int ans = _sum.at<int>(y2, x2) - _sum.at<int>(y2, x1) - _sum.at<int>(y1, x2) +
+		_sum.at<int>(y1, x1);
+	return ans;
+}
+
+constexpr int MIN_CUT_W = 5;
+constexpr int MIN_CUT_H = 2;
+
+int ImageBase::get_bk_color(inputbin bin) {
+	int y[256] = { 0 };
+	auto ptr = bin.pixels.data();
+	int n = bin.pixels.size();
+	for (int i = 0; i < n; ++i) y[ptr[i]]++;
+	// scan max
+	int m = 0;
+	for (int i = 1; i < 256; ++i) {
+		if (y[i] > y[m]) m = i;
+	}
+	return m;
+}
+
+void ImageBase::bgr2binary(vector<color_df_t>& colors) {
+	if (_src.empty()) return;
+	int ncols = _src.width, nrows = _src.height;
+	_binary.fromImage4(_src);
+	for (int i = 0; i < nrows; ++i) {
+		auto psrc = _src.ptr<color_t>(i);
+
+		auto pbin = _binary.ptr(i);
+		for (int j = 0; j < ncols; ++j) {
+			uchar g1 = psrc->toGray();
+			*pbin = WORD_BKCOLOR;
+			for (auto& it : colors) {  //对每个颜色描述
+				if (abs(g1 - it.color.toGray()) <= it.df.toGray()) {
+					*pbin = WORD_COLOR;
+					break;
+				}
+			}
+			++pbin;
+			++psrc;
+		}
+	}
+	//_binary.create(ncols, nrows);
+	// for (int i = 0; i < nrows; ++i) {
+	//	auto psrc = _src.ptr<color_t>(i);
+	//	auto pbin = _binary.ptr(i);
+	//	for (int j = 0; j < ncols; ++j) {
+	//		*pbin = WORD_BKCOLOR;
+	//		for (auto& it : colors) {//对每个颜色描述
+	//			if (IN_RANGE(*psrc, it.color, it.df)) {
+	//				*pbin = WORD_COLOR;
+	//				break;
+	//			}
+	//		}
+	//		++pbin; ++psrc;
+	//	}
+	//}
+	// test
+	// cv::imwrite("src.png", _src);
+	// cv::imwrite("binary.png", _binary);
+}
+
+//二值化
+void ImageBase::bgr2binarybk(const vector<color_df_t>& bk_colors) {
+	//创建二值图
+	_binary.create(_src.width, _src.height);
+	memset(_binary.pixels.data(), WORD_BKCOLOR, _binary.size());
+	int n = _binary.size();
+	auto pdst = _binary.data();
+	if (bk_colors.size() == 0) {  // auto
+	  //转为灰度图
+		_gray.fromImage4(_src);
+
+		//获取背景颜色
+		int bkcolor = get_bk_color(_gray);
+
+		auto pgray = _gray.data();
+		for (int i = 0; i < n; ++i) {
+			pdst[i] =
+				(std::abs((int)pgray[i] - bkcolor) < 20 ? WORD_BKCOLOR : WORD_COLOR);
+		}
+	}
+	else {
+		for (auto bk : bk_colors) {
+			for (int i = 0; i < n; ++i) {
+				auto c = (color_t*)(_src.pdata + i * 4);
+				if (!IN_RANGE(*c, bk.color, bk.df)) pdst[i] = WORD_COLOR;
+			}
+		}
+	}
+}
+
+//垂直方向投影到x轴
+void ImageBase::binshadowx(const rect_t& rc, std::vector<rect_t>& out_put) {
+	// qDebug("in x rc:%d,%d,%d,%d", rc.x1, rc.y1, rc.x2, rc.y2);
+	out_put.clear();
+	// ys.clear();
+	// Mat paintx(binary.size(), CV_8UC1, cv::Scalar(255));
+	// //创建一个全白图片,用作显示
+
+	// int* blackcout = new int[binary.cols];
+	std::vector<int> vx;
+	vx.resize(_binary.width);
+	memset(&vx[0], 0, _binary.width * 4);
+	for (int j = rc.x1; j < rc.x2; j++) {
+		for (int i = rc.y1; i < rc.y2; i++) {
+			if (_binary.at(i, j) == WORD_COLOR) {
+				vx[j]++;  //垂直投影按列在x轴进行投影
+			}
+		}
+	}
+
+	int startindex = 0;
+	int endindex = 0;
+	bool inblock = false;  //是否遍历到字符位置
+	rect_t roi;
+	for (int j = rc.x1; j < rc.x2; j++) {
+		if (!inblock && vx[j] != 0)  //进入有字符区域
+		{
+			inblock = true;
+			startindex = j;
+			// std::cout << "startindex:" << startindex << std::endl;
+		}
+		// if (inblock&&vx[j] == 0) //进入空白区
+		else if (inblock && vx[j] == 0 &&
+			j - startindex >= MIN_CUT_W)  //进入空白区域,且宽度不小于1
+		{
+			endindex = j;
+			inblock = false;
+			// Mat roi = binary.colRange(startindex, endindex + 1);
+
+			roi.x1 = startindex;
+			roi.y1 = rc.y1;
+			roi.x2 = endindex;
+			roi.y2 = rc.y2;
+			// qDebug("out xrc:%d,%d,%d,%d", roi.x1, roi.y1, roi.x2, roi.y2);
+			out_put.push_back(roi);
+		}
+	}
+	// special case
+	if (inblock) {
+		roi.x1 = startindex;
+		roi.y1 = rc.y1;
+		roi.x2 = rc.x2;
+		roi.y2 = rc.y2;
+		out_put.push_back(roi);
+	}
+}
+
+//投影到y轴
+void ImageBase::binshadowy(const rect_t& rc, std::vector<rect_t>& out_put) {
+	// qDebug("in y rc:%d,%d,%d,%d", rc.x1, rc.y1, rc.x2, rc.y2);
+	out_put.clear();
+	//是否为白色或者黑色根据二值图像的处理得来
+	// Mat painty(binary.size(), CV_8UC1, cv::Scalar(255)); //初始化为全白
+	//水平投影
+	// int* pointcount = new int[binary.rows]; //在二值图片中记录行中特征点的个数
+	std::vector<int> vy;
+	vy.resize(_binary.height);
+	memset(&vy[0], 0, _binary.height * 4);  //注意这里需要进行初始化
+
+	for (int i = rc.y1; i < rc.y2; i++) {
+		for (int j = rc.x1; j < rc.x2; j++) {
+			if (_binary.at(i, j) == WORD_COLOR) {
+				vy[i]++;  //记录每行中黑色点的个数 //水平投影按行在y轴上的投影
+			}
+		}
+	}
+
+	// std::vector<Mat> result;
+	int startindex = 0;
+	int endindex = 0;
+	bool inblock = false;  //是否遍历到字符位置
+	rect_t roi;
+	for (int i = rc.y1; i < rc.y2; i++) {
+		if (!inblock && vy[i] != 0)  //进入有字符区域
+		{
+			inblock = true;
+			startindex = i;
+			// std::cout << "startindex:" << startindex << std::endl;
+		}
+		// if (inblock&&vy[i] == 0) //进入空白区
+		if (inblock && vy[i] == 0 &&
+			i - startindex >= MIN_CUT_H)  //进入空白区,且高度不小于1
+		{
+			endindex = i;
+			inblock = false;
+
+			roi.x1 = rc.x1;
+			roi.y1 = startindex;
+			roi.x2 = rc.x2;
+			roi.y2 = endindex;
+			out_put.push_back(roi);
+		}
+	}
+
+	if (inblock) {
+		roi.x1 = rc.x1;
+		roi.y1 = startindex;
+		roi.x2 = rc.x2;
+		roi.y2 = rc.y2;
+		out_put.push_back(roi);
+	}
+}
+
+void ImageBase::bin_image_cut(int min_word_h, const rect_t& inrc, rect_t& outrc)
+{
+	//水平裁剪,缩小高度
+	std::vector<int> v;
+	outrc = inrc;
+	int i, j;
+	v.resize(_binary.height);
+	for (auto& it : v) it = 0;
+	for (i = inrc.y1; i < inrc.y2; ++i) {
+		for (j = inrc.x1; j < inrc.x2; ++j)
+			v[i] += (_binary.at(i, j) == WORD_COLOR ? 1 : 0);
+	}
+	i = inrc.y1;
+	while (v[i] == 0) i++;
+	outrc.y1 = i;
+	i = inrc.y2 - 1;
+	while (v[i] == 0) i--;
+	if (i + 1 - outrc.y1 > min_word_h) outrc.y2 = i + 1;
+	//垂直裁剪.缩小宽度
+	v.resize(_binary.width);
+	for (auto& it : v) it = 0;
+
+	for (i = inrc.y1; i < inrc.y2; ++i) {
+		for (j = inrc.x1; j < inrc.x2; ++j)
+			v[j] += _binary.at(i, j) == WORD_COLOR ? 1 : 0;
+	}
+	i = inrc.x1;
+	while (v[i] == 0) i++;
+	outrc.x1 = i;
+	i = inrc.x2 - 1;
+	while (v[i] == 0) i--;
+	outrc.x2 = i + 1;
+}
+
+void ImageBase::get_rois(int min_word_h, std::vector<rect_t>& vroi)
+{
+	vroi.clear();
+	std::vector<rect_t> vrcx, vrcy;
+	rect_t rc;
+	rc.x1 = rc.y1 = 0;
+	rc.x2 = _binary.width;
+	rc.y2 = _binary.height;
+	binshadowy(rc, vrcy);
+	for (int i = 0; i < vrcy.size(); ++i) {
+		binshadowx(vrcy[i], vrcx);
+		for (int j = 0; j < vrcx.size(); j++) {
+			if (vrcx[j].width() >= min_word_h)
+				bin_image_cut(min_word_h, vrcx[j], vrcx[j]);
+			vroi.push_back(vrcx[j]);
+		}
+	}
+}
+
+inline int full_match(const ImageBin& binary, rect_t& rc, const uint8_t* data) {
+	//匹配
+	int idx = 0;
+	for (int x = rc.x1; x < rc.x2; ++x) {
+		for (int y = rc.y1; y < rc.y2; ++y) {
+			int val = GET_BIT(data[idx / 8], idx & 7);
+			if (binary.at(y, x) != val) return 0;
+			idx++;
+		}
+	}
+	return 1;
+}
+
+inline int part_match(const ImageBin& binary, rect_t& rc, int max_error,
+	const uint8_t* data) {
+	//匹配
+	//匹配
+	int err_ct = 0;
+	int idx = 0;
+	for (int x = rc.x1; x < rc.x2; ++x) {
+		for (int y = rc.y1; y < rc.y2; ++y) {
+			int val = GET_BIT(data[idx / 8], idx & 7);
+			if (binary.at(y, x) != val) {
+				++err_ct;
+				if (err_ct > max_error) return 0;
+			}
+			idx++;
+		}
+	}
+	return 1;
+}
+
+inline void fill_rect(ImageBin& record, const rect_t& rc) {
+	int w = rc.width();
+	for (int y = rc.y1; y < rc.y2; ++y) {
+		uchar* p = record.ptr(y) + rc.x1;
+		memset(p, 1, sizeof(uchar) * w);
+	}
+}

+ 162 - 0
GameAssist/IMGProc/ImageLoc.h

@@ -0,0 +1,162 @@
+#pragma once
+#ifndef __IMAGELOC_H_
+#define __IMAGELOC_H_
+/*
+常见的图像算法,例如图像查找,颜色序列匹配(多点着色)
+由于ocr与图像查找类似,故也在类ImageLoc中实现
+*/
+#include <vector>
+#include "optype.h"
+#include <string>
+//#include "Dict.h"
+#include "color.h"
+#include "ThreadPool.h"
+#include "Image.hpp"
+
+inline int HEX2INT(wchar_t c) {
+	if (L'0' <= c && c <= L'9')
+		return c - L'0';
+	if (L'A' <= c && c <= L'Z')
+		return c - L'A' + 10;
+	if (L'a' <= c && c <= L'z')
+		return c - L'a' + 10;
+	return 0;
+}
+
+
+#define SET_BIT(x, idx) (x |= 1u << (idx))
+
+#define GET_BIT(x, idx) ((x >> (idx)) & 1u)
+
+using img_names = std::vector<TString>;
+//检查是否为透明图
+int check_transparent(Image* img);
+//获取匹配点
+void get_match_points(const Image& img, vector<int>& points);
+//generate next index for kmp
+void gen_next(const Image& img, vector<int>& next);
+//sum of all pixels
+int inline sum(uchar* begin, uchar* end) {
+	int s = 0;
+	while (begin != end)
+		s += *begin++;
+	return s;
+}
+
+void extractConnectivity(const ImageBin& src, int threshold, std::vector<ImageBin>& out);
+
+
+struct gray_diff_t {
+	gray_diff_t(color_df_t const& cd) :gray(cd.color.toGray()), diff(cd.df.toGray()) {
+
+	}
+	unsigned char gray;
+	unsigned char diff;
+};
+/*
+此类用于实现一些图像功能,如图像定位,简单ocr等
+*/
+class ImageBase
+{
+public:
+
+	const static int _max_return_obj_ct = 1800;
+
+	using vcolor_diff_t = vector<color_df_t>;//rgb color-diff
+	using vgray_diff_t = vector<gray_diff_t>;//gray
+
+	ImageBase();
+
+	~ImageBase();
+
+	//brief:输入图像,建立图形矩阵,在图像操作前调用
+	//image_data:	4子节对齐的像素指针
+	//widht:		图像宽度
+	//hegith:		h
+	//x1,y1,x2,y2 拷贝区域
+	//type:			输入类型,type=0表示正常输入,为-1时表示倒置输入
+	//long input_image(byte* psrc, int cols, int height,long x1,long y1,long x2,long y2, int type = 0);
+
+	void set_offset(int x1, int y1);
+
+	long is_valid(long x, long y)
+	{
+		return x >= 0 && y >= 0 && x < _src.width&& y < _src.height;
+	}
+
+	long GetPixel(long x, long y, color_t& cr);
+
+	long CmpColor(color_t color, std::vector<color_df_t>& colors, double sim);
+
+	long FindColor(std::vector<color_df_t>& colors, int dir, long& x, long& y);
+
+	long FindColorEx(std::vector<color_df_t>& colors, TString& retstr);
+
+	long FindMultiColor(std::vector<color_df_t>& first_color, std::vector<pt_cr_df_t>& offset_color, double sim, long dir, long& x, long& y);
+
+	long FindMultiColorEx(std::vector<color_df_t>& first_color, std::vector<pt_cr_df_t>& offset_color, double sim, long dir, TString& retstr);
+
+	long FindPic(std::vector<Image*>& pics, color_t dfcolor, double sim, long& x, long& y);
+
+	long FindPicTh(std::vector<Image*>& pics, color_t dfcolor, double sim, long& x, long& y);
+
+	long FindPicEx(std::vector<Image*>& pics, color_t dfcolor, double sim, vpoint_desc_t& vpd);
+
+	long FindPicExTh(std::vector<Image*>& pics, color_t dfcolor, double sim, vpoint_desc_t& vpd);
+
+	long FindColorBlock(double sim, long count, long height, long width, long& x, long& y);
+
+	long FindColorBlockEx(double sim, long count, long height, long width, TString& retstr);
+
+	//描述:查找目标图像中的直线
+	//输入:精度
+	//输出:outStr:直线描述[法线角度,直线到原点的距离];ret:该直线上的点的数量
+	long FindLine(double sim, TString& outStr);
+private:
+	//rgb像素匹配
+	template<bool nodfcolor>
+	long simple_match(long x, long y, Image* timg, color_t dfcolor, int tnrom, double sim);
+	//透明图匹配
+	template<bool nodfcolor>
+	long trans_match(long x, long y, Image* timg, color_t dfcolor, vector<uint>points, int max_error);
+	//灰度匹配
+	long real_match(long x, long y, ImageBin* timg, int tnorm, double sim);
+	//记录和
+	void record_sum(const ImageBin& gray);
+	//[x1,x2),[y1,y2)
+	int region_sum(int x1, int y1, int x2, int y2);
+
+	int get_bk_color(inputbin bin);
+
+	//垂直方向投影,投到x轴
+	void binshadowx(const rect_t& rc, std::vector<rect_t>& out_put);
+	//水平方向投影,投到(y)轴
+	void binshadowy(const rect_t& rc, std::vector<rect_t>& out_put);
+public:
+	/*
+	if(abs(cr-src)<=df) pixel=1;
+	else pixel=0;
+	*/
+	void bgr2binary(vector<color_df_t>& colors);
+	//二值化 auto
+	void bgr2binarybk(const vector<color_df_t>& bk_colors);
+	//图像裁剪
+	void bin_image_cut(int min_word_h, const rect_t& inrc, rect_t& outrc);
+	void get_rois(int min_word_h, std::vector<rect_t>& vroi);
+
+public:
+	Image _src;
+	ImageBin _gray;
+	ImageBin _record;
+	ImageBin _binary;
+	Image _sum;
+private:
+	//起始点
+	int _x1, _y1;
+	//偏移
+	int _dx, _dy;
+	ThreadPool m_threadPool;
+};
+
+#endif
+

+ 356 - 0
GameAssist/IMGProc/ImageProc.cpp

@@ -0,0 +1,356 @@
+//#include "stdafx.h"
+#include "ImageProc.h"
+#include "helpfunc.h"
+#include <fstream>
+#include <bitset>
+#include <algorithm>
+#include <sstream>
+
+ImageProc::ImageProc()
+{
+	_enable_cache = 1;
+}
+
+ImageProc::~ImageProc()
+{
+}
+
+long ImageProc::Capture(const TString& file)
+{
+	TString fpath = file;
+	if (fpath.find(_T('\\')) == -1)
+		fpath = _curr_path + _T("\\") + fpath;
+
+	return _src.write(fpath.c_str());
+}
+
+long ImageProc::CmpColor(long x, long y, const TString& scolor, double sim)
+{
+	std::vector<color_df_t> vcolor;
+	str2colordfs(scolor, vcolor);
+	return ImageBase::CmpColor(_src.at<color_t>(0, 0), vcolor, sim);
+}
+
+long ImageProc::FindColor(const TString& color, double sim, long dir, long& x, long& y)
+{
+	std::vector<color_df_t> colors;
+	str2colordfs(color, colors);
+	//setlog("%s cr size=%d",colors[0].color.tostr().data(), colors.size());
+	//setlog("sim:,dir:%d", dir);
+	return ImageBase::FindColor(colors, dir, x, y);
+}
+
+long ImageProc::FindColoEx(const TString& color, double sim, long dir, TString& retstr)
+{
+	std::vector<color_df_t> colors;
+	str2colordfs(color, colors);
+	return ImageBase::FindColorEx(colors, retstr);
+}
+
+long ImageProc::FindMultiColor(const TString& first_color, const TString& offset_color, double sim, long dir, long& x, long& y)
+{
+	std::vector<color_df_t> vfirst_color;
+	str2colordfs(first_color, vfirst_color);
+	std::vector<TString> vseconds;
+	split(offset_color, vseconds, _T(","));
+	std::vector<pt_cr_df_t> voffset_cr;
+	pt_cr_df_t tp;
+	for (auto& it : vseconds)
+	{
+		size_t id1, id2;
+		id1 = it.find(_T('|'));
+		id2 = (id1 == TString::npos ? TString::npos : it.find(L'|', id1));
+		if (id2 != TString::npos)
+		{
+			sscanf(it.c_str(), _T("%d|%d"), &tp.x, &tp.y);
+			if (id2 + 1 != it.length())
+				str2colordfs(it.substr(id2 + 1), tp.crdfs);
+			else
+				break;
+			voffset_cr.push_back(tp);
+		}
+	}
+	return ImageBase::FindMultiColor(vfirst_color, voffset_cr, sim, dir, x, y);
+}
+
+long ImageProc::FindMultiColorEx(const TString& first_color, const TString& offset_color, double sim, long dir, TString& retstr)
+{
+	std::vector<color_df_t> vfirst_color;
+	str2colordfs(first_color, vfirst_color);
+	std::vector<TString> vseconds;
+	split(offset_color, vseconds, _T(","));
+	std::vector<pt_cr_df_t> voffset_cr;
+	pt_cr_df_t tp;
+	for (auto& it : vseconds)
+	{
+		size_t id1, id2;
+		id1 = it.find(_T('|'));
+		id2 = (id1 == TString::npos ? TString::npos : it.find(_T('|'), id1));
+		if (id2 != TString::npos)
+		{
+			_stscanf(it.c_str(), _T("%d|%d"), &tp.x, &tp.y);
+			if (id2 + 1 != it.length())
+				str2colordfs(it.substr(id2 + 1), tp.crdfs);
+			else
+				break;
+			voffset_cr.push_back(tp);
+		}
+	}
+	return ImageBase::FindMultiColorEx(vfirst_color, voffset_cr, sim, dir, retstr);
+}
+//图形定位
+long ImageProc::FindPic(const TString& files, const TString& delta_colors, double sim, long dir, long& x, long& y)
+{
+	vector<Image*> vpic;
+	//vector<color_t> vcolor;
+	color_t dfcolor;
+	vector<TString> vpic_name;
+	files2mats(files, vpic, vpic_name);
+	dfcolor.str2color(delta_colors);
+	//str2colors(delta_colors, vcolor);
+	sim = 0.5 + sim / 2;
+	//long ret = ImageBase::FindPic(vpic, dfcolor, sim, x, y);
+	long ret = ImageBase::FindPicTh(vpic, dfcolor, sim, x, y);
+	//清理缓存
+	if (!_enable_cache)
+		_pic_cache.clear();
+	return ret;
+}
+//
+long ImageProc::FindPicEx(const TString& files, const TString& delta_colors, double sim, long dir, TString& retstr, bool returnID)
+{
+	vector<Image*> vpic;
+	vpoint_desc_t vpd;
+	//vector<color_t> vcolor;
+	color_t dfcolor;
+	vector<TString> vpic_name;
+	files2mats(files, vpic, vpic_name);
+	dfcolor.str2color(delta_colors);
+	sim = 0.5 + sim / 2;
+	long ret = ImageBase::FindPicExTh(vpic, dfcolor, sim, vpd);
+#ifdef UNICODE
+	std::wstringstream ss(std::wstringstream::in | std::wstringstream::out);
+#else
+	std::stringstream ss(std::stringstream::in | std::stringstream::out);
+#endif
+	if (returnID)
+	{
+		for (auto& it : vpd)
+		{
+			ss << it.id << _T(",") << it.pos << _T("|");
+		}
+	}
+	else
+	{
+		for (auto& it : vpd)
+		{
+			ss << vpic_name[it.id] << _T(",") << it.pos << _T("|");
+		}
+	}
+	//清理缓存
+	if (!_enable_cache)
+		_pic_cache.clear();
+	retstr = ss.str();
+	if (vpd.size())
+		retstr.pop_back();
+	return ret;
+}
+
+long ImageProc::FindColorBlock(const TString& color, double sim, long count, long height, long width, long& x, long& y)
+{
+	vector<color_df_t> colors;
+	if (str2colordfs(color, colors) == 0)
+	{
+		bgr2binary(colors);
+	}
+	else
+	{
+		bgr2binarybk(colors);
+	}
+	return ImageBase::FindColorBlock(sim, count, height, width, x, y);
+}
+
+long ImageProc::FindColorBlockEx(const TString& color, double sim, long count, long height, long width, TString& retstr)
+{
+	vector<color_df_t> colors;
+	if (str2colordfs(color, colors) == 0)
+	{
+		bgr2binary(colors);
+	}
+	else
+	{
+		bgr2binarybk(colors);
+	}
+	return ImageBase::FindColorBlockEx(sim, count, height, width, retstr);
+}
+
+TString ImageProc::GetColor(long x, long y)
+{
+	color_t cr;
+	if (ImageBase::GetPixel(x, y, cr))
+	{
+		return cr.tostr();
+	}
+	else
+	{
+		return _T("");
+	}
+}
+
+int ImageProc::str2colordfs(const TString& color_str, std::vector<color_df_t>& colors)
+{
+	std::vector<TString> vstr, vstr2;
+	color_df_t cr;
+	colors.clear();
+	int ret = 0;
+	if (color_str.empty())
+	{ //default
+		return 1;
+	}
+	if (color_str[0] == _T('@'))
+	{ //bk color info
+		ret = 1;
+	}
+	split(ret ? color_str.substr(1) : color_str, vstr, _T("|"));
+	for (auto& it : vstr)
+	{
+		split(it, vstr2, _T("-"));
+		cr.color.str2color(vstr2[0]);
+		cr.df.str2color(vstr2.size() == 2 ? vstr2[1] : _T("000000"));
+		colors.push_back(cr);
+	}
+	return ret;
+}
+
+void ImageProc::str2colors(const TString& color, std::vector<color_t>& vcolor)
+{
+	std::vector<TString> vstr, vstr2;
+	color_t cr;
+	vcolor.clear();
+	split(color, vstr, _T("|"));
+	for (auto& it : vstr)
+	{
+		cr.str2color(it);
+		vcolor.push_back(cr);
+	}
+}
+
+long ImageProc::LoadPic(const TString& files)
+{
+	//std::vector<TString>vstr, vstr2;
+	std::vector<TString> vstr;
+	int loaded = 0;
+	split(files, vstr, _T("|"));
+	TString tp;
+	for (auto& it : vstr)
+	{
+		//路径转化
+		if (!Path2GlobalPath(it, _curr_path, tp))
+			continue;
+		//先在缓存中查找
+		if (!_pic_cache.count(tp))
+		{
+			_pic_cache[tp].read(tp.data());
+		}
+		//已存在于缓存中的文件也算加载成功
+		loaded++;
+	}
+	return loaded;
+}
+
+long ImageProc::FreePic(const TString& files)
+{
+	std::vector<TString> vstr;
+	int loaded = 0;
+	split(files, vstr, _T("|"));
+	TString tp;
+	for (auto& it : vstr)
+	{
+		//看当前目录
+		auto cache_it = _pic_cache.find(it);
+		//没查到再看一下资源目录
+		if (cache_it == _pic_cache.end())
+		{
+			cache_it = _pic_cache.find(_curr_path + _T("\\") + it);
+		}
+		//查到了就释放
+		if (cache_it != _pic_cache.end())
+		{
+			cache_it->second.release();
+			_pic_cache.erase(cache_it);
+			loaded++;
+		}
+	}
+	return loaded;
+}
+
+long ImageProc::LoadMemPic(const TString& file_name, void* data, long size)
+{
+	try
+	{
+		if (!_pic_cache.count(file_name))
+		{
+			_pic_cache[file_name].read(data, size);
+		}
+	}
+	catch (...)
+	{
+		return 0;
+	}
+	return 1;
+}
+
+void ImageProc::files2mats(const TString& files, std::vector<Image*>& vpic, std::vector<TString>& vstr)
+{
+	//std::vector<TString>vstr, vstr2;
+	Image* pm;
+	vpic.clear();
+	split(files, vstr, _T("|"));
+	TString tp;
+	for (auto& it : vstr)
+	{
+		//先在缓存中查找是否已加载,包括从内存中加载的文件
+		if (_pic_cache.count(it))
+		{
+			pm = &_pic_cache[it];
+		}
+		else
+		{
+			//路径转化
+			if (!Path2GlobalPath(it, _curr_path, tp))
+				continue;
+			//再检测一次,包括绝对路径的文件
+			if (_pic_cache.count(tp))
+			{
+				pm = &_pic_cache[tp];
+			}
+			else
+			{
+				_pic_cache[tp].read(tp.data());
+				pm = &_pic_cache[tp];
+			}
+		}
+		vpic.push_back(pm);
+	}
+}
+
+long ImageProc::FindLine(const TString& color, double sim, TString& retStr)
+{
+	retStr.clear();
+	vector<color_df_t> colors;
+	if (str2colordfs(color, colors) == 0)
+	{
+		bgr2binary(colors);
+	}
+	else
+	{
+		bgr2binarybk(colors);
+	}
+	if (sim < 0. || sim > 1.)
+		sim = 1.;
+
+	_src.write(_T("_src.bmp"));
+	_gray.write(_T("gray.bmp"));
+	_binary.write(_T("_binary.bmp"));
+	return ImageBase::FindLine(sim, retStr);
+}

+ 72 - 0
GameAssist/IMGProc/ImageProc.h

@@ -0,0 +1,72 @@
+#pragma once
+#include <string>
+#include "ImageLoc.h"
+#include <map>
+
+#ifdef UNICODE 
+using TString = std::wstring;
+#else
+using TString = std::string;
+#endif
+
+/*
+此类为图像处理,包含以下工作
+1.像素比较,查找
+2.颜色转化
+3.图像定位
+4.简单OCR
+5....
+*/
+class ImageProc :public ImageBase
+{
+public:
+	const static int _max_dict = 10;
+
+	ImageProc();
+	~ImageProc();
+	//
+	long Capture(const TString& file);
+
+	long CmpColor(long x, long y, const TString& scolor, double sim);
+
+	long FindColor(const TString& color, double sim, long dir, long& x, long& y);
+
+	long FindColoEx(const TString& color, double sim, long dir, TString& retstr);
+
+	long FindMultiColor(const TString& first_color, const TString& offset_color, double sim, long dir, long& x, long& y);
+
+	long FindMultiColorEx(const TString& first_color, const TString& offset_color, double sim, long dir, TString& retstr);
+	//图形定位
+	long FindPic(const TString& files, const TString& delta_colors, double sim, long dir, long& x, long& y);
+	//
+	long FindPicEx(const TString& files, const TString& delta_colors, double sim, long dir, TString& retstr, bool returnID = true);
+
+	long FindColorBlock(const TString& color, double sim, long count, long height, long width, long& x, long& y);
+
+	long FindColorBlockEx(const TString& color, double sim, long count, long height, long width, TString& retstr);
+
+	TString GetColor(long x, long y);
+
+	long FindLine(const TString& color, double sim, TString& retStr);
+
+	long LoadPic(const TString& files);
+
+	long FreePic(const TString& files);
+
+	long LoadMemPic(const TString& file_name, void* data, long size);
+
+public:
+	//当前目录
+	TString _curr_path;
+	//图片缓存
+	std::map<TString, Image> _pic_cache;
+	//是否使用图片缓存,默认开启
+	int _enable_cache;
+	//tesseract::TessBaseAPI _tes;
+private:
+	//RETURN TYPE 0:word colors info; 1:bk color info
+	int str2colordfs(const TString& color_str, std::vector<color_df_t>& colors);
+	void str2colors(const TString& color, std::vector<color_t>& vcolor);
+	void files2mats(const TString& files, std::vector<Image*>& vpic, std::vector<TString>& vstr);
+};
+

+ 99 - 0
GameAssist/IMGProc/ThreadPool.h

@@ -0,0 +1,99 @@
+#ifndef THREAD_POOL_H
+#define THREAD_POOL_H
+
+#include <vector>
+#include <queue>
+#include <memory>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <future>
+#include <functional>
+#include <stdexcept>
+
+class ThreadPool
+{
+public:
+	ThreadPool(size_t);
+	template<class F, class... Args>
+	auto enqueue(F&& f, Args&&... args)
+		->std::future<typename std::result_of<F(Args...)>::type>;
+	~ThreadPool();
+	size_t getThreadNum()const {
+		return workers.size();
+	}
+private:
+	// need to keep track of threads so we can join them
+	std::vector< std::thread > workers;
+	// the task queue
+	std::queue< std::function<void()> > tasks;
+
+	// synchronization
+	std::mutex queue_mutex;
+	std::condition_variable condition;
+	bool stop;
+};
+
+// the constructor just launches some amount of workers
+inline ThreadPool::ThreadPool(size_t threads) :stop(false)
+{
+	for (size_t i = 0;i < threads;++i)
+	{
+		workers.emplace_back([this] {
+			for (;;)
+			{
+				std::function<void()> task;
+
+				{
+					std::unique_lock<std::mutex> lock(this->queue_mutex);
+					this->condition.wait(lock,
+						[this] { return this->stop || !this->tasks.empty(); });
+					if (this->stop && this->tasks.empty())
+						return;
+					task = std::move(this->tasks.front());
+					this->tasks.pop();
+				}
+
+				task();
+			}
+			});
+	}
+}
+
+// add new work item to the pool
+template<class F, class... Args>
+auto ThreadPool::enqueue(F&& f, Args&&... args)
+-> std::future<typename std::result_of<F(Args...)>::type>
+{
+	using return_type = typename std::result_of<F(Args...)>::type;
+
+	auto task = std::make_shared< std::packaged_task<return_type()> >(
+		std::bind(std::forward<F>(f), std::forward<Args>(args)...)
+		);
+
+	std::future<return_type> res = task->get_future();
+	{
+		std::unique_lock<std::mutex> lock(queue_mutex);
+
+		// don't allow enqueueing after stopping the pool
+		if (stop)
+			throw std::runtime_error("enqueue on stopped ThreadPool");
+
+		tasks.emplace([task]() { (*task)(); });
+	}
+	condition.notify_one();
+	return res;
+}
+
+// the destructor joins all threads
+inline ThreadPool::~ThreadPool()
+{
+	{
+		std::unique_lock<std::mutex> lock(queue_mutex);
+		stop = true;
+	}
+	condition.notify_all();
+	for (std::thread& worker : workers)
+		worker.join();
+}
+#endif

+ 22 - 0
GameAssist/IMGProc/bitfunc.h

@@ -0,0 +1,22 @@
+...........................................#pragma once
+#ifndef __BITFUNC_H_
+#define __BITFUNC_H_
+template<typename T>
+constexpr void SET_BIT(T& x,int idx) {
+	x |= 1u << idx;
+}
+template<typename T>
+constexpr int GET_BIT(T x, int idx) {
+	return (x >> idx) & 1u;
+}
+
+template<typename T>
+constexpr int get_bit_count(T x) {
+	int s = 0;
+	while (x) {
+		s += x & 1;
+		x >>= 1;
+	}
+	return s;
+}
+#endif

+ 66 - 0
GameAssist/IMGProc/color.h

@@ -0,0 +1,66 @@
+#pragma once
+#ifndef __COLOR_H_
+#define __COLOR_H_
+#include <algorithm>
+#include "optype.h"
+#include <tchar.h>
+#define WORD_BKCOLOR 0
+#define WORD_COLOR 1
+#include <math.h>
+
+#define color2uint(color) (*(uint*)&color)
+
+template<typename T>
+constexpr T OP_ABS(T x) {
+	return x < 0 ? -x : x;
+}
+
+template<typename T>
+constexpr bool IN_RANGE(T lhs, T rhs, T df) { 
+	return OP_ABS(lhs.b-rhs.b)<=df.b
+		&&OP_ABS(lhs.g-rhs.g)<=df.g
+		&&OP_ABS(lhs.r-rhs.r)<=df.r;
+}
+
+//#pragma pack(push)
+#pragma pack(1)
+struct color_t 
+{
+	//b is in low address ,alpha is in high address
+	uchar b, g, r, alpha;
+	color_t() :b(0), g(0), r(0), alpha(0) {}
+	color_t(int b_, int g_, int r_) :b(b_), g(g_), r(r_),alpha(0xffu) {}
+	
+	color_t& str2color(const TString&s) {
+		int r, g, b;
+		TString ss = s;
+		std::transform(ss.begin(), ss.end(), ss.begin(), ::toupper);
+		int cnt = _stscanf(ss.c_str(), _T("%02X%02X%02X"), &r, &g, &b);
+		this->b = b; this->r = r; this->g = g;
+		return *this;
+	}
+
+	std::string tostr() {
+		TCHAR buff[10];
+		_stprintf(buff, _T("%02X%02X%02X"), r, g, b);
+		return buff;
+	}
+
+	uchar toGray() const{
+		return (r * 299 + g * 587 + b * 114 + 500) / 1000;
+	}
+};
+
+#pragma pack()
+
+struct color_df_t {
+	color_t color;
+	color_t df;
+};
+
+struct pt_cr_df_t {
+	int x, y;
+	std::vector<color_df_t> crdfs;
+};
+
+#endif

+ 17 - 0
GameAssist/IMGProc/frameInfo.h

@@ -0,0 +1,17 @@
+#ifndef __FRAME_INFO_H_
+#define __FRAME_INFO_H_
+#pragma pack(1)
+struct FrameInfo {
+	unsigned __int64 hwnd;
+	unsigned __int32 frameId;
+	unsigned __int32 time;
+	unsigned __int32 width;
+	unsigned __int32 height;
+	unsigned __int32 chk;
+	void fmtChk() {
+		chk = (hwnd >> 32) ^ (hwnd & 0xffffffffull) ^ frameId ^ time ^ width ^ height;
+	}
+	
+};
+#pragma pack()
+#endif // !__FRAME_INFO_H_

+ 13 - 0
GameAssist/IMGProc/globalVar.cpp

@@ -0,0 +1,13 @@
+//#include "stdafx.h"
+#include "optype.h"
+#include "globalVar.h"
+#include <time.h>
+
+//HINSTANCE gInstance;
+
+//int gShowError = 1;
+
+//wstring m_opPath;
+
+//wstring g_op_name;
+

+ 98 - 0
GameAssist/IMGProc/globalVar.h

@@ -0,0 +1,98 @@
+#pragma once
+#ifndef __GLOBALVAR_H_
+#define __GLOBALVAR_H_
+#include "optype.h"
+
+#define SAFE_CLOSE(h)   \
+	if (h)              \
+		CloseHandle(h); \
+	h = NULL;
+
+template <class Type>
+void SAFE_DELETE(Type *&ptr)
+{
+	delete ptr;
+	ptr = nullptr;
+}
+
+#define SAFE_DELETE_ARRAY(ptr) \
+	if (ptr)                   \
+		delete[] ptr;          \
+	ptr = nullptr
+
+#define SAFE_RELEASE(obj) \
+	if (obj)              \
+		obj->Release();   \
+	obj = nullptr
+
+//#define _sto_wstring(s) boost::locale::conv::to_utf<wchar_t>(s, "GBK")
+//#define _wsto_string(s)  boost::locale::conv::from_utf(s,"GBK")
+
+#define DLL_API extern "C" _declspec(dllexport)
+//normal windows,gdi;,dx;opengl;
+enum RENDER_TYPE
+{
+	NORMAL = 0,
+	GDI = 1,
+	DX = 2,
+	OPENGL = 3
+};
+
+#define MAKE_RENDER(type, flag) ((type << 16) | flag)
+
+#define GET_RENDER_TYPE(t) (t >> 16)
+
+#define GET_RENDER_FLAG(t) (t & 0xffff)
+
+constexpr int RDT_NORMAL = MAKE_RENDER(NORMAL, 0);
+constexpr int RDT_GDI = MAKE_RENDER(GDI, 0);
+constexpr int RDT_GDI2 = MAKE_RENDER(GDI, 1);
+constexpr int RDT_GDI_DX2 = MAKE_RENDER(GDI, 2);
+constexpr int RDT_DX_DEFAULT = MAKE_RENDER(DX, 0);
+constexpr int RDT_DX_D3D9 = MAKE_RENDER(DX, 1);
+constexpr int RDT_DX_D3D10 = MAKE_RENDER(DX, 2);
+constexpr int RDT_DX_D3D11 = MAKE_RENDER(DX, 3);
+constexpr int RDT_GL_DEFAULT = MAKE_RENDER(OPENGL, 0);
+constexpr int RDT_GL_STD = MAKE_RENDER(OPENGL, 1);
+constexpr int RDT_GL_NOX = MAKE_RENDER(OPENGL, 2);
+constexpr int RDT_GL_ES = MAKE_RENDER(OPENGL, 3);
+constexpr int RDT_GL_FI = MAKE_RENDER(OPENGL, 4); //glFinish
+
+enum INPUT_TYPE
+{
+	IN_NORMAL = 0,
+	IN_WINDOWS = 1,
+	IN_DX = 2
+};
+//define Image byte format
+constexpr int IBF_R8G8B8A8 = 0;
+constexpr int IBF_B8G8R8A8 = 1;
+constexpr int IBF_R8G8B8 = 2;
+
+//const size_t MAX_IMAGE_WIDTH = 1<<11;
+//const size_t SHARED_MEMORY_SIZE = 1080 * 1928 * 4;
+
+constexpr auto SHARED_RES_NAME_FORMAT = L"op_mutex_%d";
+constexpr auto MUTEX_NAME_FORMAT = L"op_shared_mem_%d";
+
+#ifndef _M_X64
+#define OP64 0
+#else
+#define OP64 1
+#endif
+
+#define _TOSTRING(x) #x
+
+#define MAKE_OP_VERSION(a, b, c, d) _TOSTRING(a##.##b##.##c##.##d)
+
+#define OP_VERSION MAKE_OP_VERSION(0, 4, 0, 0)
+//模块句柄
+//extern HINSTANCE gInstance;
+//是否显示错误信息
+//extern int gShowError;
+//op 路径
+//extern wstring m_opPath;
+
+//extern wstring g_op_name;
+
+#endif

+ 194 - 0
GameAssist/IMGProc/helpfunc.cpp

@@ -0,0 +1,194 @@
+//#include "stdafx.h"
+#include <tchar.h>
+#include "helpfunc.h"
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <shlwapi.h>
+#include "globalVar.h"
+#include "opEnv.h"
+//#define USE_BOOST_STACK_TRACE
+#ifdef USE_BOOST_STACK_TRACE
+#include <boost/stacktrace.hpp>
+#endif
+
+std::wstring _s2wstring(const std::string& s) {
+	size_t nlen = s.length();
+
+	wchar_t* m_char;
+	int len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), nlen, NULL, 0);
+	m_char = new wchar_t[len + 1];
+	MultiByteToWideChar(CP_ACP, 0, s.data(), nlen, m_char, len);
+	m_char[len] = '\0';
+	std::wstring ws(m_char);
+	delete[] m_char;
+	return ws;
+}
+
+std::string _ws2string(const std::wstring& ws) {
+	// std::string strLocale = setlocale(LC_ALL, "");
+	// const wchar_t* wchSrc = ws.c_str();
+	// size_t nDestSize = wcstombs(NULL, wchSrc, 0) + 1;
+	// char *chDest = new char[nDestSize];
+	// memset(chDest, 0, nDestSize);
+	// wcstombs(chDest, wchSrc, nDestSize);
+	// std::string strResult = chDest;
+	// delete[]chDest;
+	// setlocale(LC_ALL, strLocale.c_str());
+	//return strResult;
+	int nlen = ws.length();
+
+	char* m_char;
+	int len = WideCharToMultiByte(CP_ACP, 0, ws.data(), nlen, NULL, 0, NULL, NULL);
+	m_char = new char[len + 1];
+	WideCharToMultiByte(CP_ACP, 0, ws.data(), nlen, m_char, len, NULL, NULL);
+	m_char[len] = '\0';
+	std::string s(m_char);
+	delete[] m_char;
+	return s;
+}
+
+long Path2GlobalPath(const TString& file, const TString& curr_path, TString& out) {
+	if (::PathFileExists(file.c_str())) {
+		out = file;
+		return 1;
+	}
+	out.clear();
+	out = curr_path + _T("\\") + file;
+	if (::PathFileExists(out.c_str())) {
+		return 1;
+	}
+	return 0;
+}
+
+long setlog(const wchar_t* format, ...) {
+	va_list args;
+	wchar_t buf[512];
+	va_start(args, format);
+	vswprintf(buf, format, args);
+	va_end(args);
+	std::wstring tmpw = buf;
+	std::string tmps = _ws2string(tmpw);
+
+	return setlog(tmps.data());
+}
+
+long setlog(const char* format, ...) {
+	std::stringstream ss(std::wstringstream::in | std::wstringstream::out);
+	va_list args;
+	char buf[512];
+	SYSTEMTIME sys;
+	GetLocalTime(&sys);
+	char tm[128];
+	sprintf(tm, "[%4d/%02d/%02d %02d:%02d:%02d.%03d]",
+		sys.wYear, sys.wMonth, sys.wDay,
+		sys.wHour, sys.wMinute, sys.wSecond,
+		sys.wMilliseconds);
+	va_start(args, format);
+	vsprintf(buf, format, args);
+	va_end(args);
+	ss << tm << (OP64 == 1 ? "x64" : "x32") << "info: " << buf << std::endl;
+#ifdef USE_BOOST_STACK_TRACE
+	ss << "<stack>\n"
+		<< boost::stacktrace::stacktrace() << std::endl;
+#endif // USE_BOOST_STACK_TRACE
+
+
+	std::string s = ss.str();
+	if (opEnv::m_showErrorMsg == 1) {
+		MessageBoxA(NULL, s.data(), "error", MB_ICONERROR);
+	}
+	else if (opEnv::m_showErrorMsg == 2) {
+		/*	wchar_t dll_path[MAX_PATH];
+			::GetModuleFileNameW(gInstance, dll_path, MAX_PATH);
+			wstring fname = dll_path;
+			fname = fname.substr(0, fname.rfind(L'\\'));
+			fname += L"\\op.log";*/
+		std::fstream file;
+		file.open("__op.log", std::ios::app | std::ios::out);
+		if (!file.is_open())
+			return 0;
+		file << s << std::endl;
+		file.close();
+	}
+	else if (opEnv::m_showErrorMsg == 3) {
+		std::cout << s << std::endl;
+	}
+
+	return 1;
+}
+
+void split(const TString& s, std::vector<TString>& v, const TString& c)
+{
+	std::wstring::size_type pos1, pos2;
+	size_t len = s.length();
+	pos2 = s.find(c);
+	pos1 = 0;
+	v.clear();
+	while (std::wstring::npos != pos2)
+	{
+		v.emplace_back(s.substr(pos1, pos2 - pos1));
+
+		pos1 = pos2 + c.size();
+		pos2 = s.find(c, pos1);
+	}
+	if (pos1 != len)
+		v.emplace_back(s.substr(pos1));
+}
+
+void wstring2upper(std::wstring& s) {
+	std::transform(s.begin(), s.end(), s.begin(), towupper);
+}
+
+void string2upper(std::string& s) {
+	std::transform(s.begin(), s.end(), s.begin(), toupper);
+}
+
+void wstring2lower(std::wstring& s) {
+	std::transform(s.begin(), s.end(), s.begin(), towlower);
+}
+
+void string2lower(std::string& s) {
+	std::transform(s.begin(), s.end(), s.begin(), tolower);
+}
+
+void replacea(TString& str, const TString& oldval, const TString& newval) {
+	size_t x0 = 0, dx = newval.length() - oldval.length() + 1;
+	size_t idx = str.find(oldval, x0);
+	while (idx != -1 && x0 >= 0) {
+		str.replace(idx, oldval.length(), newval);
+		x0 = idx + dx;
+		idx = str.find(oldval, x0);
+	}
+}
+
+std::ostream& operator<<(std::ostream& o, point_t const& rhs) {
+	o << rhs.x << "," << rhs.y;
+	return o;
+}
+
+std::wostream& operator<<(std::wostream& o, point_t const& rhs) {
+	o << rhs.x << L"," << rhs.y;
+	return o;
+}
+
+std::ostream& operator<<(std::ostream& o, FrameInfo const& rhs) {
+	o << "hwnd:" << rhs.hwnd << std::endl
+		<< "frameId:" << rhs.frameId << std::endl
+		<< "time:" << rhs.time << std::endl
+		<< "height" << rhs.height << std::endl
+		<< "width:" << rhs.width << std::endl;
+	return o;
+}
+std::wostream& operator<<(std::wostream& o, FrameInfo const& rhs) {
+	o << L"hwnd:" << rhs.hwnd << std::endl
+		<< L"frameId:" << rhs.frameId << std::endl
+		<< L"time:" << rhs.time << std::endl
+		<< L"height" << rhs.height << std::endl
+		<< L"width:" << rhs.width << std::endl;
+	return o;
+}
+
+
+

+ 92 - 0
GameAssist/IMGProc/helpfunc.h

@@ -0,0 +1,92 @@
+#pragma once
+#ifndef __HELPFUCN_H_
+#define __HELPFUNC_H_
+#include "optype.h"
+#include "frameInfo.h"
+
+std::wstring _s2wstring(const std::string& s);
+std::string _ws2string(const std::wstring& s);
+
+//½«Â·¾¶×ª»¯ÎªÈ«¾Ö·¾¶
+long Path2GlobalPath(const TString& file, const TString& curr_path, TString& out);
+
+void split(const TString& s, std::vector<TString>& v, const TString& c);
+
+void wstring2upper(std::wstring& s);
+void string2upper(std::string& s);
+
+void wstring2lower(std::wstring& s);
+void string2lower(std::string& s);
+
+void replacea(TString& str, const TString& oldval, const TString& newval);
+
+
+//for debug
+long setlog(const wchar_t* format, ...);
+//
+long setlog(const char* format, ...);
+
+int inline hex2bin(int c) {
+	return c <= L'9' ? c - L'0' : c - L'A' + 10;
+};
+
+int inline bin2hex(int c) {
+	int ans = 0;
+	int c1 = c >> 4 & 0xf;
+	int c2 = c & 0xf;
+	ans |= (c1 <= 9 ? c1 + L'0' : c1 + 'A' - 10) << 8;
+	ans |= c2 <= 9 ? c2 + L'0' : c2 + 'A' - 10;
+	return ans;
+};
+
+constexpr int PTY(uint pt) {
+	return pt >> 16;
+}
+
+constexpr int PTX(uint pt) {
+	return pt & 0xffff;
+}
+
+template<typename T>
+void nextVal(const T& t, int* next) {
+	next[0] = -1;
+	int k = -1, j = 0;
+	while (j < (int)t.size() - 1) {
+		if (k == -1 || t[k] == t[j]) {
+			k++;
+			j++;
+			next[j] = k;
+		}
+		else {
+			k = next[k];
+		}
+	}
+}
+
+template<typename T>
+int kmp(const T& s, const T& t) {
+	vector<int> next(t.size());
+	nextVal(t, next.data());
+	int i = 0, j = 0;
+	while (i < (int)s.size() && j < (int)t.size()) {
+		if (j == -1 || s[i] == t[j]) {
+			i++;
+			j++;
+		}
+		else {
+			j = next[j];
+		}
+	}
+	return j == s.size() ? i - j : -1;
+}
+
+std::ostream& operator<<(std::ostream& o, point_t const& rhs);
+std::wostream& operator<<(std::wostream& o, point_t const& rhs);
+
+std::ostream& operator<<(std::ostream& o, FrameInfo const& rhs);
+std::wostream& operator<<(std::wostream& o, FrameInfo const& rhs);
+
+#endif // !__TOOL_H_
+
+
+

+ 24 - 0
GameAssist/IMGProc/imageView.hpp

@@ -0,0 +1,24 @@
+#ifndef __IMAGE_VIEW_H
+#define __IMAGE_VIEW_H
+#include "optype.h"
+#include "Image.hpp"
+
+class imageView 
+{
+private:
+
+public:
+	imageView(ImageBin const& src, rect_t const& block);
+	imageView(imageView const&) = delete;
+	~imageView();
+
+	/* data */
+	const ImageBin& _src;
+	rect_t _block;
+};
+
+imageView::imageView(ImageBin const& src, rect_t const& block): _src(src), _block(block) {}
+
+imageView::~imageView() {}
+
+#endif

+ 35 - 0
GameAssist/IMGProc/opEnv.cpp

@@ -0,0 +1,35 @@
+//#include "stdafx.h"
+#include <tchar.h>
+#include "opEnv.h"
+#include <windows.h>
+void* opEnv::m_instance = nullptr;
+TString opEnv::m_basePath;
+TString opEnv::m_opName;
+int opEnv::m_showErrorMsg = 1;
+
+void opEnv::setInstance(void* instance)
+{
+	m_instance = instance;
+	TCHAR buff[512] = {};
+	::GetModuleFileName(static_cast<HINSTANCE>(m_instance), buff, 512);
+	TString s(buff);
+	size_t index = s.rfind(_T("\\"));
+	if (index != s.npos) {
+		m_basePath = s.substr(0, index);
+		m_opName = s.substr(index + 1);
+	}
+}
+
+void* opEnv::getInstance()
+{
+	return m_instance;
+}
+
+TString opEnv::getBasePath() 
+{
+	return m_basePath;
+}
+
+TString opEnv::getOpName() {
+	return m_opName;
+}

+ 25 - 0
GameAssist/IMGProc/opEnv.h

@@ -0,0 +1,25 @@
+#ifndef __OPENV_H_
+#define __OPENV_H_
+#include <string>
+
+#ifdef UNICODE 
+using TString = std::wstring;
+#else
+using TString = std::string;
+#endif
+
+class opEnv
+{
+public:
+	static void setInstance(void* instance);
+	static void* getInstance();
+	static TString getBasePath();
+	static TString getOpName();
+	static int m_showErrorMsg;
+private:
+	static void* m_instance;
+	static TString m_basePath;
+	static TString m_opName;
+
+};
+#endif

+ 102 - 0
GameAssist/IMGProc/optype.h

@@ -0,0 +1,102 @@
+#pragma once
+#ifndef __optype_h_
+#define __optype_h_
+#include <Windows.h>
+#include <assert.h>
+
+#include <map>
+#include <string>
+#include <vector>
+using uint = unsigned int;
+using uchar = unsigned char;
+
+using std::map;
+using std::vector;
+#ifdef UNICODE 
+using TString = std::wstring;
+#else
+using TString = std::string;
+#endif
+
+using bytearray = std::vector<uchar>;
+
+struct point_t
+{
+	int x, y;
+	point_t() : x(0), y(0) {}
+	point_t(int x_, int y_) : x(x_), y(y_) {}
+	bool operator<(const point_t& rhs) const
+	{
+		if (std::abs(y - rhs.y) < 9)
+			return x < rhs.x;
+		else
+			return y < rhs.y;
+	}
+
+	bool operator==(const point_t& rhs) const { return x == rhs.x && y == rhs.y; }
+};
+
+using vpoint_t = std::vector<point_t>;
+//(5,3) --> (2, 2, 1)
+class NumberGen {
+	int _q, _r;
+
+public:
+	NumberGen(int n, int cnt) : _q(n / cnt), _r(n% cnt) {}
+	int operator[](int idx) const { return idx < _r ? _q + 1 : _q; }
+};
+
+struct rect_t {
+	rect_t() : x1(0), y1(0), x2(0), y2(0) {}
+	rect_t(int x1_, int y1_, int x2_, int y2_)
+		: x1(x1_), y1(y1_), x2(x2_), y2(y2_) {}
+	int x1, y1;
+	int x2, y2;
+	int width() const { return x2 - x1; }
+	int height() const { return y2 - y1; }
+	rect_t& shrinkRect(int w, int h) {
+		x2 -= w;
+		y2 -= h;
+		x2 += 1;
+		y2 += 1;
+		return *this;
+	}
+	bool valid() const { return 0 <= x1 && x1 < x2 && 0 <= y1 && y1 < y2; }
+
+	void divideBlock(int count, bool vertical, std::vector<rect_t>& blocks) {
+		assert(valid());
+
+		assert(count > 0);
+		blocks.resize(count);
+		if (vertical) {
+			NumberGen gen(height(), count);
+			int basey = y1;
+			for (int i = 0; i < count; ++i) {
+				blocks[i] = rect_t(x1, basey, x2, basey + gen[i]);
+				basey += gen[i];
+			}
+
+		}
+		else {
+			NumberGen gen(width(), count);
+			int basex = x1;
+			for (int i = 0; i < count; ++i) {
+				blocks[i] = rect_t(basex, y1, basex + gen[i], y2);
+				basex += gen[i];
+			}
+		}
+		assert(blocks.back().x2 == x2);
+		assert(blocks.back().y2 == y2);
+	}
+};
+
+using vrect_t = std::vector<rect_t>;
+
+struct point_desc_t {
+	int id;
+	point_t pos;
+};
+
+using vpoint_desc_t = std::vector<point_desc_t>;
+
+#endif