Resize PNG image without losing color data from fully transparent pixels

做~自己de王妃 提交于 2019-12-11 01:02:23

问题


I've been trying to figure out a way to resize a PNG image without losing color data from pixels that are completely transparent. Here is the code that I'm using to achieve this:

//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
}

But the problem with this example is that anywhere that a pixel's alpha value is zero, the pixels become black.

My reputation is too low to post these images inline sadly.

Images

First Image is the image I've been using to test with. For quick reference, I've saved a copy without the alpha channel (Second Image). The entire image is a solid color and the alpha masks part of the image to create a circle.

Third Image is the resulting image from the code above. Again I've saved two copies. In Fourth Image, it is very clear that data is being lost during the conversion. What I'd expect to see is the same single color filling the whole image.

Doing some reading I discovered this in the PNG file specification:

The alpha channel can be regarded either as a mask that temporarily hides transparent parts of the image, or as a means for constructing a non-rectangular image. In the first case, the colour values of fully transparent pixels should be preserved for future use. In the second case, the transparent pixels carry no useful data and are simply there to fill out the rectangular image area required by PNG. In this case, fully transparent pixels should all be assigned the same colour value for best compression.

So it looks like it's up to the PNG encoder to decide how to handle color values where the alpha is zero. Is there any way to tell GDI+'s encoder to not zero out the color data?

The reason I need to keep the color data is for further, dynamic resizing of the image. Without a homogeneous color at the edges of the visible portion of the image, they are terribly prone to ringing artifacts. There's also a post on the Unity forums about this issue.

Please let my know if anyone else has encountered this and how you dealt with it. Also if anyone knows of any libraries that implement a C# PNG encoder that is less draconian it would be greatly appreciated.

Thanks!


回答1:


While I realize this may not be sufficient for some, in my case the answer was to switch to using ImageMagick's .NET wrapper. I was also able to find a thread on their forums with the exact problem I was experiencing.

It appears that this is a common bug when resizing images and ImageMagick has fixed this issue in their library.

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

As you can see, this also greatly simplified my code due to ImageMagick's built in support for resize operations.




回答2:


Without ImageMagick, you can simply use the following code and it works, don't change CompositingQuality or any other Graphics properties :

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


来源:https://stackoverflow.com/questions/28467645/resize-png-image-without-losing-color-data-from-fully-transparent-pixels

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!