Esempio n. 1
0
        /// <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();
        }