Skip to content

Instantly share code, notes, and snippets.

@macromaniac
Last active June 12, 2024 08:46
Show Gist options
  • Save macromaniac/a64a68f39041158d5f22959f91f9fb49 to your computer and use it in GitHub Desktop.
Save macromaniac/a64a68f39041158d5f22959f91f9fb49 to your computer and use it in GitHub Desktop.
Low level C# Bitmap Serializer. Surprisingly 5x faster than the commonly used .net BitmapData (tested on 100x100 images). Mostly for when you want finer tune control over your bitmaps than .net gives you.
using System;
public class BitmapBytes
{
/// <summary>
/// Converts RGBA bytes into an RGBA Bitmap.
/// Pixels go left to right, bottom to top.
/// Can write to file with, e.g:
/// File.WriteAllBytes("example.bmp", BitmapBytes.FromRGBABytes(imageData, width));
/// </summary>
/// <param name="rgba">array of r,g,b,a pixels, must be divisible by 4</param>
/// <param name="width">the image width, in pixels, height is omitted as it gets calculated</param>
/// <returns>a byte array in bmp format</returns>
public static byte[] FromRGBABytes(byte[] rgba, int width)
{
int height = (rgba.Length / 4) / width;
var bytes = new byte[rgba.Length + bmpHeader.Length];
//write header
Buffer.BlockCopy(bmpHeader, 0, bytes, 0, bmpHeader.Length);
//write filesize to offset 2
Buffer.BlockCopy(BitConverter.GetBytes(bytes.Length), 0, bytes, 2, 4);
//write width to offset 18
Buffer.BlockCopy(BitConverter.GetBytes(width), 0, bytes, 18, 4);
//write height to offset 22
Buffer.BlockCopy(BitConverter.GetBytes(height), 0, bytes, 22, 4);
//write image size to offset 34
Buffer.BlockCopy(BitConverter.GetBytes(width * height * 4), 0, bytes, 34, 4);
//finally, write the image data
Buffer.BlockCopy(rgba, 0, bytes, bmpHeader.Length, rgba.Length);
return bytes;
}
static byte[] bmpHeader = new byte[] // All values are little-endian
{
0x42, 0x4D, // Signature 'BM'
0xaa, 0x00, 0x00, 0x00, // Size: e.g. 170 bytes
0x00, 0x00, // Unused
0x00, 0x00, // Unused
0x8a, 0x00, 0x00, 0x00, // Offset to image data
0x7c, 0x00, 0x00, 0x00, // DIB header size (124 bytes)
0x04, 0x00, 0x00, 0x00, // Width (e.g. 4px)
0x02, 0x00, 0x00, 0x00, // Height (e.g. 2px)
0x01, 0x00, // Planes (1)
0x20, 0x00, // Bits per pixel (32)
0x03, 0x00, 0x00, 0x00, // Format (bitfield = use bitfields | no compression)
0x20, 0x00, 0x00, 0x00, // Image raw size (32 bytes)
0x13, 0x0B, 0x00, 0x00, // Horizontal print resolution (2835 = 72dpi * 39.3701)
0x13, 0x0B, 0x00, 0x00, // Vertical print resolution (2835 = 72dpi * 39.3701)
0x00, 0x00, 0x00, 0x00, // Colors in palette (none)
0x00, 0x00, 0x00, 0x00, // Important colors (0 = all)
0xFF, 0x00, 0x00, 0x00, // R bitmask (00FF0000)
0x00, 0xFF, 0x00, 0x00, // G bitmask (0000FF00)
0x00, 0x00, 0xFF, 0x00, // B bitmask (000000FF)
0x00, 0x00, 0x00, 0xFF, // A bitmask (FF000000)
0x42, 0x47, 0x52, 0x73, // sRGB color space
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Unused R, G, B entries for color space
0x00, 0x00, 0x00, 0x00, // Unused Gamma X entry for color space
0x00, 0x00, 0x00, 0x00, // Unused Gamma Y entry for color space
0x00, 0x00, 0x00, 0x00, // Unused Gamma Z entry for color space
0x00, 0x00, 0x00, 0x00, // Unknown
0x00, 0x00, 0x00, 0x00, // Unknown
0x00, 0x00, 0x00, 0x00, // Unknown
0x00, 0x00, 0x00, 0x00, // Unknown
};
}
@macromaniac
Copy link
Author

ex usage (generating and displaying static):

       var rng = new Random();
        var width = 100;
        var height = 100;
        var imageData = new byte[width * height * 4];
        rng.NextBytes(imageData);
        var data = BitmapBytes.FromRGBABytes(imageData, width);
        File.WriteAllBytes("out.bmp", data);
        Process.Start("explorer.exe", @"out.bmp");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment