2025年1月
 1234
567891011
12131415161718
19202122232425
262728293031  

カテゴリー

アーカイブ

ブログ統計情報

  • 103,604 アクセス


C++ 戻り値の異なる関数 template

先日からやっている .ini やレジストリにアクセスする関数.
MFC の CWinApp::GetProfileString , CWinApp::WriteProfileString にあたる部分は目途がついた.
それで,それらを呼出す部分.前に作成したものもそうだったが,型ごとにクラスの関数を定義していた.
レジストリにアクセスするクラス
これらをもう少し簡単にできないかと…


set の方は,特に難しい所はない(::To_tstring は,文字列に変換する関数として用意している).

template	<class T>	bool	INI_set	(LPCTSTR sec,LPCTSTR ent,const T& val)
{
	tstring	ini = ::INI_get_module_ini() ;
	tstring	str = ::To_tstring(val) ;
	return	::INI_set(ini.c_str(),sec,ent,str.c_str()) ;
	}

get の場合,文字列から変数に変換する方法をどうするか?
例えば,atoi や atof ,他にも 4 つの整数の文字列を RECT に変換するなど.
検索すると template で可能みたい だが…
よくわからなかったので,簡単な方法にした.

inline	RECT	To_RECT		(LPCTSTR str)
{
	RECT	rect	=	{	0	} ;
	{
		v_tstring	str_ary = ::String_SplitSpace(str,_T(" ,\t\r\n")) ;
		if (0 < str_ary.size())	{	rect.left	= ::ttoi4(str_ary[0]) ;	}
		if (1 < str_ary.size())	{	rect.top	= ::ttoi4(str_ary[1]) ;	}
		if (2 < str_ary.size())	{	rect.right	= ::ttoi4(str_ary[2]) ;	}
		if (3 < str_ary.size())	{	rect.bottom	= ::ttoi4(str_ary[3]) ;	}
		}
	return	rect ;
	}
inline	bool	string_to	(LPCTSTR str,RECT*  rect_)	{	*rect_ = ::To_RECT (str) ;	return	true ;	}
inline	bool	string_to	(LPCTSTR str,POINT* point)	{	*point = ::To_POINT(str) ;	return	true ;	}
inline	bool	string_to	(LPCTSTR str,SIZE*  size_)	{	*size_ = ::To_SIZE (str) ;	return	true ;	}

それぞれの型に合わせた関数を呼べるようになったので template に.

template	<class T>	T	INI_get	(LPCTSTR sec,LPCTSTR ent,const T& def)
{
	tstring	ini = ::INI_get_module_ini() ;
	tstring	dst = ::To_tstring(def) ;
	tstring	str = ::INI_get(ini.c_str(),sec,ent,dst.c_str()) ;
	T	val ;
	::string_to(str.c_str(),&val) ;
	return	val ;
	}

これで,次の様な使い方ができる様になる.

{
	POINT	point = ::POINT_set(   10,   20) ;
	SIZE	size_ = :: SIZE_set( 1111,  525) ;
	RECT	rect_ = :: RECT_set(point,size_) ;
	{
		::INI_set(_T("test"),_T("rect_"),rect_) ;
		::INI_set(_T("test"),_T("point"),point) ;
		::INI_set(_T("test"),_T("size_"),size_) ;
		std::tout << ::To_tstring(::INI_get(_T("test"),_T("rect_"),rect_)) << std::endl ;
		std::tout << ::To_tstring(::INI_get(_T("test"),_T("point"),point)) << std::endl ;
		std::tout << ::To_tstring(::INI_get(_T("test"),_T("size_"),size_)) << std::endl ;
		}
	}

::INI_set ,::INI_get の動作テスト

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

C++ NonCopyable

MFC を使用しないコードに書き直していて,代入できない構造体が欲しくなった.
オリジナルのコードは 20 年以上前のもので,CRegKey が簡単には使えなかった?頃.


C++ クラス 代入できなくする」で検索.
コピー禁止を徹底させるNoncopyableクラス
More C++ Idioms/コピー禁止ミックスイン(Non-copyable Mixin)
明示的に既定された関数および削除された関数
MFC の CObject も同様と思いソースを見ると,やはり private になっている.
MFC CObject 代入の禁止
C++11 以降では =delete も使える.
関数のdefault/delete宣言
wiki C++11


これらを調べていて,次の所を見つけた.
旧時代のC言語を使うのはそろそろやめよう。
2016年、C言語はどう書くべきか (前編)
2016年、C言語はどう書くべきか (後編)
幾つかは既に意識しているが,Windows に依存する部分はなかなかできてない.

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

RegOpenKeyEx REGSAM

レジストリアクセスのコードを書き直していて,::RegOpenKeyExsamDesired を調べてみた.

KEY_READ              (0x20019)	0010 0000 0000 0001 1001
KEY_WRITE             (0x20006)	0010 0000 0000 0000 0110
KEY_EXECUTE           (0x20019) 0010 0000 0000 0001 1001

KEY_QUERY_VALUE        (0x0001)	     0000 0000 0000 0001
KEY_SET_VALUE          (0x0002)	     0000 0000 0000 0010
KEY_CREATE_SUB_KEY     (0x0004)	     0000 0000 0000 0100
KEY_ENUMERATE_SUB_KEYS (0x0008)	     0000 0000 0000 1000
KEY_NOTIFY             (0x0010)	     0000 0000 0001 0000

