Increase or decrease color saturation

前端 未结 3 829
忘掉有多难
忘掉有多难 2020-12-16 16:10

I would like to know the algorithm to increase or decrease one RGB color saturation

for example if I have the color rgb(200, 30, 40) (red) the function

相关标签:
3条回答
  • 2020-12-16 16:55

    // My solution is for colors in HEX format. You can request the saturation by percent.

    function applySaturationToHexColor(hex, saturationPercent) {
        if (!/^#([0-9a-f]{6})$/i.test(hex)) {
            throw('Unexpected color format');
        }
    
        if (saturationPercent < 0 || saturationPercent > 100) {
            throw('Unexpected color format');
        }
    
        var saturationFloat   = saturationPercent / 100,
            rgbIntensityFloat = [
                parseInt(hex.substr(1,2), 16) / 255,
                parseInt(hex.substr(3,2), 16) / 255,
                parseInt(hex.substr(5,2), 16) / 255
            ];
    
        var rgbIntensityFloatSorted = rgbIntensityFloat.slice(0).sort(function(a, b){ return a - b; }),
            maxIntensityFloat       = rgbIntensityFloatSorted[2],
            mediumIntensityFloat    = rgbIntensityFloatSorted[1],
            minIntensityFloat       = rgbIntensityFloatSorted[0];
    
        if (maxIntensityFloat == minIntensityFloat) {
            // All colors have same intensity, which means 
            // the original color is gray, so we can't change saturation.
            return hex;
        }
    
        // New color max intensity wont change. Lets find medium and weak intensities.
        var newMediumIntensityFloat,
            newMinIntensityFloat = maxIntensityFloat * (1 - saturationFloat);
    
        if (mediumIntensityFloat == minIntensityFloat) {
            // Weak colors have equal intensity.
            newMediumIntensityFloat = newMinIntensityFloat;
        }
        else {
            // Calculate medium intensity with respect to original intensity proportion.
            var intensityProportion = (maxIntensityFloat - mediumIntensityFloat) / (mediumIntensityFloat - minIntensityFloat);
            newMediumIntensityFloat = (intensityProportion * newMinIntensityFloat + maxIntensityFloat) / (intensityProportion + 1);
        }
    
        var newRgbIntensityFloat       = [],
            newRgbIntensityFloatSorted = [newMinIntensityFloat, newMediumIntensityFloat, maxIntensityFloat];
    
        // We've found new intensities, but we have then sorted from min to max.
        // Now we have to restore original order.
        rgbIntensityFloat.forEach(function(originalRgb) {
            var rgbSortedIndex = rgbIntensityFloatSorted.indexOf(originalRgb);
            newRgbIntensityFloat.push(newRgbIntensityFloatSorted[rgbSortedIndex]);
        });
    
        var floatToHex = function(val) { return ('0' + Math.round(val * 255).toString(16)).substr(-2); },
            rgb2hex    = function(rgb) { return '#' + floatToHex(rgb[0]) + floatToHex(rgb[1]) + floatToHex(rgb[2]); };
    
        var newHex = rgb2hex(newRgbIntensityFloat);
    
        return newHex;
    }
    
    0 讨论(0)
  • 2020-12-16 17:07

    Following Bali Balo suggestion I came up with:

    RGBtoHSV= function(color) {
            var r,g,b,h,s,v;
            r= color[0];
            g= color[1];
            b= color[2];
            min = Math.min( r, g, b );
            max = Math.max( r, g, b );
    
    
            v = max;
            delta = max - min;
            if( max != 0 )
                s = delta / max;        // s
            else {
                // r = g = b = 0        // s = 0, v is undefined
                s = 0;
                h = -1;
                return [h, s, undefined];
            }
            if( r === max )
                h = ( g - b ) / delta;      // between yellow & magenta
            else if( g === max )
                h = 2 + ( b - r ) / delta;  // between cyan & yellow
            else
                h = 4 + ( r - g ) / delta;  // between magenta & cyan
            h *= 60;                // degrees
            if( h < 0 )
                h += 360;
            if ( isNaN(h) )
                h = 0;
            return [h,s,v];
        };
    
    HSVtoRGB= function(color) {
            var i;
            var h,s,v,r,g,b;
            h= color[0];
            s= color[1];
            v= color[2];
            if(s === 0 ) {
                // achromatic (grey)
                r = g = b = v;
                return [r,g,b];
            }
            h /= 60;            // sector 0 to 5
            i = Math.floor( h );
            f = h - i;          // factorial part of h
            p = v * ( 1 - s );
            q = v * ( 1 - s * f );
            t = v * ( 1 - s * ( 1 - f ) );
            switch( i ) {
                case 0:
                    r = v;
                    g = t;
                    b = p;
                    break;
                case 1:
                    r = q;
                    g = v;
                    b = p;
                    break;
                case 2:
                    r = p;
                    g = v;
                    b = t;
                    break;
                case 3:
                    r = p;
                    g = q;
                    b = v;
                    break;
                case 4:
                    r = t;
                    g = p;
                    b = v;
                    break;
                default:        // case 5:
                    r = v;
                    g = p;
                    b = q;
                    break;
            }
            return [r,g,b];
        }
    

    by converting to the HSV (hue, saturation and value) format you can manually change the S component in this manner:

    var hsv= RGBtoHSV ([200,100,100]);
    alert(hsv)
    hsv[1] *= 1.5;
    alert(hsv)
    var rgb= HSVtoRGB(hsv);
    alert(rgb); //new color
    
    0 讨论(0)
  • 2020-12-16 17:07

    Here's a quick and dirty way that probably isn't correct in any technical way, but involves less computation than converting to HSV and back (so renders quicker if that matters):

    Grayscale is the equivalent of calculating luminosity averaged over the RGB parts of the pixel. We can mix grayness by applying a value weighting to the gray part versus the colored part:

    var pixels = context.getImageData(0, 0, canvas.width, canvas.height);
    grayscale = function (pixels, value) {
        var d = pixels.data;
        for (var i = 0; i < d.length; i += 4) {
            var r = d[i];
            var g = d[i + 1];
            var b = d[i + 2];
            var gray = 0.2989*r + 0.5870*g + 0.1140*b; //weights from CCIR 601 spec
            d[i] = gray * value + d[i] * (1-value);
            d[i+1] = gray * value + d[i+1] * (1-value);
            d[i+2] = gray * value + d[i+2] * (1-value);
        }
        return pixels;
    };
    

    So instead of adding "grayness", we can take it away and add the relevant color back in to "saturate":

    saturate = function (pixels, value) {
        var d = pixels.data;
        for (var i = 0; i < d.length; i += 4) {
            var r = d[i]; 
            var g = d[i + 1];
            var b = d[i + 2];
            var gray = 0.2989*r + 0.5870*g + 0.1140*b; //weights from CCIR 601 spec
            d[i] = -gray * value + d[i] * (1+value);
            d[i+1] = -gray * value + d[i+1] * (1+value);
            d[i+2] = -gray * value + d[i+2] * (1+value);
            //normalize over- and under-saturated values
            if(d[i] > 255) d[i] = 255;
            if(d[i+1] > 255) d[i] = 255;
            if(d[i+2] > 255) d[i] = 255;
            if(d[i] < 0) d[i] = 0;
            if(d[i+1] < 0) d[i] = 0;
            if(d[i+2] < 0) d[i] = 0;
        }
        return pixels;
    };
    

    Again, disclaimer that this "looks" saturated but probably in no way adheres to the technical definition of "saturation" (whatever that is); I've simply posted this in the hope that it's helpful to passers-by.

    0 讨论(0)
提交回复
热议问题