Bawię się <canvas>
elementem, rysuję linie i tym podobne.
Zauważyłem, że moje ukośne linie są wygładzone. Wolałbym postrzępiony wygląd tego, co robię - czy jest jakiś sposób na wyłączenie tej funkcji?
Bawię się <canvas>
elementem, rysuję linie i tym podobne.
Zauważyłem, że moje ukośne linie są wygładzone. Wolałbym postrzępiony wygląd tego, co robię - czy jest jakiś sposób na wyłączenie tej funkcji?
Odpowiedzi:
W przypadku obrazów jest teraz .context.imageSmoothingEnabled
= false
Jednak nie ma nic, co bezpośrednio kontroluje rysowanie linii. Być może będziesz musiał narysować własne linie ( trudna droga ) za pomocą getImageData
i putImageData
.
putImageData
ale nadal działa aliasing pobliskich pikseli.
Narysuj swoje 1-pixel
linie na współrzędnych takich jak ctx.lineTo(10.5, 10.5)
. Narysowanie jednopikselowej linii nad punktem (10, 10)
oznacza, że ten 1
piksel w tym miejscu sięga od 9.5
do, 10.5
co powoduje powstanie dwóch linii, które zostaną narysowane na płótnie.
Fajną sztuczką, która nie zawsze musi być dodawana 0.5
do rzeczywistej współrzędnej, którą chcesz narysować, jeśli masz dużo jednopikselowych linii, jest ctx.translate(0.5, 0.5)
na początku całe płótno.
ctx.translate(0.5,0.5)
nie. na FF39.0
Można to zrobić w przeglądarce Mozilla Firefox. Dodaj to do swojego kodu:
contextXYZ.mozImageSmoothingEnabled = false;
W Operze jest to obecnie propozycja funkcji, ale miejmy nadzieję, że zostanie wkrótce dodana.
"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"
Antyaliasing jest wymagany do prawidłowego kreślenia grafiki wektorowej z wykorzystaniem współrzędnych innych niż całkowite (0,4, 0,4), co robi wszyscy oprócz bardzo niewielu klientów.
Gdy podane współrzędne nie są liczbami całkowitymi, płótno ma dwie opcje:
Późniejsza strategia będzie działać w przypadku grafik statycznych, chociaż w przypadku małych grafik (okrąg o promieniu 2) krzywe będą zawierały wyraźne kroki, a nie gładką krzywą.
Prawdziwym problemem jest to, gdy grafika jest tłumaczona (przenoszona) - skoki między jednym pikselem a drugim (1,6 => 2, 1,4 => 1), oznaczają, że pochodzenie kształtu może przeskakiwać w stosunku do kontenera nadrzędnego (ciągle przesuwa się 1 piksel w górę / w dół i w lewo / w prawo).
Wskazówka 1 : Możesz zmiękczyć (lub utwardzić) antyaliasing, skalując płótno (powiedzmy przez x), a następnie zastosuj skalę odwrotności (1 / x) do geometrii samodzielnie (nie używając płótna).
Porównaj (bez skalowania):
z (skala płótna: 0,75; skala ręczna: 1,33):
oraz (skala płótna: 1,33; skala ręczna: 0,75):
Wskazówka 2 : Jeśli naprawdę szukasz postrzępionego wyglądu, spróbuj narysować każdy kształt kilka razy (bez wymazywania). Z każdym rysowaniem piksele antyaliasingu stają się ciemniejsze.
Porównać. Po jednorazowym narysowaniu:
Po trzykrotnym losowaniu:
Narysowałbym wszystko za pomocą niestandardowego algorytmu liniowego, takiego jak algorytm liniowy Bresenham. Sprawdź tę implementację javascript: http://members.chello.at/easyfilter/canvas.html
Myślę, że to z pewnością rozwiąże Twoje problemy.
setPixel(x, y)
; Użyłem zaakceptowanej odpowiedzi tutaj: stackoverflow.com/questions/4899799/…
Chcę dodać, że miałem problem podczas zmniejszania rozmiaru obrazu i rysowania na płótnie, nadal korzystałem z wygładzania, mimo że nie było używane podczas skalowania.
Rozwiązałem za pomocą tego:
function setpixelated(context){
context['imageSmoothingEnabled'] = false; /* standard */
context['mozImageSmoothingEnabled'] = false; /* Firefox */
context['oImageSmoothingEnabled'] = false; /* Opera */
context['webkitImageSmoothingEnabled'] = false; /* Safari */
context['msImageSmoothingEnabled'] = false; /* IE */
}
Możesz użyć tej funkcji w następujący sposób:
var canvas = document.getElementById('mycanvas')
setpixelated(canvas.getContext('2d'))
Może to się komuś przyda.
ctx.translate(0.5, 0.5);
ctx.lineWidth = .5;
Dzięki tej kombinacji mogę narysować ładne cienkie linie 1px.
Zwróć uwagę na bardzo ograniczoną sztuczkę. Jeśli chcesz stworzyć obraz w dwóch kolorach, możesz narysować dowolny kształt kolorem # 010101 na tle w kolorze # 000000. Gdy to zrobisz, możesz przetestować każdy piksel w imageData.data [] i ustawić na 0xFF dowolną wartość inną niż 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);
Rezultatem będzie czarno-biały obraz bez antyaliasingu. Nie będzie to idealne, ponieważ będzie miał miejsce jakiś antyaliasing, ale ten antyaliasing będzie bardzo ograniczony, a kolor kształtu będzie bardzo podobny do koloru tła.
Dla tych, którzy wciąż szukają odpowiedzi. oto moje rozwiązanie.
Zakładając, że obraz jest 1-kanałowy szary. Właśnie przekroczyłem próg po 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;
}
}
}
jeśli twój kanał obrazu to 3 lub 4. musisz zmodyfikować indeks tablicy, np
x*image.height*number_channel + y*number_channel + channel
Tylko dwie uwagi na temat odpowiedzi StashOfCode:
Zamiast tego lepiej to zrobić:
Obrysuj i wypełnij #FFFFFF
, a następnie zrób to:
imageData.data[i] = (imageData.data[i] >> 7) * 0xFF
To rozwiązuje problem dla linii o szerokości 1 piksela.
Poza tym rozwiązanie StashOfCode jest idealne, ponieważ nie wymaga pisania własnych funkcji rasteryzacji (pomyśl nie tylko o liniach, ale także bezierach, łukach kołowych, wypełnionych wielokątach z dziurami itp.)
Oto podstawowa implementacja algorytmu Bresenham w JavaScript. Opiera się na wersji integer-arytmetycznej opisanej w tym artykule na Wikipedii: 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;
}