KEY_CREATE_LINK        (0x0020)	     0000 0000 0010 0000
KEY_WOW64_64KEY        (0x0100)	     0000 0001 0000 0000
KEY_WOW64_32KEY        (0x0200)	     0000 0010 0000 0000

KEY_ALL_ACCESS        (0xF003F)	1111 0000 0000 0011 1111

ソースを見た方が早かったか?
RegOpenKeyEx REGSAM


読み込み時,KEY_READ の方が速いなどはあるのか?それともファイルアクセスなどと同じ?

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

GetMonitorInfo

2002/08 に,マルチディスプレイ対応のコードを書いている.
今回,高 DPI 対応やディスプレイ位置が変わった時などのためもう一度…


ChatGPT で.
ChatGPT  GetMonitorInfo 使い方サンプル
そのままでは VC 6 ではうまくビルドできなかったので,VC 8 で.
printf を使用しているので #include <cstdio> が必要.
そのまま実行すると 1920×1080 となる.
GetMonitorInfo
高 DPI スケール設定を「アプリケーション」とすると,3840×2160 .


2023/04/14
以前のコードを見ると,プライマリのみの情報は ::SystemParametersInfo などを使用している.
これで求められる SPI_GETWORKAREA は,::GetMonitorInfo で戻されるものと同じ?

システム     0  61 1920 1080
アプリケーション 0 122 3840 2160

::GetSystemMetrics(SM_?VIRTUALSCREEN) は,-1920 0 .
::GetSystemMetrics(SM_C?VIRTUALSCREEN) は,上をずらしているので少し異なる.

システム     3840 1210
アプリケーション 5760 2160

Z170S0  ディスプレイの配置
https://learn.microsoft.com/ja-jp/windows/win32/gdi/the-virtual-screen

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

MFC タイトルバーの変更

ダイアログベースであれば C…Dlg::OnInitDialog() に次の様なコードを追加.

	{
		tstring	str_title = ::GetWindowText(this->GetSafeHwnd()) ;
		       	str_title+= _T(" ") + ::Get_ModuleVersion() + ::Get_BuildStrMSC() ;
		SetWindowText(str_title.c_str()) ;
		}

SDI や MDI の場合は,
MainFrm.h に OnUpdateFrameTitle を追加.

	virtual void OnUpdateFrameTitle (BOOL bAddToTitle);

MainFrm.cpp に次の様な OnUpdateFrameTitle を追加.

//	SDI
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
	CFrameWnd::OnUpdateFrameTitle(bAddToTitle) ;
	{
		tstring	str_title = ::GetWindowText(this->GetSafeHwnd()) ;
		       	str_title+= _T(" ") + ::Get_ModuleVersion() + ::Get_BuildStrMSC() ;
		SetWindowText(str_title.c_str()) ;
		}
	}
//	MDI
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
	CMDIFrameWnd::OnUpdateFrameTitle(bAddToTitle) ;
	{
		tstring	str_title = ::GetWindowText(this->GetSafeHwnd()) ;
		       	str_title = ::MDI_Add_VerBuildStr(str_title.c_str()) ;
		SetWindowText(str_title.c_str()) ;
		}
	}

S_asZ のタイトルバーにバージョンなどの付加

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

高 DPI CToolBar のリサイズ

CToolBar を使用した 自前のコード
CDialogBar や CStatusBar はスケーリングしてくれるのに,CToolBar は対応しない?
以前は設定などで変更可能なコードにしていたが,DPI を求める方法に.

	if (newExtendSize == CSize(0,0)) {
		double	dpi_s = ::GetDPI_scale(toolBar->GetSafeHwnd()) ;  // ::GetDpiForWindow()/96.
		if (dpi_s > 1) {
			CToolBarCtrl&	tbCtrl = toolBar->GetToolBarCtrl() ;
			CSize	orgSize = tbCtrl.GetButtonSize() ;
			CSize	newSize = orgSize ;
			newSize.cx = int(orgSize.cx*dpi_s) ;
			newSize.cy = int(orgSize.cy*dpi_s) ;
			newExtendSize.cx = newSize.cx - orgSize.cx ;
			newExtendSize.cy = newSize.cy - orgSize.cy ;
			resizeType = 'R' ;
			}
		}

あとは既存コードの,ビットマップのリサイズと CToolBar::SetSizes を呼出せば良さそう.


CMFCToolBar は DPI を正しく処理しているみたいで,その部分のコード.
…\VC\…\atlmfc\src\mfc\afxtoolbar.cpp の CMFCToolBar::LoadToolBarEx .
CMFCToolBar::LoadToolBarEx  SetSizes


まだ幾つか問題はあるが,今回の対応部分としてはこれで良さそう.
ToolBar::Resize のコードを変更


