This post has 0 Replies | 0 Followers

Top 25 Contributor
Male
Posts 22

Dịch bài từ MSDN, Người Dịch: Nguyễn Văn Khiếu

 

Sinh viên lớp Cử nhân tài năng,  07TT1A, 

Khoa Toán-Tin học, Đại học KHTN Tp.HCM

Email: nvkhieu89@gmail.com, nvkhieu@msdnvietnam.net

Link gốc: http://msdn.microsoft.com/en-us/magazine/ee819134.aspx

Tác Giả: Kenny Kerr

Windows with C++

Layered Windows with Direct2D

Trong kỳ ba về Direct2D, tôi sẽ giới thiệu một số tính năng chưa từng có của nó khi nói đến khả năng tương tác. Thay vì trình bày một cách thấu đáo, chi tiết tất cả các tùy chọn khả năng tương tác khác nhau mà Direct2D cung cấp, tôi sẽ hướng dẫn bạn qua việc thực hiện một ứng dụng: Lớp Windows (Layered Windows).  Layered Windows  là một trong những tính năng của Windows đã được xuất hiện trong một khoảng thời gian dài nhưng chưa phát triển nhiều và do đó cần quan tâm đặc biệt để sử dụng có hiệu quả với công nghệ đồ họa hiện đại.

Trong bài viết này tôi sẽ giả sử bạn đã quen với lập trình cơ bản với Direct2D. Nếu không, tôi đề nghị bạn đọc bài viết trước đây của tôi từ tháng sáu và tháng chín đã giới thiệu các nguyên tắc cơ bản của lập trình và vẽ với Direct2D.

Trước tiên, Layered Windows đáp ứng một số mục đích khác. Trong đó, Layered Windows có thể được sử dụng để dễ dàng tạo ra hiệu ứng thị giác và flicker-free rendering. Ngày nay, khi GDI được xem là một phương pháp chủ yếu để thiết kế, xây dựng đồ họa, đây là một phần thưởng xứng đáng. Trong ngày nay sự tăng tốc của thế giới phần cứng, tuy nhiên, nó không còn hấp dẫn bởi vì các Layered Windows vẫn thuộc về thế giới của User32/GDI và chưa được cập nhật đáng kể để hỗ trợ DirectX, nền tảng của Microsoft cho hiệu suất cao và đồ họa chất lượng cao.

Layered Windows cung cấp khả năng duy nhất để soạn (tạo) một cửa sổ trên máy tính sử dụng sự pha trộn cho mỗi điểm ảnh, điều mà không thể đạt được trong bất kỳ cách nào khác với SDK Windows.

Tôi nên đề cập đến rằng có thực sự hai loại Layered Windows. Các phân biệt dưới đây vcho dù bạn cần phải kiểm soát tính chắn sáng của mỗi pixel hoặc bạn chỉ cần để kiểm soát độ chắn sáng của cửa sổ như một toàn thể. Đây là bài viết khá cổ điển, nhưng nếu bạn thực sự chỉ cần để kiểm soát độ mờ của cửa sổ, bạn có thể làm như vậy bằng cách đơn giản gọi hàm SetLayeredWindowAttributes sau khi tạo cửa sổ để thiết lập giá trị alpha.


Verify(SetLayeredWindowAttributes(
 windowHandle, 
 0, // no color key
 180, // alpha value
 LWA_ALPHA));
  

Điều này giả định bạn đã tạo ra cửa sổ với kiểu mở rộng WS_EX_LAYERED hoặc áp dụng nó sau khi thực tế sử dụng chức năng SetWindowLong. Hình 1. Cung cấp một ví dụ về cửa sổ như vậy.Các lợi ích cần được rõ ràng: bạn không cần phải thay đổi bất cứ điều gì về cách vẽ của cửa sổ ứng dụng như cửa sổ Desktop Manager (DWM) sẽ tự động pha trộn các cửa sổ một cách thích hợp. Ở bên phần giao, bạn cần phải rút ra hoàn toàn tất cả mọi thứ cho mình. Tất nhiên, nếu bạn đang sử dụng một thương hiệu mới công nghệ rendering như Direct2D, đó không phải là một vấn đề!

Hình 1 cửa sổ với Alpha value

Vì vậy, những gì liên quan? Vâng, ở mức độ cơ bản của nó là đơn giản. Trước tiên, bạn cần điền vào một cấu trúc UPDATELAYEREDWINDOWINFO. Cấu trúc này cung cấp cho các vị trí và kích thước của một Layered Windows  cũng như một thiết bị GDI ngữ cảnh (DC) định nghĩa bề mặt của cửa sổ và nằm trong đó vấn đề. DCs thuộc về thế giới cũ của GDI và đang ở xa thế giới của DirectX và sự tăng tốc của phần cứng. Thêm vào đó trong một thời điểm.

