计算在HTML5画布上绘制的形状内的像素数量?

时间:2013-03-24 20:15:27

标签: jquery html5 html5-canvas

我正在使用sketch.js在HTML5画布上动态绘制非常简单的形状。

有没有办法计算画布上绘制的封闭形状内的像素数?

1 个答案:

答案 0 :(得分:4)

以下是如何计算画布上的非透明像素

// get a reference to your canvas
var c=document.getElementById("canvas");
var ctx=c.getContext("2d");

// get the pixel data from the canvas
var imgData=ctx.getImageData(0,0,c.width,c.height);

// loop through each pixel and count non-transparent pixels
var count=0;
for (var i=0;i<imgData.data.length;i+=4)
  {
      // nontransparent = imgData.data[i+3]==0
      if(imgData.data[i+3]==0){ count++; }
  }

[编辑:在画布上获取“if-filled”封闭形状的像素数]

我通常使用此代码在画布中进行遮罩,但我在此处对其进行了调整,以使您的像素数量达到闭合状态。

一些鱼子酱:

由于使用了“相邻”算法,对于未弯曲的形状,笔划宽度必须至少为2个像素宽,对于包含曲线的形状,行程宽度必须至少为3个像素宽。

由于Canvas会自动绘制具有消除锯齿功能的笔划,因此内部像素数将始终略大于预期。这是因为抗锯齿“进入”笔划,有效地导致比预期更多的内部像素。顺便说一句,无法关闭画布的抗锯齿,如果你尝试getImageData(),将所有形状像素设置为rbg(0,0,0),putImageData(),结果图像将也是抗锯齿 - 只是更多的锯齿!

以下是代码:

<!DOCTYPE html>
<html>
  <head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

