// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageProcessor.Formats { using System; using System.IO; /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// /// The encoder can currently only write 24-bit rgb images to streams. public class BmpEncoder : IImageEncoder { /// /// Gets or sets the quality of output for images. /// /// Bitmap is a lossless format so this is not used in this encoder. public int Quality { get; set; } /// public string MimeType => "image/bmp"; /// public string Extension => "bmp"; /// public bool IsSupportedFileExtension(string extension) { Guard.NotNullOrEmpty(extension, nameof(extension)); extension = extension.StartsWith(".") ? extension.Substring(1) : extension; return extension.Equals(this.Extension, StringComparison.OrdinalIgnoreCase) || extension.Equals("dip", StringComparison.OrdinalIgnoreCase); } /// public void Encode(ImageBase image, Stream stream) { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); int rowWidth = image.Width; int amount = (image.Width * 3) % 4; if (amount != 0) { rowWidth += 4 - amount; } BinaryWriter writer = new BinaryWriter(stream); BmpFileHeader fileHeader = new BmpFileHeader { Type = 19778, // BM Offset = 54, FileSize = 54 + (image.Height * rowWidth * 3) }; WriteHeader(writer, fileHeader); BmpInfoHeader infoHeader = new BmpInfoHeader { HeaderSize = 40, Height = image.Height, Width = image.Width, BitsPerPixel = 24, Planes = 1, Compression = BmpCompression.RGB, ImageSize = image.Height * rowWidth * 3, ClrUsed = 0, ClrImportant = 0 }; WriteInfo(writer, infoHeader); this.WriteImage(writer, image); writer.Flush(); } /// /// Writes the pixel data to the binary stream. /// /// /// The containing the stream to write to. /// /// /// The containing pixel data. /// private void WriteImage(BinaryWriter writer, ImageBase image) { // TODO: Add more compression formats. int amount = (image.Width * 3) % 4; if (amount != 0) { amount = 4 - amount; } for (int y = image.Height - 1; y >= 0; y--) { for (int x = 0; x < image.Width; x++) { // Limit the output range and multiply out from our floating point. // Convert back to b-> g-> r-> a order. // Convert to non-premultiplied color. Bgra32 color = Color.ToNonPremultiplied(image[x, y]); // Allocate 1 array instead of allocating 3. writer.Write(new[] { color.B, color.G, color.R }); } // Pad for (int i = 0; i < amount; i++) { writer.Write((byte)0); } } } /// /// 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(BinaryWriter 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 static void WriteInfo(BinaryWriter 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); } } }