[Html5] 用於分析26種畫布合成模式(globalCompositeOperation)的演示頁面

来源:https://www.cnblogs.com/zyl910/archive/2022/07/30/Html5_CanvasComposite_demo.html
-Advertisement-
Play Games

數組 數據結構 數據結構顧名思義 就數據的結構 (邏輯結構 存儲結構 演算法) 存儲結構 (數據存儲的結構方式) 線性結構 數組(順序表)隊列棧堆鏈表非線性結構樹圖 hash(散列表) 只要是能存數據的容器 就必須具備增刪改查的方法 數組(個人簡介就是一個地址吧很多東西都存起來) 數組概述:數組固定一 ...


作者: zyl910

一、緣由

Html5畫布(Canvas)的上下文(Context2D)提供globalCompositeOperation屬性,可用於控製圖形的繪製時的合成模式。
查了一下文檔,發現多達共有26種合成模式。且文字介紹很簡略,部分模式看不太懂。
於是我編寫了一個功能豐富的演示頁面,能夠隨時調整globalCompositeOperation等繪製參數,且有詳細信息文本框能用於分析像素合成的計算演算法的等。使用該頁面,能夠很好的學習這26種合成模式。
本文重點介紹演示頁面的功能,及開發過程中的問題處理。下一篇文章將介紹合成模式的計算演算法。

二、合成說明與功能設計

2.1 MDN文檔說明

下圖是MDN的globalCompositeOperation屬性的說明文檔的截圖。可見,對於每一種合成模式,只是用一段話做一下簡介而已。
Html5_globalCompositeOperation.jpg

文檔上共列出了26種合成模式:

source-over
source-in
source-out
source-atop
destination-over
destination-in
destination-out
destination-atop
lighter
copy
xor
multiply
screen
overlay
darken
lighten
color-dodge
color-burn
hard-light
soft-light
difference
exclusion
hue
saturation
color
luminosity

還好每一種模式都配了一張圖片範例,讓人稍微有一點頭緒。
每一種合成模式的附圖,由3張子圖片所組成,分別是“existing content”(現有內容)、“new content”(新內容)、“[name]”(合成模式的名稱,如“source-over”)。即將“子圖1(existing content)”的上面繪製“子圖2(new content)”時,該合成模式的處理結果是“子圖3([name])”。
每個子圖的左上角區域,還有一個小範例,演示了 藍紅方塊的合成結果。註意是在藍色方塊(子圖1:existing content)的上面繪製紅色方塊(子圖2:new content)的,且紅色方塊向左上角偏移了一點點,這樣便於觀察非重疊時的合成情況。

該文檔的後半部分,提供了一段簡單的JavaScript範例代碼,演示了xor合成模式。摘錄:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

ctx.globalCompositeOperation = 'xor';

ctx.fillStyle = 'blue';
ctx.fillRect(10, 10, 100, 100);

ctx.fillStyle = 'red';
ctx.fillRect(50, 50, 100, 100);

這段範例代碼簡潔明瞭,演示了globalCompositeOperation的用法。但有一個小問題——它繪製的藍紅方塊的位置關係為“藍色在紅色的左上側”,於是先前附圖裡“紅色在藍色的左上側”不同,不利於對比分析之前的說明。
於是我感覺有可開發一個新的演示頁面,使紅藍方塊的位置關係與文檔一致,並提供合成模式的下拉框,便於隨時切換合成模式,觀察合成結果。

2.2 統一術語

MDN文檔為了使文章更好懂,儘量減少專業術語,便採用了 “existing content”、“new content”這樣淺顯的名詞。
但這也帶來一些麻煩,因為該領域的專業資料是用專業術語的,用專業術語才能使邏輯上更清晰。例如 source-over、destination-over 等合成模式的名稱。

常用術語是這3個——

  • Source(源):待繪製的內容。一般縮寫為“S”。即MDN文檔里的 子圖1“existing content”(現有內容)。
  • Destination(目標):繪製的目標。一般縮寫為“D”。即MDN文檔里的 子圖2“new content”(新內容)。
  • Output(輸出結果):合成後的結果。一般縮寫為“O”。即MDN文檔里的 子圖3“[name]”(合成模式的名稱,如“source-over”)。

使用 Source、Destination等術語,最開始可能會感到比較抽象。但熟悉後,會覺得這些概念很簡潔、實用。
在D(Destination)的基礎上,繪製S(Source)圖像,合成模式的運算用“⊙”運算符代替,合成結果為O(Output)。那麼合成處理可以用以下數學式子來簡潔的表示:

O = D ⊙ S

在很多時候,輸出結果Output與目標Destination(現有內容,existing content)是同一個對象。例如Html5的Canva繪圖,目標(Destination)是現有的上下文(Context2D),輸出結果(Output)也是該上下文,只是狀態不同(合成後的結果)。
故O與D其實是等價的,只是人們為了突出表達狀態不同,才用到了O。所以很多時候用“D'”來代替“O”,式子為:

D' = D ⊙ S

為了進一步簡寫數學式子,可以將運算符(⊙)與等號(=)寫在一起,即:

D ⊙= S

這類似編程語言的“複合賦值運算符”——將目標變數D與源值S進行運算,運算結果保存在目標變數D里。

2.3 演示頁面功能設計

首先,能顯示跟MDN文檔一樣的紅藍方塊,便於對照文檔。
提供合成模式(globalCompositeOperation)的下拉框,便於隨時切換合成模式,觀察合成結果。
紅藍方塊能自定義顏色值。即提供文本框能隨時修改 Source、Destination 的顏色值。
紅藍方塊支持漸變繪製。即“to”覆選框右側的文本框能輸入第2顏色進行漸變。勾選“to”覆選框時啟用漸變,未勾選時不漸變。
紅藍方塊支持調整透明度。即“Alpha”覆選框右側的文本框能輸入alpha值(值域為 0~1)。
為瞭解決源漸變繪製時Alpha不同問題,提供“SourceUseImage”覆選框。當它覆選時,會先在一個臨時圖片里繪製好Source,再通過globalCompositeOperation進行繪製。預設覆選。
這些覆選框及文本框能自動生效。具體來說,當焦點離開文本框時,會自動點擊“Refresh”按鈕,使配置生效。
顯示點擊信息的坐標及顏色。首先,會在“Current: (0, 0). Destination sample, Source sample.”這一欄顯示這些信息,如“Current”是當前點擊位置的顏色,其後的括弧是點擊坐標,“Destination sample”是對應目標像素的顏色,“Source sample”是對應源像素的顏色。
能顯示點擊像素的詳細信息,並嘗試給出該合成模式的詳細計算過程。見“Current”欄下側文本框。
除了像MDN文檔那樣顯示 紅框在藍框左上的合成結果(compositeOffset)外,還展示同一位置的合成結果(composite),並顯示了合成前的 destination、source 圖。若勾選“SourceUseImage”,還會顯示半透明處理前的source 圖。
在頁面背後放上一份顏色名稱的表格,便於複製顏色名或rgb值,粘貼到自定義顏色值文本框進行測試。

下麵就是演示頁面的截圖。
Html5_CanvasComposite_pic1.png

2.3.1 詳細信息文本框

詳細信息文本框的內容範例:

Current    : RGBA(0.357, 0.106, 0.427, 0.875), Byte(91, 27, 109, 223), #5b1b6ddf, hsl(287, 0.603, 0.267). Pos(132, 94)
Destination: RGBA(0.000, 0.255, 1.000, 0.753), Byte(0, 65, 255, 192), #0041ffc0, hsl(225, 1.000, 0.500). Pos(132, 394)
Source     : RGBA(0.624, 0.000, 0.000, 0.502), Byte(159, 0, 0, 128), #9f000080, hsl(0, 1.000, 0.312). Pos(469, 431)
compositeMode:	source-over
	Fa = 1; Fb = 1 - As;	Co = As * Cs + Ab * Cb * (1 - As);	Ao = As + Ab * (1 - As)