//  The floodFill algorithm below is based on the good work by William Malone, Copyright 2010 William Malone (www.williammalone.com) -- Apache License: http://www.apache.org/licenses/LICENSE-2.0 -- Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

    var canvas=document.getElementById("canvas");
    var context = canvas.getContext("2d");
    var canvasWidth = canvas.width;
    var canvasHeight = canvas.height;
    var strokeColor =  {r: 0, g: 0, b: 0};
    var fillColor =  {r: 101,g: 155,b: 65};
    var fillData;
    var strokeData;

    function redraw() {
        context.clearRect(0, 0, canvasWidth, canvasHeight);
        context.putImageData(fillData, 0, 0);
        drawOutline(context);
    }

    function matchstrokeColor(r, g, b, a) {
      // must check for near black because of anti-aliasing
      return (r + g + b < 100 && a === 255);  
    }

    function matchStartColor(pixelPos, startR, startG, startB) {

      var r = strokeData.data[pixelPos],
        g = strokeData.data[pixelPos + 1],
        b = strokeData.data[pixelPos + 2],
        a = strokeData.data[pixelPos + 3];

      // If current pixel of the outline image is black-ish
      if (matchstrokeColor(r, g, b, a)) {
        return false;
      }

      r = fillData.data[pixelPos];
      g = fillData.data[pixelPos + 1];
      b = fillData.data[pixelPos + 2];

      // If the current pixel matches the clicked color
      if (r === startR && g === startG && b === startB) {
        return true;
      }

      // If current pixel matches the new color
      if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {
        return false;
      }

      return true;
    }

    function setPixel(pixelPos, r, g, b, a) {
      fillData.data[pixelPos] = r;
      fillData.data[pixelPos + 1] = g;
      fillData.data[pixelPos + 2] = b;
      fillData.data[pixelPos + 3] = a !== undefined ? a : 255;
    }

    function floodFill(startX, startY, startR, startG, startB) {

      var newPos;
      var x;
      var y;
      var   pixelPos;
      var   neighborLeft;
      var   neighborRight;
      var   pixelStack = [[startX, startY]];

      while (pixelStack.length) {

        newPos = pixelStack.pop();
        x = newPos[0];
        y = newPos[1];

        // Get current pixel position
        pixelPos = (y * canvasWidth + x) * 4;

        // Go up as long as the color matches and are inside the canvas
        while (y >= 0 && matchStartColor(pixelPos, startR, startG, startB)) {
          y -= 1;
          pixelPos -= canvasWidth * 4;
        }

        pixelPos += canvasWidth * 4;
        y += 1;
        neighborLeft = false;
        neighborRight = false;

        // Go down as long as the color matches and in inside the canvas
        while (y <= (canvasHeight-1) && matchStartColor(pixelPos, startR, startG, startB)) {
          y += 1;

          setPixel(pixelPos, fillColor.r, fillColor.g, fillColor.b);

          if (x > 0) {
            if (matchStartColor(pixelPos - 4, startR, startG, startB)) {
              if (!neighborLeft) {
                // Add pixel to stack
                pixelStack.push([x - 1, y]);
                neighborLeft = true;
              }
            } else if (neighborLeft) {
              neighborLeft = false;
            }
          }

          if (x < (canvasWidth-1)) {
            if (matchStartColor(pixelPos + 4, startR, startG, startB)) {
              if (!neighborRight) {
                // Add pixel to stack
                pixelStack.push([x + 1, y]);
                neighborRight = true;
              }
            } else if (neighborRight) {
              neighborRight = false;
            }
          }

          pixelPos += canvasWidth * 4;
        }
      }
    }

    // Fill
    function paintAt(startX, startY) {

      var pixelPos = (startY * canvasWidth + startX) * 4,
        r = fillData.data[pixelPos],
        g = fillData.data[pixelPos + 1],
        b = fillData.data[pixelPos + 2],
        a = fillData.data[pixelPos + 3];

      if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {
        // this one's already filled
        return;
      }

      if (matchstrokeColor(r, g, b, a)) {
        return;
      }

      floodFill(startX, startY, r, g, b);

      redraw();
    }


    function init() {

      var theShapes=document.getElementById("theShapes");
      var theShapesContext=theShapes.getContext("2d");
      drawOutline(theShapesContext);

      drawOutline(context);

      strokeData = context.getImageData(0, 0, canvasWidth, canvasHeight);
      context.clearRect(0, 0, context.canvas.width, context.canvas.height);
      fillData = context.getImageData(0, 0, canvasWidth, canvasHeight);

      $('#canvas').mousedown(function (e) {
        // Mouse down location
        var mouseX = e.pageX - this.offsetLeft;
        var mouseY = e.pageY - this.offsetTop;
        paintAt(mouseX, mouseY);
      });

      redraw();
    };

    function drawOutline(theContext){   
        theContext.beginPath();
        theContext.moveTo(55, 60);
        theContext.bezierCurveTo(35,  70,  35, 95,  85, 95);
        theContext.bezierCurveTo( 95,110, 130,110, 140, 95);
        theContext.bezierCurveTo(180, 95, 180, 80, 165, 70);
        theContext.bezierCurveTo(185, 40, 155, 35, 140, 45);
        theContext.bezierCurveTo(130, 25,  95, 30,  95, 45);
        theContext.bezierCurveTo( 70, 25,  45, 30,  55, 60);
        theContext.closePath();
        theContext.rect(200,30,100,70);
        theContext.lineWidth = 3;
        theContext.strokeStyle = 'rgb(0,0,0)';
        theContext.stroke();        
    }

    function getFilledPixelCount(theContext,theCanvas){
        // get the pixel data from the fill canvas
        var imgData=theContext.getImageData(0,0,theCanvas.width,theCanvas.height);
        console.log(imgData.data.length);
        var count=0;
        for (var i=0;i<imgData.data.length;i+=4){
              r = imgData.data[i],
              g = imgData.data[i + 1],
              b = imgData.data[i + 2],
              a = imgData.data[i + 3];
              if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {
                  count++;
              }
          }
        return(count);
    }

    $("#counter").click(function(){alert("There are "+getFilledPixelCount(context,canvas)+" filled pixels.");});

    init();

}); // end $(function(){});

</script>

  </head>
  <body>
        <p>The original stroked shapes</p>
        <canvas id="theShapes" width=350 height=150></canvas><br/>
        <p>The filled shapes used for pixel counting</p>
        <p>Click inside a shape below</p>
        <canvas id="canvas" width=350 height=150></canvas><br/>
        <button id="counter">Filled Count</button>
  </body>
</html>