Bên cạnh đó được đầy đủ các con trỏ đến cấu trúc mà bạn cần phải phân bổ cho mình, cấu trúc UPDATELAYEREDWINDOWINFO nhưng lại không đầy đủ tài liệu trong Windows SDK, làm cho nó ít rõ ràng hơn để sử dụng. Trong tất cả, bạn cần phải phân bổ năm cấu trúc. Có vị trí nguồn xác định vị trí của bitmap là sao chép từ các DC. Có vị trí cửa sổ xác định nơi cửa sổ sẽ được vị trí trên desktop một lần cập nhật. Có kích thước của bitmap để sao chép, mà cũng xác định kích thước của cửa sổ:

  POINT sourcePosition = {};
  POINT windowPosition = {};
  SIZE  size = {600, 400}; 

Sau đó có cấu trúc BLENDFUNCTION định nghĩa như thế nào trong Layered Windows  sẽ được pha trộn với desktop. Đây là một cấu trúc linh hoạt đáng ngạc nhiên là thường bị bỏ qua, nhưng có thể khá hữu ích. Bình thường, bạn có thể cư nó như sau:

  BLENDFUNCTION blend = {};
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;

Các AC_SRC_ALPHA hằng chỉ cho thấy rằng các nguồn bitmap có một kênh alpha, là kịch bản phổ biến nhất.

Các SourceConstantAlpha, tuy nhiên, điều thú vị ở đó là bạn có thể sử dụng nó tương tự như cách mà bạn có thể sử dụng SetLayeredWindowAttributes chức năng để kiểm soát độ mờ của cửa sổ như một toàn thể. Khi nó được thiết lập là 255, trong Layered Windows  sẽ chỉ sử dụng mỗi giá trị alpha điểm ảnh, nhưng bạn có thể điều chỉnh tất cả các cách về không, hay hoàn toàn trong suốt, để sản xuất hiệu quả như làm giảm cửa sổ trong hay ngoài mà không có tốn chi phí vẽ lại. Nó, nên có lý do rõ ràng cơ cấu BLENDFUNCTION được đặt tên như là: kết quả của alpha-blended windows là một chức năng của cơ cấu giá trị này.

Cuối cùng, có cơ cấu UPDATELAYEREDWINDOWINFO rằng tất cả quan hệ với nhau:

  UPDATELAYEREDWINDOWINFO info = {};
info.cbSize = sizeof(UPDATELAYEREDWINDOWINFO);
info.pptSrc = &sourcePosition;
info.pptDst = &windowPosition;
info.psize = &size;
info.pblend = &blend;
info.dwFlags = ULW_ALPHA;

 Điều này nên được để tự giải thích tại thời điểm này, chỉ với các biến thành phần dwFlags không được cung cấp. Một giá trị của ULW_ALPHA, mà nên nhìn quen thuộc nếu bạn đã sử dụng chức năng UpdateLayeredWindow cũ trước khi, chỉ cần chỉ ra rằng các chức năng pha trộn nên được sử dụng.

Cuối cùng, bạn cần cung cấp để xử lý nguồn DC và gọi hàm UpdateLayeredWindowIndirect để cập nhật các cửa sổ:

  info.hdcSrc = sourceDC;
 Verify(UpdateLayeredWindowIndirect(
  windowHandle, &info));

 Và đó là nó. Cửa sổ sẽ không nhận được bất kỳ thông điệp WM_PAINT. Bất cứ lúc nào bạn cần để hiển thị hoặc cập nhật các cửa sổ, chỉ cần gọi hàm UpdateLayeredWindowIndirect. Để giữ cho tất cả các mã mẫu này trong mọi lúc, tôi sẽ sử dụng lớp bọc LayeredWindowInfo thể hiện tronghình 2 trong phần còn lại của bài viết này.

Hình 2 LayeredWindowInfo Wrapper Class

  class LayeredWindowInfo {
  const POINT m_sourcePosition;
  POINT m_windowPosition;
  CSize m_size;
  BLENDFUNCTION m_blend;
  UPDATELAYEREDWINDOWINFO m_info;
public:
  LayeredWindowInfo(
    __in UINT width,
    __in UINT height) :
    m_sourcePosition(),
    m_windowPosition(),
    m_size(width, height),
    m_blend(),
    m_info() {
      m_info.cbSize = sizeof(UPDATELAYEREDWINDOWINFO);
      m_info.pptSrc = &m_sourcePosition;
      m_info.pptDst = &m_windowPosition;
      m_info.psize = &m_size;
      m_info.pblend = &m_blend;
      m_info.dwFlags = ULW_ALPHA;
      m_blend.SourceConstantAlpha = 255;
      m_blend.AlphaFormat = AC_SRC_ALPHA;
    }
  void Update(
    __in HWND window,
    __in HDC source) {
    m_info.hdcSrc = source;
    Verify(UpdateLayeredWindowIndirect(window, &m_info));
  }
  UINT GetWidth() const { return m_size.cx; }
  UINT GetHeight() const { return m_size.cy; }
};
 ); 

