MonoGame 3.2 下,截屏与 Texture2D 的保存
10月20日注:后来发现了这篇博文(英文),XNA 中的 Color 实际上是与 Alpha 值自左乘(premultiplied)的,这也解释了直接用 0xARGB 转译而颜色异常的原因。
注意,由于采用的是 MonoGame 3.2,因此方法与 XNA 4.0 不完全相同。
目标是将当前 GraphicsDevice 的内容保存为一个 Texture2D,同时还要能输出至文件。
截屏,在 XNA 下早就有人做了,例如这个:XNA4.0 保存屏幕截图方法[1]。
同时,针对 Texture2D.SaveAsPng()(XNA 下),也有人早就发现了其内存泄露问题,并写出了自己的解决方案:Texture2d.SaveAsPng() Memory Leak[2]。
不过 MonoGame 在 Windows 平台下是不支持 Texture2D.SaveAsPng() 和 Texture2D.SaveAsJpeg() 的,所以不管别人内存泄露啥的,这个保存方法还是要自己实现。(请参考 MonoGame 的代码;有相关外文帖子,不过地址我忘记了。)
其实呢,是我想偷懒,毕竟保存到 Texture2D 后还要写一段代码让其显示出来、消失、再进入原主循环,太麻烦;而且别人的代码还未必可行,先保存为文件看看结果如何。但是保存的时候就出问题了……
链接[2]中有保存的实现,不过那是针对 XNA 的,我在 MonoGame 上运行时出现了颜色错误。也就是说,到目前为止,至少还没有 MonoGame 开发者公开一个成功的保存方案。(当然,像 FEZ 那样的 Indie Game,写了也不会放在网上,对吧。)
其中最值得注意的是像素格式。GDI+ 是 0xARGB,但是 MonoGame 呢?(这里 MonoGame 可能没有遵循 XNA 的规范?)这个我不知道。之前几次用的都是 SurfaceFormat.Color,微软文档上说这是一个 RGB 颜色带 Alpha 通道,不过顺序未指明(从后面看来,应该是 ARGB)。不过 MonoGame 3.2 的 Windows(不是 WindowsGL)模板用的是 SharpDX,DirectX 这里定义的似乎是 A8R8G8B8。直接保存,颜色错误。对比同一个像素发现有这样的事情:0xff3dab0d(正确)→0xffd3abd0(直接保存),因此做这样的处理,还是失败。最后这个成功的结果应该说还是偶然吧。
* 2014-09-05 02:52 记录:在调试一个视频的时候,发现 SurfaceFormat.Color 对应 ffmpeg 的 PixelFormat 是 PIX_FMT_RGBA,就是说是实际上排列是 0xABGR(内存中)……抱歉我没从图像中看出来,对颜色不敏感哈……
这里就不展示错误的效果了,我附上了复现错误的注释,有兴趣的人可以自己试试。
下面就是代码。请预先添加两个引用:
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
创建一个 RenderTarget2D 对象(继承自 Texture2D)保存截屏内容:
public Texture2D TakeScreenshot()
{
int w, h;
w = GraphicsDevice.PresentationParameters.BackBufferWidth;
h = GraphicsDevice.PresentationParameters.BackBufferHeight;
RenderTarget2D screenshot;
// 注意格式一定要是 SurfaceFormat.Bgra32,与 GDI+ 统一。默认的 SurfaceFormat.Color 会导致颜色错误。
screenshot = new RenderTarget2D(GraphicsDevice, w, h, false, SurfaceFormat.Bgra32, DepthFormat.None);
GraphicsDevice.SetRenderTarget(screenshot);
Draw(_lastUpdatedGameTime != null ? _lastUpdatedGameTime : new GameTime());
GraphicsDevice.Present();
GraphicsDevice.SetRenderTarget(null);
return screenshot;
}
然后是对 Texture2D 的扩展(框架来自链接[2]):
public static void Save(this Texture2D texture, ImageFormat imageFormat, Stream stream)
{
var width = texture.Width;
var height = texture.Height;
using (Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
IntPtr safePtr;
BitmapData bitmapData;
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(, , width, height);
// 这里用 int[width * height] 而不用原文的 byte[4 * width * height],否则图像大小异常
int[] textureData = new int[width * height]; texture.GetData(textureData);
bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
safePtr = bitmapData.Scan0;
Marshal.Copy(textureData, , safePtr, textureData.Length);
bitmap.UnlockBits(bitmapData);
bitmap.Save(stream, imageFormat); textureData = null;
}
GC.Collect();
}
最后是调用:
void mainButton4_MouseClick(object sender, MouseEventArgs e)
{
var screenshot = RootControlContainer.TakeScreenshot();
if (screenshot != null)
{
// System.Drawing.Bitmap.Save() 方法的文件名必须是绝对路径
// 而在 MonoGame 下难以获取绝对路径,因此若要保存至相对路径,请使用 System.IO.FileStream
using (var fs = new System.IO.FileStream(@"screenshot.png", System.IO.FileMode.OpenOrCreate))
{
screenshot.Save(System.Drawing.Imaging.ImageFormat.Png, fs);
}
screenshot.Dispose();
}
}
MonoGame 3.2 下,截屏与 Texture2D 的保存的更多相关文章
- Linux下截屏方法
参考百度经验 https://jingyan.baidu.com/article/48a42057c8e8dfa92525047c.html 第一种: 截屏部分画面并保存 快捷键Shift+PrtSc
- 团队软件开发_基于windows下截屏软件关于NABC框架的特点
经过我们小组数次的激烈讨论,就自己的能力和时间而言,我们小组的初步的计划是开发一款基于windows下的截图软件. 关于这个软件的功能,我们初步的想法如下: 1.能在windows下后台运行,有相应的 ...
- Mac下截屏方法
Refer to:https://zh.wikihow.com/在Mac-OS-X上截取屏幕截图 先来说几个需要用到的Mac键盘和普通键盘不一样的名字: Mac键盘 普通键盘 control Ctrl ...
- VC++ 截屏代码,并保存为想要的格式(BMP,JPG,PNG,GIF等格式)
void CCaptionScreenDlg::Screen(char* filename) { HDC hdcSrc = ::GetDC(NULL); int nBitPerPixel = GetD ...
- 【转】使用adb命令对手机进行截屏(截图)保存到电脑,SDCard
原文网址:http://blog.csdn.net/huangyabin001/article/details/29198367 adb shell /system/bin/screencap -p ...
- 使用laravel框架与phantomjs实现截屏功能
在网上看到的关于phantomjs实现截屏功能很多都是与node结合在一起使用,并需要输入命令才能执行.因此我想要实现输入网址即可截屏并输出图片的功能.示例:http://120.77.171.182 ...
- c# wpf 利用截屏键实现截屏功能
原文:c# wpf 利用截屏键实现截屏功能 最近做一个wpf程序需要截图功能,查找资料费了一些曲折,跟大家分享一下. 先是找到了这样一份代码: static class Scr ...
- Android8.1 MTK平台 截屏功能分析
前言 涉及到的源码有 frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java vend ...
- 使用Python中PIL图形库进行截屏
目的:通过使用Python的一个图形库PIL(Python Image Library)对屏幕进行截图 步骤: 1.下载PIL(路径)并安装 2.新建文件“截屏.py”,右键Edit with IDL ...
随机推荐
- mysql不能插入中文
mysql不能插入中文 解决办法: 1.打开终端,连接数据库 mysql -u root -p; 2.输入 satus; 查看状态 3.输入 set char set 'gbk'; 4.如果是已有的 ...
- java的jxl技术导入Excel
项目结构: http://www.cnblogs.com/hongten/gallery/image/112177.html 在项目中我们看到Reference Libraries中的jxl.jar包 ...
- ffmpeg编译与移植问题
1.下载ffmpeg. 下载网址:http://www.ffmpeg.org/download.html 2.解压缩 tar -zxvf ffmpeg-2.0.1.tar.gz 3.配置,生成Make ...
- MBR结构和DBR结构
- 如何让PowerShell显示中文不乱码
故障描述: 这两天有个部门做了个Survey用来调查一些信息,由于对最后的结果报表有些特殊要求,不得不决定弃用SharePoint Survey自带的Export to spreadsheet功能,改 ...
- 什么情况下才要重写Objective-C中的description方法
特别注意: 千万不要在description方法中同时使用%@和self,同时使用了%@和self,代表要调用self的description方法,因此最终会导致程序陷入死循环,循环调用descrip ...
- Android零散
2016-03-13 Android零散 ListView中嵌套GridView 要实现分组列表这样的效果:点击ListView中的分组名称,即展开此分组显示其包含的项目.使用ExpandableLi ...
- Linux堆内存管理深入分析(下)
Linux堆内存管理深入分析 (下半部) 作者@走位,阿里聚安全 0 前言回顾 在上一篇文章中(链接见文章底部),详细介绍了堆内存管理中涉及到的基本概念以及相互关系,同时也着重介绍了堆中chunk分 ...
- Android提权漏洞CVE-2014-7920&CVE-2014-7921分析
没羽@阿里移动安全,更多安全类技术干货,请访问阿里聚安全博客 这是Android mediaserver的提权漏洞,利用CVE-2014-7920和CVE-2014-7921实现提权,从0权限提到me ...
- 跟vczh看实例学编译原理——一:Tinymoe的设计哲学
自从<序>胡扯了快一个月之后,终于迎来了正片.之所以系列文章叫<看实例学编译原理>,是因为整个系列会通过带大家一步一步实现Tinymoe的过程,来介绍编译原理的一些知识点. 但 ...