Frage

Ich verwende eine Drittanbieter-Bibliothek zum Rendern eines Bildes auf einem GDI-DC-und ich brauche, um sicherzustellen, dass jeder text dargestellt wird, ohne jegliche Glättung/Anti-Aliasing, so dass ich können konvertieren Sie das Bild in eine vordefinierte palette mit indizierten Farben.

Die Drittanbieter-Bibliothek, die ich verwende für das rendering nicht unterstützt diese und nur-text macht nach den aktuellen windows-Einstellungen für font-rendering.Sie haben auch gesagt, dass es unwahrscheinlich ist Sie müssen fügen Sie die Fähigkeit zu Schalter, anti-aliasing off jederzeit bald.

Die beste Arbeit, die ich bisher gefunden habe, ist der Anruf der Drittanbieter-Bibliothek, die in dieser Weise (Fehler-handling und die vorherigen Einstellungen überprüft entfallen der Kürze halber):

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

Dies hat offensichtlich eine schreckliche Wirkung auf das Betriebssystem, andere Anwendungen flimmern von cleartype aktiviert, deaktiviert und wieder jedes mal, wenn ich das Rendern des Bildes.

Die Frage ist also, weiß jemand wie ich das ändern kann das font-rendering-Einstellungen für eine bestimmte DC?

Selbst wenn ich könnte nur machen die änderungen Prozess oder thread-spezifische stattdessen auf die Auswirkungen auf das gesamte Betriebssystem, das wäre ein großer Schritt nach vorne!(Das würde mir die option der Landwirtschaft das Rendern zu einem separaten Prozess - die Ergebnisse werden auf die Festplatte geschrieben nach dem Rendern eh)

EDIT: Ich möchte noch hinzufügen, dass ich nichts dagegen wenn die Lösung ist komplexer als nur ein paar API-Aufrufe.Ich würde auch gerne mit einer Lösung beteiligt, dass die hooking system-dlls, wenn es war nur etwa einen Tag Arbeit.

EDIT:Hintergrund-Informationen Die Drittanbieter-Bibliothek rendert mit Hilfe einer palette von über 70 Farben.Nach dem Bild (das ist eigentlich eine Kartenkachel) gerendert wird, um die DC, die ich konvertieren Sie jedes pixel von es sind 32-bit-Farbe, um es zurück palette index und speichern Sie das Ergebnis als 8 bit / Pixel Graustufen-Bild.Dies ist hochgeladen, um die Grafikkarte, die als eine textur.Beim Rendern, ich re-gelten die palette (ebenfalls gespeichert, als auch eine textur) mit einem pixel-shader ausführen, die auf der video-Karte.Dies ermöglicht es mir zu wechseln und fade zwischen verschiedenen Paletten augenblicklich statt des Müssens zu regenerieren alle benötigten Fliesen.Es dauert zwischen 10-60 Sekunden zu generieren und hochladen, alle Steine für eine typische Ansicht der Welt.

EDIT:Umbenannt GraphicsDevice Graphics Die Klasse GraphicsDevice in der vorherigen version von diese Frage ist tatsächlich-System.Zeichnung.Grafik.Ich hatte es umbenannt (mit GraphicsDevice = ...), da der code in der Frage ist im namespace MyCompany.Grafiken und der compiler nicht in der Lage war zu lösen es richtig.

EDIT:Erfolg! Ich schaffte es sogar den port der PatchIat Funktion unter C# mit Hilfe von Marshal.GetFunctionPointerForDelegate.Die .NET interop-team hat wirklich einen fantastischen job!Ich bin jetzt mit der folgenden syntax, wo Patch ist eine Erweiterung der Methode auf 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];
}
War es hilfreich?

Lösung 2

Wie gewünscht, habe ich verpackt der code, den ich schrieb dieses problem zu lösen, und legte es in ein github-repository: http://github.com/jystic/patch-iat

Es sieht aus wie eine Menge code, denn ich hatte zu reproduzieren alle die Win32-Strukturen, für dieses Zeug zu arbeiten, und in der Zeit, die ich wählte zu setzen, jeder in seine eigene Datei.

Wenn Sie möchten, um geradeaus zu fahren, um das Fleisch von der code es ist in: ImportAddressTable.cs

Es ist lizenziert sehr frei und ist für alle Absichten und Zwecke, public domain, also fühlen Sie sich frei, es zu benutzen in jedem Projekt, das Sie möchten.

Andere Tipps

Leider ist Sie nicht.Die Fähigkeit zur Steuerung der schriftart anti aliasing erfolgt per font.Die GDI-Aufruf CreateFontIndirect Prozesse Mitglieder der LOGFONT-Struktur zu ermitteln, ob seine erlaubt die Verwendung von cleartype, regelmäßige oder ohne anti-aliasing.

Es gibt, wie Sie bemerkt haben, systemweiten Einstellungen.Leider ändern die systemweite Einstellung ist so ziemlich das einzige (dokumentierte) Möglichkeit zum downgrade die Qualität des font-rendering auf einem DC, wenn Sie keine Kontrolle über den Inhalt der LOGFONT.


Dieser code ist nicht von mir.Nicht verwaltete C.Und hacken jede Funktion importiert eine dll-oder exe-Datei, wenn Sie wissen, 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 );
}

Rufen Sie diesen aus Ihren code mit etwas wie (ich habe nicht überprüft, dass dieses in irgendeiner Weise kompiliert wird :P):

  1. Deklarieren Sie einen Zeiger Art der funciton Sie wollen, um hook:

    typedef FARPROC (WINAPI* PFNCreateFontIndirect)(LOGFONT*);
    
  2. Implementieren Sie eine hook-Funktion

    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. Haken Sie die Funktion irgendwann während der Initialisierung

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

Natürlich, wenn das Modul, das Sie anschließen existiert .NET land seine sehr unklar, wo die CreateFontIndirect rufen wird, stammen. mscoree.dll?Das eigentliche Modul, Sie nennen?Viel Glück denke ich mal :P

Sie benötigen mehr Farben als schwarz und weiß auf Ihre Schriften?Wenn nicht, könnten Sie Ihren bitmap Objekt a 1 bit pro pixel Bild (Format1bppIndexed?).

Das system wird wohl nicht glatt font-rendering auf 1bpp Bilder.

ist die GraphicsDevice Klasse a 3rd party Klasse?

die Art, wie ich tun würde, ist dies:

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

oder in Ihrem Fall:

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

wenn die GraphicsDevice Klasse erbt die Klasse Graphics (andernfalls versuchen Sie es mit der Graphics-Klasse?)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top