Skip to content

Instantly share code, notes, and snippets.

@bojanrajkovic
Created February 20, 2018 03:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bojanrajkovic/3b17bce7efd59cd808d82f187c54c1d9 to your computer and use it in GitHub Desktop.
Save bojanrajkovic/3b17bce7efd59cd808d82f187c54c1d9 to your computer and use it in GitHub Desktop.
CPBitmap Reader
using System;
using System.IO;
using System.Threading.Tasks;
namespace CodeRinseRepeat.CPBitmapReader
{
public sealed class CPBitmap
{
public int Width { get; set; }
public int Height { get; set; }
public byte[] Data { get; set; }
private CPBitmap() { }
public static async Task<CPBitmap> ReadAsync(Stream stream)
{
// We need to be able to seek the stream--the data we want starts
// at -24 bytes from the end. If we can't seek the stream, read it
// into memory, pass it off.
if (!stream.CanRead)
throw new ArgumentException(
"Given stream cannot be read.",
nameof(stream)
);
if (!stream.CanSeek) {
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream);
stream = memoryStream;
}
stream.Seek(-24, SeekOrigin.End);
// We need to grab a BinaryReader so we can read the W & H. The first 4 bytes
// we don't care about, the following 8 are the width and height.
var reader = new BinaryReader(stream);
reader.ReadInt32();
int width = reader.ReadInt32(), height = reader.ReadInt32();
// Now that we know the W&H, we need to rewind the stream back to the beginning.
stream.Seek(0, SeekOrigin.Begin);
// We'll use a MemoryStream to make writing the data a little easier.
var imageData = new MemoryStream();
// CPBitmaps are BGRA32 data, so each scanline is width * 4 bytes.
var scanlineBuffer = new byte[width * 4];
for (var i = 0; i < height; i++) {
// Each scanline is composed of `width` 4-byte pixels, with some padding. I don't
// have enough test data to verify the padding, but I'm going to guess it's always
// a full 8 bytes.
var read = await stream.ReadAsync(scanlineBuffer, 0, scanlineBuffer.Length);
await imageData.WriteAsync(scanlineBuffer, 0, read);
}
return new CPBitmap {
Width = width,
Height = height,
Data = imageData.ToArray()
};
}
private static Task<CPBitmap> ReadAsync(byte[] data)
{
// Wrap a stream over the data...
var memoryStream = new MemoryStream(data);
return ReadAsync(memoryStream);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment