Spec-Zone.ru › Web APIs

Обработка пикселей с помощью холста

  • Предыдущая
  • Следующая

До сих пор мы не рассматривали фактические пиксели нашего холста. С помощью объекта 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 , представляющий изображение, содержащееся в холсте.

См. также

  • ImageData
  • Обработка видео с помощью холста
  • Загрузка изображений, сгенерированных API Canvas, с помощью toBlob
  • Учебники по HTML5 Canvas
  • Предыдущая
  • Следующая

© 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API