예제 #1
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);
        }
예제 #2
0
        /// <summary>
        /// Produce uncompressed bitmap data from a RLE8 stream.
        /// </summary>
        /// <remarks>
        /// RLE8 is a 2-byte run-length encoding.
        /// <br/>If first byte is 0, the second byte may have special meaning.
        /// <br/>Otherwise, the first byte is the length of the run and second byte is the color for the run.
        /// </remarks>
        /// <param name="w">The width of the bitmap.</param>
        /// <param name="buffer">Buffer for uncompressed data.</param>
        private void UncompressRle8(int w, Span <byte> buffer)
        {
#if NETCOREAPP2_1
            Span <byte> cmd = stackalloc byte[2];
#else
            byte[] cmd = new byte[2];
#endif
            int count = 0;

            while (count < buffer.Length)
            {
                if (this.stream.Read(cmd, 0, cmd.Length) != 2)
                {
                    BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from stream while uncompressing RLE8 bitmap.");
                }

                if (cmd[0] == RleCommand)
                {
                    switch (cmd[1])
                    {
                    case RleEndOfBitmap:
                        return;

                    case RleEndOfLine:
                        int extra = count % w;
                        if (extra > 0)
                        {
                            count += w - extra;
                        }

                        break;

                    case RleDelta:
                        int dx = this.stream.ReadByte();
                        int dy = this.stream.ReadByte();
                        count += (w * dy) + dx;

                        break;

                    default:
                        // If the second byte > 2, we are in 'absolute mode'
                        // Take this number of bytes from the stream as uncompressed data
                        int length = cmd[1];

                        byte[] run = new byte[length];

                        this.stream.Read(run, 0, run.Length);

                        run.AsSpan().CopyTo(buffer.Slice(count));

                        count += run.Length;

                        // Absolute mode data is aligned to two-byte word-boundary
                        int padding = length & 1;

                        this.stream.Skip(padding);

                        break;
                    }
                }
                else
                {
                    int  max  = count + cmd[0]; // as we start at the current count in the following loop, max is count + cmd[0]
                    byte cmd1 = cmd[1];         // store the value to avoid the repeated indexer access inside the loop

                    for (; count < max; count++)
                    {
                        buffer[count] = cmd1;
                    }
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Produce uncompressed bitmap data from a RLE4 stream.
        /// </summary>
        /// <remarks>
        /// RLE4 is a 2-byte run-length encoding.
        /// <br/>If first byte is 0, the second byte may have special meaning.
        /// <br/>Otherwise, the first byte is the length of the run and second byte contains two color indexes.
        /// </remarks>
        /// <param name="w">The width of the bitmap.</param>
        /// <param name="buffer">Buffer for uncompressed data.</param>
        private void UncompressRle4(int w, Span <byte> buffer)
        {
#if NETCOREAPP2_1
            Span <byte> cmd = stackalloc byte[2];
#else
            byte[] cmd = new byte[2];
#endif
            int count = 0;

            while (count < buffer.Length)
            {
                if (this.stream.Read(cmd, 0, cmd.Length) != 2)
                {
                    BmpThrowHelper.ThrowImageFormatException("Failed to read 2 bytes from the stream while uncompressing RLE4 bitmap.");
                }

                if (cmd[0] == RleCommand)
                {
                    switch (cmd[1])
                    {
                    case RleEndOfBitmap:
                        return;

                    case RleEndOfLine:
                        int extra = count % w;
                        if (extra > 0)
                        {
                            count += w - extra;
                        }

                        break;

                    case RleDelta:
                        int dx = this.stream.ReadByte();
                        int dy = this.stream.ReadByte();
                        count += (w * dy) + dx;

                        break;

                    default:
                        // If the second byte > 2, we are in 'absolute mode'.
                        // The second byte contains the number of color indexes that follow.
                        int max         = cmd[1];
                        int bytesToRead = (max + 1) / 2;

                        byte[] run = new byte[bytesToRead];

                        this.stream.Read(run, 0, run.Length);

                        int idx = 0;
                        for (int i = 0; i < max; i++)
                        {
                            byte twoPixels = run[idx];
                            if (i % 2 == 0)
                            {
                                byte leftPixel = (byte)((twoPixels >> 4) & 0xF);
                                buffer[count++] = leftPixel;
                            }
                            else
                            {
                                byte rightPixel = (byte)(twoPixels & 0xF);
                                buffer[count++] = rightPixel;
                                idx++;
                            }
                        }

                        // Absolute mode data is aligned to two-byte word-boundary
                        int padding = bytesToRead & 1;

                        this.stream.Skip(padding);

                        break;
                    }
                }
                else
                {
                    int max = cmd[0];

                    // The second byte contains two color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
                    byte twoPixels  = cmd[1];
                    byte rightPixel = (byte)(twoPixels & 0xF);
                    byte leftPixel  = (byte)((twoPixels >> 4) & 0xF);

                    for (int idx = 0; idx < max; idx++)
                    {
                        if (idx % 2 == 0)
                        {
                            buffer[count] = leftPixel;
                        }
                        else
                        {
                            buffer[count] = rightPixel;
                        }

                        count++;
                    }
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Decodes the image from the specified this._stream and sets
        /// the data to image.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="stream">The stream, where the image should be
        /// decoded from. Cannot be null (Nothing in Visual Basic).</param>
        /// <exception cref="System.ArgumentNullException">
        ///    <para><paramref name="stream"/> is null.</para>
        /// </exception>
        /// <returns>The decoded image.</returns>
        public Image <TPixel> Decode <TPixel>(Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            try
            {
                int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);

                var image = new Image <TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metaData);

                Buffer2D <TPixel> pixels = image.GetRootFramePixelBuffer();

                switch (this.infoHeader.Compression)
                {
                case BmpCompression.RGB:
                    if (this.infoHeader.BitsPerPixel == 32)
                    {
                        if (this.bmpMetaData.InfoHeaderType == BmpInfoHeaderType.WinVersion3)
                        {
                            this.ReadRgb32Slow(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
                        }
                        else
                        {
                            this.ReadRgb32Fast(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
                        }
                    }
                    else if (this.infoHeader.BitsPerPixel == 24)
                    {
                        this.ReadRgb24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
                    }
                    else if (this.infoHeader.BitsPerPixel == 16)
                    {
                        this.ReadRgb16(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
                    }
                    else if (this.infoHeader.BitsPerPixel <= 8)
                    {
                        this.ReadRgbPalette(
                            pixels,
                            palette,
                            this.infoHeader.Width,
                            this.infoHeader.Height,
                            this.infoHeader.BitsPerPixel,
                            bytesPerColorMapEntry,
                            inverted);
                    }

                    break;

                case BmpCompression.RLE8:
                case BmpCompression.RLE4:
                    this.ReadRle(this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);

                    break;

                case BmpCompression.BitFields:
                    this.ReadBitFields(pixels, inverted);

                    break;

                default:
                    BmpThrowHelper.ThrowNotSupportedException("Does not support this kind of bitmap files.");

                    break;
                }

                return(image);
            }
            catch (IndexOutOfRangeException e)
            {
                throw new ImageFormatException("Bitmap does not have a valid format.", e);
            }
        }