我正在使用第三方库将图像渲染到GDI DC,我需要确保在没有任何平滑/抗锯齿的情况下渲染任何文本,以便我可以将图像转换为带有索引颜色的预定义调色板.
我用于渲染的第三方库不支持这个,只是根据字体渲染的当前窗口设置呈现文本.他们还表示他们不太可能在短时间内添加切换抗锯齿功能.
到目前为止,我发现的最好的工作是以这种方式调用第三方库(为简洁起见,省略了错误处理和先前的设置检查):
private static void SetFontSmoothing(bool enabled) { int pv = 0; SystemParametersInfo(Spi.SetFontSmoothing, enabled ? 1 : 0, ref pv, Spif.None); } // snip Graphics graphics = Graphics.FromImage(bitmap) IntPtr deviceContext = graphics.GetHdc(); SetFontSmoothing(false); thirdPartyComponent.Render(deviceContext); SetFontSmoothing(true);
这显然对操作系统产生了可怕的影响,每次我渲染图像时,其他应用程序都会从禁用的cleartype闪烁到禁用和返回.
所以问题是,有没有人知道如何改变特定DC的字体渲染设置?
即使我可以只进行更改过程或线程而不是影响整个操作系统,这将是向前迈出的一大步!(这样我就可以选择将这个渲染转换为一个单独的进程 - 无论如何都会在渲染后将结果写入磁盘)
编辑:我想补充一点,我不介意解决方案是否比一些API调用更复杂.我甚至会对一个解决方案感到满意,如果它只用了一天工作就会挂钩系统dll.
编辑:背景信息 第三方库使用约70种颜色的调色板渲染.在将图像(实际上是地图图块)渲染到DC之后,我将每个像素从其32位颜色转换回其调色板索引,并将结果存储为8bpp灰度图像.这将作为纹理上传到视频卡.在渲染过程中,我重新应用了调色板(也存储为纹理),并在视频卡上执行了像素着色器.这允许我立即在不同的调色板之间切换和淡入淡出,而不需要重新生成所有需要的切片.生成并上传所有瓷砖需要10-60秒才能获得典型的世界观.
编辑:将GraphicsDevice重命名为图形 此问题的先前版本中的GraphicsDevice类实际上是System.Drawing.Graphics.我重命名了它(使用GraphicsDevice = ...),因为有问题的代码在命名空间MyCompany.Graphics中,编译器无法正确解析它.
编辑:成功!
我甚至设法将PatchIat
下面的函数移植到C#的帮助下Marshal.GetFunctionPointerForDelegate
..NET互操作团队确实做得非常出色!我现在使用以下语法,其中Patch
是一个扩展方法System.Diagnostics.ProcessModule
:
module.Patch( "Gdi32.dll", "CreateFontIndirectA", (CreateFontIndirectA original) => font => { font->lfQuality = NONANTIALIASED_QUALITY; return original(font); }); private unsafe delegate IntPtr CreateFontIndirectA(LOGFONTA* lplf); private const int NONANTIALIASED_QUALITY = 3; [StructLayout(LayoutKind.Sequential)] private struct LOGFONTA { public int lfHeight; public int lfWidth; public int lfEscapement; public int lfOrientation; public int lfWeight; public byte lfItalic; public byte lfUnderline; public byte lfStrikeOut; public byte lfCharSet; public byte lfOutPrecision; public byte lfClipPrecision; public byte lfQuality; public byte lfPitchAndFamily; public unsafe fixed sbyte lfFaceName [32]; }
Chris Becke.. 5
不幸的是你不能.每种字体都可以控制字体抗锯齿.GDI调用CreateFontIndirect处理LOGFONT结构的成员,以确定是否允许使用cleartype,常规或无抗锯齿.
如您所述,有系统范围的设置.不幸的是,如果无法控制LOGFONT的内容,更改系统范围设置几乎是降低DC上字体渲染质量的唯一(记录)方式.
这段代码不是我的.是非托管的C.如果你知道它的HMODULE,它将挂钩由dll或exe文件导入的任何函数.
#define PtrFromRva( base, rva ) ( ( ( PBYTE ) base ) + rva ) /*++ Routine Description: Replace the function pointer in a module's IAT. Parameters: Module - Module to use IAT from. ImportedModuleName - Name of imported DLL from which function is imported. ImportedProcName - Name of imported function. AlternateProc - Function to be written to IAT. OldProc - Original function. Return Value: S_OK on success. (any HRESULT) on failure. --*/ HRESULT PatchIat( __in HMODULE Module, __in PSTR ImportedModuleName, __in PSTR ImportedProcName, __in PVOID AlternateProc, __out_opt PVOID *OldProc ) { PIMAGE_DOS_HEADER DosHeader = ( PIMAGE_DOS_HEADER ) Module; PIMAGE_NT_HEADERS NtHeader; PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor; UINT Index; assert( Module ); assert( ImportedModuleName ); assert( ImportedProcName ); assert( AlternateProc ); NtHeader = ( PIMAGE_NT_HEADERS ) PtrFromRva( DosHeader, DosHeader->e_lfanew ); if( IMAGE_NT_SIGNATURE != NtHeader->Signature ) { return HRESULT_FROM_WIN32( ERROR_BAD_EXE_FORMAT ); } ImportDescriptor = ( PIMAGE_IMPORT_DESCRIPTOR ) PtrFromRva( DosHeader, NtHeader->OptionalHeader.DataDirectory [ IMAGE_DIRECTORY_ENTRY_IMPORT ].VirtualAddress ); // // Iterate over import descriptors/DLLs. // for ( Index = 0; ImportDescriptor[ Index ].Characteristics != 0; Index++ ) { PSTR dllName = ( PSTR ) PtrFromRva( DosHeader, ImportDescriptor[ Index ].Name ); if ( 0 == _strcmpi( dllName, ImportedModuleName ) ) { // // This the DLL we are after. // PIMAGE_THUNK_DATA Thunk; PIMAGE_THUNK_DATA OrigThunk; if ( ! ImportDescriptor[ Index ].FirstThunk || ! ImportDescriptor[ Index ].OriginalFirstThunk ) { return E_INVALIDARG; } Thunk = ( PIMAGE_THUNK_DATA ) PtrFromRva( DosHeader, ImportDescriptor[ Index ].FirstThunk ); OrigThunk = ( PIMAGE_THUNK_DATA ) PtrFromRva( DosHeader, ImportDescriptor[ Index ].OriginalFirstThunk ); for ( ; OrigThunk->u1.Function != NULL; OrigThunk++, Thunk++ ) { if ( OrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG ) { // // Ordinal import - we can handle named imports // ony, so skip it. // continue; } PIMAGE_IMPORT_BY_NAME import = ( PIMAGE_IMPORT_BY_NAME ) PtrFromRva( DosHeader, OrigThunk->u1.AddressOfData ); if ( 0 == strcmp( ImportedProcName, ( char* ) import->Name ) ) { // // Proc found, patch it. // DWORD junk; MEMORY_BASIC_INFORMATION thunkMemInfo; // // Make page writable. // VirtualQuery( Thunk, &thunkMemInfo, sizeof( MEMORY_BASIC_INFORMATION ) ); if ( ! VirtualProtect( thunkMemInfo.BaseAddress, thunkMemInfo.RegionSize, PAGE_EXECUTE_READWRITE, &thunkMemInfo.Protect ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } // // Replace function pointers (non-atomically). // if ( OldProc ) { *OldProc = ( PVOID ) ( DWORD_PTR ) Thunk->u1.Function; } #ifdef _WIN64 Thunk->u1.Function = ( ULONGLONG ) ( DWORD_PTR ) AlternateProc; #else Thunk->u1.Function = ( DWORD ) ( DWORD_PTR ) AlternateProc; #endif // // Restore page protection. // if ( ! VirtualProtect( thunkMemInfo.BaseAddress, thunkMemInfo.RegionSize, thunkMemInfo.Protect, &junk ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } return S_OK; } } // // Import not found. // return HRESULT_FROM_WIN32( ERROR_PROC_NOT_FOUND ); } } // // DLL not found. // return HRESULT_FROM_WIN32( ERROR_MOD_NOT_FOUND ); }
你可以通过做类似的事情从你的代码中调用它(我没有检查过这会以任何方式编译:P):
声明要挂钩的函数的指针类型:
typedef FARPROC (WINAPI* PFNCreateFontIndirect)(LOGFONT*);
实现一个钩子函数
static PFNCreateFontIndirect OldCreateFontIndirect = NULL; WINAPI MyNewCreateFontIndirectCall(LOGFONT* plf) { // do stuff to plf (probably better to create a copy than tamper with passed in struct) // chain to old proc if(OldCreateFontIndirect) return OldCreateFontIndirect(plf); }
在初始化期间的某个时间挂钩功能
HMODULE h = LoadLibrary(TEXT("OtherDll")); PatchIat(h, "USER32.DLL", "CreateFontIndirectW", MyNewCreateFontIndirectProc, (void**)&OldCreateFontIndirectProc);
当然,如果你正在挂起的模块存在于.NET中,那么它的CreateFontIndirect
起源地非常不清楚.mscoree.dll
?你打电话的实际模块?祝你好运:P
不幸的是你不能.每种字体都可以控制字体抗锯齿.GDI调用CreateFontIndirect处理LOGFONT结构的成员,以确定是否允许使用cleartype,常规或无抗锯齿.
如您所述,有系统范围的设置.不幸的是,如果无法控制LOGFONT的内容,更改系统范围设置几乎是降低DC上字体渲染质量的唯一(记录)方式.
这段代码不是我的.是非托管的C.如果你知道它的HMODULE,它将挂钩由dll或exe文件导入的任何函数.
#define PtrFromRva( base, rva ) ( ( ( PBYTE ) base ) + rva ) /*++ Routine Description: Replace the function pointer in a module's IAT. Parameters: Module - Module to use IAT from. ImportedModuleName - Name of imported DLL from which function is imported. ImportedProcName - Name of imported function. AlternateProc - Function to be written to IAT. OldProc - Original function. Return Value: S_OK on success. (any HRESULT) on failure. --*/ HRESULT PatchIat( __in HMODULE Module, __in PSTR ImportedModuleName, __in PSTR ImportedProcName, __in PVOID AlternateProc, __out_opt PVOID *OldProc ) { PIMAGE_DOS_HEADER DosHeader = ( PIMAGE_DOS_HEADER ) Module; PIMAGE_NT_HEADERS NtHeader; PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor; UINT Index; assert( Module ); assert( ImportedModuleName ); assert( ImportedProcName ); assert( AlternateProc ); NtHeader = ( PIMAGE_NT_HEADERS ) PtrFromRva( DosHeader, DosHeader->e_lfanew ); if( IMAGE_NT_SIGNATURE != NtHeader->Signature ) { return HRESULT_FROM_WIN32( ERROR_BAD_EXE_FORMAT ); } ImportDescriptor = ( PIMAGE_IMPORT_DESCRIPTOR ) PtrFromRva( DosHeader, NtHeader->OptionalHeader.DataDirectory [ IMAGE_DIRECTORY_ENTRY_IMPORT ].VirtualAddress ); // // Iterate over import descriptors/DLLs. // for ( Index = 0; ImportDescriptor[ Index ].Characteristics != 0; Index++ ) { PSTR dllName = ( PSTR ) PtrFromRva( DosHeader, ImportDescriptor[ Index ].Name ); if ( 0 == _strcmpi( dllName, ImportedModuleName ) ) { // // This the DLL we are after. // PIMAGE_THUNK_DATA Thunk; PIMAGE_THUNK_DATA OrigThunk; if ( ! ImportDescriptor[ Index ].FirstThunk || ! ImportDescriptor[ Index ].OriginalFirstThunk ) { return E_INVALIDARG; } Thunk = ( PIMAGE_THUNK_DATA ) PtrFromRva( DosHeader, ImportDescriptor[ Index ].FirstThunk ); OrigThunk = ( PIMAGE_THUNK_DATA ) PtrFromRva( DosHeader, ImportDescriptor[ Index ].OriginalFirstThunk ); for ( ; OrigThunk->u1.Function != NULL; OrigThunk++, Thunk++ ) { if ( OrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG ) { // // Ordinal import - we can handle named imports // ony, so skip it. // continue; } PIMAGE_IMPORT_BY_NAME import = ( PIMAGE_IMPORT_BY_NAME ) PtrFromRva( DosHeader, OrigThunk->u1.AddressOfData ); if ( 0 == strcmp( ImportedProcName, ( char* ) import->Name ) ) { // // Proc found, patch it. // DWORD junk; MEMORY_BASIC_INFORMATION thunkMemInfo; // // Make page writable. // VirtualQuery( Thunk, &thunkMemInfo, sizeof( MEMORY_BASIC_INFORMATION ) ); if ( ! VirtualProtect( thunkMemInfo.BaseAddress, thunkMemInfo.RegionSize, PAGE_EXECUTE_READWRITE, &thunkMemInfo.Protect ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } // // Replace function pointers (non-atomically). // if ( OldProc ) { *OldProc = ( PVOID ) ( DWORD_PTR ) Thunk->u1.Function; } #ifdef _WIN64 Thunk->u1.Function = ( ULONGLONG ) ( DWORD_PTR ) AlternateProc; #else Thunk->u1.Function = ( DWORD ) ( DWORD_PTR ) AlternateProc; #endif // // Restore page protection. // if ( ! VirtualProtect( thunkMemInfo.BaseAddress, thunkMemInfo.RegionSize, thunkMemInfo.Protect, &junk ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } return S_OK; } } // // Import not found. // return HRESULT_FROM_WIN32( ERROR_PROC_NOT_FOUND ); } } // // DLL not found. // return HRESULT_FROM_WIN32( ERROR_MOD_NOT_FOUND ); }
你可以通过做类似的事情从你的代码中调用它(我没有检查过这会以任何方式编译:P):
声明要挂钩的函数的指针类型:
typedef FARPROC (WINAPI* PFNCreateFontIndirect)(LOGFONT*);
实现一个钩子函数
static PFNCreateFontIndirect OldCreateFontIndirect = NULL; WINAPI MyNewCreateFontIndirectCall(LOGFONT* plf) { // do stuff to plf (probably better to create a copy than tamper with passed in struct) // chain to old proc if(OldCreateFontIndirect) return OldCreateFontIndirect(plf); }
在初始化期间的某个时间挂钩功能
HMODULE h = LoadLibrary(TEXT("OtherDll")); PatchIat(h, "USER32.DLL", "CreateFontIndirectW", MyNewCreateFontIndirectProc, (void**)&OldCreateFontIndirectProc);
当然,如果你正在挂起的模块存在于.NET中,那么它的CreateFontIndirect
起源地非常不清楚.mscoree.dll
?你打电话的实际模块?祝你好运:P