ホーム » Windows (ページ 4)

Windows」カテゴリーアーカイブ

2025年1月
 1234
567891011
12131415161718
19202122232425
262728293031  

カテゴリー

アーカイブ

ブログ統計情報

  • 103,583 アクセス


高 DPI VC 「高い DPI 認識」

引き続きいろいろとやっているが…
異なる DPI での対応は簡単ではなさそう.
VC のプロパティで「モニターごと高い DPI 認識」としてビルドしていたが,モニタ間を移動するとうまくない.
ちゃんと対応すれば良いのだろうが,簡単ではない.
そのため exe のプロパティで「システム(拡張)」で良いと思っていたが,幾つかのバグ?(未対応)がある.


VC のプロパティで「高い DPI 認識」にしてビルドしたものの方がうまく機能している様な気がする.
「DPI 認識」設定 VC 10 と VC 14 でビルドしたもの
左から VC 10 ,14 ,10 ,14 としたもの.
タスクマネージャで見ると,VC 10 exe は「システム」として表示される.


左側のモニタの範囲は次の様になる.

-1920  130  0  1210	100%   1920  1080	「非対応」や「モニタごと」の場合
-3840  260  0  2420	200%   3840  2160	「システム」とした exe の場合

右側は ( 0 , 0 ) – ( 3840 , 2160 ) .


GDI スケーリングの動作として,次の記述があった.

アプリケーションが 100% (96 DPI) の倍数ではないディスプレイで実行されている場合、ベクター グラフィックスとテキストは、ディスプレイの倍率より 100% 高い最初の整数倍にレンダリングされます。たとえば、アプリケーションが 225% の縮尺のディスプレイ上にある場合、ベクター グラフィックスとビットマップは 300% でレンダリングされます。その後、DWM はレンダリングされたコンテンツを 225% のスケールに縮小します。この場合、スケールダウンによりアプリケーションのあいまいさが目立ちますが、100%レンダリングされたコンテンツを単純にスケールアップするよりも見栄えが良くなります。

Improving the high-DPI experience in GDI based Desktop Apps



How to build high DPI aware native Windows desktop applications


2023/05/12
「システム(拡張)」で幾つか変な動作があったが,「高い DPI 認識」でビルドしたものであれば良さそう.
「高い DPI 認識」

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

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.

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.

高 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.

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.

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.

error C2678: 二項演算子 ‘=’ :

ある処理を関数にしてコンパイルすると,

--------------------構成: VFDiv_1 - Win32 Debug--------------------
コンパイル中...
VFDiv_1V.cpp
c:\program files (x86)\microsoft visual studio\vc98\include\algorithm(232) : error C2678: 二項演算子 '=' : 型 'const struct Vector2<double>' の左オペランドを扱う演算子は定義されていません。(または変換できません)(新しい動作; ヘルプを参照)
        c:\program files (x86)\microsoft visual studio\vc98\include\algorithm(228) : コンパイルされたクラスのテンプレートのインスタンス化 'void __cdecl std::_Iter_swap(const struct Vector2<double> *,const struct Vector2<double> *,struct Vector2<double> *)' の参照を確認してください
c:\program files (x86)\microsoft visual studio\vc98\include\algorithm(232) : error C2678: 二項演算子 '=' : 型 'const struct Vector2<double>' の左オペランドを扱う演算子は定義されていません。(または変換できません)(新しい動作; ヘルプを参照)
        c:\program files (x86)\microsoft visual studio\vc98\include\algorithm(228) : コンパイルされたクラスのテンプレートのインスタンス化 'void __cdecl std::_Iter_swap(const struct Vector2<double> *,const struct Vector2<double> *,struct Vector2<double> *)' の参照を確認してください
cl.exe の実行エラー

VFDiv_1.exe - エラー 2、警告 0

対象が ‘const struct Vector2<double>’ となっているので,わかり辛かった.
次の関数の,’const Vd2A& v2a’ を std::reverse(v2a.begin(),v2a.end()) として使用しているのが原因.

PLF_face_l	PLF_triangulation	(const Vd2A& v2a)
{
//	...
	std::reverse(v2a.begin(),v2a.end()) ;
//	...
	}

一度関数内の変数に置く必要がある.
error C2678

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

Win7 Windows Update で 80072EFE

先日仮想マシンにセットアップした Win7 環境の Windows Update でエラー 80072EFE .
.NET Framework を使用しているものを実行すると,.NET … を要求され,そのセットアップに失敗してしまう.
Win7 Windows Update 80072EFE」で検索すると…
Windows7 をインストール後、Windows Update が 80072EFE エラーでできない場合の対処方法
Windows Updateのエラー 8007000Eの対処はKB3138612で解決(Windows7)
2021年版:Windows 7 を久しぶりに更新する時の注意
KB3138612 を手動でインストールする必要があるみたい.
https://www.catalog.update.microsoft.com/Search.aspx?q=KB3138612
環境は x64 なので「Windows 7 for x64-Based Systems … (KB3138612)」をダウンロードしてインストール.
仮想マシンの再起動後 Windows Update に入ると,167 個の更新があった.
Win7  KB3138612 インストール後の更新プログラム

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

VirtualBox 7.0 に更新

Win10 と Win11 の ViurtalBox を 6.1.40 から 7.0.4 に更新した.
4K 環境になってから,いろいろと表示でうまくない部分があった.


Win10 環境は,Win11 の仮想マシンを除いては問題なさそう.
Win10  VirtualBox 7.0.4
一度 Win11 仮想マシンを「除去」して,作成し直してみたが,うまく動作しない.
起動中の「お待ちください」の表示で止まってしまう.
仮想マシンの「設定」-「ディスプレイ」の「3Dアクセラレーションを有効化」にチェックして止まらなくなった.
「設定」-「ディスプレイ」-「スクリーン」-「3Dアクセラレーションを有効化」
どこかに設定があるものと思うが,OpenGL での表示がうまくされない.
VirtualBox 7  OpenGL 表示ができない?


Win11 環境は,LAN 関係のエラーでうまく起動しない.
6.1.40 に戻したがうまく起動しなくなってしまった.
Win11 VirtualBox 7  VERR_SUPDRV_COMPONENT_NOT_FOUND
「ネットワークアダプターを有効化」のチェックを外せば起動するが,これだとあまり使い物にならない.
ASUSTOR NAS でも同様のエラー になったことがあるので,設定ではないのか?


もう一度 7.0.4 にしてみたが,変わらず.
VM Name: Win10-VS6
Failed to open/create the internal network ‘HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter’ (VERR_SUPDRV_COMPONENT_NOT_FOUND).
Failed to attach the network LUN (VERR_SUPDRV_COMPONENT_NOT_FOUND).
Result Code:
E_FAIL (0X80004005)
Component:
ConsoleWrap
Interface:
IConsole {6ac83d89-6ee7-4e33-8ae6-b257b2e81be8}
Failed to open/create the internal network 'HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter' (VERR_SUPDRV_COMPONENT_NOT_FOUND).


アンインストールやデバイスの削除を行って,再度インストールしたら少し動作する様になった.
デバイスマネージャーで VirtualBox Host-Only Ethernet Adapter がうまくなかったので,そこが原因だったか?
まだうまく動作しない仮想マシンもあるが,あとは個別の設定なので何とかなるかな?


2023/06/05
Win11 環境で 7.0.8 にあげて,LAN 関係で同じ様な現象.
PC を再起動して,うまく動作する様になった.

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