paint-brush
Fun with Flags: How to Make Flags in C# with Enums and Bit Shifting by@turnerj
170 reads

Fun with Flags: How to Make Flags in C# with Enums and Bit Shifting

by TurnerjDecember 17th, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Using enums and bit shifting, you can encode complex values into a single number. This article walks through an example of how I encoded and decoded a number can be used to render different country flags.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Fun with Flags: How to Make Flags in C# with Enums and Bit Shifting
Turnerj HackerNoon profile picture

While thinking of posts to write about, the title "Fun with Flags" came to mind 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


I 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 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


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 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 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
}


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


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
}


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


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)


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
}


Identifying the right combinations of values for colours was relatively straightforward - I used an RGB colour picker in 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
}


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
================================================================
0000000000000000000000000000000


As a decimal, that would be 52357729848. This is only half the job though, we have our flag data as a 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;


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.


0000000000000000000000000000000
                                    // We only want this part ^^


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;


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;
    }
}


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()));


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 a few flags I've generated:


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

Germany

Value:
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,

Italy

Value:
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,

France

Value:
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,

Ireland

Value:
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,

Luxembourg

Value:
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,

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.



바카라사이트 바카라사이트 온라인바카라