更にツールバー上のコンボボックスのスケーリングは次の様にした.

	{
		int		width = 50 ;
		{
			double	dpi_s = 1. ;
			    	dpi_s = ::GetDPI_scale(AfxGetMainWnd()->GetSafeHwnd()) ;
			    	dpi_s = ::GetDPI_scale(    m_wndToolBar.GetSafeHwnd()) ;
			    	dpi_s = ::GetDPI_scale(           this->GetSafeHwnd()) ;
			if (dpi_s > 1.) {
				width = int(width*dpi_s) ;
				}
			}
		m_wndToolBar.SetButtonInfo  (19,ID_COMBO_ANGLE,TBBS_SEPARATOR,width) ;
		CRect	rectCombo ;
		m_wndToolBar.GetItemRect    (19,&rectCombo) ;
		rectCombo.top = 1 ;
		rectCombo.bottom = rectCombo.top + 300 ;
		if (!m_ComboAngle.Create(CBS_DROPDOWN | WS_VSCROLL | WS_VISIBLE,rectCombo,&m_wndToolBar,ID_COMBO_ANGLE)) {
			TRACE0("Failed to ComboBox\n");
			return -1;
			}
		m_ComboAngle.SetFont(m_wndToolBar.GetFont()) ;
		m_ComboAngle.AddString(_T(" 0.")) ;
		m_ComboAngle.AddString(_T("10.")) ;
	//	...
		m_ComboAngle.AddString(_T("90.")) ;
		}
Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

drawlog が更新されない?

Web サーバ管理用の,アクセスログの表示が更新されなくなった.
DS220  Web サーバへのアクセスログ
/tmp/ がいっぱいになったかと思い,解放したが変わらず.
それならと思い NAS を再起動したが,それでも変わらない.


不正なアクセスにより,ログにゴミが入ったみたいで,そこで表示データの生成が止まっていた.
ログに "\0" が入っている

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

高 DPI テスト exe

先日の exe をテストしていると…
TToPA.exe を「システム(拡張)」で実行


高 DPI をテストするために,次の様なコードの exe を作成.

void CT_aesDlg::OnDropFiles(HDROP hDropInfo) 
{
	v_tstring	drop_files = ::DropFilesTo(hDropInfo) ;
	for (size_t index=0 ; index<drop_files.size() ; index++) {
		tstring	drop_file = drop_files[index] ;
		tstring	ext = ::Path_GetExtLow(drop_file) ;
		if (ext != _T("exe"))	{	continue ;	}
		{
			S_Exec	se ;
			se.SetFile(drop_file.c_str()) ;
			se.Execute() ;
			}
		}
	CDialog::OnDropFiles(hDropInfo);
	}

この exe に,他の exe をドロップして起動すると,「高 DPI スケール設定」が引き継がれる.
「高 DPI スケール設定」
これらの設定は,次の所に持っている?

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
   A	~ HIGHDPIAWARE	
   S	~ DPIUNAWARE	
   E	~ GDIDPISCALING DPIUNAWARE	

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers


それぞれを実行すると表示が異なることはわかるが…
HiDPI テスト exe
左から,「システム」,「システム(拡張)」,「アプリケーション」.


「システム」と「システム(拡張)」を区別する方法がわからない.
また「システム(拡張)」.exe で,何かの情報の取得が違っていて 表示が正しくない ものと思う.


::GetDeviceCaps の情報の表示は dc.DrawText(str,rect,DT_LEFT) としている.
この時,表示するフォントを指定していないため,「アプリケーション」では小さくなってしまう.
CFont::CreatePointFontIndirect
CFont::CreatePointFont などを呼ぶことで対応可能.

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

tcsncpy_s Buffer is too small

先日更新した ツール をテストしていると,フォントによりアプリケーションエラー?となってしまう.
デバッガで追いかけると,
—————————
Microsoft Visual C++ Runtime Library
—————————
Debug Assertion Failed!
Program: c:\Temp\i_Tools\TToPA\Debug.120\TToPA.exe
File: f:\dd\vctools\crt\crtw32\h\tcsncpy_s.inl
Line: 62
Expression: (L"Buffer is too small" && 0)
For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.
(Press Retry to debug the application)
—————————
中止(A) 再試行(R) 無視(I)
—————————
tcsncpy_s Buffer is too small
CRichEditCtrl を使用した CHARFORMATW の szFaceName の指定がうまくなかった.
::TcsNCpy(cf.szFaceName,LF_FACESIZE-1,faceName,LF_FACESIZE-1) ;
::TcsNCpy は,幾つかの環境でビルドできる様にしたもので,今回の場合は ::tcsncpy_s と同様もの.
faceName に与えたものが L"Bahnschrift Light SemiCondensed" で,コピー先のバッファが足りないため.
正しくは,
::TcsNCpy(cf.szFaceName,LF_FACESIZE-0,faceName,LF_FACESIZE-1) ;


これらの動作は,LOGFONT の時にもよく利用する.
既存のコードを検索してみると,LF_FACESIZE を正しく指定できていた.
::TcsCpy(lf.lfFaceName,LF_FACESIZE,tstring(faceName).substr(0,LF_FACESIZE-1).c_str()) ;
与えるデータは LF_FACESIZE-1 としている.

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

GetGlyphOutline ::PolyPolygon

求めたアウトラインをウィンドウに表示できる様な変換のコードを書いたので,その動作のテスト.

