public static void DoFile(byte[] data, ParseContext context, Action <IcoFrame> processFrame) { var reader = new ByteReader(data, ByteOrder.LittleEndian); var idReserved = reader.NextUint16(); var idType = reader.NextUint16(); var idCount = reader.NextUint16(); if (idReserved != FileFormatConstants._iconMagicHeader) { throw new InvalidIcoFileException(IcoErrorCode.InvalidIcoHeader_idReserved, $"ICONDIR.idReserved should be {FileFormatConstants._iconMagicHeader}, was {idReserved}.", context); } if (idType != FileFormatConstants._iconMagicType) { throw new InvalidIcoFileException(IcoErrorCode.InvalidIconHeader_idType, $"ICONDIR.idType should be {FileFormatConstants._iconMagicType}, was {idType}.", context); } if (idCount == 0 || idCount > FileFormatConstants._iconMaxEntries) { throw new InvalidIcoFileException(IcoErrorCode.TooManyFrames, $"ICONDIR.idCount is {idCount}, an implausible value for an ICO file.", context); } for (var i = 0u; i < idCount; i++) { context.ImageDirectoryIndex = i; var source = ProcessIcoFrame(reader, context); processFrame(source); } context.ImageDirectoryIndex = null; }
private static void ReadBitmap16(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 = reader.NextUint16(); source.CookedData[x, y] = new Rgba32( _5To8[colorValue >> 10], _5To8[(colorValue >> 5) & 0x1f], _5To8[colorValue & 0x1f], 255); } } source.Encoding.PixelFormat = BitmapEncoding.Pixel_rgb15; 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 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); } }