Search code examples
c#winformscolorshexcolor-conversion

Converting BGRA 8-Bit Hexadecimal string into System.Drawing.Color


I have Colors in a BGRA-Format which I get from a Delphi Application. I read these as a 8-Bit string into my C# Application. Now I try to convert these into the C# Color Format. The string I get looks like this:

string color = "$00FF00FF";

I tried to reconvert the Color and then Converting it to Int to Convert it to the System.Drawing Color

private Color ConvertColorFromBgra(string color)
{
    string argb = "#";
    var bgra = FunctionLibrary.SplitByLength(color.Replace("$", ""), 2);
    foreach (var value in bgra.Revert())
    {
        argb += value;
    }
    return Color.FromArgb(Convert.ToInt32(argb, 16));
}

Where SplitByLenght is a function I found on SO (which I call from another Class):

public static IEnumerable<string> SplitByLength(this string str, int maxLength)
{
    for (int index = 0; index < str.Length; index += maxLength)
    {
        yield return str.Substring(index, Math.Min(maxLength, str.Length - index));
    }
}

But somehow it doesn't work and I get the error:

System.FormatException: 'Could not find any recognizable digits.

I feel like the string isn't Converted to Int as supposed, but I didn't find any fixes. Can you either help me with the problem or have an alternative better approach to solve the problem?


Solution

  • To avoid string manipulations, you can remove the first char with string.Substring(1), then use the Convert.ToInt32() overload which accepts a specific base (16) for the string conversion to a LE int.

    BitConverter.GetBytes() can then be used to extract the bytes from the converted int and reverse the order.
    ► You may want to add a check before you reverse the byte order: if BitConverter.IsLittleEndian returns false, we're already using a BigEndian processor, so we don't reverse the order in this case.

    Recompose the int value after and use either Color.FromArgb to return a Color that includes an Alpha channel or ColorTranslator.FromWin32 to ignore it (setting the Alpha channel to 255 no matter what: e.g., if you need to use this color in a Control that doesn't support Color transparency).

    I'm adding this method as a Sting extension (string.ToARGBColor(bool)). Name it as you prefer.

    public static class StringsExtensions
    {
        public static Color ToARGBColor(this string hexColor, bool includeAlpha)
        {
            int ci = BitConverter.ToInt32(BitConverter.GetBytes(
                Convert.ToInt32(hexColor.Substring(1), 16)).Reverse().ToArray(), 0);
            return includeAlpha ? Color.FromArgb(ci) : ColorTranslator.FromWin32(ci);
        }
    }
    

    So you can just call it as:

    string colorString = "$00FF00AA";
    Color colorWithAlpha = colorString.ToARGBColor(true);
    Color colorNoAlpha = colorString.ToARGBColor(false);
    

    Using another classic BigEndian to LittleEndian conversion method:
    (it should be slightly faster, maybe give it a test drive)

    byte[] b = BitConverter.GetBytes(Convert.ToInt32(colorString.Substring(1), 16));
    int ci = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
    Color c = Color.FromArgb(ci);