C# Software Bitmap Utility
- Posted on: 17/04/2015
Post Type: Blog Entry
So I've been working on some procedural content for Dodeka (more details on that in next dev update!) and I needed a way to visualise it as I worked on it so I could see exactly what I was doing. Originally I output ASCII to a text file but that really was not ideal.
The obvious choice is to create an image then. The problem is that I needed to quickly create a LARGE (giant even) image which is a problem when dealing with XNA Texture2D objects because they are all stored on the graphics card which severely limits the maximum size. I did not have access to System.Drawing in this project and nor did I wish to add it.
Googling around a bit I found I was not alone in this problem however there were little in satisfactory solutions. So what I've done is knock up a very quick (*see foot note) software bitmap handler for C#. It relies on very little and does NOT use unsafe to do it's stuff.
It's not fast and it's not full featured - but it does allow you to very easily create bitmap images, edit them programmatically and then save them to disk in a proper format for your OS to open and display. As I found I was not alone in needing this I've decided to share it, so here it is:
HS_Bitmap.cs
Feel free to use/modify/whatever. It does not really have a real-time application as it's not fast - but as a utility it may be very useful. I have thrown in some basic inline documentation for your intellisense etc - if you'd like more details on the bitmap format then check wikipedia, it's where I got the info from!
As a bonus, here's a quick example of how you would copy an XNA Texture2D into the bitmap object if you wanted to do tile map representation:
First load your image data and create your objects:
HS_Bitmap WorldBmp = new HS_Bitmap(WorldWidth * TileSize, WorldHeight * TileSize);
HS_Bitmap WaterBmp = new HS_Bitmap(TileSize, TileSize);
HS_Bitmap PlainsBmp = new HS_Bitmap(TileSize, TileSize);
HS_Bitmap MountainBmp = new HS_Bitmap(TileSize, TileSize);
Texture2D WaterImg = SystemVars.g_MainContentManager.Load<Texture2D>("DATA_2D\\Dodeka\\Water");
Texture2D PlainsImg = SystemVars.g_MainContentManager.Load<Texture2D>("DATA_2D\\Dodeka\\Plains");
Texture2D MountainImg = SystemVars.g_MainContentManager.Load<Texture2D>("DATA_2D\\Dodeka\\Mountain");
Create some arrays to hold colour data and copy the XNA Texture2D data into them:
Color[] WaterPixels = new Color[TileSize * TileSize];
Color[] PlainsPixels = new Color[TileSize * TileSize];
Color[] MountainPixels = new Color[TileSize * TileSize];
WaterImg.GetData<Color>(WaterPixels);
PlainsImg.GetData<Color>(PlainsPixels);
MountainImg.GetData<Color>(MountainPixels);
Set the HS_Bitmap pixel data to match the Texture2D Color data:
for (int x = 0; x < TileSize; x++ )
{
for (int y = 0; y < TileSize; y++)
{
WaterBmp.SetPixel(x, y, WaterPixels[y * TileSize + x].R, WaterPixels[y * TileSize + x].G, WaterPixels[y * TileSize + x].B);
PlainsBmp.SetPixel(x, y, PlainsPixels[y * TileSize + x].R, PlainsPixels[y * TileSize + x].G, PlainsPixels[y * TileSize + x].B);
MountainBmp.SetPixel(x, y, MountainPixels[y * TileSize + x].R, MountainPixels[y * TileSize + x].G, MountainPixels[y * TileSize + x].B);
}
}
Then you can draw to the WorldBmp bitmap using normal Tile Map methods. IE, draw a mountain tile:
WorldBmp.SetPixels(x * TileSize, y * TileSize, MountainBmp.GetPixels());
I hope this is useful to someone :)
*Footnote: This actually took me an entire evening to write. No matter what I did, windows picture viewer would always report the bitmap was corrupt. In the end it turned out I had accidently opened the destination file as a text document and was writing in ASCII instead of binary - it probably worked first time. I'll admit to raging a little after that one. Then laughing.
The obvious choice is to create an image then. The problem is that I needed to quickly create a LARGE (giant even) image which is a problem when dealing with XNA Texture2D objects because they are all stored on the graphics card which severely limits the maximum size. I did not have access to System.Drawing in this project and nor did I wish to add it.
Googling around a bit I found I was not alone in this problem however there were little in satisfactory solutions. So what I've done is knock up a very quick (*see foot note) software bitmap handler for C#. It relies on very little and does NOT use unsafe to do it's stuff.
It's not fast and it's not full featured - but it does allow you to very easily create bitmap images, edit them programmatically and then save them to disk in a proper format for your OS to open and display. As I found I was not alone in needing this I've decided to share it, so here it is:
HS_Bitmap.cs
Feel free to use/modify/whatever. It does not really have a real-time application as it's not fast - but as a utility it may be very useful. I have thrown in some basic inline documentation for your intellisense etc - if you'd like more details on the bitmap format then check wikipedia, it's where I got the info from!
As a bonus, here's a quick example of how you would copy an XNA Texture2D into the bitmap object if you wanted to do tile map representation:
First load your image data and create your objects:
HS_Bitmap WorldBmp = new HS_Bitmap(WorldWidth * TileSize, WorldHeight * TileSize);
HS_Bitmap WaterBmp = new HS_Bitmap(TileSize, TileSize);
HS_Bitmap PlainsBmp = new HS_Bitmap(TileSize, TileSize);
HS_Bitmap MountainBmp = new HS_Bitmap(TileSize, TileSize);
Texture2D WaterImg = SystemVars.g_MainContentManager.Load<Texture2D>("DATA_2D\\Dodeka\\Water");
Texture2D PlainsImg = SystemVars.g_MainContentManager.Load<Texture2D>("DATA_2D\\Dodeka\\Plains");
Texture2D MountainImg = SystemVars.g_MainContentManager.Load<Texture2D>("DATA_2D\\Dodeka\\Mountain");
Create some arrays to hold colour data and copy the XNA Texture2D data into them:
Color[] WaterPixels = new Color[TileSize * TileSize];
Color[] PlainsPixels = new Color[TileSize * TileSize];
Color[] MountainPixels = new Color[TileSize * TileSize];
WaterImg.GetData<Color>(WaterPixels);
PlainsImg.GetData<Color>(PlainsPixels);
MountainImg.GetData<Color>(MountainPixels);
Set the HS_Bitmap pixel data to match the Texture2D Color data:
for (int x = 0; x < TileSize; x++ )
{
for (int y = 0; y < TileSize; y++)
{
WaterBmp.SetPixel(x, y, WaterPixels[y * TileSize + x].R, WaterPixels[y * TileSize + x].G, WaterPixels[y * TileSize + x].B);
PlainsBmp.SetPixel(x, y, PlainsPixels[y * TileSize + x].R, PlainsPixels[y * TileSize + x].G, PlainsPixels[y * TileSize + x].B);
MountainBmp.SetPixel(x, y, MountainPixels[y * TileSize + x].R, MountainPixels[y * TileSize + x].G, MountainPixels[y * TileSize + x].B);
}
}
Then you can draw to the WorldBmp bitmap using normal Tile Map methods. IE, draw a mountain tile:
WorldBmp.SetPixels(x * TileSize, y * TileSize, MountainBmp.GetPixels());
I hope this is useful to someone :)
*Footnote: This actually took me an entire evening to write. No matter what I did, windows picture viewer would always report the bitmap was corrupt. In the end it turned out I had accidently opened the destination file as a text document and was writing in ASCII instead of binary - it probably worked first time. I'll admit to raging a little after that one. Then laughing.