#include "stdafx.h"
#include "ImgAssist.h"


namespace ImgAssist
{
	std::string CaptureGameWnd(HWND hWnd)
	{
		TCHAR szName[MAX_PATH] = { 0 };
		_stprintf_s(szName, _T("%s%p.bmp"), GameGlobal::g_szTempDir, hWnd);
		CaptureGameWnd(hWnd, szName);

		return std::string(szName);
	}

	void CaptureGameWnd(HWND hWnd, std::string strSavePath)
	{
		HDC hDC = ::GetWindowDC(hWnd);
		ASSERT(hDC);
		HDC hMemDC = ::CreateCompatibleDC(hDC);
		ASSERT(hMemDC);

		RECT rc;
		::GetWindowRect(hWnd, &rc);
		HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);

		ASSERT(hBitmap);
		HBITMAP hOldBmp = (HBITMAP)::SelectObject(hMemDC, hBitmap);

		BOOL bRet = FALSE;
		BOOL bProcessed = FALSE;
		// 针对win10 DWM虚拟缩放时的处理
		if (GetDeviceCaps(hDC, LOGPIXELSX) != 96)
		{
			DEVMODE curDevMode;
			memset(&curDevMode, 0, sizeof(curDevMode));
			curDevMode.dmSize = sizeof(DEVMODE);
			BOOL bEnumRet = ::EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &curDevMode);
			if (bEnumRet < curDevMode.dmPelsWidth)
			{
				bProcessed = TRUE;
				//::SetStretchBltMode(hMemDC, STRETCH_HALFTONE);
				//bRet = ::StretchBlt(hMemDC, 0, 0, nWidth, nHeight, hScrDC, 0, 0, curDevMode.dmPelsWidth, curDevMode.dmPelsHeight, SRCCOPY | CAPTUREBLT);
			}
		}

		::PrintWindow(hWnd, hMemDC, 0);
		BITMAP bitmap = { 0 };
		::GetObject(hBitmap, sizeof(BITMAP), &bitmap);
		BITMAPINFOHEADER bi = { 0 };
		BITMAPFILEHEADER bf = { 0 };
		CONST int nBitCount = 24;
		bi.biSize = sizeof(BITMAPINFOHEADER);
		bi.biWidth = bitmap.bmWidth;
		bi.biHeight = bitmap.bmHeight;
		bi.biPlanes = 1;
		bi.biBitCount = nBitCount;
		bi.biCompression = BI_RGB;
		DWORD dwSize = ((bitmap.bmWidth * nBitCount + 31) / 32) * 4 * bitmap.bmHeight;
		HANDLE hDib = GlobalAlloc(GHND, dwSize + sizeof(BITMAPINFOHEADER));
		if (hDib == NULL)
			return;
		LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
		//memcpy(lpbi, &bi, sizeof(BITMAPINFOHEADER));
		*lpbi = bi;
		::GetDIBits(hMemDC, hBitmap, 0, bitmap.bmHeight, (BYTE*)lpbi + sizeof(BITMAPINFOHEADER), (BITMAPINFO*)lpbi, DIB_RGB_COLORS);

		CFile file;
		CFileException fep;
		if (file.Open(strSavePath.c_str(), CFile::modeCreate | CFile::modeWrite, &fep)) {
			bf.bfType = 0x4d42;
			dwSize += sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
			bf.bfSize = dwSize;
			bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
			file.Write((BYTE*)&bf, sizeof(BITMAPFILEHEADER));
			file.Write((BYTE*)lpbi, dwSize);
			file.Close();
		}

		GlobalUnlock(hDib);
		GlobalFree(hDib);
		::SelectObject(hMemDC, hOldBmp);
		::DeleteObject(hBitmap);
		::DeleteObject(hMemDC);
		::ReleaseDC(hWnd, hDC);

		Sleep(30);
	}

	cv::Mat CaptureWnd(HWND hWnd)
	{
		HDC hdcMemDC = NULL;		// 兼容DC;
		HDC hdcWindow = NULL;		// 窗口DC;
		BITMAP bmpWindow = {0};		// BITMAP窗口对象;
		HBITMAP hbmpWindow = NULL;	// HBITMAP窗口对象;
		DWORD dwBmpSize = 0;		// BITMAP大小;
		char* lpbitmap = NULL;		// BITMAP数据;
		HANDLE hDIB = NULL;
		DWORD dwSizeofDIB = 0;
		byte* pImgData = NULL;
		// 获取窗口DC;
		hdcWindow = ::GetWindowDC(hWnd);
		// 创建窗口DC的兼容DC;
		hdcMemDC = ::CreateCompatibleDC(hdcWindow);

		RECT rc,rcc;
		::GetWindowRect(hWnd, &rc);
		::GetClientRect(hWnd, &rcc);
		int width = rcc.right - rcc.left;
		int height = rcc.bottom - rcc.top;
		int dx = rc.right - rc.left - width;
		int dy = rc.bottom - rc.top - height;
		// 创建兼容位图;
		hbmpWindow = ::CreateCompatibleBitmap(hdcWindow, width, height);
		// 将位图选入兼容DC;
		SelectObject(hdcMemDC, hbmpWindow);
#if 1
		// 将窗口复制到兼容DC中（当窗口被遮挡时，这个函数效果好）
		if(!::PrintWindow(hWnd, hdcMemDC, PW_CLIENTONLY))
		{
			goto done;
		}
#else
		// 将目标DC的内容复制到兼容DC中；
		if (!BitBlt(hdcMemDC, 0, 0, width, height, hdcWindow, 0, 0, SRCCOPY))
		{
			goto done;
		}
#endif		
		// 通过HBITMAP获取BITMAP
		GetObject(hbmpWindow, sizeof(BITMAP), &bmpWindow);

		BITMAPFILEHEADER   bmfHeader = {0};
		BITMAPINFOHEADER   bi = {0};

		bi.biSize = sizeof(BITMAPINFOHEADER);
		bi.biWidth = bmpWindow.bmWidth;
		bi.biHeight = bmpWindow.bmHeight;
		bi.biPlanes = 1;
		bi.biBitCount = 32;
		bi.biCompression = BI_RGB;
		bi.biSizeImage = 0;
		bi.biXPelsPerMeter = 0;
		bi.biYPelsPerMeter = 0;
		bi.biClrUsed = 0;
		bi.biClrImportant = 0;

		dwBmpSize = ((bmpWindow.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpWindow.bmHeight;

		// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that 
		// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc 
		// have greater overhead than HeapAlloc.
		hDIB = GlobalAlloc(GHND, dwBmpSize);
		if (hDIB == NULL)
		{
			goto done;
		}
		lpbitmap = (char*)GlobalLock(hDIB);

		// Gets the "bits" from the bitmap, and copies them into a buffer 
		// that's pointed to by lpbitmap.
		GetDIBits(hdcMemDC, hbmpWindow, 0,
			(UINT)bmpWindow.bmHeight,
			lpbitmap,
			(BITMAPINFO*)&bi, DIB_RGB_COLORS);

		// Add the size of the headers to the size of the bitmap to get the total file size.
		dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

		// Offset to where the actual bitmap bits start.
		bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);

		// Size of the file.
		bmfHeader.bfSize = dwSizeofDIB;

		// bfType must always be BM for Bitmaps.
		bmfHeader.bfType = 0x4D42; // BM.

		pImgData = new byte[dwSizeofDIB];
		memset(pImgData, 0, dwSizeofDIB);
		memcpy(pImgData, &bmfHeader, sizeof(BITMAPFILEHEADER));
		memcpy(pImgData + sizeof(BITMAPFILEHEADER), &bi, sizeof(BITMAPINFOHEADER));
		memcpy(pImgData + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER), lpbitmap, dwBmpSize);
		GlobalUnlock(hDIB);
		GlobalFree(hDIB);
		
	done:
		DeleteObject(hbmpWindow);
		DeleteObject(hdcMemDC);
		ReleaseDC(hWnd, hdcWindow);
#if 0   // 原始的保留图片做法;
		CFile file;
		CFileException fep;
		if (file.Open("E:\\1.jpg", CFile::modeCreate | CFile::modeWrite, &fep)) {
			file.Write(pImgData, dwSizeofDIB);
			file.Close();
		}

		cv::Mat s22 = cv::imread("E:\\1.jpg");
		cv::imwrite("E:\\2.jpg",s22);
#endif


		//cv::Mat pic;
		//int nChannels = bmpWindow.bmBitsPixel == 1 ? 1 : bmpWindow.bmBitsPixel / 8;
		//pic.create(cv::Size(width, height), CV_8UC4);
		//memcpy(pic.data, lpbitmap, dwBmpSize);
		if (pImgData)
		{
			cv::_InputArray pirArrary(pImgData, dwSizeofDIB);
			cv::Mat src = cv::imdecode(pirArrary, cv::IMREAD_COLOR);
			delete[]pImgData;
			pImgData = NULL;
#ifdef DEBUG
			cv::imwrite("E:\\3.bmp", src);	// 后缀不一样，得到的大小也不一样;
			cv::imwrite("E:\\3.jpg", src);
#endif
			return src;
		}

#if 0   // 能保存截图，但是被270度镜像;
		cv::Mat matPic(height, width, CV_8UC4, pImgData + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));
		cv::imwrite("E:\\4.jpg", matPic);
