Ich spiele mit dem <canvas>
Element herum , zeichne Linien und so.
Ich habe festgestellt, dass meine diagonalen Linien antialiasiert sind. Ich würde den zackigen Look für das, was ich mache, vorziehen - gibt es eine Möglichkeit, diese Funktion auszuschalten?
javascript
html
canvas
antialiasing
Blorgbeard ist raus
quelle
quelle
Antworten:
Für Bilder gibt es jetzt .
context.imageSmoothingEnabled
= false
Es gibt jedoch nichts, was das Zeichnen von Linien explizit steuert. Möglicherweise müssen Sie Ihre eigenen Linien (auf die harte Tour ) mit
getImageData
und zeichnenputImageData
.quelle
putImageData
aber es macht immer noch Aliasing von nahegelegenen Pixeln, verdammt.Zeichnen Sie Ihre
1-pixel
Linien auf Koordinaten wiectx.lineTo(10.5, 10.5)
. Das Zeichnen einer Ein-Pixel-Linie über den Punkt(10, 10)
bedeutet, dass dieses1
Pixel an dieser Position bis9.5
zu10.5
zwei Linien reicht, die auf der Leinwand gezeichnet werden.Ein netter Trick, den Sie nicht immer
0.5
zu der tatsächlichen Koordinate hinzufügen müssen, über die Sie zeichnen möchten, wenn Sie viele Ein-Pixel-Linien haben, ist zuctx.translate(0.5, 0.5)
Beginn Ihre gesamte Leinwand.quelle
ctx.translate(0.5,0.5)
aber nicht getan. am FF39.0Dies kann in Mozilla Firefox erfolgen. Fügen Sie dies Ihrem Code hinzu:
contextXYZ.mozImageSmoothingEnabled = false;
In Opera handelt es sich derzeit um eine Funktionsanforderung, die jedoch hoffentlich bald hinzugefügt wird.
quelle
"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"
Es muss Antialias Vektorgrafiken
Antialiasing ist erforderlich, um Vektorgrafiken mit nicht ganzzahligen Koordinaten (0,4, 0,4) korrekt zu zeichnen, was alle bis auf sehr wenige Clients tun.
Wenn nicht ganzzahlige Koordinaten angegeben werden, hat die Zeichenfläche zwei Optionen:
Die spätere Strategie funktioniert für statische Grafiken, obwohl Kurven für kleine Grafiken (ein Kreis mit einem Radius von 2) eher klare Schritte als eine glatte Kurve zeigen.
Das eigentliche Problem ist, wenn die Grafiken übersetzt (verschoben) werden - die Sprünge zwischen einem Pixel und einem anderen (1,6 => 2, 1,4 => 1) bedeuten, dass der Ursprung der Form in Bezug auf den übergeordneten Container springen kann (sich ständig verschieben) 1 Pixel hoch / runter und links / rechts).
Einige Hinweise
Tipp 1 : Sie können Antialiasing mildern (oder härten), indem Sie die Leinwand skalieren (z. B. mit x) und dann die reziproke Skala (1 / x) selbst auf die Geometrien anwenden (ohne die Leinwand zu verwenden).
Vergleichen (keine Skalierung):
mit (Leinwandmaßstab: 0,75; manueller Maßstab: 1,33):
und (Leinwandmaßstab: 1,33; manueller Maßstab: 0,75):
Tipp 2 : Wenn Sie wirklich nach einem zackigen Look suchen, versuchen Sie, jede Form einige Male zu zeichnen (ohne sie zu löschen). Mit jeder Zeichnung werden die Antialiasing-Pixel dunkler.
Vergleichen Sie. Nach einmaligem Zeichnen:
Nach dreimaligem Zeichnen:
quelle
Ich würde alles mit einem benutzerdefinierten Linienalgorithmus wie Bresenhams Linienalgorithmus zeichnen. Schauen Sie sich diese Javascript-Implementierung an: http://members.chello.at/easyfilter/canvas.html
Ich denke, das wird definitiv Ihre Probleme lösen.
quelle
setPixel(x, y)
; Ich habe die akzeptierte Antwort hier verwendet: stackoverflow.com/questions/4899799/…Ich möchte hinzufügen, dass ich Probleme hatte, ein Bild zu verkleinern und auf Leinwand zu zeichnen. Es wurde immer noch geglättet, obwohl es beim Hochskalieren nicht verwendet wurde.
Ich habe damit gelöst:
function setpixelated(context){ context['imageSmoothingEnabled'] = false; /* standard */ context['mozImageSmoothingEnabled'] = false; /* Firefox */ context['oImageSmoothingEnabled'] = false; /* Opera */ context['webkitImageSmoothingEnabled'] = false; /* Safari */ context['msImageSmoothingEnabled'] = false; /* IE */ }
Sie können diese Funktion folgendermaßen verwenden:
var canvas = document.getElementById('mycanvas') setpixelated(canvas.getContext('2d'))
Vielleicht ist das für jemanden nützlich.
quelle
ctx.translate(0.5, 0.5); ctx.lineWidth = .5;
Mit dieser Kombination kann ich schöne 1px dünne Linien zeichnen.
quelle
Beachten Sie einen sehr begrenzten Trick. Wenn Sie ein 2-Farben-Bild erstellen möchten, können Sie eine beliebige Form mit der Farbe # 010101 auf einem Hintergrund mit der Farbe # 000000 zeichnen. Sobald dies erledigt ist, können Sie jedes Pixel in der imageData.data [] testen und auf 0xFF setzen, unabhängig davon, welcher Wert nicht 0x00 ist:
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);
Das Ergebnis ist ein nicht antialiasiertes Schwarzweißbild. Dies wird nicht perfekt sein, da ein gewisses Antialiasing stattfinden wird, aber dieses Antialiasing wird sehr begrenzt sein, da die Farbe der Form der Farbe des Hintergrunds sehr ähnlich ist.
quelle
Für diejenigen, die noch nach Antworten suchen. Hier ist meine Lösung.
Angenommen, das Bild ist 1 Kanal grau. Ich habe gerade nach ctx.stroke () einen Schwellenwert festgelegt.
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; } } }
Wenn Ihr Bildkanal 3 oder 4 ist, müssen Sie den Array-Index wie folgt ändern
quelle
Nur zwei Anmerkungen zur Antwort von StashOfCode:
Es ist besser, dies stattdessen zu tun:
Strich und fülle mit
#FFFFFF
, dann mache das:imageData.data[i] = (imageData.data[i] >> 7) * 0xFF
Das löst es für Linien mit 1px Breite.
Abgesehen davon ist die Lösung von StashOfCode perfekt, da Sie keine eigenen Rasterfunktionen schreiben müssen (denken Sie nicht nur an Linien, sondern auch an Bezier, Kreisbögen, gefüllte Polygone mit Löchern usw.).
quelle
Hier ist eine grundlegende Implementierung des Bresenham-Algorithmus in JavaScript. Es basiert auf der in diesem Wikipedia-Artikel beschriebenen ganzzahlig-arithmetischen Version: 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; }
quelle