DEV Community

Cover image for Fun with Flags, Enums and Bit Shifting
James Turner
James Turner

Posted on • Updated on • Originally published at turnerj.com

Fun with Flags, Enums and Bit Shifting

While thinking of posts to write about, the title "Fun with Flags" came to mind from a certain TV show and I wondered how I might connect that to programming. There are enum flags in C# via the Flags attribute so maybe that was something I could write about. That said, I wanted to do something more creative than some humdrum post about using enums even if the end result isn't really practical.

Instead I decided to make real flags in C# with enums - turning a number like 52357729848 into a flag:

The German Flag

A gave myself certain requirements:

  • I needed to be able to generate more than one flag
  • I wanted to encode everything about the flag in a single value via enums

There are technical limitations too as I can't store a lot of data in an enum and will need to make compromises. An enum can be backed by a few different types but I chose long so I could get a full 64-bits of data to play with.

Encoding a Value

My initial thought with this was to pick the easiest form of flag - simple flags with stripes. If I split a typical flag into 9 segments, maybe I can store 9 colours and that would allow drawing of horizontal and vertical stripes. It seemed like the most straightforward approach at the time (I realised later it might have been better if I stored "shape" data instead for more flag variety but oh well).

The problem is, storing 9 segments in 64 bits is pretty hard and would leave me with about 7.11-bits per segment making colour data very limited. I wanted 3 colour channels so that only gives me really 2-bits per colour which is not a lot of variety. Having then 6-bits per segment, it left me with 10-bits that I don't really have much use for. Initially I tried using those bits to help extend the range of colours, acting as a multiplier for a specific channel. In the end though, it wasn't overly useful for this so I cut it.

This is the data structure I ended with:

[PPPPPPPPPP]
[RRGGBB][RRGGBB][RRGGBB]
[RRGGBB][RRGGBB][RRGGBB]
[RRGGBB][RRGGBB][RRGGBB]

P = Padding
R = Red Intensity
G = Green Intensity
B = Blue Intensity
Enter fullscreen mode Exit fullscreen mode

My primary data only takes 54-bits so my data structure has 10-bits of padding at the front. Having the padding at the front allows the generated number to be smaller.

I will be using ImageSharp for converting this value into an actual image. Because I have 9 segments, it seemed like the best idea to treat the image as a 3x3 pixel square and get ImageSharp to resize it for me. The pixel format for the data though was RGB24 so I needed to work out how to scale up my colours from 2-bits to 8-bits per channel.

With 8-bits, the max value I can have is 255 for a single channel.
Full colour intensity for any channel is 3 so I decided to simply divide the max value by the full colour intensity leaving me the magic number of 85 to scale my values by.

That is the nuts-and-bolts of the format, now it was just to make that work in code.

A Bit Shifty

Knowing I can store the data is one thing, actually making it work was another. I don't often use bitwise and shift operators but for this, it was going to use them quite heavily.

Firstly, we need an enum of colour intensity values:

public enum Intensity : byte
{
    None = 0,
    OneThird = 1,
    TwoThirds = 2,
    Max = 3
}
Enter fullscreen mode Exit fullscreen mode

The specific values here are important because of what they represent in binary.

00000000 // Intensity.None
00000001 // Intensity.OneThird
00000010 // Intensity.TwoThirds
00000011 // Intensity.Max
Enter fullscreen mode Exit fullscreen mode

Because these values represent a single channel's intensity, we need to combine 3 of them together to form our full colour. We combine them by using bit shifting and bitwise OR operations to create our 6-bit colour value.

public enum Colour : long
{
    Black = 0,
    Red = Intensity.Max << 4,
    Green = Intensity.Max << 2,
    Blue = Intensity.Max,
    White = Red | Green | Blue
}
Enter fullscreen mode Exit fullscreen mode

While we are using a long here (helping us with our later bit shifting operations), the values we are setting fit within 6-bits. Viewing the colours as bytes in binary, the shifting and OR-ing of data would look a little like this:

00110000 // Red = Intensity.Max << 4
00001100 // Green = Intensity.Max << 2
00000011 // Blue = Intensity.Max
00111111 // White = Red | Green | Blue
Enter fullscreen mode Exit fullscreen mode

Using different intensity values for the different colour channels, we can create new colours too.

00110000 // Red = Intensity.Max << 4
00001000 // Green = Intensity.TwoThirds << 2
00000000 // Blue = Intensity.None
========
00111000 // Yellow = (Intensity.Max << 4) | (Intensity.TwoThirds << 2)
Enter fullscreen mode Exit fullscreen mode

To create a few different types of flags, we will need a few more colours...

public enum Colour : long
{
    Black = 0,
    Red = Intensity.Max << 4,
    Green = Intensity.Max << 2,
    Blue = Intensity.Max,
    White = Red | Green | Blue,
    Orange = (Intensity.Max << 4) |
        (Intensity.OneThird << 2),
    Yellow = (Intensity.Max << 4) |
        (Intensity.TwoThirds << 2),
    MediumGreen = Intensity.TwoThirds << 2,
    LightBlue = (Intensity.TwoThirds << 2) |
        Intensity.Max,
    DarkBlue = Intensity.OneThird
}
Enter fullscreen mode Exit fullscreen mode

Identifying the right combinations of values for colours was relatively straight forward - I used an RGB colour picker in Paint.NET and selected thirds of the different colour channels. Like if I had two thirds red and one third green, I'd approximately have orange.

Colour picker showing 2 thirds red and 1 third green

So now we've got our colours, we need to encode the final value of a flag. In a similar approach to combining the colour channels, we need to combine the colours of the 9 segments by shifting and OR-ing.

public enum CountryFlags : long
{
    Germany = Colour.Black << 48 | Colour.Black << 42 | Colour.Black << 36 |
        Colour.Red << 30 | Colour.Red << 24 | Colour.Red << 18 |
        Colour.Yellow << 12 | Colour.Yellow << 6 | Colour.Yellow
}
Enter fullscreen mode Exit fullscreen mode

While Colour.Black does encode as 0 so the first 3 values aren't actually needed, it made it easier to still think of it as 9 distinct segments that all needed colours set.

In binary, the operation to encode our German flag would look like:

        00000000 // Black, shifted by 48-bits
              00000000
                    00000000
                          00110000 // Red, shifted by 30-bits
                                00110000
                                      00110000
                                            00111000 // Yellow, shifted by 12-bits
                                                  00111000
                                                        00111000
================================================================
0000000000000000000000000000110000110000110000111000111000111000
Enter fullscreen mode Exit fullscreen mode

As a decimal, that would be 52357729848. This is only half the job though, we have our flag data as number but we also need to decode it to an image.

Generating an Image

So how do we take 52357729848 and turn it into an image? We use more bit shifting and now AND-ing of our data to get each individual colour. Also, we will be reading the data in reverse.

var blueComponent = (byte)(flagData & 3) * 85;
Enter fullscreen mode Exit fullscreen mode

The value flagData here is a long of our generated number.

To get the blue component, we don't need to shift but we do need to perform a logical AND of the data. We only want the last two bits of the number - if we just convert the number to a byte, we will get the last 8-bits.

0000000000000000000000000000110000110000110000111000111000111000
                                    // We only want this part ^^
Enter fullscreen mode Exit fullscreen mode

By doing flagData & 3, we get just the last 2-bits from the full value. To get the next components, we do the same but now on a bit shifted value so the last 2-bits are of the colour we want.

var greenComponent = (byte)((flagData >> 2) & 3) * 85;
var redComponent = (byte)((flagData >> 4) & 3) * 85;
Enter fullscreen mode Exit fullscreen mode

