Search code examples
flashactionscript-3bitmapdataerase

AS3 How to check if BitmapData is empty


I have a code to erase a masked movieclip. (credits here) I would like to know how I can check if the whole movieclip is been erased. So I thought I had to check if the BitmapData is empty, but I could be terribly wrong! How can I check if every pixel of the movieclip has been erased?
Of course my example below is wrong, but I think it has to be something like that.

if (erasableBitmapData = empty)
    { 
    trace("empty")
    }

    var lineSize:Number=40;
    var doDraw:Boolean=false;
    var resumeDrawing:Boolean=false;

    var erasableBitmapData:BitmapData = new BitmapData(700, 500, true, 0xFFFFFFFF);
    var erasableBitmap:Bitmap = new Bitmap(erasableBitmapData);
    erasableBitmap.cacheAsBitmap = true;
    addChild(erasableBitmap);

    maskee.cacheAsBitmap = true;
    maskee.mask = erasableBitmap;

    var eraserClip:Sprite = new Sprite();
    initEraser();
    function initEraser():void {
        eraserClip.graphics.lineStyle(lineSize,0xff0000);
        eraserClip.graphics.moveTo(stage.mouseX,stage.mouseY);
    }

    var drawnBitmapData:BitmapData = new BitmapData(700, 500, true, 0x00000000);
    var drawnBitmap:Bitmap = new Bitmap(drawnBitmapData);

    stage.addEventListener(MouseEvent.MOUSE_MOVE,maskMove);
    stage.addEventListener(MouseEvent.ROLL_OUT, maskOut); 
    stage.addEventListener(MouseEvent.ROLL_OVER,maskOver);
    stage.addEventListener(MouseEvent.MOUSE_DOWN,startDrawing);
    stage.addEventListener(MouseEvent.MOUSE_UP,stopDrawing);

    function startDrawing(e:MouseEvent):void {
        eraserClip.graphics.moveTo(stage.mouseX,stage.mouseY);
        doDraw=true;
    }

    function stopDrawing(e:MouseEvent):void {
        doDraw=false;
        resumeDrawing = false;
    }

    function maskOut(e:Event):void {
        if (doDraw){
            resumeDrawing = true;
        }
    }

    function maskOver(e:MouseEvent):void {
        if (resumeDrawing){
            resumeDrawing = false;
            eraserClip.graphics.moveTo(stage.mouseX,stage.mouseY);
        }
    }

    function maskMove(e:MouseEvent):void {
        if (doDraw && !resumeDrawing){
            eraserClip.graphics.lineTo(stage.mouseX,stage.mouseY);
            drawnBitmapData.fillRect(drawnBitmapData.rect, 0x00000000); 
            drawnBitmapData.draw(eraserClip , new Matrix(), null, BlendMode.NORMAL);
            erasableBitmapData.fillRect(erasableBitmapData.rect, 0xFFFFFFFF);
            erasableBitmapData.draw(drawnBitmap, new Matrix(), null, BlendMode.ERASE);
        }
            e.updateAfterEvent();
    }


    reset_btn.addEventListener(MouseEvent.CLICK,reset);

    function reset(e:Event):void {
        eraserClip.graphics.clear();
        initEraser();
        erasableBitmapData.fillRect(erasableBitmapData.rect, 0xFFFFFFFF);
    }

Solution

  • You can check if getColorBoundsRect returns an rectangle with an width and height of 0 for the colour you consider as 'empty', setting the findColor argument to false. There are other ways to do this, but this is at least many times faster than checking every single pixel.

    The default value of true for the findColor argument gives you an rectangle that encloses all pixels for those (pixelColor & mask) == emptyColor is true. When dealing with alpha values, a mask of 0xFF000000 can be used to ignore rbg values of a pixel and to only check its alpha value. So the mask 0xFF000000 and the colour 0xFF000000 would match all fully opaque pixels, while the mask 0xFF000000 and the colour 0x00000000 would match all fully transparent pixels. As meddlingwithfire pointed out, this won't do the job here. By setting findColor to false, this process is kind of reversed so that the rectangle will enclose all pixels that are not using the empty colour. For bitmaps containing no other colours, the result will be a rectangle with an area of 0.

    var maskColor:uint = 0xFF000000;
    var emptyColor:uint = 0x00000000;
    var bounds:Rectangle = erasableBitmapData.getColorBoundsRect(maskColor, emptyColor, false);
    if (bounds.width == 0 && bounds.height == 0){
        trace("empty"); // no visible pixels
    }
    

    Technically there is a difference between a black transparent pixel 0x00000000 and for example a red transparent pixel 0x00FF0000 - but there is no visible difference (both are invisible) so you should ignore the rgb values completely, as I did in the example by using that particular mask.