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. 关于兼容IE的一些策略

    --css 盒子模型下的 box-sizing 属性,只兼容到ie8: -moz-box-sizing: border-box; -webkit-box-sizing: border-box; -o- ...

  2. Django实现注册

    前言 对于web开来说,用户登陆.注册.文件上传等是最基础的功能,针对不同的web框架,相关的文章非常多,但搜索之后发现大多都不具有完整性,对于想学习web开发的新手来说不具有很强的操作性:对于web ...

  3. elasticsearch 之mapping

    搭好elk 后,有时候会发现kibana 统计数据时,数据类型不对,这个时候就和elasticsearch的mapping有关,虽然我们可以用logstash修改传入es里的数据类型,比如 float ...

  4. 2016/11/17 周四 <javascript的封装简单示例>

    这是一个简单的javascript代码封装的示例以及封装后的调用方法: var ticker={ n:0, add:function() { this.n++; }, show:function() ...

  5. jquery 购物车飞入效果

    github https://github.com/amibug/fly demo https://github.com/amibug/fly

  6. 8天掌握EF的Code First开发系列之动手写第一个Code First应用

    返回<8天掌握EF的Code First开发>总目录 本篇目录 创建控制台项目 根据.Net中的类来创建数据库 简单的CRUD操作 数据库模式更改介绍 本章小结 自我测试 上一篇<8 ...

  7. WPF+通过配置文件生成菜单(Menu)+源码

    这个月做项目,遇到过一个通过配置文件来生成菜单的解决方案,感觉挺优雅的,特地放到博客园来,以飨读者. 说来惭愧,以前做的项目都没有这样用过,都是固定死了.如果后续有需要加入菜单,还得在重新修改UI,然 ...

  8. JS、JQuery和ExtJs的跨域处理

    1.什么是跨域?跨域,JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象.简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a. ...

  9. 容易被忽略CSS特性

    CSS初学感觉很简单,但随着学习的深入才感觉CSS的水由多深,平常总会遇到各种坑,先总结一些经常遇到的坑 大小写不敏感 虽然我们平时在写CSS的时候都是用小写,但其实CSS并不是大小写敏感的 .tes ...

  10. 《Entity Framework 6 Recipes》翻译系列(2) -----第一章 开始使用实体框架之使用介绍

    Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件 ...