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