// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Formats { using System; using System.IO; using IO; /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// internal sealed class BmpEncoderCore { /// /// The options for the encoder. /// private readonly IBmpEncoderOptions options; /// /// The amount to pad each row by. /// private int padding; /// /// Initializes a new instance of the class. /// /// The options for the encoder. public BmpEncoderCore(IBmpEncoderOptions options) { this.options = options ?? new BmpEncoderOptions(); } /// /// Encodes the image to the specified stream from the . /// /// The pixel format. /// The to encode from. /// The to encode the image data to. public void Encode(ImageBase image, Stream stream) where TColor : struct, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); // Cast to int will get the bytes per pixel short bpp = (short)(8 * (int)this.options.BitsPerPixel); int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); this.padding = bytesPerLine - (image.Width * (int)this.options.BitsPerPixel); // Do not use IDisposable pattern here as we want to preserve the stream. EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); BmpInfoHeader infoHeader = new BmpInfoHeader { HeaderSize = BmpInfoHeader.Size, Height = image.Height, Width = image.Width, BitsPerPixel = bpp, Planes = 1, ImageSize = image.Height * bytesPerLine, ClrUsed = 0, ClrImportant = 0 }; BmpFileHeader fileHeader = new BmpFileHeader { Type = 19778, // BM Offset = 54, FileSize = 54 + infoHeader.ImageSize }; WriteHeader(writer, fileHeader); this.WriteInfo(writer, infoHeader); this.WriteImage(writer, image); writer.Flush(); } /// /// Writes the bitmap header data to the binary stream. /// /// /// The containing the stream to write to. /// /// /// The containing the header data. /// private static void WriteHeader(EndianBinaryWriter writer, BmpFileHeader fileHeader) { writer.Write(fileHeader.Type); writer.Write(fileHeader.FileSize); writer.Write(fileHeader.Reserved); writer.Write(fileHeader.Offset); } /// /// Writes the bitmap information to the binary stream. /// /// /// The containing the stream to write to. /// /// /// The containing the detailed information about the image. /// private void WriteInfo(EndianBinaryWriter writer, BmpInfoHeader infoHeader) { writer.Write(infoHeader.HeaderSize); writer.Write(infoHeader.Width); writer.Write(infoHeader.Height); writer.Write(infoHeader.Planes); writer.Write(infoHeader.BitsPerPixel); writer.Write((int)infoHeader.Compression); writer.Write(infoHeader.ImageSize); writer.Write(infoHeader.XPelsPerMeter); writer.Write(infoHeader.YPelsPerMeter); writer.Write(infoHeader.ClrUsed); writer.Write(infoHeader.ClrImportant); } /// /// Writes the pixel data to the binary stream. /// /// The pixel format. /// The containing the stream to write to. /// /// The containing pixel data. /// private void WriteImage(EndianBinaryWriter writer, ImageBase image) where TColor : struct, IPixel { using (PixelAccessor pixels = image.Lock()) { switch (this.options.BitsPerPixel) { case BmpBitsPerPixel.Pixel32: this.Write32Bit(writer, pixels); break; case BmpBitsPerPixel.Pixel24: this.Write24Bit(writer, pixels); break; } } } /// /// Writes the 32bit color palette to the stream. /// /// The pixel format. /// The containing the stream to write to. /// The containing pixel data. private void Write32Bit(EndianBinaryWriter writer, PixelAccessor pixels) where TColor : struct, IPixel { using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyxw, this.padding)) { for (int y = pixels.Height - 1; y >= 0; y--) { pixels.CopyTo(row, y); writer.Write(row.Bytes, 0, row.Length); } } } /// /// Writes the 24bit color palette to the stream. /// /// The pixel format. /// The containing the stream to write to. /// The containing pixel data. private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels) where TColor : struct, IPixel { using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyx, this.padding)) { for (int y = pixels.Height - 1; y >= 0; y--) { pixels.CopyTo(row, y); writer.Write(row.Bytes, 0, row.Length); } } } } }