.NET框架升级后SkiaSharp与System.Drawing编码字节数组不一致问题咨询
问题
将项目从.NET Framework 4.7.2升级至.NET Standard 2.0,为适配Linux环境采用MSDN推荐的SkiaSharp处理图片。但在将BMP转为PNG/JPEG格式并获取字节数组时,SkiaSharp的输出结果与原System.Drawing.Image.Save的结果不一致,出于兼容性需求,需要让两者输出的字节数组完全一致,已尝试调整SKJpegEncoderOptions和SKPngEncoderOptions但未解决。
原System.Drawing实现代码
.... var encoder = GetEncoder(imageFormat); if (encoder == null) { return Array.Empty<byte>(); } var myEncoder = Encoder.Quality; var myEncoderParameters = new EncoderParameters(1); var myEncoderParameter = new EncoderParameter(myEncoder, 50L); myEncoderParameters.Param[0] = myEncoderParameter; byte[] byteArray; using (var stream = new MemoryStream()) { bmp.Save(stream, encoder, myEncoderParameters); byteArray = stream.ToArray(); } .... public ImageCodecInfo GetEncoder(ImageFormat format) { try { var imageDecoders = ImageCodecInfo.GetImageDecoders(); return imageDecoders.FirstOrDefault(codec => codec.FormatID == format.Guid); } catch (Exception) { return null; } }
原SkiaSharp实现代码
public byte[] BmpToByteArrayByImageFormat(SKBitmap bmp, SKEncodedImageFormat imageFormat) { try { var data = bmp.Encode(imageFormat, 50); return data.ToArray(); } catch (Exception) { return Array.Empty<byte>(); } }
解决方案
针对JPEG格式的对齐调整
System.Drawing的GDI+ JPEG编码器和SkiaSharp编码器在压缩细节、色彩采样上存在默认差异,需精准匹配参数:
- 指定YCbCr采样比例为4:4:4(GDI+对质量≥50的JPEG默认用该采样)
- 禁用渐进式模式(GDI+默认生成非渐进式JPEG)
- 对齐质量参数映射逻辑(GDI+与SkiaSharp的质量数值并非完全等价,需微调测试)
调整后的SkiaSharp JPEG处理代码:
public byte[] BmpToByteArrayByImageFormat(SKBitmap bmp, SKEncodedImageFormat imageFormat) { try { if (imageFormat == SKEncodedImageFormat.Jpeg) { var options = new SKJpegEncoderOptions { Quality = 50, Subsample = SKJpegEncoderSubsample.Ratio444, Progressive = false }; using var data = bmp.Encode(options); return data.ToArray(); } var data = bmp.Encode(imageFormat, 50); return data.ToArray(); } catch (Exception) { return Array.Empty<byte>(); } }
针对PNG格式的对齐调整
PNG的差异主要来自调色板处理、压缩级别和元数据:
- 索引色BMP:需确保SkiaSharp保留原调色板,避免自动转RGB
- 压缩级别:GDI+默认压缩级别对应SkiaSharp的6-7级
- 滤镜设置:GDI+默认无PNG滤镜
调整后的SkiaSharp PNG处理代码:
public byte[] BmpToByteArrayByImageFormat(SKBitmap bmp, SKEncodedImageFormat imageFormat) { try { if (imageFormat == SKEncodedImageFormat.Png) { var options = new SKPngEncoderOptions { CompressionLevel = SKPngEncoderCompressionLevel.Level6, FilterFlags = SKPngEncoderFilterFlags.None }; // 若原BMP为索引色,需提前将SKBitmap配置为对应索引颜色类型 using var data = bmp.Encode(options); return data.ToArray(); } var data = bmp.Encode(imageFormat, 50); return data.ToArray(); } catch (Exception) { return Array.Empty<byte>(); } }
额外注意事项
- 色彩空间:确保SkiaSharp的SKBitmap使用sRGB色彩配置(与GDI+默认一致)
- 透明通道:若原BMP含透明通道,需明确是否保留,两者处理逻辑有差异
- 测试验证:对24位、32位、索引色等不同类型BMP逐一测试,对比字节数组哈希值确认一致性
内容的提问来源于stack exchange,提问作者Willis Liou
相关产品推荐
相关产品推荐

