Johannes
Johannes

Reputation: 177

HTML5 Canvas, replacing colors in an image not working on some machines

I have a 2d RTS HTML5 / Javascript game. I use images to display the player's units and buildings. I provide the image and then use a script to replace certain colors in the images with other color, to get different versions of an image with different colors (so the soldier of player 1 has a red sword and the soldier of player 2 has a blue sword and so on...). The problem is, for maybe ~20% of the users this replacing thing doesnt work and they see all units in the same (default) color. Im now wondering why this is. Heres the function i use to replayce the colors:

// returns a image with some colors replaced, specified by search and replace, which are arrays of color arrays ([[255, 255, 255], [...], ...], )
ImageTransformer.replaceColors = function(img, search, replace)
{
    var canv = document.createElement('canvas');
    canv.height = img.height;
    canv.width = img.width
    var ctx = canv.getContext('2d');

    ctx.drawImage(img, 0, 0);

    var imgData = ctx.getImageData(0, 0, canv.width, canv.height);

    for(var i = 0; i < imgData.data.length; i += 4)
        for(var k = 0; k < search.length; k++)
            if(imgData.data[i] == search[k][0] && imgData.data[i + 1] == search[k][1] && imgData.data[i + 2] == search[k][2])
            {
                imgData.data[i] = replace[k][0];
                imgData.data[i + 1] = replace[k][1];
                imgData.data[i + 2] = replace[k][2];
            }

    ctx.putImageData(imgData, 0, 0);

    return canv;
}

Upvotes: 0

Views: 98

Answers (1)

GameAlchemist
GameAlchemist

Reputation: 19294

Browsers may or may not apply a gamma to the image prior to drawing them, the intent is to have more natural colors (...).
I bet this is the Browsers which apply a gama that fool your algorithm.

Rather than test for strict equality, you might use a color distance, and decide of a threshold to decide wether to switch or not :

var imgData = ctx.getImageData(0, 0, canv.width, canv.height);
var data = imgData.data, length = imgData.data.length ;

    for(var k = 0; k < search.length; k++) {
       var thisCol = search[k];
       for(var i = 0; i < length; i += 4) {
            var colDist = Math.abs(data[i] - thisCol[0] ) 
                          + Math.abs(data[i+1] - thisCol[1] ) 
                            + Math.abs(data[i+2] - thisCol[2] );
            if( colDist < 5 )
           {
              data[i] = thisCol[0];
              data[i + 1] = thisCol[1];
              data[i + 2] = thisCol[2];
           }
     }
}

ctx.putImageData(imgData, 0, 0);

return canv; 

(here i used as distance the sum of absolute differences in between r,g,b ; as @MarkE suggest, you can choose others, euclidian being this:

        var colDist = sq(data[i] - thisCol[0] ) 
                      + sq(data[i+1] - thisCol[1] ) 
                        + sq(data[i+2] - thisCol[2] );
       // notice this is the squared euclidian distance.
       // whith function sq(x) { return x*x }

test several pictures / distances, and see what fits.

test several threshold also.
).

Upvotes: 1

Related Questions