IwaoDev

この画面は、簡易表示です

Windows

VC 2022 17.6.?

投稿日 by Iwao

先日 VS 2022 の更新版 があったのでアップデート.
個人的なツールの 3D ビューア をビルドすると,

ALYac           	Gen:Variant.Tedy.373496
Arcabit         	Trojan.Tedy.D5B2F8
BitDefender     	Gen:Variant.Tedy.373496
Cylance         	Unsafe
DeepInstinct    	MALICIOUS
Emsisoft        	Gen:Variant.Tedy.373496 (B)
eScan           	Gen:Variant.Tedy.373496
GData           	Gen:Variant.Tedy.373496
MAX              	Malware (ai Score=81)
McAfee          	Artemis!AE3A9CE560AE
McAfee-GW-Edition	Artemis
Trellix (FireEye)	Gen:Variant.Tedy.373496
TrendMicro-HouseCall	TROJ_GEN.R002H09EQ23
VIPRE           	Gen:Variant.Tedy.373496

VirusTotal  VC 2022 17.6
VC 2019 などでビルドしたものは問題ない.また VC 2022 更新前のものも問題なかった.


今まで VS の更新版が出てすぐにそれでビルドすることは少なかった.
テストが不十分なこともあり,リリース用は主に VC 2017 を使用している.
更新版によっては,この様なことがあるのかもしれない.
落ち着くまでしばらく時間がかかるのか?

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


error during ReadSymbolTable

投稿日 by Iwao

5 年位前に作成したプロジェクトをビルドしていると,

--------------------Configuration: phpup_mb - Win32 Debug--------------------
Compiling...
ComPrj01.cpp
phpup_mb.cpp
...
Linking...
c:\Temp\HTM\phpup\phpup_mb\Debug.060\ComPrj01.obj : error : Internal error during ReadSymbolTable
  ExceptionCode            = C0000005
  ExceptionFlags           = 00000000
  ExceptionAddress         = 004623F2
  NumberParameters         = 00000002
  ExceptionInformation[ 0] = 00000000
  ExceptionInformation[ 1] = 0090B470
CONTEXT:
  Eax    = 3FFF1E64  Esp    = 0019F050
  Ebx    = FFFF8000  Ebp    = 01B834C7
  Ecx    = 3FFF1E64  Esi    = 401F1EC0
  Edx    = 0094B478  Edi    = 401F1EC0
  Eip    = 004623F2  EFlags = 00010246
  SegCs  = 00000023  SegDs  = 0000002B
  SegSs  = 0000002B  SegEs  = 0000002B
  SegFs  = 00000053  SegGs  = 0000002B
  Dr0    = 0019F050  Dr3    = FFFF8000
  Dr1    = 01B834C7  Dr6    = 3FFF1E64
  Dr2    = 00000000  Dr7    = 00000000
Error executing link.exe.
Tool execution canceled by user.

Internal error during ReadSymbolTable
exe は存在するが正しくできていないので,exe のみ削除して再度ビルド.

--------------------Configuration: phpup_mb - Win32 Debug--------------------
Linking...
LINK : LNK6004: c:\Temp\HTM\phpup\phpup_mb\Debug.060/phpup_mb.exe not found or not built by the last incremental link; performing full link
ComPrj01.obj : fatal error LNK1143: invalid or corrupt file: no symbol for comdat section 0xffff8000
Error executing link.exe.

phpup_mb.exe - 1 error(s), 0 warning(s)

LNK6004  LNK1143
obj のサイズを見ると,20 MB 位になっている.
それで思い出したのが,前にも同じ様な現象が…
LNK1143 : ファイルが無効であるか…
ソースを分割して対応.

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


CSpinButtonCtrl Set buddy integer

投稿日 by Iwao

引継いだプロジェクトを変更していて,CSpinButtonCtrl が意図した動作にならない.
元は「整数」だったものを,0.25 のステップに変更.


デバッガで追いかけると,0.25 で増減はできている.
が,対応する「エディット ボックス」の表示は「整数」で 1 のステップで増減してしまう.


影響しているコードを見たが,特に問題になりそうな部分は見当たらない.
こうなると,どこかの設定と思い見直すと,あった.
「Spin プロパティ」-「数値の自動表示」
「Set buddy integer」にチェックが入っていた.

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


CComboBox::SetItemHeight (-1,…)

投稿日 by Iwao

高 DPI 対応で修正していて,コンボボックス(特に,オーナードロー?)の高さが他に比べて不自然.
コンボボックスの高さの変更