Hình 3 cung cấp một bộ xương cơ bản cho một Layered Windows  sử dụng ATL / WTL và lớp bọc LayeredWindowInfo từ hình 2. Điều đầu tiên này để thông báo là không cần gọi UpdateWindow kể từ mã này không sử dụng WM_PAINT. Thay vào đó nó ngay lập tức các cuộc gọi phương thức Render, do đó là cần thiết để thực hiện một số bản vẽ và để cung cấp một phương pháp DC để cập nhật LayeredWindowInfo's. Làm thế nào mà vẽ ra DC và nơi đến từ nơi nó là điều thú vị.

Hình 3 Layered Window Skeleton

class LayeredWindow :
  public CWindowImpl
  CWindow, CWinTraits > {
  
  LayeredWindowInfo m_info;
  
public:
  
  BEGIN_MSG_MAP(LayeredWindow)
    MSG_WM_DESTROY(OnDestroy)
  END_MSG_MAP()
  
  LayeredWindow() :
    m_info(600, 400) {
  
    Verify(0 != __super::Create(0)); // parent
    ShowWindow(SW_SHOW);
    Render();
  }
  
  void Render() {
    // Do some drawing here
  
    m_info.Update(m_hWnd,
      /* source DC goes here */);
  }
  
  void OnDestroy() {
    PostQuitMessage(1);
  }
};

 GDI / GDI + Way

Lần đầu tiên tôi sẽ cho bạn thấy làm thế nào nó đã được thực hiện trong GDI / GDI +. Trước tiên, bạn cần phải tạo một pre-multiplied  32-bit-per-pixel (bpp) bitmap sử dụng một màu xanh lá cây-xanh-đỏ-alpha (BGRA) màu kênh tự byte. pre-multiplied  chỉ có nghĩa là giá trị kênh màu đã được nhân với giá trị alpha. Điều này có xu hướng để cung cấp hiệu suất tốt hơn cho hình ảnh pha trộn alpha, nhưng nó có nghĩa là bạn cần phải đảo ngược quá trình bằng cách chia các giá trị màu bằng giá trị alphađể có được giá trị màu thật sự của họ. Trong GDI thuật ngữ, điều này được gọi là 32-bpp thiết bị độc lập bitmap (DIB) và được tạo ra bằng cách điền vào một cấu trúc BITMAPINFO và đi qua nó để chức năng CreateDIBSection (xem hình 4).

Hình 4 Tạo DIB

  BITMAPINFO bitmapInfo = {};
bitmapInfo.bmiHeader.biSize = 
  sizeof(bitmapInfo.bmiHeader);
bitmapInfo.bmiHeader.biWidth = 
  m_info.GetWidth();
bitmapInfo.bmiHeader.biHeight = 
  0 – m_info.GetHeight();
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = 
  BI_RGB;
  
void* bits = 0;
  
CBitmap bitmap(CreateDIBSection(
  0, // no DC palette
  &bitmapInfo,
  DIB_RGB_COLORS,
  &bits,
  0, // no file mapping object
  0)); // no file offset

Có rất nhiều chi tiết ở đây, nhưng chúng không có liên quan đến cuộc thảo luận. Chức năng này API đi trở lại một chặng đường dài. Những gì bạn nên lưu ý là tôi đã chỉ định một chiều cao tiêu cực cho các bitmap. Cơ cấu BITMAPINFOHEADER hoặc là một định nghĩa dưới lên hoặc từ trên xuống bitmap.Nếu chiều cao là tích cực bạn sẽ kết thúc với một từ dưới lên bitmap, và nếu nó phủ định bạn sẽ nhận được từ trên xuống bitmap. Từ trên xuống bitmap có nguồn gốc của họ trong góc trên bên trái, trong khi xuống dưới cùng bitmap có xuất xứ của họ ở dưới góc trái.

Mặc dù không nghiêm chỉnh cần thiết trong trường hợp này, tôi có xu hướng sử dụng trên xuống bitmap như đó là định dạng được sử dụng bởi hầu hết các thành phần hình ảnh hiện đại trong Windows và do đó cải thiện khả năng tương tác. Điều này cũng dẫn đến một bước tích cực, có thể được tính như sau:

  UINT stride = (width * 32 + 31) / 32 * 4;

 Tại thời điểm này bạn có đủ thông tin để bắt đầu vẽ trong bitmap thông qua con trỏ bit. Tất nhiên, trừ khi bạn hoàn toàn mất trí, bạn sẽ muốn sử dụng một số chức năng vẽ, nhưng tiếc là hầu hết những người cung cấp bởi GDI không hỗ trợ các kênh alpha. Đó là nơi mà GDI + đi thôi

