/// <summary> /// Reads the <see cref="BmpInfoHeader"/> from the stream. /// </summary> private void ReadInfoHeader() { #if NETCOREAPP2_1 Span <byte> buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize]; #else byte[] buffer = new byte[BmpInfoHeader.MaxHeaderSize]; #endif this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); // read the header size int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer); if (headerSize < BmpInfoHeader.CoreSize) { throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}."); } int skipAmount = 0; if (headerSize > BmpInfoHeader.MaxHeaderSize) { skipAmount = headerSize - BmpInfoHeader.MaxHeaderSize; headerSize = BmpInfoHeader.MaxHeaderSize; } // read the rest of the header this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); if (headerSize == BmpInfoHeader.CoreSize) { // 12 bytes this.infoHeader = BmpInfoHeader.ParseCore(buffer); } else if (headerSize == BmpInfoHeader.Os22ShortSize) { // 16 bytes this.infoHeader = BmpInfoHeader.ParseOs22Short(buffer); } else if (headerSize >= BmpInfoHeader.Size) { // >= 40 bytes this.infoHeader = BmpInfoHeader.Parse(buffer); } else { throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}."); } // Resolution is stored in PPM. var meta = new ImageMetaData { ResolutionUnits = PixelResolutionUnit.PixelsPerMeter }; if (this.infoHeader.XPelsPerMeter > 0 && this.infoHeader.YPelsPerMeter > 0) { meta.HorizontalResolution = this.infoHeader.XPelsPerMeter; meta.VerticalResolution = this.infoHeader.YPelsPerMeter; } else { // Convert default metadata values to PPM. meta.HorizontalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetaData.DefaultHorizontalResolution)); meta.VerticalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetaData.DefaultVerticalResolution)); } this.metaData = meta; short bitsPerPixel = this.infoHeader.BitsPerPixel; BmpMetaData bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance); // We can only encode at these bit rates so far. if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel32)) { bmpMetaData.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; } // skip the remaining header because we can't read those parts this.stream.Skip(skipAmount); }
/// <summary> /// Encodes the image to the specified stream from the <see cref="ImageFrame{TPixel}"/>. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> public void Encode<TPixel>(Image<TPixel> image, Stream stream) where TPixel : struct, IPixel<TPixel> { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); ImageMetaData metaData = image.MetaData; BmpMetaData bmpMetaData = metaData.GetFormatMetaData(BmpFormat.Instance); this.bitsPerPixel = this.bitsPerPixel ?? bmpMetaData.BitsPerPixel; short bpp = (short)this.bitsPerPixel; int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F)); // Set Resolution. int hResolution = 0; int vResolution = 0; if (metaData.ResolutionUnits != PixelResolutionUnit.AspectRatio) { if (metaData.HorizontalResolution > 0 && metaData.VerticalResolution > 0) { switch (metaData.ResolutionUnits) { case PixelResolutionUnit.PixelsPerInch: hResolution = (int)Math.Round(UnitConverter.InchToMeter(metaData.HorizontalResolution)); vResolution = (int)Math.Round(UnitConverter.InchToMeter(metaData.VerticalResolution)); break; case PixelResolutionUnit.PixelsPerCentimeter: hResolution = (int)Math.Round(UnitConverter.CmToMeter(metaData.HorizontalResolution)); vResolution = (int)Math.Round(UnitConverter.CmToMeter(metaData.VerticalResolution)); break; case PixelResolutionUnit.PixelsPerMeter: hResolution = (int)Math.Round(metaData.HorizontalResolution); vResolution = (int)Math.Round(metaData.VerticalResolution); break; } } } var infoHeader = new BmpInfoHeader( headerSize: BmpInfoHeader.Size, height: image.Height, width: image.Width, bitsPerPixel: bpp, planes: 1, imageSize: image.Height * bytesPerLine, clrUsed: 0, clrImportant: 0, xPelsPerMeter: hResolution, yPelsPerMeter: vResolution); var fileHeader = new BmpFileHeader( type: 19778, // BM offset: 54, reserved: 0, fileSize: 54 + infoHeader.ImageSize); #if NETCOREAPP2_1 Span<byte> buffer = stackalloc byte[40]; #else byte[] buffer = new byte[40]; #endif fileHeader.WriteTo(buffer); stream.Write(buffer, 0, BmpFileHeader.Size); infoHeader.WriteTo(buffer); stream.Write(buffer, 0, 40); this.WriteImage(stream, image.Frames.RootFrame); stream.Flush(); }