Rewrite screenshot saving.
This commit is contained in:
@@ -530,31 +530,19 @@ namespace OpenRA
|
|||||||
|
|
||||||
static void TakeScreenshotInner()
|
static void TakeScreenshotInner()
|
||||||
{
|
{
|
||||||
Log.Write("debug", "Taking screenshot");
|
using (new PerfTimer("Renderer.SaveScreenshot"))
|
||||||
|
|
||||||
Bitmap bitmap;
|
|
||||||
using (new PerfTimer("Renderer.TakeScreenshot"))
|
|
||||||
bitmap = Renderer.Context.TakeScreenshot();
|
|
||||||
|
|
||||||
ThreadPool.QueueUserWorkItem(_ =>
|
|
||||||
{
|
{
|
||||||
var mod = ModData.Manifest.Metadata;
|
var mod = ModData.Manifest.Metadata;
|
||||||
var directory = Platform.ResolvePath(Platform.SupportDirPrefix, "Screenshots", ModData.Manifest.Id, mod.Version);
|
var directory = Platform.ResolvePath(Platform.SupportDirPrefix, "Screenshots", ModData.Manifest.Id, mod.Version);
|
||||||
Directory.CreateDirectory(directory);
|
Directory.CreateDirectory(directory);
|
||||||
|
|
||||||
var filename = TimestampedFilename(true);
|
var filename = TimestampedFilename(true);
|
||||||
var format = Settings.Graphics.ScreenshotFormat;
|
var path = Path.Combine(directory, string.Concat(filename, ".png"));
|
||||||
var extension = ImageCodecInfo.GetImageEncoders().FirstOrDefault(x => x.FormatID == format.Guid)
|
Log.Write("debug", "Taking screenshot " + path);
|
||||||
.FilenameExtension.Split(';').First().ToLowerInvariant().Substring(1);
|
|
||||||
var destination = Path.Combine(directory, string.Concat(filename, extension));
|
|
||||||
|
|
||||||
using (new PerfTimer("Save Screenshot ({0})".F(format)))
|
Renderer.Context.SaveScreenshot(path);
|
||||||
bitmap.Save(destination, format);
|
Debug("Saved screenshot " + filename);
|
||||||
|
}
|
||||||
bitmap.Dispose();
|
|
||||||
|
|
||||||
RunAfterTick(() => Debug("Saved screenshot " + filename));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void InnerLogicTick(OrderManager orderManager)
|
static void InnerLogicTick(OrderManager orderManager)
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace OpenRA
|
|||||||
IShader CreateShader(string name);
|
IShader CreateShader(string name);
|
||||||
void EnableScissor(int left, int top, int width, int height);
|
void EnableScissor(int left, int top, int width, int height);
|
||||||
void DisableScissor();
|
void DisableScissor();
|
||||||
Bitmap TakeScreenshot();
|
void SaveScreenshot(string path);
|
||||||
void Present();
|
void Present();
|
||||||
void DrawPrimitives(PrimitiveType pt, int firstVertex, int numVertices);
|
void DrawPrimitives(PrimitiveType pt, int firstVertex, int numVertices);
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing.Imaging;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
@@ -166,8 +165,6 @@ namespace OpenRA
|
|||||||
|
|
||||||
public string Language = "english";
|
public string Language = "english";
|
||||||
public string DefaultLanguage = "english";
|
public string DefaultLanguage = "english";
|
||||||
|
|
||||||
public ImageFormat ScreenshotFormat = ImageFormat.Png;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SoundSettings
|
public class SoundSettings
|
||||||
|
|||||||
@@ -11,7 +11,11 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using OpenRA.FileFormats;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
|
using OpenRA.Support;
|
||||||
using SDL2;
|
using SDL2;
|
||||||
|
|
||||||
namespace OpenRA.Platforms.Default
|
namespace OpenRA.Platforms.Default
|
||||||
@@ -111,39 +115,49 @@ namespace OpenRA.Platforms.Default
|
|||||||
OpenGL.CheckGLError();
|
OpenGL.CheckGLError();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap TakeScreenshot()
|
public void SaveScreenshot(string path)
|
||||||
{
|
{
|
||||||
var rect = new Rectangle(Point.Empty, window.SurfaceSize);
|
var s = window.SurfaceSize;
|
||||||
var bitmap = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
var raw = new byte[s.Width * s.Height * 4];
|
||||||
var data = bitmap.LockBits(rect,
|
|
||||||
System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
|
||||||
|
|
||||||
OpenGL.glPushClientAttrib(OpenGL.GL_CLIENT_PIXEL_STORE_BIT);
|
OpenGL.glPushClientAttrib(OpenGL.GL_CLIENT_PIXEL_STORE_BIT);
|
||||||
|
|
||||||
OpenGL.glPixelStoref(OpenGL.GL_PACK_ROW_LENGTH, data.Stride / 4f);
|
OpenGL.glPixelStoref(OpenGL.GL_PACK_ROW_LENGTH, s.Width);
|
||||||
OpenGL.glPixelStoref(OpenGL.GL_PACK_ALIGNMENT, 1);
|
OpenGL.glPixelStoref(OpenGL.GL_PACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
OpenGL.glReadPixels(rect.X, rect.Y, rect.Width, rect.Height, OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, data.Scan0);
|
|
||||||
OpenGL.glFinish();
|
|
||||||
|
|
||||||
OpenGL.glPopClientAttrib();
|
|
||||||
|
|
||||||
// Reset alpha channel to fully opaque
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
var colors = (int*)data.Scan0;
|
fixed (byte* pRaw = raw)
|
||||||
var stride = data.Stride / 4;
|
OpenGL.glReadPixels(0, 0, s.Width, s.Height,
|
||||||
for (var y = 0; y < rect.Height; y++)
|
OpenGL.GL_BGRA, OpenGL.GL_UNSIGNED_BYTE, (IntPtr)pRaw);
|
||||||
for (var x = 0; x < rect.Width; x++)
|
|
||||||
colors[y * stride + x] |= 0xFF << 24;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap.UnlockBits(data);
|
OpenGL.glFinish();
|
||||||
|
OpenGL.glPopClientAttrib();
|
||||||
|
|
||||||
// OpenGL standard defines the origin in the bottom left corner which is why this is upside-down by default.
|
ThreadPool.QueueUserWorkItem(_ =>
|
||||||
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
|
{
|
||||||
|
// Convert GL pixel data into format expected by png
|
||||||
|
// - Flip vertically
|
||||||
|
// - BGRA to RGBA
|
||||||
|
// - Force A to 255 (no transparent pixels!)
|
||||||
|
var data = new byte[raw.Length];
|
||||||
|
for (var y = 0; y < s.Height; y++)
|
||||||
|
{
|
||||||
|
for (var x = 0; x < s.Width; x++)
|
||||||
|
{
|
||||||
|
var iData = 4 * (y * s.Width + x);
|
||||||
|
var iRaw = 4 * ((s.Height - y - 1) * s.Width + x);
|
||||||
|
data[iData] = raw[iRaw + 2];
|
||||||
|
data[iData + 1] = raw[iRaw + 1];
|
||||||
|
data[iData + 2] = raw[iRaw + 0];
|
||||||
|
data[iData + 3] = byte.MaxValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return bitmap;
|
var screenshot = new Png(data, window.SurfaceSize.Width, window.SurfaceSize.Height);
|
||||||
|
screenshot.Save(path);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Present()
|
public void Present()
|
||||||
|
|||||||
@@ -43,13 +43,13 @@ namespace OpenRA.Platforms.Default
|
|||||||
Action doPresent;
|
Action doPresent;
|
||||||
Func<string> getGLVersion;
|
Func<string> getGLVersion;
|
||||||
Func<ITexture> getCreateTexture;
|
Func<ITexture> getCreateTexture;
|
||||||
Func<Bitmap> getTakeScreenshot;
|
|
||||||
Func<object, IFrameBuffer> getCreateFrameBuffer;
|
Func<object, IFrameBuffer> getCreateFrameBuffer;
|
||||||
Func<object, IShader> getCreateShader;
|
Func<object, IShader> getCreateShader;
|
||||||
Func<object, IVertexBuffer<Vertex>> getCreateVertexBuffer;
|
Func<object, IVertexBuffer<Vertex>> getCreateVertexBuffer;
|
||||||
Action<object> doDrawPrimitives;
|
Action<object> doDrawPrimitives;
|
||||||
Action<object> doEnableScissor;
|
Action<object> doEnableScissor;
|
||||||
Action<object> doSetBlendMode;
|
Action<object> doSetBlendMode;
|
||||||
|
Action<object> doSaveScreenshot;
|
||||||
|
|
||||||
public ThreadedGraphicsContext(Sdl2GraphicsContext context, int batchSize)
|
public ThreadedGraphicsContext(Sdl2GraphicsContext context, int batchSize)
|
||||||
{
|
{
|
||||||
@@ -86,7 +86,6 @@ namespace OpenRA.Platforms.Default
|
|||||||
doPresent = () => context.Present();
|
doPresent = () => context.Present();
|
||||||
getGLVersion = () => context.GLVersion;
|
getGLVersion = () => context.GLVersion;
|
||||||
getCreateTexture = () => new ThreadedTexture(this, (ITextureInternal)context.CreateTexture());
|
getCreateTexture = () => new ThreadedTexture(this, (ITextureInternal)context.CreateTexture());
|
||||||
getTakeScreenshot = () => context.TakeScreenshot();
|
|
||||||
getCreateFrameBuffer = s => new ThreadedFrameBuffer(this, context.CreateFrameBuffer((Size)s, (ITextureInternal)CreateTexture()));
|
getCreateFrameBuffer = s => new ThreadedFrameBuffer(this, context.CreateFrameBuffer((Size)s, (ITextureInternal)CreateTexture()));
|
||||||
getCreateShader = name => new ThreadedShader(this, context.CreateShader((string)name));
|
getCreateShader = name => new ThreadedShader(this, context.CreateShader((string)name));
|
||||||
getCreateVertexBuffer = length => new ThreadedVertexBuffer(this, context.CreateVertexBuffer((int)length));
|
getCreateVertexBuffer = length => new ThreadedVertexBuffer(this, context.CreateVertexBuffer((int)length));
|
||||||
@@ -103,6 +102,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
context.EnableScissor(t.Item1, t.Item2, t.Item3, t.Item4);
|
context.EnableScissor(t.Item1, t.Item2, t.Item3, t.Item4);
|
||||||
};
|
};
|
||||||
doSetBlendMode = mode => { context.SetBlendMode((BlendMode)mode); };
|
doSetBlendMode = mode => { context.SetBlendMode((BlendMode)mode); };
|
||||||
|
doSaveScreenshot = path => context.SaveScreenshot((string)path);
|
||||||
|
|
||||||
Monitor.Pulse(syncObject);
|
Monitor.Pulse(syncObject);
|
||||||
}
|
}
|
||||||
@@ -437,9 +437,9 @@ namespace OpenRA.Platforms.Default
|
|||||||
Post(doSetBlendMode, mode);
|
Post(doSetBlendMode, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap TakeScreenshot()
|
public void SaveScreenshot(string path)
|
||||||
{
|
{
|
||||||
return Send(getTakeScreenshot);
|
Post(doSaveScreenshot, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user