Mặc dù bạn có thể vượt qua các dữ liệu bitmap trực tiếp đến GDI +, thay vào đó hãy tạo một DC cho nó, vì bạn sẽ cần nó mọi cách để thông qua chức năng UpdateLayeredWindowIndirect. Để tạo DC, khéo léo đặt tên  hàm CreateCompatibleDC, nó tạo ra một Bộ nhớ DC tương thích với máy tính. Sau đó bạn có thể gọi hàm SelectObject để chọn bitmap vào DC. Các lớp wrapper GdiBitmap trong hình 5 kết thúc tốt đẹp tất cả những điều này và cung cấp một số tiện ích quan lý mở rộng.

Hình 5 DIB Wrapper Class

class GdiBitmap {
  const UINT m_width;
  const UINT m_height;
  const UINT m_stride;
  void* m_bits;
  HBITMAP m_oldBitmap;
  
  CDC m_dc;
  CBitmap m_bitmap;
  
public:
  
  GdiBitmap(__in UINT width,
            __in UINT height) :
    m_width(width),
    m_height(height),
    m_stride((width * 32 + 31) / 32 * 4),
    m_bits(0),
    m_oldBitmap(0) {
  
    BITMAPINFO bitmapInfo = { };
    bitmapInfo.bmiHeader.biSize = 
      sizeof(bitmapInfo.bmiHeader);
    bitmapInfo.bmiHeader.biWidth = 
      width;
    bitmapInfo.bmiHeader.biHeight = 
      0 - height;
    bitmapInfo.bmiHeader.biPlanes = 1;
    bitmapInfo.bmiHeader.biBitCount = 32;
    bitmapInfo.bmiHeader.biCompression = 
      BI_RGB;
  
    m_bitmap.Attach(CreateDIBSection(
      0, // device context
      &bitmapInfo,
      DIB_RGB_COLORS,
      &m_bits,
      0, // file mapping object
      0)); // file offset
    if (0 == m_bits) {
      throw bad_alloc();
    }
  
    if (0 == m_dc.CreateCompatibleDC()) {
      throw bad_alloc();
    }
  
    m_oldBitmap = m_dc.SelectBitmap(m_bitmap);
  }
  
  ~GdiBitmap() {
    m_dc.SelectBitmap(m_oldBitmap);
  }
  
  UINT GetWidth() const {
    return m_width;
  }
  
  UINT GetHeight() const {
    return m_height;
  }
  
  UINT GetStride() const {
    return m_stride;
  }
  
  void* GetBits() const {
    return m_bits;
  }
  
  HDC GetDC() const {
    return m_dc;
  }
};

GDI + Graphics class, cung cấp phương pháp để vẽ một số thiết bị, có thể được xây dựng với DC của bitmap Hình 6 cho thấy. Làm thế nào lớp LayeredWindow từ Hình 3 có thể được cập nhật để hỗ trợ rendering với GDI +. Một khi bạn có tất cả các GDI mã mẫu trên đường đi, nó khá đơn giản.Kích thước của cửa sổ là được truyền cho constructor GdiBitmap và bitmap của DC là được truyền cho các nhà xây dựng đồ họa và phương pháp Cập nhật. Mặc dù đơn giản, không GDI GDI + cũng không phải là phần cứng tăng tốc (đối với hầu hết các phần), và cũng không thể cung cấp chức năng đặc biệt rendering mạnh mẽ.

