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