0
votes

Given an RGB pixel map of a picture, what would be good and meaningful values to make the picture more red, green, blue, cyan, magenta, or yellow?

Currently in my JavaScript code, I have the following RGB change values, but I wonder if there are more optimized ratios based on color theory — for instance, for more red I use rgb(+45,-27,-27), but I am not so sure about e.g. the -27.

var strength = 45;
var strengthLess = strength - 18;
this.changeColorModes = {
        'moreRed'   : {r: strength, g: -strengthLess, b: -strengthLess},
        'moreGreen' : {r: -strengthLess, g: strength, b: -strengthLess},
        'moreBlue'  : {r: -strengthLess, g: -strengthLess, b: strength},
        'moreCyan'  : {r: -strengthLess, g: strengthLess, b: strengthLess},
        'moreMagenta'  : {r: strengthLess, g: -strengthLess, b: strengthLess},
        'moreYellow'  : {r: strengthLess, g: strengthLess, b: -strengthLess}
        };
2

2 Answers

0
votes

RGB values cap at 255. So what would happen with the RGB value (240, 10, 10) when you do this operation? It would become (285, -35, -35) which would then overflow to (29, 221, 221) which is certainly not what you want.

A correct solution is hard to give in this case, because it depends on aesthetics and what effect exactly you want to achieve. When graphic artists color-correct images in an image manipulation program like Photoshop, they usually edit the curves of each channel and make them more or less steep, which is a multiplication operation on the values of that color channel. So when you want to make an image "20% more red", I would multiply all red values with 1.2 and set everything over 255 to 255 to avoid overflow. To make an image "20% less cyan" (opposite of red), I would multiply all green and blue values with 0.8.

0
votes
var pad = function(num, totalChars) {
    var pad = '0';
    num = num + '';
    while (num.length < totalChars) {
        num = pad + num;
    }
    return num;
};

// Ratio is between 0 and 1
var changeColor = function(color, ratio, darker) {
    // Trim trailing/leading whitespace
    color = color.replace(/^\s*|\s*$/, '');

    // Expand three-digit hex
    color = color.replace(
        /^#?([a-f0-9])([a-f0-9])([a-f0-9])$/i,
        '#$1$1$2$2$3$3'
    );

    // Calculate ratio
    var difference = Math.round(ratio * 256) * (darker ? -1 : 1),
        // Determine if input is RGB(A)
        rgb = color.match(new RegExp('^rgba?\\(\\s*' +
            '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
            '\\s*,\\s*' +
            '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
            '\\s*,\\s*' +
            '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
            '(?:\\s*,\\s*' +
            '(0|1|0?\\.\\d+))?' +
            '\\s*\\)$'
        , 'i')),
        alpha = !!rgb && rgb[4] != null ? rgb[4] : null,

        // Convert hex to decimal
        decimal = !!rgb? [rgb[1], rgb[2], rgb[3]] : color.replace(
            /^#?([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])/i,
            function() {
                return parseInt(arguments[1], 16) + ',' +
                    parseInt(arguments[2], 16) + ',' +
                    parseInt(arguments[3], 16);
            }
        ).split(/,/),
        returnValue;

    // Return RGB(A)
    return !!rgb ?
        'rgb' + (alpha !== null ? 'a' : '') + '(' +
            Math[darker ? 'max' : 'min'](
                parseInt(decimal[0], 10) + difference, darker ? 0 : 255
            ) + ', ' +
            Math[darker ? 'max' : 'min'](
                parseInt(decimal[1], 10) + difference, darker ? 0 : 255
            ) + ', ' +
            Math[darker ? 'max' : 'min'](
                parseInt(decimal[2], 10) + difference, darker ? 0 : 255
            ) +
            (alpha !== null ? ', ' + alpha : '') +
            ')' :
        // Return hex
        [
            '#',
            pad(Math[darker ? 'max' : 'min'](
                parseInt(decimal[0], 10) + difference, darker ? 0 : 255
            ).toString(16), 2),
            pad(Math[darker ? 'max' : 'min'](
                parseInt(decimal[1], 10) + difference, darker ? 0 : 255
            ).toString(16), 2),
            pad(Math[darker ? 'max' : 'min'](
                parseInt(decimal[2], 10) + difference, darker ? 0 : 255
            ).toString(16), 2)
        ].join('');
};
var lighterColor = function(color, ratio) {
    return changeColor(color, ratio, false);
};
var darkerColor = function(color, ratio) {
    return changeColor(color, ratio, true);
};

// Use
var darker = darkerColor('rgba(80, 75, 52, .5)', .2);
var lighter = lighterColor('rgba(80, 75, 52, .5)', .2);

Now handles RGB(A) input, as well as hex (3 digit or 6).