Tim S.
Tim S.

Reputation: 13843

Weird results combining colours

I'm currently working on a horizontal blur algorithm in javascript, though I doubt the language matters.

I get the data from a canvas which is basically a huge array where every four (RGBA) values stand for one pixel. A value can contain an int ranging from 0 to 255.

When I blur the image, the area's between two different colours turn into strange colours! I've drawn a red rectangle on a black background. Using the algorithm below, I get the following result (4px size):

4px undesired result

Though when a use a 1 or 2 pixel size, everything seems to work normally.

2px desired result

Please note this is somewhat messy build up. I'm planning to make this all OOP!

// s: size
// w: width
// h: height
function blur( s, w, h ) {
    var src = ctx.getImageData( 0, 0, w, h ); // get imagedata from source
    var dst = ctx.createImageData( w, h );    // create imagedata for dest
    var x, y, xo, index, rgb; // predefine vars

    // loop through y axis
    for( y = 0; y < h; y++ ) {
        // loop through x axis
        for( x = 0; x < w; x++ ) {
            rgb = 0; // set total to 0

            // loop through area around current pixel
            for( xo = 0 - s; xo <= s; xo++ ) {
                // get specific index
                index = getIndex( x + xo, y, w );

                // add nothing if the value doesn't exist (borders)
                // if( isNaN( src.data[index] ) ) continue;
                if( typeof src.data[index] === 'undefined' ) continue;

                // add the values to total
                rgb += ( src.data[index] << 16 ) + ( src.data[index + 1] << 8 ) + src.data[index + 2];
            }

            // get the average of all pixels in that area
            rgb = rgb / ( s * 2 + 1);

            // get index of current pixel
            index = getIndex( x, y, w );

            // set pixel in dest
            dst.data[index] = ( rgb & 0xff0000 ) >> 16;    // red
            dst.data[index + 1] = ( rgb & 0x00ff00 ) >> 8; // green
            dst.data[index + 2] = ( rgb & 0x0000ff );      // blue
            dst.data[index + 3] = 255; // alpha
        }
    }

    // add the new image data
    ctx.putImageData( dst, 0, 0 );
}

function getIndex( x, y, w ) {
    // calculate the appropriate index, since every pixel has 4 array values
    return ( y * ( w * 4 ) + ( x * 4 ) );
}

So what is wrong with my algorithm? I'm a bit lost. Please note that I'm not looking for existing objects/libraries/files for canvas blurring. I like to reinvent everything to educate myself.


Edit: I also like to add that the values I get back are truly the values that represent the colours shown on the canvas. That means that's definitely a miscalculation in my algorithm.

Upvotes: 1

Views: 88

Answers (2)

unkulunkulu
unkulunkulu

Reputation: 11922

The average between 0x030000 (dark red) and 0x000000 (black) becomes 0x018000, which gets a lot of green (0x80) You should average the channels separately.

Upvotes: 2

lanzz
lanzz

Reputation: 43178

You should average your channels separately. Dividing a packed three-channel value is unlikely to keep each channel within its byte.

Upvotes: 2

Related Questions