Beispiel #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.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();
            meta.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;

            // skip the remaining header because we can't read those parts
            this.stream.Skip(skipAmount);
        }
Beispiel #2
0
 /// <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>
        /// Reads the <see cref="BmpInfoHeader"/> from the stream.
        /// </summary>
        private void ReadInfoHeader()
        {
            byte[] data = new byte[BmpInfoHeader.MaxHeaderSize];

            // read header size
            this.currentStream.Read(data, 0, BmpInfoHeader.HeaderSizeSize);
            int headerSize = BitConverter.ToInt32(data, 0);

            if (headerSize < BmpInfoHeader.BitmapCoreHeaderSize)
            {
                throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported.");
            }

            int skipAmmount = 0;

            if (headerSize > BmpInfoHeader.MaxHeaderSize)
            {
                skipAmmount = headerSize - BmpInfoHeader.MaxHeaderSize;
                headerSize  = BmpInfoHeader.MaxHeaderSize;
            }

            // read the rest of the header
            this.currentStream.Read(data, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);

            switch (headerSize)
            {
            case BmpInfoHeader.BitmapCoreHeaderSize:
                this.infoHeader = this.ParseBitmapCoreHeader(data);
                break;

            case BmpInfoHeader.BitmapInfoHeaderSize:
                this.infoHeader = this.ParseBitmapInfoHeader(data);
                break;

            default:
                if (headerSize > BmpInfoHeader.BitmapInfoHeaderSize)
                {
                    this.infoHeader = this.ParseBitmapInfoHeader(data);
                    break;
                }
                else
                {
                    throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported.");
                }
            }

            // skip the remaining header because we can't read those parts
            this.currentStream.Skip(skipAmmount);
        }
Beispiel #4
0
        /// <summary>
        /// Parses a OS/2 version 2 bitmap header (64 bytes). Only the first 40 bytes are parsed which are
        /// very similar to the Bitmap v3 header. The other 24 bytes are ignored, but they do not hold any
        /// useful information for decoding the image.
        /// </summary>
        /// <param name="data">The data to parse.</param>
        /// <returns>The parsed header.</returns>
        /// <seealso href="https://www.fileformat.info/format/os2bmp/egff.htm"/>
        public static BmpInfoHeader ParseOs2Version2(ReadOnlySpan <byte> data)
        {
            var infoHeader = new BmpInfoHeader(
                headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
                width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
                height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
                planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
                bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)));

            int compression = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4));

            // The compression value in OS/2 bitmap has a different meaning than in windows bitmaps.
            // Map the OS/2 value to the windows values.
            switch (compression)
            {
            case 0:
                infoHeader.Compression = BmpCompression.RGB;
                break;

            case 1:
                infoHeader.Compression = BmpCompression.RLE8;
                break;

            case 2:
                infoHeader.Compression = BmpCompression.RLE4;
                break;

            case 4:
                infoHeader.Compression = BmpCompression.RLE24;
                break;

            default:
                // Compression type 3 (1DHuffman) is not supported.
                BmpThrowHelper.ThrowImageFormatException("Compression type is not supported. ImageSharp only supports uncompressed, RLE4, RLE8 and RLE24.");
                break;
            }

            infoHeader.ImageSize     = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4));
            infoHeader.XPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4));
            infoHeader.YPelsPerMeter = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4));
            infoHeader.ClrUsed       = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4));
            infoHeader.ClrImportant  = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4));

            // The following 24 bytes of the header are omitted.
            return(infoHeader);
        }
Beispiel #5
0
        /// <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));

            // Cast to int will get the bytes per pixel
            short bpp          = (short)(8 * (int)this.bitsPerPixel);
            int   bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);

            this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);

            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);

            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();
        }