Ro = As * Rs + Ab * Rb * (1 - As) = 0.502 * 0.624 + 0.753 * 0.000 * (1 - 0.502) = 0.313
Go = As * Gs + Ab * Gb * (1 - As) = 0.502 * 0.000 + 0.753 * 0.255 * (1 - 0.502) = 0.096
Bo = As * Bs + Ab * Bb * (1 - As) = 0.502 * 0.000 + 0.753 * 1.000 * (1 - 0.502) = 0.375
Ao = As + Ab * (1 - As) = 0.502 + 0.753 * (1 - 0.502) = 0.877
Premultiplie:RGBA(0.314, 0.094, 0.376, 0.878), Byte(80, 24, 96, 224), #501860e0, hsl(287, 0.600, 0.235)
Output     : RGBA(0.357, 0.110, 0.427, 0.878), Byte(91, 28, 109, 224), #5b1c6de0, hsl(287, 0.591, 0.269)

說明——

  • Current:當前點擊位置的顏色信息及坐標信息。格式為“RGBA、Byte、#rrggbbaa、hsl、Pos”,即分別為“歸一化的RGBA值、各分量的位元組值、十六進位表示的顏色值、hsl格式的顏色值、點擊坐標”。
  • Destination:當前點擊位置對應目標像素的顏色信息及坐標信息。格式為“RGBA、Byte、#rrggbbaa、hsl、Pos”,即分別為“歸一化的RGBA值、各分量的位元組值、十六進位表示的顏色值、hsl格式的顏色值、點擊坐標”。後面公式里用小寫的“b”或“d”來表示目標像素。
  • Source:當前點擊位置對應源像素的顏色信息及坐標信息。格式為“RGBA、Byte、#rrggbbaa、hsl、Pos”,即分別為“歸一化的RGBA值、各分量的位元組值、十六進位表示的顏色值、hsl格式的顏色值、點擊坐標”。後面公式里用小寫的“s”來表示源像素。
  • compositeMode:合成模式。
  • 公式。為了表示在引用公式,且為了便於與其他內容隔開,公式的的前面加了多個空格。
  • Ro、Go、Bo、Ao:分別顯示 R、G、B、A 通道的計算過程。
  • Premultiplie:顯示原始計算結果,它是 預乘Alpha(Premultiplie Alpha)模式的顏色值。格式為“RGBA、Byte、#rrggbbaa、hsl”,即分別為“歸一化的RGBA值、各分量的位元組值、十六進位表示的顏色值、hsl格式的顏色值”。
  • Output:顯示計算結果,它是 直通Alpha(Straight Alpha)模式的顏色值。格式為“RGBA、Byte、#rrggbbaa、hsl”,即分別為“歸一化的RGBA值、各分量的位元組值、十六進位表示的顏色值、hsl格式的顏色值”。

註——

  1. Html5的畫布,總是使用直通Alpha(Straight Alpha)模式。因運算公式的中間結果是預乘Alpha(Premultiplie Alpha)的,故最終輸出時,需做“預乘Alpha 轉 直通Alpha”的轉換。
  2. Output計算結果,理應與Current相同的,而有時會發現位元組值會有 12的誤差。這是因為Chrome在運算過程中可能用到了低精度整數運算等速度優化手段,而本頁面嚴格按照公式,且使用高精度的浮點運算。對於有256種值的8位色彩通道來說,有位元組值12的誤差,其實只是 2/256=1/128≈0.781% 的誤差,人眼看不出差別,故這些速度優化處理很常見。
  3. 只是對常用混合模式,編寫了了公式與計算過程。有些混合模式尚沒有編寫內容。

三、問題處理經驗

在演示頁面的開發過程中,遇到了一些事先沒想到的問題。現在分享一下處理經驗。

3.1 源漸變繪製時Alpha不同問題

