diff --git a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs index 6e8fef959e..ebea548da7 100755 --- a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs +++ b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs @@ -71,6 +71,7 @@ namespace OpenRA.FileFormats.Graphics public interface ITexture { void SetData( Bitmap bitmap ); + void SetData(int[,] colors); } public interface IFont diff --git a/OpenRA.FileFormats/Graphics/VqaReader.cs b/OpenRA.FileFormats/Graphics/VqaReader.cs index 97fd750ab4..3a66514b41 100644 --- a/OpenRA.FileFormats/Graphics/VqaReader.cs +++ b/OpenRA.FileFormats/Graphics/VqaReader.cs @@ -33,7 +33,7 @@ namespace OpenRA.FileFormats byte cbParts; int2 blocks; UInt32[] offsets; - Color[] palette; + int[] palette; // Stores a list of subpixels, referenced by the VPTZ chunk byte[] cbf; @@ -42,8 +42,11 @@ namespace OpenRA.FileFormats int cbOffset = 0; // Top half contains block info, bottom half contains references to cbf array - byte[] framedata; - + byte[] origData; + + // Final frame output + int[,] frameData; + public VqaReader( Stream stream ) { this.stream = stream; @@ -81,12 +84,12 @@ namespace OpenRA.FileFormats /*var bits = */reader.ReadByte(); /*var unknown3 = */reader.ReadChars(14); - + var frameSize = NextPowerOf2(Math.Max(Width,Height)); cbf = new byte[Width*Height]; cbp = new byte[Width*Height]; - palette = new Color[numColors]; - framedata = new byte[2*blocks.X*blocks.Y]; - + palette = new int[numColors]; + origData = new byte[2*blocks.X*blocks.Y]; + frameData = new int[frameSize,frameSize]; // Decode FINF chunk if (new String(reader.ReadChars(4)) != "FINF") @@ -187,13 +190,13 @@ namespace OpenRA.FileFormats byte r = reader.ReadByte(); byte g = reader.ReadByte(); byte b = reader.ReadByte(); - palette[i] = Color.FromArgb(255,ToColorByte(r),ToColorByte(g),ToColorByte(b)); + palette[i] = Color.FromArgb(255,ToColorByte(r),ToColorByte(g),ToColorByte(b)).ToArgb(); } break; // Frame data case "VPTZ": - Format80.DecodeInto( reader.ReadBytes(subchunkLength), framedata ); + Format80.DecodeInto( reader.ReadBytes(subchunkLength), origData ); // This is the last subchunk return; default: @@ -202,29 +205,33 @@ namespace OpenRA.FileFormats } } - public void FrameData(ref Bitmap frame) + public int[,] FrameData() { - var bitmapData = frame.LockBits(new Rectangle(0, 0, Width, Height), - ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); - unsafe - { - int* c = (int*)bitmapData.Scan0; - - for (var y = 0; y < blocks.Y; y++) - for (var x = 0; x < blocks.X; x++) - { - var px = framedata[x + y*blocks.X]; - var mod = framedata[x + (y + blocks.Y)*blocks.X]; - for (var j = 0; j < blockHeight; j++) - for (var i = 0; i < blockWidth; i++) - { - var cbfi = (mod*256 + px)*8 + j*blockWidth + i; - byte color = (mod == 0x0f) ? px : cbf[cbfi]; - *(c + ((y*blockHeight + j) * bitmapData.Stride >> 2) + x*blockWidth + i) = palette[color].ToArgb(); - } - } - } - frame.UnlockBits(bitmapData); + for (var y = 0; y < blocks.Y; y++) + for (var x = 0; x < blocks.X; x++) + { + var px = origData[x + y*blocks.X]; + var mod = origData[x + (y + blocks.Y)*blocks.X]; + for (var j = 0; j < blockHeight; j++) + for (var i = 0; i < blockWidth; i++) + { + var cbfi = (mod*256 + px)*8 + j*blockWidth + i; + byte color = (mod == 0x0f) ? px : cbf[cbfi]; + frameData[y*blockHeight + j, x*blockWidth + i] = palette[color]; + } + } + return frameData; + } + + int NextPowerOf2(int v) + { + --v; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + ++v; + return v; } public byte ToColorByte(byte b) diff --git a/OpenRA.Game/Widgets/VqaPlayerWidget.cs b/OpenRA.Game/Widgets/VqaPlayerWidget.cs index 6df871588e..8a6228622b 100644 --- a/OpenRA.Game/Widgets/VqaPlayerWidget.cs +++ b/OpenRA.Game/Widgets/VqaPlayerWidget.cs @@ -23,7 +23,6 @@ namespace OpenRA.Widgets float timestep; Sprite videoSprite; - Bitmap videoFrame; VqaReader video = null; public void LoadVideo(string filename) @@ -32,11 +31,7 @@ namespace OpenRA.Widgets timestep = 1e3f/video.Framerate; var size = OpenRA.Graphics.Util.NextPowerOf2(Math.Max(video.Width, video.Height)); - videoFrame = new Bitmap(size,size); - video.FrameData(ref videoFrame); - videoSprite = new Sprite(new Sheet(new Size(size,size)), new Rectangle( 0, 0, video.Width, video.Height ), TextureChannel.Alpha); - videoSprite.sheet.Texture.SetData(videoFrame); } int lastTime; @@ -59,8 +54,7 @@ namespace OpenRA.Widgets { lastTime = t; advanceNext = true; - video.FrameData(ref videoFrame); - videoSprite.sheet.Texture.SetData(videoFrame); + videoSprite.sheet.Texture.SetData(video.FrameData()); } Game.Renderer.RgbaSpriteRenderer.DrawSprite(videoSprite, new int2(RenderBounds.X,RenderBounds.Y), "chrome"); diff --git a/OpenRA.Gl/OpenRA.Gl.csproj b/OpenRA.Gl/OpenRA.Gl.csproj index 9005583e28..3ebcbf77bb 100644 --- a/OpenRA.Gl/OpenRA.Gl.csproj +++ b/OpenRA.Gl/OpenRA.Gl.csproj @@ -21,6 +21,7 @@ DEBUG;TRACE prompt 4 + true pdbonly @@ -29,6 +30,7 @@ TRACE prompt 4 + true diff --git a/OpenRA.Gl/Texture.cs b/OpenRA.Gl/Texture.cs index 75cc7bb62c..ec319d2571 100644 --- a/OpenRA.Gl/Texture.cs +++ b/OpenRA.Gl/Texture.cs @@ -12,6 +12,8 @@ using System.Drawing; using System.Drawing.Imaging; using OpenRA.FileFormats.Graphics; using Tao.OpenGl; +using System.IO; +using System; namespace OpenRA.GlRenderer { @@ -26,6 +28,33 @@ namespace OpenRA.GlRenderer SetData(bitmap); } + // An array of Color.ToARGB()'s + public void SetData(int[,] colors) + { + int width = colors.GetUpperBound(0) + 1; + int height = colors.GetUpperBound(1) + 1; + + if (!IsPowerOf2(width) || !IsPowerOf2(height)) + throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width,height)); + + IntPtr intPtr; + unsafe + { + fixed (int* ptr = colors) + intPtr = new IntPtr((void *) ptr); + } + + Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture); + GraphicsDevice.CheckGlError(); + Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_BASE_LEVEL, 0); + GraphicsDevice.CheckGlError(); + Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAX_LEVEL, 0); + GraphicsDevice.CheckGlError(); + Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA8, width, height, + 0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, intPtr); + GraphicsDevice.CheckGlError(); + } + public void SetData(Bitmap bitmap) { if (!IsPowerOf2(bitmap.Width) || !IsPowerOf2(bitmap.Height))