Beispiel #6
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.Size)
            {
                // >= 40 bytes
                this.infoHeader = BmpInfoHeader.Parse(buffer);
            }
            else
            {
                throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
            }

            // 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));

            // Cast to int will get the bytes per pixel
            short bpp          = (short)(8 * (int)this.bitsPerPixel);
            int   bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);

            this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);

            // Do not use IDisposable pattern here as we want to preserve the stream.
            var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);

            var infoHeader = new BmpInfoHeader
            {
                HeaderSize   = sizeof(uint),
                Height       = image.Height,
                Width        = image.Width,
                BitsPerPixel = (ushort)bpp,
                Planes       = 1,
                ImageSize    = (uint)(image.Height * bytesPerLine),
                ClrUsed      = 0,
                ClrImportant = 0
            };

            uint offset     = (uint)(BmpFileHeader.Size + infoHeader.HeaderSize);
            var  fileHeader = new BmpFileHeader
            {
                Type      = 0x4D42, // BM
                FileSize  = offset + (uint)infoHeader.ImageSize,
                Reserved1 = 0,
                Reserved2 = 0,
                Offset    = offset
            };

            WriteHeader(writer, fileHeader);
            this.WriteInfo(writer, infoHeader);
            this.WriteImage(writer, image.Frames.RootFrame);

            writer.Flush();
        }
Beispiel #8
0
        /// <summary>
        /// Reads the <see cref="BmpInfoHeader"/> from the stream.
        /// </summary>
        private void ReadInfoHeader()
        {
            byte[] buffer = new byte[BmpInfoHeader.MaxHeaderSize];

            // read header size
            this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);

            int headerSize = BitConverter.ToInt32(buffer, 0);

            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.Size)
            {
                // >= 40 bytes
                this.infoHeader = BmpInfoHeader.Parse(buffer.AsSpan(0, 40));
            }
            else
            {
                throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
            }

            // skip the remaining header because we can't read those parts
            this.stream.Skip(skipAmount);
        }
Beispiel #9
0
        /// <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 : unmanaged, IPixel <TPixel>
        {
            Guard.NotNull(image, nameof(image));
            Guard.NotNull(stream, nameof(stream));

            this.configuration = image.GetConfiguration();
            ImageMetadata metadata    = image.Metadata;
            BmpMetadata   bmpMetadata = metadata.GetBmpMetadata();

            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;
                    }
                }
            }

            int infoHeaderSize = this.writeV4Header ? BmpInfoHeader.SizeV4 : BmpInfoHeader.SizeV3;
            var infoHeader     = new BmpInfoHeader(
                headerSize: infoHeaderSize,
                height: image.Height,
                width: image.Width,
                bitsPerPixel: bpp,
                planes: 1,
                imageSize: image.Height * bytesPerLine,
                clrUsed: 0,
                clrImportant: 0,
                xPelsPerMeter: hResolution,
                yPelsPerMeter: vResolution);

            if (this.writeV4Header && this.bitsPerPixel == BmpBitsPerPixel.Pixel32)
            {
                infoHeader.AlphaMask   = Rgba32AlphaMask;
                infoHeader.RedMask     = Rgba32RedMask;
                infoHeader.GreenMask   = Rgba32GreenMask;
                infoHeader.BlueMask    = Rgba32BlueMask;
                infoHeader.Compression = BmpCompression.BitFields;
            }

            int colorPaletteSize = this.bitsPerPixel == BmpBitsPerPixel.Pixel8 ? ColorPaletteSize8Bit : 0;

            var fileHeader = new BmpFileHeader(
                type: BmpConstants.TypeMarkers.Bitmap,
                fileSize: BmpFileHeader.Size + infoHeaderSize + infoHeader.ImageSize,
                reserved: 0,
                offset: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize);

            Span <byte> buffer = stackalloc byte[infoHeaderSize];

            fileHeader.WriteTo(buffer);

            stream.Write(buffer, 0, BmpFileHeader.Size);

            if (this.writeV4Header)
            {
                infoHeader.WriteV4Header(buffer);
            }
            else
            {
                infoHeader.WriteV3Header(buffer);
            }

            stream.Write(buffer, 0, infoHeaderSize);

            this.WriteImage(stream, image.Frames.RootFrame);

            stream.Flush();
        }