{
	HWND	hWnd = ::GetConsoleHwnd() ;
	RECT	rect = ::GetClientRect(hWnd) ;
	POINT	point= ::RECT_center(rect) ;
	int  	ch   = ::RECT_width(rect)/str.length()*10/7 ;
	{
		HDC  	hDC  = ::GetDC(hWnd) ;
		{
			#define	face_A	_T("Arial")
			#define	face_U	_T("Meiryo UI")
			#define	face_W	_T("Wingdings")
			{
				::ClearWindow(hWnd) ;
				int  	ry = -30 ;
				tstring	face_name = face_U ;
				vv_PLF	vv_plf= ::GetTTOutline(hDC,point,false,str.c_str(),face_name.c_str(),ch,0,0,ry,FW_DONTCARE) ;
				v_Vd2A	v_v2a = ::ToVd2A(vv_plf) ;
				::PolyPolygon(hDC,v_v2a) ;
				}
			}
		::ReleaseDC(hWnd,hDC) ;
		}
	}

::GetTTOutline の中では,文字の傾き,文字列の回転,Y の反転,表示位置の移動を行っている.
アウトラインデータを ::PolyPolygon で描画
更に三角形に分割.

		vv_PLF	vv_plf= ::GetTTOutline(hDC,point,false,str.c_str(),face_name.c_str(),ch,0,0,ry,FW_DONTCARE) ;
	//	v_Vd2A	v_v2a = ::ToVd2A(vv_plf) ;
		v_Vd2A	v_v2a = ::PLF_triangulation(vv_plf) ;

三角形に分割


文字列 DXF 変換 Ver. 1.72
poly_3d.hxx
poly_2d.hxx

Is this 投稿 useful? Useful Useless 2 of 2 people say this 投稿 is useful.

ダイアログバーのイベント処理

あまり使わないと,すぐ忘れてしまう…


1999/10 に作成したプロジェクトを変更していて,ダイアログバーにコントロールを追加.
ダイアログバーのイベント処理
ClassWizard で CMainFrame に対してイベント処理を追加すれば良い.
ダイアログバー CMainFrame にイベントの追加
今回の SDI.exe は,ビューでの処理のみなので「再描画」のコマンドメッセージを Post している.

void CMainFrame::OnUpdateRx() 
{
	PostMessage(WM_COMMAND,ID_REDRAW,0) ;
	}

ビューでは次の様にしてコントロールにアクセスしている.

	CMainFrame* pMain = (CMainFrame*)AfxGetApp()->m_pMainWnd ;
	CDialogBar* dlgBar= &pMain->m_wndMyDialogBar ;
	CComboBox*  font  = (CComboBox*) dlgBar->GetDlgItem(IDC_FONT_NAME) ;
	CEdit*      text  = (CEdit*)     dlgBar->GetDlgItem(IDC_STRING) ;
	CEdit*      e_rx  = (CEdit*)     dlgBar->GetDlgItem(IDC_RX) ;
	CEdit*      e_ry  = (CEdit*)     dlgBar->GetDlgItem(IDC_RY) ;

ダイアログバーの追加手順は次の所.
https://dev.mish.work/wordpress/2021/12/27/vc-6-dialog-bar/

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

ShowWindow SW_HIDE SW_SHOW

ダイアログに「ピクチャーコントロール」を追加して,それの再描画.


以前よくやっていた方法.

void CFOutLineDlg::OnHeight() 
{
	if (m_CountSpin.m_hWnd == NULL)	{	return ;	}
	UpdateData(TRUE) ;
	InvalidateRect(NULL) ;
	}

これだと描画対象以外のコントロールも再描画するのでちらついてしまう.
ShowWindow SW_HIDE SW_SHOW


一度 ShowWindow で SW_HIDE して SW_SHOW することで,対象がコントロールだけになる.

void CFOutLineDlg::OnSize(UINT nType, int cx, int cy) 
{
	CDialog::OnSize(nType, cx, cy);
	if (m_CountSpin.m_hWnd == NULL)	{	return ;	}
	::FitWindow(this,&m_Image,5,TRUE,FALSE) ;
	m_Image.ShowWindow(SW_HIDE) ;
	m_Image.ShowWindow(SW_SHOW) ;
	}

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

error C2061 , C2091 , C2809 , C2556

1997/06 に作成したプロジェクトをビルドすると…

--------------------Configuration: FontFam - Win32 Release--------------------

FontFam.exe - 0 error(s), 0 warning(s)
--------------------Configuration: FontFam - Win32 Debug--------------------
Compiling...
FontFDlg.cpp
c:\program files (x86)\microsoft visual studio\vc98\include\new(35) : error C2061: syntax error : identifier 'THIS_FILE'
c:\program files (x86)\microsoft visual studio\vc98\include\new(35) : error C2091: function returns function
c:\program files (x86)\microsoft visual studio\vc98\include\new(35) : error C2809: 'operator new' has no formal parameters
c:\program files (x86)\microsoft visual studio\vc98\include\new(36) : error C2061: syntax error : identifier 'THIS_FILE'
c:\program files (x86)\microsoft visual studio\vc98\include\new(37) : error C2091: function returns function
c:\program files (x86)\microsoft visual studio\vc98\include\new(37) : error C2556: 'void *(__cdecl *__cdecl operator new(void))(unsigned int,const struct std::nothrow_t &)' : overloaded function differs only by return type from 'void *(__cdecl *__cd
ecl operator new(void))(unsigned int)'
        c:\program files (x86)\microsoft visual studio\vc98\include\new(35) : see declaration of 'new'