Hình 6 GDI Layered Window

  class LayeredWindow :
  public CWindowImpl< ... {
  
  LayeredWindowInfo m_info;
  GdiBitmap m_bitmap;
  Graphics m_graphics;
  
public:
  LayeredWindow() :
    m_info(600, 400),
    m_bitmap(m_info.GetWidth(), m_info.GetHeight()),
    m_graphics(m_bitmap.GetDC()) {
    ...
  }
  
  void Render() {
    // Do some drawing with m_graphics object
  
    m_info.Update(m_hWnd,
      m_bitmap.GetDC());
  }
...

 Vấn đề Kiến trúc

Ngược lại, đây là tất cả cần tạo ra một Layered Windows  với Windows Presentation Foundation (WPF):

  class LayeredWindow : Window {
  public LayeredWindow() {
    WindowStyle = WindowStyle.None;
    AllowsTransparency = true;
  
    // Do some drawing here
  }
}

 Mặc dù vô cùng đơn giản, nó cũng làm nhầm các liên quan đến phức tạp kiến trúc và những hạn chế của việc sử dụng các Layered Windows . Không có vấn đề làm thế nào bạn loại bỏ nó, Layered Windows  phải tuân theo các nguyên tắc kiến trúc nêu vậy, đến nay trong bài viết này. Mặc dù WPF có thể sử dụng tăng tốc phần cứng cho rendering của nó, các kết quả vẫn còn cần phải được sao chép vào trước một nhân BGRA bitmap đã chọn vào một DC tương thích trước khi hiển thị được cập nhật thông qua một cuộc gọi đến các chức năng UpdateLayeredWindowIndirect. Kể từ WPF không lộ bất cứ điều gì hơn một biến bool, nó đã làm cho sự lựa chọn nhất định thay cho bạn rằng bạn đã không kiểm soát. Tại sao có vấn đề? Nó liên quan đến phần cứng.

Một đơn vị xử lý đồ họa (GPU) thích chuyên dụng bộ nhớ để đạt được hiệu quả tốt nhất. Điều này có nghĩa là nếu bạn cần thao tác một bitmap đã có, nó cần phải được sao chép từ bộ nhớ hệ thống (RAM) để xử lí bộ nhớ, mà có xu hướng chậm hơn nhiều so với việc sao chép giữa hai vị trí trong bộ nhớ của hệ thống. Các điều ngược lại cũng đúng: nếu bạn tạo và đưa ra một bitmap sử dụng GPU, sau đó quyết định để sao chép nó vào bộ nhớ hệ thống, đó là một hoạt động sao đắt tiền.

Bình thường, điều này không nên xảy ra như bitmap rendered của GPU thường được gửi trực tiếp đến thiết bị hiển thị. Trong trường hợp của các Layered Windows , các bitmap phải đi lại cho hệ thống bộ nhớ từ User32/GDI tài nguyên liên quan đến hạt nhân-cả hai chế độ và người sử dụng nguồn tài nguyên chế độ đó có yêu cầu truy cập vào các bitmap. Xem xét, ví dụ, một thực tế là User32 cần nhấn kiểm tra các Layered Windows . Lượt thử nghiệm của một Layered Windows  được dựa trên các giá trị alpha của bitmap, cho phép tin nhắn chuột qua nếu điểm ảnh tại một điểm đặc biệt là trong suốt. Kết quả là, một bản sao của bitmap được yêu cầu trong bộ nhớ hệ thống cho phép điều này xảy ra. Một khi bitmap đã được sao chép bằng cách UpdateLayeredWindowIndirect, nó được gửi thẳng về GPU vì vậy DWM có thể soạn các máy tính.

Bên cạnh đó chi phí của bộ nhớ sao chép lui, buộc GPU để đồng bộ hóa với CPU là tốn kém là tốt.Không giống như các điển hình CPU-bound hoạt động, GPU có xu hướng hoạt động cho tất cả được thực hiện đồng bộ, cung cấp hiệu năng tuyệt vời khi Trạm trộn của rendering một dòng lệnh. Mỗi khi chúng ta cần phải qua đường dẫn với CPU, nó buộc batched lệnh được flushed và CPU phải đợi cho đến khi GPU đã hoàn thành, dẫn đến ít hơn so với hiệu suất tối ưu.

Điều này có nghĩa là tất cả các bạn cần phải cẩn thận về các roundtrips và tần suất và chi phí liên quan. Nếu những cảnh được thực hiện được đầy đủ phức tạp, sau đó thực hiện tăng tốc phần cứng có thể dễ dàng lớn hơn chi phí của việc sao chép các bitmap. Mặt khác, nếu các rendering không phải là rất tốn kém và có thể được thực hiện bởi CPU, bạn có thể chọn cho thấy rằng không có tăng tốc phần cứng cuối cùng sẽ cho hiệu quả tốt hơn. Những lựa chọn này không phải dễ dàng để thực hiện.Một số GPU thậm chí không có chuyên dụng bộ nhớ và thay vì sử dụng một phần của bộ nhớ hệ thống, do đó giảm chi phí sao chép.

Nắm bắt được mà không GDI WPF cũng không cung cấp cho bạn một sự lựa chọn. Trong trường hợp của GDI, bạn đang mắc kẹt với CPU. Trong trường hợp của WPF, bạn buộc phải vào sử dụng cách tiếp cận rendering WPF sử dụng bất cứ điều gì, mà là điển hình phần cứng tăng tốc qua Direct3D.

Sau đó Direct2D đến cùng.

Direct2D để GDI / DC

Direct2D được thiết kế để khiến cho bất kỳ mục tiêu mà bạn chọn. Nếu đó là một cửa sổ hoặc kết cấu Direct3D, Direct2D thực hiện điều này trực tiếp trên GPU mà không có bất kỳ liên quan đến việc sao chép. Nếu đó là một Windows Imaging Component (WIC) bitmap, Direct2D tương tự ám trực tiếp sử dụng CPU để thay thế. Trong khi đó WPF phấn đấu để đặt nhiều rendering của nó trên GPU và sử dụng một phần mềm rasterizer như một dự phòng, Direct2D cung cấp tốt nhất của cả hai thế giới với chế độ vô song rendering ngay trên GPU để tăng tốc phần cứng, và tối ưu hóa cao rendering vào CPU khi một GPU là hoặc không có sẵn hoặc không mong muốn.

Như bạn có thể tưởng tượng, có khá một số cách để render một Layered Windows  với Direct2D. Chúng ta hãy xem xét một vài và tôi sẽ chỉ ra các phương pháp tiếp cận được đề nghị tuỳ thuộc vào việc bạn muốn sử dụng tăng tốc phần cứng.

Trước tiên, bạn chỉ có thể trích ra các GDI + Graphics class từ Hình 3 và thay thế bằng một Direct2D DC render mục tiêu. Điều này có thể làm cho ý nghĩa nếu bạn có một ứng dụng di sản với rất nhiều đầu tư vào GDI, nhưng chắc chắn không phải là giải pháp hiệu quả nhất. Thay vì rendering trực tiếp đến DC, Direct2D ám đầu tiên một bitmap WIC nội bộ, sau đó bản kết quả cho DC. Mặc dù nhanh hơn so với GDI +, điều này vẫn liên quan đến việc sao chép thêm rằng có thể tránh được nếu bạn không cần phải sử dụng một DC cho rendering.

Để sử dụng cách tiếp cận này, bắt đầu bằng cách khởi tạo một cấu trúc D2D1_RENDER_TARGET_PROPERTIES. Này cho Direct2D định dạng của bitmap để sử dụng cho mục tiêu của nó render. Nhớ lại rằng nó cần phải được chuẩn bị trước nhân BGRA định dạng pixel. Điều này được thể hiện với một cấu trúc D2D1_PIXEL_FORMAT và có thể được định nghĩa như sau:

  const D2D1_PIXEL_FORMAT format = 
  D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
  D2D1_ALPHA_MODE_PREMULTIPLIED);
