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.