Disabilita l'interpolazione quando ridimensiona un <canvas>


126

NOTA : ciò ha a che fare con il modo in cui vengono renderizzati gli elementi di tela esistenti quando vengono ingranditi , non con il modo in cui le linee o la grafica vengono renderizzate su una superficie di tela . In altre parole, questo ha tutto a che fare con l' interpolazione di elementi in scala e nulla a che fare con l' antialiasing della grafica disegnata su una tela. Non mi preoccupo di come il browser disegna le linee; Mi interessa il modo in cui il browser esegue il rendering dell'elemento canvas stesso quando viene ridimensionato.


Esiste una proprietà canvas o un'impostazione del browser che posso modificare a livello di codice per disabilitare l'interpolazione durante il ridimensionamento degli <canvas>elementi? Una soluzione cross-browser è l'ideale ma non essenziale; I browser basati su Webkit sono il mio obiettivo principale. Le prestazioni sono molto importanti.

Questa domanda è molto simile ma non illustra sufficientemente il problema. Per quello che vale, ho provato image-rendering: -webkit-optimize-contrastinutilmente.

L'applicazione sarà un gioco "retrò" in stile 8 bit scritto in HTML5 + JS per chiarire ciò di cui ho bisogno.


Per illustrare, ecco un esempio. ( versione live )

Supponiamo che io abbia una tela 21x21 ...

<canvas id='b' width='21' height='21'></canvas>

... che ha CSS che rende l'elemento 5 volte più grande (105x105):

