public static byte[] EncodeBitmap(ParseContext context, BitmapEncoding encoding, Dialect dialect, IcoFrame source) { context.LastEncodeError = IcoErrorCode.NoError; return((BmpUtil.GetBitDepthForPixelFormat(encoding) < 16) ? EncodeIndexedBitmap(context, encoding, dialect, source) : EncodeRgbBitmap(source, context, encoding, dialect)); }
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 void EncodeBitmapHeader(IcoFrame source, Dialect dialect, BitmapEncoding encoding, Rgba32[] colorTable, ByteWriter writer, out uint offsetToImageSize) { if (dialect != Dialect.Ico) { writer.AddUint16(FileFormatConstants._bitmapFileMagic); writer.AddUint32(0); // Size will be filled in later writer.AddUint32(0); // Reserved writer.AddUint32(0); // Offset will be filled in later } writer.AddUint32(FileFormatConstants._bitmapInfoHeaderSize); writer.AddUint32((uint)source.CookedData.Width); writer.AddUint32((uint)source.CookedData.Height * ((dialect == Dialect.Ico) ? 2u : 1u)); writer.AddUint16(1); // biPlanes writer.AddUint16((ushort)BmpUtil.GetBitDepthForPixelFormat(encoding)); // biBitCount writer.AddUint32(FileFormatConstants.BI_RGB); // biCompression offsetToImageSize = (uint)writer.SeekOffset; writer.AddUint32(0); // biSizeImage writer.AddUint32((dialect == Dialect.Ico) ? 0u : FileFormatConstants._72dpiInPixelsPerMeter); // biXPelsPerMeter writer.AddUint32((dialect == Dialect.Ico) ? 0u : FileFormatConstants._72dpiInPixelsPerMeter); // biYPelsPerMeter writer.AddUint32((uint)(colorTable?.Length ?? 0)); // biClrUsed writer.AddUint32(0); // biClrImportant if (colorTable != null) { foreach (var color in colorTable) { writer.AddUint8(color.B); writer.AddUint8(color.G); writer.AddUint8(color.R); writer.AddUint8(0); } } if (dialect != Dialect.Ico) { while (writer.Data.Count % 4 != 0) { writer.AddUint8(0); } } }
public static void DoPngEntry(ByteReader bitmapHeader, ParseContext context, IcoFrame source) { if (source.Encoding.ClaimedBitDepth != 32) { context.Reporter.WarnLine(IcoErrorCode.PngNot32Bit, $"PNG-encoded image with bit depth {source.Encoding.ClaimedBitDepth} (expected 32).", context.DisplayedPath, context.ImageDirectoryIndex.Value); } using (var stream = new MemoryStream(bitmapHeader.Data.ToArray())) { var decoder = new SixLabors.ImageSharp.Formats.Png.PngDecoder(); source.CookedData = decoder.Decode <Rgba32>(new Configuration(), stream); } source.Encoding.Type = IcoEncodingType.Png; source.Encoding.PixelFormat = BmpUtil.IsAnyPartialTransparency(source.CookedData) ? BitmapEncoding.Pixel_argb32 : BitmapEncoding.Pixel_0rgb32; source.Mask = GenerateMaskFromAlpha(source.CookedData); // Conservatively assume that the output wouldn't have used palette trimming, if it had been a bmp frame. if (source.Encoding.ClaimedBitDepth < 16) { source.Encoding.PaletteSize = 1u << (int)source.Encoding.ClaimedBitDepth; } var encoding = GetPngFileEncoding(bitmapHeader.Data); if (encoding.ColorType != PngColorType.RGBA) { context.Reporter.WarnLine(IcoErrorCode.PngNotRGBA32, $"ICO files require the embedded PNG image to be encoded in RGBA32 format; this is {encoding.ColorType}", context.DisplayedPath, context.ImageDirectoryIndex.Value); } else if (encoding.BitsPerChannel != 8) { context.Reporter.WarnLine(IcoErrorCode.PngNotRGBA32, $"ICO files require the embedded PNG image to be encoded in RGBA32 format; this is RGBA{encoding.BitsPerChannel * 4}", context.DisplayedPath, context.ImageDirectoryIndex.Value); } uint numChannels = 0; switch (encoding.ColorType) { case PngColorType.Grayscale: numChannels = 1; break; case PngColorType.RGB: numChannels = 3; break; case PngColorType.GrayscaleAlpha: numChannels = 2; break; case PngColorType.RGBA: numChannels = 4; break; case PngColorType.Palette: default: break; } source.Encoding.ActualHeight = encoding.Height; source.Encoding.ActualWidth = encoding.Width; source.Encoding.ActualBitDepth = encoding.BitsPerChannel * numChannels; }
private static byte[] EncodeIndexedBitmap(ParseContext context, BitmapEncoding encoding, Dialect dialect, IcoFrame source) { var numBits = BmpUtil.GetBitDepthForPixelFormat(encoding); var colorTable = BuildColorTable(1u << numBits, context, source); if (colorTable == null) { context.LastEncodeError = IcoErrorCode.TooManyColorsForBitDepth; return(null); } var writer = new ByteWriter(ByteOrder.LittleEndian); EncodeBitmapHeader(source, dialect, encoding, colorTable, writer, out var offsetToImageSize); var reverseTable = new Dictionary <Rgba32, int>(); for (var i = 0; i < colorTable.Length; i++) { if (!reverseTable.ContainsKey(colorTable[i])) { reverseTable.Add(colorTable[i], i); } } var offsetToData = (uint)writer.Data.Count; var padding = writer.Data.Count % 4; for (var y = source.CookedData.Height - 1; y >= 0; y--) { var bits = new BitWriter(writer); for (var x = 0; x < source.CookedData.Width; x++) { var color = source.CookedData[x, y]; if (source.Mask[x, y]) { switch (context.MaskedImagePixelEmitOptions) { case StrictnessPolicy.Compliant: color = new Rgba32(0, 0, 0, 255); break; case StrictnessPolicy.PreserveSource: // Pass through whatever the original pixel was. break; case StrictnessPolicy.Loose: color = colorTable.First(); break; } } color.A = 255; var index = reverseTable[color]; bits.AddBits((uint)numBits, (byte)index); } while ((writer.Data.Count % 4) != padding) { writer.AddUint8(0); } } return(FinalizeBitmap(source, encoding, dialect, writer, offsetToData, offsetToImageSize)); }