2009-03-13 48 views
9

Tôi đang sử dụng thư viện của bên thứ ba để hiển thị hình ảnh cho GDI DC và tôi cần đảm bảo rằng mọi văn bản được hiển thị mà không có bất kỳ hiệu ứng làm mịn/khử răng cưa nào để tôi có thể chuyển đổi hình ảnh thành một bảng màu được xác định trước với các màu được lập chỉ mục.Tắt tính năng chống răng cưa cho bối cảnh thiết bị GDI cụ thể

Thư viện của bên thứ ba mà tôi đang sử dụng để hiển thị không hỗ trợ điều này và chỉ hiển thị văn bản theo cài đặt cửa sổ hiện tại để hiển thị phông chữ. Họ cũng nói rằng không chắc họ sẽ thêm khả năng chuyển đổi chống răng cưa bất kỳ lúc nào.

Công việc tốt nhất xung quanh tôi đã tìm thấy cho đến nay là để gọi thư viện của bên thứ ba theo cách này (xử lý lỗi và các thiết lập kiểm tra trước ommitted cho ngắn gọn):

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

Điều này rõ ràng có ảnh hưởng khủng khiếp trên hệ điều hành, các ứng dụng khác nhấp nháy từ cleartype được kích hoạt để vô hiệu hóa và trở lại mỗi khi tôi render hình ảnh.

Vì vậy, câu hỏi là, có ai biết cách tôi có thể thay đổi cài đặt hiển thị phông chữ cho một DC cụ thể không?

Thậm chí nếu tôi chỉ có thể thực hiện quy trình thay đổi hoặc chủ đề cụ thể thay vì ảnh hưởng đến toàn bộ hệ điều hành, đó sẽ là một bước tiến lớn! (Điều đó sẽ cung cấp cho tôi tùy chọn canh tác kết xuất này với một quy trình riêng biệt- kết quả được ghi vào đĩa sau khi hiển thị)

EDIT: Tôi muốn thêm rằng tôi không quan tâm nếu giải pháp phức tạp hơn chỉ là một vài cuộc gọi API. Tôi thậm chí muốn được hạnh phúc với một giải pháp có liên quan đến hooking hệ thống dlls nếu nó chỉ là về một ngày làm việc.

CHỈNH SỬA: Thông tin cơ bản Thư viện của bên thứ ba hiển thị bằng bảng màu khoảng 70 màu. Sau khi hình ảnh (mà thực sự là một lát bản đồ) được trả về DC, tôi chuyển đổi mỗi điểm ảnh từ màu 32 bit trở lại chỉ mục bảng màu của nó và lưu kết quả dưới dạng hình ảnh màu xám 8bpp. Điều này được tải lên thẻ video dưới dạng kết cấu. Trong khi dựng hình, tôi áp dụng lại bảng màu (cũng được lưu trữ dưới dạng kết cấu) với trình đổ bóng pixel thực thi trên thẻ video. Điều này cho phép tôi chuyển đổi và mờ dần giữa các bảng màu khác nhau ngay lập tức thay vì cần phải tạo lại tất cả các ô được yêu cầu. Phải mất từ ​​10-60 giây để tạo và tải lên tất cả các ô xếp để có cái nhìn tiêu biểu của thế giới.

EDIT: Đổi tên GraphicsDevice thành Graphics Lớp GraphicsDevice trong phiên bản trước của câu hỏi này thực sự là System.Drawing.Graphics. Tôi đã đổi tên nó (sử dụng GraphicsDevice = ...) bởi vì mã được đề cập nằm trong vùng tên MyCompany.Graphics và trình biên dịch không thể giải quyết nó đúng cách.

EDIT: Thành công! Tôi thậm chí còn quản lý chức năng PatchIat dưới đây để C# với sự trợ giúp của Marshal.GetFunctionPointerForDelegate. Nhóm interop .NET thực sự đã làm một công việc tuyệt vời! Bây giờ tôi đang sử dụng cú pháp sau, nơi Patch là một phương pháp mở rộng trên 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]; 
} 

Trả lời

3

Theo yêu cầu, tôi đã đóng gói lên mã tôi đã viết để giải quyết vấn đề này và đặt nó trong một kho github: http://github.com/jystic/patch-iat

Dường như rất nhiều mã vì tôi đã phải sinh sản tất cả các cấu trúc Win32 cho công cụ này để làm việc, và tại thời điểm tôi đã chọn để đặt mỗi một trong tập tin riêng của mình.

Nếu bạn muốn đi thẳng vào thịt của mã nó trong: ImportAddressTable.cs

Nó được cấp phép rất tự do và là dành cho tất cả các tính năng, phạm vi công cộng, vì vậy cảm thấy tự do để sử dụng nó trong bất kỳ dự án mà bạn thích.

+0

nếu mã này giải quyết được sự cố, có thể bạn nên đánh dấu câu trả lời của riêng bạn là câu trả lời được chấp nhận. –

+0

