Sto giocando con l' <canvas>
elemento, disegnando linee e così via.
Ho notato che le mie linee diagonali sono antialias. Preferirei l'aspetto frastagliato per quello che sto facendo: c'è un modo per disattivare questa funzione?
Sto giocando con l' <canvas>
elemento, disegnando linee e così via.
Ho notato che le mie linee diagonali sono antialias. Preferirei l'aspetto frastagliato per quello che sto facendo: c'è un modo per disattivare questa funzione?
Risposte:
Per le immagini c'è adesso .context.imageSmoothingEnabled
= false
Tuttavia, non c'è nulla che controlli esplicitamente il disegno delle linee. Potrebbe essere necessario disegnare le proprie linee ( nel modo più duro ) usando getImageData
e putImageData
.
putImageData
ma fa ancora aliasing dei pixel vicini dannatamente.
Disegna le tue 1-pixel
linee su coordinate come ctx.lineTo(10.5, 10.5)
. Tracciando una linea da un pixel sul punto (10, 10)
mezzi, quali l' 1
pixel in quel raggiunge la posizione da 9.5
a 10.5
che si traduce in due linee che vengono disegnate sulla tela.
Un bel trucco per non dover sempre aggiungere la 0.5
coordinata effettiva su cui vuoi disegnare se hai molte linee di un pixel, è ctx.translate(0.5, 0.5)
all'intera tela all'inizio.
ctx.translate(0.5,0.5)
non l'ha fatto. su FF39.0
Può essere fatto in Mozilla Firefox. Aggiungi questo al tuo codice:
contextXYZ.mozImageSmoothingEnabled = false;
In Opera è attualmente una richiesta di funzionalità, ma si spera che verrà aggiunta presto.
"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"
L'antialiasing è necessario per la stampa corretta della grafica vettoriale che coinvolge coordinate non intere (0.4, 0.4), cosa che fanno tutti tranne pochissimi client.
Quando vengono fornite coordinate non intere, la tela ha due opzioni:
La strategia successiva funzionerà per la grafica statica, sebbene per i grafici piccoli (un cerchio con raggio di 2) le curve mostreranno passaggi chiari piuttosto che una curva liscia.
Il vero problema è quando la grafica viene traslata (spostata) - i salti tra un pixel e l'altro (1.6 => 2, 1.4 => 1), significano che l'origine della forma può saltare rispetto al contenitore genitore (spostandosi costantemente 1 pixel su / giù e sinistra / destra).
Suggerimento n. 1 : puoi ammorbidire (o indurire) l'antialias ridimensionando la tela (diciamo per x) quindi applica la scala reciproca (1 / x) alle geometrie tu stesso (non usando la tela).
Confronta (senza ridimensionamento):
con (scala tela: 0,75; scala manuale: 1,33):
e (scala tela: 1,33; scala manuale: 0,75):
Suggerimento n. 2 : se un aspetto frastagliato è davvero quello che stai cercando, prova a disegnare ogni forma un paio di volte (senza cancellare). Ad ogni estrazione, i pixel di antialiasing diventano più scuri.
Confrontare. Dopo aver disegnato una volta:
Dopo aver disegnato tre volte:
Disegnerei tutto usando un algoritmo di linea personalizzato come l'algoritmo di linea di Bresenham. Dai un'occhiata a questa implementazione di javascript: http://members.chello.at/easyfilter/canvas.html
Penso che questo risolverà sicuramente i tuoi problemi.
setPixel(x, y)
; Ho usato la risposta accettata qui: stackoverflow.com/questions/4899799/…
Voglio aggiungere che ho avuto problemi durante il ridimensionamento di un'immagine e il disegno su tela, utilizzava ancora la levigatura, anche se non veniva utilizzata durante l'upscaling.
Ho risolto usando questo:
function setpixelated(context){
context['imageSmoothingEnabled'] = false; /* standard */
context['mozImageSmoothingEnabled'] = false; /* Firefox */
context['oImageSmoothingEnabled'] = false; /* Opera */
context['webkitImageSmoothingEnabled'] = false; /* Safari */
context['msImageSmoothingEnabled'] = false; /* IE */
}
Puoi usare questa funzione in questo modo:
var canvas = document.getElementById('mycanvas')
setpixelated(canvas.getContext('2d'))
Forse questo è utile per qualcuno.
ctx.translate(0.5, 0.5);
ctx.lineWidth = .5;
Con questa combinazione posso disegnare belle linee sottili 1px.
Nota un trucco molto limitato. Se vuoi creare un'immagine a 2 colori, puoi disegnare qualsiasi forma tu voglia con il colore # 010101 su uno sfondo con il colore # 000000. Fatto ciò, puoi testare ogni pixel in imageData.data [] e impostare a 0xFF qualunque valore non sia 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);
Il risultato sarà un'immagine in bianco e nero senza antialiasing. Questo non sarà perfetto, poiché avrà luogo un po 'di antialiasing, ma questo antialiasing sarà molto limitato, il colore della forma sarà molto simile al colore dello sfondo.
Per chi è ancora alla ricerca di risposte. ecco la mia soluzione.
Supponendo che l'immagine sia grigia a 1 canale. Ho appena superato la soglia dopo 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;
}
}
}
se il tuo canale immagine è 3 o 4. devi modificare l'indice dell'array come
x*image.height*number_channel + y*number_channel + channel
Solo due note sulla risposta di StashOfCode:
È meglio farlo invece:
Traccia e riempi con #FFFFFF
, quindi fai questo:
imageData.data[i] = (imageData.data[i] >> 7) * 0xFF
Questo lo risolve per le linee con 1px di larghezza.
Oltre a questo, la soluzione di StashOfCode è perfetta perché non richiede di scrivere le tue funzioni di rasterizzazione (pensa non solo a linee ma bezier, archi circolari, poligoni pieni con buchi, ecc ...)
Ecco un'implementazione di base dell'algoritmo di Bresenham in JavaScript. Si basa sulla versione aritmetica dei numeri interi descritta in questo articolo di 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;
}
Prova qualcosa di simile canvas { image-rendering: pixelated; }
.
Questo potrebbe non funzionare se stai cercando di rendere solo una riga non antialias.
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>
Tuttavia, non l'ho testato su molti browser.