ドキュメントを見ると,CB_SETITEMHEIGHT と思うが,指定方法がよくわからない.
コンボボックス 高さ 変更」で 検索する と「できない」と記述されている所もある.


CB_SETITEMHEIGHT で検索していると,-1 を指定しているものがある.
さらに,CB_GETITEMHEIGHT のドキュメントを見ると,次の様にある.

選択フィールドの高さを取得するには、このパラメーターは -1 である必要があります

どうも CB_SETITEMHEIGHT の表現がうまくないものと思う.


次の様なコードで対応.

{
	CRect	rect ;
	m_CtrlStrText.GetClientRect(&rect) ;
	FontFace.SendMessage(CB_SETITEMHEIGHT,-1,rect.Height()) ;
	FontFace.SendMessage(CB_SETITEMHEIGHT, 0,rect.Height()*15/10) ;
	}

CComboBox::SetItemHeight (-1,…)


2023/05/16
ドロップダウンの幅がうまくなかったが,共通コードを見るといろいろ考慮しなければならない部分が多い.
そのため,次の様なコードで対応.

{
	new_w = now_w ;
//	new_w = now_w * 15 / 10 ;
	new_w = int(new_w * ::GetDPI_scale(this->GetSafeHwnd())) ;
	FontFace.SendMessage(CB_SETDROPPEDWIDTH,new_w) ;
	}

SendMessage(CB_SETDROPPEDWIDTH,...)


間違っているかもしれないが,CB_SETITEMHEIGHT を簡単にテストしたことのまとめ.

通常のコンボボックスの場合 -1 で,全ての項目の高さを設定できる
オーナードロー 固定 -1 で,エディットボックス部分の高さを指定
0 で,ドロップ部分の項目の高さを指定
オーナードロー 可変 -1 で,エディットボックス部分の高さを指定
0 以上で,ドロップ部分のそれぞれの高さを指定
この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


VirtualBox PXE-E06

投稿日 by Iwao

Win11 上の仮想マシン Win10 の Windows Update での再起動で,
PXE-E06: Option ROM requires DDIM support.
PXE-E06: Option ROM requires DDIM support.
検索してみた が,あまりいい情報に引っ掛からない.


Windows Update での再起動だったので心配だったが,仮想マシンを一度 OFF して,再起動.
特に何もなかったかの様に起動した.
何だったのだろう.

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


高 DPI VC 「高い DPI 認識」

投稿日 by Iwao

引き続きいろいろとやっているが…
異なる 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 認識」

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


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

投稿日 by Iwao

先日からやっている .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 の動作テスト

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


RegOpenKeyEx REGSAM

投稿日 by Iwao

レジストリアクセスのコードを書き直していて,::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 の方が速いなどはあるのか?それともファイルアクセスなどと同じ?

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


GetMonitorInfo

投稿日 by Iwao

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

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


MFC タイトルバーの変更

投稿日 by Iwao

ダイアログベースであれば 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 のタイトルバーにバージョンなどの付加

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


高 DPI CToolBar のリサイズ

投稿日 by Iwao

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

	if (newExtendSize == CSize(0,0)) {
		double	dpi_s = ::GetDPI_scale(toolBar->GetSafeHwnd()) ;
		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.")) ;
		}
この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


高 DPI テスト exe

投稿日 by Iwao

先日の 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 などを呼ぶことで対応可能.

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


tcsncpy_s Buffer is too small

投稿日 by Iwao

先日更新した ツール をテストしていると,フォントによりアプリケーションエラー?となってしまう.
デバッガで追いかけると,
—————————
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 としている.

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


GetGlyphOutline ::PolyPolygon

投稿日 by Iwao

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

{
	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) ;

三角形に分割

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


ShowWindow SW_HIDE SW_SHOW

投稿日 by Iwao

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


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

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) ;
	}

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


GetGlyphOutline ポリゴン化

投稿日 by Iwao

まだまだだが…

文字ごとのポリラインを取得できる様になったので,穴の処理と組み合わせてポリゴン化.
文字によっては,穴の処理でクロスしてしまうことがある.
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

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


GetGlyphOutline ポリラインの配列に

投稿日 by Iwao

「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 ;
	}

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


Q243285 の動作の確認

投稿日 by Iwao

先日からやっている 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 の動作の確認

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


GetGlyphOutline TTPOLYCURVE

投稿日 by Iwao

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 ;
	}
この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。


GetGlyphOutline の続き

投稿日 by Iwao

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  月

この投稿は役に立ちましたか? 役に立った 役に立たなかった 0 人中 0 人がこの 投稿 は役に立ったと言っています。



top