Этап 10
This commit is contained in:
80
Minint.Infrastructure/Export/BmpExporter.cs
Normal file
80
Minint.Infrastructure/Export/BmpExporter.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using Minint.Core.Services;
|
||||
|
||||
namespace Minint.Infrastructure.Export;
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 32-bit BGRA BMP (BITMAPV4HEADER) from an ARGB pixel buffer.
|
||||
/// BMP rows are bottom-up, so we flip vertically during write.
|
||||
/// </summary>
|
||||
public sealed class BmpExporter : IBmpExporter
|
||||
{
|
||||
private const int BmpFileHeaderSize = 14;
|
||||
private const int BitmapV4HeaderSize = 108;
|
||||
private const int HeadersTotal = BmpFileHeaderSize + BitmapV4HeaderSize;
|
||||
|
||||
public void Export(Stream stream, uint[] pixels, int width, int height)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(stream);
|
||||
ArgumentNullException.ThrowIfNull(pixels);
|
||||
if (pixels.Length != width * height)
|
||||
throw new ArgumentException("Pixel buffer size does not match dimensions.");
|
||||
|
||||
int rowBytes = width * 4;
|
||||
int imageSize = rowBytes * height;
|
||||
int fileSize = HeadersTotal + imageSize;
|
||||
|
||||
using var w = new BinaryWriter(stream, System.Text.Encoding.UTF8, leaveOpen: true);
|
||||
|
||||
// BITMAPFILEHEADER (14 bytes)
|
||||
w.Write((byte)'B');
|
||||
w.Write((byte)'M');
|
||||
w.Write(fileSize);
|
||||
w.Write((ushort)0); // reserved1
|
||||
w.Write((ushort)0); // reserved2
|
||||
w.Write(HeadersTotal); // pixel data offset
|
||||
|
||||
// BITMAPV4HEADER (108 bytes)
|
||||
w.Write(BitmapV4HeaderSize);
|
||||
w.Write(width);
|
||||
w.Write(height); // positive = bottom-up
|
||||
w.Write((ushort)1); // planes
|
||||
w.Write((ushort)32); // bpp
|
||||
w.Write(3); // biCompression = BI_BITFIELDS
|
||||
w.Write(imageSize);
|
||||
w.Write(2835); // X pixels per meter (~72 DPI)
|
||||
w.Write(2835); // Y pixels per meter
|
||||
w.Write(0); // colors used
|
||||
w.Write(0); // important colors
|
||||
|
||||
// Channel masks (BGRA order in file)
|
||||
w.Write(0x00FF0000u); // red mask
|
||||
w.Write(0x0000FF00u); // green mask
|
||||
w.Write(0x000000FFu); // blue mask
|
||||
w.Write(0xFF000000u); // alpha mask
|
||||
|
||||
// Color space type: LCS_sRGB
|
||||
w.Write(0x73524742); // 'sRGB'
|
||||
// CIEXYZTRIPLE endpoints (36 bytes zeroed)
|
||||
w.Write(new byte[36]);
|
||||
// Gamma RGB (12 bytes zeroed)
|
||||
w.Write(new byte[12]);
|
||||
|
||||
// Pixel data: BMP is bottom-up, our buffer is top-down
|
||||
for (int y = height - 1; y >= 0; y--)
|
||||
{
|
||||
int rowStart = y * width;
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
uint argb = pixels[rowStart + x];
|
||||
byte a = (byte)(argb >> 24);
|
||||
byte r = (byte)((argb >> 16) & 0xFF);
|
||||
byte g = (byte)((argb >> 8) & 0xFF);
|
||||
byte b = (byte)(argb & 0xFF);
|
||||
w.Write(b);
|
||||
w.Write(g);
|
||||
w.Write(r);
|
||||
w.Write(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user