Обработка пикселей с помощью холста
До сих пор мы не рассматривали фактические пиксели нашего холста. С помощью объекта ImageData вы можете напрямую читать и записывать массив данных для обработки данных пикселей. Мы также рассмотрим, как можно управлять сглаживанием изображения (антиалиасингом) и как сохранять изображения с вашего холста.
Объект ImageData
Объект ImageData представляет собой данные пикселей для области объекта холста. Он содержит следующие атрибуты только для чтения:
width-
Ширина изображения в пикселях.
height-
Высота изображения в пикселях.
data-
Uint8ClampedArray, представляющий одномерный массив, содержащий данные в порядке RGBA, со значениями целых чисел от0до255(включительно).
Свойство data возвращает Uint8ClampedArray, к которому можно обратиться, чтобы посмотреть на исходные данные пикселей; каждый пиксель представлен четырьмя значениями одного байта (красный, зелёный, синий и альфа в этом порядке; то есть, в формате «RGBA»). Каждый цветовой компонент представлен целым числом от 0 до 255. Каждый компонент получает последовательный индекс в массиве, при этом красный компонент пикселя в левом верхнем углу находится на индексе 0 в массиве. Затем пиксели продвигаются слева направо, а затем вниз по всему массиву.
Uint8ClampedArray содержит height × width × 4 байта данных, со значениями индексов от 0 до (height × width × 4) - 1.
Например, чтобы прочитать значение синего компонента пикселя в столбце 200, строке 50 на изображении, нужно сделать следующее:
const blueComponent = imageData.data[50 * (imageData.width * 4) + 200 * 4 + 2];
Если заданы координаты (X и Y), вы можете сделать что-то вроде этого:
const xCoord = 50;
const yCoord = 100;
const canvasWidth = 1024;
const getColorIndicesForCoord = (x, y, width) => {
const red = y * (width * 4) + x * 4;
return [red, red + 1, red + 2, red + 3];
};
const colorIndices = getColorIndicesForCoord(xCoord, yCoord, canvasWidth);
const [redIndex, greenIndex, blueIndex, alphaIndex] = colorIndices;
Вы также можете получить размер массива пикселей в байтах, прочитав атрибут Uint8ClampedArray.length:
const numBytes = imageData.data.length;
Создание объекта ImageData
Чтобы создать новый пустой объект ImageData , используйте метод createImageData(). Существует два варианта метода createImageData():
const myImageData = ctx.createImageData(width, height);
Это создаёт новый объект ImageData с указанными размерами. Все пиксели предварительно установлены в прозрачный чёрный цвет (все нули, т.е. rgb(0 0 0 / 0%)).
Вы также можете создать новый объект ImageData с теми же размерами, что и объект, указанный в anotherImageData. Пиксели нового объекта предварительно установлены в прозрачный чёрный цвет. Это не копирует данные изображения!
const myImageData = ctx.createImageData(anotherImageData);
Получение данных пикселей для контекста
Чтобы получить объект ImageData , содержащий копию данных пикселей для контекста холста, вы можете использовать метод getImageData():
const myImageData = ctx.getImageData(left, top, width, height);
Этот метод возвращает объект ImageData , представляющий данные пикселей для области холста, углы которой представлены точками (left, top), (left+width, top), (left, top+height), и (left+width, top+height). Координаты заданы в единицах пространства координат холста.
Примечание: Любые пиксели за пределами холста возвращаются как прозрачный чёрный цвет в результирующем объекте ImageData.
Этот метод также продемонстрирован в статье Обработка видео с помощью холста.
Выбор цвета
В этом примере мы используем метод getImageData() для отображения цвета под курсором мыши. Для этого нам нужна текущая позиция мыши, затем мы ищем данные пикселей в этой позиции в массиве пикселей, который предоставляет нам getImageData(). Наконец, мы используем данные массива для установки цвета фона и текста в <div> для отображения цвета. Щелчок по изображению выполнит ту же операцию, но запомнит выбранный цвет.
const img = new Image();
img.crossOrigin = "anonymous";
img.src = "./assets/rhino.jpg";
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
img.addEventListener("load", () => {
ctx.drawImage(img, 0, 0);
img.style.display = "none";
});
const hoveredColor = document.getElementById("hovered-color");
const selectedColor = document.getElementById("selected-color");
function pick(event, destination) {
const bounding = canvas.getBoundingClientRect();
const x = event.clientX - bounding.left;
const y = event.clientY - bounding.top;
const pixel = ctx.getImageData(x, y, 1, 1);
const data = pixel.data;
const rgbColor = `rgb(${data[0]} ${data[1]} ${data[2]} / ${data[3] / 255})`;
destination.style.background = rgbColor;
destination.textContent = rgbColor;
return rgbColor;
}
canvas.addEventListener("mousemove", (event) => pick(event, hoveredColor));
canvas.addEventListener("click", (event) => pick(event, selectedColor));
Использование кода продемонстрировано в следующем интерактивном примере:
Также см. исходный код — HTML, JavaScript.
Отрисовка данных пикселей в контексте
Вы можете использовать метод putImageData() для отрисовки данных пикселей в контексте:
ctx.putImageData(myImageData, dx, dy);
Параметры dx и dy указывают координаты устройства внутри контекста, в которых нужно нарисовать верхний левый угол данных пикселей, которые вы хотите отрисовать.
Например, чтобы нарисовать всё изображение, представленное объектом myImageData в верхнем левом углу контекста, можно сделать следующее:
ctx.putImageData(myImageData, 0, 0);
Сепия и инвертирование цветов
В этом примере мы перебираем все пиксели, чтобы изменить их значения, затем мы помещаем изменённый массив пикселей обратно на холст с помощью putImageData(). Функция инвертирования вычитает каждый цвет из максимального значения 255. Функция сепии использует среднее значение красного, зелёного и синего. Вы также можете использовать взвешенное среднее, заданное формулой x = 0.299r + 0.587g + 0.114b, например. См. Сепия в Википедии для получения дополнительной информации.
const img = new Image();
img.crossOrigin = "anonymous";
img.src = "./assets/rhino.jpg";
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
img.onload = () => {
ctx.drawImage(img, 0, 0);
};
const original = () => {
ctx.drawImage(img, 0, 0);
};
const invert = () => {
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let 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);
};
const grayscale = () => {
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const 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);
};
const inputs = document.querySelectorAll("[name=color]");
for (const input of inputs) {
input.addEventListener("change", (evt) => {
switch (evt.target.value) {
case "inverted":
return invert();
case "grayscale":
return grayscale();
default:
return original();
}
});
}
Использование кода продемонстрировано в следующем интерактивном примере:
Также см. исходный код — HTML, JavaScript.
Масштабирование и антиалиасинг
С помощью метода drawImage(), второго холста и свойства imageSmoothingEnabled мы можем приблизиться к изображению и увидеть детали. Третий холст без imageSmoothingEnabled также нарисован, чтобы обеспечить сравнение.
Мы получаем позицию мыши и обрезаем изображение на 5 пикселей слева и сверху до 5 пикселей справа и снизу. Затем мы копируем это на другой холст и изменяем размер изображения до желаемого размера. На масштабируемом холсте мы изменяем размер обрезки из 10×10 пикселей исходного холста до 200×200.
zoomCtx.drawImage( canvas, Math.min(Math.max(0, x - 5), img.width - 10), Math.min(Math.max(0, y - 5), img.height - 10), 10, 10, 0, 0, 200, 200, );
Пример масштабирования:
const img = new Image();
img.crossOrigin = "anonymous";
img.src = "./assets/rhino.jpg";
img.onload = () => {
draw(this);
};
function draw(img) {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
const smoothedZoomCtx = document
.getElementById("smoothed-zoom")
.getContext("2d");
smoothedZoomCtx.imageSmoothingEnabled = true;
const pixelatedZoomCtx = document
.getElementById("pixelated-zoom")
.getContext("2d");
pixelatedZoomCtx.imageSmoothingEnabled = false;
const zoom = (ctx, x, y) => {
ctx.drawImage(
canvas,
Math.min(Math.max(0, x - 5), img.width - 10),
Math.min(Math.max(0, y - 5), img.height - 10),
10,
10,
0,
0,
200,
200,
);
};
canvas.addEventListener("mousemove", (event) => {
const x = event.layerX;
const y = event.layerY;
zoom(smoothedZoomCtx, x, y);
zoom(pixelatedZoomCtx, x, y);
});
}
Использование кода продемонстрировано в следующем интерактивном примере:
Также см. исходный код — HTML, JavaScript.
Сохранение изображений
Элемент HTMLCanvasElement предоставляет метод toDataURL(), который полезен при сохранении изображений. Он возвращает URL-адрес данных с представлением изображения в формате, указанном параметром type (по умолчанию PNG). Возвращаемое изображение имеет разрешение 96 dpi.
Примечание: Обратите внимание, что если холст содержит пиксели, полученные из другого источника без использования CORS, холст считается загрязнённым и его содержимое больше нельзя читать и сохранять. См. Безопасность и загрязнённые холсты.
canvas.toDataURL('image/png')-
Значение по умолчанию. Создаёт изображение PNG.
canvas.toDataURL('image/jpeg', quality)-
Создаёт изображение JPG. Дополнительно, вы можете указать качество в диапазоне от 0 до 1, где 1 — наилучшее качество, а 0 — практически не различимое изображение, но с небольшим размером файла.
После создания URL-адреса данных с вашего холста вы можете использовать его в качестве источника для любого <img> или поместить его в гиперссылку с атрибутом скачивания, чтобы сохранить его на диск, например.
Вы также можете создать Blob из холста.
canvas.toBlob(callback, type, encoderOptions)-
Создаёт объект
Blob, представляющий изображение, содержащееся в холсте.
См. также
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas