首页 文章

调整PNG图像大小而不会丢失完全透明像素的颜色数据

提问于
浏览
2

我一直试图找到一种方法来调整PNG图像的大小而不会丢失完全透明像素的颜色数据 . 这是我用来实现此目的的代码:

//sourceImage is also a bitmap read in earlier
using (var scaledBitmap = new Bitmap(desiredWidth, desiredHeight, PixelFormat.Format32bppArgb)){
    using (var g = Graphics.FromImage(scaledBitmap)){

        var attr = new ImageAttributes();
        attr.SetWrapMode(WrapMode.TileFlipXY);
        g.CompositingQuality = CompositingQuality.HighQuality;
        g.CompositingMode    = CompositingMode.SourceCopy;
        g.InterpolationMode  = InterpolationMode.HighQualityBicubic;
        g.SmoothingMode      = SmoothingMode.AntiAlias;
        g.PixelOffsetMode    = PixelOffsetMode.HighQuality;

        g.DrawImage(sourceImage, new Rectangle(0, 0, desiredWidth, desiredHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, attr);
    }
    scaledBitmap.Save(memoryStream, ImageFormat.Png);
   //use new bitmap here
}

但是这个例子的问题是像素的alpha值为零的任何地方,像素都变成黑色 .

我的声誉太低,不能遗忘地内嵌这些图像 .

Images

First Image是我用来测试的图像 . 为了快速参考,我保存了一个没有alpha通道的副本(第二张图像) . 整个图像是纯色,alpha遮罩部分图像以创建圆形 .

第三张图像是上面代码的结果图像 . 我再次保存了两份副本 . 在第四张图像中,很明显在转换过程中数据丢失了 . 我期望看到的是填充整个图像的相同单一颜色 .

做一些阅读我在PNG文件规范中发现了这个:

alpha通道可以被视为暂时隐藏图像的透明部分的掩模,或者被视为构造非矩形图像的手段 . 在第一种情况下,应保留完全透明像素的颜色值以供将来使用 . 在第二种情况下,透明像素不携带有用的数据,只是填充PNG所需的矩形图像区域 . 在这种情况下,应为全透明像素分配相同的颜色值以获得最佳压缩效果 .

所以看起来由PNG编码器决定如何处理alpha为零的颜色值 . 有没有办法告诉GDI的编码器不要将颜色数据归零?

我需要保留颜色数据的原因是为了进一步动态调整图像大小 . 如果图像的可见部分的边缘没有均匀的颜色,则它们非常容易产生振铃伪影 . 关于这个问题,Unity forums上也有一篇文章 .

请让我知道是否有其他人遇到过此以及您是如何处理的 . 此外,如果有人知道任何实现C#PNG编码器的库不那么粗糙,我们将不胜感激 .

谢谢!

2 回答

  • 0

    虽然我意识到这对某些人来说可能还不够,但就我而言,答案是切换到使用ImageMagick's .NET wrapper . 我也能够在他们的论坛上找到一个thread,其中包含我遇到的确切问题 .

    在调整图像大小时,这似乎是一个常见的错误,ImageMagick已在其库中修复了此问题 .

    using ImageMagick;
    
    using (var sourceImage = new MagickImage(texture)){ //texture is of type Stream
        sourceImage.Resize(desiredWidth, desiredHeight);
        sourceImage.Write(memoryStream, MagickFormat.Png);
    }
    // use memoryStream here
    

    正如您所看到的,由于ImageMagick内置支持调整大小操作,这也极大地简化了我的代码 .

  • 2

    没有ImageMagick,您可以简单地使用以下代码并且它可以工作,不要更改CompositingQuality或任何其他Graphics属性:

    Bitmap NewImage = new Bitmap(OriginalImage.Width, OriginalImage.Height, OriginalImage.PixelFormat);
    using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(NewImage)) {
        g.DrawImage(OriginalImage, new Rectangle(0, 0, NewWidth, NewHeight), 0, 0, OriginalImage.Width, OriginalImage.Height, GraphicsUnit.Pixel);
    }
    NewImage.Save("myTransparent.png");
    

相关问题