나는 <canvas>
요소, 선 그리기 등을 가지고 놀고 있습니다 .
내 대각선이 앤티 앨리어싱 된 것으로 나타났습니다. 내가하는 일에 대해 재기 모양을 선호합니다.이 기능을 끄는 방법이 있습니까?
나는 <canvas>
요소, 선 그리기 등을 가지고 놀고 있습니다 .
내 대각선이 앤티 앨리어싱 된 것으로 나타났습니다. 내가하는 일에 대해 재기 모양을 선호합니다.이 기능을 끄는 방법이 있습니까?
답변:
이미지의 경우 .context.imageSmoothingEnabled
= false
그러나 선 그리기를 명시 적으로 제어하는 것은 없습니다. 당신은 당신의 자신의 선 (그릴 필요가 있습니다 어려운 방법을 사용) getImageData
하고 putImageData
.
putImageData
있지만 여전히 근처 픽셀의 앨리어싱을 수행합니다.
당신의 그리기 1-pixel
와 같은 좌표에 줄을 ctx.lineTo(10.5, 10.5)
. 점 위에 1 픽셀 라인 드로잉 (10, 10)
이 있다는 의미 1
에서, 그 위치에서 화소에 도달 9.5
하는 10.5
어떤 캔버스 그려 질 두 라인을 초래한다.
0.5
1 픽셀 선이 많은 경우 그릴 실제 좌표에를 항상 추가 할 필요가없는 좋은 방법 ctx.translate(0.5, 0.5)
은 처음에 전체 캔버스에 추가하는 것입니다.
ctx.translate(0.5,0.5)
그렇지 않았습니다. FF39.0
Mozilla Firefox에서 수행 할 수 있습니다. 코드에 다음을 추가하십시오.
contextXYZ.mozImageSmoothingEnabled = false;
Opera에서는 현재 기능 요청이지만 곧 추가 될 예정입니다.
앤티 앨리어싱은 정수가 아닌 좌표 (0.4, 0.4)를 포함하는 벡터 그래픽을 올바르게 플로팅하는 데 필요 합니다.
정수가 아닌 좌표가 주어지면 캔버스에는 두 가지 옵션이 있습니다.
작은 그래픽 (반지름이 2 인 원)의 경우 곡선은 부드러운 곡선이 아닌 명확한 단계를 표시하지만 이후 전략은 정적 그래픽에 적용됩니다.
실제 문제는 그래픽이 변환 (이동) 될 때입니다. 즉, 한 픽셀과 다른 픽셀 사이의 점프 (1.6 => 2, 1.4 => 1)는 모양의 원점이 부모 컨테이너와 관련하여 점프 할 수 있음을 의미합니다 (계속 이동). 1 픽셀 위 / 아래 및 왼쪽 / 오른쪽).
팁 # 1 : 캔버스 크기를 조정 (예 : x)하여 앤티 앨리어싱을 부드럽게 (또는 강화) 한 다음 (캔버스를 사용하지 않고) 형상에 상호 배율 (1 / x)을 직접 적용 할 수 있습니다.
비교 (배율 없음) :
(캔버스 배율 : 0.75; 수동 배율 : 1.33) :
및 (캔버스 스케일 : 1.33; 수동 스케일 : 0.75) :
팁 # 2 : 칙칙한 표정이 정말로 당신이 추구하는 것이라면, 각 모양을 몇 번 (지우지 않고) 그려보십시오. 그릴 때마다 앤티 앨리어싱 픽셀이 더 어두워집니다.
비교. 한 번 그린 후 :
세 번 그린 후 :
Bresenham의 선 알고리즘과 같은 사용자 정의 선 알고리즘을 사용하여 모든 것을 그립니다. 이 자바 스크립트 구현을 확인하세요 : http://members.chello.at/easyfilter/canvas.html
나는 이것이 당신의 문제를 확실히 해결할 것이라고 생각합니다.
setPixel(x, y)
. 여기 허용 대답을 사용 : stackoverflow.com/questions/4899799/...
이미지를 축소하고 캔버스에 그릴 때 어려움이 있었는데, 업 스케일링시에는 사용하지 않았는데도 여전히 스무딩을 사용하고 있었다고 덧붙이고 싶습니다.
나는 이것을 사용하여 해결했습니다.
function setpixelated(context){
context['imageSmoothingEnabled'] = false; /* standard */
context['mozImageSmoothingEnabled'] = false; /* Firefox */
context['oImageSmoothingEnabled'] = false; /* Opera */
context['webkitImageSmoothingEnabled'] = false; /* Safari */
context['msImageSmoothingEnabled'] = false; /* IE */
}
이 기능을 다음과 같이 사용할 수 있습니다.
var canvas = document.getElementById('mycanvas')
setpixelated(canvas.getContext('2d'))
누군가에게 유용 할 수도 있습니다.
매우 제한된 트릭에 주목하십시오. 2 가지 색상의 이미지를 만들고 싶다면 색상 # 000000으로 배경에 색상 # 010101로 원하는 모양을 그릴 수 있습니다. 이 작업이 완료되면 imageData.data []의 각 픽셀을 테스트하고 0x00이 아닌 값을 0xFF로 설정할 수 있습니다.
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);
결과는 앤티 앨리어싱되지 않은 흑백 사진이됩니다. 일부 앤티 앨리어싱이 발생하기 때문에 완벽하지는 않지만이 앤티 앨리어싱은 매우 제한되어 모양의 색상이 배경색과 매우 유사합니다.
여전히 답을 찾고있는 사람들을 위해. 여기 내 해결책이 있습니다.
이미지가 1 채널 회색이라고 가정합니다. 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;
}
}
}
이미지 채널이 3 또는 4 인 경우 다음과 같이 배열 인덱스를 수정해야합니다.
x*image.height*number_channel + y*number_channel + channel
StashOfCode의 답변에 대한 두 가지 참고 사항 :
대신 이렇게하는 것이 좋습니다.
획을 긋고으로 채운 #FFFFFF
다음 다음을 수행하십시오.
imageData.data[i] = (imageData.data[i] >> 7) * 0xFF
1px 너비의 선에 대해 해결됩니다.
그 외에도 StashOfCode의 솔루션은 자체 래스터 화 함수를 작성할 필요가 없기 때문에 완벽합니다 (선뿐만 아니라 베 지어, 원호, 구멍이있는 채워진 다각형 등을 생각해보십시오.).
다음은 JavaScript에서 Bresenham 알고리즘의 기본 구현입니다. 이 위키피디아 문서에 설명 된 정수 산술 버전을 기반으로합니다 : 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;
}