4

I have a Visualforce Colorpicker that allows users to set a background-color.

I have access to this in Apex as a string, e.g. '#FF0000'. I want to know if this String is a dark color or a light color, to use either black or white font color.

I have been searching for a way to do this, and found threads such as this one. However, I have been unable to find a Salesforce specific way to do this, as Salesforce does not for example use java.awt.Color nor bytes as SF does not support it.

The convertToHex(); function in the EncodingUtil class looks like it might be useful, but I don't know what to do with the hexadecimal string. Can anyone lend a hand?

4

Update: ported the code to apex below.

Per the thread which you linked to, it appears pretty straightforward to adapt the code from the java.awt.color class, specifically the RGBtoHSB method and build it yourself in Apex. (Using Decimal instead of Float where appropriate)

You would then need to write some test code to cover this new code of yours, but it looks like a pretty straight forward porting of the Java implementation into Apex.

Source Code: java.awt Color.java

You would then just grab the 2nd element from the returned array and compare it above or below 0.5 for light and dark (Saturation).


HexUtils class Used to convert the hex values of Red, Green, Blue into the integer representations

public class HexUtils {

    public class HexUtilsException extends Exception {}

    public static List<Integer> hexPairsToIntegers(String s) {
        // make sure we have an even number
        if (Math.mod(s.length(), 2) != 0) {
            throw new HexUtilsException('An even number of characters is required.');
        }
        // list that we'll return
        List<Integer> returnInts = new List<Integer>();
        // base 16 values
        String digits = '0123456789ABCDEF';
        // normalize the string
        s = s.toUpperCase();
        // matches an empty string that has the last match (\G) followed by two characters (..) before it ((?<= ))
        List<String> characterPairs = s.split('(?<=\\G..)');
        // iterate the pairs
        for (String characterPair : characterPairs) {
            // determine the integer value of the pair
            Integer val = 0;
            // split on every character in the pair
            List<String> characters = characterPair.split('|');
            // calculate the value of the new character
            for (String c: characters) { 
                Integer d = digits.indexOf(c);
                if (d != -1) {
                    val = 16 * val + d;
                } else {
                    throw new HexUtilsException('Non hex character found. "' + c + '"');
                }
            }
            // add it to the list
            returnInts.add(val);
        }
        return returnInts;
    }
}

Color class Ported code from Java Color class to Apex

public class Color {

    public static Decimal[] RGBtoHSB(Integer r, Integer g, Integer b, Decimal[] hsbvals) {
        Decimal hue, saturation, brightness;
        if (hsbvals == null) {
            hsbvals = new Decimal[3];
        }
        Integer cmax = (r > g) ? r : g;
        if (b > cmax) { 
            cmax = b;
        }

        Integer cmin = (r < g) ? r : g;
        if (b < cmin) { 
            cmin = b; 
        }
        brightness = ((Decimal) cmax) / 255.0;
        if (cmax != 0) {
            saturation = ((Decimal) (cmax - cmin)) / ((Decimal) cmax);
        } else {
            saturation = 0;
        }
        if (saturation == 0) { 
            hue = 0;
        } else {
            Decimal redc    = ((Decimal) (cmax - r)) / ((Decimal) (cmax - cmin));
            Decimal greenc  = ((Decimal) (cmax - g)) / ((Decimal) (cmax - cmin));
            Decimal bluec   = ((Decimal) (cmax - b)) / ((Decimal) (cmax - cmin));
            if (r == cmax) {
                hue = bluec - greenc;
            } else if (g == cmax) {
                hue = 2.0 + redc - bluec;
            } else {
                hue = 4.0 + greenc - redc;
            }
            hue = hue / 6.0;

            if (hue < 0) {
                hue = hue + 1.0;
            }
        }

        hsbvals[0] = hue;
        hsbvals[1] = saturation;
        hsbvals[2] = brightness;

        return hsbvals;
    }
}

Example Usage:

String fontColor = '#0cf356';
List<Integer> fontIntegers = HexUtils.hexPairsToIntegers((fontColor.substring(1)));
system.debug(fontIntegers); // (12, 245, 86)

List<Decimal> hsbValues = Color.RGBtoHSB(fontIntegers[0], fontIntegers[1], fontIntegers[2], null);
system.debug(hsbValues); // (0.386266..., 0.951020..., 0.960784...)

Decimal brightness = hsbValues[2];
// 0 is dark, 1 is light
if (brightness < 0.5) {
   // use a bright background
    system.debug('I am a dark color.');
} else {
   // use a dark background
    system.debug('I am light color.'); // 0.95 = 'I am light color.'
}

This code hasn't been regression tested and you'll need to write your own test coverage for it but it appears to work correctly based on my limited testing of it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.