Pixel manipulation with canvas
直到目前為止,我們還沒真正了解 pixels 在 canvas 上的運用。使用ImageData
物件,可直接對 pixel 裡的陣列資料讀(read)和寫(write)。在接下的內容中,也可了解到如何使影像平滑化(反鋸齒)及如何將影像保存在 canvas 之中。
ImageData
物件
ImageData
物件代表 canvas 區中最基礎的像素。
包含它只可讀的屬性:
width
影像中的寬度,以 pixels 為單位
height
影像中的高度,以 pixels 為單位
data
Uint8ClampedArray
代表一維陣列包含 RGBA 格式。整數值介於 0 到 255 之間(包含 255)。
data
屬性返回一個Uint8ClampedArray
,它可被當作為 pixel 的初始資料。每個 pixel 用 4 個 1byte 值做代表分別為紅,綠,藍,透明值(也就是RGBA格式)。每個顏色組成皆是介於整數值介於 0 到 255 之間。而每個組成在一個陣列中被分配為一個連續的索引。從左上角 pixel 的紅色組成中的陣列由索引 0 為始。Pixels 執行順序為從左到右,再由上到下,直到整個陣列。
Uint8ClampedArray
包含height
×width
× 4 bytes 的資料,同索引值從 0 到 (height
×width
×4)-1
例如,讀取影像的藍色組成的值。從 pixel 的第 200 欄、第 50 行,你可以照著下面的步驟:
blueComponent = imageData.data[50 * (imageData.width * 4) + 200 * 4 + 2];
使用Uint8ClampedArray.length
屬性來讀取影像 pixel 的陣列大小
var numBytes = imageData.data.length;
創造一個ImageData
物件
可以使用createImageData()
方法創造一個全新空白的ImageData
物件。
這裡有兩種createImageData()
的方法:
var myImageData = ctx.createImageData(width, height);
這個方法是有規定大小尺寸.所有 pixels 預設是透明的黑色。
下面的方法一樣是由anotherImageData
參考尺寸大小,由ImageData
物件創造一個與新的一樣的大小。這些新的物件的 pixel 皆預設為透明的黑色。
var myImageData = ctx.createImageData(anotherImageData);
得到 pixel 資料的內容
可以使用getImageData()
這個方法,去取得 canvas 內容中ImageData
物件的資料含 pixel 數據(data)
var myImageData = ctx.getImageData(left, top, width, height);
這個方法會返回ImageData
物件,它代表著在這 canvas 區域之中 pixel 的數據(data) 。從各角落的點代表著 (left
,top
), (left+width
,top
), (left
,top+height
), and (left+width
,top+height
)。這些作標被設定為 canvas 的空間座標單位。
備註:在ImageData
物件中,任何超出 canvas 外的 pixels 皆會返回透明的黑色的形式。
這個方法也被展示在使用 canvas 操作影像之中。
調色盤
這個範例使用getImageData() 方法去顯示在鼠標下的顏色。
首先,需要一個正確的滑鼠點layerX
和layerY
。在從getImageData() 提供 pixel 陣列中(array)該點的 pixel 數據(data) 。最後,使用陣列數據(array data)在<div>
中設置背景色和文字去顯示該色。
<canvas width="300" height="227"></canvas><div></div>
var img = new Image();img.src = "rhino.jpg";var canvas = document.getElementById("canvas");var ctx = canvas.getContext("2d");img.onload = function () { ctx.drawImage(img, 0, 0); img.style.display = "none";};var color = document.getElementById("color");function pick(event) { var x = event.layerX; var y = event.layerY; var pixel = ctx.getImageData(x, y, 1, 1); var data = pixel.data; var rgba = "rgba(" + data[0] + ", " + data[1] + ", " + data[2] + ", " + data[3] / 255 + ")"; color.style.background = rgba; color.textContent = rgba;}canvas.addEventListener("mousemove", pick);
在內容中寫入 pixel 資料
可以使用putImageData() 方法將自訂 pixel 數據(data) 放入內容中:
ctx.putImageData(myImageData, dx, dy);
dx
和dy
參數表示填入你所希望的座標,將它代入內容中左上角的 pixel 數據(data)。
For example, to paint the entire image represented bymyImageData
to the top left corner of the context, you can simply do the following:
ctx.putImageData(myImageData, 0, 0);
灰階和負片效果
In this example we iterate over all pixels to change their values, then we put the modified pixel array back to the canvas usingputImageData(). The invert function simply subtracts each color from the max value 255. The grayscale function simply uses the average of red, green and blue. You can also use a weighted average, given by the formulax = 0.299r + 0.587g + 0.114b
, for example. SeeGrayscale on Wikipedia for more information.
<canvas width="300" height="227"></canvas><div> <input value="Grayscale" type="button" /> <input value="Invert" type="button" /></div>
var img = new Image();img.src = "rhino.jpg";img.onload = function () { draw(this);};function draw(img) { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); img.style.display = "none"; var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); var data = imageData.data; var invert = function () { for (var i = 0; i < data.length; i += 4) { data[i] = 255 - data[i]; // red data[i + 1] = 255 - data[i + 1]; // green data[i + 2] = 255 - data[i + 2]; // blue } ctx.putImageData(imageData, 0, 0); }; var grayscale = function () { for (var i = 0; i < data.length; i += 4) { var avg = (data[i] + data[i + 1] + data[i + 2]) / 3; data[i] = avg; // red data[i + 1] = avg; // green data[i + 2] = avg; // blue } ctx.putImageData(imageData, 0, 0); }; var invertbtn = document.getElementById("invertbtn"); invertbtn.addEventListener("click", invert); var grayscalebtn = document.getElementById("grayscalebtn"); grayscalebtn.addEventListener("click", grayscale);}
放大和平滑化(反鋸齒)
With the help of thedrawImage()
method, a second canvas and theimageSmoothingEnabled
property, we are able to zoom into our picture and see the details.
We get the position of the mouse and crop an image of 5 pixels left and above to 5 pixels right and below. Then we copy that one over to another canvas and resize the image to the size we want it to. In the zoom canvas we resize a 10×10 pixel crop of the original canvas to 200×200.
zoomctx.drawImage( canvas, Math.abs(x - 5), Math.abs(y - 5), 10, 10, 0, 0, 200, 200,);
Because anti-aliasing is enabled by default, we might want to disable the smoothing to see clear pixels. You can toggle the checkbox to see the effect of theimageSmoothingEnabled
property (which needs prefixes for different browsers).
Zoom example
<canvas width="300" height="227"></canvas><canvas width="300" height="227"></canvas><div> <label for="smoothbtn"> <input type="checkbox" name="smoothbtn" checked="checked" /> Enable image smoothing </label></div>
var img = new Image();img.src = "rhino.jpg";img.onload = function () { draw(this);};function draw(img) { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); img.style.display = "none"; var zoomctx = document.getElementById("zoom").getContext("2d"); var smoothbtn = document.getElementById("smoothbtn"); var toggleSmoothing = function (event) { zoomctx.imageSmoothingEnabled = this.checked; zoomctx.mozImageSmoothingEnabled = this.checked; zoomctx.webkitImageSmoothingEnabled = this.checked; zoomctx.msImageSmoothingEnabled = this.checked; }; smoothbtn.addEventListener("change", toggleSmoothing); var zoom = function (event) { var x = event.layerX; var y = event.layerY; zoomctx.drawImage( canvas, Math.abs(x - 5), Math.abs(y - 5), 10, 10, 0, 0, 200, 200, ); }; canvas.addEventListener("mousemove", zoom);}
儲存圖片
TheHTMLCanvasElement
provides atoDataURL()
method, which is useful when saving images. It returns adata URI containing a representation of the image in the format specified by thetype
parameter (defaults toPNG). The returned image is in a resolution of 96 dpi.
canvas.toDataURL('image/png')
Default setting. Creates a PNG image.
canvas.toDataURL('image/jpeg', quality)
Creates a JPG image. Optionally, you can provide a quality in the range from 0 to 1, with one being the best quality and with 0 almost not recognizable but small in file size.
Once you have generated a data URI from you canvas, you are able to use it as the source of any<image>
or put it into a hyper link with adownload attribute to save it to disc, for example.
You can also create aBlob
from the canvas.
canvas.toBlob(callback, type, encoderOptions)
Creates a
Blob
object representing the image contained in the canvas.