diff --git a/OpenRA.Game/Graphics/SpriteLoader.cs b/OpenRA.Game/Graphics/SpriteLoader.cs index 420ae9141d..64148bc5cb 100644 --- a/OpenRA.Game/Graphics/SpriteLoader.cs +++ b/OpenRA.Game/Graphics/SpriteLoader.cs @@ -22,20 +22,30 @@ namespace OpenRA.Graphics /// public enum SpriteFrameType { - // 8 bit index into an external palette + /// + /// 8 bit index into an external palette. + /// Indexed8, - // 32 bit color such as returned by Color.ToArgb() or the bmp file format - // (remember that little-endian systems place the little bits in the first byte!) + /// + /// 32 bit color such as returned by Color.ToArgb() or the bmp file format + /// (remember that little-endian systems place the little bits in the first byte). + /// Bgra32, - // Like BGRA, but without an alpha channel + /// + /// Like BGRA, but without an alpha channel. + /// Bgr24, - // 32 bit color in big-endian format, like png + /// + /// 32 bit color in big-endian format, like png. + /// Rgba32, - // Like RGBA, but without an alpha channel + /// + /// Like RGBA, but without an alpha channel. + /// Rgb24 } diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index 65076727dc..f8d6be32db 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Runtime.InteropServices; using OpenRA.FileFormats; using OpenRA.Primitives; @@ -106,67 +107,21 @@ namespace OpenRA.Graphics public static void FastCopyIntoChannel(Sprite dest, byte[] src, SpriteFrameType srcType, bool premultiplied = false) { var destData = dest.Sheet.GetData(); + var stride = dest.Sheet.Size.Width; + var x = dest.Bounds.Left; + var y = dest.Bounds.Top; var width = dest.Bounds.Width; var height = dest.Bounds.Height; if (dest.Channel == TextureChannel.RGBA) { - var destStride = dest.Sheet.Size.Width; - unsafe - { - // Cast the data to an int array so we can copy the src data directly - fixed (byte* bd = &destData[0]) - { - var data = (uint*)bd; - var x = dest.Bounds.Left; - var y = dest.Bounds.Top; - - var k = 0; - for (var j = 0; j < height; j++) - { - for (var i = 0; i < width; i++) - { - byte r, g, b, a; - switch (srcType) - { - case SpriteFrameType.Bgra32: - case SpriteFrameType.Bgr24: - { - b = src[k++]; - g = src[k++]; - r = src[k++]; - a = srcType == SpriteFrameType.Bgra32 ? src[k++] : (byte)255; - break; - } - - case SpriteFrameType.Rgba32: - case SpriteFrameType.Rgb24: - { - r = src[k++]; - g = src[k++]; - b = src[k++]; - a = srcType == SpriteFrameType.Rgba32 ? src[k++] : (byte)255; - break; - } - - default: - throw new InvalidOperationException($"Unknown SpriteFrameType {srcType}"); - } - - var cc = Color.FromArgb(a, r, g, b); - if (premultiplied) - data[(y + j) * destStride + x + i] = cc.ToArgb(); - else - data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb(); - } - } - } - } + CopyIntoRgba(src, srcType, premultiplied, destData, x, y, width, height, stride); } else { - var destStride = dest.Sheet.Size.Width * 4; - var destOffset = destStride * dest.Bounds.Top + dest.Bounds.Left * 4 + ChannelMasks[(int)dest.Channel]; + // Copy into single channel of destination. + var destStride = stride * 4; + var destOffset = destStride * y + x * 4 + ChannelMasks[(int)dest.Channel]; var destSkip = destStride - 4 * width; var srcOffset = 0; @@ -183,56 +138,119 @@ namespace OpenRA.Graphics } } + static void CopyIntoRgba( + byte[] src, SpriteFrameType srcType, bool premultiplied, byte[] dest, int x, int y, int width, int height, int stride) + { + var si = 0; + var di = y * stride + x; + var d = MemoryMarshal.Cast(dest); + + // SpriteFrameType.Brga32 is a common source format, and it matches the destination format. + // Provide a fast past that just performs memory copies. + if (srcType == SpriteFrameType.Bgra32) + { + var s = MemoryMarshal.Cast(src); + for (var h = 0; h < height; h++) + { + s[si..(si + width)].CopyTo(d[di..(di + width)]); + + if (!premultiplied) + { + for (var w = 0; w < width; w++) + { + d[di] = PremultiplyAlpha(Color.FromArgb(d[di])).ToArgb(); + di++; + } + + di -= width; + } + + si += width; + di += stride; + } + + return; + } + + for (var h = 0; h < height; h++) + { + for (var w = 0; w < width; w++) + { + byte r, g, b, a; + switch (srcType) + { + case SpriteFrameType.Bgra32: + case SpriteFrameType.Bgr24: + b = src[si++]; + g = src[si++]; + r = src[si++]; + a = srcType == SpriteFrameType.Bgra32 ? src[si++] : byte.MaxValue; + break; + + case SpriteFrameType.Rgba32: + case SpriteFrameType.Rgb24: + r = src[si++]; + g = src[si++]; + b = src[si++]; + a = srcType == SpriteFrameType.Rgba32 ? src[si++] : byte.MaxValue; + break; + + default: + throw new InvalidOperationException($"Unknown SpriteFrameType {srcType}"); + } + + var c = Color.FromArgb(a, r, g, b); + if (!premultiplied) + c = PremultiplyAlpha(c); + d[di++] = c.ToArgb(); + } + + di += stride - width; + } + } + public static void FastCopyIntoSprite(Sprite dest, Png src) { var destData = dest.Sheet.GetData(); - var destStride = dest.Sheet.Size.Width; + var stride = dest.Sheet.Size.Width; + var x = dest.Bounds.Left; + var y = dest.Bounds.Top; var width = dest.Bounds.Width; var height = dest.Bounds.Height; - unsafe + var si = 0; + var di = y * stride + x; + var d = MemoryMarshal.Cast(destData); + + for (var h = 0; h < height; h++) { - // Cast the data to an int array so we can copy the src data directly - fixed (byte* bd = &destData[0]) + for (var w = 0; w < width; w++) { - var data = (uint*)bd; - var x = dest.Bounds.Left; - var y = dest.Bounds.Top; - - var k = 0; - for (var j = 0; j < height; j++) + Color c; + switch (src.Type) { - for (var i = 0; i < width; i++) - { - Color cc; - switch (src.Type) - { - case SpriteFrameType.Indexed8: - { - cc = src.Palette[src.Data[k++]]; - break; - } + case SpriteFrameType.Indexed8: + c = src.Palette[src.Data[si++]]; + break; - case SpriteFrameType.Rgba32: - case SpriteFrameType.Rgb24: - { - var r = src.Data[k++]; - var g = src.Data[k++]; - var b = src.Data[k++]; - var a = src.Type == SpriteFrameType.Rgba32 ? src.Data[k++] : (byte)255; - cc = Color.FromArgb(a, r, g, b); - break; - } + case SpriteFrameType.Rgba32: + case SpriteFrameType.Rgb24: + var r = src.Data[si++]; + var g = src.Data[si++]; + var b = src.Data[si++]; + var a = src.Type == SpriteFrameType.Rgba32 ? src.Data[si++] : byte.MaxValue; + c = Color.FromArgb(a, r, g, b); + break; - // Pngs don't support BGR[A], so no need to include them here - default: - throw new InvalidOperationException($"Unknown SpriteFrameType {src.Type}"); - } - - data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb(); - } + // PNGs don't support BGR[A], so no need to include them here + default: + throw new InvalidOperationException($"Unknown SpriteFrameType {src.Type}"); } + + d[di++] = PremultiplyAlpha(c).ToArgb(); } + + di += stride - width; } }