#endif

		return cv::Mat();
	}

	BOOL OpenImage(std::string strImag, cv::Mat& img)
	{
		if (!PathFileExists(strImag.c_str()))
			return FALSE;

		if (img.data != NULL)
			img.release();

		img = cv::imread(strImag.c_str(), cv::IMREAD_COLOR);
		if (img.data == NULL)
			return FALSE;

		return TRUE;
	}

	BOOL SetImgThreshold(cv::Mat& srcImg, cv::Mat& thresholdImg, long nThresholdVal, long nMaxThresholdVal, int type)
	{
		if (srcImg.data == NULL)
			return FALSE;

		if (thresholdImg.data && srcImg.data != thresholdImg.data)
			thresholdImg.release();

		switch (type)
		{
		case 0://全局阀值;
		{
			// 转成灰阶图;
			cv::cvtColor(srcImg, thresholdImg, cv::COLOR_BGR2GRAY);
			// 再高斯模糊处理(滤波);
			cv::GaussianBlur(thresholdImg, thresholdImg, cv::Size(5, 5), 0, 0);
			// 二值化;//全局化指定的阀值与返回值相等;
			double dRetVal = cv::threshold(thresholdImg, thresholdImg, nThresholdVal, nMaxThresholdVal, cv::THRESH_BINARY);
		}
		break;
		case 1://自适应阀值;
		{
			// 转成灰阶图;
			cv::cvtColor(srcImg, thresholdImg, cv::COLOR_BGR2GRAY);
			// 再高斯模糊处理(滤波);
			cv::GaussianBlur(thresholdImg, thresholdImg, cv::Size(3, 3), 0, 0);
			// 自适应阀值;
			cv::medianBlur(thresholdImg, thresholdImg, 3);
			// 局部二值化;//blocksize一般取3、5、7
			cv::adaptiveThreshold(thresholdImg, thresholdImg, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 3, 4.5);
		}
		break;
		case 2:// otsu阀值;
		{
			cv::cvtColor(srcImg, thresholdImg, cv::COLOR_BGR2GRAY);
			// 高斯模糊;
			cv::GaussianBlur(thresholdImg, thresholdImg, cv::Size(3, 3), 0, 0);
			cv::threshold(thresholdImg, thresholdImg, nThresholdVal, nMaxThresholdVal, cv::THRESH_BINARY | cv::THRESH_OTSU);
		}
		break;
		default:
			break;
		}

		return TRUE;
	}

	BOOL GetImgMatchtemplate(HWND hWnd, std::string strTempImg, RECT& rc, double lowestMatchValue)
	{
		std::string strImage = CaptureGameWnd(hWnd);
		cv::Mat srcImg, tempImg, matchImg;
		if (!OpenImage(strImage, srcImg) || !OpenImage(strTempImg, tempImg))
		{
			if (srcImg.data) srcImg.release();
			if (tempImg.data) tempImg.release();
			return FALSE;
		}

		cv::matchTemplate(srcImg, tempImg, matchImg, cv::TM_CCOEFF_NORMED);
		// 归一化到0~1
		//cv::normalize(matchImg, matchImg, 0, 1, cv::NORM_MINMAX, -1);

		double lv_nMinVal = 0.0;
		double lv_nMaxVal = 0.0;
		cv::Point lv_nMinLoc = cv::Point(0, 0);
		cv::Point lv_nMaxLoc = cv::Point(0, 0);
		cv::Point lv_MatchLoc = cv::Point(0, 0);
		cv::minMaxLoc(matchImg, &lv_nMinVal, &lv_nMaxVal, &lv_nMinLoc, &lv_nMaxLoc);

		cv::Rect roi = cv::Rect(lv_nMaxLoc.x, lv_nMaxLoc.y, tempImg.cols, tempImg.rows);
		cv::Mat roiImg = srcImg(roi);
		cv::imwrite(GameGlobal::BuildImgPath(hWnd, _T("match")), roiImg);

		rc.left = lv_nMaxLoc.x - GameGlobal::frameWidth;
		rc.top = lv_nMaxLoc.y - GameGlobal::titleBarHeight;
		rc.right = rc.left + tempImg.cols;
		rc.bottom = rc.top + tempImg.rows;

		srcImg.release();
		tempImg.release();
		matchImg.release();
		roiImg.release();

		// 是否大于最低匹配度;
		return (lv_nMaxVal >= lowestMatchValue ? TRUE : FALSE);
	}

	BOOL GetImgMatchtemplate(std::string strSrcImg, std::string strTempImg, RECT& rc, double lowestMatchValue)
	{
		cv::Mat srcImg, tempImg, matchImg;
		if (!OpenImage(strSrcImg, srcImg) || !OpenImage(strTempImg, tempImg))
		{
			if (srcImg.data) srcImg.release();
			if (tempImg.data) tempImg.release();
			return FALSE;
		}

		cv::matchTemplate(srcImg, tempImg, matchImg, cv::TM_CCOEFF_NORMED);
		// 归一化到0~1
		//cv::normalize(matchImg, matchImg, 0, 1, cv::NORM_MINMAX, -1);

		double lv_nMinVal = 0.0;
		double lv_nMaxVal = 0.0;
		cv::Point lv_nMinLoc = cv::Point(0, 0);
		cv::Point lv_nMaxLoc = cv::Point(0, 0);
		cv::Point lv_MatchLoc = cv::Point(0, 0);
		cv::minMaxLoc(matchImg, &lv_nMinVal, &lv_nMaxVal, &lv_nMinLoc, &lv_nMaxLoc);

		cv::Rect roi = cv::Rect(lv_nMaxLoc.x, lv_nMaxLoc.y, tempImg.cols, tempImg.rows);
		cv::Mat roiImg = srcImg(roi);
		//cv::imwrite("G:\\bin\\GameAssist\\match.bmp", roiImg);

		rc.left = lv_nMaxLoc.x - GameGlobal::frameWidth;
		rc.top = lv_nMaxLoc.y - GameGlobal::titleBarHeight;
		rc.right = rc.left + tempImg.cols;
		rc.bottom = rc.top + tempImg.rows;

		srcImg.release();
		tempImg.release();
		matchImg.release();
		roiImg.release();

		// 是否大于最低匹配度;
		return (lv_nMaxVal >= lowestMatchValue ? TRUE : FALSE);
	}

	BOOL IsMatchIcon(HWND hWnd, std::string strTempImg, RECT srcRect, RECT& matchRect, double lowestMatchValue)
	{
		std::string strImg = CaptureGameWnd(hWnd);
		cv::Mat srcImg, tempImg, matchImg;
		if (!OpenImage(strImg, srcImg) || !OpenImage(strTempImg, tempImg))
		{
			if (srcImg.data) srcImg.release();
			if (tempImg.data) tempImg.release();
			return FALSE;
		}

		// 指定源图片区域来做模块比较,提高效率;
		cv::Mat imgROI;
		imgROI = srcImg(cv::Rect(srcRect.left, srcRect.top, srcRect.right - srcRect.left, srcRect.bottom - srcRect.top));
		cv::imwrite(GameGlobal::BuildImgPath(hWnd, _T("roi")), imgROI);
		/*
		m_ImgROI = srcImg([&]()-> cv::Rect {
			// 注意是x,y,w=cols,h=rows
			return cv::Rect(srcRect.left, srcRect.top, srcRect.right - srcRect.left, srcRect.bottom - srcRect.top);
		}());
		*/

		cv::matchTemplate(imgROI, tempImg, matchImg, cv::TM_CCOEFF_NORMED);
		// 归一化到0~1
		//cv::normalize(matchImg, matchImg, 0, 1, cv::NORM_MINMAX, -1);

		double lv_nMinVal = 0.0;
		double lv_nMaxVal = 0.0;
		cv::Point lv_nMinLoc = cv::Point(0, 0);
		cv::Point lv_nMaxLoc = cv::Point(0, 0);
		cv::Point lv_MatchLoc = cv::Point(0, 0);
		cv::minMaxLoc(matchImg, &lv_nMinVal, &lv_nMaxVal, &lv_nMinLoc, &lv_nMaxLoc);

		cv::Rect roi = cv::Rect(lv_nMaxLoc.x, lv_nMaxLoc.y, tempImg.cols, tempImg.rows);
		cv::Mat roiImg = imgROI(roi);
		cv::imwrite(GameGlobal::BuildImgPath(hWnd, _T("match")), roiImg);

		matchRect.left = srcRect.left + lv_nMaxLoc.x - GameGlobal::frameWidth;
		matchRect.top = srcRect.top + lv_nMaxLoc.y - GameGlobal::titleBarHeight;
		matchRect.right = matchRect.left + tempImg.cols;
		matchRect.bottom = matchRect.top + tempImg.rows;

		imgROI.release();
		srcImg.release();
		tempImg.release();
		matchImg.release();
		roiImg.release();

		// 是否大于最低匹配度;
		return (lv_nMaxVal >= lowestMatchValue ? TRUE : FALSE);
	}

	BOOL CropPicture(HWND hWnd, CRect rc, const char* szSaveName)
	{
		std::string strImg = CaptureGameWnd(hWnd);
		cv::Mat srcImg, tempImg, matchImg;
		if (!OpenImage(strImg, srcImg))
		{
			if (srcImg.data) srcImg.release();
			return FALSE;
		}

		// 指定源图片区域来做模块比较,提高效率;
		cv::Mat imgROI;
		imgROI = srcImg(cv::Rect(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top));
		return cv::imwrite(szSaveName, imgROI);
	}

	BOOL IsSimilarPicture(const char* szp1, const char* szp2, double lowestMatchValue)
	{
		cv::Mat src, dest, matchImg;
		src = cv::imread(szp1);
		dest = cv::imread(szp2);

		cv::matchTemplate(src, dest, matchImg, cv::TM_CCOEFF_NORMED);
		// 归一化到0~1
		//cv::normalize(matchImg, matchImg, 0, 1, cv::NORM_MINMAX, -1);

		double lv_nMinVal = 0.0;
		double lv_nMaxVal = 0.0;
		cv::Point lv_nMinLoc = cv::Point(0, 0);
		cv::Point lv_nMaxLoc = cv::Point(0, 0);
		cv::Point lv_MatchLoc = cv::Point(0, 0);
		cv::minMaxLoc(matchImg, &lv_nMinVal, &lv_nMaxVal, &lv_nMinLoc, &lv_nMaxLoc);

		src.release();
		matchImg.release();
		dest.release();

		// 是否大于最低匹配度;
		return (lv_nMaxVal >= lowestMatchValue ? TRUE : FALSE);
	}

	int GetColorOccupiedLength(std::string strImg, int r, int g, int b)
	{
		cv::Mat src = cv::imread(strImg.c_str());
		// 获取图片的矩形大小;
		int width = src.cols;
		int height = src.rows;

		int i = 0;
		for (; i < width; ++i)
		{
			cv::Vec3b pixel = src.at<cv::Vec3b>(height / 2, i);	// 读取第 height / 2行 第 i 列像素值;
			if (abs(pixel[0] - b) <= 3 && abs(pixel[1] - g) <= 3 && abs(pixel[2] - r) <= 3)
			{
				continue;
			}

			break;
		}
		src.release();

		GameGlobal::DebugLog(_T("[GameAssist] 颜色占比=%d,%d,%d"), i, width, i * 100 / width);

		return i * 100 / width;
	}

};