c:\program files (x86)\microsoft visual studio\vc98\include\new(41) : error C2061: syntax error : identifier 'THIS_FILE'
c:\program files (x86)\microsoft visual studio\vc98\include\new(42) : error C2091: function returns function
c:\program files (x86)\microsoft visual studio\vc98\include\new(42) : error C2556: 'void *(__cdecl *__cdecl operator new(void))(unsigned int,void *)' : overloaded function differs only by return type from 'void *(__cdecl *__cdecl operator new(void))
(unsigned int)'
        c:\program files (x86)\microsoft visual studio\vc98\include\new(35) : see declaration of 'new'
c:\program files (x86)\microsoft visual studio\vc98\include\new(42) : error C2809: 'operator new' has no formal parameters
c:\program files (x86)\microsoft visual studio\vc98\include\new(42) : error C2065: '_P' : undeclared identifier
Error executing cl.exe.

FontFam.exe - 11 error(s), 0 warning(s)

以前は <memory>インクルードを追加 することで対応していた.


原因は,幾つかのインクルードが次のコードより後に存在しているため?

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

今回は,インクルードしている hxx を前に持ってくることで対応.
error C2061: syntax error : identifier 'THIS_FILE' の対応

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

GetGlyphOutline ポリゴン化

まだまだだが…

文字ごとのポリラインを取得できる様になったので,穴の処理と組み合わせてポリゴン化.
文字によっては,穴の処理でクロスしてしまうことがある.
https://itl.mish.work/Iwao/Doc/algo/div_cnvx/div_23_4.html
https://itl.mish.work/Iwao/Doc/algo/div_cnvx/div_23_5.html

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

GetGlyphOutline ポリラインの配列に

「MS ゴシック」と「MS Pゴシック」などの幅が,思っていたものと違った.
「MS ゴシック」と「MS Pゴシック」

v_PLF	TTPOLYGON_to	(LPTTPOLYGONHEADER buf,const DWORD rSize)
{
	vv_LQC	vv_lqc = ::to_LQC(buf,rSize) ;		//	buf を座標の配列に
	v_PLF	v_plf  = ::LQC_to(vv_lqc) ;		//	ポリラインに変換
	return	v_plf ;
	}

v_PLF	GetTTOutline	(HDC hDC,const UINT chr,GLYPHMETRICS* gm,const UINT format=GGO_NATIVE)
{
	v_PLF	v_plf ;
	{
		MAT2		mat2 = { {0, 1}, {0, 0}, {0, 0}, {0, 1} } ;
		DWORD	dwSize = ::GetGlyphOutline(hDC,chr, format, gm, 0, NULL, &mat2 ) ;
		if (dwSize != GDI_ERROR) {
			v_char	buffer(dwSize) ;
			GLYPHMETRICS gm2 = { 0 } ;
			::GetGlyphOutline(hDC, chr, format, &gm2, buffer.size(), &buffer[0], &mat2) ;
			v_plf = TTPOLYGON_to((LPTTPOLYGONHEADER)&buffer[0],dwSize) ;
			}
		}
	return	v_plf ;
	}

vv_PLF	GetTTOutline	(HDC hDC,LPCTSTR str,const bool tate,const UINT format=GGO_NATIVE)
{
	vv_PLF	vv_plf ;
	double	lastX = 0 ;		//	left
	double	lastY = 0 ;		//	top	tate
	int	ch = 0 ;
	int	cw = 0 ;
	{	//	chracter size
		TEXTMETRIC	tm = { 0 } ;
		::GetTextMetrics(hDC,&tm) ;
		ch = tm.tmHeight ;
		cw = tm.tmAveCharWidth ;
		}
	#ifdef	_UNICODE
	LPCTSTR		strP = str ;
	#else
	unsigned char*	strP = (unsigned char*)str ;
	#endif
	for (size_t mbCount=0 ; mbCount<_tcsclen(str) && *strP!='\0' ; mbCount++) {
		UINT	chr = 0 ;
		#ifdef	_UNICODE
		{
			chr = UINT(*strP) ;
			strP++ ;
			}
		#else
		{
			chr = _mbsnextc(strP) ;
			strP= _mbsinc(strP) ;
			}
		#endif
		if (chr == _T('\r'))	{	lastX = 0 ;	continue ;	}
		if (chr == _T('\n'))	{	lastY-= ch ;	continue ;	}
		if (chr == _T('\t'))	{	lastX+= cw ;	continue ;	}
		{
			GLYPHMETRICS	gm = { 0 } ;
			v_PLF	v_plf = ::GetTTOutline(hDC,chr,&gm,format) ;
			if (tate)	{	lastY -= (double(gm.gmBlackBoxY)+gm.gmCellIncX)/2 ;	}
			else		{								}
			Vd2		last(lastX,lastY) ;
			for (size_t index=0 ; index<v_plf.size() ; index++) {
				PLF	plf = v_plf[index] ;
				Vd2A	v2a = ::V2_Translate(::ToVd2A(::ToVd3A(plf)),last) ;
				v_plf[index] = PLF(PLF::line,::ToVd4A(::ToVd3A(v2a))) ;
				}
			vv_plf.push_back(v_plf) ;
			if (tate)	{					}
			else		{	lastX += gm.gmCellIncX ;	}
			}
		}
	return	vv_plf ;
	}

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

