我正在使用第三方库将图像渲染到 GDI DC,并且我需要确保渲染任何文本时都不会进行任何平滑/抗锯齿,以便我可以将图像转换为具有索引颜色的预定义调色板。
我用于渲染的第三方库不支持此功能,并且仅根据当前 Windows 设置的字体渲染来渲染文本。他们还表示,他们不太可能很快添加关闭抗锯齿功能。
到目前为止,我发现的最好的解决方法是以这种方式调用第三方库(为简洁起见,省略了错误处理和先前的设置检查):
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 的字体渲染设置?
即使我可以只对特定于进程或线程的更改进行更改,而不影响整个操作系统,那也将是向前迈出的一大步! (这将使我可以选择将此渲染分配给单独的进程 - 无论如何,结果都会在渲染后写入磁盘)
EDIT:我想补充一点,我不介意解决方案是否比几个 API 调用更复杂。如果只需要大约一天的工作,我什至会对涉及挂钩系统 dll 的解决方案感到满意。
编辑:背景信息第三方库使用大约 70 种颜色的调色板进行渲染。将图像(实际上是地图图块)渲染到 DC 后,我将每个像素从 32 位颜色转换回其调色板索引,并将结果存储为 8bpp 灰度图像。这将作为纹理上传到视频卡。在渲染过程中,我通过在显卡上执行的像素着色器重新应用调色板(也存储为纹理)。这使我可以立即在不同调色板之间切换和淡入淡出,而无需重新生成所有所需的图块。生成并上传典型世界视图的所有图块需要 10-60 秒。
编辑:将 GraphicsDevice 重命名为 Graphics这个问题的前一个版本中的类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];
}