Pergunta

Eu estou usando uma biblioteca de terceiros para processar uma imagem a um GDI DC e eu preciso para garantir que qualquer texto é processado sem qualquer suavização / antialiasing para que eu possa converter a imagem em uma paleta pré-definida com cores indexadas.

A biblioteca de terceiros que estou usando para prestação não suporta isso e apenas torna o texto de acordo com as configurações do Windows atuais para renderização de fontes. Eles também disse que é improvável que eles vão adicionar a capacidade de alternar anti-aliasing off qualquer momento em breve.

O melhor trabalho ao redor eu encontrei até agora é para chamar a biblioteca de terceiros desta forma (tratamento de erros e verifica as configurações anteriores omitidos por brevidade):

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

Isto, obviamente, tem um efeito terrível sobre o sistema operacional, outras aplicações piscar de cleartype activado para deficientes e voltar cada vez que processar a imagem.

Então a questão é, alguém sabe como eu posso alterar o tipo de letra configurações de renderização para uma específica DC?

Mesmo se eu pudesse fazer as mudanças processar ou específica fio em vez de afetar todo o sistema operacional, que seria um grande passo em frente! (Isso me daria a opção da agricultura Esta rendição para um Process- separado os resultados são gravados em disco após a renderização de qualquer maneira)

EDIT: eu gostaria de acrescentar que eu não me importo se a solução é mais complexa do que apenas algumas chamadas de API. Eu mesmo ser feliz com uma solução que DLLs do sistema enganchando envolvidos se fosse apenas cerca de um dia de trabalho.

EDIT: Informações gerais A biblioteca de terceiros processa usando uma paleta de cerca de 70 cores. Depois que a imagem (que na verdade é um mapa telhas) é processado para a DC, converter cada pixel dela é cor de volta 32-bit a ele do índice paleta e armazenar o resultado como uma imagem em tons de cinza 8bpp. Este é carregado para a placa de vídeo como uma textura. Durante a renderização, eu voltar a aplicar a paleta (também armazenado como uma textura) com um pixel shader execução da placa de vídeo. Isso me permite mudar e desaparecer entre diferentes paletas instantaneamente em vez de precisar de regenerar todas as peças necessárias. Demora entre 10-60 segundos para gerar e fazer upload de todas as telhas para uma visão típica do mundo.

EDIT: Renomeado GraphicsDevice para gráficos O GraphicsDevice classe na versão anterior desta questão é realmente System.Drawing.Graphics. Eu tinha rebatizou-o (usando GraphicsDevice = ...), porque o código em questão está nas MyCompany.Graphics namespace e o compilador não conseguiu resolvê-lo corretamente.

EDIT: Sucesso Consegui até porta a função PatchIat abaixo para C # com a ajuda de Marshal.GetFunctionPointerForDelegate. A equipe de interoperabilidade .NET realmente fez um trabalho fantástico! Agora estou usando a seguinte sintaxe, onde Patch é um método de extensão em 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];
}
Foi útil?

Solução 2

Como pedido, eu embalados o código que eu escrevi para resolver este problema e colocou-a em um repositório GitHub: http://github.com/jystic/patch-iat

Parece que um monte de código, porque eu tinha para reproduzir todas as estruturas do Win32 para este material para o trabalho, e no momento em que eu escolhi para colocar cada um em seu próprio arquivo.

Se você quiser ir direto para a carne de do código está em: ImportAddressTable.cs

É licenciado muito livremente e é para todos os efeitos, de domínio público, tão à vontade para usá-lo em qualquer projeto que você gosta.

Outras dicas

Infelizmente você não pode. A capacidade de controle da fonte anti aliasing é feito por fonte. Os processos chamada GDI CreateFontIndirect membros da estrutura LOGFONT para determinar se a sua permissão de uso cleartype, regular ou não anti aliasing.

Há, como você observou, as configurações de todo o sistema. Infelizmente, alterando a configuração de todo o sistema é praticamente o único caminho (documentado) para rebaixar a qualidade de renderização de fontes em um DC se você não pode controlar o conteúdo do LOGFONT.


Este código não é meu. É gerenciado C. E vai ligar qualquer função importada por uma dll ou exe se você sabe sua 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 );
}

Você chamaria isso de seu código, fazendo algo parecido (Eu não tenho verificado que esta em nenhum compila maneira: P):

  1. declarar um tipo de ponteiro para o funciton você quiser ligar:

    typedef FARPROC (WINAPI* PFNCreateFontIndirect)(LOGFONT*);
    
  2. Implementar uma função 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. Encaixe a algum função durante a inicialização

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

Claro que, se o módulo que você está ligando existe no .NET terra é muito claro quanto ao local onde a chamada CreateFontIndirect vai originam. mscoree.dll? O módulo real que você ligou? Boa sorte Eu acho que: P

Você precisa de mais cores do que preto e branco em suas fontes? Se não, você pode fazer o seu bitmap objeto uma imagem de 1 bit por pixel ( Format1bppIndexed ?).

O sistema irá provavelmente não suavizar renderização de fontes em imagens 1bpp.

é a classe GraphicsDevice uma 3ª classe festa?

a maneira que eu iria fazer isso é:

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

ou no seu caso:

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

Se a classe GraphicsDevice herda a classe Graphics (caso contrário, tente usar a classe Graphics?)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top