@Justin, vâng điểm tốt. Như bạn có thể thấy từ những ngày tháng, tôi đã không đặt mã C# cho đến một năm sau đó, vì vậy tôi đã không nghĩ rằng để làm điều đó vào thời điểm đó. Tôi hy vọng @Chris Becke không mất đại diện cho câu trả lời đang được giao lại, tôi sẽ không bao giờ giải quyết vấn đề này nếu không có sự giúp đỡ của anh ấy. –

+0

Làm tốt lắm! Lưu ý rằng điều này cũng hoạt động trên nền tảng x64 nhưng bạn cần sửa đổi cấu trúc ImageOptionalHeader một chút khi trường _BaseOfData_ không có trong IMAGE_OPTIONAL_HEADER64. – Poustic

0

Bạn có cần màu sắc hơn màu đen và trắng trên phông chữ của bạn? Nếu không, bạn có thể tạo đối tượng bitmap bitmap đối tượng hình ảnh 1 bit cho mỗi pixel (Format1bppIndexed?).

Hệ thống có thể sẽ không hiển thị phông chữ mịn trên hình ảnh 1bpp.

+0

Thật không may tôi cần khoảng ~ 70 màu: ( –

+0

Bạn có thể tạo đối tượng bitmap làm hình ảnh 8bpp được lập chỉ mục bằng "bảng màu được xác định trước" không? Phông chữ sẽ được làm mịn nhưng ít nhất nó sẽ sử dụng bảng màu mà bạn muốn .. – Unbeknown

+0

Tôi thực sự đã thử điều đó, và tôi nghĩ nó đã hoạt động rất tốt, cho đến khi tôi chuyển sang một bảng màu khác và các phần chống răng cưa của văn bản được tham chiếu màu trong bảng màu mới hoàn toàn khác với vùng bao quanh Thật không may –

0

là lớp GraphicsDevice lớp thứ 3?

cách tôi sẽ làm điều này là:

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

hoặc trong trường hợp của bạn:

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

nếu Lớp GraphicsDevice kế thừa lớp Graphics (nếu không cố gắng sử dụng các lớp đồ họa?)

+0

Cảm ơn bạn đã gợi ý, tôi đã thử, nhưng có vẻ như việc đặt SmoothingMode chỉ ảnh hưởng đến việc hiển thị được thực hiện thông qua chính đối tượng Đồ họa. Do thư viện của bên thứ ba sử dụng trực tiếp ngữ cảnh thiết bị GDI, nên nó bỏ qua cài đặt này. –

+0

Trên thực tế, mã bạn cung cấp thậm chí không xử lý văn bản được vẽ bằng cách sử dụng Graphics. Đối với văn bản có một thuộc tính TextRenderingHint riêng biệt. –

5

Thật không may là bạn không thể. Khả năng kiểm soát chống răng cưa phông chữ được thực hiện trên mỗi phông chữ. Cuộc gọi GDI CreateFontIndirect xử lý các thành viên của cấu trúc LOGFONT để xác định xem nó có được phép sử dụng cleartype, thường xuyên hoặc không có chống răng cưa hay không.

Có, như bạn đã lưu ý, cài đặt trên toàn hệ thống. Thật không may, việc thay đổi thiết lập toàn hệ thống là khá nhiều cách duy nhất (tài liệu) để hạ cấp chất lượng hiển thị phông chữ trên DC nếu bạn không thể kiểm soát nội dung của LOGFONT.


Mã này không phải của tôi. Là không được quản lý C. Và sẽ móc bất kỳ hàm nào được nhập bởi tệp dll hoặc exe nếu bạn biết HMODULE của nó.

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

Bạn sẽ gọi đây là từ mã của bạn bằng cách làm một cái gì đó tương tự (tôi đã không kiểm tra rằng đây trong bất kỳ cách nào biên dịch: P):

  1. Khai báo một kiểu con trỏ đến các funciton bạn muốn để treo:

    typedef FARPROC (WINAPI* PFNCreateFontIndirect)(LOGFONT*); 
    
  2. Thực hiện một chức năng móc

    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. Hook chức năng đôi khi trong quá trình khởi

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

Tất nhiên, nếu các module bạn đang hooking tồn tại trong .NET đất của nó rất rõ ràng như đến nơi gọi CreateFontIndirect sẽ có nguồn gốc từ đâu. mscoree.dll? Mô-đun thực tế bạn gọi? Chúc may mắn tôi đoán: P

+0

Đây là kết luận khá nhiều mà tôi đã đến: (Tôi đã hy vọng một người nào đó có một số phép thuật để giải quyết vấn đề của tôi. Tôi tự hỏi nếu nó có thể móc cuộc gọi đến CreateFontIndirect và sau đó sửa đổi LOGFONT vì vậy nó không có anti-aliasing? –

+0

Giả sử rằng bạn đang inprocess với mô-đun cần được nối, và bạn biết xử lý HMODULE của nó - điều này xảy ra là địa chỉ cơ sở của nó, 'dễ dàng' của nó để vá Bảng Địa chỉ Nhập để nối một cuộc gọi api. –

+0

Xin lỗi, tôi đã bỏ lỡ bản chỉnh sửa của bạn cho đến bây giờ, tôi sẽ thử lại trong vài ngày tới –

Các vấn đề liên quan