Пиксельная манипуляция с холстом

До сих пор мы не смотрели на фактические пиксели нашего объекта canvas (далее "холст"). С объектом ImageData вы можете напрямую читать и писать массив данных для управления пиксельными данными. Мы также рассмотрим, как можно сгладить сглаживание изображения (сглаживание) и как сохранить изображения с вашего холста.

Объект ImageData

Объект ImageData представляет базовые пиксельные данные области объекта холста. Он содержит следующие атрибуты только для чтения:

width

Ширина изображения в пикселях.

height

Высота изображения в пикселях.

data

A Uint8ClampedArray представляет собой одномерный массив, содержащий данные в порядке RGBA, с целыми значениями от 0 до 255 (в комплекте).

Свойство data возвращает Uint8ClampedArray, к которому можно получить доступ, чтобы посмотреть на необработанные пиксельные данные; каждый пиксель представлен четырьмя однобайтовыми значениями (красный, зелёный, синий и альфа в этом порядке, то есть формат «RGBA»). Каждый компонент цвета представлен целым числом от 0 до 255. Каждому компоненту присваивается последовательный индекс внутри массива, причём красный компонент верхнего левого пикселя находится в индексе 0 внутри массива. Затем пиксели идут слева направо, затем вниз, по всему массиву.

Uint8ClampedArray содержит высоту × ширину × 4 байта данных, значения индекса варьируются от 0 до (высота × ширина × 4) -1.

Например, чтобы прочитать значение синего компонента из пикселя в столбце 200, строка 50 на изображении, вы должны сделать следующее:

js
blueComponent = imageData.data[50 * (imageData.width * 4) + 200 * 4 + 2];

Вы можете получить доступ к размеру массива пикселей в байтах, прочитав атрибут Uint8ClampedArray.length:

js
var numBytes = imageData.data.length;

Создание объекта ImageData

Чтобы создать новый пустой объект ImageData , вы должны использовать метод createImageData () (en-US). Существуют две версии метода createImageData() :

js
var myImageData = ctx.createImageData(width, height);

Это создаёт новый объект ImageData с указанными параметрами. Все пиксели заданы прозрачным черным.

Вы также можете создать новый объект ImageData ImageData с теми же размерами, что и объект, заданный anotherImageData . Все пиксели нового объекта установлены на прозрачный чёрный. Это не копирует данные изображения!

js
var myImageData = ctx.createImageData(anotherImageData);

Получение пиксельных данных для контекста

Чтобы получить объект ImageData , содержащий копию пиксельных данных для контекста холста, вы можете использовать метод getImageData() :

js
var myImageData = ctx.getImageData(left, top, width, height);

Этот метод возвращает объект ImageData , представляющий пиксельные данные для области холста, углы которого представлены точками (left , top), (left+width , top), (left , top+height) и (left+width , top+height). Координаты задаются в единицах пространства координат холста.

Примечание: Любые пиксели за пределами холста возвращаются как прозрачный чёрный цвет в результирующий объект ImageData .

Этот метод также показан в статье Manipulating video using canvas (en-US).

Выбор цвета

В этом примере мы используем метод getImageData() для отображения цвета под курсором мыши. Для этого нам нужна текущая позиция мыши с layerX и layerY, затем мы просматриваем пиксельные данные в этой позиции в массиве пикселей, который предоставляет нам getImageData(). Наконец, мы используем данные массива для установки цвета фона и текста <div> для отображения цвета.

js
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);

Отображение пиксельных данных в контекст

Вы можете использовать метод putImageData() для рисования пиксельных данных в контексте:

js
ctx.putImageData(myImageData, dx, dy);

Параметры dx и dy указывают координаты устройства в контексте, в котором будет отображаться верхний левый угол пиксельных данных, которые вы хотите нарисовать.

Например, чтобы нарисовать все изображение, представленное myImageData, в верхнем левом углу контекста, вы можете просто сделать следующее:

js
ctx.putImageData(myImageData, 0, 0);

Оттенки серого цвета и инвертирование цветов

В этом примере мы перебираем все пиксели для изменения их значений, а затем помещаем модифицированный массив пикселей обратно в canvas с помощью putImageData(). Функция инвертирования просто вычитает каждый цвет из максимального значения 255. Функция оттенков серого просто использует среднее значение красного, зелёного и синего. Вы также можете использовать средневзвешенное значение, заданное формулой x = 0.299r + 0.587g + 0.114b, например. Для дополнительной информации см. Grayscale в Википедии.

js
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);
}

Масштабирование и сглаживание

С помощью метода drawImage (), второго холста и свойства imageSmoothingEnabled (en-US) мы способны увеличить изображение и посмотреть его более детально.

Мы получаем положение мыши и обрезаем изображение на 5 пикселей левее и выше и на 5 пикселей правее и ниже положения мыши. Затем мы копируем его на другой холст и изменяем размер изображения до размера, который мы хотим. При масштабировании мы изменяем холст с исходного размера 10×10 пикселей до 200×200.

js
zoomctx.drawImage(
  canvas,
  Math.abs(x - 5),
  Math.abs(y - 5),
  10,
  10,
  0,
  0,
  200,
  200,
);

Поскольку по умолчанию включено сглаживание, мы можем захотеть отключить сглаживание, чтобы увидеть чёткие пиксели. Вы можете переключить флажок, чтобы увидеть эффект свойства imageSmoothingEnabled (которому нужны префиксы для разных браузеров).

js
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);
}

Сохранение изображений

HTMLCanvasElement предоставляет метод toDataURL(), который полезен при сохранении изображений. Он возвращает data URI (en-US), содержащий представление изображения в формате, заданном параметром type (по умолчанию используется в PNG ). Возвращаемое изображение имеет разрешение 96 точек на дюйм.

Примечание:

Имейте в виду, что если холст содержит пиксели, полученные из другого origin без использования CORS, холст будет испорчен, и его содержимое больше не будет считываться и сохраняться. Смотрите Безопасность и испорченные холсты

canvas.toDataURL('image/png')

Настройки по умолчанию. Создаёт изображение в формате PNG.

canvas.toDataURL('image/jpeg', quality)

Создаёт изображение в формате JPG. Дополнительно вы можете задать параметр "качество" (quality) в диапазоне от 0 до 1, причём единица задаёт лучшее качество и 0 - почти не распознаваемый, но небольшой по размеру файл.

После того как вы создали URI данные из своего холста, вы можете использовать его как источник любого <image> или поместить его в гиперссылку с download attribute, чтобы сохранить его на диске, например.

Вы также можете создать Blob из холста.

canvas.toBlob(callback, type, encoderOptions)

Создаёт объект Blob, представляющий изображение, содержащееся в холсте.

Смотрите также