Sheets carry a managed buffer of data that allows updates to be made without having to constantly fetch and set data to the texture memory of the video card. This is useful for things like SheetBuilder which make small progressive changes to sheets. However these buffers are often large and are kept alive because sheets are referenced by the sprites that use them. If this buffer is explicitly null'ed when it is no longer needed then the GC can reclaim it. Sometimes a buffer need not even be created because the object using the sheet only works on the texture directly anyway. In practise, this reduced memory consumed by such buffers from ~165 MiB to ~112 MiB (at the start of a new RA skirmish mission).
165 lines
3.8 KiB
C#
165 lines
3.8 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
|
* This file is part of OpenRA, which is free software. It is made
|
|
* available to you under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation. For more information,
|
|
* see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Drawing;
|
|
using OpenRA.FileFormats;
|
|
using OpenRA.FileSystem;
|
|
using OpenRA.Graphics;
|
|
|
|
namespace OpenRA.Widgets
|
|
{
|
|
public class VqaPlayerWidget : Widget
|
|
{
|
|
Sprite videoSprite, overlaySprite;
|
|
VqaReader video = null;
|
|
string cachedVideo;
|
|
float invLength;
|
|
float2 videoOrigin, videoSize;
|
|
uint[,] overlay;
|
|
bool stopped;
|
|
bool paused;
|
|
|
|
Action onComplete;
|
|
|
|
public bool Paused { get { return paused; } }
|
|
|
|
readonly World world;
|
|
[ObjectCreator.UseCtor]
|
|
public VqaPlayerWidget(World world)
|
|
{
|
|
this.world = world;
|
|
}
|
|
|
|
public bool DrawOverlay = true;
|
|
public void Load(string filename)
|
|
{
|
|
if (filename == cachedVideo)
|
|
return;
|
|
|
|
stopped = true;
|
|
paused = true;
|
|
Sound.StopVideo();
|
|
onComplete = () => { };
|
|
|
|
cachedVideo = filename;
|
|
video = new VqaReader(GlobalFileSystem.Open(filename));
|
|
|
|
invLength = video.Framerate * 1f / video.Frames;
|
|
|
|
var size = Math.Max(video.Width, video.Height);
|
|
var textureSize = Exts.NextPowerOf2(size);
|
|
var videoSheet = new Sheet(new Size(textureSize, textureSize), false);
|
|
videoSheet.Texture.SetData(video.FrameData);
|
|
videoSprite = new Sprite(videoSheet, new Rectangle(0, 0, video.Width, video.Height), TextureChannel.Alpha);
|
|
|
|
var scale = Math.Min(RenderBounds.Width / video.Width, RenderBounds.Height / video.Height);
|
|
videoOrigin = new float2(RenderBounds.X + (RenderBounds.Width - scale * video.Width) / 2, RenderBounds.Y + (RenderBounds.Height - scale * video.Height) / 2);
|
|
videoSize = new float2(video.Width * scale, video.Height * scale);
|
|
|
|
if (!DrawOverlay)
|
|
return;
|
|
|
|
overlay = new uint[2 * textureSize, 2 * textureSize];
|
|
var black = (uint)255 << 24;
|
|
for (var y = 0; y < video.Height; y++)
|
|
for (var x = 0; x < video.Width; x++)
|
|
overlay[2 * y, x] = black;
|
|
|
|
var overlaySheet = new Sheet(new Size(2 * textureSize, 2 * textureSize), false);
|
|
overlaySheet.Texture.SetData(overlay);
|
|
overlaySprite = new Sprite(overlaySheet, new Rectangle(0, 0, video.Width, 2 * video.Height), TextureChannel.Alpha);
|
|
}
|
|
|
|
public override void Draw()
|
|
{
|
|
if (video == null)
|
|
return;
|
|
|
|
if (!(stopped || paused))
|
|
{
|
|
var nextFrame = (int)float2.Lerp(0, video.Frames, Sound.VideoSeekPosition * invLength);
|
|
if (nextFrame > video.Frames)
|
|
{
|
|
Stop();
|
|
return;
|
|
}
|
|
|
|
while (nextFrame > video.CurrentFrame)
|
|
{
|
|
video.AdvanceFrame();
|
|
if (nextFrame == video.CurrentFrame)
|
|
videoSprite.sheet.Texture.SetData(video.FrameData);
|
|
}
|
|
}
|
|
|
|
Game.Renderer.RgbaSpriteRenderer.DrawSprite(videoSprite, videoOrigin, videoSize);
|
|
|
|
if (DrawOverlay)
|
|
Game.Renderer.RgbaSpriteRenderer.DrawSprite(overlaySprite, videoOrigin, videoSize);
|
|
}
|
|
|
|
public override bool HandleKeyPress(KeyInput e)
|
|
{
|
|
if (e.Event == KeyInputEvent.Down)
|
|
{
|
|
if (e.Key == Keycode.ESCAPE)
|
|
{
|
|
Stop();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void Play()
|
|
{
|
|
PlayThen(() => { });
|
|
}
|
|
|
|
public void PlayThen(Action after)
|
|
{
|
|
if (video == null)
|
|
return;
|
|
|
|
onComplete = after;
|
|
if (stopped)
|
|
Sound.PlayVideo(video.AudioData);
|
|
else
|
|
Sound.PlayVideo();
|
|
|
|
stopped = paused = false;
|
|
}
|
|
|
|
public void Pause()
|
|
{
|
|
if (stopped || paused || video == null)
|
|
return;
|
|
|
|
paused = true;
|
|
Sound.PauseVideo();
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if (stopped || video == null)
|
|
return;
|
|
|
|
stopped = true;
|
|
paused = true;
|
|
Sound.StopVideo();
|
|
video.Reset();
|
|
videoSprite.sheet.Texture.SetData(video.FrameData);
|
|
world.AddFrameEndTask(_ => onComplete());
|
|
}
|
|
}
|
|
}
|