2016-01-26 14 views
3

編輯:原始問題仍包含在下面,但我決定重新標題的形式,將在開發人員在各種情況下更有用,其中一些描述在我的回答如下,因爲原來問題的解決方案竟然提供了更廣泛的應用領域。使用任意QML項目作爲緩存圖像源


我有一組應用程序的灰度圖標,並且要求用戶可以更改圖標顏色。

因此,顯而易見的解決方案是使用QtGraphicalEffects中的股票Colorize元素。

該效果本身有一個cached屬性 - 緩存該特定效果的結果,以便它不被連續計算。但是,這僅適用於特定的效果實例,這意味着如果有多個圖標實例,並且每個實例都具有着色效果,則不會在不同實例之間共享此緩存。

顯然,考慮到所有圖標的大小和顏色都相同,因此可以重複使用一個緩存,從而節省VRAM和GPU時間。

所以最大的問題是如何重用單一緩存的單一效果並多次顯示它而沒有任何開銷。

此外,前面的問題是關於我用彩色圖標拍攝的當前課程。但是,我可能會缺少另一種方法。當然,效率是關鍵,但簡單也是我們想要的,我的意思是我可以想到幾種低效率的方法可以非常有效地做到這一點,但它們都需要更復雜的低級別實現,它們不可能在QML中實現。

回答

1

我所做的優化QtGraphicalEffects使用Item.grabToImage() :)

這個函數會返回一個具有url()功能QQuickItemGrabResult項目。該函數返回一個QUrl,可以將其設置爲Image對象的源。

所以你需要做的是創建一個ImageColorize應用於它。當它準備好使用grabToImage(),併成功搶後保存QUrl某處安全並銷燬源對象。


我想你需要在應用程序運行時不時更改圖標的顏色。如果是這樣的話,請記住只更改Image對象的來源,以便沒有人使用抓取的圖像url將使其從內存中釋放。不是即時的,而是在需要時。

由於一些不兼容的情況,如果我在main.cpp文件中使用QGuiApplication::setAttribute(Qt::AA_UseOpenGLES);,我的應用程序只能正確地管理內存。


而且,這裏是一個重要的事實:

