mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 398e93818c9c9e901b125b7daa7a345fcc8d2787 Former-commit-id: 9aba9f2e190fc8a986e16d632a58ebc94fda2d5b Former-commit-id: fd99bf7129eb85360cae9484cccac3241af0681eaf/merge-core
16 changed files with 378 additions and 187 deletions
@ -0,0 +1,23 @@ |
|||
// <copyright file="BmpBitsPerPixel.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Formats |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the available bits per pixel for bitmap.
|
|||
/// </summary>
|
|||
public enum BmpBitsPerPixel |
|||
{ |
|||
/// <summary>
|
|||
/// 24 bits per pixel. Each pixel consists of 3 bytes.
|
|||
/// </summary>
|
|||
Pixel24 = 3, |
|||
|
|||
/// <summary>
|
|||
/// 32 bits per pixel. Each pixel consists of 4 bytes.
|
|||
/// </summary>
|
|||
Pixel32 = 4, |
|||
} |
|||
} |
|||
@ -0,0 +1,205 @@ |
|||
// <copyright file="BmpEncoderCore.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Formats |
|||
{ |
|||
using System; |
|||
using System.IO; |
|||
|
|||
using ImageProcessorCore.IO; |
|||
|
|||
/// <summary>
|
|||
/// Image encoder for writing an image to a stream as a Windows bitmap.
|
|||
/// </summary>
|
|||
/// <remarks>The encoder can currently only write 24-bit rgb images to streams.</remarks>
|
|||
internal sealed class BmpEncoderCore |
|||
{ |
|||
/// <summary>
|
|||
/// The number of bits per pixel.
|
|||
/// </summary>
|
|||
private BmpBitsPerPixel bmpBitsPerPixel; |
|||
|
|||
/// <summary>
|
|||
/// Encodes the image to the specified stream from the <see cref="ImageBase"/>.
|
|||
/// </summary>
|
|||
/// <param name="image">The <see cref="ImageBase"/> to encode from.</param>
|
|||
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
|
|||
/// <param name="bitsPerPixel">The <see cref="BmpBitsPerPixel"/></param>
|
|||
public void Encode(ImageBase image, Stream stream, BmpBitsPerPixel bitsPerPixel) |
|||
{ |
|||
Guard.NotNull(image, nameof(image)); |
|||
Guard.NotNull(stream, nameof(stream)); |
|||
|
|||
this.bmpBitsPerPixel = bitsPerPixel; |
|||
|
|||
int rowWidth = image.Width; |
|||
|
|||
int amount = (image.Width * (int)this.bmpBitsPerPixel) % 4; |
|||
if (amount != 0) |
|||
{ |
|||
rowWidth += 4 - amount; |
|||
} |
|||
|
|||
using (EndianBinaryWriter writer = new EndianBinaryWriter(EndianBitConverter.Little, stream)) |
|||
{ |
|||
int bpp = (int)this.bmpBitsPerPixel; |
|||
|
|||
BmpFileHeader fileHeader = new BmpFileHeader |
|||
{ |
|||
Type = 19778, // BM
|
|||
Offset = 54, |
|||
FileSize = 54 + (image.Height * rowWidth * bpp) |
|||
}; |
|||
|
|||
BmpInfoHeader infoHeader = new BmpInfoHeader |
|||
{ |
|||
HeaderSize = 40, |
|||
Height = image.Height, |
|||
Width = image.Width, |
|||
BitsPerPixel = (short)(8 * bpp), |
|||
Planes = 1, |
|||
ImageSize = image.Height * rowWidth * bpp, |
|||
ClrUsed = 0, |
|||
ClrImportant = 0 |
|||
}; |
|||
|
|||
WriteHeader(writer, fileHeader); |
|||
this.WriteInfo(writer, infoHeader); |
|||
this.WriteImage(writer, image); |
|||
|
|||
writer.Flush(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the bitmap header data to the binary stream.
|
|||
/// </summary>
|
|||
/// <param name="writer">
|
|||
/// The <see cref="EndianBinaryWriter"/> containing the stream to write to.
|
|||
/// </param>
|
|||
/// <param name="fileHeader">
|
|||
/// The <see cref="BmpFileHeader"/> containing the header data.
|
|||
/// </param>
|
|||
private static void WriteHeader(EndianBinaryWriter writer, BmpFileHeader fileHeader) |
|||
{ |
|||
writer.Write(fileHeader.Type); |
|||
writer.Write(fileHeader.FileSize); |
|||
writer.Write(fileHeader.Reserved); |
|||
writer.Write(fileHeader.Offset); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the bitmap information to the binary stream.
|
|||
/// </summary>
|
|||
/// <param name="writer">
|
|||
/// The <see cref="EndianBinaryWriter"/> containing the stream to write to.
|
|||
/// </param>
|
|||
/// <param name="infoHeader">
|
|||
/// The <see cref="BmpFileHeader"/> containing the detailed information about the image.
|
|||
/// </param>
|
|||
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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the pixel data to the binary stream.
|
|||
/// </summary>
|
|||
/// <param name="writer">
|
|||
/// The <see cref="EndianBinaryWriter"/> containing the stream to write to.
|
|||
/// </param>
|
|||
/// <param name="image">
|
|||
/// The <see cref="ImageBase"/> containing pixel data.
|
|||
/// </param>
|
|||
private void WriteImage(EndianBinaryWriter writer, ImageBase image) |
|||
{ |
|||
// TODO: Add more compression formats.
|
|||
int amount = (image.Width * (int)this.bmpBitsPerPixel) % 4; |
|||
if (amount != 0) |
|||
{ |
|||
amount = 4 - amount; |
|||
} |
|||
|
|||
switch (this.bmpBitsPerPixel) |
|||
{ |
|||
case BmpBitsPerPixel.Pixel32: |
|||
this.Write32bit(writer, image, amount); |
|||
break; |
|||
|
|||
case BmpBitsPerPixel.Pixel24: |
|||
this.Write24bit(writer, image, amount); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the 32bit color palette to the stream.
|
|||
/// </summary>
|
|||
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
|
|||
/// <param name="image">The <see cref="ImageBase"/> containing pixel data.</param>
|
|||
/// <param name="amount">The amount to pad each row by.</param>
|
|||
private void Write32bit(EndianBinaryWriter writer, ImageBase image, int 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]); |
|||
|
|||
// We can take advantage of BGRA here
|
|||
writer.Write(color.Bgra); |
|||
} |
|||
|
|||
// Pad
|
|||
for (int i = 0; i < amount; i++) |
|||
{ |
|||
writer.Write((byte)0); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Writes the 24bit color palette to the stream.
|
|||
/// </summary>
|
|||
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
|
|||
/// <param name="image">The <see cref="ImageBase"/> containing pixel data.</param>
|
|||
/// <param name="amount">The amount to pad each row by.</param>
|
|||
private void Write24bit(EndianBinaryWriter writer, ImageBase image, int 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
// <copyright file="BitmapTests.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Tests |
|||
{ |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
|
|||
using Formats; |
|||
|
|||
using Xunit; |
|||
|
|||
public class BitmapTests : FileTestBase |
|||
{ |
|||
[Fact] |
|||
public void BitmapCanEncodeDifferentBitRates() |
|||
{ |
|||
if (!Directory.Exists("TestOutput/Encode/Bitmap")) |
|||
{ |
|||
Directory.CreateDirectory("TestOutput/Encode/Bitmap"); |
|||
} |
|||
|
|||
foreach (string file in Files) |
|||
{ |
|||
using (FileStream stream = File.OpenRead(file)) |
|||
{ |
|||
Stopwatch watch = Stopwatch.StartNew(); |
|||
Image image = new Image(stream); |
|||
|
|||
string encodeFilename = "TestOutput/Encode/Bitmap/" + "24-" + Path.GetFileNameWithoutExtension(file) + ".bmp"; |
|||
|
|||
using (FileStream output = File.OpenWrite(encodeFilename)) |
|||
{ |
|||
image.Save(output, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel24 }); |
|||
} |
|||
|
|||
encodeFilename = "TestOutput/Encode/Bitmap/" + "32-" + Path.GetFileNameWithoutExtension(file) + ".bmp"; |
|||
|
|||
using (FileStream output = File.OpenWrite(encodeFilename)) |
|||
{ |
|||
image.Save(output, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel32 }); |
|||
} |
|||
|
|||
Trace.WriteLine($"{file} : {watch.ElapsedMilliseconds}ms"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
// <copyright file="PngTests.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessorCore.Tests |
|||
{ |
|||
using System.IO; |
|||
|
|||
using Formats; |
|||
|
|||
using Xunit; |
|||
|
|||
public class PngTests : FileTestBase |
|||
{ |
|||
[Fact] |
|||
public void ImageCanSaveIndexedPng() |
|||
{ |
|||
if (!Directory.Exists("TestOutput/Encode/Png")) |
|||
{ |
|||
Directory.CreateDirectory("TestOutput/Encode/Png"); |
|||
} |
|||
|
|||
foreach (string file in Files) |
|||
{ |
|||
using (FileStream stream = File.OpenRead(file)) |
|||
{ |
|||
Image image = new Image(stream); |
|||
|
|||
using (FileStream output = File.OpenWrite($"TestOutput/Encode/Png/{Path.GetFileNameWithoutExtension(file)}.png")) |
|||
{ |
|||
image.Quality = 256; |
|||
image.Save(output, new PngFormat()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue