javascriptcolorsopenlayersalphablendingcolor-blending

OpenLayers: can I get a color which gets by overlaying layers with some opacity?


I have 2 layers with some opacity:

var vectorLayer = new ol.layer.Vector({
    source: vectorSource,
    style: function (feature) {
        return new ol.style.Style({
            fill: new ol.style.Fill({
                color: *some function to get color from data*
            }),
            stroke: new ol.style.Stroke({
                color: 'rgb(23, 23, 23)',
                width: 0.5
            }),
        });
    }
});

vectorLayer.setOpacity(0.7)

and 1 "final" layer without opacity:

var finalLayer = new ol.layer.Vector({
source: vectorSource,
    style: function (feature) {
        return new ol.style.Style({
            fill: new ol.style.Fill({
                color: 'rbg(255, 255, 255)'
            }),
            stroke: new ol.style.Stroke({
                color: 'rgb(23, 23, 23)',
                width: 0.5
            }),
        });
    }
});

So the color I get as a result is the result of overlaying 3 layers

I tried to get the color using

R = R1 + (R2 - R1) * Alfa / 255; 
G = G1 + (G2 - G1) * Alfa / 255; 
B = B1 + (B2 - B1) * Alfa / 255;

but the color I get was not the same I see on the map

The colors of two layers with opacity I store as a feature property:

var color1 = feature.get('color1').match(/\d+(\.\d+)?/g).map(Number)

var color2 = feature.get('color2').match(/\d+(\.\d+)?/g).map(Number)

const selectStyle = new ol.style.Style({
    fill: new ol.style.Fill({
        color: function (feature) {
            var color1 = feature.get('color1').match(/\d+(\.\d+)?/g).map(Number)
            var color2 = feature.get('color2').match(/\d+(\.\d+)?/g).map(Number)
            return rbg(???);
        },
    }),
    stroke: new ol.style.Stroke({
        color: 'rgba(30, 210, 255)',
        width: 1,
    }),
});

Solution

  • Any blending with canvas 2d contexts is slightly non-standard as it applies alpha premultiplication.

    If you draw rgba(r1, g1, b1, a1) to a blank canvas

    then with source-over draw rgba(r2, g2, b2, a2)

    so the result is rgba(r3, g3, b3, a3)

    the final alpha a3 will be

    a3 = a1 * (1 - a2) + a2
    

    while the r3, g3 and b3 values can each be predicted using

    c3 = (c1 * a1 * (1 - a2) + c2 * a2) / a3
    

    Working backwards from that if you already have R1, G1 and B1 for your color1 and wish to produce R2, G2 and B2 for your color2 the R, G and B values you would need to use with an opacity of alpha would be

    R = R1 + (R2 - R1) / alpha
    G = G1 + (G2 - G1) / alpha
    B = B1 + (B2 - B1) / alpha
    

    Depending on the values used that could calculate required values outside the 0 to 255 range which would need to be clamp to the value range. For example you can never make a white background fully black by overlaying it with semi-transparent black or vice versa.

    In OpenLayers it might be better to use a Select interaction which replaced the color1 fill with a color2 fill when hovering instead of overlaying and attempting to blend another layer.