3

I paint freehand strokes on my canvas with a code like below. I need to check how much of the canvas is covered with strokes. What is a good way to check that? The only thing I can think of is to count the number of pixels that have the specific color on mouse up event. But it is lame because it is slow...

Any help?

$(document).ready(function(){
    var draw = false;
    var x_prev = null, y_prev = null;
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");
    canvas.mousedown(function(e){
        draw = true;
        x_prev = e.pageX - this.offsetLeft;
        y_prev = e.pageY - this.offsetTop;
    });
    window.mouseup(function(){draw=false});
    canvas.mousemove(function(e){
        if(draw){
            context.beginPath();
            context.moveTo(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
            context.lineTo(x_prev, y_prev);
            context.stroke();
            context.closePath();
            x_prev = e.pageX - this.offsetLeft;
            y_prev = e.pageY - this.offsetTop;
        }
    });
Phrogz
  • 296,393
  • 112
  • 651
  • 745
akonsu
  • 28,824
  • 33
  • 119
  • 194
  • How thick is the line? Also, are you accounting for anti-aliasing? – Blender Feb 14 '12 at 03:53
  • arbitrarily thick. but the thickness is hardcoded. I do not care much about anti-aliasing, I just need something that gives me a good approximation. this is for a game and I do not need a high precision. – akonsu Feb 14 '12 at 03:55
  • How are you going to handle line overlaps? You could just use `area += thickness * sqrt((e.pageX - this.offsetLeft - x_prev)^2 + (e.pageY - this.offsetTop - y_prev)^2);`, but that doesn't account for lines that intersect. – Blender Feb 14 '12 at 04:00
  • right, that is the problem :) I can figure out how to calculate the area of each line segment, given its thickness, but how to efficiently do it for all lines I do not know... – akonsu Feb 14 '12 at 04:03
  • You'll probably need to do something similar to [http://stackoverflow.com/questions/244452/what-is-an-efficient-algorithm-to-find-area-of-overlapping-rectangles](http://stackoverflow.com/questions/244452/what-is-an-efficient-algorithm-to-find-area-of-overlapping-rectangles). – Bill Feb 14 '12 at 05:01
  • Have you actually tried counting the number of pixels with an alpha over a particular threshold to prove that it is slow? – Phrogz Feb 15 '12 at 20:59

2 Answers2

3

Computers are fast. It seems plenty fast to me to re-count the number of pixels over a particular alpha each frame when drawing. Test it yourself here: http://jsfiddle.net/ZC8cB/3/

Relevant code:

var w = canvas.attr('width'),
    h = canvas.attr('height'),
    area = w * h;

function updateArea() {
  var data = context.getImageData(0, 0, w, h).data;
  for (var ct=0, i=3, len=data.length; i<len; i+=4) if (data[i]>50) ct++;
  $fill.html(ct);
  $pct.html((100 * ct / area).toFixed(2));
}

If this is really too slow, you could choose to update the area every other mousemove, every third mousemove, etc. or on an interval timer. For example, here's a very-slightly-modified version that only updates every tenth mousemove: http://jsfiddle.net/ZC8cB/4/

And if a single frame of counting is too slow—because you have a slow computer or huge canvas or both—then you can fetch the ImageData in one frame and each update frame count a particular portion of the pixels.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
1

Quantify the area to line width sized squares and count the number of unique squares encountered during draw.

var thickness = 4
var height = ..
var width = ..
var drawn = []
var covered = 0;

canvas.mousemove(function(e) {

    var x = e.pageX - this.ofsetLeft;
    var y = e.pageY - this.offsetTop;
    x = parseInt( x / width ) * ( width / thickness )
    y = parseInt( y / height ) * ( height / thickness )
    id = x + y * parseInt(thickness / width)
    if ( !drawn[ id ] ) {
       drawn[ id ] = 1;
       covered++;
    }

}

You can get drawn area in percents by dividing the covered squares by number of total squares

var a = covered / ((width / thickness) * (height / thickness))
Teemu Ikonen
  • 11,861
  • 4
  • 22
  • 35
  • thank you for your answer, would you please explain this code? – akonsu Feb 16 '12 at 03:08
  • Its very simple, it maintains a grid that keeps track how many squares (the thickness defines size) the mouse has travelled while drawing. The covered counter counts the number of painted squares. – Teemu Ikonen Feb 17 '12 at 08:46
  • thanks. this could work for me except for cases when mouse moves too fast and the event handler is not called for all points in the trajectory (that is why I use moveTo() and lineTo() in my code). – akonsu Feb 17 '12 at 13:56
  • right, in those cases you need to just extrapolate some to keep estimation more accurate. – Teemu Ikonen Feb 17 '12 at 14:43