圖像緩存和共享的內部,所以如果幾個圖像項目具有相同的源,只有一個圖像的副本將被加載。(source


下面是工作示例。源對象是Rectangle,但它可以很容易地更改爲Image

import QtQuick 2.3 
import QtQuick.Window 2.2 
import QtGraphicalEffects 1.0 

Window { 
    visible: true  
    width: 500 
    height: 500 

    property string urlOfMyIcon: "" 


    Grid { 
     columns: 1 
     spacing: 10 
     Image { 
      width: 100 
      height: 100 
      source: urlOfMyIcon 
     } 
     Image { 
      width: 100 
      height: 100 
      source: urlOfMyIcon 
     } 
     Image { 
      width: 100 
      height: 100 
      source: urlOfMyIcon 
     } 
    } 

    Component.onCompleted: { 
     component.createObject(this) 
    } 

    Component { 
     id: component 
     Item { 
      id: yourImageWithLoadedIconContainer 
      Rectangle { 
       id: yourImageWithLoadedIcon 
       width: 80 
       height: 80 
       color: "white" 
       visible: false 

       // needed because I used Rectangle instead of Image 
       Component.onCompleted: { 
        colorizeEffect.grabToImage(function(result) { 
         urlOfMyIcon = result.url; 
         yourImageWithLoadedIconContainer.destroy() 
        }, Qt.size(width, height)); 
       } 

       // use this when using Image instead of Rectangle 
//    onStatusChanged: { 
//     if (status === Image.Ready) 
//      colorizeEffect.grabToImage(function(result) { 
//       urlOfMyIcon = result.url; 
//       yourImageWithLoadedIconContainer.destroy() 
//      }, yourImageWithLoadedIcon.sourceSize); 
//    } 
      } 
      Colorize { 
       id: colorizeEffect 
       anchors.fill: yourImageWithLoadedIcon 
       source: yourImageWithLoadedIcon 
       hue: 0.8 
       saturation: 0.5 
       lightness: -0.2 
       visible: false 
      } 
     } 
    } 
} 
+1

在我的情況下,兩個圖標的顏色和大小可以改變在運行期間,所以我將不得不查看是否可以使用綁定來自動化該過程。但我不得不承認,這種解決方案並不理想,因爲它不會保留在VRAM中 - 從''grabToImage()'從VRAM複製到RAM,只有在用作圖像源時,才能將其複製回VRAM。最好省略那些實際上不必要的,並且已知是相對較慢的轉移。 – dtech

+0

@ddriver尼斯,你是對的。如果您需要另一種方法,我只能推薦我在Docs中找到的有關'Item.grabToImage()'的內容:_注意:該函數會將項目渲染到離屏表面並將該表面從GPU內存複製到CPU內存中,這可能相當昂貴。對於「實時」預覽,使用圖層或ShaderEffectSource._ –

+0

昨天晚上睡覺後,我實際上想出了一個使用ShaderEffectSource的可能解決方案,但尚未測試它是否可用。如果成功,我會在獲得空閒時間後發佈測試。 – dtech

4

該解決方案竟然非常簡單。

在這種情況下,特定於OP - 也就是着色的圖標,最有效的方法是簡單地使用自定義ShaderEffect瑣碎片段着色器 - 設置gl_FragColor到所需的顏色,作爲vec4和傳遞來自源圖像的alpha值。真的沒有必要緩存任何東西,因爲着色器非常簡單,快速,速度也非常快。

只有一件事需要考慮 - QML場景圖可能會在紋理圖集中分配原始圖像,默認實現會將紋理從圖集複製到另一個紋理。我們不想要這個,因爲它破壞了目的 - VRAM的使用將會增加,因爲這將會爲每個「實例」完成,並且還有可能新分配的紋理會比他們需要的大,因爲on在一些平臺上,紋理的尺寸有多小是有限制的,在這種情況下,我們正在討論圖標,所以它們不會那麼大。

解決方法是明確地將supportsAtlasTextures設置爲true。這意味着您還必須傳遞地圖集中紋理的偏移量並計算偏移量 - 仍然非常小的開銷。這將確保效率,地圖集紋理不會在內存中複製,而且,渲染引擎實際上允許使用來自同一圖集的不同紋理的不同着色器效果在一次調用中一起進行批處理。


類似的方法可以用來緩存幾乎任何東西,並使用高速緩存,以顯示「圖像」 - 使用ShaderEffectSource來「捕捉」所需的圖像,然後用ShaderEffect有更簡單的片段着色器 - 僅從源採樣器輸出數據。一些非常有用的用例立刻浮現在腦海:

  • 它可以被用來「實例」的圖像,計算intenssive着色器的結果,請記住,ShaderEffectSource S和ShaderEffect S能以任意順序
  • 被鏈接
  • 它可以用於實例化程序生成的圖像,再次使用着色器,例如可以用作平鋪紋理,甚至可以非常有效地進行動畫
  • 它可以與QML Canvas一起使用,以使用複雜的畫布作爲緩存和來源多個「圖像」
  • 它可以用作圖像,由複雜的QML Item組成 - 這些實際上在RAM上非常沉重,想象一下你有1000個對象,每個對象都由20個不同的QML項目組成 - 矩形,文本,圖像,上帝禁止動畫,這是內存中的20000個對象 - 這就像基於我的測試的500 MB RAM使用率,但如果它們相同,則可以使用單個對象來提供緩存,而所有其他對象可以僅使用單個着色器效果來顯示該緩存。它也影響CPU時間 - 比如說你的設計必然會改變價值 - 如果你有20000個對象在內存中,那就是20000評估的綁定 - 即使是對於微不足道的表達式,這可能需要幾秒鐘的時間在移動設備上,在此期間凍結屏幕。緩存可以將凍結時間縮短1000倍,幾乎不存在。

  • 它也可以用來緩存和實例化的動畫,顯著減少了所需的CPU時間,並且還可以與視頻工作以及