예제 #1
0
        private static void ReadBitmap32(ByteReader reader, ParseContext context, int height, int width, IcoFrame source)
        {
            for (var y = height - 1; y >= 0; y--)
            {
                for (var x = 0; x < width; x++)
                {
                    var colorValue = new Bgra32 {
                        PackedValue = reader.NextUint32()
                    };
                    source.CookedData[x, y] = colorValue.ToRgba32();
                }
            }

            source.Encoding.PixelFormat = BmpUtil.IsAnyAlphaChannel(source.CookedData)
                ? BitmapEncoding.Pixel_argb32
                : BitmapEncoding.Pixel_0rgb32;

            ReadBitmapMask(reader, context, height, width, source);
        }
예제 #2
0
        private static IcoFrame ProcessIcoFrame(ByteReader reader, ParseContext context)
        {
            var bWidth        = reader.NextUint8();
            var bHeight       = reader.NextUint8();
            var bColorCount   = reader.NextUint8();
            var bReserved     = reader.NextUint8();
            var wPlanes       = reader.NextUint16();
            var wBitCount     = reader.NextUint16();
            var dwBytesInRes  = reader.NextUint32();
            var dwImageOffset = reader.NextUint32();

            if (bWidth != bHeight)
            {
                context.Reporter.WarnLine(IcoErrorCode.NotSquare, $"Icon is not square ({bWidth}x{bHeight}).", context.DisplayedPath, context.ImageDirectoryIndex.Value);
            }

            if (bReserved != FileFormatConstants._iconEntryReserved)
            {
                throw new InvalidIcoFileException(IcoErrorCode.InvalidFrameHeader_bReserved, $"ICONDIRECTORY.bReserved should be {FileFormatConstants._iconEntryReserved}, was {bReserved}.", context);
            }

            if (wPlanes > 1)
            {
                throw new InvalidIcoFileException(IcoErrorCode.InvalidFrameHeader_wPlanes, $"ICONDIRECTORY.wPlanes is {wPlanes}.  Only single-plane bitmaps are supported.", context);
            }

            if (dwBytesInRes > int.MaxValue)
            {
                throw new InvalidIcoFileException(IcoErrorCode.InvalidFrameHeader_dwBytesInRes, $"ICONDIRECTORY.dwBytesInRes == {dwBytesInRes}, which is unreasonably large.", context);
            }

            if (dwImageOffset > int.MaxValue)
            {
                throw new InvalidIcoFileException(IcoErrorCode.InvalidFrameHeader_dwImageOffset, $"ICONDIRECTORY.dwImageOffset == {dwImageOffset}, which is unreasonably large.", context);
            }

            var source = new IcoFrame
            {
                TotalDiskUsage = dwBytesInRes + /* sizeof(ICONDIRENTRY) */ 16,

                Encoding = new IcoFrameEncoding
                {
                    ClaimedBitDepth = wBitCount,
                    ClaimedHeight   = bHeight > 0 ? bHeight : 256u,
                    ClaimedWidth    = bWidth > 0 ? bWidth : 256u,
                },
            };

            source.RawData = reader.Data.Slice((int)dwImageOffset, (int)dwBytesInRes).ToArray();
            var bitmapHeader = new ByteReader(source.RawData, ByteOrder.LittleEndian);

            var signature = bitmapHeader.NextUint64();

            bitmapHeader.SeekOffset = 0;

            if (PngDecoder.IsProbablyPngFile(ByteOrderConverter.To(ByteOrder.NetworkEndian, signature)))
            {
                PngDecoder.DoPngEntry(bitmapHeader, context, source);
            }
            else
            {
                BmpDecoder.DoBitmapEntry(bitmapHeader, context, source);
            }

            return(source);
        }
예제 #3
0
        public static PngFileEncoding GetPngFileEncoding(Memory <byte> data)
        {
            var reader = new ByteReader(data, ByteOrder.NetworkEndian);

            if (FileFormatConstants._pngHeader != reader.NextUint64())
            {
                throw new InvalidPngFileException(IcoErrorCode.NotPng, $"Data stream does not begin with the PNG magic constant");
            }

            var chunkLength = reader.NextUint32();
            var chunkType   = reader.NextUint32();

            if (chunkType != _ihdrChunkName)
            {
                throw new InvalidPngFileException(IcoErrorCode.PngBadIHDR, $"PNG file should begin with IHDR chunk; found {chunkType} instead");
            }

            if (chunkLength < 13)
            {
                throw new InvalidPngFileException(IcoErrorCode.PngBadIHDR, $"IHDR chunk is invalid length {chunkLength}; expected at least 13 bytes");
            }

            var result = new PngFileEncoding
            {
                Width          = reader.NextUint32(),
                Height         = reader.NextUint32(),
                BitsPerChannel = reader.NextUint8(),
                ColorType      = (PngColorType)reader.NextUint8(),
            };

            if (result.Width == 0 || result.Height == 0)
            {
                throw new InvalidPngFileException(IcoErrorCode.PngIllegalInputDimensions, $"Illegal Width x Height of {result.Width} x {result.Height}");
            }

            switch (result.BitsPerChannel)
            {
            case 1:
            case 2:
            case 4:
            case 8:
            case 16:
                break;

            default:
                throw new InvalidPngFileException(IcoErrorCode.PngIllegalInputDepth, $"Illegal bits per color channel / palette entry of {result.BitsPerChannel}");
            }

            switch (result.ColorType)
            {
            case PngColorType.Grayscale:
            case PngColorType.RGB:
            case PngColorType.Palette:
            case PngColorType.GrayscaleAlpha:
            case PngColorType.RGBA:
                break;

            default:
                throw new InvalidPngFileException(IcoErrorCode.PngIllegalColorType, $"Illegal color type {result.ColorType}");
            }

            return(result);
        }
예제 #4
0
        private static void ReadIndexedBitmap(ByteReader reader, ParseContext context, uint bitDepth, uint colorTableSize, int height, int width, IcoFrame source)
        {
            var anyReservedChannel  = false;
            var anyIndexOutOfBounds = false;

            if (colorTableSize == 0)
            {
                colorTableSize = 1u << (int)bitDepth;
            }

            source.Encoding.PaletteSize = colorTableSize;

            if (colorTableSize > 1u << (int)bitDepth)
            {
                throw new InvalidIcoFileException(IcoErrorCode.InvalidBitapInfoHeader_biClrUsed, $"BITMAPINFOHEADER.biClrUsed is greater than 2^biBitCount (biClrUsed == {colorTableSize}, biBitCount = {bitDepth}).", context);
            }
            else if (colorTableSize < 1u << (int)bitDepth)
            {
                context.Reporter.WarnLine(IcoErrorCode.UndersizedColorTable, $"This bitmap uses a color table that is smaller than the bit depth ({colorTableSize} < 2^{bitDepth})", context.DisplayedPath, context.ImageDirectoryIndex.Value);
            }

            var colorTable = new Rgba32[colorTableSize];

            for (var i = 0; i < colorTableSize; i++)
            {
                var c = new Bgra32
                {
                    PackedValue = reader.NextUint32()
                };

                if (c.A != 0)
                {
                    anyReservedChannel = true;
                }

                c.A           = 255;
                colorTable[i] = c.ToRgba32();
            }

            var padding = reader.SeekOffset % 4;

            for (var y = height - 1; y >= 0; y--)
            {
                var bits = new BitReader(reader);

                for (var x = 0; x < width; x++)
                {
                    var colorIndex = bits.NextBit(bitDepth);

                    if (colorIndex >= colorTableSize)
                    {
                        anyIndexOutOfBounds     = true;
                        source.CookedData[x, y] = Rgba32.Black;
                    }
                    else
                    {
                        source.CookedData[x, y] = colorTable[colorIndex];
                    }
                }

                while ((reader.SeekOffset % 4) != padding)
                {
                    reader.SeekOffset += 1;
                }
            }

            switch (bitDepth)
            {
            case 1:
                source.Encoding.PixelFormat = BitmapEncoding.Pixel_indexed1;
                break;

            case 2:
                source.Encoding.PixelFormat = BitmapEncoding.Pixel_indexed2;
                break;

            case 4:
                source.Encoding.PixelFormat = BitmapEncoding.Pixel_indexed4;
                break;

            case 8:
                source.Encoding.PixelFormat = BitmapEncoding.Pixel_indexed8;
                break;
            }

            ReadBitmapMask(reader, context, height, width, source);

            if (anyReservedChannel)
            {
                context.Reporter.WarnLine(IcoErrorCode.NonzeroAlpha, $"Reserved Alpha channel used in color table.", context.DisplayedPath, context.ImageDirectoryIndex.Value);
            }

            if (anyIndexOutOfBounds)
            {
                context.Reporter.WarnLine(IcoErrorCode.IndexedColorOutOfBounds, $"Bitmap uses color at illegal index; pixel filled with Black color.", context.DisplayedPath, context.ImageDirectoryIndex.Value);
            }
        }
