/////////////////////////////
// ECCSkelton
// CANVASクラス定義ファイル
// Copyright (c) August, 2022
//        By Ysama
/////////////////////////////

class CANVAS
{
public:
	HWND		m_hWnd;			//対象となるウィンドウ
	HDC			m_hDC;			//コンパチブルDC
	HBITMAP		m_hCanvas;		//仮想ウィンドウであるビットマップ
	HBITMAP		m_hBitmap;		//キャンバスで使うビットマップハンドル
	HFONT		m_hFont;		//キャンバスで使うフォント
	int			m_BMPw;			//キャンバスで使うビットマップ幅
	int			m_BMPh;			//キャンバスで使うビットマップ高さ
	int			m_Left;			//フルスクリーンの左
	int			m_Top;			//フルスクリーンの上
	int			m_Right;		//フルスクリーンの右
	int			m_Bottom;		//フルスクリーンの下
	POINT		m_Start;		//描画開始座標
	POINT		m_End;			//描画終了座標
	HPEN		m_Pen;			//ペンのハンドル
	int			m_PStyle;		//ペンスタイル
	int			m_PWidth;		//ペン幅
	HBRUSH		m_Brush;		//ブラシのハンドル
	COLORREF	m_Color;		//描画色
	bool		m_Focus;		//DragRect関数のフラグ

public:
	CANVAS();													//コンストラクター
	CANVAS(HWND);												//コンストラクター＋初期化
	~CANVAS();													//デストラクター
	bool SetCanvas(HWND);										//CANVASの初期化
	HWND hWnd() {return m_hWnd;}								//ウィンドウハンドルを返す
	HDC  hDC() {return m_hDC;}									//DCハンドルを返す
	void Clear();												//画面消去
	void Color(int);											//色指定
	void Dot(int, int);											//点を描画
	void Line(int, int, int, int);								//線を描画
	void Box(int, int, int, int, int);							//矩形を描画
	void Circle(int, int, int, int);							//円を描画
	void Ellipsed(int, int, int, int, int);						//楕円を描画
	void Arced(int, int, int, int, int, int, int, int);			//円弧(線無)を描画
	void Chorded(int, int, int, int, int, int, int, int, int);	//円弧(線有)を描画
	bool Paint(int, int, COLORREF);								//指定色で塗潰す
	HBITMAP GetBMP(int, int, int, int);							//ビットマップ画像取込み
	bool PutBMP(int, int, int);									//ビットマップ画像張付け
	bool PutBMP(HBITMAP, int, int, int);						//外部ビットマップ画像張付け
	bool FitBMP(int, int, int, int, int);						//ビットマップ画像をサイズに合せて張付け
	HBRUSH BrSelection(int);									//ブラシ選択
	void OnPaint(HDC, int, int);								//WM_PAINT時の処理
	void ReDraw(int, int, int, int);							//InvalidateRect処理
	void DragRect(int, int, bool);								//描画領域に矩形を描く
	void PrintText(int, int, LPCTSTR, int, UINT);				//文字を描画（DrawText類似）
	void PrintTextEx(LPCTSTR, LPRECT,
			COLORREF, LPCTSTR, int, UINT,
			int, int, int, DWORD, DWORD, DWORD, DWORD);			//文字を描画（文字色とフォントを指定）
	void Font(HFONT);											//フォントを選択
};

//コンストラクター
CANVAS::CANVAS() {

	m_hWnd		= 0;			//対象となるウィンドウ
	m_hDC		= 0;			//コンパチブルDC
	m_hCanvas 	= 0;			//仮想ウィンドウであるビットマップ
	m_hBitmap	= 0;			//キャンバスで使うビットマップハンドル
	m_hFont		= 0;			//キャンバスで使うフォント
	m_Pen		= 0;			//ペンのハンドル
	m_Brush		= 0;			//ブラシのハンドル
}

