Tìm kiếm trang web

javascript - Cách tốt nhất để đặt một điểm ảnh đơn lẻ trong canvas HTML5 là gì?


HTML5 Canvas không có phương thức nào để thiết lập rõ ràng một điểm ảnh đơn lẻ.

Có thể thiết lập một điểm ảnh bằng cách sử dụng một đường ngắn, nhưng sau đó khử răng cưa và đầu mút đường thẳng có thể can thiệp.

Một cách khác có thể là tạo một đối tượng nhỏ ImageData và sử dụng:

context.putImageData(data, x, y)

đặt nó vào đúng chỗ.

Ai có thể mô tả một cách hiệu quả và đáng tin cậy để làm điều này không?

Câu trả lời #1

Có hai ứng cử viên xuất sắc nhất:

  1. Tạo dữ liệu hình ảnh 1×1, đặt màu, và putImageData tại vị trí:

    var id = myContext.createImageData(1,1); // only do this once per page
    var d  = id.data;                        // only do this once per page
    d[0]   = r;
    d[1]   = g;
    d[2]   = b;
    d[3]   = a;
    myContext.putImageData( id, x, y );     
    
  2. Sử dụng fillRect() để vẽ một điểm ảnh (không nên có vấn đề răng cưa):

    ctx.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
    ctx.fillRect( x, y, 1, 1 );
    

Bạn có thể kiểm tra tốc độ của chúng tại đây: http://jsperf.com/setting-canvas-pixel/9 hoặc tại đây https://www.measurethat.net/Benchmarks/Show/1664/1

Tôi khuyên bạn nên kiểm tra trên các trình duyệt bạn quan tâm để đạt tốc độ tối đa. Tính đến tháng 7 năm 2017, fillRect() nhanh hơn 5-6 lần trên Firefox v54 và Chrome v59 (Win7x64).

Những lựa chọn khác, ngớ ngẩn hơn là:

  • sử dụng getImageData()/putImageData() trên toàn bộ canvas; điều này chậm hơn khoảng 100× so với các lựa chọn khác.

  • tạo một hình ảnh tùy chỉnh sử dụng URL dữ liệu và sử dụng drawImage() để hiển thị nó:

    var img = new Image;
    img.src = "data:image/png;base64," + myPNGEncoder(r,g,b,a);
    // Writing the PNGEncoder is left as an exercise for the reader
    
  • tạo một hình ảnh hoặc canvas khác chứa tất cả các pixel bạn muốn và sử dụng drawImage() để vẽ chỉ những pixel bạn muốn sang. Điều này có thể rất nhanh, nhưng có hạn chế là bạn cần tính toán trước các pixel bạn cần.

Lưu ý rằng các bài kiểm tra của tôi không cố gắng lưu và khôi phục ngữ cảnh canvas fillStyle; điều này sẽ làm chậm hiệu suất của fillRect(). Cũng lưu ý rằng tôi không bắt đầu với một bảng vẽ sạch hoặc kiểm tra cùng một tập hợp pixel cho mỗi bài kiểm tra.

Câu trả lời #2

Một phương pháp chưa được đề cập là sử dụng getImageData và sau đó là putImageData.
Phương pháp này tốt khi bạn muốn vẽ nhiều trong một lần, nhanh.
http://next.plnkr.co/edit/mfNyalsAR2MWkccr

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
var id = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
var pixels = id.data;

var x = Math.floor(Math.random() * canvasWidth);
var y = Math.floor(Math.random() * canvasHeight);
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
var off = (y * id.width + x) * 4;
pixels[off] = r;
pixels[off + 1] = g;
pixels[off + 2] = b;
pixels[off + 3] = 255;

ctx.putImageData(id, 0, 0);

Câu trả lời #3

Tôi đã không nghĩ tới fillRect(), nhưng những câu trả lời đã thúc đẩy tôi so sánh hiệu năng của nó với putImage().

Đặt 100.000 pixel có màu ngẫu nhiên ở các vị trí ngẫu nhiên, với Chrome 9.0.597.84 trên một MacBook Pro (cũ), mất chưa đến 100ms với putImage(), nhưng gần 900ms khi sử dụng fillRect(). (Mã đo hiệu năng tại http://pastebin.com/4ijVKJcC).

Nếu thay vào đó tôi chọn một màu duy nhất bên ngoài các vòng lặp và chỉ vẽ màu đó tại các vị trí ngẫu nhiên, putImage() mất 59ms so với 102ms cho fillRect().

Có vẻ như chi phí chung của việc tạo và phân tích cú pháp một định nghĩa màu CSS theo cú pháp rgb(...) là nguyên nhân chính gây ra hầu hết sự khác biệt.

Việc đưa trực tiếp các giá trị RGB thô vào một khối ImageData, mặt khác, không yêu cầu xử lý chuỗi hay phân tích cú pháp.

Câu trả lời #4

function setPixel(imageData, x, y, r, g, b, a) {
    var index = 4 * (x + y * imageData.width);
    imageData.data[index+0] = r;
    imageData.data[index+1] = g;
    imageData.data[index+2] = b;
    imageData.data[index+3] = a;
}

Bài viết liên quan