Suggestion: Add more static methods to ColorRGBA

A frequent top referral topic to this forum is this:

And whilst it’s trivial once you stop to think about it - I actually frequent the internet a lot to get color palettes - and they are often in various formats.

I suggest adding the following static methods to the ColorRGBA class to make life easier.

  • Convert any valid hex code to ColorRGBA
  • Convert any RGBA-255 color to ColorRGBA
  • Provide an actual random color generator (the current one will return the same color in the same tick of time).
package com.jayfella.devkit.theme;

import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;

public class ColorUtils {

    /**
     * Provides a ColorRGBA color from hexadecimal values.
     * Input may be in 3,4,6 or 8 length format, with or without a hashtag.
     * Examples are:
     * "f2d"        - single-digit rgb hex code (alpha will be 1.0)
     * "f2df"       - single-digit rgba hex code
     * "ff22dd"     - two-digit rgb hex code (alpha will be 1.0)
     * "ff22ddff"   - two-digit rgba hex code
     *
     * @param hexString the hex string to convert.
     * @return the ColorRGBA equivalent of the hexadecimal color.
     */
    public static ColorRGBA fromHex(String hexString) {

        int startIndex = hexString.startsWith("#") ? 1 : 0;
        boolean noAlpha = hexString.substring(startIndex).length() == 3 || hexString.substring(startIndex).length() == 6;
        boolean singular = hexString.substring(startIndex).length() == 3 || hexString.substring(startIndex).length() == 4;

        String rString = singular
                ? hexString.substring(startIndex, startIndex + 1) + hexString.substring(startIndex, startIndex + 1)
                : hexString.substring(startIndex, startIndex + 2);

        String gString = singular
                ? hexString.substring(startIndex + 1, startIndex + 2) + hexString.substring(startIndex + 1, startIndex + 2)
                : hexString.substring(startIndex + 2, startIndex + 4);

        String bString = singular
                ? hexString.substring(startIndex + 2, startIndex + 3) + hexString.substring(startIndex + 2, startIndex + 3)
                : hexString.substring(startIndex + 4, startIndex + 6);

        String aString = noAlpha
                ? "FF"
                : singular
                    ? hexString.substring(startIndex + 3, startIndex + 4) + hexString.substring(startIndex + 3, startIndex + 4)
                    : hexString.substring(startIndex + 6, startIndex + 8);

        int r = Integer.parseInt(rString, 16);
        int g = Integer.parseInt(gString, 16);
        int b = Integer.parseInt(bString, 16);
        int a = Integer.parseInt(aString, 16);

        // System.out.println("INPUT: " + rString + ", " + gString + ", " + bString + ", " + aString);
        // System.out.println("RGBA: " + r + ", " + g + ", " + b + ", " + a);

        return fromRGBA255(r, g, b, a);
    }

    /**
     * Converts a color from RGBA 255 values.
     * @param r the red value in 0-255 range.
     * @param g the green value in 0-255 range.
     * @param b the blue value in 0-255 range.
     * @param a the alpha value in 0-255 range.
     * @return the ColorRGBA equivalent of the RGBA 255 color.
     */
    public static ColorRGBA fromRGBA255(int r, int g, int b, int a) {
        return new ColorRGBA(r / 255.0F, g / 255.0F, b / 255.0F, a / 255.0F);
    }

    /**
     * Provides a random color with an alpha value of 1.0
     * @return a random color.
     */
    public static ColorRGBA random() {
        return random(1.0f);
    }

    /**
     * Provides a random color with the given alpha value.
     * @param alpha the opacity value between 0.0 and 1.0
     * @return a random color with the given alpha value.
     */
    public static ColorRGBA random(float alpha) {
        return new ColorRGBA(FastMath.nextRandomFloat(), FastMath.nextRandomFloat(), FastMath.nextRandomFloat(), alpha);
    }

}

And a test case I wrote to make sure the hex converter works.

import com.jayfella.devkit.theme.ColorUtils;
import com.jme3.math.ColorRGBA;

public class TestColorConverter {

