Để thay đổi màu của hình ảnh đã vẽ trên canvas, bạn phải thao tác trực tiếp trên pixel data qua getImageData(), sửa đổi mảng pixel, sau đó putImageData().
Quy trình
- Vẽ hình ảnh nguồn lên canvas tạm (hoặc canvas chính).
- Lấy
ImageDatabằnggetImageData(0, 0, width, height). - Duyệt mảng
data(RGBA: 4 giá trị/pixel) và sửa đổi. - Đặt lại dữ liệu bằng
putImageData().
Ví dụ: Thay đổi màu theo kênh (Colorize/Tint)
function colorizeImage(img, r, g, b) {
// Tạo canvas tạm
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
// Vẽ ảnh gốc
ctx.drawImage(img, 0, 0);
// Lấy pixel data
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = imageData.data;
// Duyệt từng pixel (4 giá trị: R, G, B, A)
for (var i = 0; i < data.length; i += 4) {
// Kết hợp màu gốc với màu mới (OR bitwise - effect "tint")
// Hoặc thay thế hoàn toàn: data[i] = r; data[i+1] = g; data[i+2] = b;
// Ví dụ: Tint - giữ độ sáng (luminance) nhưng đổi hue
var gray = (data[i] * 0.299 + data[i+1] * 0.587 + data[i+2] * 0.114);
data[i] = Math.min(255, gray * r / 255); // R
data[i+1] = Math.min(255, gray * g / 255); // G
data[i+2] = Math.min(255, gray * b / 255); // B
// data[i+3] = alpha (giữ nguyên)
}
// Vẽ lại
ctx.putImageData(imageData, 0, 0);
return canvas; // Trả về canvas đã xử lý
}
// Sử dụng
var img = new Image();
img.crossOrigin = 'anonymous'; // Cần nếu cross-origin
img.onload = function() {
var newCanvas = colorizeImage(img, 255, 0, 0); // Tint đỏ
document.body.appendChild(newCanvas);
};
img.src = 'image.png';
Các hiệu ứng pixel phổ biến
| Hiệu ứng | Công thức (trong loop i+=4) |
|---|---|
| Grayscale | var gray = 0.299*R + 0.587*G + 0.114*B; R=G=B=gray; |
| Invert (Negative) | R=255-R; G=255-G; B=255-B; |
| Sepia | R=min(255,0.393R+0.769G+0.189B); G=min(255,0.349R+0.686G+0.168B); B=min(255,0.272R+0.534G+0.131B); |
| Brightness | R=R*factor; G=G*factor; B=B*factor; (factor > 1 sáng hơn) |
| Threshold (Đen trắng) | var avg=(R+G+B)/3; R=G=B=avg>128?255:0; |
| Tint (Colorize) | R=R*tintR/255; G=G*tintG/255; B=B*tintB/255; |
Hiệu năng & Tối ưu
- Duyệt pixel JS chậm trên ảnh lớn (>1MP). Dùng Web Workers hoặc WebGL shaders.
Uint32Arrayview trênimageData.data.bufferđể xử lý 4 byte cùng lúc (fast).- OffscreenCanvas + Worker cho xử lý nền.
// Fast pixel manipulation with Uint32Array (little-endian: BGRA hoặc RGBA tùy platform)
var buf = new Uint32Array(imageData.data.buffer);
for (var i = 0; i < buf.length; i++) {
var pixel = buf[i];
// Extract components (platform dependent order!)
// Thường là BGRA trên little-endian
var b = pixel & 0xFF;
var g = (pixel >> 8) & 0xFF;
var r = (pixel >> 16) & 0xFF;
var a = (pixel >> 24) & 0xFF;
// Modify...
buf[i] = (a << 24) | (r << 16) | (g << 8) | b;
}
Lưu ý CORS (Quan trọng)
Nếu ảnh từ domain khác, phải có header CORS và set img.crossOrigin = 'anonymous' trước khi gán src, nếu không getImageData() sẽ ném SecurityError (tainted canvas).