Отключить интерполяцию при масштабировании <canvas>

ПРИМЕЧАНИЕ. Это связано с тем, как визуализируются существующие элементы холста при масштабировании, не, чтобы сделать, как линии или графика отображаются на поверхности холста. Другими словами, это имеет все, что связано с интерполяцией масштабированных элементов, и не имеет никакого отношения к сглаживанию графики, нарисованной на холсте. Меня не интересует, как браузер рисует строки; Я забочусь о том, как браузер отображает сам элемент холста при его масштабировании.


Есть ли свойство canvas или параметр браузера, который я могу изменить программно, чтобы отключить интерполяцию при масштабировании элементов <canvas>? Кросс-браузерное решение идеально, но не обязательно; Основными задачами являются браузеры на основе Webkit. Производительность очень важна.

Этот вопрос наиболее похож, но недостаточно иллюстрирует проблему. Для чего это стоит, я пробовал image-rendering: -webkit-optimize-contrast безрезультатно.

Приложение будет "ретро" 8-битной стилизованной игрой, написанной в HTML5 + JS, чтобы было ясно, что мне нужно.


Чтобы проиллюстрировать это, вот пример. (живая версия)

Предположим, что у меня есть холст 21x21...

<canvas id='b' width='21' height='21'></canvas>

... который имеет css, что делает элемент в 5 раз больше (105x105):