//コンストラクター＋初期化
CANVAS::CANVAS(HWND hWnd) {

	SetCanvas(hWnd);
}

//デストラクター
CANVAS::~CANVAS() {

	DeleteObject(m_hCanvas);	//スクリーンビットマップと
	DeleteObject(m_hBitmap);	//画像ビットマップと
	DeleteDC(m_hDC);			//コンパチＤＣを削除
	if(m_hFont)
		DeleteObject(m_hFont);	//現在使用しているフォントを削除
}

//CANVASの初期化
bool CANVAS::SetCanvas(HWND hWnd) {

	if(!hWnd) {
		MessageBoxW(NULL, L"ウィンドウハンドルが指定されていません",
					L"エラー", MB_OK | MB_ICONEXCLAMATION);
		return FALSE;
	}
	m_hWnd = hWnd;									//ウィンドウハンドルを記録
	HDC hDC = GetDC(hWnd);							//対象ウィンドウのDCを取得
	m_hDC = CreateCompatibleDC(hDC);				//コンパチＤＣを作成
	m_Left = m_Top = 0;								//フルスクリーンに拡大した
	m_Right = GetSystemMetrics(SM_CXFULLSCREEN);	//時のキャンヴァスサイズを
	m_Bottom = GetSystemMetrics(SM_CYFULLSCREEN);	//記録しておく
	m_hCanvas = CreateCompatibleBitmap(hDC,			//m_hDCではないので注意
				m_Right,							//m_hDCにするとモノクロとなる
				m_Bottom);
	SelectObject(m_hDC, m_hCanvas);					//キャンヴァスの実体
	m_Pen = (HPEN)GetCurrentObject(hDC, OBJ_PEN);
	m_PStyle = PS_SOLID;							//ソリッドペン
	m_PWidth = 0;									//1ピクセル
	m_Brush = (HBRUSH)GetCurrentObject(hDC, OBJ_BRUSH);
	m_Color = RGB(0, 0, 0);							//黒
	PatBlt(m_hDC, m_Left, m_Top, m_Right, m_Bottom, PATCOPY);	//初期描画
	ReleaseDC(hWnd, hDC);							//hDCを開放
	m_Focus = FALSE;								//DragRectフラグの初期化
	m_hFont = 0;									//m_hFontの初期化

	return TRUE;
}

//画面消去
void CANVAS::Clear() {

	PatBlt(m_hDC, m_Left, m_Top, m_Right, m_Bottom, PATCOPY);
	InvalidateRect(m_hWnd, NULL, TRUE);
}

//色の選択（0 - 15迄の16色指定、それ以外は色選択コモンダイアログが立ち上がる
void CANVAS::Color(int Col) {	//｛黒、青、赤、マゼンダ、緑、シアン、黄色、白｝x 2輝度

	static	CHOOSECOLOR clr;
	static	COLORREF cref[16];
	static	COLORREF selected_color;
	switch(Col) {
		case 0:	//黒
			m_Color = RGB(0, 0, 0);
			break;
		case 1:	//青
			m_Color = RGB(0, 0, 128);
			break;
		case 2:	//赤
			m_Color = RGB(128, 0, 0);
			break;
		case 3:	//マゼンダ
			m_Color = RGB(128, 0, 128);
			break;
		case 4:	//緑
			m_Color = RGB(0, 128, 0);
			break;
		case 5:	//シアン
			m_Color = RGB(0, 128, 128);
			break;
		case 6:	//黄色
			m_Color = RGB(128, 128, 0);
			break;
		case 7:	//明るい灰色
			m_Color = RGB(192, 192, 192);
			break;
		case 8:	//暗い灰色
			m_Color = RGB(128, 128, 128);
			break;
		case 9:	//明るい青
			m_Color = RGB(0, 0, 255);
			break;
		case 10:	//明るい赤
			m_Color = RGB(255, 0, 0);
			break;
		case 11:	//明るいマゼンダ
			m_Color = RGB(255, 0, 255);
			break;
		case 12:	//明るい緑
			m_Color = RGB(0, 255, 0);
			break;
		case 13:	//明るいシアン
			m_Color = RGB(0, 255, 255);
			break;
		case 14:	//明るい黄色
			m_Color = RGB(255, 255, 0);
			break;
		case 15:	//白
			m_Color = RGB(255, 255, 255);
			break;
		default:	//色選択コモンダイアログ
			clr.lStructSize	= sizeof(CHOOSECOLOR);
			clr.hwndOwner		= m_hWnd;
			clr.hInstance		= 0;
			clr.rgbResult		= selected_color;
			clr.lpCustColors	= cref;
			clr.Flags			= CC_RGBINIT;
			clr.lCustData		= 0L;
			clr.lpfnHook		= NULL;
			clr.lpTemplateName	= NULL;
			if( ChooseColor(&clr) == TRUE )
	  			m_Color = clr.rgbResult;
			break;
	}
}