勾選Source的Alpha覆選框,並設為“0.5”,若未啟用漸變(Source的“to”覆選框 未勾選時),可觀察到此時繪製的Source區域是正常的,各像素的Alpha為0.5左右。
若啟用啟用漸變(Source的“to”覆選框 被勾選時),可觀察到Source區域的Alpha不太對勁,各像素的Alpha為0.75左右。

為瞭解決源漸變繪製時Alpha不同問題,提供“SourceUseImage”覆選框。當它覆選時,會先在一個臨時圖片里繪製好Source,再通過globalCompositeOperation進行繪製。預設覆選。
“SourceUseImage”勾選時,可觀察到Source區域的Alpha仍是0.5左右,與配置的值相符。

3.2 Image.onload事件是非同步觸發的

因“SourceUseImage”覆選框,故需要先在另一塊區域繪製源圖,且需要將它轉為Image對象,這樣能便於使用 drawImage 進行繪圖。
使用Image時要註意,它的載入處理是非同步的。
若在設置了Image.src後立即進行繪圖,會發現大多數時候是空的。
為瞭解決這一問題,應處理onload事件,該事件觸發時才表示已載入完畢,可進行後續的繪圖等操作。

代碼摘錄:

// sourceUseImage
let sourceImage = null;
if (sourceUseImage) {
	try{
		//let canvasTemp = document.createElement("canvas");
		let canvasTemp = document.getElementById('canvasTemp');
		canvasTemp.style = "display:block";
		canvasTemp.width = blockWidth;
		canvasTemp.height = blockHeight;
		//canvas.getContext("2d").drawImage(image, 0, 0);
		let ctxTemp = canvasTemp.getContext("2d");
		ctxTemp.save();
		ctxTemp.clearRect(0, 0, blockWidth, blockHeight);
		//ctxTemp.globalAlpha = alphaS;
		drawRectS(ctxTemp, 0, 0, blockWidth, blockHeight, sColor0, sColor1);
		ctxTemp.restore();
		// to image.
		sourceImage = new Image();
		sourceImage.onload = function() {
			doRefresh_draw(sourceImage);
		}
		sourceImage.src = ctxTemp.canvas.toDataURL("image/png");
	} catch(ex) {
		sourceImage = null;
		console.log("Make sourceImage fail! ", ex);
	}
}
//console.log("sourceUseImage: ", sourceUseImage, "sourceImage: ", sourceImage);
if (null!=sourceImage) return;
let canvasTemp = document.getElementById('canvasTemp');
canvasTemp.style = "display:none";
doRefresh_draw(sourceImage);

3.3 部分合成模式會將區域外的顏色均清除為透明的

使用 source-out、destination-out 等合成模式時,不僅影響了Sourcet覆蓋的區域,且會將區域外的顏色均清除為透明的。
若畫布里只需繪製Sourcet,這種情況還可接收。但若是畫布里還有其他內容,這種情況會將區域外的其他內容均清理,變為透明。
例如本演示頁面上會繪製 compositeOffset、composite、destination、source 這四類圖形。因composite是最後進行合成繪製的,當選擇使用 source-out、destination-out 等合成模式時,會將compositeOffset、destination、source 的內容均清除,僅保留composite的。

為瞭解決這一問題,需要在合成繪製前,設置好剪裁區域。
對於Html5畫布來說,剪裁功能是這樣使用的:先調用beginPath方法開啟路徑,隨後進行rect等操作添加路徑形狀,最後調用clip將路徑轉為剪裁區域。
另外為了在處理後恢復為未剪裁的最初環境,可利用Html5畫布的 save/restore 機制。save方法用於在處理前保存環境,restore方法用於在處理後回覆環境。

代碼摘錄:

