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 的保存的更多相关文章

  1. Linux下截屏方法

    参考百度经验 https://jingyan.baidu.com/article/48a42057c8e8dfa92525047c.html 第一种: 截屏部分画面并保存 快捷键Shift+PrtSc

  2. 团队软件开发_基于windows下截屏软件关于NABC框架的特点

    经过我们小组数次的激烈讨论,就自己的能力和时间而言,我们小组的初步的计划是开发一款基于windows下的截图软件. 关于这个软件的功能,我们初步的想法如下: 1.能在windows下后台运行,有相应的 ...

  3. Mac下截屏方法

    Refer to:https://zh.wikihow.com/在Mac-OS-X上截取屏幕截图 先来说几个需要用到的Mac键盘和普通键盘不一样的名字: Mac键盘 普通键盘 control Ctrl ...

  4. VC++ 截屏代码,并保存为想要的格式(BMP,JPG,PNG,GIF等格式)

    void CCaptionScreenDlg::Screen(char* filename) { HDC hdcSrc = ::GetDC(NULL); int nBitPerPixel = GetD ...

  5. 【转】使用adb命令对手机进行截屏(截图)保存到电脑,SDCard

    原文网址:http://blog.csdn.net/huangyabin001/article/details/29198367 adb shell /system/bin/screencap -p  ...

  6. 使用laravel框架与phantomjs实现截屏功能

    在网上看到的关于phantomjs实现截屏功能很多都是与node结合在一起使用,并需要输入命令才能执行.因此我想要实现输入网址即可截屏并输出图片的功能.示例:http://120.77.171.182 ...

  7. c# wpf 利用截屏键实现截屏功能

    原文:c# wpf 利用截屏键实现截屏功能     最近做一个wpf程序需要截图功能,查找资料费了一些曲折,跟大家分享一下.     先是找到了这样一份代码:     static class Scr ...

  8. Android8.1 MTK平台 截屏功能分析

    前言 涉及到的源码有 frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java vend ...

  9. 使用Python中PIL图形库进行截屏

    目的:通过使用Python的一个图形库PIL(Python Image Library)对屏幕进行截图 步骤: 1.下载PIL(路径)并安装 2.新建文件“截屏.py”,右键Edit with IDL ...

随机推荐

  1. bootstarp3 支持ie8

    http://blog.csdn.net/hyb3280660/article/details/51850832 想要bootstarp3 支持ie8? 引入 <!-- 存放全局css/js - ...

  2. spring mvc + ehcache 利用注解实现缓存功能

    我的spring是3.1的,因为项目需求,需要在查询时候加上缓存,小白一个,完全没有用过缓存(ehcache),摸索了一天终于会了一点通过注解来使用ehcache进行缓存,立刻给记录下来. 首先 我的 ...

  3. Centos7 Docker 多主机 容器互连--基于OVS

    来一张自己画的图,mark:2016年6月27日17:09:14 自己理解,如有错误 多谢指教. centos7, 部署OVS和docker.以及基于centos6.8的ssh images 命令. ...

  4. redis 操作

    redis 1 启动服务 redis-2.8.20 wangchengcheng$ pwd /Users/wangchengcheng/Documents/GameJelly/server/redis ...

  5. SQL Server复制出错文章集锦

    SQL Server复制出错文章集锦 为了方便大家对数据库复制过程中出错的时候更好地解决问题 本人收集了SQL Server相关复制出错解决的文章   The process could not ex ...

  6. 通过一组RESTful API暴露CQRS系统功能

    命令和查询责任分离(CQRS)是由Greg Young提出的一种将系统的读(查询).写(命令)操作分离为两种独立子系统的架构模式.命令通常是异步执行的,并存储在一个事务型数据库中,而读操作则通常是最终 ...

  7. [.net 面向对象程序设计进阶] (14) 缓存(Cache) (一) 认识缓存技术

    [.net 面向对象程序设计进阶] (14) 缓存(Cache)(一) 认识缓存技术 本节导读: 缓存(Cache)是一种用空间换时间的技术,在.NET程序设计中合理利用,可以极大的提高程序的运行效率 ...

  8. jQuery插件编写及链式编程模型小结

    JQuery极大的提高了我们编写JavaScript的效率,让我们可以愉快的编写代码,做出各种特效.大多数情况下,我们都是使用别人开发的JQuery插件,今天我们就来看看如何把我们常用的功能做出JQu ...

  9. IIS发布站点错误收集(持续更新)

    本文主要收集IIS在发布站点过程中遇到的错误,并提供解决办法.并亲测可行.如果您也在使用IIS发布站点的过程中遇到了一些问题,欢迎留言提问. (1.) HTTP错误500.21-Internal Se ...

  10. TODO:小程序的春天你想做什么

    TODO:小程序的春天你想做什么 微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验. 初步了解小程序的特点 导航明确,来去自如 统一稳定, 视觉规范 ...