Question

J'utilise une bibliothèque tierce pour restituer une image sur un contrôleur de domaine GDI et je dois m'assurer que tout texte est restitué sans lissage / antialiasing afin de pouvoir convertir l'image en une palette prédéfinie avec des couleurs indexées. <

La bibliothèque tierce que j'utilise pour le rendu ne prend pas cela en charge et rend le texte conformément aux paramètres actuels de Windows pour le rendu des polices. Ils ont également déclaré qu'il était peu probable qu'ils ajoutent la possibilité de désactiver l'anti-aliasing dans un proche avenir.

La meilleure solution que j'ai trouvée jusqu'à présent consiste à appeler la bibliothèque tierce de cette façon (traitement des erreurs et vérification des paramètres antérieurs omis par souci de concision):

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

Ceci a évidemment un effet horrible sur le système d'exploitation, les autres applications clignotant de cleartype activé à désactivé et inversement à chaque rendu de l'image.

La question est donc de savoir si quelqu'un sait comment je peux modifier les paramètres de rendu des polices pour un contrôleur de domaine spécifique?

Même si je pouvais simplement modifier le processus de modification ou les threads au lieu d’affecter l’ensemble du système d’exploitation, ce serait un grand pas en avant! (Cela me donnerait la possibilité d’agrandir ce rendu en un processus séparé: les résultats sont écrits sur le disque après le rendu de toute façon.)

MODIFIER: J'aimerais ajouter que le fait que la solution soit plus complexe que quelques appels d'API ne me dérange pas. Je serais même heureux avec une solution qui impliquait d’accrocher des dll système s’il ne s’agissait que d’un travail de quelques jours.

EDIT: informations générales La bibliothèque tierce rend en utilisant une palette d'environ 70 couleurs. Une fois que l'image (qui est en réalité une mosaïque de carte) est restituée au contrôleur de domaine, je convertis chaque pixel de sa couleur 32 bits en un index de palette et enregistre le résultat sous forme d'image en niveaux de gris de 8 bpp. Ceci est chargé sur la carte vidéo sous forme de texture. Pendant le rendu, je réapplique la palette (également stockée en tant que texture) avec un pixel shader exécuté sur la carte vidéo. Cela me permet de basculer et de disparaître instantanément entre les différentes palettes au lieu de devoir régénérer toutes les mosaïques requises. Il faut entre 10 et 60 secondes pour générer et télécharger toutes les vignettes pour une vue typique du monde.

ÉDITER: renommé GraphicsDevice en Graphics La classe GraphicsDevice dans la version précédente de cette question est en fait System.Drawing.Graphics. Je l’avais renommé (avec GraphicsDevice = ...) car le code en question se trouve dans l’espace de noms MyCompany.Graphics et que le compilateur n’a pas pu le résoudre correctement.

EDIT: succès! J'ai même réussi à porter la fonction PatchIat ci-dessous en C # à l'aide de Marshal.GetFunctionPointerForDelegate . L'équipe d'interopérabilité .NET a vraiment fait un travail fantastique! J'utilise maintenant la syntaxe suivante, où Patch est une méthode d'extension sur 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];
}
Était-ce utile?

La solution 2

Comme demandé, j'ai regroupé le code que j'ai écrit pour résoudre ce problème et je l'ai placé dans un référentiel github: http://github.com/jystic/patch-iat

Cela ressemble à beaucoup de code car je devais reproduire toutes les structures Win32 pour que cela fonctionne, et à ce moment-là, j'ai choisi de les placer chacune dans son propre fichier.

Si vous voulez aller directement à la fin du code c'est dans: ImportAddressTable.cs

Il est licencié très librement et est à toutes fins pratiques du domaine public, alors n'hésitez pas à l'utiliser dans tout projet que vous aimez.

Autres conseils

Malheureusement, vous ne pouvez pas. La possibilité de contrôler l’anti-aliasing des polices s’effectue par police. L’appel GDI CreateFontIndirect traite les membres de la structure LOGFONT pour déterminer s’il est autorisé à utiliser cleartype, avec ou sans anti-aliasing.

Il existe, comme vous l'avez indiqué, des paramètres système. Malheureusement, la modification du paramètre global du système est pratiquement le seul moyen (documenté) de dégrader la qualité du rendu des polices sur un contrôleur de domaine si vous ne pouvez pas contrôler le contenu du LOGFONT.

Ce code n'est pas le mien. Est unmanaged C. Et accrochera toute fonction importée par une dll ou un fichier exe si vous connaissez son 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 );
}

Vous appelez cela depuis votre code en faisant quelque chose comme (je n'ai pas vérifié si cela compilait: P):

  1. Déclarez un type de pointeur sur la fonction à accrocher:

    typedef FARPROC (WINAPI* PFNCreateFontIndirect)(LOGFONT*);
    
  2. Implémenter une fonction de raccordement

    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. Accrochez la fonction pendant l'initialisation

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

Bien sûr, si le module que vous raccordez existe dans .NET, il est très difficile de savoir d'où doit provenir l'appel CreateFontIndirect . mscoree.dll ? Le module actuel que vous appelez? Bonne chance je suppose: P

Avez-vous besoin de plus de couleurs que le noir et blanc pour vos polices? Sinon, vous pouvez créer un objet bitmap en image 1 bit par pixel ( Format1bppIndexed ?).

Le système ne lissera probablement pas le rendu des polices sur les images 1bpp.

la classe GraphicsDevice est-elle une classe tierce?

la façon dont je le ferais est:

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

ou dans votre cas:

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

si la classe GraphicsDevice hérite de la classe Graphics (sinon, essayez d'utiliser la classe Graphics?)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top