GDI+的快速渲染-什麼該做,什麼不該做

原文:http://www.codeproject.com/Tips/66909/Rendering-fast-with-GDIplus-What-to-do-and-what-no.aspx

翻譯於2015-1-07 


  每個人都知道GDI+在渲染過程中通常是緩慢的,然而怎樣的設置最快卻並不爲人所知。我有一個的APP,它包含了很多 可以被繪製爲有位圖圖源的精靈在一個頁面上(通常是1024*768).第一次嘗試跑了大約3幀/秒,通過加速後,我獲得的速率高達41幀/秒。


  我的應用程序大致做了以下內容:
  load a background Bitmap: bgndBitmap
    load any Bitmaps used for 'sprites': spriteBitmap
    create a Bitmap to render into: renderBitmap
    create a Graphics object to draw on the Bitmap
    while( forever )
    {
         draw bgndBitmap 
         for( all items... )
             draw part of spriteBitmap
 
         draw the renderBitmap into a dialog
    }
 
  什麼該做
  始終使用PixelFormat32bppPARGB
  
  預乘Alpha,即使有些特定的圖像或位圖可能不會有任何的Alpha.由於我的應用程序的用戶可以使用多種格式的背景(包括png),那麼就加載圖像使用...
  Bitmap *LoadABitmap( char *filename )
{
    WCHAR wname[1024];
    MultiByteToWideChar( CP_ACP, 0, filename, -1, wname, 1000 );
    Bitmap *loadBmp= new Bitmap( wname, FALSE );
    if( loadBmp->GetLastStatus() == Ok )
    ...
...可能導致幾種格式之一.
  我們要的是一個已知的格式的“堅實的背景,所以我們重繪已經加載的位圖到一個固定格式的新的位圖上.


  PixelFormat32bppRGB似乎合適,因爲這將消除任何可能存在的alpha(透明度)組件 - 然而這將不會導致最快的渲染.


  我們所需的是所有Alpha設置爲固定的PixelFormatPARGB.
  在我的例子我僅僅:
    Bitmap *newBmp= new Bitmap( loadBmp->GetWidth(), loadBmp->GetHeight(),
                               PixelFormat32bppPARGB );
    Graphics *pGraphics= new Graphics( newBmp );
 
    pGraphics->Clear( 0xffffffff );  // clear to solid white
    
    Rect sizeR( 0,0,loadBmp->GetWidth(), loadBmp->GetHeight());
    pGraphics->DrawImage( loadBmp, sizeR, 0,0, 
                             (int)loadBmp->GetWidth(),
                             (int)loadBmp->GetHeight(), 
                             UnitPixel );
    delete pGraphics;
    delete loadBmp;
 
    return newBmp;


  更通常的,你可以繪製已經加載好的位圖到一個RGB位圖中,然後再繪製RGB位圖到PARGB位圖中.


  我做了相同風格的位圖精靈,但這裏我想保留任何透明度——只是讓格式“PARGB”。


  設置圖像選項


  接下來的設置一般來說是最快的:
  Graphics *pGraphics= Graphics::FromHWND( hwndMyPictureWindow, FALSE );
 
    pGraphics->SetCompositingMode( CompositingModeSourceCopy );
    pGraphics->SetCompositingQuality( CompositingQualityHighSpeed );
    pGraphics->SetPixelOffsetMode( PixelOffsetModeNone );
    pGraphics->SetSmoothingMode( SmoothingModeNone );
    pGraphics->SetInterpolationMode( InterpolationModeDefault );
 
    pGraphics->DrawImage( RenderBitmap, 0, 0 );
    delete pOutputGraphics;


   使用這些設置爲“塊傳輸'的位圖,在1:1的比例,如上面的例子中,我們只是簡單地複製位圖到輸出窗口。


  然後,如果你正在做任何縮放,精靈的渲染或者文本,結果將是有點令人失望的。


  對於正在繪製的精靈等,這些設置都是高質量和相當快的:
   pGraphics->SetCompositingMode( CompositingModeSourceOver );  // 'Over for tranparency
   pGraphics->SetCompositingQuality( CompositingQualityHighSpeed );
   pGraphics->SetPixelOffsetMode( PixelOffsetModeHighSpeed );
   pGraphics->SetSmoothingMode( SmoothingModeHighSpeed );              
   pGraphics->SetInterpolationMode( InterpolationModeHighQuality );   


  我認爲不是,例如,InterpolationModeLowQuality 應該更快,但他不是(不要問我爲什麼!).
  
  最快的設置對於正在繪製的透明的精靈,如果你可以處理一些邊緣效應:
    pGraphics->SetCompositingMode( CompositingModeSourceOver );
    pGraphics->SetCompositingQuality( CompositingQualityHighSpeed );
    pGraphics->SetSmoothingMode( SmoothingModeNone );
    pGraphics->SetPixelOffsetMode( PixelOffsetModeHalf );
    pGraphics->SetInterpolationMode( InterpolationModeNearestNeighbor );


  注:
  其中的一些設置是相互關聯的,InterpolationModeNearestNeighbor僅僅在你有PixelOffsetModeHalf設定時是最快的。
  
  只是一個警告:PixelOffsetModeHalf 有一些其他的效應-GetVisibleClipBounds()將返回 {1,1,X,Y} ,而不是 {0,0,X,Y},這將導致LockBits()失敗.


  對於縮放一整個位圖到另外一個,我用
 // set quality for scaling
    pGraphics->SetCompositingMode( CompositingModeSourceCopy );
    pGraphics->SetCompositingQuality( CompositingQualityHighSpeed );
    pGraphics->SetPixelOffsetMode( PixelOffsetModeHighSpeed );
    pGraphics->SetSmoothingMode( SmoothingModeHighSpeed );
    pDestGraphics->SetInterpolationMode( InterpolationModeHighQuality );
 
    if( g_RenderFastest )  // global flag to render fastest, but reduce quality
    {
        pGraphics->SetInterpolationMode( InterpolationModeDefault );
        pGraphics->SetSmoothingMode( SmoothingModeNone );
        pGraphics->SetPixelOffsetMode( PixelOffsetModeHalf );
    }


  爲了完整性,這裏有我對於 正在繪製的文字DrawString()的設置:
    pGraphics->SetCompositingMode( CompositingModeSourceOver );
    pGraphics->SetCompositingQuality( CompositingQualityHighSpeed );
    pGraphics->SetInterpolationMode( InterpolationModeHighQuality );
    pGraphics->SetPixelOffsetMode( PixelOffsetModeHighSpeed );
    pGraphics->SetSmoothingMode( SmoothingModeNone );
    // Note: TextRenderingHintClearTypeGridFit looks real crap
    //       if we're drawing onto a transparent object
    //       but now we're not... anyhow, this is OK:
    pToGraphics->SetTextRenderingHint( TextRenderingHintAntiAliasGridFit );


  什麼是不該做的
  不要混合位圖格式
  渲染一個ARGB位圖到一個RGB位圖10倍慢於兩張PARGB位圖.通常的,似乎總是ARGB導致GDI+在內部做了預乘吃的CPU。
  
  想都別想線程


  我提到這點是因爲我想...(我的app事實上需要顯示在一個機器的4個顯示器上)代碼運行得很好,但GDI +不是爲多線程設計的,如果你獲得的臨界區是正確的,幾乎都是正常順序渲染的。


  參閱MSDN中關於 Gdi+安全方面的考慮,他最終是正確的(in a locked filing cabinet marked "Beware of the leopard")


  猜測...
  
  The options available are not obvious, so if you're doing some investigation, you must profile your code to see what effect each of 'em has. Guessing doesn't work, what may sound like a faster/less quality option probably isn't.


  The End


  That's the lot, hope it's of some use to someone.
 
  Yours,
  Tony Wilk
 
  [mail at tonywilk dot co dot uk]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章