// Top Left: compositeOffset
//ctx.globalCompositeOperation = "source-over";
ctx.save();
ctx.globalAlpha = alphaD;
drawRectD(ctx, blockLeft, blockTop, blockWidth, blockHeight, dColor0, dColor1);
if (useClip) {
	ctx.beginPath();
	ctx.rect(0, 0, blockWidth*2, blockHeight*2);
	ctx.clip();
}
ctx.globalCompositeOperation = compositeModeLast;
ctx.globalAlpha = alphaS;
if (null==sourceImage) {
	drawRectS(ctx, blockLeft-blockOffsetX, blockTop-blockOffsetY, blockWidth, blockHeight, sColor0, sColor1);
} else {
	ctx.drawImage(sourceImage, blockLeft-blockOffsetX, blockTop-blockOffsetY);
}
ctx.restore();

四、小結

源碼地址:
https://github.com/zyl910/zhtml5info/blob/master/src/canvas/CanvasComposite.htm

參考文獻

作者:zyl910 出處:http://www.cnblogs.com/zyl910/ 版權聲明:自由轉載-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0.
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 沒想到粉絲對界面效果這麼喜歡,接下來就儘量多來點特效,當然,特效也算是動畫的一部分了。WPF裡面已經包含了很多動畫特效的功能支持了,但是,還是得自己實現,我這邊就來個自繪實現的。 彈動小球 彈動小球是一個很常見的頁面特效,類似於,拖動物體或者窗體,實現了抖動效果一樣。還是值得學習一二的,實際上,也很 ...
  • CentOS7擴容 最近在練習Elasticsearch做數據插入的時候,突然發現磁碟居然占滿了! 關於ES磁碟占用問題可以參考博客:https://blog.csdn.net/qq_16504067/article/details/119132849 擴容之前,我要提醒一下,我因為之前玩的Ubun ...
  • 痞子衡嵌入式半月刊: 第 59 期 這裡分享嵌入式領域有用有趣的項目/工具以及一些熱點新聞,農曆年分二十四節氣,希望在每個交節之日準時發佈一期。 本期刊是開源項目(GitHub: JayHeng/pzh-mcu-bi-weekly),歡迎提交 issue,投稿或推薦你知道的嵌入式那些事兒。 上期回顧 ...
  • 資料庫概述 為什麼要使用資料庫 什麼是數據持久化? 數據持久化就是把數據保存到可掉電式存儲設備中供以後使用。大多數情況下,特別是企業級應用,數據持久化意味著將記憶體中的數據保存到硬碟上加以”固化”,而持久化的實現過程大多通過各種關係資料庫來完成。 持久化的主要作用是將記憶體中的數據存儲在關係型資料庫中, ...
  • MYSQL(基本篇)——一篇文章帶你走進MYSQL的奇妙世界 MYSQL算是我們程式員必不可少的一份求職工具了 無論在什麼崗位,我們都可以看到應聘要求上所書寫的”精通MYSQL等資料庫及優化“ 那麼我們今天就先來瞭解一下MYSQL的整體佈局並且帶你掌握MYSQL的基本內容,希望能為你帶來幫助! MY ...
  • 今天在開發項目時,試著一次性插入大量數據,結果出現了以下報錯: 依稀記得以前也遇到過類似的問題,於是打算記錄下錯誤原因及解決過程: 首先,這是由於sql語句中占位符數量限制導致的 Mysql預設支持的占位符最多為65535(2^16-1)個,即:寫入數據為m列,n行。m*n必須小於65535 比如你 ...
  • 原文地址: Jetpack Compose學習(8)——State狀態及remeber關鍵字 - Stars-One的雜貨小窩 之前我們使用TextField,使用到了兩個關鍵字remember和mutableStateOf,這兩個是做什麼用的呢?本篇特來補充說明下 mutableStateOf 之 ...
  • AppCode是一個全新的 Objective-C的集成開發環境,幫助開發者來開發的Mac,iPhone和iPad上的iOS等系統的相關應用程式.AppCode是由大名鼎鼎的捷克JetBrAIns的公司開發,在iOS的開發者中有很高的聲譽。適用於iOS / macOS開發的智能IDE。 詳情:App ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...