Я бавлюся <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)
ні. на 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;
}