在HTML5Canvas中绘制和填充NOT抗锯齿圈的最快方法

时间:2017-08-17 19:44:42

标签: javascript algorithm html5-canvas

我需要为HTML5Canvas中的基本绘图应用绘制并填充一个非消除锯齿的圆圈,因为填充刀具工具算法不能很好地填充抗锯齿形状边界。

我使用了此页面的javascript算法https://en.wikipedia.org/wiki/Midpoint_circle_algorithm 并实现它绘制实心圆,但它非常慢。

canvas = document.getElementById("canvas");
const CHANNELS_PER_PIXEL = 4; //rgba

function drawCircle (x0, y0, radius, canvas) {
var x = radius-1;
var y = 0;
var dx = 1;
var dy = 1;
var decisionOver2 = dx - (radius << 1);   // Decision criterion divided by 2 evaluated at x=r, y=0
var imageWidth = canvas.width;
var imageHeight = canvas.height;
var context = canvas.getContext('2d');
var imageData = context.getImageData(0, 0, imageWidth, imageHeight);
var pixelData = imageData.data;
var makePixelIndexer = function (width) {
    return function (i, j) {
        var index = CHANNELS_PER_PIXEL * (j * width + i);
        //index points to the Red channel of pixel 
        //at column i and row j calculated from top left
        return index;
    };
};
var pixelIndexer = makePixelIndexer(imageWidth);
var drawPixel = function (x, y) {
    var idx = pixelIndexer(x,y);
    pixelData[idx] = 152;   //red
    pixelData[idx + 1] = 152; //green
    pixelData[idx + 2] = 152;//blue
    pixelData[idx + 3] = 255;//alpha
};

while (x >= y) {

    if(x0 + x>=0){drawPixel(x0 + x, y0 + y);}
    if(x0 + y>=0){drawPixel(x0 + y, y0 + x);}
    if(x0 - x>=0){drawPixel(x0 - x, y0 + y);} 
    if(x0 - y>=0){drawPixel(x0 - y, y0 + x);} 
    if(x0 - x>=0){drawPixel(x0 - x, y0 - y);}
    if(x0 - y>=0){drawPixel(x0 - y, y0 - x);}
    if(x0 + x>=0){drawPixel(x0 + x, y0 - y);}
    if(x0 + y>=0){drawPixel(x0 + y, y0 - x);}

    //fill circle code
    var x1=x0-x;
    var x2=x0+x;
    var xx=x2-x1;

        for(i=x2-x1;i>0; i--){
            if((x1+(xx-i))>=0){
            drawPixel(x1+(xx-i),y0+y);
            }
        }

    var x1=x0-y;
    var x2=x0+y;
    var xx=x2-x1;

        for(i=x2-x1;i>0; i--){
            if((x1+(xx-i))>=0){
            drawPixel(x1+(xx-i),y0+x);
            }
        }

    var x1=x0-x;
    var x2=x0+x;
    var xx=x2-x1;

        for(i=x2-x1;i>0; i--){
            if((x1+(xx-i))>=0){
            drawPixel(x1+(xx-i),y0-y);
            }
        }

    var x1=x0-y;
    var x2=x0+y;
    var xx=x2-x1;

        for(i=x2-x1;i>0; i--){
            if((x1+(xx-i))>=0){
            drawPixel(x1+(xx-i),y0-x);
            }
        }
    //fill end

    if (decisionOver2 <= 0) 
    {
        y++;
        decisionOver2 += dy; // Change in decision criterion for y -> y+1
        dy += 2;
    } 
    if (decisionOver2 > 0)
    {
        x--;
        dx += 2;
        decisionOver2 += (-radius << 1) + dx; // Change for y -> y+1, x -> x-1
    }
}

context.putImageData(imageData, 0, 0);
}

此外,

context.translate(0.5, 0.5);

context.imageSmoothingEnabled = !1;

不适用于一个圈子。

你有更好的功能,或者你知道如何压缩和连接这个圆形算法吗?

由于

1 个答案:

答案 0 :(得分:2)

我制作了Breseham圈算法的这个修改版本来填充#34;别名&#34;圈了一会儿回来了#34;复古&#34;项目

修改是从8个切片中取出值并将它们转换为4行。我们可以使用rect()来创建一条线,但必须将绝对(x2,y2)坐标转换为宽度和高度。

该方法只需将rect添加到相当快的路径中,您就不必通过慢速getImageData() / putImageData()(和没有受到CORS问题的影响)。最后,调用一个单一的填充操作。这意味着您也可以直接在画布上使用它,而不必担心大多数情况下的现有内容。

翻译和给定值是整数值,并且半径&gt;是非常重要的。 0

要强制整数值,只需移动值0:

xc = xc|0;  // you can add these to the function below
yc = yc|0;
r  = r|0;

(如果你想制作一个大纲(&#34;抚摸&#34;)版本,你必须使用所有8个切片位置,并将rect()的宽度更改为1。)

演示

snapshot 4x

&#13;
&#13;
var ctx = c.getContext("2d");
ctx.fillStyle = "#09f";
aliasedCircle(ctx, 200, 200, 180);
ctx.fill();
  
function aliasedCircle(ctx, xc, yc, r) {  // NOTE: for fill only!
  var x = r, y = 0, cd = 0;

  // middle line
  ctx.rect(xc - x, yc, r<<1, 1);

  while (x > y) {
    cd -= (--x) - (++y);
    if (cd < 0) cd += x++;
    ctx.rect(xc - y, yc - x, y<<1, 1);    // upper 1/4
    ctx.rect(xc - x, yc - y, x<<1, 1);    // upper 2/4
    ctx.rect(xc - x, yc + y, x<<1, 1);    // lower 3/4
    ctx.rect(xc - y, yc + x, y<<1, 1);    // lower 4/4
  }
}
&#13;
<canvas id=c width=400 height=400></canvas>
&#13;
&#13;
&#13;

放大演示:

&#13;
&#13;
var ctx = c.getContext("2d");
ctx.scale(4,4);
ctx.fillStyle = "#09f";
aliasedCircle(ctx, 50, 50, 45);
ctx.fill();

ctx.font = "6px sans-serif";
ctx.fillText("4x", 2, 8);

function aliasedCircle(ctx, xc, yc, r) {
  var x = r, y = 0, cd = 0;

  // middle line
  ctx.rect(xc - x, yc, r<<1, 1);

  while (x > y) {
    cd -= (--x) - (++y);
    if (cd < 0) cd += x++;
    ctx.rect(xc - y, yc - x, y<<1, 1);  // upper 1/4
    ctx.rect(xc - x, yc - y, x<<1, 1);  // upper 2/4
    ctx.rect(xc - x, yc + y, x<<1, 1);  // lower 3/4
    ctx.rect(xc - y, yc + x, y<<1, 1);  // lower 4/4
  }
}
&#13;
<canvas id=c width=400 height=400></canvas>
&#13;
&#13;
&#13;