Q243285 の動作の確認

先日からやっている GetGlyphOutline .その Q243285 の動作を確認方法.
Q243285: HOWTO: Draw TrueType Glyph Outlines


その中の ::DrawT2Outline の動作を確認しようとコンソール AP でやってみたが,うまく表示されない.
コードを見ればわかるが,::PolyBezier に与えられている pt の y がマイナスの範囲になっている.


以前書いたコードで emf に出力するものがある.また,それの HWND への再生も.
それで,HDC に対して直接出力するのではなく,一度 emf に出力して,それを表示することに.


#include	<clocale>
#include	<iostream>
#include	<fstream>

#include	<Windows.h>

#include	"Q243285.hxx"

#include	"con_wnd.hxx"
#include	"E_MF.hxx"

LOGFONT	GetLogFont(
	LPCTSTR			faceName
,	const	bool	tate
,	const	int	ch
,	const	int	cw	=	0
,	const	double	rx	=	0
,	const	double	ry	=	0
,	const	int	weight	=	FW_DONTCARE
	)
{
	LOGFONT		lf =	{	0	} ;
	::GetObject(::GetStockObject(SYSTEM_FONT),sizeof(lf),&lf) ;
	if (tate)	{	lf.lfHeight	= (cw!=0)?cw:ch ;	lf.lfWidth	= ch/2 ;		}
	else		{	lf.lfHeight	= ch ;			lf.lfWidth	= cw ;			}
	if (tate)	{	lf.lfEscapement	= int((rx+ry-90)*10) ;	lf.lfOrientation= int((ry-90)*10) ;	}
	else		{	lf.lfEscapement	= int(rx*10) ;		lf.lfOrientation= int(ry*10) ;		}
	lf.lfWeight		= weight ;
	::TcsCpy(lf.lfFaceName,LF_FACESIZE,tstring(faceName).substr(0,LF_FACESIZE-1).c_str()) ;
	return	lf ;
	}

tstring	Get_TMP_path	(void)
{
	tstring	tmp_path ;
	size_t	size = MAX_PATH ;
	tmp_path.resize(size,0) ;
	::GetTempPath(DWORD(size),&tmp_path[0]) ;
	tmp_path = tstring(tmp_path.c_str()) ;
	return	tmp_path ;
	}

bool	DrawOutline(HDC hDC,int xStart,int yStart,LPCTSTR str)
{
	#ifdef	_UNICODE
	LPCTSTR			strP = str ;
	#else
	unsigned char*	strP = (unsigned char*)str ;
	#endif
//	tstring		tmp_path = ::Get_i_Tools_tmp_date() ;
	tstring		tmp_path = ::Get_TMP_path() ;
	tstring		emf_name ;
	for (size_t mbCount=0 ; mbCount<_tcsclen(str) && *strP!='\0' ; mbCount++) {
		UINT	chr = 0 ;
		#ifdef	_UNICODE
		{
			chr	= UINT(*strP) ;
			strP++ ;
			}
		#else
		{
			chr	= _mbsnextc(strP) ;
			strP	= _mbsinc(strP) ;
			}
		#endif
		{
			tstring	tcs ;
			{
				if (chr < 0xff) {
					tcs += TCHAR(chr) ;
					}
				else {
					tstring	buf ;	buf.resize(255,0) ;
					::_itot(chr,&buf[0],16)  ;
					tcs = buf.c_str() ;
					}
				std::tout << tcs << std::endl ;
				}
			{
				emf_name = tmp_path + tcs + _T(".emf") ;
				}
			}
		{
			E_MetaF	em(emf_name.c_str()) ;
			HDC	eDC = em.Get_HDC() ;
			{
				MAT2		mat2 = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
				GLYPHMETRICS	gm = { 0 };
				DWORD	dwSize = ::GetGlyphOutline(hDC,chr, GGO_NATIVE, &gm, 0, NULL, &mat2);
				if (dwSize != GDI_ERROR) {
					std::vector<char> buffer(dwSize);
					GLYPHMETRICS gm2 = { 0 };
					::GetGlyphOutline(hDC, chr, GGO_NATIVE, &gm2, buffer.size(), &buffer[0], &mat2);
					::DrawT2Outline  (eDC,(LPTTPOLYGONHEADER)&buffer[0],dwSize) ;
					}
				}
			}
		{
			E_MetaF::Play(::GetConsoleHwnd(),emf_name.c_str()) ;
			}
		}
	::TextOut(hDC,xStart,yStart+150,str,_tcslen(str)) ;
	return	true ;
	}

bool	test	(c_tstring& str)
{
	std::terr << str << std::endl ;
	{
		HWND	hWnd = ::GetConsoleHwnd  () ;
		{
			HDC	hDC  = ::GetDC(hWnd) ;
			{
				#define	face_A	_T("Arial")
				#define	face_M	_T("Meiryo UI")
				#define	face_W	_T("Wingdings")
				LOGFONT	lf    = ::GetLogFont(face_M,false,100) ;
				HFONT	hFont = ::CreateFontIndirect(&lf) ;
				HFONT	oFont = (HFONT)::SelectObject(hDC,hFont) ;
				::DrawOutline(hDC,200,200,str.c_str()) ;
				::SelectObject(hDC,oFont) ;
				::DeleteObject(hFont) ;
				}
			::ReleaseDC(hWnd,hDC) ;
			}
		}
	return	true ;
	}

