HTML5画布与抗锯齿

如何打开 帆布上的 反锯齿

下面的代码没有画出一条平滑的线:

var context = mainCanv.getContext("2d");
if (context) {
context.moveTo(0,0);
context.lineTo(100,75);


context.strokeStyle = "#df4b26";
context.lineWidth = 3;
context.stroke();
}
129472 次浏览

反走样不能打开或关闭,并由浏览器控制。

我可以关闭 HTML < Canvab> 元素的反锯齿效果吗?

我不需要打开反别名,因为它是默认打开的,但我需要关闭它。如果它可以关闭,它也可以打开。

ctx.imageSmoothingEnabled = true;

我通常关闭它,当我在我的画布 rpg 工作,所以当我放大的图像看起来不模糊。

这里有一个解决方案,需要您绘制线像素的像素,但将防止反走样。

// some helper functions
// finds the distance between points
function DBP(x1,y1,x2,y2) {
return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
// finds the angle of (x,y) on a plane from the origin
function getAngle(x,y) { return Math.atan(y/(x==0?0.01:x))+(x<0?Math.PI:0); }
// the function
function drawLineNoAliasing(ctx, sx, sy, tx, ty) {
var dist = DBP(sx,sy,tx,ty); // length of line
var ang = getAngle(tx-sx,ty-sy); // angle of line
for(var i=0;i<dist;i++) {
// for each point along the line
ctx.fillRect(Math.round(sx + Math.cos(ang)*i), // round for perfect pixels
Math.round(sy + Math.sin(ang)*i), // thus no aliasing
1,1); // fill in one pixel, 1x1
}
}

基本上,你可以找到这条线的长度,然后一步一步地遍历这条线,四舍五入每个位置,然后填充一个像素。

叫它

var context = cv.getContext("2d");
drawLineNoAliasing(context, 20,30,20,50); // line from (20,30) to (20,50)

你可以将画布平移半个像素的距离。

ctx.translate(0.5, 0.5);

最初在物理像素之间的画布定位点。

如果你需要像素级的画布控制,你可以使用 CreateImageDataPutImageData

HTML:

<canvas id="qrCode" width="200", height="200">
QR Code
</canvas>

还有 JavaScript:

function setPixel(imageData, pixelData) {
var index = (pixelData.x + pixelData.y * imageData.width) * 4;
imageData.data[index+0] = pixelData.r;
imageData.data[index+1] = pixelData.g;
imageData.data[index+2] = pixelData.b;
imageData.data[index+3] = pixelData.a;
}


element = document.getElementById("qrCode");
c = element.getContext("2d");


pixcelSize = 4;
width = element.width;
height = element.height;




imageData = c.createImageData(width, height);


for (i = 0; i < 1000; i++) {
x = Math.random() * width / pixcelSize | 0; // |0 to Int32
y = Math.random() * height / pixcelSize| 0;


for(j=0;j < pixcelSize; j++){
for(k=0;k < pixcelSize; k++){
setPixel( imageData, {
x: x * pixcelSize + j,
y: y * pixcelSize + k,
r: 0 | 0,
g: 0 | 0,
b: 0 * 256 | 0,
a: 255 // 255 opaque
});
}
}
}


c.putImageData(imageData, 0, 0);

这是工作样本

现在是2018年,我们终于有了便宜的方法来做一些事情..。

事实上,由于2d 上下文 API 现在有了一个 filter属性,并且这个过滤器属性可以接受 SVGFilters,我们可以构建一个 SVGFilter,它只保留我们绘图中完全不透明的像素,从而消除默认的反锯齿。

因此,它不会禁用反走样本身,但提供了一个廉价的方式,无论是在实现和性能,以消除所有半透明像素绘制时。

我并不是 SVGFilters 的专家,所以可能有更好的方法,但是对于本例,我将使用 <feComponentTransfer>节点仅抓取完全不透明的像素。

var ctx = canvas.getContext('2d');
ctx.fillStyle = '#ABEDBE';
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = 'black';
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';


// first without filter
ctx.fillText('no filter', 60, 20);
drawArc();
drawTriangle();
// then with filter
ctx.setTransform(1, 0, 0, 1, 120, 0);
ctx.filter = 'url(#remove-alpha)';
// and do the same ops
ctx.fillText('no alpha', 60, 20);
drawArc();
drawTriangle();


// to remove the filter
ctx.filter = 'none';




function drawArc() {
ctx.beginPath();
ctx.arc(60, 80, 50, 0, Math.PI * 2);
ctx.stroke();
}


function drawTriangle() {
ctx.beginPath();
ctx.moveTo(60, 150);
ctx.lineTo(110, 230);
ctx.lineTo(10, 230);
ctx.closePath();
ctx.stroke();
}
// unrelated
// simply to show a zoomed-in version
var zCtx = zoomed.getContext('2d');
zCtx.imageSmoothingEnabled = false;
canvas.onmousemove = function drawToZoommed(e) {
var x = e.pageX - this.offsetLeft,
y = e.pageY - this.offsetTop,
w = this.width,
h = this.height;
    

zCtx.clearRect(0,0,w,h);
zCtx.drawImage(this, x-w/6,y-h/6,w, h, 0,0,w*3, h*3);
}
<svg width="0" height="0" style="position:absolute;z-index:-1;">
<defs>
<filter id="remove-alpha" x="0" y="0" width="100%" height="100%">
<feComponentTransfer>
<feFuncA type="discrete" tableValues="0 1"></feFuncA>
</feComponentTransfer>
</filter>
</defs>
</svg>


<canvas id="canvas" width="250" height="250" ></canvas>
<canvas id="zoomed" width="250" height="250" ></canvas>

对于那些不喜欢在 DOM 中附加 <svg>元素的用户,还可以将其保存为外部 svg 文件,并将 filter属性设置为 path/to/svg_file.svg#remove-alpha

所以我假设这个已经不再使用了,但是有一种方法是使用 document.body.style.zoom=2.0;,但是如果你这样做了,那么你所有的画布测量值将会被缩放所除。此外,设置更高的缩放为更多的别名。这很有帮助,因为它是可调节的。此外,如果使用这种方法,我建议您使函数做相同的 ctx.fillRect()等,但与缩放考虑。例如。

function fillRect(x, y, width, height) {
var zoom = document.body.style.zoom;
ctx.fillRect(x/zoom, y/zoom, width/zoom, height/zoom);
}

希望这个能帮上忙!

此外,一个旁注: 这也可以用来锐化圆形边缘,使他们看起来不那么模糊。只要使用0.5这样的变焦!

我需要结合0.5转换和像素数据操作:

    context.strokeStyle = "rgb(255, 255, 255)";
for (let [x1, y1, x2, y2] of linePoints) {
context.beginPath();
context.moveTo(x1 + 0.5, y1 + 0.5);
context.lineTo(x2 + 0.5, y2 + 0.5);
context.stroke();
}


// remove anti-aliasing
const id = context.getImageData(0, 0, width, height);
for (let y = 0; y < id.height; ++y) {
for (let x = 0; x < id.width; ++x) {
const index = (y * id.width + x) * 4;
id.data[index] = id.data[index] < 128 ? 0 : 255;
id.data[index+1] = id.data[index+1] < 128 ? 0 : 255;
id.data[index+2] = id.data[index+2] < 128 ? 0 : 255;
id.data[index+3] = id.data[index+3] < 128 ? 0 : 255;
}
}
context.putImageData(id, 0, 0);

我在坐标上加了0.5。这可以用 translate来完成。但那0.5很重要。如果没有它,左边缘像素和顶部边缘像素将被推离图像。它还修正了一些不一致的舍入。原本应该是一条直线的地方有些凹陷。

然后将所有像素值舍入为0或255。如果需要不同的颜色,可以四舍五入到这些值,而不是0或255。

我也有同样的问题,通过使用一个小阴影和线条上下文,我可以得到更平滑的线条,比如:

ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 0
ctx.shadowBlur = 2
ctx.shadowColor = 'black'