From c4ab7041b8685c8f1709184b1b0355ad75d5daa3 Mon Sep 17 00:00:00 2001 From: penev92 Date: Wed, 22 Sep 2021 01:58:33 +0300 Subject: [PATCH] Updated VideoPlayerWidget to play new IVideo data Added optional padding to video frames because that's what VideoPlayerWidget expects. Keeping the option to not use padding for other use-cases like converting frames to PNG. --- OpenRA.Game/Graphics/VideoLoader.cs | 6 +++--- OpenRA.Mods.Cnc/FileFormats/VqaReader.cs | 19 +++++++++++++++---- OpenRA.Mods.Cnc/FileFormats/WsaReader.cs | 18 ++++++++++++++++-- OpenRA.Mods.Cnc/VideoLoaders/VqaLoader.cs | 4 ++-- OpenRA.Mods.Cnc/VideoLoaders/WsaLoader.cs | 4 ++-- OpenRA.Mods.Common/Scripting/Media.cs | 2 +- .../Widgets/Logic/AssetBrowserLogic.cs | 2 +- .../Widgets/VideoPlayerWidget.cs | 13 +++++++------ 8 files changed, 47 insertions(+), 21 deletions(-) diff --git a/OpenRA.Game/Graphics/VideoLoader.cs b/OpenRA.Game/Graphics/VideoLoader.cs index 10ee8242ac..e2969ba8bf 100644 --- a/OpenRA.Game/Graphics/VideoLoader.cs +++ b/OpenRA.Game/Graphics/VideoLoader.cs @@ -15,15 +15,15 @@ namespace OpenRA.Video { public interface IVideoLoader { - bool TryParseVideo(Stream s, out IVideo video); + bool TryParseVideo(Stream s, bool useFramePadding, out IVideo video); } public static class VideoLoader { - public static IVideo GetVideo(Stream stream, IVideoLoader[] loaders) + public static IVideo GetVideo(Stream stream, bool useFramePadding, IVideoLoader[] loaders) { foreach (var loader in loaders) - if (loader.TryParseVideo(stream, out var video)) + if (loader.TryParseVideo(stream, useFramePadding, out var video)) return video; return null; diff --git a/OpenRA.Mods.Cnc/FileFormats/VqaReader.cs b/OpenRA.Mods.Cnc/FileFormats/VqaReader.cs index 1b5ff90593..48438315cf 100644 --- a/OpenRA.Mods.Cnc/FileFormats/VqaReader.cs +++ b/OpenRA.Mods.Cnc/FileFormats/VqaReader.cs @@ -41,6 +41,7 @@ namespace OpenRA.Mods.Cnc.FileFormats readonly uint[] offsets; readonly uint[] palette; readonly uint videoFlags; // if 0x10 is set the video is a 16 bit hq video (ts and later) + readonly ushort totalFrameWidth; // Stores a list of subpixels, referenced by the VPTZ chunk byte[] cbf; @@ -60,7 +61,7 @@ namespace OpenRA.Mods.Cnc.FileFormats // Top half contains block info, bottom half contains references to cbf array byte[] origData; - public VqaReader(Stream stream) + public VqaReader(Stream stream, bool useFramePadding) { this.stream = stream; @@ -145,7 +146,17 @@ namespace OpenRA.Mods.Cnc.FileFormats CollectAudioData(); - CurrentFrameData = new byte[Width * Height * 4]; + if (useFramePadding) + { + var frameSize = Exts.NextPowerOf2(Math.Max(Width, Height)); + CurrentFrameData = new byte[frameSize * frameSize * 4]; + totalFrameWidth = (ushort)frameSize; + } + else + { + CurrentFrameData = new byte[Width * Height * 4]; + totalFrameWidth = Width; + } Reset(); } @@ -485,7 +496,7 @@ namespace OpenRA.Mods.Cnc.FileFormats var pixelX = x * blockWidth + i; var pixelY = y * blockHeight + j; - var pos = pixelY * Width + pixelX; + var pos = pixelY * totalFrameWidth + pixelX; CurrentFrameData[pos * 4] = (byte)(color & 0xFF); CurrentFrameData[pos * 4 + 1] = (byte)(color >> 8 & 0xFF); CurrentFrameData[pos * 4 + 2] = (byte)(color >> 16 & 0xFF); @@ -511,7 +522,7 @@ namespace OpenRA.Mods.Cnc.FileFormats var pixelX = x * blockWidth + bx; var pixelY = y * blockHeight + by; - var pos = pixelY * Width + pixelX; + var pos = pixelY * totalFrameWidth + pixelX; CurrentFrameData[pos * 4] = cbf[offset + p + 2]; CurrentFrameData[pos * 4 + 1] = cbf[offset + p + 1]; CurrentFrameData[pos * 4 + 2] = cbf[offset + p]; diff --git a/OpenRA.Mods.Cnc/FileFormats/WsaReader.cs b/OpenRA.Mods.Cnc/FileFormats/WsaReader.cs index 2075b5648a..6e05ffbf94 100644 --- a/OpenRA.Mods.Cnc/FileFormats/WsaReader.cs +++ b/OpenRA.Mods.Cnc/FileFormats/WsaReader.cs @@ -34,11 +34,12 @@ namespace OpenRA.Mods.Cnc.FileFormats readonly Stream stream; readonly uint[] palette; readonly uint[] frameOffsets; + readonly ushort totalFrameWidth; byte[] previousFramePaletteIndexData; byte[] currentFramePaletteIndexData; - public WsaReader(Stream stream) + public WsaReader(Stream stream, bool useFramePadding) { this.stream = stream; @@ -78,7 +79,17 @@ namespace OpenRA.Mods.Cnc.FileFormats frameOffsets[i] += 768; } - CurrentFrameData = new byte[Width * Height * 4]; + if (useFramePadding) + { + var frameSize = Exts.NextPowerOf2(Math.Max(Width, Height)); + CurrentFrameData = new byte[frameSize * frameSize * 4]; + totalFrameWidth = (ushort)frameSize; + } + else + { + CurrentFrameData = new byte[Width * Height * 4]; + totalFrameWidth = Width; + } Reset(); } @@ -133,6 +144,9 @@ namespace OpenRA.Mods.Cnc.FileFormats CurrentFrameData[position++] = (byte)(color >> 16 & 0xFF); CurrentFrameData[position++] = (byte)(color >> 24 & 0xFF); } + + // Recalculate the position in the byte array to the start of the next pixel row just in case there is padding in the frame. + position = (y + 1) * totalFrameWidth * 4; } } } diff --git a/OpenRA.Mods.Cnc/VideoLoaders/VqaLoader.cs b/OpenRA.Mods.Cnc/VideoLoaders/VqaLoader.cs index 500cd5a393..b4531d6bc5 100644 --- a/OpenRA.Mods.Cnc/VideoLoaders/VqaLoader.cs +++ b/OpenRA.Mods.Cnc/VideoLoaders/VqaLoader.cs @@ -17,14 +17,14 @@ namespace OpenRA.Mods.Cnc.VideoLoaders { public class VqaLoader : IVideoLoader { - public bool TryParseVideo(Stream s, out IVideo video) + public bool TryParseVideo(Stream s, bool useFramePadding, out IVideo video) { video = null; if (!IsWestwoodVqa(s)) return false; - video = new VqaReader(s); + video = new VqaReader(s, useFramePadding); return true; } diff --git a/OpenRA.Mods.Cnc/VideoLoaders/WsaLoader.cs b/OpenRA.Mods.Cnc/VideoLoaders/WsaLoader.cs index 217fb58768..84d4891327 100644 --- a/OpenRA.Mods.Cnc/VideoLoaders/WsaLoader.cs +++ b/OpenRA.Mods.Cnc/VideoLoaders/WsaLoader.cs @@ -18,14 +18,14 @@ namespace OpenRA.Mods.Cnc.VideoLoaders { public class WsaLoader : IVideoLoader { - public bool TryParseVideo(Stream s, out IVideo video) + public bool TryParseVideo(Stream s, bool useFramePadding, out IVideo video) { video = null; if (!IsWsa(s)) return false; - video = new WsaReader(s); + video = new WsaReader(s, useFramePadding); return true; } diff --git a/OpenRA.Mods.Common/Scripting/Media.cs b/OpenRA.Mods.Common/Scripting/Media.cs index 47fe64c447..3baba063e8 100644 --- a/OpenRA.Mods.Common/Scripting/Media.cs +++ b/OpenRA.Mods.Common/Scripting/Media.cs @@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Scripting public static IVideo LoadVideo(Stream s) { - return VideoLoader.GetVideo(s, Game.ModData.VideoLoaders); + return VideoLoader.GetVideo(s, true, Game.ModData.VideoLoaders); } } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs index a67a0b045b..5c77bfba0f 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/AssetBrowserLogic.cs @@ -506,7 +506,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic // Mute music so it doesn't interfere with the current asset. MuteSounds(); - var video = VideoLoader.GetVideo(Game.ModData.DefaultFileSystem.Open(filename), Game.ModData.VideoLoaders); + var video = VideoLoader.GetVideo(Game.ModData.DefaultFileSystem.Open(filename), true, Game.ModData.VideoLoaders); if (video != null) { player = panel.Get("PLAYER"); diff --git a/OpenRA.Mods.Common/Widgets/VideoPlayerWidget.cs b/OpenRA.Mods.Common/Widgets/VideoPlayerWidget.cs index ea774d9a5f..f37aa0b5e9 100644 --- a/OpenRA.Mods.Common/Widgets/VideoPlayerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/VideoPlayerWidget.cs @@ -37,6 +37,7 @@ namespace OpenRA.Mods.Common.Widgets float overlayScale; bool stopped; bool paused; + int textureSize; Action onComplete; @@ -45,7 +46,7 @@ namespace OpenRA.Mods.Common.Widgets if (filename == cachedVideo) return; - var video = VideoLoader.GetVideo(Game.ModData.DefaultFileSystem.Open(filename), Game.ModData.VideoLoaders); + var video = VideoLoader.GetVideo(Game.ModData.DefaultFileSystem.Open(filename), true, Game.ModData.VideoLoaders); Open(video); cachedVideo = filename; @@ -63,11 +64,11 @@ namespace OpenRA.Mods.Common.Widgets invLength = video.Framerate * 1f / video.FrameCount; var size = Math.Max(video.Width, video.Height); - var textureSize = Exts.NextPowerOf2(size); + textureSize = Exts.NextPowerOf2(size); var videoSheet = new Sheet(SheetType.BGRA, new Size(textureSize, textureSize)); videoSheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear; - videoSheet.GetTexture().SetData(video.CurrentFrameData); + videoSheet.GetTexture().SetData(video.CurrentFrameData, textureSize, textureSize); videoSprite = new Sprite(videoSheet, new Rectangle( @@ -110,12 +111,12 @@ namespace OpenRA.Mods.Common.Widgets while (nextFrame > video.CurrentFrameNumber) { video.AdvanceFrame(); - videoSprite.Sheet.GetTexture().SetData(video.CurrentFrameData); + videoSprite.Sheet.GetTexture().SetData(video.CurrentFrameData, textureSize, textureSize); skippedFrames++; } if (skippedFrames > 1) - Log.Write("perf", "VqaPlayer : {0} skipped {1} frames at position {2}", cachedVideo, skippedFrames, video.CurrentFrameNumber); + Log.Write("perf", "VideoPlayer: {0} skipped {1} frames at position {2}", cachedVideo, skippedFrames, video.CurrentFrameNumber); } WidgetUtils.DrawSprite(videoSprite, videoOrigin, videoSize); @@ -218,7 +219,7 @@ namespace OpenRA.Mods.Common.Widgets paused = true; Game.Sound.StopVideo(); video.Reset(); - videoSprite.Sheet.GetTexture().SetData(video.CurrentFrameData); + videoSprite.Sheet.GetTexture().SetData(video.CurrentFrameData, textureSize, textureSize); Game.RunAfterTick(onComplete); }