Now we have the 3 colour channels of the bottom right segment of our flag. As a reminder, the 85x multiplier is to adjust the colour to fit within a full 8-bits for the RGB24 pixel format. Really now, it is just a matter of wrapping the code within some loops to set it to an image.

using var image = new Image<Rgb24>(3, 3);
for (var y = 2; y >= 0; --y)
{
    for (var x = 2; x >= 0; --x)
    {
        var pixel = image[x, y];
        var blueComponent = (byte)((flagData >> 0) & 3) * 85;
        pixel.B = (byte)blueComponent;
        var greenComponent = (byte)((flagData >> 2) & 3) * 85;
        pixel.G = (byte)greenComponent;
        var redComponent = (byte)((flagData >> 4) & 3) * 85;
        pixel.R = (byte)redComponent;
        flagData >>= 6;
        image[x, y] = pixel;
    }
}
Enter fullscreen mode Exit fullscreen mode

In our inner-most loop, we also shift our bits in flagData over 6-bits so we are in the next segment for the next iteration. This code though would only leave us with a 3x3 flag which doesn't look right so with a little more code, we can make it be bigger and more flag-like.

image.Mutate(x => x.Resize(400, 240, new NearestNeighborResampler()));
Enter fullscreen mode Exit fullscreen mode

The NearestNeighborResampler here is important - it allows us to scale up our specific "blocky" image here without distorting or blurring it.

And that's basically it - we can take a bunch of enums and encode a value then take the value and decode it to an image. I've set up an Azure Function running this code to show it working and few flags I've generated:

https://funwithflags.turnerj.com/api/flag/generate.png?v=ENCODED_VALUE

Germany

Value: 52357729848

Germany = Colour.Black << 48 | Colour.Black << 42 | Colour.Black << 36 |
    Colour.Red << 30 | Colour.Red << 24 | Colour.Red << 18 |
    Colour.Yellow << 12 | Colour.Yellow << 6 | Colour.Yellow,
Enter fullscreen mode Exit fullscreen mode

Italy

Value: 2532184938287088

Italy = Colour.MediumGreen << 48 | Colour.White << 42 | Colour.Red << 36 |
    Colour.MediumGreen << 30 | Colour.White << 24 | Colour.Red << 18 |
    Colour.MediumGreen << 12 | Colour.White << 6 | Colour.Red,
Enter fullscreen mode Exit fullscreen mode

France

Value: 561852585091056

France = Colour.DarkBlue << 48 | Colour.White << 42 | Colour.Red << 36 |
    Colour.DarkBlue << 30 | Colour.White << 24 | Colour.Red << 18 |
    Colour.DarkBlue << 12 | Colour.White << 6 | Colour.Red,
Enter fullscreen mode Exit fullscreen mode

Ireland

Value: 2532459817242612

Ireland = Colour.MediumGreen << 48 | Colour.White << 42 | Colour.Orange << 36 |
    Colour.MediumGreen << 30 | Colour.White << 24 | Colour.Orange << 18 |
    Colour.MediumGreen << 12 | Colour.White << 6 | Colour.Orange,
Enter fullscreen mode Exit fullscreen mode

Luxembourg

Value: 13725272368788171

Luxembourg = Colour.Red << 48 | Colour.Red << 42 | Colour.Red << 36 |
    Colour.White << 30 | Colour.White << 24 | Colour.White << 18 |
    Colour.LightBlue << 12 | Colour.LightBlue << 6 | Colour.LightBlue,
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

What started out as a bit of a weird challenge I set myself turned into a fun and interesting learning experience. I rarely mess around with bit shifting and bitwise operations. While I still probably won't need to very often, I like that I know a lot more about them now.

If I were to approach this again, I'd probably look at encoding shapes instead of pixels of colours. That way I could encode a wider variety of flags like the flags of Sweden, Japan and perhaps even South Korea.

If you liked this...

If you liked the kinda strange and interesting nature of this, you might like my deep dive into Levenshtein Distance and the various optimizations.

Latest comments (0)