Dynamic alpha

July 24th, 2011

The original article has been retrieved from the internet archive machine and edited for fluidity.

Since early 2010, coverflows have appeared everywhere on the internet. They're considered one of the best way to show lots of things in a small space, thanks to their hidden "slides".

Most of the time, you can expect a simple box with an image, and a overlay with a semi-transparent black background and white font color. Just by saying it, you can smell the shit happening and despite your tries to educate your client about it, most of the time what you predicted becomes reality.

With a bright image, if the black background is too opaque, you hiding some parts of the image but you also change the perception you had about shape of the image. If the black background is too transparent, you may find the white font unreadable very quickly.

So I went up with this stupid and almost useless idea to adapt the alpha of the background depending on what’s behind. That way, wherever the overlay could be, the alpha would always be the way it should to provide a good compromize between the visibility of the image and the readability of the font.

For a small proof of concept, i just put 4 greys and tried differents overlay. To see what i meant with visibility and readability, i made those overlays draggable:

Non Adaptive (.2)
Non Adaptive (.8)
Adaptive

I turned to be usefull with a real image.

Here, i put the .8 alpha overlay on dark side but onto bright details, which are lost behind the overly black background ; the .2 alpha overlay on a bright area to show that a too transparent background could affect the readability of a text. The perfect case would be to switch them but… we can't always predict which image will be loaded in a carousel. Hopefully, the dynamic overlay fits everywhere correctly !

Non Adaptive (.2)
Non Adaptive (.8)
Adaptive
var rect:Rectangle = new Rectangle(overlay.x, overlay.y, overlay.width, overlay.height);
var ba:ByteArray = image.bitmapData.getPixels(rect);

ba.position = 0;
var total:uint, color:Array;
while (ba.position < ba.length)
{
    color = int2rgb(ba.readUnsignedInt() - 0xff000000);
    total += color[0] < color[1] ? color[1] < color[2] ? color[2] : color[1] : color[0];
}

var average:Number = (total / luminosity.position) / 100;
var ratio:Number = total / (0xff * luminosity.position);

overlay.refresh({ text:'dynamic alpha (' + average.toFixed(2) + ')', alpha:average })

The original code was for AS3, so here's a HTML5 canvas version

const getOpacityForRegion = (context, x, y, w, h, min = 0, max = 1) => {
    const { data } = context.getImageData(x, y, w, h)

    let average = 0
    for (let i = 0; i < data.length; i += 4)
        average += data[i]

    average /= (data.length / 4)
    return min + (average / 255) * (max - min)
}