private static void WriteBmpFrame(CommandLineOptions opts, ParseContext context, IcoFrame frame) { var encoding = opts.BitmapEncodingOverride ?? BmpUtil.GetIdealBitmapEncoding(frame.CookedData, hasIcoMask: true); if (opts.MaskImagePath != null) { var maskSource = File.ReadAllBytes(opts.MaskImagePath); using (var stream = new MemoryStream(maskSource)) { var decoder = new SixLabors.ImageSharp.Formats.Bmp.BmpDecoder(); var mask = decoder.Decode <Rgba32>(new Configuration(), stream); if (mask.Width != frame.CookedData.Width || mask.Height != frame.CookedData.Height) { Reporter.ErrorLine( IcoErrorCode.BitmapMaskWrongDimensions, $"The mask's dimentions {mask.Height}x{mask.Width} don't match " + $"the frame dimensions {frame.CookedData.Height}x{frame.CookedData.Width}.", opts.MaskImagePath); throw new ArgumentException(); } for (var x = 0; x < mask.Width; x++) { for (var y = 0; y < mask.Height; y++) { if (mask[x, y].PackedValue != 0xffffffff && mask[x, y].PackedValue != 0x000000ff) { Reporter.ErrorLine( IcoErrorCode.BitampMaskWrongColors, $"The mask must be comprised entirely of black and white pixels (where black means transparent).", opts.MaskImagePath); throw new ArgumentException(); } } } frame.Mask = BmpUtil.CreateMaskFromImage(mask, blackIsTransparent: true); } } else { frame.Mask = BmpUtil.CreateMaskFromImage(frame.CookedData, blackIsTransparent: false); } frame.RawData = BmpEncoder.EncodeBitmap(context, encoding, BmpEncoder.Dialect.Ico, frame); if (frame.RawData == null) { Reporter.ErrorLine(context.LastEncodeError, $"Cannot encode the source image as a bitmap of type: {encoding}. Try reducing the number of colors, or changing the bitmap encoding.", opts.SourceImagePath); throw new ArgumentException(); } frame.Encoding.ClaimedBitDepth = (uint)BmpUtil.GetBitDepthForPixelFormat(encoding); frame.Encoding.ActualBitDepth = frame.Encoding.ClaimedBitDepth; }
private static void AddFrame(IcoFrame frame, ParseContext context, CommandLineOptions opts) { var outputEncoding = GetOutputEncoding(frame, opts); var extension = outputEncoding == IcoEncodingType.Bitmap ? "bmp" : "png"; var outputFilename = $"{context.FullPath}.{context.ImageDirectoryIndex:D2}.{frame.Encoding.ActualWidth}x{frame.Encoding.ActualHeight}x{frame.Encoding.ActualBitDepth}.{extension}"; if (File.Exists(outputFilename) && opts.ForceOverwrite == false) { Reporter.WarnLine(IcoErrorCode.FileExists, $"Not overwriting existing file {outputFilename}. Use --force-overwrite to change behavior."); return; } Reporter.VerboseLine($"Writing frame to {outputFilename}..."); switch (outputEncoding) { case IcoEncodingType.Bitmap: var bitmap = BmpEncoder.EncodeBitmap(context, frame.Encoding.PixelFormat, BmpEncoder.Dialect.Bmp, frame); File.WriteAllBytes(outputFilename, bitmap); break; case IcoEncodingType.Png: if (frame.Encoding.Type == IcoEncodingType.Png) { File.WriteAllBytes(outputFilename, frame.RawData); } else { using (var stream = new MemoryStream()) { frame.CookedData.SaveAsPng(stream, context.PngEncoder); stream.Flush(); var png = stream.GetBuffer(); File.WriteAllBytes(outputFilename, stream.GetBuffer()); } } break; } }
private static void EmitAllPossibleEncodings(ParseContext context, IcoFrame source) { byte[] png; var pathPrefix = $"{context.FullPath}.{context.ImageDirectoryIndex:D2}."; using (var stream = new MemoryStream()) { source.CookedData.SaveAsPng(stream, context.PngEncoder); stream.Flush(); png = stream.GetBuffer(); var pngPath = $"{pathPrefix}png"; File.WriteAllBytes(pngPath, stream.GetBuffer()); } var bitmap1 = BmpEncoder.EncodeBitmap(context, BitmapEncoding.Pixel_indexed1, BmpEncoder.Dialect.Bmp, source); if (bitmap1 != null) { var bmp1Path = $"{pathPrefix}01bpp.bmp"; File.WriteAllBytes(bmp1Path, bitmap1); } var bitmap4 = BmpEncoder.EncodeBitmap(context, BitmapEncoding.Pixel_indexed4, BmpEncoder.Dialect.Bmp, source); if (bitmap4 != null) { var bmp4Path = $"{pathPrefix}04bpp.bmp"; File.WriteAllBytes(bmp4Path, bitmap4); } var bitmap8 = BmpEncoder.EncodeBitmap(context, BitmapEncoding.Pixel_indexed8, BmpEncoder.Dialect.Bmp, source); if (bitmap8 != null) { var bmp8Path = $"{pathPrefix}08bpp.bmp"; File.WriteAllBytes(bmp8Path, bitmap8); } var bitmap16 = BmpEncoder.EncodeBitmap(context, BitmapEncoding.Pixel_rgb15, BmpEncoder.Dialect.Bmp, source); if (bitmap16 != null) { var bmp16Path = $"{pathPrefix}16bpp.bmp"; File.WriteAllBytes(bmp16Path, bitmap16); } var bitmap32 = BmpEncoder.EncodeBitmap(context, BitmapEncoding.Pixel_0rgb32, BmpEncoder.Dialect.Bmp, source); if (bitmap32 != null) { var bmp32Path = $"{pathPrefix}32bpp0.bmp"; File.WriteAllBytes(bmp32Path, bitmap32); } var bitmap32a = BmpEncoder.EncodeBitmap(context, BitmapEncoding.Pixel_argb32, BmpEncoder.Dialect.Bmp, source); if (bitmap32a != null) { var bmp32Path = $"{pathPrefix}32Abpp.bmp"; File.WriteAllBytes(bmp32Path, bitmap32a); } }
private static IcoFrame GetBestEncodingForFrame(ParseContext context, CommandLineOptions opts, IcoFrame source) { var result = new IcoFrameEncoding { ClaimedBitDepth = source.Encoding.ClaimedBitDepth, ClaimedHeight = source.Encoding.ClaimedHeight, ClaimedWidth = source.Encoding.ClaimedWidth, ActualHeight = (uint)source.CookedData.Height, ActualWidth = (uint)source.CookedData.Width, }; byte[] png = null; byte[] bitmap = null; var policy = opts.BestFormatPolicy; if (policy == BestFormatPolicy.Inherited) { policy = BestFormatPolicy.PreserveSource; } if (source.Encoding.ClaimedHeight == 16 && source.Encoding.ClaimedWidth == 16 && source.Encoding.ClaimedBitDepth == 32 && opts.BestFormatPolicy16x16x32 != BestFormatPolicy.Inherited) { policy = opts.BestFormatPolicy16x16x32; } if (source.Encoding.ClaimedHeight == 32 && source.Encoding.ClaimedWidth == 32 && source.Encoding.ClaimedBitDepth == 32 && opts.BestFormatPolicy32x32x32 != BestFormatPolicy.Inherited) { policy = opts.BestFormatPolicy32x32x32; } if (policy != BestFormatPolicy.AlwaysBmp && (source.Encoding.ClaimedBitDepth == 32 || opts.BestFormatPolicy == BestFormatPolicy.AlwaysPng)) { png = EncodePng(source, context); if (opts.PngToolPath != null) { png = ReprocessPngFile(png, context, opts); } } if (opts.BestFormatPolicy != BestFormatPolicy.AlwaysPng) { foreach (var encoding in _allEncodings) { if (encoding == BitmapEncoding.Pixel_indexed2) { switch (opts.Emit2BitBitmaps) { case StrictnessPolicy.Compliant: continue; case StrictnessPolicy.PreserveSource: if (source.Encoding.PixelFormat != BitmapEncoding.Pixel_indexed2) { continue; } break; case StrictnessPolicy.Loose: break; } } if (encoding == BitmapEncoding.Pixel_rgb24) { switch (opts.Emit24BitBitmaps) { case StrictnessPolicy.Compliant: continue; case StrictnessPolicy.PreserveSource: if (source.Encoding.PixelFormat != BitmapEncoding.Pixel_rgb24) { continue; } break; case StrictnessPolicy.Loose: break; } } if (source.Encoding.PixelFormat != encoding) { if (!opts.AllowDownsample) { continue; } // Going to rgb15 can lose some color data, due to rounding. Avoid unless the source // was already in rgb15. if (encoding == BitmapEncoding.Pixel_rgb15) { continue; } // Don't lose the per-pixel alpha channel, if one was in the source. if (source.Encoding.PixelFormat == BitmapEncoding.Pixel_argb32 && BmpUtil.IsAlphaSignificant(source)) { continue; } } bitmap = BmpEncoder.EncodeBitmap(context, encoding, BmpEncoder.Dialect.Ico, source); if (bitmap != null) { result.PixelFormat = encoding; break; } } } switch (opts.BestFormatPolicy) { case BestFormatPolicy.PreserveSource: result.Type = source.Encoding.Type; break; case BestFormatPolicy.MinimizeStorage: result.Type = (bitmap.Length < ((png?.Length ?? 0) + opts.PngSizePenalty)) ? IcoEncodingType.Bitmap : IcoEncodingType.Png; break; case BestFormatPolicy.PngLargeImages: result.Type = (source.CookedData.Width >= opts.LargeImagePixelThreshold) ? IcoEncodingType.Png : IcoEncodingType.Bitmap; break; case BestFormatPolicy.AlwaysPng: result.Type = IcoEncodingType.Png; break; case BestFormatPolicy.AlwaysBmp: result.Type = IcoEncodingType.Bitmap; break; default: break; } if (png == null && result.Type == IcoEncodingType.Png) { result.Type = IcoEncodingType.Bitmap; } var finalData = (result.Type == IcoEncodingType.Bitmap) ? bitmap : png; return(new IcoFrame { Encoding = result, RawData = finalData }); }