const D2D1_RENDER_TARGET_PROPERTIES properties = 
  D2D1::RenderTargetProperties(
  D2D1_RENDER_TARGET_TYPE_DEFAULT,
  format);

 Bây giờ bạn có thể tạo DC render mục tiêu bằng cách sử dụng các đối tượng factory Direct2D:

CComPtr target;
Verify(factory->CreateDCRenderTarget(
  &properties,
  &target));

 Cuối cùng, bạn cần phải nói cho render mục tiêu mà DC để gửi lệnh vẽ của nó:

  const RECT rect = {0, 0, bitmap.GetWidth(), bitmap.GetHeight()};
Verify(target->BindDC(bitmap.GetDC(), &rect));

Tại thời điểm này bạn có thể vẽ với Direct2D như bình thường giữa BeginDraw và phương thức gọi EndDraw, và sau đó gọi phương thức cập nhật như trước với DC của bitmap. Phương pháp EndDraw đảm bảo rằng tất cả các bản vẽ đã được flushed để DC ràng buộc.

Direct2D to WIC

Bây giờ nếu bạn có thể tránh DC GDI hoàn toàn và chỉ sử dụng một bitmap WIC trực tiếp, bạn có thể đạt được hiệu quả tốt nhất mà không cần tăng tốc phần cứng. Để sử dụng cách tiếp cận này bắt đầu bằng cách tạo sẵn nhân BGRA bitmap trực tiếp với WIC:

  CComPtr factory;
Verify(factory.CoCreateInstance(
  CLSID_WICImagingFactory));
  
CComPtr bitmap;
Verify(factory->CreateBitmap(
  m_info.GetWidth(),
  m_info.GetHeight(),
  GUID_WICPixelFormat32bppPBGRA,
  WICBitmapCacheOnLoad,
  &bitmap));

 Tiếp theo bạn cần một lần nữa khởi tạo một cấu trúc D2D1_RENDER_TARGET_PROPERTIES trong cách nhiều như trước, ngoại trừ bạn cũng phải báo cho Direct2D rằng render mục tiêu cần phải được GDI tương thích:

const D2D1_PIXEL_FORMAT format = 
  D2D1::PixelFormat(
  DXGI_FORMAT_B8G8R8A8_UNORM,
  D2D1_ALPHA_MODE_PREMULTIPLIED);
const D2D1_RENDER_TARGET_PROPERTIES properties = 
  D2D1::RenderTargetProperties(
  D2D1_RENDER_TARGET_TYPE_DEFAULT,
  format,
  0.0f, // default dpi
  0.0f, // default dpi
  D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE);

 Bây giờ bạn có thể tạo các WIC render mục tiêu bằng cách sử dụng các đối tượng factory Direct2D:

  CComPtr target;
Verify(factory->CreateWicBitmapRenderTarget(
  bitmap,
  properties,
  &target));

 Nhưng điều không chính xác D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE làm gì? Đó là một gợi ý để Direct2D rằng bạn sẽ truy vấn render mục tiêu cho giao diện ID2D1GdiInteropRenderTarget:

  CComPtr interopTarget;
Verify(target.QueryInterface(&interopTarget));

 Để đơn giản và hiệu quả thực hiện, truy vấn cho giao diện này sẽ luôn luôn thành công. Đó là chỉ khi bạn cố gắng sử dụng nó, tuy nhiên, nó sẽ thất bại nếu bạn đã không xác định mong muốn của bạn lên phía trước.

Giao diện ID2D1GdiInteropRenderTarget cũng đã có hai phương pháp: GetDC và ReleaseDC. Để tối ưu hóa các trường hợp tăng tốc phần cứng được sử dụng, những phương pháp này bị giới hạn để được sử dụng giữa các cuộc gọi đến render BeginDraw mục tiêu và phương pháp EndDraw. GetDC sẽ tuôn ra những mục tiêu trước khi trở về render DC. Kể từ khi giao diện interop của phương pháp cần phải được ghép nối, nó làm cho cảm giác để bọc chúng trong một lớp C + + như trong hình 7.

Hình 7 Render Target DC Wrapper Class

  class RenderTargetDC {
  ID2D1GdiInteropRenderTarget* m_renderTarget;
  HDC m_dc;
public:
  RenderTargetDC(ID2D1GdiInteropRenderTarget* renderTarget) :
    m_renderTarget(renderTarget),
    m_dc(0) {
    Verify(m_renderTarget->GetDC(
      D2D1_DC_INITIALIZE_MODE_COPY,
      &m_dc));
  }
  ~RenderTargetDC() {
    RECT rect = {};
    m_renderTarget->ReleaseDC(&rect);
  }
  operator HDC() const {
    return m_dc;
  }
};

 Phương pháp Render của cửa sổ có thể được cập nhật để sử dụng RenderTargetDC, như trong hình 8. Những điều tốt đẹp về cách tiếp cận này là tất cả các mã số đó là cụ thể để tạo ra một mục tiêu là WIC render giấu đi trong phương pháp CreateDeviceResources. Tiếp tôi sẽ cho bạn thấy làm thế nào để tạo một Direct3D render để đạt được mục tiêu tăng tốc phần cứng, nhưng trong trường hợp, phương pháp Render hình 8 giữ nguyên. Điều này làm cho nó có thể cho các ứng dụng của bạn để render khá dễ dàng chuyển đổi mục tiêu hiện thực mà không thay đổi tất cả các mã vẽ của bạn.

Hình 8 GDI-Tương thích Phương pháp Render

  void Render() {
  CreateDeviceResources();
  m_target->BeginDraw();
  // Do some drawing here
  {
    RenderTargetDC dc(m_interopTarget);
    m_info.Update(m_hWnd, dc);
  }
  const HRESULT hr = m_target->EndDraw();
  
  if (D2DERR_RECREATE_TARGET == hr) {
    DiscardDeviceResources();
  }
  else {
    Verify(hr);
  }
}

 Direct2D để Direct3D/DXGI

Để có được phần cứng tăng tốc rendering, bạn cần phải sử dụng Direct3D. Bởi vì bạn không phải rendering trực tiếp đến một HWND qua ID2D1HwndRenderTarget, mà sẽ được tự động tăng tốc phần cứng, bạn cần tạo các thiết bị Direct3D mình và kết nối các dấu chấm ở lớp dưới DirectX đồ họa Cơ sở hạ tầng (DXGI) để bạn có thể nhận được kết quả tương thích GDI .

DXGI là một hệ thống phụ tương đối mới mà cuộc sống ngày một lớp dưới đây Direct3D để Direct3D trừu tượng từ phần cứng nằm bên dưới và cung cấp một cửa ngõ-hiệu năng cao cho các kịch bản interop. Direct2D cũng lợi dụng API này mới để đơn giản hóa việc di chuyển sang phiên bản tương lai của Direct3D. Để sử dụng cách tiếp cận này, bắt đầu bằng cách tạo ra một thiết bị phần cứng Direct3D. Đây là thiết bị đại diện cho GPU sẽ thực hiện các rendering. Ở đây tôi đang sử dụng các Direct3D 10,1 API như đây là yêu cầu của Direct2D hiện nay:

  CComPtr device;
  
Verify(D3D10CreateDevice1(
  0, // adapter
  D3D10_DRIVER_TYPE_HARDWARE,
  0, // reserved
  D3D10_CREATE_DEVICE_BGRA_SUPPORT,
  D3D10_FEATURE_LEVEL_10_0,
  D3D10_1_SDK_VERSION,
  &device));

 Cờ D3D10_CREATE_DEVICE_BGRA_SUPPORT là rất quan trọng cho Direct2D khả năng tương tác, và định dạng pixel BGRA nên bây giờ trông quen thuộc. Trong một ứng dụng Direct3D truyền thống, bạn có thể tạo ra một chuỗi trao đổi và truy xuất bộ đệm trở lại của nó như là một kết cấu để render thành trước khi trình bày trong cửa sổ rendered. Vì bạn đang sử dụng Direct3D cho rendering chỉ và không cho trình bày, bạn chỉ có thể tạo ra một nguồn lực kết cấu trực tiếp. Kết cấu là một nguồn tài nguyên Direct3D để lưu trữ texels, mà đó là tương đương với Direct3D của pixel. Mặc dù Direct3D cung cấp 1 -, 2 - và 3-chiều kết cấu, tất cả bạn cần là một kết cấu 2D, mà gần gũi nhất với một bản đồ bề mặt 2D (xem Hình 9).

Hình 9 A 2D Texture

  D3D10_TEXTURE2D_DESC description = {};
description.ArraySize = 1;
description.BindFlags = 
  D3D10_BIND_RENDER_TARGET;
description.Format = 
  DXGI_FORMAT_B8G8R8A8_UNORM;
description.Width = GetWidth();
description.Height = GetHeight();
description.MipLevels = 1;
description.SampleDesc.Count = 1;
description.MiscFlags = 
  D3D10_RESOURCE_MISC_GDI_COMPATIBLE;
CComPtr texture;
Verify(device->CreateTexture2D(
  &description,
  0, // no initial data
  &texture));

 Cơ cấu D3D10_TEXTURE2D_DESC mô tả các kết cấu để tạo ra. Các D3D10_BIND_RENDER_TARGET liên tục chỉ ra rằng các kết cấu là ràng buộc như là bộ đệm đầu ra, hoặc khiến mục tiêu, các đường ống dẫn Direct3D. Hằng số DXGI_FORMAT_B8G8R8A8_UNORM đảm bảo rằng Direct3D sẽ sản xuất định dạng đúng pixel cho GDI. Cuối cùng, hằng D3D10_RESOURCE_MISC_GDI_COMPATIBLE chỉ thị DXGI nằm bên dưới bề mặt để đưa ra một DC GDI thông qua đó các kết quả của rendering có thể thu được. Direct2D Điều này cho thấy nhiều thông qua giao diện ID2D1GdiInteropRenderTarget tôi đã thảo luận trong phần trước.

Như tôi đã đề cập, Direct2D có khả năng render đến một bề mặt Direct3D qua API DXGI để tránh kiểu gõ API cho bất kỳ phiên bản đặc biệt của Direct3D. Điều này có nghĩa là bạn cần phải nhận được các kết cấu nằm bên dưới giao diện Direct3D của bề mặt DXGI để vượt qua để Direct2D:

  CComPtr surface;
Verify(texture.QueryInterface(&surface));

 Vào lúc này, bạn có thể sử dụng các đối tượng nhà máy Direct2D để tạo ra một bề mặt DXGI render mục tiêu:

  CComPtr target;
Verify(factory->CreateDxgiSurfaceRenderTarget(
  surface,
  &properties,
  &target));

 Các tài sản khiến mục tiêu đều giống nhau như những người tôi mô tả trong phần trước. Chỉ cần nhớ để sử dụng định dạng đúng yêu cầu GDI pixel và khả năng tương thích. Bạn có thể truy vấn sau đó cho giao diện ID2D1GdiInteropRenderTarget và sử dụng cùng một phương pháp Render từ Hình 8.

Và đó là tất cả để có nó. Nếu bạn muốn render Layered Windows  của bạn với gia tốc phần cứng, sử dụng một kết cấu Direct3D. Nếu không sử dụng một bitmap WIC. Hai phương pháp tiếp cận sẽ cung cấp hiệu suất tốt nhất có thể với số tiền ít nhất của việc sao chép.

Hãy chắc chắn kiểm tra blog của DirectX và đặc biệt, Ben Constable của tháng tám 2009 bài viết về componentization và khả năng tương tác tại http://blogs.msdn.com/directx 


Kenny Kerr là một đam mê công nghệ phần mềm Windows. Ông cũng là tác giả của Window Clippings (windowclippings.com). Liên hệ ông tại http://weblogs.asp.net/kennykerr 

Gửi câu hỏi và ý kiến của bạn cho Kerr tới mmwincpp@microsoft.com.

Cảm ơn các chuyên gia kỹ thuật sau đây đã xem lại bài viết này: Ben Constable và Mark Lawrence

Biệt danh: Khiếu Nobita, BunhiacopKhiếu

Khiếu Nguyễn

Gifted Student

Faculty of Mathematics and Computer Science

University of Science, HCM City

Page 1 of 1 (1 items) | RSS