diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index 32025f69fc..247ed78117 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -163,7 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer);
this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize);
this.WriteImage(stream, image.Frames.RootFrame);
- this.WriteColorProfile(stream, metadata, buffer);
+ this.WriteColorProfile(stream, iccProfileData, buffer);
stream.Flush();
}
@@ -246,14 +246,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes the color profile to the stream.
///
/// The stream to write to.
- /// The metadata.
+ /// The color profile data.
/// The buffer.
- private void WriteColorProfile(Stream stream, ImageMetadata metadata, Span buffer)
+ private void WriteColorProfile(Stream stream, byte[] iccProfileData, Span buffer)
{
- if (metadata.IccProfile != null)
+ if (iccProfileData != null)
{
- int streamPositionAfterImageData = (int)stream.Position;
- stream.Write(metadata.IccProfile.ToByteArray());
+ // The offset, in bytes, from the beginning of the BITMAPV5HEADER structure to the start of the profile data.
+ int streamPositionAfterImageData = (int)stream.Position - BmpFileHeader.Size;
+ stream.Write(iccProfileData);
BinaryPrimitives.WriteInt32LittleEndian(buffer, streamPositionAfterImageData);
stream.Position = BmpFileHeader.Size + 112;
stream.Write(buffer.Slice(0, 4));
diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
index 43ec45a34f..b42569a232 100644
--- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
@@ -29,10 +29,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
public static readonly string[] BitfieldsBmpFiles = BitFields;
- private static BmpDecoder BmpDecoder => new BmpDecoder();
+ private static BmpDecoder BmpDecoder => new();
public static readonly TheoryData RatioFiles =
- new TheoryData
+ new()
{
{ Car, 3780, 3780, PixelResolutionUnit.PixelsPerMeter },
{ V5Header, 3780, 3780, PixelResolutionUnit.PixelsPerMeter },
diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
index 073cf5fcf2..2215d5f0a5 100644
--- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
@@ -301,6 +301,33 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
public void Encode_PreservesAlpha(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel)
where TPixel : unmanaged, IPixel => TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true);
+ [Theory]
+ [WithFile(IccProfile, PixelTypes.Rgba32)]
+ public void Encode_PreservesColorProfile(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using (Image input = provider.GetImage(new BmpDecoder()))
+ {
+ ImageSharp.Metadata.Profiles.Icc.IccProfile expectedProfile = input.Metadata.IccProfile;
+ byte[] expectedProfileBytes = expectedProfile.ToByteArray();
+
+ using (var memStream = new MemoryStream())
+ {
+ input.Save(memStream, new BmpEncoder());
+
+ memStream.Position = 0;
+ using (var output = Image.Load(memStream))
+ {
+ ImageSharp.Metadata.Profiles.Icc.IccProfile actualProfile = output.Metadata.IccProfile;
+ byte[] actualProfileBytes = actualProfile.ToByteArray();
+
+ Assert.NotNull(actualProfile);
+ Assert.Equal(expectedProfileBytes, actualProfileBytes);
+ }
+ }
+ }
+ }
+
[Theory]
[WithFile(Car, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)]
[WithFile(V5Header, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel32)]
diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs
index 8931c242ef..6a582e0199 100644
--- a/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpMetadataTests.cs
@@ -3,7 +3,7 @@
using System.IO;
using SixLabors.ImageSharp.Formats.Bmp;
-
+using SixLabors.ImageSharp.PixelFormats;
using Xunit;
using static SixLabors.ImageSharp.Tests.TestImages.Bmp;
@@ -47,5 +47,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
Assert.Equal(expectedInfoHeaderType, bitmapMetadata.InfoHeaderType);
}
}
+
+ [Theory]
+ [WithFile(IccProfile, PixelTypes.Rgba32)]
+ public void Decoder_CanReadColorProfile(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ using (Image image = provider.GetImage(new BmpDecoder()))
+ {
+ ImageSharp.Metadata.ImageMetadata metaData = image.Metadata;
+ Assert.NotNull(metaData);
+ Assert.NotNull(metaData.IccProfile);
+ Assert.Equal(16, metaData.IccProfile.Entries.Length);
+ }
+ }
}
}
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index fa51fb2254..d36cec630d 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -379,6 +379,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Rgb24jpeg = "Bmp/rgb24jpeg.bmp";
public const string Rgb24png = "Bmp/rgb24png.bmp";
public const string Rgba32v4 = "Bmp/rgba32v4.bmp";
+ public const string IccProfile = "Bmp/BMP_v5_with_ICC_2.bmp";
// Bitmap images with compression type BITFIELDS.
public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp";
diff --git a/tests/Images/Input/Bmp/BMP_v5_with_ICC_2.bmp b/tests/Images/Input/Bmp/BMP_v5_with_ICC_2.bmp
new file mode 100644
index 0000000000..d6328d429f
--- /dev/null
+++ b/tests/Images/Input/Bmp/BMP_v5_with_ICC_2.bmp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b5b483e9a9d3f3ebdeada2eff70800002c27c046bf971206af0ecc73fa1416e6
+size 27782