Q243285 の動作の確認

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

GetGlyphOutline TTPOLYCURVE

TTPOLYCURVE の TT_PRIM_LINE と TT_PRIM_QSPLINE は昨日までのコードでできそう.
TT_PRIM_CSPLINE についてもコードはわかりそうだが,実際のデータがちょっとわからない.
Q243285 の ::DrawT2Outline では,処理はしていないみたい.
::MakeBezierFromQBSpline では Quadratic から Cubic にしている.
12: 2次ベジェ曲線を3次に変換する方法
2次ベジェ曲線から3次ベジェ曲線への変換
今まで使っていたコードの TT_PRIM_CSPLINE 部分も同様で,特に問題はなかったようにも思う.


曲線の種類と Vd2A(2D の double の配列)を持つ「LQC クラス」を用意するか.
その配列 v_LQC が 1つの TTPOLYGONHEADER となる?
‘1’,’2′,’3′ などは 1つの TTPOLYGONHEADER .’A’ や ‘b’ は 2つ,’B’ や ‘8’ は 3つとなる.
MS ゴシック   あいうえお ...
1つの文字を表現するために v_LQC の配列の vv_LQC を用意.
ここまでで ::GetGlyphOutline の出力を vv_LQC として変換できる.
この vv_LQC をポリラインに変換して,v_PLF(複数のポリラインなどを保持可能)に登録する.
これで文字列を vv_PLF(そのままでは輪郭,先日の穴の処理をしてポリゴンに)として保持できる.


2023/03/17
TT_PRIM_CSPLINE に関して
GetGlyphOutline で uFormat に GGO_NATIVE ではなく GGO_BEZIER を指定.
すると,制御点が 2 つのデータが返される.
ポリラインに変換するコードは次の様なもの?

Vd2A	LQC_3_to	(const LQC& lqc)
{
	Vd2A	pts ;
	if (lqc.size() > 0)	{
		Vd2	q0(lqc[0]) ;
		Vd2	q1(lqc[0]) ;
		Vd2	q2(lqc[0]) ;
		Vd2	q3(lqc[0]) ;
		for (size_t index=0 ; index+3<lqc.size() ; index++) {
			if (index == 0)	{	q0 = lqc[index+0] ;	}
						q1 = lqc[index+1] ;
						q2 = lqc[index+2] ;
						q3 = lqc[index+3] ;
			Vd2A	va = ::bezier(q0,q1,q2,q3) ;
			pts.insert(pts.end(),va.begin(),va.end()) ;
			q0 = q3 ;
			}
		}
	return	pts ;
	}
Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

GetGlyphOutline の続き

TTPOLYCURVE は,ポリラインはそのまま扱えるが,ベジェ曲線の場合はそれなりの処理が必要.
TT_PRIM_LINE , TT_PRIM_QSPLINE , TT_PRIM_CSPLINE
それで ChatGPT に Bezier と問合わせると,
ChatGPT  Bezier
2次ベジェ曲線
B(t) = (1-t)^2 * P0 + 2t(1-t) * P1 + t^2 * P2
3次ベジェ曲線
B(t) = (1-t)^3 * P0 + 3t(1-t)^2 * P1 + 3t^2(1-t) * P2 + t^3 * P3


w0 から w3 を求める部分の挿入位置が違っていたが,ほぼそのままのコード.
ベジェ曲線の Python のコード
C++ でのコードは次の様なもの.5 年位前に作成した 3 次ベジェ曲線 に合わせたコード.

Vd3A	bezier	(const Vd3& q0,const Vd3& q1,const Vd3& q2,const long div_c)
{
	Vd3A	vd3a ;
	for (long index=0 ; index<=div_c ; index++) {
		double	t  = 1./div_c*index ;
		double	b0 = (1.-t)*(1.-t) ;			//	(1-t)^2
		double	b1 = 2.*t  *(1.-t) ;			//	2 * (1-t) * t
		double	b2 = t*t ;         			//	t^2
		double	px = (b0*q0.x) + (b1*q1.x) + (b2*q2.x) ;
		double	py = (b0*q0.y) + (b1*q1.y) + (b2*q2.y) ;
		double	pz = (b0*q0.z) + (b1*q1.z) + (b2*q2.z) ;
		vd3a.push_back(Vd3(px,py,pz)) ;
		}
	return	vd3a ;
	}

2023/03/14
最初,1 つの TT_PRIM_QSPLINE が次の様になっている場合の始点,終点と制御点の与え方がわからなかった.
(8,0) (5,0) (1,4) (1,8)    …   ‘O’ の左下,見やすい様に整数にしている.
::bezier を 2 回呼出す.それぞれの値は次の様に指定する.
(8,0) (5,0) (3,2)
(3,2) (1,4) (1,8)
(3,2) は (5,0) と (1,4) の中点.
TT_PRIM_QSPLINE  O
まだうまくない部分はあるが,何とか.
TT_PRIM_QSPLINE  月

