鸟语天空
预乘Alpha
post by:追风剑情 2022-2-17 17:25

需要引用 System.Drawing.dll
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;


//预乘Alpha
public static void PremultiplyAlpha(string filePath)
{
	if (!File.Exists(filePath) || !filePath.EndsWith(".png"))
		return;

	FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
	byte[] bytes = new byte[fs.Length];
	fs.Read(bytes, 0, bytes.Length);
	fs.Close();

	Stream stream = null;
	try
	{
		stream = new MemoryStream(bytes);
		stream.Seek(0, SeekOrigin.Begin);
		Bitmap bmp = new Bitmap(stream);

		for (int y = 0; y < bmp.Height; y++)
		{
			for (int x = 0; x < bmp.Width; x++)
			{
				Color c = bmp.GetPixel(x, y);
				//公式
				float alpha = (float)c.A / 255f;
				int A = c.A;
				int R = (int)(c.R * alpha);
				int G = (int)(c.G * alpha);
				int B = (int)(c.B * alpha);

				c = Color.FromArgb(A, R, G, B);
				bmp.SetPixel(x, y, c);
			}
		}
		bmp.Save(filePath);
		bmp.Dispose();
		bmp = null;
	}
	catch(ArgumentException ex)
	{
		//stream不是图片或为空时引发此异常
		Console.WriteLine("\r\n{0};{1}", ex.Message, filePath);
	}
	finally
	{
		stream.Close();
		stream = null;
		bytes = null;
		GC.Collect();
	}
}

//快速-预乘Alpha,采用内存拷贝
public static void FastPremultiplyAlpha(string filePath)
{
	if (!File.Exists(filePath) || !filePath.EndsWith(".png"))
		return;

	FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
	byte[] bytes = new byte[fs.Length];
	fs.Read(bytes, 0, bytes.Length);
	fs.Close();

	Stream stream = null;
	try
	{
		stream = new MemoryStream(bytes);
		stream.Seek(0, SeekOrigin.Begin);
		Bitmap bmp = new Bitmap(stream);
		Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
		BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
		//图片第1个像素的内存地址
		IntPtr ptr = bmpData.Scan0;
		//两种计算方式
		int byteSize = bmp.Width * bmp.Height * 4;
		//int byteSize = Math.Abs(bmpData.Stride) * bmp.Height;
		byte[] rgbaValues = new byte[byteSize];
		//内存拷贝, 颜色分量排列顺序要根据 PixelFormat 判断
		Marshal.Copy(ptr, rgbaValues, 0, byteSize);
		for (int i=0; i<rgbaValues.Length; i+=4)
		{
			//公式 RGBA
			float a = (float)rgbaValues[i+3] / 255f;
			rgbaValues[i] = (byte)(rgbaValues[i] * a);
			rgbaValues[i+1] = (byte)(rgbaValues[i+1] * a);
			rgbaValues[i+2] = (byte)(rgbaValues[i+2] * a);
		}
		Marshal.Copy(rgbaValues, 0, ptr, byteSize);
		bmp.UnlockBits(bmpData);

		bmp.Save(filePath);
		bmp.Dispose();
		bmp = null;
		bmpData = null;
		rgbaValues = null;
	}
	catch (ArgumentException ex)
	{
		//stream不是图片或为空时引发此异常
		Console.WriteLine("\r\n{0};{1}", ex.Message, filePath);
	}
	finally
	{
		stream.Close();
		stream = null;
		bytes = null;
		GC.Collect();
	}
}

//超快速-预乘Alpha,采用指针直接修改内存数据
public static void FastestPremultiplyAlpha(string filePath)
{
	if (!File.Exists(filePath) || !filePath.EndsWith(".png"))
		return;

	FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
	byte[] bytes = new byte[fs.Length];
	fs.Read(bytes, 0, bytes.Length);
	fs.Close();

	Stream stream = null;
	try
	{
		stream = new MemoryStream(bytes);
		stream.Seek(0, SeekOrigin.Begin);
		Bitmap bmp = new Bitmap(stream);
		Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
		//以读写方式销定位图
		BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
		//需要勾选上 [属性]->[生成]->允许不安全代码
		unsafe
		{
			//图片第1个像素的内存地址
			byte* ptr = (byte*)bmpData.Scan0;
			for (int i=0; i<bmpData.Height; i++)
			{
				for (int j=0; j<bmpData.Width; j++)
				{
					float a = ptr[3] / 255f;
					ptr[2] = (byte)(ptr[2] * a);
					ptr[1] = (byte)(ptr[1] * a);
					ptr[0] = (byte)(ptr[0] * a);
					ptr += 4;
				}
				//跳过空白内存块
				ptr += bmpData.Stride - bmpData.Width * 4;
			}
		}
		//解锁位图
		bmp.UnlockBits(bmpData);

		bmp.Save(filePath);
		bmp.Dispose();
		bmp = null;
		bmpData = null;
	}
	catch (ArgumentException ex)
	{
		//stream不是图片或为空时引发此异常
		Console.WriteLine("\r\n{0};{1}", ex.Message, filePath);
	}
	finally
	{
		stream.Close();
		stream = null;
		bytes = null;
		GC.Collect();
	}
}


评论:
发表评论:
昵称

邮件地址 (选填)

个人主页 (选填)

内容