Я играю с <canvas>
элементом, рисую линии и тому подобное.
Я заметил, что мои диагональные линии сглажены. Я бы предпочел неровный вид того, что я делаю - есть ли способ отключить эту функцию?
Я играю с <canvas>
элементом, рисую линии и тому подобное.
Я заметил, что мои диагональные линии сглажены. Я бы предпочел неровный вид того, что я делаю - есть ли способ отключить эту функцию?
Ответы:
Для изображений есть сейчас .context.imageSmoothingEnabled
= false
Однако нет ничего, что явно управляло бы рисованием линий. Возможно, вам придется рисовать свои собственные линии ( сложный способ ), используя getImageData
и putImageData
.
putImageData
но он все еще чертовски сглаживает соседние пиксели.
Проведите свои 1-pixel
линии в координатах вроде ctx.lineTo(10.5, 10.5)
. Проведение линии в один пиксель над точкой (10, 10)
означает, что этот 1
пиксель в этой позиции простирается от 9.5
до10.5
результате чего на холсте рисуются две линии.
Хороший трюк, позволяющий не всегда добавлять 0.5
к фактической координате, которую вы хотите нарисовать, если у вас много линий в один пиксель, - ctx.translate(0.5, 0.5)
это вначале весь ваш холст.
ctx.translate(0.5,0.5)
не работает. on FF39.0
Это можно сделать в Mozilla Firefox. Добавьте это в свой код:
contextXYZ.mozImageSmoothingEnabled = false;
В Opera в настоящее время это запрос функции, но, надеюсь, он скоро будет добавлен.
"whether pattern fills and the drawImage() method will attempt to smooth images if their pixels don't line up exactly with the display, when scaling images up"
Сглаживание требуется для правильного построения векторной графики, которая использует нецелочисленные координаты (0,4, 0,4), что делают все, кроме очень немногих клиентов.
Если заданы нецелочисленные координаты, у холста есть два варианта:
Более поздняя стратегия будет работать для статической графики, хотя для небольшой графики (круг с радиусом 2) кривые будут показывать четкие шаги, а не плавную кривую.
Настоящая проблема заключается в том, что графика переводится (перемещается) - скачки между одним пикселем и другим (1.6 => 2, 1.4 => 1) означают, что начало координат фигуры может перескакивать относительно родительского контейнера (постоянно смещается 1 пиксель вверх / вниз и влево / вправо).
Совет №1 : вы можете смягчить (или усилить) сглаживание, масштабируя холст (скажем, по x), а затем самостоятельно применив обратный масштаб (1 / x) к геометрии (не используя холст).
Сравнить (без масштабирования):
с (масштаб холста: 0,75; ручной масштаб: 1,33):
и (масштаб холста: 1,33; ручной масштаб: 0,75):
Совет №2 : Если вы действительно хотите получить неровный вид, попробуйте нарисовать каждую форму несколько раз (без стирания). С каждым рисованием пиксели сглаживания становятся темнее.
Сравните. После нанесения один раз:
После трехкратного рисования:
Я бы нарисовал все, используя собственный линейный алгоритм, такой как линейный алгоритм Брезенхема. Посмотрите эту реализацию javascript: http://members.chello.at/easyfilter/canvas.html
Думаю, это точно решит ваши проблемы.
setPixel(x, y)
; Я использовал принятый ответ здесь: stackoverflow.com/questions/4899799/…
Хочу добавить, что у меня возникли проблемы при уменьшении размера изображения и рисовании на холсте, оно все еще использовало сглаживание, хотя оно не использовалось при масштабировании.
Я решил использовать это:
function setpixelated(context){
context['imageSmoothingEnabled'] = false; /* standard */
context['mozImageSmoothingEnabled'] = false; /* Firefox */
context['oImageSmoothingEnabled'] = false; /* Opera */
context['webkitImageSmoothingEnabled'] = false; /* Safari */
context['msImageSmoothingEnabled'] = false; /* IE */
}
Вы можете использовать эту функцию так:
var canvas = document.getElementById('mycanvas')
setpixelated(canvas.getContext('2d'))
Может это кому пригодится.
ctx.translate(0.5, 0.5);
ctx.lineWidth = .5;
С помощью этой комбинации я могу нарисовать красивые тонкие линии в 1 пиксель.
Обратите внимание на очень ограниченный трюк. Если вы хотите создать двухцветное изображение, вы можете нарисовать любую фигуру цветом # 010101 на фоне с цветом # 000000. Как только это будет сделано, вы можете протестировать каждый пиксель в imageData.data [] и установить в 0xFF любое значение, отличное от 0x00:
imageData = context2d.getImageData (0, 0, g.width, g.height);
for (i = 0; i != imageData.data.length; i ++) {
if (imageData.data[i] != 0x00)
imageData.data[i] = 0xFF;
}
context2d.putImageData (imageData, 0, 0);
В результате будет черно-белое изображение без антиалиасинга. Это не будет идеально, поскольку будет иметь место некоторое сглаживание, но это сглаживание будет очень ограниченным, так как цвет формы очень похож на цвет фона.
Для тех, кто все еще ищет ответы. вот мое решение.
Предполагается, что изображение - 1 канал серого цвета. Я просто установил порог после ctx.stroke ().
ctx.beginPath();
ctx.moveTo(some_x, some_y);
ctx.lineTo(some_x, some_y);
...
ctx.closePath();
ctx.fill();
ctx.stroke();
let image = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height)
for(let x=0; x < ctx.canvas.width; x++) {
for(let y=0; y < ctx.canvas.height; y++) {
if(image.data[x*image.height + y] < 128) {
image.data[x*image.height + y] = 0;
} else {
image.data[x*image.height + y] = 255;
}
}
}
если ваш канал изображения 3 или 4. вам нужно изменить индекс массива, например
x*image.height*number_channel + y*number_channel + channel
Всего две заметки об ответе StashOfCode:
Лучше сделать так:
Обведите и залейте #FFFFFF
, затем сделайте следующее:
imageData.data[i] = (imageData.data[i] >> 7) * 0xFF
Это решает проблему для линий шириной 1 пиксель.
Помимо этого, решение StashOfCode идеально, потому что оно не требует написания ваших собственных функций растеризации (подумайте не только о линиях, но и о кривых Безье, дугах окружности, заполненных многоугольниках с отверстиями и т. Д.)
Вот базовая реализация алгоритма Брезенхэма на JavaScript. Он основан на версии целочисленной арифметики, описанной в этой статье в Википедии: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
function range(f=0, l) {
var list = [];
const lower = Math.min(f, l);
const higher = Math.max(f, l);
for (var i = lower; i <= higher; i++) {
list.push(i);
}
return list;
}
//Don't ask me.
//https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
function bresenhamLinePoints(start, end) {
let points = [];
if(start.x === end.x) {
return range(f=start.y, l=end.y)
.map(yIdx => {
return {x: start.x, y: yIdx};
});
} else if (start.y === end.y) {
return range(f=start.x, l=end.x)
.map(xIdx => {
return {x: xIdx, y: start.y};
});
}
let dx = Math.abs(end.x - start.x);
let sx = start.x < end.x ? 1 : -1;
let dy = -1*Math.abs(end.y - start.y);
let sy = start.y < end.y ? 1 : - 1;
let err = dx + dy;
let currX = start.x;
let currY = start.y;
while(true) {
points.push({x: currX, y: currY});
if(currX === end.x && currY === end.y) break;
let e2 = 2*err;
if (e2 >= dy) {
err += dy;
currX += sx;
}
if(e2 <= dx) {
err += dx;
currY += sy;
}
}
return points;
}
Попробуйте что-нибудь вроде canvas { image-rendering: pixelated; }
.
Это может не сработать, если вы пытаетесь сделать не сглаженным только одну строку.
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.fillRect(4, 4, 2, 2);
canvas {
image-rendering: pixelated;
width: 100px;
height: 100px; /* Scale 10x */
}
<html>
<head></head>
<body>
<canvas width="10" height="10">Canvas unsupported</canvas>
</body>
</html>
Однако я не тестировал это во многих браузерах.