Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Image alpha on default Android browser

I'm trying to duotone an image and plaster it onto a canvas.

Works in desktop browsers:

  • Chrome
  • Firefox
  • Safari
  • Internet Explorer

Fails in mobile browsers:

  • Android

Workable demo on JSFiddle, this example works in Chrome but fails in Android's default browser.

The code is:

<style>
    body {
        background-color: gray;
    }
</style>

<canvas id="mycanvas" width="64" height="64"></canvas>

<script>
    var image = new Image();
    image.src = 'image.png';

    image.onload = function () { //once the image finishes loading
        var context = document.getElementById("mycanvas").getContext("2d");

        context.drawImage(image, 0, 0);

        var imageData = context.getImageData(0, 0, 64, 64);
        var pixels = imageData.data;
        var numPixels = pixels.length;

        for (var i = 0; i < numPixels; i++) { //for every pixel in the image
            var index = i * 4;
            var average = (pixels[index] + pixels[index + 1] + pixels[index + 2]) / 3;

            pixels[index] = average + 255; //red is increased
            pixels[index + 1] = average; //green
            pixels[index + 2] = average; //blue
            //pixels[index + 3] = pixels[index + 3]; //no changes to alpha
        }

        context.clearRect(0, 0, 64, 64); //clear the image
        context.putImageData(imageData, 0, 0); //places the modified image instead
    }
</script>

The summary is:

  • set the background color to gray so alpha can be observed easier
  • create a canvas 64 by 64
  • load a image of a smiley face on a transparent background
  • draw the image onto the canvas
  • get the image's data
  • for every pixel, make the red stronger
  • replace the altered image on the canvas

The smiley face looks like this (block-quoted so you can tell it's transparent):

However, in comparison with the chrome and android browser,

The background of the android drawing is reddish while the chrome drawing is completely transparent.

So...

  • What happened here?
  • How can I change the code so that the android drawing matches with the chrome drawing?

Note: I already tried if (pixels[index + 3] == 0) continue;, and I'm aware of this, but it won't work for images with varying opacity.

like image 296
Dave Chen Avatar asked Sep 30 '22 07:09

Dave Chen


1 Answers

Ok, I have a result.

First of all look at your loop:

for (var i = 0; i < numPixels; i++) { //for every pixel in the image
     var index = i * 4;

index will be more than 3 times bigger than numPixels. And will get undefined values. And average will be NaN. But, then I fixed this - result was the same.

So I tried to use fillStyle and fillRect for every pixel. All becomes fine. My code:

function putImageData(ctx, imageData, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
    var data = imageData.data;
    var height = imageData.height;
    var width = imageData.width;

    dirtyX = dirtyX || 0;
    dirtyY = dirtyY || 0;
    dirtyWidth = dirtyWidth !== undefined ? dirtyWidth : width;
    dirtyHeight = dirtyHeight !== undefined ? dirtyHeight : height;

    var limitBottom = dirtyY + dirtyHeight;
    var limitRight = dirtyX + dirtyWidth;

    for (var y = dirtyY; y < limitBottom; y++) {
        for (var x = dirtyX; x < limitRight; x++) {
            var pos = y * width + x;
            //adding red increased here
            ctx.fillStyle = 'rgba(' + data[pos * 4 + 0] + 255 + ',' + data[pos * 4 + 1] + ',' + data[pos * 4 + 2] + ',' + (data[pos * 4 + 3] / 255) + ')';
            ctx.fillRect(x + dx, y + dy, 1, 1);
        }
    }
};

var image = new Image();
image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gcVBAYmslkm5QAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABc0lEQVR42u2a3QrDMAiFZ9j7v7K7CpTRVtt4NCEnsJt1BP1y4l8nqvrZebXP5osACIAACIAACIAACIAACIAACIAAqpaIlPXkgpwHiIiqqtw5efa8f7csgIgTVVU5AlwGAELOSAjfLKndOWFBQyohVAHWPR/dCwEBCiDCYDSIViH5p/v87xUZZ8IUkJHGEGpoyLu/QjZomffeE+37xwthFH5aKdwNtQyOyhjT1QGzXosWaYBljMdY72laPUZZDLBq+6jTPO41xRXwGB/1Gw5ECOC6StxSASMQ2o5OQ9Jg5VxviYFI5KxgSQBXzmYNPlLaYctw72jr7ncd2DQx4KkCKud/cACeQNghvAmaCDjhCvBCOFODJX2Pgkp7gben+vR5pAqgL0aip8KI2ABNgyMRGzFiT1FARI+eWTDB3w7P6nhKHVBd5pYDGFVABsCU/wccHblLaxUNEwzACvKHB8EVFoeiBEAABEAABEAABEAABEAAe64fre4tZAeLAbcAAAAASUVORK5CYII=';

image.onload = function () {
    var context = document.getElementById("mycanvas").getContext("2d"),
        imageData;

    context.drawImage(image, 0, 0);
    imageData = context.getImageData(0, 0, 64, 64);

    putImageData(context, imageData, 0, 0);
}
like image 66
Pinal Avatar answered Oct 06 '22 19:10

Pinal