HTML <canvas> 요소에서 앤티 앨리어싱을 끌 수 있습니까?


85

나는 <canvas>요소, 선 그리기 등을 가지고 놀고 있습니다 .

내 대각선이 앤티 앨리어싱 된 것으로 나타났습니다. 내가하는 일에 대해 재기 모양을 선호합니다.이 기능을 끄는 방법이 있습니까?


오히려 브라우저 관련이라고 생각합니다. 사용하는 소프트웨어에 대한 추가 정보가 도움이 될 수 있습니다.
Tomalak

나는 크로스 브라우저 방법, 그러나 어떤 하나의 브라우저에서 작동 여전히 나에게 흥미로운 일이 될 것이라고하는 방법을 선호하는 것
Blorgbeard은 밖으로

이 주제에 대해 아직 변경 사항이 있는지 확인하고 싶었습니다.
vternal3 2011 년


이것에 대한 업데이트가 있습니까?
Roland

답변:


60

이미지의 경우 .context.imageSmoothingEnabled= false

그러나 선 그리기를 명시 적으로 제어하는 ​​것은 없습니다. 당신은 당신의 자신의 선 (그릴 필요가 있습니다 어려운 방법을 사용) getImageData하고 putImageData.


1
자바 스크립트 라인 알고리즘의 성능에 대해 궁금합니다.
Blorgbeard는

브라우저 공급 업체는 최근 새로운 초고속 JS 엔진을 선전하고 있으므로 마침내 좋은 사용이 될 것입니다.
Kornel

1
정말 효과가 있나요? 나는 사용하여 선을 그리고 putImageData 있지만 여전히 근처 픽셀의 앨리어싱을 수행합니다.
Pacerier

더 작은 캔버스 (캐시)에 그린 다음 해당 설정을 끈 상태에서 다른 캔버스에 drawImage를 그린 경우 의도 한대로 작동합니까?
SparK

69

당신의 그리기 1-pixel와 같은 좌표에 줄을 ctx.lineTo(10.5, 10.5). 점 위에 1 픽셀 라인 드로잉 (10, 10)이 있다는 의미 1에서, 그 위치에서 화소에 도달 9.5하는 10.5어떤 캔버스 그려 질 두 라인을 초래한다.

0.51 픽셀 선이 많은 경우 그릴 실제 좌표에를 항상 추가 할 필요가없는 좋은 방법 ctx.translate(0.5, 0.5)은 처음에 전체 캔버스에 추가하는 것입니다.


흠,이 기술을 사용하여 앤티 앨리어싱을 제거하는 데 문제가 있습니다. 어쩌면, 내가 뭔가를 이해하고 싶습니까? 어떤 곳에 예를 올려 주시겠습니까?
Xavi

7
이것은 앤티 앨리어싱을 제거하지는 않지만 실제로 1 픽셀을 원할 때 2 픽셀 두께의 당황스러운 수평선 또는 수직선을 제거하는 것과 같이 앤티 앨리어싱 된 선을 훨씬 더보기 좋게 만듭니다.
데이비드 감안할

1
@porneL : 아니요, 픽셀 모서리 사이에 선이 그려집니다. 선 너비가 1 픽셀이면 어느 방향 으로든 1/2 픽셀 확장
Eric

+0.5를 더하면 효과가 있지만 ctx.translate(0.5,0.5)그렇지 않았습니다. FF39.0
Paulo Bueno

대단히 감사합니다! 변경을 위해 실제 1px 라인이 있다는 것을 믿을 수 없습니다!
땅딸막 한 덩어리

24

Mozilla Firefox에서 수행 할 수 있습니다. 코드에 다음을 추가하십시오.

contextXYZ.mozImageSmoothingEnabled = false;

Opera에서는 현재 기능 요청이지만 곧 추가 될 예정입니다.


멋있는. 귀하의 기여에 +1. AA의 비활성화가 linedrawing 속도 궁금
marcusklaas

7
OP는 안티 앨리어싱 라인을 원하지만 이것은 이미지에서만 작동합니다. 당 사양 , 그것은 결정"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

14

벡터 그래픽을 앤티 앨리어싱해야합니다.

앤티 앨리어싱은 정수가 아닌 좌표 (0.4, 0.4)를 포함하는 벡터 그래픽을 올바르게 플로팅하는 데 필요 합니다.

정수가 아닌 좌표가 주어지면 캔버스에는 두 가지 옵션이 있습니다.

  • 앤티 앨리어싱 -정수 좌표가 정수가 아닌 좌표 (즉, 반올림 오류)에서 얼마나 멀리 떨어져 있는지에 따라 좌표 주변의 픽셀을 페인트합니다.
  • 반올림 -정수가 아닌 좌표에 반올림 기능을 적용합니다 (예를 들어 1.4는 1이됩니다).

작은 그래픽 (반지름이 2 인 원)의 경우 곡선은 부드러운 곡선이 아닌 명확한 단계를 표시하지만 이후 전략은 정적 그래픽에 적용됩니다.

