作者: zyl910
一、緣由
Html5畫布(Canvas)的背景關系(Context2D)提供globalCompositeOperation屬性,可用于控制圖形的繪制時的合成模式,
查了一下檔案,發現多達共有26種合成模式,且文字介紹很簡略,部分模式看不太懂,
于是我撰寫了一個功能豐富的演示頁面,能夠隨時調整globalCompositeOperation等繪制引數,且有詳細資訊文本框能用于分析像素合成的計算演算法的等,使用該頁面,能夠很好的學習這26種合成模式,
本文重點介紹演示頁面的功能,及開發程序中的問題處理,下一篇文章將介紹合成模式的計算演算法,
二、合成說明與功能設計
2.1 MDN檔案說明
下圖是MDN的globalCompositeOperation屬性的說明檔案的截圖,可見,對于每一種合成模式,只是用一段話做一下簡介而已,

檔案上共列出了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值,粘貼到自定義顏色值文本框進行測驗,
下面就是演示頁面的截圖,

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格式的顏色值”,
注——
- Html5的畫布,總是使用直通Alpha(Straight Alpha)模式,因運算公式的中間結果是預乘Alpha(Premultiplie Alpha)的,故最終輸出時,需做“預乘Alpha 轉 直通Alpha”的轉換,
- Output計算結果,理應與Current相同的,而有時會發現位元組值會有 12的誤差,這是因為Chrome在運算程序中可能用到了低精度整數運算等速度優化手段,而本頁面嚴格按照公式,且使用高精度的浮點運算,對于有256種值的8位色彩通道來說,有位元組值12的誤差,其實只是 2/256=1/128≈0.781% 的誤差,人眼看不出差別,故這些速度優化處理很常見,
- 只是對常用混合模式,撰寫了了公式與計算程序,有些混合模式尚沒有撰寫內容,
三、問題處理經驗
在演示頁面的開發程序中,遇到了一些事先沒想到的問題,現在分享一下處理經驗,
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 = https://www.cnblogs.com/zyl910/archive/2022/07/30/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
參考文獻
- MDN《CanvasRenderingContext2D.globalCompositeOperation》. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
- W3C《Compositing and Blending Level 1》. https://www.w3.org/TR/compositing-1/
- 么賀貴《canvas像素操作、save與restore、合成與變形》. https://blog.csdn.net/ayhg1/article/details/118071037
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/500654.html
標籤:其他
上一篇:陣列的常用方法
