Posso disattivare l'antialias su un elemento <canvas> HTML?


88

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?


Penso che sia piuttosto correlato al browser. Forse potrebbero essere utili alcune informazioni aggiuntive sul software che utilizzi.
Tomalak

Preferirei un metodo cross-browser, ma un metodo che funziona su qualsiasi browser sarebbe comunque interessante per me
Blorgbeard uscirà il

Volevo solo vedere se ci sono stati ancora dei cambiamenti su questo argomento?
vternal3


C'e 'qualche aggiornamento su questo?
Roland

Risposte:


64

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 getImageDatae putImageData.


1
Mi chiedo quali siano le prestazioni di un algoritmo di riga javascript .. Ad un certo punto potrei provare Bresenham.
Blorgbeard uscirà il

I fornitori di browser stanno pubblicizzando ultimamente nuovi motori JS super veloci, quindi finalmente ce ne sarebbe un buon uso.
Kornel

1
Funziona davvero? Sto disegnando una linea usando putImageData ma fa ancora aliasing dei pixel vicini dannatamente.
Pacerier

se disegno su una tela più piccola (cache) e poi disegno su un'altra tela con l'impostazione disattivata, funzionerà come previsto?
SparK

69

Disegna le tue 1-pixellinee su coordinate come ctx.lineTo(10.5, 10.5). Tracciando una linea da un pixel sul punto (10, 10)mezzi, quali l' 1pixel in quel raggiunge la posizione da 9.5a 10.5che si traduce in due linee che vengono disegnate sulla tela.

Un bel trucco per non dover sempre aggiungere la 0.5coordinata effettiva su cui vuoi disegnare se hai molte linee di un pixel, è ctx.translate(0.5, 0.5)all'intera tela all'inizio.


hmm, ho problemi a sbarazzarmi dell'anti-aliasing usando questa tecnica. Forse mi manca capire qualcosa? Ti dispiacerebbe pubblicare un esempio da qualche parte?
Xavi

7
Questo non elimina l'antialiasing, ma fa sembrare le linee con antialiasing molto migliori --- come sbarazzarsi di quelle imbarazzanti linee orizzontali o verticali che sono spesse due pixel quando in realtà volevi un pixel.
David Given

1
@ porneL: No, le linee vengono tracciate tra gli angoli dei pixel. Quando la tua linea è larga 1 pixel, si estende mezzo pixel in entrambe le direzioni
Eric

L'aggiunta di +0,5 funziona per me, ma ctx.translate(0.5,0.5)non l'ha fatto. su FF39.0
Paulo Bueno

Grazie mille! Tanto per cambiare, non riesco a credere di avere linee effettive di 1px!
Chunky Chunk

24

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.


freddo. +1 per il tuo contributo. mi chiedo se la disabilitazione di AA acceleri il disegno a righe
marcusklaas

7
L'OP vuole rimuovere le linee anti-alias, ma questo funziona solo sulle immagini. Secondo le specifiche , determina"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"
rvighne

13

Deve antialias grafica vettoriale

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:

  • Antialias : dipingi i pixel attorno alla coordinata in base a quanto è lontana la coordinata intera da quella non intera (cioè l'errore di arrotondamento).
  • Round : applica una funzione di arrotondamento alla coordinata non intera (quindi 1.4 diventerà 1, ad esempio).

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).

Alcuni suggerimenti

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):

Alcuni rettangoli

con (scala tela: 0,75; scala manuale: 1,33):

Stessi rettangoli con bordi più morbidi

e (scala tela: 1,33; scala manuale: 0,75):

Stessi rettangoli con bordi più scuri

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:

Alcuni percorsi

Dopo aver disegnato tre volte:

Stessi percorsi ma più oscuri e nessun antialiasing visibile.


@vanowm sentiti libero di clonare e giocare con: github.com/Izhaki/gefri . Tutte le immagini sono screenshot della cartella / demo (con codice leggermente modificato per il suggerimento n. 2). Sono sicuro che troverai facile introdurre l'arrotondamento intero alle cifre disegnate (mi ci sono voluti 4 minuti) e poi trascina per vedere l'effetto.
Izhaki

Può sembrare incredibile, ma ci sono rare situazioni in cui vuoi disattivare l'antialias. In effetti, ho appena programmato un gioco in cui le persone devono dipingere aree su una tela e ho bisogno che ci siano solo 4 colori e sono bloccato a causa degli antialias. Ripetere lo schema tre volte non ha risolto il problema (sono salito a 200) e ci sono ancora pixel dei colori sbagliati.
Arnaud


8

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.


perché non context.imageSmoothingEnabled = false?
Martijn Scheffer

Questo non ha funzionato nel momento in cui ho scritto la mia risposta. Funziona adesso?
eri0o

1
lo ha fatto, è ESATTAMENTE la stessa cosa, nella scrittura di javascript obj ['name'] o obj.name è sempre stato e sarà sempre lo stesso, un oggetto è una raccolta di valori denominati (tuple), utilizzando qualcosa che assomiglia a un tabella hash, entrambe le notazioni verranno trattate allo stesso modo, non c'è motivo per cui il tuo codice non avrebbe funzionato prima, nel peggiore dei casi assegna un valore che non ha effetto (perché è destinato a un altro browser. un semplice esempio: scrivi obj = {a: 123}; console.log (obj ['a'] === obj.a? "yes its true": "no it's not")
Martijn Scheffer

Pensavo intendessi perché avere tutte le altre cose, quello che volevo dire con il mio commento è che all'epoca i browser richiedevano proprietà diverse.
eri0o

ok si certo :) stavo parlando della sintassi, non della validità del codice stesso (funziona)
Martijn Scheffer

6
ctx.translate(0.5, 0.5);
ctx.lineWidth = .5;

Con questa combinazione posso disegnare belle linee sottili 1px.


6
Non è necessario impostare lineWidth su .5 ... questo renderà (o dovrebbe) solo metà opacità.
aaaidan

4

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.


1

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

0

Solo due note sulla risposta di StashOfCode:

  1. Funziona solo per una tela opaca in scala di grigi (fillRect con il bianco, quindi disegna con il nero o viceversa)
  2. Potrebbe non riuscire quando le linee sono sottili (larghezza della linea ~ 1 px)

È 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 ...)


0

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;

    }

0

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.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.