실제 문제는 그래픽이 변환 (이동) 될 때입니다. 즉, 한 픽셀과 다른 픽셀 사이의 점프 (1.6 => 2, 1.4 => 1)는 모양의 원점이 부모 컨테이너와 관련하여 점프 할 수 있음을 의미합니다 (계속 이동). 1 픽셀 위 / 아래 및 왼쪽 / 오른쪽).

몇 가지 팁

팁 # 1 : 캔버스 크기를 조정 (예 : x)하여 앤티 앨리어싱을 부드럽게 (또는 강화) 한 다음 (캔버스를 사용하지 않고) 형상에 상호 배율 (1 / x)을 직접 적용 할 수 있습니다.

비교 (배율 없음) :

몇 개의 직사각형

(캔버스 배율 : 0.75; 수동 배율 : 1.33) :

가장자리가 더 부드러운 동일한 직사각형

및 (캔버스 스케일 : 1.33; 수동 스케일 : 0.75) :

가장자리가 더 어두운 동일한 직사각형

팁 # 2 : 칙칙한 표정이 정말로 당신이 추구하는 것이라면, 각 모양을 몇 번 (지우지 않고) 그려보십시오. 그릴 때마다 앤티 앨리어싱 픽셀이 더 어두워집니다.

비교. 한 번 그린 후 :

몇 가지 경로

세 번 그린 후 :

경로는 동일하지만 더 어둡고 눈에 보이는 앤티 앨리어싱이 없습니다.


@vanowm이와 복제 및 재생을 주시기 바랍니다 : github.com/Izhaki/gefri를 . 모든 이미지는 / demo 폴더의 스크린 샷입니다 (팁 # 2에서 코드가 약간 수정 됨). 그려진 그림에 정수 반올림 (4 분 걸림)을 쉽게 도입 한 다음 드래그하여 효과를 확인하는 것이 쉬울 것이라고 확신합니다.
Izhaki

9

Bresenham의 선 알고리즘과 같은 사용자 정의 선 알고리즘을 사용하여 모든 것을 그립니다. 이 자바 스크립트 구현을 확인하세요 : http://members.chello.at/easyfilter/canvas.html

나는 이것이 당신의 문제를 확실히 해결할 것이라고 생각합니다.


2
정확히 내가 필요한 것은 구현해야한다는 것뿐입니다 setPixel(x, y). 여기 허용 대답을 사용 : stackoverflow.com/questions/4899799/...
티나에 Vall에게

8

이미지를 축소하고 캔버스에 그릴 때 어려움이 있었는데, 업 스케일링시에는 사용하지 않았는데도 여전히 스무딩을 사용하고 있었다고 덧붙이고 싶습니다.

나는 이것을 사용하여 해결했습니다.

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

누군가에게 유용 할 수도 있습니다.


context.imageSmoothingEnabled = false가 아닌 이유는 무엇입니까?
마티 페르

이것은 내가 내 대답을 썼을 때 작동하지 않았습니다. 지금 작동합니까?
eri0o

1
obj [ 'name'] 또는 obj.name을 작성하는 자바 스크립트에서 정확히 똑같습니다. 객체는 이름이 지정된 값 (튜플)의 모음입니다. 해시 테이블, 두 표기법 모두 동일한 방식으로 처리됩니다. 이전에 코드가 작동하지 않았을 이유가 전혀 없으며, 최악의 경우 효과가없는 값을 할당합니다 (다른 브라우저를위한 것이기 때문입니다. 간단한 예 : write) OBJ = {A : (123)};을 console.log (OBJ [ 'A'] === obj.a "예 진정한": "아니오가 아니다"?)
마티 페르

나는 당신이 왜 다른 모든 것을 가지고 있는지를 의미한다고 생각했는데, 내 의견에서 의미하는 것은 브라우저가 다른 속성을 필요로한다는 것입니다.
eri0o

네 물론 그렇습니다 :) 나는 코드 자체의 유효성이 아니라 구문에 대해 이야기했습니다 (작동합니다)
Martijn Scheffer

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

이 콤보로 멋진 1px 선을 그릴 수 있습니다.


6
lineWidth를 .5로 설정할 필요는 없습니다.
aaaidan

4

매우 제한된 트릭에 주목하십시오. 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

여전히 답을 찾고있는 사람들을 위해. 여기 내 해결책이 있습니다.

이미지가 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

0

StashOfCode의 답변에 대한 두 가지 참고 사항 :

  1. 회색조, 불투명 캔버스에서만 작동합니다 (흰색으로 fillRect 한 다음 검정으로 그리거나 그 반대).
  2. 선이 얇 으면 실패 할 수 있습니다 (~ 1px 선 너비).

대신 이렇게하는 것이 좋습니다.

획을 긋고으로 채운 #FFFFFF다음 다음을 수행하십시오.

imageData.data[i] = (imageData.data[i] >> 7) * 0xFF

1px 너비의 선에 대해 해결됩니다.

그 외에도 StashOfCode의 솔루션은 자체 래스터 화 함수를 작성할 필요가 없기 때문에 완벽합니다 (선뿐만 아니라 베 지어, 원호, 구멍이있는 채워진 다각형 등을 생각해보십시오.).


0

다음은 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;

    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.