You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

保存Bitmap为JPG时图像质量差,需输出与源图同32位深度的JPG

解决Bitmap保存为JPG时保留32位深度的问题

首先,咱们先拆解下你遇到的核心问题:

  1. 你当前创建的目标Bitmap是硬编码的Format64bppPArgb格式,和原图像的32位格式完全不匹配
  2. 默认的JPEG保存逻辑会使用系统默认的24位配置,不会自动继承原文件的位深度设置

另外要先澄清一个点:标准JPEG格式本身并不支持带Alpha通道的32位透明图像(PNG才是做这个的),如果你的原JPG是32位,大概率是RGBX格式(32位但Alpha通道无实际透明数据,只是占位)。下面是针对性的解决方案:

步骤1:让目标Bitmap匹配原图像的像素格式

首先,不要硬编码目标Bitmap的像素格式,而是从原图像中获取格式来创建:

修改Start方法,获取原图像的像素格式并传入GetDestinationImage

public void Start() {
    Bitmap destinationImage = null;
    var exportFolderPath = GetExportFolderPath();
    Directory.CreateDirectory(exportFolderPath);
    using (var sourceImage = Image.FromFile(imageProcessorParams.SourceImagePath)) {
        // 获取原图像的像素格式
        var sourcePixelFormat = sourceImage.PixelFormat;
        destinationImage = GetDestinationImage(sourcePixelFormat); // 传入格式
        using (var graphics = Graphics.FromImage(destinationImage)) {
            SetGraphicSettings(graphics);
            graphics.DrawImage(sourceImage, new Rectangle(0, 0, (int)(imageProcessorParams.Width.ToInch() * imageProcessorParams.Dpi), (int)(imageProcessorParams.Height.ToInch() * imageProcessorParams.Dpi)));
            
            // 替换默认保存逻辑,用自定义编码器参数
            var jpegCodec = GetJpegCodec();
            var encoderParams = GetJpegEncoderParameters(sourceImage);
            destinationImage.Save(Path.Combine(exportFolderPath, GetDestinationImageName()), jpegCodec, encoderParams);
        }
    }
    destinationImage.Dispose();
}

然后修改GetDestinationImage方法,使用传入的像素格式:

private Bitmap GetDestinationImage(PixelFormat pixelFormat) {
    int width = (int)(imageProcessorParams.Width.ToInch() * imageProcessorParams.Dpi);
    int height = (int)((imageProcessorParams.Height.ToInch() + imageProcessorParams.FooterSize.ToInch()) * imageProcessorParams.Dpi);
    // 使用原图像的像素格式创建目标Bitmap
    var destinationImage = new Bitmap(width, height, pixelFormat);
    destinationImage.SetResolution(imageProcessorParams.Dpi, imageProcessorParams.Dpi);
    return destinationImage;
}

步骤2:配置JPEG编码器以保留32位深度

默认的Image.Save会忽略自定义位深度,所以需要手动获取JPEG编码器并配置参数:

添加两个辅助方法:

// 获取JPEG编码器
private ImageCodecInfo GetJpegCodec()
{
    var codecs = ImageCodecInfo.GetImageEncoders();
    return codecs.FirstOrDefault(c => c.FormatID == ImageFormat.Jpeg.Guid);
}

// 获取匹配原图像的JPEG编码器参数
private EncoderParameters GetJpegEncoderParameters(Image sourceImage)
{
    var jpegCodec = GetJpegCodec();
    if (jpegCodec == null)
    {
        //  fallback到默认参数
        var defaultParams = new EncoderParameters(1);
        defaultParams.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
        return defaultParams;
    }

    // 尝试获取原图像的编码器参数,完全继承原文件配置
    var sourceParams = sourceImage.GetEncoderParameterList(jpegCodec.Clsid);
    if (sourceParams != null && sourceParams.Count > 0)
    {
        return sourceParams;
    }

    // 如果获取不到原参数,手动设置32位深度(针对RGBX格式)
    var customParams = new EncoderParameters(1);
    customParams.Param[0] = new EncoderParameter(Encoder.ColorDepth, 32L);
    // 同时可以添加质量参数,避免过度压缩
    // customParams.Param[1] = new EncoderParameter(Encoder.Quality, 95L);
    return customParams;
}

关键注意事项

  • 如果你的原JPG是带Alpha通道的32位图像(Format32bppArgb),保存为JPG时Alpha通道会被强制丢弃,因为JPEG标准不支持透明。如果需要保留透明,请改用PNG格式。
  • 部分旧版JPEG解码器可能不支持32位JPG,建议测试目标环境的兼容性。

内容的提问来源于stack exchange,提问作者Mohsen Magharehei

火山引擎 最新活动