canvas { border: 5px solid #ddd; }
canvas#b { width: 105px; height: 105px; } /* 5 * 21 = 105 */

Traccio una semplice 'X' sulla tela in questo modo:

$('canvas').each(function () {
    var ctx = this.getContext("2d");
    ctx.moveTo(0,0);
    ctx.lineTo(21,21);
    ctx.moveTo(0,21);
    ctx.lineTo(21,0);
    ctx.stroke();
});

L'immagine a sinistra è ciò che rende Chromium (14.0). L'immagine a destra è ciò che voglio (disegnato a mano a scopo illustrativo).

Chrome interpola gli elementi in tela ridimensionati Una versione non interpolata



1
Credo che questa domanda si riferisca alle funzioni di disegno al tratto che disegnano linee anti-alias. Mi riferisco a come vengono renderizzati gli elementi canvas ridimensionati con interpolazione per impostazione predefinita.
namuol,

Sì, scusa ... Ho pensato prima le domande allo stesso modo, poi ho notato immediatamente il mio errore. : - / E se dovessi provare a utilizzare il filtro di pixelizzazione di jsmanipulate? joelb.me/jsmanipulate
HostileFork afferma di non fidarsi del

1
È più un filtro di immagine pensato per le fotografie.
namuol,

Sì, ma se trasforma l'immagine a sinistra in una versione abbastanza buona della tua immagine a destra per renderti felice, allora potrebbe fornire una soluzione cross-browser (considerando che non sembra troppo promettente per trovare una bandiera per questa modalità di disegno, universale o altro , in WebKit). Che si tratti di un sostituto praticabile dipende dal problema che stai cercando di risolvere ... se hai davvero bisogno di un disegno discreto di pixel o se stai solo cercando di assicurarti che il risultato sia pixelato con una certa granularità.
HostileFork dice di non fidarsi del

Risposte:


126

Ultimo aggiornamento: 2014-09-12

Esiste una proprietà canvas o un'impostazione del browser che posso modificare a livello di codice per disabilitare l'interpolazione durante il ridimensionamento degli elementi?

La risposta è forse un giorno . Per ora, dovrai ricorrere a trucchi per ottenere ciò che desideri.


image-rendering

La bozza di lavoro di CSS3 delinea una nuova proprietà,image-rendering che dovrebbe fare quello che voglio:

La proprietà di rendering delle immagini fornisce un suggerimento all'utente-agente su quali aspetti di un'immagine sono più importanti da conservare quando l'immagine viene ridimensionata, per aiutare l'agente-utente nella scelta di un algoritmo di ridimensionamento appropriato.

La specifica descrive tre valori accettati: auto, crisp-edges, e pixelated.

pixelated:

Quando si ridimensiona l'immagine, è necessario utilizzare il "vicino più vicino" o un algoritmo simile, in modo che l'immagine appaia semplicemente composta da pixel molto grandi. Quando si ridimensiona, questo è lo stesso di auto.

Standard? Cross-browser?

Dal momento che questa è solo una bozza di lavoro , non vi è alcuna garanzia che diventerà standard. Il supporto del browser è attualmente discutibile, nella migliore delle ipotesi.

Mozilla Developer Network ha una pagina abbastanza approfondita dedicata allo stato dell'arte attuale che consiglio vivamente di leggere.

Gli sviluppatori di Webkit inizialmente hanno scelto di implementarlo provvisoriamente come-webkit-optimize-contrast , ma Chromium / Chrome non sembrano utilizzare una versione di Webkit che lo implementa.

Aggiornamento: 2014-09-12

Chrome 38 ora supportaimage-rendering: pixelated !

Firefox ha una segnalazione di bug aperta da image-rendering: pixelatedimplementare, ma -moz-crisp-edgesfunziona per ora.

Soluzione?

La soluzione multipiattaforma, solo CSS, finora è quindi:

canvas {
  image-rendering: optimizeSpeed;             /* Older versions of FF          */
  image-rendering: -moz-crisp-edges;          /* FF 6.0+                       */
  image-rendering: -webkit-optimize-contrast; /* Safari                        */
  image-rendering: -o-crisp-edges;            /* OS X & Windows Opera (12.02+) */
  image-rendering: pixelated;                 /* Awesome future-browsers       */
  -ms-interpolation-mode: nearest-neighbor;   /* IE                            */
}

Purtroppo questo non funzionerà ancora su tutte le principali piattaforme HTML5 (Chrome, in particolare).

Certo, si potrebbe ridimensionare manualmente le immagini usando l'interpolazione del vicino più vicino su superfici di tela ad alta risoluzione in javascript, o anche pre-ridimensionare le immagini sul lato server, ma nel mio caso questo sarà pericolosamente costoso, quindi non è un'opzione praticabile.

ImpactJS utilizza una tecnica di pre-ridimensionamento delle trame per aggirare tutto questo FUD. Lo sviluppatore di Impact, Dominic Szablewski, ha scritto un articolo molto approfondito su questo (ha persino finito per citare questa domanda nella sua ricerca).

Vedi la risposta di Simon per una soluzione basata su tela che si basa sulla imageSmoothingEnabledproprietà (non disponibile nei browser meno recenti, ma più semplice del pre-ridimensionamento e abbastanza supportata).

Dimostrazione dal vivo

Se desideri testare le proprietà CSS discusse nell'articolo MDN sugli canvaselementi, ho creato questo violino che dovrebbe mostrare qualcosa di simile, sfocato o no, a seconda del tuo browser: un'immagine 4: 1 (da 64x64 a 256x256) di una TV in stile pixel art isometrico


Il rendering delle immagini sembra funzionare altrove, ma i browser webkit. Spero davvero che il personale di Chrome / Chromium / Safari legga bene cosa significa "passare all'interpolazione EPX in situazioni di carico ridotto". Quando ho letto la frase per la prima volta ho pensato che il contrasto di ottimizzazione consentisse nuovi colori creati a basso carico, ma NO: en.wikipedia.org/wiki/…
Timo Kähkönen

2
Opera 12.02 su Mac e Windows supporta il rendering delle immagini: -o-crisp- edge ( jsfiddle.net/VAXrL/21 ), in modo da poterlo aggiungere alla tua risposta.
Timo Kähkönen,

Dovrebbe funzionare quando sto aumentando lo zoom del browser? Ho provato a disegnare con FF 22, Chrome 27 e IE 10 su Windows 7 quando lo zoom del browser è del 150% e il risultato è sfocato su tutti i browser. Quando raddoppio la larghezza e l'altezza della tela e la ripristino alla larghezza e all'altezza originali con CSS, disegna linee nette.
pablo,

Questa è una buona domanda. Non sono sicuro se esiste una specifica per il comportamento di ridimensionamento specificato dall'utente.
namuol,

1
Sono (dal presente futuro!) Su Chrome 78. Vedo "rendering delle immagini: pixelato;" Lavorando. Sembra che questo sia stato aggiunto nel 2015 a Chrome 41: developers.google.com/web/updates/2015/01/pixelated
MrColes

61

Nuova risposta 31/07/2012

Questo è finalmente nelle specifiche della tela!

La specifica ha recentemente aggiunto una proprietà chiamata imageSmoothingEnabled, che per impostazione predefinita truedetermina se le immagini disegnate su coordinate non intere o ridimensionate in scala utilizzeranno un algoritmo più fluido. Se è impostato su falseviene utilizzato il vicino più vicino, producendo un'immagine meno uniforme e creando semplicemente pixel più grandi.

Il smoothing delle immagini è stato aggiunto di recente alle specifiche del canvas e non è supportato da tutti i browser, ma alcuni browser hanno implementato versioni con prefisso del fornitore di questa proprietà. Nel contesto esiste mozImageSmoothingEnabledin Firefox e webkitImageSmoothingEnabledin Chrome e Safari, e impostando questi su false non si verificherà l'antialiasing. Sfortunatamente, al momento della stesura di questo documento, IE9 e Opera non hanno implementato questa proprietà, con prefisso del fornitore o altro.


Anteprima: JSFiddle

Risultato:

inserisci qui la descrizione dell'immagine


1
Sfortunatamente sembra che questo non funzioni ancora. Forse mi manca qualcosa? Ecco un test: jsfiddle.net/VAXrL/187
namuol

12
devi ridimensionare usando l'API di tela per farlo funzionare, non puoi mai ridimensionare con CSS e avere l'API di tela influenzarla! Quindi qualcosa del genere: jsfiddle.net/VAXrL/190
Simon Sarris

1
Ma la natura di questo problema non riguarda davvero la tela. Riguarda il modo in cui il browser esegue il rendering delle immagini ridimensionate, inclusi gli elementi <canvas>.
namuol,

4
Se ridimensionate utilizzando CSS, la soluzione di namuol funzionerà. Se ridimensionate manualmente l'immagine sulla tela, la soluzione di Simon funziona. È pulito che ci siano soluzioni per entrambi i casi, quindi grazie a entrambi!
Shaun Lebron,

Per IE 11: msImageSmoothingEnabled funziona per me! msdn.microsoft.com/en-us/library/dn265062(v=vs.85).aspx
steve

11

Modifica 31/07/2012 - Questa funzionalità è ora nelle specifiche della tela! Vedi la risposta separata qui:

https://stackoverflow.com/a/11751817/154112

La vecchia risposta è sotto per i posteri.


A seconda dell'effetto desiderato, hai questa opzione come unica:

var can = document.getElementById('b');
var ctx = can.getContext('2d');
ctx.scale(5,5);
$('canvas').each(function () {
    var ctx = this.getContext("2d");
    ctx.moveTo(0,0);
    ctx.lineTo(21,21);
    ctx.moveTo(0,21);
    ctx.lineTo(21,0);
    ctx.stroke();
});

http://jsfiddle.net/wa95p/

Che crea questo:

inserisci qui la descrizione dell'immagine

Probabilmente non quello che vuoi. Ma se stessi semplicemente cercando di avere zero sfocatura, allora quello sarebbe il biglietto, quindi lo offrirò per ogni evenienza.

Un'opzione più difficile è usare la manipolazione dei pixel e scrivere un algoritmo per il lavoro. Ogni pixel della prima immagine diventa un blocco 5x5 di pixel sulla nuova immagine. Non sarebbe troppo difficile avere a che fare con l'imagedata.

Ma Canvas e CSS da soli non ti aiuteranno qui a ridimensionare l'uno con l'altro con l'effetto esatto che desideri.


Sì, questo è quello di cui avevo paura. Apparentemente, image-rendering: -webkit-optimize-contrastdovrebbe fare il trucco ma non sembra fare nulla. Potrei cercare di far rotolare una funzione per ridimensionare i contenuti di una tela usando l'interpolazione del vicino più vicino.
namuol,

Inizialmente ho accettato la tua risposta; L'ho revocato per ora perché sembra esserci confusione sul fatto che si tratti di un duplicato o meno.
namuol,

Ho una risposta più completa dopo aver fatto qualche ricerca e vorrei riaprire la domanda per fornire la mia risposta.
namuol,

3

In Google Chrome, i motivi delle immagini su tela non sono interpolati.

Ecco un esempio funzionante modificato dalla risposta namuol http://jsfiddle.net/pGs4f/

ctx.scale(4, 4);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fillRect(0, 0, 64, 64);

Questa è la soluzione più interessante che ho visto finora. Non sono ancora sicuro delle prestazioni, ma vale la pena esaminarlo. Lo proverò.
namuol,

usare ripeti invece di non ripetere è più veloce. Non so perché. È anche abbastanza veloce, tre.js lo usano in CanvasRenderer
saviski il

6
aggiornamento: non funziona più con Chrome 21 (a causa del supporto display retina aggiunto?) nuova versione jsfiddle.net/saviski/pGs4f/12 utilizzando imageSmoothingEnabled indicato da Simon
saviski il

Quando la risoluzione della Retina diventa generale, le cose cambiano enormemente. Quando il display Retina 326 dpi (128 dpi) di iPhone4-5 arriva a monitor da 52 pollici (52x32cm), le dimensioni dello schermo saranno 6656 x 4096 px. Quindi l'antialiasing è negativo e tutti i fornitori di browser (anche basati su Webkit) sono costretti a consentire la disabilitazione dell'antialiasing. L'antialias è un'operazione ad alta intensità di CPU e l'immagine di 6656 x 4096 px sarebbe troppo lenta, ma fortunatamente non necessaria in tale visualizzazione.
Timo Kähkönen,

1
Proprio come una nota a margine
yckart

1

Soluzione di Saviski esplicitata qui è promettente, perché funziona su:

  • Chrome 22.0.1229.79 Mac OS X 10.6.8
  • Chrome 22.0.1229.79 m Windows 7
  • Chromium 18.0.1025.168 (Developer Build 134367 Linux) Ubuntu 11.10
  • Firefox 3.6.25 Windows 7

Ma non funziona nel modo seguente, ma lo stesso effetto può essere ottenuto utilizzando il rendering di immagini CSS:

  • Firefox 15.0.1 Mac OS X 10.6.8 (rendering delle immagini: -moz-croccante-bordi funziona in questo )
  • Opera 12.02 Mac OS X 10.6.8 (rendering delle immagini: -o-nitido-bordi funziona in questo )
  • Opera 12.02 Windows 7 (rendering delle immagini: -o-nitido-bordi funziona in questo )

I problemi sono questi, perché ctx.XXXImageSmoothingEnabled non funziona e il rendering delle immagini non funziona:

  • Safari 5.1.7 Mac OS X 10.6.8. (rendering delle immagini: -webkit-optimise-contrast NON funziona)
  • Safari 5.1.7 Windows 7 (rendering delle immagini: -webkit-optimise-contrast NON funziona)
  • IE 9 Windows 7 (-ms-interpolazione-mode: il vicino più vicino NON funziona)
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.