void CANVAS::Dot(int x, int y) {

	SetPixel(m_hDC, x, y, m_Color);
	RECT rec;
	rec.left = x;	rec.right = x + 1;
	rec.top = y;	rec.bottom = y + 1;
	InvalidateRect(m_hWnd, &rec, TRUE);
}

//線を引く
void CANVAS::Line(int sx, int sy, int ex, int ey) {

	HPEN pen = CreatePen(m_PStyle, m_PWidth, m_Color);
	m_Pen = (HPEN)SelectObject(m_hDC, pen);
	MoveToEx(m_hDC, sx, sy, NULL);
	LineTo(m_hDC, ex, ey);
	SelectObject(m_hDC, m_Pen);
	DeleteObject(pen);
	ReDraw(sx, sy, ex, ey);
}

//矩形を描く
void CANVAS::Box(int sx, int sy, int ex, int ey, int type = 0) {

	HPEN pen = CreatePen(m_PStyle, m_PWidth, m_Color);
	m_Pen = (HPEN)SelectObject(m_hDC, pen);
	HBRUSH brush = BrSelection(type);
	Rectangle(m_hDC, sx, sy, ex, ey);
	SelectObject(m_hDC, m_Brush);
	SelectObject(m_hDC, m_Pen);
	DeleteObject(brush);			//NULL_BRUSHの場合でも問題は無い
	DeleteObject(pen);
	ReDraw(sx, sy, ex, ey);
}

//円を描く
void CANVAS::Circle(int x, int y, int rd, int type = 0) {

	HPEN pen = CreatePen(m_PStyle, m_PWidth, m_Color);
	m_Pen = (HPEN)SelectObject(m_hDC, pen);
	HBRUSH brush = BrSelection(type);
	Ellipse(m_hDC, x - rd, y - rd, x + rd, y + rd);
	SelectObject(m_hDC, m_Brush);
	SelectObject(m_hDC, m_Pen);
	DeleteObject(brush);			//NULL_BRUSHの場合でも問題は無い
	DeleteObject(pen);
	ReDraw(x - rd, y - rd, x + rd + 1, y + rd + 1);
}

//縦横比可変の楕円を描く
void CANVAS::Ellipsed(int sx, int sy, int ex, int ey, int type = 0) {

	HPEN pen = CreatePen(m_PStyle, m_PWidth, m_Color);
	m_Pen = (HPEN)SelectObject(m_hDC, pen);
	HBRUSH brush = BrSelection(type);
	Ellipse(m_hDC, sx, sy, ex, ey);
	SelectObject(m_hDC, m_Brush);
	SelectObject(m_hDC, m_Pen);
	DeleteObject(brush);			//NULL_BRUSHの場合でも問題は無い
	DeleteObject(pen);
	ReDraw(sx, sy, ex, ey);
}

