JS學習:建立一個演示用的渲染庫4(渲染表面,畫素格式等)

yzf01發表於2021-09-09

本篇的目的是要了解:

canvas 畫素格式

canvas渲染表面以及記憶體大小的計算

光柵化

位塊傳輸

圖形和影像的區別

我們瞭解到: 當css sizeelem size 不一致的時候,會導致渲染後的效果發生形變。

css size 決定了顯示區域的大小,但是實際渲染區域卻是由elem size確定的!!!

計算機在渲染圖形影像時,總是以畫素(px)這個物理單位進行定位置,定尺寸,定效果的。

而elem size 只能以px為單位設定元素大小,因為canvas需要使用這個尺寸分配渲染表面的畫素集合記憶體,所有的canvas後續繪圖操作都是繪製到這個渲染表面上的。

所以中的文字繪製實際上是繪製到了渲染表面上去了,而不是繪製到css size規定的顯示區中!

一些基礎概念:

什麼是畫素格式(pixel format)?

畫素格式就是畫素色彩每個分量大小排列方式。這種格式以每個畫素所使用的總位數以及用於儲存畫素色彩的紅、綠、藍和 alpha 分量的位數指定。這個是百度解釋,太學術化了。簡單說(按最常規的情況進行解釋),就是:

  • 分量(component)包括: R(red)、G(green)、B(blue)、A(alpha)

  • 一個畫素(pixel)可以由3個(RGB)或4個(RGBA)分量組成(一般情況下如此,但還有很多特殊畫素格式並不是由3/4個分量組成)

  • *分量大小是指每個分量的bit(不是bytes)數是多少,例如我們一般都是使用每個分量為8 bit的顏色表示法(canvas2d中就是如此),R8G8B8A8, 1 pixle = 4 compent = 4 componet 8 bits = 32 bits = 32 bits / 8 bits(1 bytes = 8 bits) = 4 bytes。也就是說R8G8B8A8(canvas2d畫素格式)的話,一個畫素佔用4個位元組的記憶體**

  • 實際上很多3D api,例如dx/gl還有更多畫素格式:R16B16G16A16 /R32G32B32A32...,每種api都支援遍歷擁有的畫素格式方法,可以列出一長串

  • 分量的排列方式: 不同的渲染API可能使用不同的分量排列方式,例如:canvas RGBA方式排序各個分量,而GL/DirectX可以使用ARGB,BGRA,RBGA....等排序方式

總結一下畫素格式:

 畫素是由哪些分量組成
 每個分量多少個bit數量(不是byte)
 分量是如何排列的

什麼是渲染表面?

渲染表面就是以畫素為單位表示的記憶體塊(若分配在視訊記憶體,則是視訊記憶體塊)。

簡而言之,渲染表面(render surface)就是記憶體影像,所有的繪製操作最後都以畫素色彩方式填充到這個記憶體影像中

具有兩個必須的特性:

  • size(width/height)
  • 畫素格式(RGB,RGBA,R8G8B8A8,R32G32B32A32,....,......,n多種)

有了上面兩個特性,就能確定記憶體塊的大小了,計算公式很簡單:
RGBA RenderSurface memory bytes = width height 4bytes

所以elem size 以及canvas2d raga畫素格式確定了canvas2d渲染表面的記憶體大小

有了渲染表面,就可以直接操作畫素進行顏色獲取或設定。

什麼是光柵化:

光柵化就是把頂點資料轉換為畫素片元的過程。畫素片元中的每一個元素對應於渲染表面中的一個畫素。

簡而言之,光柵化就是將圖形【graphics】變為影像【image】的過程。

還是有點學術化,解釋如下:

  • 頂點資料(既向量資料【vector】,或圖形【graphics】),因為向量或圖形是以頂點【vertex】方式進行定義的。例如一條線段由起點/終點來定義,而三角形由三個點來確定。

  • 一條線兩個點,然後渲染表面需要使用畫素表示,所以必須將兩個點變成一系列畫素,這就是光柵化【rasterization】(點線面體各種光柵化演算法)。

  • 上面解釋使用畫素片元(fragment)而不是畫素,是因為畫素僅僅包含三個資訊:分量的大小(8/16/32等),分量的數量(3/4),分量的排列方式【就是畫素格式】,但是片元包含更多資訊,例如3D api在光柵話的過程中片元還帶有深度資訊,紋理座標

位塊傳輸(bit-block transfer,bitblt for short):

當我們將canvas2d中的rendersurface 繪製到顯示區表面時,發生了位塊傳輸操作

位塊傳輸需要兩個(源【source】/目標【destination】)記憶體表面(或點陣圖,影像等等,反正就是具有大小和畫素格式的記憶體塊)。

能將源表面的全部或一部分割槽域畫素色彩資料傳輸到目標表面的全部或一部分割槽域。

並非是簡單的資料複製(copy),因為bitblt不止是原圖複製,有可能是進行源和目標表面之間畫素的與運算、或運算,異或運算等等

如果源表面和目標表面的size不一致的情況下,將源矩形中的點陣圖複製到目標矩形中,可以擴充套件或壓縮該點陣圖使其與目標矩形尺寸吻合。這個過程中會使用各種過濾插值演算法讓顏色儘量平和過度。但是會導致影像發生形變,這也是為什麼上一篇中發生了形變!

bitblit會盡可能使用硬體加速,如果實在不行,它們會使用具有很好最佳化手段的軟體傳輸方法(例如有硬體加速,則使用硬體加速,沒有硬體加速,但支援simd(單指令多資料流),則使用simd演算法,最後不行,就直接記憶體操作,反正bitblit是極其核心的一個功能,每個作業系統或硬體供應商都有關鍵最佳化程式碼對其支援)

圖形處理和影像處理的區別:

圖形學: 輸入的是模型頂點資料(向量表示),輸出是影像(畫素)

影像學: 輸入是畫素,輸出還是畫素,對畫素進行處理,例如ps中的各種濾鏡,現在很流行的影像識別等等

兩者完美的結合: AR技術(Augmented Reality) = 影像識別處理+圖形渲染效果

聊了這麼多,我們總結一下canvas中的繪圖的流程:

  • elem size 以及rgba畫素格式決定了canvas渲染表面分配的記憶體大小

  • 所有canvas2d 的繪圖操作(文字/各種形體都是向量),經過光柵化後,將頂點表示的資料生成了畫素表示,最終填充到了渲染表面中

  • canvas2d中的drawImage函式並不進行光柵化,而是進行位塊傳輸(bitblt),根據源表面和目標表面尺寸的不同,自動進行stretch blt操作

  • css 擁有一個虛擬座標系統,可以適應各種位置和尺寸單位(px,cm,rem...),是上層表示,但是最後必須要轉換成px表示,所以render surface最後需要進行位塊傳輸,將結果畫素傳送到css size定義的顯示錶面上去

所以,對於/等具有elem size以及顯示效果的元素,如果不想發生明顯的變形,就需要掌握兩個關鍵訣竅:

1) 儘量規定elem size,不要使用css size。即使使用css size,儘量和elem size 一致

2) 如果使用css size,為了不變形,請儘量和elem size 的縱橫比(height/width)一致,這時候render surface stretch blt to destination surface的時候,會進行等比擴充套件或縮放,至少比例是正確的,不會明顯變形(iphone的縱橫比基本只有兩個:1.5/1.78,相對android各式各樣的縱橫比,好多了,不過目前來說,主流的android機的縱橫比基本都是1.78)

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2334/viewspace-2799219/,如需轉載,請註明出處,否則將追究法律責任。

相關文章