前言
最近碰到一个需要将图片由原始的PNG转化为JPG的需求,由于PNG图片本身质量等原因,导致转化为JPG之后,存在失真的问题,后来一个同事分享了下述的HighQualityPNGToJPG方法解决PNG转JPG失真的问题。
1、准备
using System.Drawing; using System.Net; using System.Drawing.Imaging; /// <summary> /// 常见图片格式枚举 /// </summary> public enum ImageFormatenmu { jpg, gif, bmp, png }
2、给透明PNG图片添加白色底图
/// <summary> /// 透明PNG图片添加白色底图 /// </summary> /// <param name="pngFilePath">Png文件路径</param> /// <param name="savePngFilePath">新Png文件保存路径</param> /// <returns></returns> public static void AddBlankBaseMap(string pngFilePath, string savePngFilePath) { Bitmap bitmap = new Bitmap(pngFilePath); int w = bitmap.Width; int h = bitmap.Height; Bitmap dstBitmap = new Bitmap(bitmap.Width, bitmap.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); System.Drawing.Imaging.BitmapData srcData = bitmap.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); System.Drawing.Imaging.BitmapData dstData = dstBitmap.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); //不安全代码,编译时需将项目生成操作中添加【允许不安全代码】 unsafe { byte* pIn = (byte*)srcData.Scan0.ToPointer(); byte* pOut = (byte*)dstData.Scan0.ToPointer(); byte* p; int stride = srcData.Stride; int r, g, b, a; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { p = pIn; b = pIn[0]; g = pIn[1]; r = pIn[2]; a = pIn[3]; if (a == 0) { pOut[1] = (byte)255; pOut[2] = (byte)255; pOut[3] = (byte)255; pOut[0] = (byte)255; } else { pOut[1] = (byte)g; pOut[2] = (byte)r; pOut[3] = (byte)a; pOut[0] = (byte)b; } pIn += 4; pOut += 4; } pIn += srcData.Stride - w * 4; pOut += srcData.Stride - w * 4; } bitmap.UnlockBits(srcData); dstBitmap.UnlockBits(dstData); //也可以再这里讲dstBitmap转化为其他类型,如转gif:bitmap.Save(saveFilePath, ImageFormat.Gif) dstBitmap.Save(savePngFilePath); } }
3、根据图片url保存图片
/// <summary> /// 根据图片url保存图片 /// </summary> /// <param name="url">url地址</param> /// <param name="savePath">保存本地路径【含文件名,注意扩展名要和url对应文件本身一样,否则可能出错】</param> public static void GetImageByURL(String url, String savePath) { WebClient webClient = new WebClient(); webClient.DownloadFile(url, savePath); }
4、普通PNG格式转换
/// <summary> /// 普通PNG格式转换 /// </summary> /// <param name="pngFilePath">png文件路径</param> /// <param name="saveFilePath">保存文件路径</param> /// <param name="imageFromat">转化格式</param> public static void GeneralPngChange(String pngFilePath,String saveFilePath,ImageFormatenmu imageFromat) { Bitmap bitmap = new Bitmap(pngFilePath); switch (imageFromat) { case ImageFormatenmu.bmp: { bitmap.Save(saveFilePath, ImageFormat.Bmp); }; break; case ImageFormatenmu.gif: { bitmap.Save(saveFilePath, ImageFormat.Gif); }; break; case ImageFormatenmu.jpg: { bitmap.Save(saveFilePath, ImageFormat.Jpeg); }; break; } }
5、PNG转JPG一般操作方法,可设置图片质量
/// <summary> /// PNG转JPG一般操作方法,可设置图片质量 /// </summary> /// <param name="pngFilePath">png文件路径</param> /// <param name="saveFilePath">jpg文件路径</param> /// <param name="percentage">图片质量>0</param> public static void GeneralPngToJpg(String pngFilePath, String saveFilePath, long percentage) { Bitmap bmp1 = new Bitmap(pngFilePath); ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg); System.Drawing.Imaging.Encoder myEncoder =System.Drawing.Imaging.Encoder.Quality; EncoderParameters myEncoderParameters = new EncoderParameters(1); EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, percentage); myEncoderParameters.Param[0] = myEncoderParameter; bmp1.Save(saveFilePath, jgpEncoder, myEncoderParameters); } /// <summary> /// 获取图片编码信息 /// </summary> /// <param name="mimeType">图片格式MIME类型</param> /// <returns></returns> private static ImageCodecInfo GetEncoder(ImageFormat format) { ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders(); foreach (ImageCodecInfo codec in codecs) { if (codec.FormatID == format.Guid) { return codec; } } return null; }
6、高保真PNG转JPG,解决部分PNG转JPG过程中图像失真的问题
/// <summary> /// 高保真PNG转JPG,解决部分PNG转JPG过程中图像失真的问题 /// 例如:本人将一张透明的PNG转JPG后,JEP图片失真 /// </summary> /// <param name="pngFilePath">png文件路径</param> /// <param name="jpgFilePath">jpg文件路径</param> public static void HighQualityPNGToJPG(String pngFilePath, String jpgFilePath) { Image pngImg = Image.FromFile(pngFilePath); var width = pngImg.Width; var height = pngImg.Height; Image bitmap = new System.Drawing.Bitmap(width, height); Graphics g = System.Drawing.Graphics.FromImage(bitmap); //设置高质量插值法 g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; g.Clear(Color.White); //在指定位置并且按指定大小绘制原图片的指定部分 g.DrawImage(pngImg, new Rectangle(0, 0, width, height), new Rectangle(0, 0, width, height), GraphicsUnit.Pixel); bitmap.Save(jpgFilePath, System.Drawing.Imaging.ImageFormat.Jpeg); bitmap.Dispose(); g.Dispose(); } }
我遇到将一个透明的PNG或一个质量较低的PNG用上述方法转JPG出现失真的问题。
例如:
使用GeneralPngToJpg方法将png图转jpg,透明底图PNG转换情况如下:
图表 1 透明底图PNG
图表 2透明底图PNG转化后的JPG图片【较严重失真】
非透明底图PNG转换情况如下:
图表 3非透明底图PNG
图表 4 非透明底图PNG转JPG【较严重失真】
HighQualityPNGToJPG分别转化透明及非透明低图的PNG,结果如下:
图表 5 非透明底图的PNG格式转化后的JPG
图表 6 透明底图的PNG格式转化后的JPG
7、测试代码
String imageDirectory = string.Format("{0}image/",AppDomain.CurrentDomain.BaseDirectory); //透明PNG图片添加白色底图 String transparentPng = Path.Combine(imageDirectory, "transparent.png"); String noTransparentPng = Path.Combine(imageDirectory, "noTransparent.png"); PngHelp.AddBlankBaseMap(transparentPng, noTransparentPng); //根据图片url保存图片 String urlPng=Path.Combine(imageDirectory, "url.png"); PngHelp.GetImageByURL("http://image.bitauto.com/dealer/brandgroup/m/389.png", urlPng); //PNG转JPG一般操作方法【存在失真的情况,原因决定于原始PNG图片的质量】 String changeJpg=Path.Combine(imageDirectory, "changeJpg.jpg"); PngHelp.GeneralPngChange(noTransparentPng, changeJpg,ImageFormatenmu.jpg); String changeBmp = Path.Combine(imageDirectory, "changeBmp.bmp"); PngHelp.GeneralPngChange(noTransparentPng, changeBmp, ImageFormatenmu.bmp); String changeGif = Path.Combine(imageDirectory, "changeGif.gif"); PngHelp.GeneralPngChange(noTransparentPng, changeBmp, ImageFormatenmu.gif);
//【透明图失真更多】 String changeNewJpg = Path.Combine(imageDirectory, "changeNewJpg.jpg"); PngHelp.GeneralPngChange(transparentPng, changeNewJpg, ImageFormatenmu.jpg); //PNG转JPG一般操作方法,可设置图片质量 String changeJpg0=Path.Combine(imageDirectory, "changeJpg0.jpg"); PngHelp.GeneralPngToJpg(noTransparentPng, changeJpg0,0L); String changeJpg50 = Path.Combine(imageDirectory, "changeJpg50.jpg"); PngHelp.GeneralPngToJpg(noTransparentPng, changeJpg50, 50L); String changeJpg100 = Path.Combine(imageDirectory, "changeJpg100.jpg"); PngHelp.GeneralPngToJpg(noTransparentPng, changeJpg100, 50L); //高保真PNG转JPG,解决部分PNG转JPG过程中图像失真的问题 String highqualityJpgFromTrasparent = Path.Combine(imageDirectory, "highqualityJpgFromTrasparent.jpg"); String highqualityJpgFromNoTrasparent = Path.Combine(imageDirectory, "highqualityJpgFromNoTrasparent.jpg"); PngHelp.HighQualityPNGToJPG(transparentPng, highqualityJpgFromTrasparent); PngHelp.HighQualityPNGToJPG(noTransparentPng, highqualityJpgFromNoTrasparent);