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

126

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


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

Этот вопрос наиболее похож, но недостаточно иллюстрирует проблему. Как бы то ни было, я безуспешно пытался image-rendering: -webkit-optimize-contrast.

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


Для иллюстрации приведу пример. ( живая версия )

Допустим, у меня холст 21х21 ...

<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 */

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

$('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 интерполирует масштабированные элементы холста Версия без интерполяции

namuol
источник
1
В некоторой степени связано, хотя и не идентично: stackoverflow.com/questions/195262/…
HostileFork говорит, что не доверяйте SE
1
Я считаю, что этот вопрос относится к функциям рисования линий, которые рисуют сглаженные линии. Я говорю о том, как элементы холста с измененным размером по умолчанию отображаются с интерполяцией.
намуол
Да, извините ... Я сначала подумал, что вопросы такие же, а потом сразу заметил свою ошибку. : - / Что, если бы вы попробовали использовать фильтр пикселизации jsmanipulate? joelb.me/jsmanipulate
HostileFork говорит, что не доверяйте SE
1
Это скорее фильтр изображений, предназначенный для фотографий.
namuol
Да, но если он преобразует ваше левое изображение в достаточно хорошую версию вашего правого изображения, чтобы сделать вас счастливыми, тогда он может предоставить кросс-браузерное решение (учитывая, что это не выглядит слишком многообещающим, чтобы найти флаг для этого режима рисования, универсального или другого в WebKit). Будет ли это жизнеспособной заменой, зависит от проблемы, которую вы пытаетесь решить ... действительно ли вам нужно рисование дискретных пикселей или вы просто пытаетесь обеспечить пикселирование результата с определенной степенью детализации.
HostileFork говорит, что не доверяйте SE

Ответы:

126

Последнее обновление: 12.09.2014

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

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


image-rendering

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

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

Спецификация описывает одно из следующих значений: auto, crisp-edges, и pixelated.

пикселизированный:

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

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

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

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

Первоначально разработчики 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, Доминик Саблевски, написал об этом очень подробную статью (в конечном итоге он даже процитировал этот вопрос в своем исследовании).

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

Живая демонстрация

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

namuol
источник
Рендеринг изображений работает где-то еще, но не в браузерах webkit. Я очень надеюсь, что сотрудники Chrome / Chromium / Safari хорошо понимают, что означает «переход на интерполяцию EPX в ситуациях с низкой нагрузкой». Когда я впервые прочитал это предложение, я подумал, что оптимизация-контраст позволяет создавать новые цвета при низкой загрузке, но НЕТ: en.wikipedia.org/wiki/…
Тимо Кяхкёнен
2
Opera 12.02 на Mac и Windows поддерживает рендеринг изображений: -o-crisp- edge ( jsfiddle.net/VAXrL/21 ), поэтому вы можете добавить его в свой ответ.
Timo Kähkönen
Должен ли он работать, когда я увеличиваю масштаб браузера? Я тестировал рисование с помощью FF 22, Chrome 27 и IE 10 в Windows 7, когда масштаб браузера составляет 150%, а результат во всех браузерах размыт. Когда я удваиваю ширину и высоту холста и устанавливаю исходные ширину и высоту с помощью CSS, он рисует четкие линии.
pablo 09
Это хороший вопрос. Я не уверен, есть ли спецификация для определяемого пользователем поведения масштабирования.
namuol
1
Я (из настоящего будущего!) Использую Chrome 78. Я вижу «рендеринг изображения: пиксельный»; работает. Похоже, это было добавлено в Chrome 41 в 2015 году: developers.google.com/web/updates/2015/01/pixelated
MrColes
61

Новый ответ 31.07.2012

Это наконец-то в спецификации холста!

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

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


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

Результат:

введите описание изображения здесь

Саймон Саррис
источник
1
К сожалению, похоже, что и это пока не работает. Может я что то упускаю? Вот тест: jsfiddle.net/VAXrL/187
намуол
12
вам нужно масштабировать с помощью API холста, чтобы он работал, вы не можете масштабировать с помощью CSS, и API холста повлияет на него! Примерно так
Саймон Саррис,
1
Но суть проблемы не в холсте. Речь идет о том, как браузер отображает масштабированные изображения, включая элементы <canvas>.
намуол 04
4
Если вы масштабируете с помощью CSS, решение namuol будет работать. Если вы вручную масштабируете изображение на холсте, решение Саймона работает. Приятно, что есть решения для обоих случаев, поэтому спасибо вам обоим!
Шон Леброн,
Для IE 11: msImageSmoothingEnabled у меня работает! msdn.microsoft.com/en-us/library/dn265062(v=vs.85).aspx
Стив
11

Изменить 31.07.2012 - Эта функция теперь включена в спецификацию холста! См. Отдельный ответ здесь:

https://stackoverflow.com/a/11751817/154112

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


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

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/

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

введите описание изображения здесь

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

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

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

Саймон Саррис
источник
Ага, вот чего я боялся. По-видимому, image-rendering: -webkit-optimize-contrastдолжен помочь, но, похоже, ничего не делает. Я могу рассмотреть возможность развертывания функции для масштабирования содержимого холста с использованием интерполяции ближайшего соседа.
намуол
Первоначально я принял ваш ответ; Я отозвал его на данный момент, потому что, похоже, неясно, является ли это дубликатом или нет.
намуол 03
У меня есть более полный ответ после некоторого исследования, и я хотел бы снова открыть вопрос, чтобы дать свой ответ.
намуол 03
3

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

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

ctx.scale(4, 4);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fillRect(0, 0, 64, 64);
saviski
источник
Это самый привлекательный обходной путь, который я когда-либо видел. Пока не уверен в производительности, но на это стоит обратить внимание. Я попробую.
намуол
использование повтора вместо без повтора быстрее. Не знаю почему. Это также довольно быстро, three.js используют это в CanvasRenderer
saviski
6
обновление: больше не работает с Chrome 21 (из-за добавленной поддержки дисплея Retina ?) новая версия jsfiddle.net/saviski/pGs4f/12 с использованием imageSmoothingEnabled, на который указал Саймон
saviski
Когда разрешение Retina становится общим, все очень сильно меняется. Когда дисплей Retina в стиле iPhone4-5 с разрешением 326 точек на дюйм (128 точек на дюйм) устанавливается на 24-дюймовые (52 x 32 см) мониторы, размер экрана будет 6656 x 4096 пикселей. Тогда сглаживание плохо, и все поставщики браузеров (даже на базе Webkit) вынуждены разрешить отключение сглаживания. Сглаживание - это операция, интенсивно использующая процессор, и сглаживание изображения 6656 x 4096 пикселей было бы слишком медленным, но, к счастью, ненужным при таком отображении.
Timo Kähkönen
1
В качестве примечания, вот хороший тест паттернов и изображения: jsperf.com/drawimage-vs-canvaspattern/9
yckart
1

Обходной путь Сависки объяснен здесь является перспективным, так как он работает на:

  • Chrome 22.0.1229.79 Mac OS X 10.6.8
  • Chrome 22.0.1229.79 м Windows 7
  • Chromium 18.0.1025.168 (сборка разработчика 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 )
  • Opera 12.02 Mac OS X 10.6.8 (рендеринг изображений: -o-crisp-Edge работает в этом )
  • Opera 12.02 Windows 7 (рендеринг изображений: -o-crisp-Edge работает в этом )

Проблема заключается в следующем, потому что 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-interpolation-mode: ближайший сосед НЕ работает)
Тимо Кяхконен
источник