//半径無円弧を描く
void CANVAS::Arced(int sx, int sy, int ex, int ey, int srx, int sry, int erx, int ery) {

	HPEN pen = CreatePen(m_PStyle, m_PWidth, m_Color);
	m_Pen = (HPEN)SelectObject(m_hDC, pen);
	Arc(m_hDC, sx, sy, ex, ey, srx, sry, erx, ery);
	SelectObject(m_hDC, m_Pen);
	DeleteObject(pen);
	ReDraw(sx, sy, ex, ey);
}

//半径付円弧を描く
void CANVAS::Chorded(int sx, int sy, int ex, int ey, int srx, int sry, int erx, int ery, int type = 0) {

	HPEN pen = CreatePen(m_PStyle, m_PWidth, m_Color);
	m_Pen = (HPEN)SelectObject(m_hDC, pen);
	HBRUSH brush = BrSelection(type);
	Chord(m_hDC, sx, sy, ex, ey, srx, sry, erx, ery);
	SelectObject(m_hDC, m_Brush);
	SelectObject(m_hDC, m_Pen);
	DeleteObject(brush);			//NULL_BRUSHの場合でも問題は無い
	DeleteObject(pen);
	ReDraw(sx, sy, ex, ey);
}

//指定色で塗潰す
bool CANVAS::Paint(int x, int y, COLORREF col) {		

	bool result;
	COLORREF sfCol;
	HBRUSH hBrush = CreateSolidBrush(col);
	HGDIOBJ hOldBrush = SelectObject(m_hDC, hBrush);
	sfCol = GetPixel(m_hDC, x, y);
	result = ExtFloodFill(m_hDC, x, y, sfCol, FLOODFILLSURFACE);
	SelectObject(m_hDC, hOldBrush);
	DeleteObject(hBrush);
	InvalidateRect(m_hWnd, NULL, TRUE);
	return result;
}

//ビットマップ画像取込み
HBITMAP CANVAS::GetBMP(int x, int y, int w, int h) {

	if(m_hBitmap)					//既に読みこまれたビットマップは
		DeleteObject(m_hBitmap);	//削除する

	HDC hDC = GetDC(m_hWnd);						//ウィンドウDCを取得
	HDC memDC = CreateCompatibleDC(hDC);			//コンパチＤＣを作成
	m_hBitmap = CreateCompatibleBitmap(hDC, w, h);	//コンパチビットマップを作成
	SelectObject(memDC, m_hBitmap);					//ビットマップを選択
	StretchBlt(memDC, 0, 0, w, h, m_hDC, x, y, w, h, SRCCOPY);
	ReleaseDC(m_hWnd, hDC);							//ウィンドウDCを開放
	DeleteObject(memDC);							//コンパチＤＣを削除
	m_BMPw = w;										//ビットマップ幅を記録
	m_BMPh = h;										//ビットマップ高を記録
	return m_hBitmap;
}

//ビットマップ画像貼付け（１）
bool CANVAS::PutBMP(int x, int y, int ro) {

	if(!m_hBitmap)				//既に読みこまれたビットマップが無ければ
		return FALSE;			//偽を返す

	DWORD dwRop;
	switch(ro) {
		case 1:		dwRop = SRCAND;		break;	//And
		case 2:		dwRop = SRCPAINT;	break;	//Or
		case 3:		dwRop = SRCINVERT;	break;	//Xor
		default:	dwRop = SRCCOPY;	break;	//Pset
	}

	HDC hDC = GetDC(m_hWnd);						//ウィンドウDCを取得
	HDC memDC = CreateCompatibleDC(hDC);			//コンパチＤＣを作成
	SelectObject(memDC, m_hBitmap);					//ビットマップを選択
	bool success = BitBlt(m_hDC, x, y, m_BMPw, m_BMPh, memDC, 0, 0, dwRop);
	ReDraw(x, y, x + m_BMPw, y + m_BMPh);			//描画
	ReleaseDC(m_hWnd, hDC);							//ウィンドウDCを開放
	DeleteObject(memDC);							//コンパチＤＣを削除
	return success;
}

