문제

이미지를 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에 대한 글꼴 렌더링 설정을 어떻게 변경할 수 있는지 아는 사람이 있습니까?

전체 운영 체제에 영향을 주지 않고 변경 프로세스나 스레드를 특정하게 만들 수 있다고 해도 이는 큰 진전이 될 것입니다!(그러면 이 렌더링을 별도의 프로세스로 파밍할 수 있는 옵션이 제공됩니다. 결과는 어쨌든 렌더링 후에 디스크에 기록됩니다.)

편집하다: 솔루션이 단지 몇 가지 API 호출보다 더 복잡하더라도 상관없다는 점을 덧붙이고 싶습니다.며칠만 작업하면 시스템 DLL을 연결하는 솔루션에 만족할 것입니다.

편집하다:배경 정보타사 라이브러리는 약 70가지 색상의 팔레트를 사용하여 렌더링합니다.이미지(실제로 지도 타일)가 DC에 렌더링된 후 각 픽셀을 32비트 색상에서 팔레트 인덱스로 다시 변환하고 결과를 8bpp 회색조 이미지로 저장합니다.이는 비디오 카드에 텍스처로 업로드됩니다.렌더링하는 동안 비디오 카드에서 실행되는 픽셀 셰이더를 사용하여 팔레트(텍스처로도 저장됨)를 다시 적용합니다.이를 통해 필요한 모든 타일을 다시 생성할 필요 없이 즉시 다른 팔레트 간에 전환하고 페이드할 수 있습니다.일반적인 세계관에 대한 모든 타일을 생성하고 업로드하는 데 10~60초가 걸립니다.

편집하다:GraphicsDevice의 이름이 그래픽으로 변경되었습니다.이 질문의 이전 버전에 있는 GraphicsDevice 클래스는 실제로 System. Drawing.Graphics입니다.문제의 코드가 MyCompany.Graphics 네임스페이스에 있고 컴파일러가 이를 제대로 해결할 수 없었기 때문에 이름을 바꿨습니다(GraphicsDevice = ... 사용).

편집하다:성공!나는 심지어 포트에 성공했다 PatchIat 다음의 도움으로 아래 함수를 C#으로 변환합니다. Marshal.GetFunctionPointerForDelegate..NET Interop 팀은 정말 환상적인 일을 해냈습니다!이제 다음 구문을 사용하고 있습니다. 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];
}
도움이 되었습니까?

해결책 2

요청에 따라, 나는이 문제를 해결하기 위해 쓴 코드를 포장하고 GitHub 저장소에 배치했습니다. http://github.com/jystic/patch-iat

이 물건이 작동하기 위해 모든 Win32 구조를 재현해야했기 때문에 많은 코드처럼 보입니다. 당시에는 각 파일을 자체 파일에 넣기로 선택했습니다.

코드의 고기로 바로 가려면 다음과 같습니다. ImportAddressTable.cs

그것은 매우 자유롭게 라이센스가 부여되며 모든 의도와 목적, 공개 도메인이므로 원하는 프로젝트에서 자유롭게 사용하십시오.

다른 팁

불행히도 당신은 할 수 없습니다.글꼴 앤티앨리어싱을 제어하는 ​​기능은 글꼴별로 수행됩니다.GDI 호출 CreateFontIndirect는 LOGFONT 구조체의 멤버를 처리하여 클리어타입, 일반 또는 안티앨리어싱 사용이 허용되는지 여부를 결정합니다.

언급한 대로 시스템 전체 설정이 있습니다.안타깝게도 시스템 전체 설정을 변경하는 것은 LOGFONT의 내용을 제어할 수 없는 경우 DC에서 글꼴 렌더링 품질을 저하시키는 유일한(문서화된) 방법입니다.


이 코드는 내 것이 아닙니다.관리되지 않는 C입니다.HMODULE을 알고 있는 경우 dll 또는 exe 파일로 가져온 모든 기능을 연결합니다.

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

다음과 같은 작업을 수행하여 코드에서 이를 호출할 수 있습니다. (저는 이것이 어떤 방식으로든 컴파일되는지 확인하지 않았습니다 :P):

  1. 연결하려는 기능에 대한 포인터 유형을 선언하십시오.

    typedef FARPROC (WINAPI* PFNCreateFontIndirect)(LOGFONT*);
    
  2. 후크 기능 구현

    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. 초기화 중 언젠가 함수를 연결합니다.

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

물론, 연결하려는 모듈이 .NET에 존재하는 경우 해당 모듈이 어디에 있는지 매우 불분명합니다. CreateFontIndirect 통화는 다음에서 시작될 예정입니다. mscoree.dll?호출하는 실제 모듈은 무엇입니까?행운을 빌어요 :P

글꼴에 흑백보다 더 많은 색상이 필요합니까? 그렇지 않다면, 당신은 당신을 만들 수 있습니다 비트 맵 픽셀 이미지 당 1 비트 (형식 1BPPINDEXED?).

시스템은 아마도 1BPP 이미지에서 글꼴 렌더링을 부드럽게하지 않을 것입니다.

GraphicsDevice 클래스 A 제 3 자 클래스입니까?

내가 이것을하는 방법은 다음과 같습니다.

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

또는 귀하의 경우 :

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

GraphicsDevice 클래스가 그래픽 클래스를 상속하는 경우 (그렇지 않으면 그래픽 클래스를 사용해보십시오?)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top