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>
中設置背景色和文字去顯示該色。
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 by myImageData
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 using putImageData(). 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 formula x = 0.299r + 0.587g + 0.114b
, for example. See Grayscale on Wikipedia for more information.
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 the drawImage()
method, a second canvas and the imageSmoothingEnabled
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 the imageSmoothingEnabled
property (which needs prefixes for different browsers).
Zoom example
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);
}
儲存圖片
The HTMLCanvasElement
provides a toDataURL()
method, which is useful when saving images. It returns a data URI containing a representation of the image in the format specified by the type
parameter (defaults to PNG). 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 a download attribute to save it to disc, for example.
You can also create a Blob
from the canvas.
canvas.toBlob(callback, type, encoderOptions)
-
Creates a
Blob
object representing the image contained in the canvas.