예제 #5
0
        public static void DoBitmapEntry(ByteReader reader, ParseContext context, IcoFrame source)
        {
            var biSize          = reader.NextUint32();
            var biWidth         = reader.NextInt32();
            var biHeight        = reader.NextInt32();
            var biPlanes        = reader.NextUint16();
            var biBitCount      = reader.NextUint16();
            var biCompression   = reader.NextUint32();
            var biSizeImage     = reader.NextUint32();
            var biXPelsPerMeter = reader.NextInt32();
            var biYPelsPerMeter = reader.NextInt32();
            var biClrUsed       = reader.NextUint32();
            var biClrImportant  = reader.NextUint32();

            if (biSize != FileFormatConstants._bitmapInfoHeaderSize)
            {
                throw new InvalidIcoFileException(IcoErrorCode.InvalidBitapInfoHeader_ciSize, $"BITMAPINFOHEADER.ciSize should be {FileFormatConstants._bitmapInfoHeaderSize}, was {biSize}.", context);
            }

            if (biXPelsPerMeter != 0)
            {
                context.Reporter.WarnLine(IcoErrorCode.InvalidBitapInfoHeader_biXPelsPerMeter, $"BITMAPINFOHEADER.biXPelsPerMeter should be 0, was {biXPelsPerMeter}.", context.DisplayedPath, context.ImageDirectoryIndex.Value);
            }

            if (biYPelsPerMeter != 0)
            {
                context.Reporter.WarnLine(IcoErrorCode.InvalidBitapInfoHeader_biYPelsPerMeter, $"BITMAPINFOHEADER.biYPelsPerMeter should be 0, was {biYPelsPerMeter}.", context.DisplayedPath, context.ImageDirectoryIndex.Value);
            }

            if (biCompression == FileFormatConstants.BI_BITFIELDS)
            {
                throw new InvalidIcoFileException(IcoErrorCode.BitfieldCompressionNotSupported, $"This tool does not implement icon bitmaps that use BI_BITFIELDS compression.  (The .ICO file may be okay, although it is certainly unusual.)", context);
            }

            if (biCompression != FileFormatConstants.BI_RGB)
            {
                throw new InvalidIcoFileException(IcoErrorCode.BitmapCompressionNotSupported, $"BITMAPINFOHEADER.biCompression is unknown value ({biCompression}).", context);
            }

            if (biHeight != source.Encoding.ClaimedHeight * 2)
            {
                context.Reporter.WarnLine(IcoErrorCode.MismatchedHeight, $"BITMAPINFOHEADER.biHeight is not exactly double ICONDIRECTORY.bHeight ({biHeight} != 2 * {source.Encoding.ClaimedHeight}).", context.DisplayedPath, context.ImageDirectoryIndex.Value);
            }

            if (biWidth != source.Encoding.ClaimedWidth)
            {
                context.Reporter.WarnLine(IcoErrorCode.MismatchedWidth, $"BITMAPINFOHEADER.biWidth is not exactly equal to ICONDIRECTORY.bWidth ({biWidth} != 2 * {source.Encoding.ClaimedWidth}).", context.DisplayedPath, context.ImageDirectoryIndex.Value);
            }

            var height = biHeight / 2;
            var width  = biWidth;

            source.Encoding.ActualHeight   = (uint)height;
            source.Encoding.ActualWidth    = (uint)width;
            source.Encoding.ActualBitDepth = biBitCount;
            source.Encoding.Type           = IcoEncodingType.Bitmap;
            source.CookedData = new Image <Rgba32>(width, height);

            switch (biBitCount)
            {
            case 1:
            case 2:
            case 4:
            case 8:
                ReadIndexedBitmap(reader, context, biBitCount, biClrUsed, height, width, source);
                break;

            case 16:
                ReadBitmap16(reader, context, height, width, source);
                break;

            case 24:
                ReadBitmap24(reader, context, biClrUsed, height, width, source);
                break;

            case 32:
                ReadBitmap32(reader, context, height, width, source);
                break;

            default:
                throw new InvalidIcoFileException(IcoErrorCode.InvalidBitapInfoHeader_biBitCount, $"BITMAPINFOHEADER.biBitCount is unknown value ({biBitCount}); expected 1, 4, 8, 16, or 32 bit depth.", context);
            }
        }