Estoy jugando con el <canvas>
elemento, dibujando líneas y demás.
He notado que mis líneas diagonales están suavizadas. Preferiría el aspecto irregular para lo que estoy haciendo. ¿Hay alguna forma de desactivar esta función?
Estoy jugando con el <canvas>
elemento, dibujando líneas y demás.
He notado que mis líneas diagonales están suavizadas. Preferiría el aspecto irregular para lo que estoy haciendo. ¿Hay alguna forma de desactivar esta función?
Respuestas:
Para imágenes hay ahora .context.imageSmoothingEnabled
= false
Sin embargo, no hay nada que controle explícitamente el dibujo de líneas. Es posible que deba dibujar sus propias líneas (de la manera difícil ) usando getImageData
y putImageData
.
putImageData
pero todavía hace aliasing de píxeles cercanos maldita sea.
Dibuja tus 1-pixel
líneas en coordenadas como ctx.lineTo(10.5, 10.5)
. Dibujo de una línea de un píxel sobre el punto (10, 10)
medio, que este 1
píxel en que alcanza la posición de 9.5
a 10.5
lo que resulta en dos líneas que se dibujen sobre el lienzo.
Un buen truco para no tener que agregar siempre el 0.5
a la coordenada real sobre la que desea dibujar si tiene muchas líneas de un píxel, es ctx.translate(0.5, 0.5)
todo el lienzo al principio.
ctx.translate(0.5,0.5)
no lo hizo. en FF39.0
Se puede hacer en Mozilla Firefox. Agregue esto a su código:
contextXYZ.mozImageSmoothingEnabled = false;
En Opera, actualmente es una solicitud de función, pero con suerte se agregará pronto.
"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"
El suavizado es necesario para el trazado correcto de gráficos vectoriales que involucran coordenadas no enteras (0.4, 0.4), lo que hacen todos los clientes, excepto muy pocos.
Cuando se le dan coordenadas no enteras, el lienzo tiene dos opciones:
La última estrategia funcionará para gráficos estáticos, aunque para gráficos pequeños (un círculo con un radio de 2) las curvas mostrarán pasos claros en lugar de una curva suave.
El problema real es cuando los gráficos se traducen (mueven): los saltos entre un píxel y otro (1.6 => 2, 1.4 => 1), significan que el origen de la forma puede saltar en relación con el contenedor principal (cambiando constantemente 1 píxel arriba / abajo e izquierda / derecha).
Consejo n. ° 1 : puede suavizar (o endurecer) el antialiasing escalando el lienzo (por ejemplo, por x) y luego aplique la escala recíproca (1 / x) a las geometrías usted mismo (sin usar el lienzo).
Comparar (sin escala):
con (escala de lienzo: 0,75; escala manual: 1,33):
y (escala de lienzo: 1,33; escala manual: 0,75):
Consejo n. ° 2 : si lo que buscas es un aspecto irregular, intenta dibujar cada forma varias veces (sin borrar). Con cada dibujo, los píxeles de suavizado se oscurecen.
Comparar. Después de dibujar una vez:
Después de dibujar tres veces:
Dibujaría todo usando un algoritmo de línea personalizado como el algoritmo de línea de Bresenham. Consulte esta implementación de JavaScript: http://members.chello.at/easyfilter/canvas.html
Creo que esto definitivamente resolverá tus problemas.
setPixel(x, y)
; Usé la respuesta aceptada aquí: stackoverflow.com/questions/4899799/…
Quiero agregar que tuve problemas al reducir el tamaño de una imagen y dibujar en el lienzo, todavía usaba suavizado, aunque no se usaba al aumentar la escala.
Resolví usando esto:
function setpixelated(context){
context['imageSmoothingEnabled'] = false; /* standard */
context['mozImageSmoothingEnabled'] = false; /* Firefox */
context['oImageSmoothingEnabled'] = false; /* Opera */
context['webkitImageSmoothingEnabled'] = false; /* Safari */
context['msImageSmoothingEnabled'] = false; /* IE */
}
Puede utilizar esta función de esta manera:
var canvas = document.getElementById('mycanvas')
setpixelated(canvas.getContext('2d'))
Quizás esto sea útil para alguien.
ctx.translate(0.5, 0.5);
ctx.lineWidth = .5;
Con este combo puedo dibujar bonitas líneas finas de 1px.
Note un truco muy limitado. Si desea crear una imagen de 2 colores, puede dibujar cualquier forma que desee con el color # 010101 sobre un fondo con el color # 000000. Una vez hecho esto, puede probar cada píxel en imageData.data [] y establecer en 0xFF cualquier valor que no sea 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);
El resultado será una imagen en blanco y negro sin suavizado. Esto no será perfecto, ya que se producirá algo de antialiasing, pero este antialiasing será muy limitado, siendo el color de la forma muy parecido al color del fondo.
Para aquellos que aún buscan respuestas. aquí está mi solución.
Suponiendo que la imagen es de 1 canal gris. Acabo de establecer un umbral después de 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;
}
}
}
si su canal de imagen es 3 o 4, necesita modificar el índice de la matriz como
x*image.height*number_channel + y*number_channel + channel
Solo dos notas sobre la respuesta de StashOfCode:
Es mejor hacer esto en su lugar:
Trace y rellene con #FFFFFF
, luego haga esto:
imageData.data[i] = (imageData.data[i] >> 7) * 0xFF
Eso lo resuelve para líneas con un ancho de 1 px.
Aparte de eso, la solución de StashOfCode es perfecta porque no requiere escribir sus propias funciones de rasterización (piense no solo en líneas sino también en béziers, arcos circulares, polígonos rellenos con agujeros, etc.)
Aquí hay una implementación básica del algoritmo de Bresenham en JavaScript. Se basa en la versión aritmética de enteros descrita en este artículo de wikipedia: 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;
}