    public static void main(String... args) {
        // test colors

        ColorRGBA test1 = ColorUtils.fromHex("ff22ddff");
        ColorRGBA test3 = ColorUtils.fromHex("ff22dd");
        ColorRGBA test2 = ColorUtils.fromHex("f2df");
        ColorRGBA test4 = ColorUtils.fromHex("f2d");

        ColorRGBA test5 = ColorUtils.fromHex("#ff22ddff");
        ColorRGBA test6 = ColorUtils.fromHex("#ff22dd");
        ColorRGBA test7 = ColorUtils.fromHex("#f2df");
        ColorRGBA test8 = ColorUtils.fromHex("#f2d");

        System.out.println("Test 1: " + test1);
        System.out.println("Test 2: " + test2);
        System.out.println("Test 3: " + test3);
        System.out.println("Test 4: " + test4);

        System.out.println("Test 5: " + test5);
        System.out.println("Test 6: " + test6);
        System.out.println("Test 7: " + test7);
        System.out.println("Test 8: " + test8);

    }

}

> Task :devkit-theme:TestColorConverter.main()
Test 1: Color[1.0, 0.13333334, 0.8666667, 1.0]
Test 2: Color[1.0, 0.13333334, 0.8666667, 1.0]
Test 3: Color[1.0, 0.13333334, 0.8666667, 1.0]
Test 4: Color[1.0, 0.13333334, 0.8666667, 1.0]
Test 5: Color[1.0, 0.13333334, 0.8666667, 1.0]
Test 6: Color[1.0, 0.13333334, 0.8666667, 1.0]
Test 7: Color[1.0, 0.13333334, 0.8666667, 1.0]
Test 8: Color[1.0, 0.13333334, 0.8666667, 1.0]

Edit: Added a test case to make sure the converter works on all intended inputs.

4 Likes

It shouldn’t.

With the difference of not allowing to specify an alpha to be random.

Indeed. I wonder why I thought that. Must have been a mistake on my part in some project that I just continued to believe.

1 Like

good one :wink:

just a question, i were using(example pseudocode):

(well yes its actually AWT, so if someone dont want use AWT, then might be problematic, but why not)

String value = "#009933"; // example
Coplr color = Color.decode(value);
ColorRGBA colorRGBA = new ColorRGBA((float) color.getRed() / 255, (float) color.getGreen() / 255, (float) color.getBlue() / 255, 1);

thats very simple, just 3 lines of code. i understand there is no alpha, but adding alpha just by getting last part of hash would be anyway less code. (im not sure if this decode can take alpha in it)

Because Android.

3 Likes

so android dont have awt classes at all? i thought it just cant use something in it.

sorry, were not doing android games, good to know, thanks :slight_smile:

if i would remind now, i belive i were once seen some android app that were using AWT, or maybe it was just swing.

I’ve often wanted an int 0-255 version on ColorRGBA. The others I think are either not needed or belong somewhere else maybe. Parsing text in ColorRGBA feels like something that could eventually take over half the file if we aren’t careful. It’s also the thing where there will be endless arguments on the “best way”, “should special case x,y,z be handled”, etc.

As for something like:

public static ColorRGBA random(float alpha) {

Personally, I’d rather have a setAlpha() that returns ‘this’ (like the other setters) so we could easily set alpha inline on any call. ColorRGBA.Black.clone().setAlpha(0.5f) would be one I’d use particularly often.

2 Likes

I’ve added the suggested methods. I refrained from adding the hex color conversion as per your advice.

1 Like

just wondering, would it be useful to also add the same methods for non linear space colors? Similar to #setAsSrgb()

I use that method a lot when using colors from color pickers etc.

I like to use HSV to RGB conversion to generate colors that are sufficiently distinct. A static factory method ColorRGBA.fromHSV() A fromHSV() member method would be nice.
I can’t find how I did that in the past right now, but there’s a Color class in the terrain lib that also provides some functions:

This one’s HSB though, how is that different?
If you think that could be integrated into ColorRGBA, I could check it out and make a pull request later.


Wouldn’t it be better to make those conversion functions members instead of static, so an existing color could be changed and an object reused if desired? The fromInt…() methods are also members.
Although ugly, usage would be similar…

ColorRGBA color = new ColorRGBA().fromHex("#00000000");