我正在使用第三方库将图像渲染到 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);
这显然对操作系统产生了可怕的影响,每次我渲染图像时,其他应用程序都会从启用清除类型闪烁到禁用并返回。
所以问题是,有谁知道我可以如何更改特定 DC 的字体渲染设置?
即使我可以只使更改过程或线程特定而不影响整个操作系统,那将是向前迈出的一大步!(这将使我可以选择将此渲染输出到一个单独的进程 - 无论如何渲染后结果都会写入磁盘)
编辑:我想补充一点,我不介意解决方案是否比几个 API 调用更复杂。如果只需要一天的工作,我什至会对涉及挂钩系统 dll 的解决方案感到满意。
编辑:背景信息 第三方库使用大约 70 种颜色的调色板进行渲染。将图像(实际上是地图图块)渲染到 DC 后,我将每个像素从它的 32 位颜色转换回它的调色板索引,并将结果存储为 8bpp 灰度图像。这作为纹理上传到视频卡。在渲染期间,我使用在视频卡上执行的像素着色器重新应用调色板(也存储为纹理)。这让我可以立即在不同的调色板之间切换和淡入淡出,而无需重新生成所有需要的图块。为典型的世界视图生成和上传所有图块需要 10-60 秒。
编辑:将 GraphicsDevice 重命名为 Graphics 这个问题的先前版本中的 GraphicsDevice 类实际上是 System.Drawing.Graphics。我重命名了它(使用 GraphicsDevice = ...),因为有问题的代码位于命名空间 MyCompany.Graphics 中,编译器无法正确解析它。
编辑:成功!
我什至设法PatchIat
在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];
}