Beispiel #10
0
        /// <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));

            // Cast to int will get the bytes per pixel
            short bpp          = (short)(8 * (int)this.bitsPerPixel);
            int   bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);

            this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);

            // Set Resolution.
            ImageMetaData meta        = image.MetaData;
            int           hResolution = 0;
            int           vResolution = 0;

            if (meta.ResolutionUnits != PixelResolutionUnit.AspectRatio)
            {
                if (meta.HorizontalResolution > 0 && meta.VerticalResolution > 0)
                {
                    switch (meta.ResolutionUnits)
                    {
                    case PixelResolutionUnit.PixelsPerInch:

                        hResolution = (int)Math.Round(UnitConverter.InchToMeter(meta.HorizontalResolution));
                        vResolution = (int)Math.Round(UnitConverter.InchToMeter(meta.VerticalResolution));
                        break;

                    case PixelResolutionUnit.PixelsPerCentimeter:

                        hResolution = (int)Math.Round(UnitConverter.CmToMeter(meta.HorizontalResolution));
                        vResolution = (int)Math.Round(UnitConverter.CmToMeter(meta.VerticalResolution));
                        break;

                    case PixelResolutionUnit.PixelsPerMeter:
                        hResolution = (int)Math.Round(meta.HorizontalResolution);
                        vResolution = (int)Math.Round(meta.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();
        }
Beispiel #11
0
        /// <summary>
        /// Parses the <see cref="BmpInfoHeader"/> from the stream, assuming it uses the <c>WinInfoHeaderV3</c>, <c>WinInfoHeaderV4</c> or <see cref="WinInfoHeaderV5"/> format.
        /// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376.aspx">See this MSDN link for more information.</seealso>
        /// </summary>
        /// <param name="data">Header bytes read from the stream</param>
        /// <param name="headerSize">Header maximum bytes to read from <c>data</c></param>
        /// <returns>Parsed header</returns>
        private BmpInfoHeader ParseBitmapInfoHeader(byte[] data, int headerSize = (int)BmpNativeStructuresSizes.BITMAPINFOHEADER)
        {
            // Invalid header size
            if ((headerSize < (int)BmpNativeStructuresSizes.BITMAPINFOHEADER) || (headerSize > (int)BmpNativeStructuresSizes.BITMAPV5HEADER))
            {
                throw new NotSupportedException($"This kind of IBM OS/2 bitmap files (header size $headerSize) is not supported.");
            }

            var bmpInfo = new BmpInfoHeader
            {
                // Mark the header as a Microsoft Windows v3 or above informatiom header for the DIB
                IsOS2InfoHeaderV2 = false,

                HeaderSize    = BitConverter.ToUInt32(data, 0),
                Width         = BitConverter.ToInt32(data, 4),
                Height        = BitConverter.ToInt32(data, 8),
                Planes        = BitConverter.ToUInt16(data, 12),
                BitsPerPixel  = BitConverter.ToUInt16(data, 14),
                Compression   = (BmpCompression)BitConverter.ToUInt32(data, 16),
                ImageSize     = BitConverter.ToUInt32(data, 20),
                XPelsPerMeter = BitConverter.ToInt32(data, 24),
                YPelsPerMeter = BitConverter.ToInt32(data, 28),
                ClrUsed       = BitConverter.ToUInt32(data, 32),
                ClrImportant  = BitConverter.ToUInt32(data, 36)
            };

            // Read the bitmask fields if necessary for BITMAPINFOHEADER
            if (headerSize == (int)BmpNativeStructuresSizes.BITMAPINFOHEADER)
            {
                if (bmpInfo.Compression == BmpCompression.BitFields)
                {
                    // Windows NT 4 and Windows 98 RGB bitmask extention to BITMAPINFOHEADER
                    if ((bmpInfo.ComputePaletteStorageSize((long)this.fileHeader.Offset) - 3) >= bmpInfo.ClrUsed)
                    {
                        this.currentStream.Read(data, 40, 3 * sizeof(uint));
                        headerSize = (int)BmpNativeStructuresSizes.BITMAPINFOHEADER_NT;
                    }
                    else
                    {
                        switch ((int)bmpInfo.BitsPerPixel)
                        {
                        case (int)BmpBitsPerPixel.RGB16:
                            bmpInfo.RedMask   = BmpStandardBitmask.RGB555RedMask;
                            bmpInfo.GreenMask = BmpStandardBitmask.RGB555GreenMask;
                            bmpInfo.BlueMask  = BmpStandardBitmask.RGB555BlueMask;
                            bmpInfo.AlphaMask = BmpStandardBitmask.RGB555AlphaMask;
                            break;

                        case (int)BmpBitsPerPixel.RGB24:
                            bmpInfo.RedMask   = BmpStandardBitmask.RGB888RedMask;
                            bmpInfo.GreenMask = BmpStandardBitmask.RGB888GreenMask;
                            bmpInfo.BlueMask  = BmpStandardBitmask.RGB888BlueMask;
                            bmpInfo.AlphaMask = BmpStandardBitmask.RGB888AlphaMask;
                            break;

                        case (int)BmpBitsPerPixel.RGB32:
                            bmpInfo.RedMask   = BmpStandardBitmask.RGB8888RedMask;
                            bmpInfo.GreenMask = BmpStandardBitmask.RGB8888GreenMask;
                            bmpInfo.BlueMask  = BmpStandardBitmask.RGB8888BlueMask;
                            bmpInfo.AlphaMask = BmpStandardBitmask.RGB888AlphaMask;
                            break;
                        }
                    }
                }
                else if (bmpInfo.Compression == BmpCompression.AlphaBitFields)
                {
                    // Windows CE 5 RGBA bitmask extention to BITMAPINFOHEADER
                    if ((bmpInfo.ComputePaletteStorageSize((long)this.fileHeader.Offset) - 4) >= bmpInfo.ClrUsed)
                    {
                        this.currentStream.Read(data, 40, 4 * sizeof(uint));
                        headerSize = (int)BmpNativeStructuresSizes.BITMAPINFOHEADER_CE;
                    }
                    else
                    {
                        switch ((int)bmpInfo.BitsPerPixel)
                        {
                        case (int)BmpBitsPerPixel.RGB16:
                            bmpInfo.RedMask   = BmpStandardBitmask.RGB555RedMask;
                            bmpInfo.GreenMask = BmpStandardBitmask.RGB555GreenMask;
                            bmpInfo.BlueMask  = BmpStandardBitmask.RGB555BlueMask;
                            bmpInfo.AlphaMask = BmpStandardBitmask.RGB555AlphaMask;
                            break;

                        case (int)BmpBitsPerPixel.RGB24:
                            bmpInfo.RedMask   = BmpStandardBitmask.RGB888RedMask;
                            bmpInfo.GreenMask = BmpStandardBitmask.RGB888GreenMask;
                            bmpInfo.BlueMask  = BmpStandardBitmask.RGB888BlueMask;
                            bmpInfo.AlphaMask = BmpStandardBitmask.RGB888AlphaMask;
                            break;

                        case (int)BmpBitsPerPixel.RGB32:
                            bmpInfo.RedMask   = BmpStandardBitmask.RGB8888RedMask;
                            bmpInfo.GreenMask = BmpStandardBitmask.RGB8888GreenMask;
                            bmpInfo.BlueMask  = BmpStandardBitmask.RGB8888BlueMask;
                            bmpInfo.AlphaMask = BmpStandardBitmask.RGB888AlphaMask;
                            break;
                        }
                    }
                }
            }

            // Read the RGB bitmasks
            if (headerSize >= (int)BmpNativeStructuresSizes.BITMAPINFOHEADER_NT)
            {
                // RGB bitmasks
                bmpInfo.RedMask   = BitConverter.ToUInt32(data, 40);
                bmpInfo.GreenMask = BitConverter.ToUInt32(data, 44);
                bmpInfo.BlueMask  = BitConverter.ToUInt32(data, 48);

                if (headerSize >= (int)BmpNativeStructuresSizes.BITMAPINFOHEADER_CE)
                {
                    // Alpha bitmask
                    bmpInfo.AlphaMask = BitConverter.ToUInt32(data, 52);
                }
                else
                {
                    switch ((int)bmpInfo.BitsPerPixel)
                    {
                    case (int)BmpBitsPerPixel.RGB16:
                        bmpInfo.AlphaMask = BmpStandardBitmask.RGB555AlphaMask;
                        break;

                    case (int)BmpBitsPerPixel.RGB24:
                        bmpInfo.AlphaMask = BmpStandardBitmask.RGB888AlphaMask;
                        break;

                    case (int)BmpBitsPerPixel.RGB32:
                        bmpInfo.AlphaMask = BmpStandardBitmask.RGB888AlphaMask;
                        break;
                    }
                }
            }
            else
            {
                switch ((int)bmpInfo.BitsPerPixel)
                {
                case (int)BmpBitsPerPixel.RGB16:
                    bmpInfo.RedMask   = BmpStandardBitmask.RGB555RedMask;
                    bmpInfo.GreenMask = BmpStandardBitmask.RGB555GreenMask;
                    bmpInfo.BlueMask  = BmpStandardBitmask.RGB555BlueMask;
                    bmpInfo.AlphaMask = BmpStandardBitmask.RGB555AlphaMask;
                    break;

                case (int)BmpBitsPerPixel.RGB24:
                    bmpInfo.RedMask   = BmpStandardBitmask.RGB888RedMask;
                    bmpInfo.GreenMask = BmpStandardBitmask.RGB888GreenMask;
                    bmpInfo.BlueMask  = BmpStandardBitmask.RGB888BlueMask;
                    bmpInfo.AlphaMask = BmpStandardBitmask.RGB888AlphaMask;
                    break;

                case (int)BmpBitsPerPixel.RGB32:
                    bmpInfo.RedMask   = BmpStandardBitmask.RGB8888RedMask;
                    bmpInfo.GreenMask = BmpStandardBitmask.RGB8888GreenMask;
                    bmpInfo.BlueMask  = BmpStandardBitmask.RGB8888BlueMask;
                    bmpInfo.AlphaMask = BmpStandardBitmask.RGB888AlphaMask;
                    break;
                }
            }

            return(bmpInfo);
        }
Beispiel #12
0
        /// <summary>
        /// Parses the <see cref="BmpInfoHeader"/> from the stream, assuming it uses the <see cref="OS2InfoHeaderV2"/> format.
        /// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183372.aspx">See this MSDN link for more information.</seealso>
        /// </summary>
        /// <param name="data">Header bytes read from the stream</param>
        /// <param name="headerSize">Header maximum bytes to read from <c>data</c></param>
        /// <returns>Parsed header</returns>
        private BmpInfoHeader ParseBitmapOS2V2Header(byte[] data, int headerSize)
        {
            // Invalid header size
            if ((headerSize < (int)BmpNativeStructuresSizes.OS22XBITMAPHEADER_MIN) || (headerSize > (int)BmpNativeStructuresSizes.OS22XBITMAPHEADER_MAX))
            {
                throw new NotSupportedException($"This kind of IBM OS/2 bitmap files (header size $headerSize) is not supported.");
            }

            if ((headerSize == (int)BmpNativeStructuresSizes.BITMAPINFOHEADER) ||
                (headerSize == (int)BmpNativeStructuresSizes.BITMAPINFOHEADER_NT) ||
                (headerSize == (int)BmpNativeStructuresSizes.BITMAPINFOHEADER_CE))
            {
                // Check to see if is  valid Windows DIB v3 header
                uint bitsPerPixel = BitConverter.ToUInt16(data, 14);
                if ((bitsPerPixel == (uint)BmpBitsPerPixel.RGB16) || (bitsPerPixel == (uint)BmpBitsPerPixel.RGB32))
                {
                    // OS/2 bitmaps don’t support 16-bit and 32-bit per pixel formats, so must be a Windows DIB v3 header
                    // The bitfields and alpha-bitfield compression extensions for Windows NT and CE, hare detected here
                    return(this.ParseBitmapInfoHeader(data, headerSize));
                }
                else
                {
                    uint compression = BitConverter.ToUInt32(data, 16);
                    if ((headerSize == (int)BmpNativeStructuresSizes.BITMAPINFOHEADER) &&
                        (compression != (uint)BmpOS2Compression.Huffman1D) &&
                        (compression != (uint)BmpOS2Compression.RLE24))
                    {
                        // OS/2 40 bytes DIB v2 header is the same format as the Windows 40 byte DIB v3 header
                        // Only 1-bit Huffman-1D and 24-bit RLE-24 compressed DIBs hare different (Windows DIB headers don't support them)
                        return(this.ParseBitmapInfoHeader(data, headerSize));
                    }
                }
            }

            // OK, it's an OS/2 DIB v2 header
            var bmpInfo = new BmpInfoHeader
            {
                // Mark the header as an IBM OS/2 v2 informatiom header for the DIB
                IsOS2InfoHeaderV2 = true,

                // Minimum fields
                HeaderSize   = BitConverter.ToUInt32(data, 0),
                Width        = BitConverter.ToInt32(data, 4),
                Height       = BitConverter.ToInt32(data, 8),
                Planes       = BitConverter.ToUInt16(data, 12),
                BitsPerPixel = BitConverter.ToUInt16(data, 14),

                // Reset the fields that may not be defined
                Compression      = BmpCompression.RGB,
                ImageSize        = 0,
                XPelsPerMeter    = 0,
                YPelsPerMeter    = 0,
                ClrUsed          = 0,
                ClrImportant     = 0,
                Os2Units         = 0,
                Os2Reserved      = 0,
                Os2Recording     = 0,
                Os2Rendering     = BmpOS2CompressionHalftoning.NoHalftoning,
                Os2Size1         = 0,
                Os2Size2         = 0,
                Os2ColorEncoding = 0,
                Os2Identifier    = 0
            };

            // Extra fields
            if (headerSize > (int)BmpNativeStructuresSizes.OS22XBITMAPHEADER_MIN)
            {
                // IBM OS/2 v2 pixel data compression method used
                bmpInfo.Os2Compression = (BmpOS2Compression)BitConverter.ToUInt32(data, 16);
                if ((bmpInfo.Os2Compression == BmpOS2Compression.RGB) ||
                    (bmpInfo.Os2Compression == BmpOS2Compression.RLE8) ||
                    (bmpInfo.Os2Compression == BmpOS2Compression.RLE4))
                {
                    // It's the same as the Microsoft Windows compression, so update it
                    bmpInfo.Compression = (BmpCompression)((int)bmpInfo.Os2Compression);
                }

                if (headerSize > 20)
                {
                    bmpInfo.ImageSize = BitConverter.ToUInt32(data, 20);
                    if (headerSize > 24)
                    {
                        bmpInfo.XPelsPerMeter = BitConverter.ToInt32(data, 24);
                        if (headerSize > 28)
                        {
                            bmpInfo.YPelsPerMeter = BitConverter.ToInt32(data, 28);
                            if (headerSize > 32)
                            {
                                bmpInfo.ClrUsed = BitConverter.ToUInt32(data, 32);
                                if (headerSize > 36)
                                {
                                    bmpInfo.ClrImportant = BitConverter.ToUInt32(data, 36);
                                    if (headerSize > 40)
                                    {
                                        bmpInfo.Os2Units = BitConverter.ToUInt16(data, 40);
                                        if (headerSize > 42)
                                        {
                                            bmpInfo.Os2Reserved = BitConverter.ToUInt16(data, 42);
                                            if (headerSize > 44)
                                            {
                                                bmpInfo.Os2Recording = BitConverter.ToUInt16(data, 44);
                                                if (headerSize > 46)
                                                {
                                                    bmpInfo.Os2Rendering = (BmpOS2CompressionHalftoning)BitConverter.ToUInt16(data, 46);
                                                    if (headerSize > 48)
                                                    {
                                                        bmpInfo.Os2Size1 = BitConverter.ToUInt32(data, 48);
                                                        if (headerSize > 52)
                                                        {
                                                            bmpInfo.Os2Size2 = BitConverter.ToUInt32(data, 52);
                                                            if (headerSize > 56)
                                                            {
                                                                bmpInfo.Os2ColorEncoding = BitConverter.ToUInt32(data, 56);
                                                                if (headerSize > 60)
                                                                {
                                                                    bmpInfo.Os2Identifier = BitConverter.ToUInt32(data, 60);
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(bmpInfo);
        }
Beispiel #13
0
        /// <summary>
        /// Reads the <see cref="BmpInfoHeader"/> from the stream.
        /// </summary>
        private void ReadInfoHeader()
        {
            byte[] data = new byte[(int)BmpNativeStructuresSizes.BITMAPV5HEADER];

            // read header size
            this.currentStream.Read(data, 0, sizeof(uint));
            int headerSize = BitConverter.ToInt32(data, 0);

            // read the rest of the header
            int skipAmmount = 0;

            if (headerSize > (int)BmpNativeStructuresSizes.BITMAPV5HEADER)
            {
                // FIXME: If the header size is bigger than BITMAPV5HEADER structure, this is a bug
                skipAmmount = headerSize - (int)BmpNativeStructuresSizes.BITMAPV5HEADER;
                headerSize  = (int)BmpNativeStructuresSizes.BITMAPV5HEADER;
            }

            this.currentStream.Read(data, sizeof(uint), headerSize - sizeof(uint));

            switch (headerSize)
            {
            // Windows DIB Info Header v2 and OS/2 DIB Info Header v1
            case (int)BmpNativeStructuresSizes.BITMAPCOREHEADER:
                this.infoHeader = this.ParseBitmapCoreHeader(data);
                break;

            // OS/2 DIB Info Header v2 minimum size
            case (int)BmpNativeStructuresSizes.OS22XBITMAPHEADER_MIN:
            // OS/2 DIB Info Header v2 full size
            case (int)BmpNativeStructuresSizes.OS22XBITMAPHEADER:
                this.infoHeader = this.ParseBitmapOS2V2Header(data, headerSize);
                break;

            // Windows DIB Info Header v4
            case (int)BmpNativeStructuresSizes.BITMAPV4HEADER:
                this.infoHeader = this.ParseBitmapInfoHeader(data, headerSize);
                break;

            // Windows DIB Info Header v5
            case (int)BmpNativeStructuresSizes.BITMAPV5HEADER:
                this.infoHeader = this.ParseBitmapInfoHeader(data, headerSize);
                break;

            default:
                if ((headerSize > (int)BmpNativeStructuresSizes.OS22XBITMAPHEADER_MIN) &&
                    (headerSize < (int)BmpNativeStructuresSizes.OS22XBITMAPHEADER_MAX))
                {
                    // OS/2 DIB Info Header v2 variable size
                    this.infoHeader = this.ParseBitmapOS2V2Header(data, headerSize);
                    break;
                }
                else
                {
                    // UPS! Unknow DIB header
                    throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported.");
                }
            }

            // Check if the DIB is top-down (negative height => origin is the upper-left corner)
            // or bottom-up (positeve height => origin is the lower-left corner)
            if (this.infoHeader.Height < 0)
            {
                this.infoHeader.IsTopDown = true;
                this.infoHeader.Height    = -this.infoHeader.Height;
            }

            // Check if the DIB is pre-rotated
            if ((SourcePreRotateMask & (uint)this.infoHeader.Compression) == SourcePreRotateMask)
            {
                this.infoHeader.IsSourcePreRotate = true;
                this.infoHeader.Compression       = (BmpCompression)((uint)this.infoHeader.Compression & (~SourcePreRotateMask));
            }

            // skip the remaining header because we can't read those parts
            this.currentStream.Skip(skipAmmount);
        }