canvas { border: 5px solid #ddd; }
canvas#b { width: 105px; height: 105px; } /* 5 * 21 = 105 */

Я рисую простой "X" на холсте так:

$('canvas').each(function () {
    var ctx = this.getContext("2d");
    ctx.moveTo(0,0);
    ctx.lineTo(21,21);
    ctx.moveTo(0,21);
    ctx.lineTo(21,0);
    ctx.stroke();
});

Изображение слева - это то, что делает Chromium (14.0). Изображение справа - это то, что я хочу (рисованный для иллюстративных целей).

Chrome interpolates scaled canvas elementsA non-interpolated version

Ответ 1

Последнее обновление: 2014-09-12

Есть ли свойство canvas или параметр браузера, который я могу изменить программно, чтобы отключить интерполяцию при масштабировании элементов?

Ответ может быть в какой-то день. На данный момент вам придётся прибегнуть к хакерам, чтобы получить то, что вы хотите.


image-rendering

Рабочий проект CSS3 описывает новое свойство image-rendering, которое должно делать то, что я хочу:

Свойство image-rendering предоставляет подсказку пользователю-агенту о том, какие аспекты изображения наиболее важны для сохранения при масштабировании изображения, чтобы помочь пользовательскому агенту в выборе подходящего алгоритма масштабирования.

В спецификации описываются три принятых значения: auto, crisp-edges и pixelated.

некачественно:

При масштабировании изображения нужно использовать "ближайший сосед" или аналогичный алгоритм, чтобы изображение просто состояло из очень больших пикселей. При уменьшении масштаба это то же самое, что и авто.

Стандарт? Кросс-браузер?

Поскольку это всего лишь рабочий проект, нет никакой гарантии, что это станет стандартным. Поддержка браузера в настоящее время является пятнистой, в лучшем случае.

В Mozilla Developer Network довольно подробная страница, посвященная текущему уровню техники, которую я настоятельно рекомендую читать.

Сначала разработчики Webkit выбрали условно реализовать это как -webkit-optimize-contrast, но Chromium/Chrome, похоже, не использует версию Webkit, который реализует это.

Обновление: 2014-09-12

Теперь Chrome 38 поддерживает image-rendering: pixelated!

В Firefox есть отчет об ошибке, чтобы получить image-rendering: pixelated, но -moz-crisp-edges работает пока.

Решение?

Самое кросс-платформенное, только CSS-решение до сих пор:

canvas {
  image-rendering: optimizeSpeed;             /* Older versions of FF          */
  image-rendering: -moz-crisp-edges;          /* FF 6.0+                       */
  image-rendering: -webkit-optimize-contrast; /* Safari                        */
  image-rendering: -o-crisp-edges;            /* OS X & Windows Opera (12.02+) */
  image-rendering: pixelated;                 /* Awesome future-browsers       */
  -ms-interpolation-mode: nearest-neighbor;   /* IE                            */
}

К сожалению, это не будет работать на всех основных платформах HTML5 (особенно в Chrome).

Конечно, можно вручную масштабировать изображения с использованием интерполяции ближайшего соседа на поверхности холста с высоким разрешением в javascript или даже с предварительным масштабированием изображений на стороне сервера, но в моем случае это будет дорого стоить, поэтому это не жизнеспособный вариант.

ImpactJS использует технику предварительного масштабирования текстуры, чтобы обойти все это FUD. Разработчик Impact, Dominic Szablewski, написал очень подробную статью об этом (он даже цитировал этот вопрос в своих исследованиях).

См. ответ Саймона для решения на основе холста, которое зависит от свойства imageSmoothingEnabled (недоступно в старых браузерах, но проще, чем предварительное масштабирование и довольно широко -Поддерживаемые).

Демо-версия

Если вы хотите проверить свойства CSS, обсуждаемые в статье MDN, на элементах canvas, я сделал эту скрипту который должен отображать что-то вроде этого, размытое или нет, в зависимости от вашего браузера: a 4:1 (64x64 to 256x256) image of an isometric pixel-art style TV

Ответ 2

Новый ответ 7/31/2012

Это, наконец, в спецификации canvas!

В спецификации недавно добавлено свойство с именем imageSmoothingEnabled, которое по умолчанию равно true и определяет, будут ли образы, нарисованные на нецелочисленных координатах или нарисованных масштабированных, использовать более плавный алгоритм. Если он установлен на false, то используется ближайший сосед, создающий менее плавное изображение и вместо этого просто создавая большие пиксели.

Сглаживание изображений только недавно было добавлено в спецификацию холста и не поддерживается всеми браузерами, но некоторые браузеры внедрили версии этого префикса. В контексте существует mozImageSmoothingEnabled в Firefox и webkitImageSmoothingEnabled в браузерах Chrome и Safari, а установка их в false приведет к сбою в сглаживании. К сожалению, на момент написания статьи IE9 и Opera не реализовали это свойство, префикс поставщика или иначе.


Предварительный просмотр: JSFiddle

Результат:

enter image description here

Ответ 3

Изменить 7/31/2012 - эта функциональность теперь находится в спецификации холста! См. Отдельный ответ здесь:

fooobar.com/questions/25047/...

Старый ответ ниже для потомков.


В зависимости от желаемого эффекта у вас есть это как один из вариантов:

var can = document.getElementById('b');
var ctx = can.getContext('2d');
ctx.scale(5,5);
$('canvas').each(function () {
    var ctx = this.getContext("2d");
    ctx.moveTo(0,0);
    ctx.lineTo(21,21);
    ctx.moveTo(0,21);
    ctx.lineTo(21,0);
    ctx.stroke();
});

http://jsfiddle.net/wa95p/

Что создает это:

enter image description here

Наверное, не то, что вы хотите. Но если бы вы просто искали нулевой размытие, тогда это был бы билет, поэтому я предлагаю его на всякий случай.

Более сложным вариантом является использование манипуляции с пикселями и самостоятельная запись алгоритма для работы. Каждый пиксель первого изображения становится 5x5 блоком пикселей на новом изображении. Это не было бы слишком сложно сделать с imagedata.

Но Canvas и CSS сами по себе не помогут вам масштабировать друг друга с точным желаемым эффектом.

Ответ 4

В google chrome шаблоны изображения холста не интерполируются.

Вот рабочий пример, отредактированный из ответа namuol http://jsfiddle.net/pGs4f/

ctx.scale(4, 4);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fillRect(0, 0, 64, 64);

Ответ 5

Обходное решение Saviski explicated здесь является многообещающим, потому что оно работает на:

  • Chrome 22.0.1229.79 Mac OS X 10.6.8
  • Chrome 22.0.1229.79 м Windows 7
  • Chromium 18.0.1025.168 (Developer Build 134367 Linux) Ubuntu 11.10
  • Firefox 3.6.25 Windows 7

Но не работает в следующем, но тот же эффект может быть достигнут с помощью CSS-рендеринга изображений:

  • Firefox 15.0.1 Mac OS X 10.6.8 (рендеринг изображений: -moz-crisp-edge работает в this)
  • Opera 12.02 Mac OS X 10.6.8 (рендеринг изображений: -o-crisp-edge работает в this)
  • Opera 12.02 Windows 7 (рендеринг изображений: -o-crisp-edge работает в this)

Проблемными являются эти проблемы, поскольку ctx.XXXImageSmoothingEnabled не работает, а рендеринг изображений не работает:

  • Safari 5.1.7 Mac OS X 10.6.8. (рендеринг изображений: -webkit-optimize-contrast НЕ работает)
  • Safari 5.1.7 Windows 7 (рендеринг изображений: -webkit-optimize-contrast НЕ работает)
  • IE 9 Windows 7 (-ms-интерполяция-режим: ближайший сосед НЕ работает)