禁用特定 GDI 设备上下文的抗锯齿功能

2023-11-26

我正在使用第三方库将图像渲染到 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];
}

不幸的是你不能。控制字体抗锯齿的能力是按字体完成的。 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):

  1. 声明一个指向要挂钩的函数的指针类型:

    typedef FARPROC (WINAPI* PFNCreateFontIndirect)(LOGFONT*);
    
  2. 实现一个钩子函数

    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);
    }
    
  3. 在初始化期间的某个时候挂钩该函数

    HMODULE h = LoadLibrary(TEXT("OtherDll"));
    PatchIat(h, "USER32.DLL", "CreateFontIndirectW", MyNewCreateFontIndirectProc, (void**)&OldCreateFontIndirectProc);
    

当然,如果您要挂钩的模块存在于 .NET 领域,则很难确定该模块在哪里CreateFontIndirect呼叫将源自。mscoree.dll?您调用的实际模块?我想祝你好运:P

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

禁用特定 GDI 设备上下文的抗锯齿功能 的相关文章

  • 检查两个数是否是彼此的排列?

    给定两个数字 a b 使得 1 例如 123 是 312 的有效排列 我也不想对数字中的数字进行排序 如果您指的是数字的字符 例如 1927 和 9721 则 至少 有几种方法 如果允许排序 一种方法是简单地sprintf将它们放入两个缓冲
  • 如何使 Windows 窗体的关闭按钮不关闭窗体但使其不可见?

    该表单有一个 NotifyIcon 对象 当用户单击 关闭 按钮时 我希望表单不关闭而是变得不可见 然后 如果用户想再次查看该表单 可以双击系统托盘中的图标 如果用户想关闭表单 可以右键单击该图标并选择 关闭 有人可以告诉我如何使关闭按钮不
  • 访问私人成员[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 通过将类的私有成员转换为 void 指针 然后转换为结构来访问类的私有成员是否合适 我认为我无权修改包含我需要访问的数据成员的类 如果不道德 我
  • 是否可以强制 XMLWriter 将元素写入单引号中?

    这是我的代码 var ptFirstName tboxFirstName Text writer WriteAttributeString first ptFirstName 请注意 即使我使用 ptFirstName 也会以双引号结束 p
  • 未解决的包含:“cocos2d.h” - Cocos2dx

    当我在 Eclipse 中导入 cocos2dx android 项目时 我的头文件上收到此警告 Unresolved inclusion cocos2d h 为什么是这样 它实际上困扰着我 该项目可以正确编译并运行 但我希望这种情况消失
  • WPF 中的调度程序和异步等待

    我正在尝试学习 WPF C 中的异步编程 但我陷入了异步编程和使用调度程序的困境 它们是不同的还是在相同的场景中使用 我愿意简短地回答这个问题 以免含糊不清 因为我知道我混淆了 WPF 中的概念和函数 但还不足以在功能上正确使用它 我在这里
  • C - 找到极限之间的所有友好数字

    首先是定义 一对友好的数字由两个不同的整数组成 其中 第一个整数的除数之和等于第二个整数 并且 第二个整数的除数之和等于第一个整数 完美数是等于其自身约数之和的数 我想做的是制作一个程序 询问用户一个下限和一个上限 然后向他 她提供这两个限
  • Qt moc 在头文件中实现?

    是否可以告诉 Qt MOC 我想声明该类并在单个文件中实现它 而不是将它们拆分为 h 和 cpp 文件 如果要在 cpp 文件中声明并实现 QObject 子类 则必须手动包含 moc 文件 例如 文件main cpp struct Sub
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • 从路径中获取文件夹名称

    我有一些路c server folderName1 another name something another folder 我如何从那里提取最后一个文件夹名称 我尝试了几件事 但没有成功 我只是不想寻找最后的 然后就去休息了 Thank
  • 当操作繁忙时,表单不执行任何操作(冻结)

    我有一个使用 C 的 WinForms 应用程序 我尝试从文件中读取一些数据并将其插入数据表中 当此操作很忙时 我的表单冻结并且无法移动它 有谁知道我该如何解决这个问题 这可能是因为您在 UI 线程上执行了操作 将文件和数据库操作移至另一个
  • 实体框架 4 DB 优先依赖注入?

    我更喜欢创建自己的数据库 设置索引 唯一约束等 使用 edmx 实体框架设计器 从数据库生成域模型是轻而易举的事 现在我有兴趣使用依赖注入来设置一些存储库 我查看了 StackOverflow 上的一些文章和帖子 似乎重点关注代码优先方法
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • 需要哪个版本的 Visual C++ 运行时库?

    microsoft 的最新 vcredist 2010 版 是否包含以前的版本 2008 SP1 和 2005 SP1 还是我需要安装全部 3 个版本 谢谢 你需要所有这些
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • x86 上未对齐的指针

    有人可以提供一个示例 将指针从一种类型转换为另一种类型由于未对齐而失败吗 在评论中这个答案 https stackoverflow com questions 544928 reading integer size bytes from a
  • mysql-connector-c++ - “get_driver_instance”不是“sql::mysql”的成员

    我是 C 的初学者 我认为学习的唯一方法就是接触一些代码 我正在尝试构建一个连接到 mysql 数据库的程序 我在 Linux 上使用 g 没有想法 我运行 make 这是我的错误 hello cpp 38 error get driver
  • C 中的异或运算符

    在进行按位操作时 我在确定何时使用 XOR 运算符时遇到一些困难 按位与和或非常简单 当您想要屏蔽位时 请使用按位 AND 常见用例是 IP 寻址和子网掩码 当您想要打开位时 请使用包含或 然而 XOR 总是让我明白 我觉得如果在面试中被问
  • 限制C#中的并行线程数

    我正在编写一个 C 程序来生成并通过 FTP 上传 50 万个文件 我想并行处理4个文件 因为机器有4个核心 文件生成需要更长的时间 是否可以将以下 Powershell 示例转换为 C 或者是否有更好的框架 例如 C 中的 Actor 框

随机推荐

  • 有没有办法监视对象的变化?

    您是否可以只监听对象的更改并做出反应 而不是根据用户操作触发事件 那么 根据您正在使用的域 您可以使用主干 js 它有一个 模型 对象 您可以扩展该对象并将其连接到模型更改时自动由事件触发的回调 也就是说 您在模型上设置一个字段 它将自动触
  • 为什么Python在递归方面比node.js慢很多

    我编写了一个简单的斐波那契测试程序来比较node js 和python 的性能 事实证明 python 花了 5s 才完成计算 而 Node js 则花了 200ms 结束 为什么 python 在这种情况下表现这么差 python imp
  • 如何在 SQL Server 列中的两个字符串之间选择一个字符串?

    我想获取表列中两个字符串之间的字符串 如何选择列 我已经写了两边的字符串 谁能帮助我如何在此处包含带有字符串的列名称 DECLARE Text NVARCHAR MAX SELECT SUBSTRING Text CHARINDEX Man
  • 现在在JavaFX中如何从TableView获取TableHeaderRow?

    我在很多地方看到了如何使用代码获取表头的示例 TableHeaderRow header TableHeaderRow tableView lookup TableHeaderRow 像这儿 如何防止 TableView 在 javaFX
  • Symfony 4 - 两个目录中的控制器

    在我的应用程序中 我使用 Symfony 4 我希望 Symfony 在两个目录中搜索控制器 A 和 B 我发现了类似这样的内容 controllers resource src DirectoryA Controller type ann
  • 抑制 PyDev 中的警告

    我在 Python 项目中的所有模块的开头使用以下内容 import setup loggers setup loggers是一个完全可以做到这一点的模块 这import语句确保无论首先加载哪个模块 记录器都已设置并准备就绪 但是 由于我不
  • const、let 和 var 对 v8 JavaScript 性能的影响?

    无论功能差异如何 使用新关键字 let 和 const 相对于 var 是否会对性能产生任何普遍或特定的影响 运行程序后 function timeit f N S var start timeTaken var stats min 1e5
  • 在azure中设置webapp%PATH%环境变量

    我正在开发一个 azure webapp 项目 为了使我的应用程序正常工作 我需要在服务器上安装第三方开源软件 我发现在 azure webapp 上执行此操作的唯一方法是手动复制项目上软件的所有文件夹 然后添加所有必需的环境变量 并向路径
  • 我应该扔掉一个弦乐作家吗?或以某种方式重用?

    我会让事情变得简单 我有一个字符串编写器作为类成员 因此我不能使用 using 每次调用某个函数时我都想要一个空的软件 我应该在 sw 上调用 Dispose 并分配一个新对象吗 或者我应该做一些类似 close 的事情并做其他事情来清空缓
  • 将地址传递给数组而不是数组会导致问题吗?

    我遇到了这段代码 char str 600 scanf s str 当然 这会发出这个警告 a c 6 17 warning format specifies type char but the argument has type char
  • 在闪亮的应用程序部署中隐藏密钥

    我正在使用从 S3 获取的数据将应用程序部署到shinyapps io 并且我想确保我的 AWS 密钥是安全的 目前 在 app R 代码中 我正在设置环境变量 然后查询 S3 以获取数据 有没有办法创建一个隐藏密钥的文件并将其与我的 ap
  • 如何使用 ctypes 将 Python 列表转换为 C 数组?

    如果我有以下两组代码 如何将它们粘合在一起 void c function void ptr int i for i 0 i lt 10 i printf p ptr i return def python routine y x for
  • 对于移动应用程序来说,正确的 OAuth 2.0 流程是什么

    我正在尝试使用 OAuth 2 0 在移动应用程序的 Web API 中实现委派授权 根据规范 隐式授权流程不支持刷新令牌 这意味着一旦在特定时间段内授予访问令牌 一旦令牌过期或被撤销 用户必须再次向应用程序授予权限 我想对于在浏览器上运行
  • iOS 7 Sprite Kit 释放内存

    我正在构建一款针对新 iOS 7 和 Sprite Kit 的 iOS 游戏 使用发射器节点和物理来增强游戏玩法 在开发应用程序时 我遇到了一个严重的问题 您创建了场景 节点 效果 但是当您完成并需要返回主屏幕时 如何释放这些资源分配的所有
  • HTML5 iFrame 高度仅为 150px

    基本上我想做的是在页面顶部有一个单一的 薄的工具栏 其中有一排各种 DIV 容器用作按钮 我希望这些按钮能够更新工具栏下方 iframe 的内容 问题是我的工具栏下方的 iframe 高度只有 150px 具体来说 当我指定时会发生这种情况
  • 使用jedis如何写入redis集群中的特定槽/节点

    我正在尝试提高将数据写入redis集群的性能 我们计划从 redi sentinel 迁移到集群模式以实现可扩展性 但是 与redis sentinel相比 写操作的性能要差很多 我们在redis sentinel中利用了管道 但集群模式不
  • 在 PHP 中迭代复杂的关联数组

    有没有一种简单的方法可以在 PHP 中迭代此结构的关联数组 数组 searches有一个编号索引 包含 4 到 5 个关联部分 所以我不仅需要迭代 searches 0 通过 searches n 但是也 searches 0 part0
  • 获取有关 MATLAB com.mathworks 内部结构的帮助

    可以访问 MATLAB 的内部 java 代码位 以编程方式更改 MATLAB 本身 例如 您可以使用以下命令以编程方式在编辑器中打开文档 editorServices com mathworks mlservices MLEditorSe
  • UITableView自定义滚动条

    如何为 UITableView 创建自定义滚动条 我想删除跟踪开始时弹出并在跟踪结束时消失的默认选项 相反 我想要一个类似于计算机程序中的程序 a 它位于屏幕的右侧并且永久可见 b 手动滚动栏会将 UITableView 滚动到适当的位置
  • 禁用特定 GDI 设备上下文的抗锯齿功能

    我正在使用第三方库将图像渲染到 GDI DC 并且我需要确保渲染任何文本时都不会进行任何平滑 抗锯齿 以便我可以将图像转换为具有索引颜色的预定义调色板 我用于渲染的第三方库不支持此功能 并且仅根据当前 Windows 设置的字体渲染来渲染文