Is this 投稿 useful? Useful Useless 1 of 1 people say this 投稿 is useful.

GetGlyphOutline の再調査

今動作しているコードを見ると,20 年以上前に書き始めたもの.
UNICODE 対応などの変更はあるが,基本的な動作はほとんど変わっていない.
MFC や古い自前のコードに依存している部分があるので,書き直そうかと…


その当時,どこを参考にしたのかわからない.一番古い部分は 1998 になっている.
テスト用に作成したプロジェクトのスケルトンの作成も 1997/04 のものがある.
次の所を参考にしたものと思うが,内容がだいぶ変わっている.
Q243285: HOWTO: Draw TrueType Glyph Outlines


::GetGlyphOutline の呼び出し方はそれほど難しくないが,返された TTPOLYGONHEADER などが難解.
当時はあまり理解できず,単純にポリラインに変換(QSpline2Polyline)していた.
今回検索すると,TTPOLYGONHEADER と TTPOLYCURVE などが詳しく書かれている所があった.
GetGlyphOutlineからアウトラインデータをもらおう
文字アウトラインの取得
Q87115: HOWTO: GetGlyphOutline() Native Buffer Format


2023/03/13
ここ のコードの結果を,2D データとしてダンプ.

	{
		tstring	tmp_path ;
		{
			size_t	size = MAX_PATH ;
			tmp_path.resize(size,0) ;
			::GetTempPath(DWORD(size),&tmp_path[0]) ;
			tmp_path = tstring(tmp_path.c_str()) ;
			}
		tstring	out_ipl = tmp_path ;
		{
		//	UINT	chr = character ;
			if (chr < 0xff) {
				out_ipl += TCHAR(chr) ;
				}
			else {
				tstring	buf ;	buf.resize(255,0) ;
				::_itot(chr,&buf[0],16)  ;
				out_ipl += buf.c_str() ;
				}
			out_ipl += _T(".ipl") ;
			}
		std::tout << out_ipl << std::endl ;
		std::ofstream	ipl_file(out_ipl.c_str()) ;
		for (size_t r_index=0 ; r_index<result.size() ; r_index++) {
			std::vector <LINE> vd = result[r_index] ;
			for (size_t v_index=0 ; v_index<vd.size() ; v_index++) {
				LINE	d = vd[v_index] ;
				ipl_file << _T("L") << std::endl ;
				for (size_t p_index=0 ; p_index<d.points.size() ; p_index++) {
					Vec2	p = d.points[p_index] ;
					ipl_file << p.x << _T(",") << p.y << std::endl ;
					}
				ipl_file  << std::endl ;
				}
			}
		ipl_file.close() ;
		}

%TEMP%\A.ipl は次の様になる.


L
36.5313,22
26.3125,50.2344
16.125,22

L
44.4219,0
38.6875,16
13.9375,16
8.20313,0
1,0
22.0469,57
30.9844,57
52,0


GetGlyphOutline  A
ipl の形式 f_ipl.hxx
個人的に作成した次のツールで表示可能.
https://jml.mish.work/index.php/i-tools/i3dx.html

Is this 投稿 useful? Useful Useless 1 of 1 people say this 投稿 is useful.

コンソール AP で DC の使用

ChatGPT で「Windows コンソールアプリで DC を使用する方法」と入力してみた.
ChatGPT  Windows コンソールアプリで DC を使用する方法
すると,使えそうなコードが表示された.

#include	<Windows.h>
#include	"i_define.hxx"
#include	"con_wnd.hxx"

int main() {
    // 1. コンソールウィンドウのハンドルを取得する
    HWND consoleWindow = ::GetConsoleWindow();
    // 2. コンソールウィンドウのDCを取得する
    HDC consoleDC = GetDC(consoleWindow);
    // 3. DCを使用して描画する
    TextOut(consoleDC, 10, 100, _T("Hello, World!"), _tcslen(_T("Hello, World!")));
    // 4. DCを解放する
    ReleaseDC(consoleWindow, consoleDC);
    return 0;
}

そのままではエラーになる部分があったので,少し修正している.
コンソール AP のウィンドウに TextOut (dc,...)
コンソール AP のウィンドウで GDI が使えるのは知らなかった.


BMP を表示するコード.

{
//	...
	tstring	dib_name = file_name ;
	if (::File_IsNothing(dib_name))	{	return	false ;		}
	i_DIB	dib = ::DIB_Load(dib_name.c_str()) ;
	{
		HWND	hWnd = ::GetConsoleWindow() ;
		{
			RECT	rect =	{	0	} ;
			::GetWindowRect(hWnd,&rect) ;
			HDC	hDC  = ::GetDC(hWnd) ;
			::DrawDIB(hDC,rect,dib) ;
			::ReleaseDC(hWnd,hDC) ;
			}
		}
//	...
	}

コンソール AP で BMP の表示
::AlphaBlend
更に自前のコードを利用して,サムネイルの表示.

	{
		HWND	hWnd = ::GetConsoleHwnd  () ;
		D_I_E	die ;
		die.SetDocPath(dib_name.c_str()) ;
		die.Draw(hWnd) ;
		}
Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.