Trigun
Trigun

Reputation: 93

slow script in the first 3 executions

i made a simple script for color a pixel and all the near pixel with the same color

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <script src="http://code.jquery.com/jquery-latest.js"></script>
        <title>
            Click foto
        </title>
<style type="text/css">
/*<![CDATA[*/
html, body{
    height: 100%;
}

/*]]>*/
</style>
    </head>
    <body>
     <div id="canvasDiv">
     </div>
    </body>

  <script>
    var canvasDiv = document.getElementById('canvasDiv');
canvas = document.createElement('canvas');
canvas.setAttribute('width', 500);
canvas.setAttribute('height', 500);
canvas.setAttribute('id', 'canvas');
$(canvasDiv).prepend(canvas);
if(typeof G_vmlCanvasManager != 'undefined') {
    canvas = G_vmlCanvasManager.initElement(canvas);
}

var context = canvas.getContext('2d');
var imageObj = new Image();

imageObj.onload = function() {
    $(canvas).attr({width : this.width, height: this.height});
    context.drawImage(imageObj,0,0);
};
imageObj.src = 'cartina_italia.png';


   $('#canvas').click(function(e){
   console.time('click');
     mouseX = e.pageX - this.offsetLeft;
     mouseY = e.pageY - this.offsetTop;
      c = this.getContext('2d');
      p = c.getImageData(mouseX, mouseY, 1, 1).data;
    hex = ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
    console.timeEnd('click');
    console.time('selectArea');
    selectArea(mouseX,mouseY,c,hex);
    console.timeEnd('selectArea');
    });

    function selectArea(x,y,c,color){
    if (x>=0 && y>=0){
      p = c.getImageData(x, y, 1, 1).data;
      hex =("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);

      if (color==hex){
        c.fillStyle = "rgba(255,0,0,0.1)";
        c.fillRect( x, y, 1, 1 );
        selectArea(x+1,y,c,color);
        selectArea(x-1,y,c,color);
        selectArea(x,y+1,c,color);
        selectArea(x,y-1,c,color);
      }
      }
    }

function rgbToHex(r, g, b) {
    if (r > 255 || g > 255 || b > 255)
        throw "Invalid color component";
    return ((r << 16) | (g << 8) | b).toString(16);
}

  </script>
</html>

i'm using for test this file: http://mappa.italiachecambia.org/assets/homemap/cartina_italia.png the first 3 times i click on a region for color it i have a slow response(1000-5000ms), after the first 3 times the function end in 50ms i can't use jsfiddle for show the problem bc i get cross-origin error

the code is simple recursive function that change the color of the clicked pixel and launched on the near pixel untill the pixel color is different from the first
but i don't understand why the first 3 times have this slow response and after the 4th is with 0 lag....

Upvotes: 0

Views: 47

Answers (1)

cyr_x
cyr_x

Reputation: 14257

You should cache the imageData of the hole image and work with that data instead of invoking getImageData on each selectArea call. Also you could think of implementing it iterative, to prevent maximum call stack errors.

Here's an example:

var ExtendedCanvas = (function() {
    var context, data, canvas;
    function ExtendedCanvas(selector, imageSrc) {
        var wrapper = document.querySelector(selector);
        this.element = canvas = document.createElement('canvas');
        context = this.element.getContext('2d');
        loadImage.call(this, imageSrc, function(image) {
            canvas.setAttribute('width', image.width);
            canvas.setAttribute('height', image.height);
            context.drawImage(image,0,0);
            data = context.getImageData(0,0,canvas.width, canvas.height);
        });
        wrapper.appendChild(this.element);
    }

    function loadImage(src, cb) {
        var image = new Image();
        var canvas = this.element;
        image.onload = function() {
            cb(this);
        }
        image.crossOrigin = 'Anonymous';
        image.src = src;
    }

    ExtendedCanvas.prototype.getPixelIndex = function(x, y) {
        return (Math.floor(y) * canvas.width + Math.floor(x)) * 4;
    }

    ExtendedCanvas.prototype.getPixelColor = function(x, y) {
        var index = this.getPixelIndex(x, y);
        var d =  data.data;
        var r = d[index];
        var g = d[index + 1];
        var b = d[index + 2];
        var a = d[index + 3];
        return [r, g, b, a];
    }

    ExtendedCanvas.prototype.setPixelColor = function(x, y, color) {
        var index = this.getPixelIndex(x, y);
        var d = data.data;
        d[index] = color[0];
        d[index + 1] = color[1];
        d[index + 2] = color[2];
        d[index + 3] = color[3];
    }

    ExtendedCanvas.prototype.fill = function(x, y, fillColor) {
        if(x < 0 || y < 0 || x > canvas.width || y > canvas.height) {
            return;
        }
        fillColor = fillColor || [0,0,0,255];
        var stack = [];
        var color = this.getPixelColor(x, y).join();

        if(color === fillColor) {
            return;
        }

        stack.push([x, y]);
        context.fillStyle = fillColor;

        if(color === fillColor.join()) {
            return;
        }
        while(stack.length > 0) {
            var position = stack.pop();
            var posX = position[0];
            var posY = position[1];
            var posColor = this.getPixelColor(posX, posY).join();
            if(posColor === color) {
                this.setPixelColor(posX, posY, fillColor);
                stack.push([posX, posY + 1]);
                stack.push([posX, posY - 1]);
                stack.push([posX + 1, posY]);
                stack.push([posX - 1, posY]);
            }
        }
        context.putImageData(data, 0, 0);
    }

    return ExtendedCanvas;
})();

document.addEventListener('DOMContentLoaded', function() {
    var c = new ExtendedCanvas('#canvasWrapper', 'https://i.imgur.com/QWaKVGO.png');

    c.element.addEventListener('click', function(e) {
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        c.fill(x, y);
    });
});
<div id="canvasWrapper"></div>

Upvotes: 1

Related Questions