//ビットマップ画像貼付け（２）
bool CANVAS::PutBMP(HBITMAP bmp, int x, int y, int ro) {

	if(!bmp)					//有効なビットマップが無ければ
		return FALSE;			//偽を返す

	DWORD dwRop;
	switch(ro) {
		case 1:		dwRop = SRCAND;		break;	//And
		case 2:		dwRop = SRCPAINT;	break;	//Or
		case 3:		dwRop = SRCINVERT;	break;	//Xor
		default:	dwRop = SRCCOPY;	break;	//Pset
	}

	HDC hDC = GetDC(m_hWnd);						//ウィンドウDCを取得
	HDC memDC = CreateCompatibleDC(hDC);			//コンパチＤＣを作成
	SelectObject(memDC, bmp);						//ビットマップを選択
	BITMAP bm;										//BITMAP構造体
	GetObject(bmp, sizeof(BITMAP), &bm);			//サイズをszに取り出す
	bool success = BitBlt(m_hDC, x, y, bm.bmWidth, bm.bmHeight, memDC, 0, 0, dwRop);
	ReDraw(x, y, x + bm.bmWidth, y + bm.bmHeight);	//描画
	ReleaseDC(m_hWnd, hDC);							//ウィンドウDCを開放
	DeleteObject(memDC);							//コンパチＤＣを削除
	return success;
}

//ビットマップ画像貼付け（３）
bool CANVAS::FitBMP(int x, int y, int w, int h, int ro) {

	if(!m_hBitmap)				//既に読みこまれたビットマップが無ければ
		return FALSE;			//偽を返す

	DWORD dwRop;
	switch(ro) {
		case 1:		dwRop = SRCAND;		break;	//And
		case 2:		dwRop = SRCPAINT;	break;	//Or
		case 3:		dwRop = SRCINVERT;	break;	//Xor
		default:	dwRop = SRCCOPY;	break;	//Pset
	}

	HDC hDC = GetDC(m_hWnd);						//ウィンドウDCを取得
	HDC memDC = CreateCompatibleDC(hDC);			//コンパチＤＣを作成
	SelectObject(memDC, m_hBitmap);					//ビットマップを選択
	bool success = StretchBlt(m_hDC, x, y, w, h,
							memDC, 0, 0, m_BMPw, m_BMPh, dwRop);
	ReDraw(x, y, x + w, y + h);						//描画
	ReleaseDC(m_hWnd, hDC);							//ウィンドウDCを開放
	DeleteObject(memDC);							//コンパチＤＣを削除
	return success;
}

//ブラシ選択（0-透明、1-ソリッド、2〜6-異なるハッチブラシ）
HBRUSH CANVAS::BrSelection(int type) {

	HBRUSH brush;
	switch(type) {
		case 1:	//Solid Brush
			brush = CreateSolidBrush(m_Color);
			break;
		case 2:	//A 45-degree upward, left-to-right hatch
			brush = CreateHatchBrush(HS_BDIAGONAL, m_Color);
			break;
		case 3:	//Horizontal and vertical cross-hatch
			brush = CreateHatchBrush(HS_CROSS, m_Color);
			break;
		case 4:	//45-degree crosshatch
			brush = CreateHatchBrush(HS_DIAGCROSS, m_Color);
			break;
		case 5:	//A 45-degree downward, left-to-right hatch
			brush = CreateHatchBrush(HS_FDIAGONAL, m_Color);
			break;
		case 6:	//Horizontal hatch
			brush = CreateHatchBrush(HS_HORIZONTAL, m_Color);
			break;
		case 7:	//Vertical hatch
			brush = CreateHatchBrush(HS_VERTICAL, m_Color);
			break;
		case 0:	//0又は上記以外の値の場合は透明（NULL）ブラシ
		default:
			brush = (HBRUSH)GetStockObject(NULL_BRUSH);
			break;
	}
	m_Brush = (HBRUSH)SelectObject(m_hDC, brush);
	return brush;
}

