CSharpGL(19)用glReadPixels把渲染的内容保存为PNG图片(C#)
2016-04-24 17:35
791 查看
[b]CSharpGL(19)用glReadPixels把渲染的内容保存为PNG图片(C#) [/b]
struct Pixel
为了使用非托管数组,还需要用到 UnmanagedArray<T> 。关于这个类型详情见(C#+无unsafe的非托管大数组(large unmanaged array in c# without 'unsafe' keyword))。
先把读到的内容写入一个byte[],然后再用Marshal.Copy()复制到Bitmap。这个方法的思路是(非托管数组->托管数组->bmpData.Scan0)
显然这个转换步骤也是多余的,直接让ReadPixels写入bmpData.Scan0的位置不就好了嘛。
原CSharpGL的其他功能(UI、3ds解析器、TTF2Bmp、CSSL等),我将逐步加入新CSharpGL。
欢迎对OpenGL有兴趣的同学关注(https://github.com/bitzhuwei/CSharpGL)
效果图
本文解决了将OpenGL渲染出来的内容保存到PNG图片的方法。struct Pixel { public byte r; public byte g; public byte b; public byte a; public Pixel(byte r, byte g, byte b, byte a) { this.r = r; this.g = g; this.b = b; this.a = a; } public Color ToColor() { return Color.FromArgb(a, r, g, b); } public override string ToString() { return string.Format("{0}, {1}, {2}, {3}", r, g, b, a); } }
struct Pixel
为了使用非托管数组,还需要用到 UnmanagedArray<T> 。关于这个类型详情见(C#+无unsafe的非托管大数组(large unmanaged array in c# without 'unsafe' keyword))。
方法一:Bitmap.SetPixel()
最直接的方法是用Bitmap.SetPixel()来一个一个地指定图片的像素值。/// <summary> /// 把OpenGL渲染的内容保存到图片文件。 /// </summary> /// <param name="x">左下角坐标为(0, 0)</param> /// <param name="y">左下角坐标为(0, 0)</param> /// <param name="width">宽度</param> /// <param name="height">高度</param> /// <param name="filename"></param> public static void Save2Picture(int x, int y, int width, int height, string filename) { var pdata = new UnmanagedArray<Pixel>(width * height); GL.ReadPixels(x, y, width, height, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pdata.Header); var bitmap = new Bitmap(width, height); int index = 0; for (int j = height - 1; j >= 0; j--) { for (int i = 0; i < width; i++) { Pixel v = pdata[index++]; Color c = v.ToColor(); bitmap.SetPixel(i, j, c); } } bitmap.Save(filename); }
方法二:Marshal.Copy
方法一用到的SetPixel()速度是很慢的。先把读到的内容写入一个byte[],然后再用Marshal.Copy()复制到Bitmap。这个方法的思路是(非托管数组->托管数组->bmpData.Scan0)
/// <summary> /// 把OpenGL渲染的内容保存到图片文件。 /// </summary> /// <param name="x">左下角坐标为(0, 0)</param> /// <param name="y">左下角坐标为(0, 0)</param> /// <param name="width">宽度</param> /// <param name="height">高度</param> /// <param name="filename"></param> public static void Save2Picture(int x, int y, int width, int height, string filename) { var pdata = new UnmanagedArray<Pixel>(width * height); GL.ReadPixels(x, y, width, height, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pdata.Header); var format = System.Drawing.Imaging.PixelFormat.Format32bppArgb; var lockMode = System.Drawing.Imaging.ImageLockMode.WriteOnly; var bitmap = new Bitmap(width, height, format); var bitmapRect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); System.Drawing.Imaging.BitmapData bmpData = bitmap.LockBits(bitmapRect, lockMode, format); { int length = Math.Abs(bmpData.Stride) * bitmap.Height; byte[] bitmapBytes = new byte[length]; int index = 0; for (int j = height - 1; j >= 0; j--) { for (int i = 0; i < width; i++) { Pixel v = pdata[index++]; bitmapBytes[j * bmpData.Stride + i * 4 + 0] = v.b; bitmapBytes[j * bmpData.Stride + i * 4 + 1] = v.g; bitmapBytes[j * bmpData.Stride + i * 4 + 2] = v.r; bitmapBytes[j * bmpData.Stride + i * 4 + 3] = v.a; } } System.Runtime.InteropServices.Marshal.Copy(bitmapBytes, 0, bmpData.Scan0, length); } bitmap.UnlockBits(bmpData); bitmap.Save(filename); }
方法三:直接写入bmpData.Scan0
上一个方法里,通过托管数组byte[]进行过渡,是为了使用Marshal.Copy()这个method。但是我明明可以直接操作bmpData.Scan0啊,何必弄个byte[]。/// <summary> /// 把OpenGL渲染的内容保存到图片文件。 /// </summary> /// <param name="x">左下角坐标为(0, 0)</param> /// <param name="y">左下角坐标为(0, 0)</param> /// <param name="width">宽度</param> /// <param name="height">高度</param> /// <param name="filename"></param> public static void Save2Picture(int x, int y, int width, int height, string filename) { var pdata = new UnmanagedArray<Pixel>(width * height); GL.ReadPixels(x, y, width, height, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pdata.Header); var format = System.Drawing.Imaging.PixelFormat.Format32bppArgb; var lockMode = System.Drawing.Imaging.ImageLockMode.WriteOnly; var bitmap = new Bitmap(width, height, format); Rectangle bitmapRect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); System.Drawing.Imaging.BitmapData bmpData = bitmap.LockBits(bitmapRect, lockMode, format); unsafe { var array = (byte*)bmpData.Scan0.ToPointer(); int index = 0; for (int j = height - 1; j >= 0; j--) { for (int i = 0; i < width; i++) { Pixel v = pdata[index++]; array[j * bmpData.Stride + i * 4 + 0] = v.b; array[j * bmpData.Stride + i * 4 + 1] = v.g; array[j * bmpData.Stride + i * 4 + 2] = v.r; array[j * bmpData.Stride + i * 4 + 3] = v.a; } } } bitmap.UnlockBits(bmpData); bitmap.Save(filename); }
方法四:ReadPixels直接搞定
在上面的方法里,思路是(非托管数组->非托管数组)。显然这个转换步骤也是多余的,直接让ReadPixels写入bmpData.Scan0的位置不就好了嘛。
/// <summary> /// 把OpenGL渲染的内容保存到图片文件。 /// </summary> /// <param name="x">左下角坐标为(0, 0)</param> /// <param name="y">左下角坐标为(0, 0)</param> /// <param name="width">宽度</param> /// <param name="height">高度</param> /// <param name="filename"></param> public static void Save2Picture(int x, int y, int width, int height, string filename) { var format = System.Drawing.Imaging.PixelFormat.Format32bppArgb; var lockMode = System.Drawing.Imaging.ImageLockMode.WriteOnly; var bitmap = new Bitmap(width, height, format); var bitmapRect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); System.Drawing.Imaging.BitmapData bmpData = bitmap.LockBits(bitmapRect, lockMode, format); GL.ReadPixels(x, y, width, height, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, bmpData.Scan0); bitmap.UnlockBits(bmpData); bitmap.RotateFlip(RotateFlipType.Rotate180FlipX); bitmap.Save(filename); }
总结
从OpenGL窗口读取出图片,是非常有用的。原CSharpGL的其他功能(UI、3ds解析器、TTF2Bmp、CSSL等),我将逐步加入新CSharpGL。
欢迎对OpenGL有兴趣的同学关注(https://github.com/bitzhuwei/CSharpGL)
相关文章推荐
- 简单对比C#程序中的单线程与多线程设计
- 【FTP】C# System.Net.FtpClient库连接ftp服务器(下载文件)
- C#详解struct和class的区别
- 关于C# 委托(delegate)与事件(event)的用法及事例
- [转]LibreOffice-SDK 开发实战:嵌入MFC-View 和 C# Winform
- C#OOP之三 控制结构
- C#OOP之三 控制结构
- C#OOP之三 控制结构
- c# 中button加图片
- C#基础-replace()过滤非法字符
- C# 通过循环清空窗体中所有 TextBox 的值
- c# winform笔记
- c#中using的用法详解
- C# textBox限定输入数字
- C#第7周实验(2)
- C# ZXing.Net生成二维码、识别二维码、生成带Logo的二维码(二)
- C# ZXing.Net生成二维码、识别二维码、生成带Logo的二维码(一)
- c#创建静态类,在其中定义一个泛型方法,实现查找数组元素的功能
- C# 静态类(static class)
- c# abstract抽象类与继承类子类的构造函数_base