Deshabilitar el antialiasing para un contexto de dispositivo GDI específico

Estoy utilizando una biblioteca de terceros para representar una imagen en un GDI DC y debo asegurarme de que cualquier texto se represente sin suavizado / suavizado para poder convertir la imagen a una paleta predefinida con colores indexados.

La biblioteca de terceros que estoy usando para la representación no es compatible con esto y solo representa el texto de acuerdo con la configuración actual de Windows para la representación de fonts. También han dicho que es poco probable que agreguen la capacidad de desactivar el suavizado en algún momento.

La mejor solución que he encontrado hasta ahora es llamar a la biblioteca de terceros de esta manera (el control de errores y las comprobaciones previas omitidas por brevedad):

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); 

Esto obviamente tiene un efecto horrible en el sistema operativo, otras aplicaciones cambian de cleartype habilitado a deshabilitado y regresan cada vez que renderizo la imagen.

Entonces, la pregunta es, ¿alguien sabe cómo puedo modificar la configuración de representación de la fuente para un DC específico?

Incluso si pudiera hacer que el proceso de cambios o el subproceso sea específico en lugar de afectar a todo el sistema operativo, ¡sería un gran paso adelante! (Eso me daría la opción de agrupar esta representación en un proceso por separado; de todos modos, los resultados se escriben en el disco)

EDITAR: Me gustaría agregar que no me importa si la solución es más compleja que solo unas pocas llamadas a la API. Incluso me sentiría feliz con una solución que involucraba conectar archivos dlls del sistema si solo se trataba de unos días de trabajo.

EDITAR: Información de fondo La biblioteca de terceros procesa utilizando una paleta de aproximadamente 70 colores. Después de que la imagen (que en realidad es un mosaico de mapa) se represente en el DC, convierto cada píxel de su color de 32 bits a su índice de paleta y almaceno el resultado como una imagen de escala de grises de 8 bpp. Esto se carga en la tarjeta de video como una textura. Durante el renderizado, vuelvo a aplicar la paleta (también almacenada como textura) con un sombreador de píxeles ejecutado en la tarjeta de video. Esto me permite cambiar y desvanecerme instantáneamente entre diferentes paletas en lugar de tener que regenerar todas las fichas requeridas. Se tarda entre 10 y 60 segundos en generar y cargar todos los mosaicos para una vista típica del mundo.

EDIT: Renombrado GraphicsDevice to Graphics La clase GraphicsDevice en la versión anterior de esta pregunta es en realidad System.Drawing.Graphics. Lo había cambiado de nombre (usando GraphicsDevice = …) porque el código en cuestión está en el espacio de nombres MyCompany.Graphics y el comstackdor no pudo resolverlo correctamente.

EDIT: ¡Éxito! Incluso logré PatchIat función PatchIat a C # con la ayuda de Marshal.GetFunctionPointerForDelegate . ¡El equipo de interoperabilidad .NET realmente hizo un trabajo fantástico! Ahora estoy usando la siguiente syntax, donde Patch es un método de extensión en 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]; } 

Desafortunadamente no puedes. La capacidad de controlar el suavizado de fonts se realiza por fuente. La llamada GDI CreateFontIndirect procesa a los miembros de la estructura LOGFONT para determinar si está permitido usar cleartype, regular o no alias.

Hay, como se señaló, la configuración de todo el sistema. Desafortunadamente, cambiar la configuración de todo el sistema es prácticamente la única forma (documentada) de degradar la calidad de la representación de fonts en un DC si no puede controlar el contenido de LOGFONT.


Este código no es mío. Está sin gestionar C. Y enganchará cualquier función importada por un archivo dll o exe si conoce su HMODULE.

 #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 ); } 

Llamarías a esto desde tu código haciendo algo como (no he comprobado que esto compile de ninguna manera: P):

  1. Declare un tipo de puntero a la función que desea enganchar:

     typedef FARPROC (WINAPI* PFNCreateFontIndirect)(LOGFONT*); 
  2. Implementar una función de gancho

     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. Enganche la función en algún momento durante la inicialización

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

Por supuesto, si el módulo que está enganchando existe en .NET, no está claro de dónde se originará la llamada CreateFontIndirect . mscoree.dll ? ¿El módulo real al que llamas? Buena suerte supongo: p

Según lo solicitado, he empaquetado el código que escribí para resolver este problema y lo coloqué en un repository de github: http://github.com/jystic/patch-iat

Parece mucho código porque tuve que reproducir todas las estructuras de Win32 para que esto funcionara, y en ese momento elegí poner cada una en su propio archivo.

Si quieres ir directamente a la parte del código está en: ImportAddressTable.cs

Tiene una licencia muy libre y es para todos los fines, dominio público, así que siéntase libre de usarlo en cualquier proyecto que desee.

¿Necesitas más colores que blanco y negro en tus fonts? Si no, puede hacer que su objeto de mapa de bits sea 1 bit por píxel de imagen (¿ Format1bppIndexed ?).

El sistema probablemente no suavizará la representación de fonts en imágenes de 1bpp.

¿La clase GraphicsDevice es una clase de terceros?

la forma en que haría esto es:

 Graphics g = Graphics.FromImage(memImg); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; 

o en su caso:

 GraphicsDevice graphics = GraphicsDevice.FromImage(bitmap) graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; 

si la clase GraphicsDevice hereda la clase Graphics (de lo contrario, intente usar la clase Graphics)