//m_hWndのハンドルのWM_PAINTメッセージの所にこの関数を置く
void CANVAS::OnPaint(HDC hDC, int x = 0, int y = 0) {

	//ウィンドウを仮想ウィンドウ（Bitmap）で描画
	BitBlt(hDC, m_Left, m_Top, m_Right, m_Bottom, m_hDC, x, y, SRCCOPY);
}

//仮想ウィンドウに描画した矩形部分（(sx, sy)-(ex, ey)）を再描画させる
void CANVAS::ReDraw(int sx, int sy, int ex, int ey) {

	RECT rec;
	rec.left = sx > ex ? ex : sx;
	rec.right = sx > ex ? sx + 1 : ex + 1;
	rec.top = sy > ey ? ey : sy;
	rec.bottom = sy > ey ? sy + 1 : ey + 1;
	InvalidateRect(m_hWnd, &rec, TRUE);
}


//描画領域に矩形を描く
void CANVAS::DragRect(int x, int y, bool flag) {

	static RECT rect;
	HDC hDC = GetDC(m_hWnd);
	if(flag) {		//描画
		if(m_Focus) {
			DrawFocusRect(hDC, &rect);
			rect.left = m_Start.x <= x ? m_Start.x : x;
			rect.right = m_Start.x > x ? m_Start.x : x;
			rect.top = m_Start.y <= y ? m_Start.y : y;
			rect.bottom = m_Start.y > y ? m_Start.y : y;
			DrawFocusRect(hDC, &rect);
		}
		else {
			m_Start.x = rect.left = rect.right = x;
			m_Start.y = rect.top = rect.bottom = y;
			DrawFocusRect(hDC, &rect);
			m_Focus = TRUE;
		}
	}
	else {
		DrawFocusRect(hDC, &rect);
		m_Focus = FALSE;
	}
	ReleaseDC(m_hWnd, hDC);
}

void CANVAS::PrintText(int x, int y, LPCTSTR str, int w = 0,
					UINT fmt = DT_LEFT | DT_NOCLIP) {

	RECT rec;
	GetClientRect(m_hWnd, &rec);
	rec.left = x;
	rec.top = y;
	if(w)	rec.right = x + w;
	rec.bottom = y + DrawText(m_hDC, str, -1, &rec, DT_CALCRECT);
	DrawText(m_hDC, str, -1, &rec, fmt);
	InvalidateRect(m_hWnd, &rec, TRUE);
}

void CANVAS::PrintTextEx(LPCTSTR str, LPRECT lprec, COLORREF rgb, LPCTSTR FontName, int FontSize,
				UINT fmt = DT_LEFT | DT_NOCLIP,
				int nEscapement = 0, int nOrientation = 0, int nWeight = FW_NORMAL,
				DWORD fdwItalic = FALSE, DWORD fdwUnderline = FALSE,
				DWORD fdwStrikeOut = FALSE, DWORD fdwCharSet = DEFAULT_CHARSET) {
	if(m_hFont)
		DeleteObject(m_hFont);
	m_hFont = CreateFont(FontSize, 0,
			nEscapement, nOrientation, nWeight,
			fdwItalic, fdwUnderline, fdwStrikeOut,
			fdwCharSet,
			OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
			DEFAULT_QUALITY,
			DEFAULT_PITCH,
			FontName);
	SelectObject(m_hDC, m_hFont);
	SetTextColor(m_hDC, rgb);
	DrawText(m_hDC, str, -1, lprec, fmt);
	InvalidateRect(m_hWnd, lprec, TRUE);
}

void CANVAS::Font(HFONT newfont) {

	if(m_hFont)
		DeleteObject(m_hFont);	//現在使用しているフォントを削除
	SelectObject(m_hDC, (m_hFont = newfont));
}
