Я использую стороннюю библиотеку для рендеринга изображения в 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 секунд.
EDIT: переименование GraphicsDevice в Graphics Класс GraphicsDevice в предыдущей версии этого вопроса на самом деле является System.Drawing.Graphics. Я переименовал его (используя GraphicsDevice = ...), потому что рассматриваемый код находится в пространстве имен MyCompany.Graphics, и компилятор не смог правильно его разрешить.
EDIT: успех! Мне даже удалось перенести функцию 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];
}