public static BmpInfo Load( ImageBinReader s, ReadState ri, ArrayPool <byte>?bytePool = null) { // TODO: optimize by pulling out some branching from loops bytePool ??= ArrayPool <byte> .Shared; var info = ParseHeader(s, ri) ?? throw new StbImageReadException(ErrorCode.UnknownHeader); ri.OutComponents = ri.Components; ri.OutDepth = Math.Max(ri.Depth, 8); ri.StateReady(); int psize = 0; if (info.headerSize == 12) { if (info.bitsPerPixel < 24) { psize = (info.offset - 14 - 24) / 3; } } else { if (info.bitsPerPixel < 16) { psize = (info.offset - 14 - info.headerSize) >> 2; } } int easy = 0; if (info.bitsPerPixel == 32) { if (info.mb == 0xffu << 0 && info.mg == 0xffu << 8 && info.mr == 0xffu << 16 && info.ma == 0xffu << 24) { easy = 2; } } else if (info.bitsPerPixel == 24) { easy = 1; } bool flipRows = (ri.Orientation & ImageOrientation.BottomToTop) == ImageOrientation.BottomToTop; int rowByteSize = ri.Width * ri.OutComponents; var rowBuffer = bytePool.Rent(rowByteSize); try { if (info.bitsPerPixel < 16) { if ((psize == 0) || (psize > 256)) { throw new StbImageReadException(ErrorCode.InvalidPLTE); } // TODO: output palette Span <byte> palette = stackalloc byte[256 * 4]; for (int x = 0; x < psize; ++x) { palette[x * 4 + 2] = s.ReadByte(); palette[x * 4 + 1] = s.ReadByte(); palette[x * 4 + 0] = s.ReadByte(); palette[x * 4 + 3] = info.headerSize == 12 ? (byte)255 : s.ReadByte(); } s.Skip( info.offset - 14 - info.headerSize - psize * (info.headerSize == 12 ? 3 : 4)); int width; if (info.bitsPerPixel == 4) { width = (ri.Width + 1) / 2; } else if (info.bitsPerPixel == 8) { width = ri.Width; } else { throw new StbImageReadException(ErrorCode.BadBitsPerPixel); } int pad = (-width) & 3; for (int y = 0; y < ri.Height; ++y) { for (int x = 0, z = 0; x < ri.Width; x += 2) { void WriteFromPalette(int comp, Span <byte> palette) { rowBuffer[z + 0] = palette[0]; rowBuffer[z + 1] = palette[1]; rowBuffer[z + 2] = palette[2]; if (comp == 4) { rowBuffer[z + 3] = palette[3]; } z += comp; } int v2 = 0; int v1 = s.ReadByte(); if (info.bitsPerPixel == 4) { v2 = v1 & 15; v1 >>= 4; } WriteFromPalette(ri.OutComponents, palette[(v1 * 4)..]);
public static TgaInfo ParseHeader(ImageBinReader reader, ReadState state) { if (reader == null) { throw new ArgumentNullException(nameof(reader)); } if (state == null) { throw new ArgumentNullException(nameof(state)); } Span <byte> tmp = stackalloc byte[HeaderSize]; if (!reader.TryReadBytes(tmp)) { throw new EndOfStreamException(); } bool test = TestCore(tmp, out var info); if (info.colormap_type == 1) { if (info.image_type != 1 && info.image_type != 9) { throw new StbImageReadException(ErrorCode.BadImageType); } info.palette_start = reader.ReadInt16LE(); info.palette_len = reader.ReadInt16LE(); info.palette_bpp = reader.ReadByte(); if (info.palette_bpp != 8 && info.palette_bpp != 15 && info.palette_bpp != 16 && info.palette_bpp != 24 && info.palette_bpp != 32) { throw new StbImageReadException(ErrorCode.BadPalette); } } else { if (info.image_type != 2 && info.image_type != 3 && info.image_type != 10 && info.image_type != 11) { throw new StbImageReadException(ErrorCode.BadImageType); } reader.Skip(5); // 16bit: Color Map Origin // 16bit: Color Map Length // 8bit: Color Map Entry Size } Debug.Assert(test); // Prior checks should throw if test was unsucessful. info.x_origin = reader.ReadInt16LE(); info.y_origin = reader.ReadInt16LE(); state.Width = reader.ReadUInt16LE(); if (state.Width < 1) { throw new StbImageReadException(ErrorCode.ZeroWidth); } state.Height = reader.ReadUInt16LE(); if (state.Height < 1) { throw new StbImageReadException(ErrorCode.ZeroHeight); } info.bits_per_pixel = reader.ReadByte(); info.inverted = reader.ReadByte(); info.inverted = 1 - ((info.inverted >> 5) & 1); // use the number of bits from the palette if paletted if (info.palette_bpp != 0) { if (info.bits_per_pixel != 8 && info.bits_per_pixel != 16) { throw new StbImageReadException(ErrorCode.BadBitsPerPixel); } state.Components = GetComponentCount( info.palette_bpp, false, out state.Depth); } else { state.Components = GetComponentCount( info.bits_per_pixel, info.image_type == 3, out state.Depth); } if (state.Components == 0) { throw new StbImageReadException(ErrorCode.BadComponentCount); } state.OutComponents = state.Components; state.OutDepth = state.Depth; state.Orientation = ImageOrientation.LeftToRight | (info.inverted != 0 ? ImageOrientation.BottomToTop : ImageOrientation.TopToBottom); state.StateReady(); return(info); }