diff --git a/OpenRA.Game/Graphics/CursorProvider.cs b/OpenRA.Game/Graphics/CursorProvider.cs index f310360fba..54bf495879 100644 --- a/OpenRA.Game/Graphics/CursorProvider.cs +++ b/OpenRA.Game/Graphics/CursorProvider.cs @@ -37,6 +37,7 @@ namespace OpenRA.Graphics pals[p.Palette] = p; Palettes = nodesDict["Cursors"].Nodes.Select(n => n.Value.Value) + .Where(p => p != null) .Distinct() .ToDictionary(p => p, p => pals[p].ReadPalette(modData.DefaultFileSystem)) .AsReadOnly(); diff --git a/OpenRA.Game/Graphics/HardwareCursor.cs b/OpenRA.Game/Graphics/HardwareCursor.cs index d4652a87de..1e6d9ea842 100644 --- a/OpenRA.Game/Graphics/HardwareCursor.cs +++ b/OpenRA.Game/Graphics/HardwareCursor.cs @@ -21,8 +21,6 @@ namespace OpenRA.Graphics readonly CursorProvider cursorProvider; readonly Dictionary sprites = new Dictionary(); readonly SheetBuilder sheetBuilder; - readonly HardwarePalette hardwarePalette = new HardwarePalette(); - readonly Cache paletteReferences; CursorSequence cursor; bool isLocked = false; @@ -32,16 +30,11 @@ namespace OpenRA.Graphics { this.cursorProvider = cursorProvider; - paletteReferences = new Cache(CreatePaletteReference); - foreach (var p in cursorProvider.Palettes) - hardwarePalette.AddPalette(p.Key, p.Value, false); - - hardwarePalette.Initialize(); sheetBuilder = new SheetBuilder(SheetType.Indexed); foreach (var kv in cursorProvider.Cursors) { var frames = kv.Value.Frames; - var palette = cursorProvider.Palettes[kv.Value.Palette]; + var palette = !string.IsNullOrEmpty(kv.Value.Palette) ? cursorProvider.Palettes[kv.Value.Palette] : null; // Hardware cursors have a number of odd platform-specific bugs/limitations. // Reduce the number of edge cases by padding the individual frames such that: @@ -67,12 +60,14 @@ namespace OpenRA.Graphics for (var i = 0; i < frames.Length; i++) { // Software rendering is used when the cursor is locked - frameSprites[i] = sheetBuilder.Add(frames[i].Data, frames[i].Size, 0, frames[i].Offset); + // SheetBuilder expects data in BGRA + var data = SoftwareCursor.FrameToBGRA(kv.Key, frames[i], palette); + frameSprites[i] = sheetBuilder.Add(data, frames[i].Size, 0, frames[i].Offset); // Calculate the padding to position the frame within sequenceBounds var paddingTL = -(sequenceBounds.Location + frameHotspots[i]); var paddingBR = paddedSize - new int2(frames[i].Size) - paddingTL; - cursors[i] = CreateCursor(kv.Key, frames[i], palette, paddingTL, paddingBR, -sequenceBounds.Location); + cursors[i] = CreateCursor(kv.Key, data, frames[i].Size, paddingTL, paddingBR, -sequenceBounds.Location); } hardwareCursors.Add(kv.Key, cursors); @@ -84,26 +79,24 @@ namespace OpenRA.Graphics Update(); } - PaletteReference CreatePaletteReference(string name) - { - var pal = hardwarePalette.GetPalette(name); - return new PaletteReference(name, hardwarePalette.GetPaletteIndex(name), pal, hardwarePalette); - } - - IHardwareCursor CreateCursor(string name, ISpriteFrame frame, ImmutablePalette palette, int2 paddingTL, int2 paddingBR, int2 hotspot) + IHardwareCursor CreateCursor(string name, byte[] data, Size size, int2 paddingTL, int2 paddingBR, int2 hotspot) { // Pad the cursor and convert to RBGA - var newWidth = paddingTL.X + frame.Size.Width + paddingBR.X; - var newHeight = paddingTL.Y + frame.Size.Height + paddingBR.Y; + var newWidth = paddingTL.X + size.Width + paddingBR.X; + var newHeight = paddingTL.Y + size.Height + paddingBR.Y; var rgbaData = new byte[4 * newWidth * newHeight]; - for (var j = 0; j < frame.Size.Height; j++) + for (var j = 0; j < size.Height; j++) { - for (var i = 0; i < frame.Size.Width; i++) + for (var i = 0; i < size.Width; i++) { - var bytes = BitConverter.GetBytes(palette[frame.Data[j * frame.Size.Width + i]]); - var o = 4 * ((j + paddingTL.Y) * newWidth + i + paddingTL.X); - for (var k = 0; k < 4; k++) - rgbaData[o + k] = bytes[k]; + var src = 4 * (j * size.Width + i); + var dest = 4 * ((j + paddingTL.Y) * newWidth + i + paddingTL.X); + + // CreateHardwareCursor expects data in RGBA + rgbaData[dest] = data[src + 2]; + rgbaData[dest + 1] = data[src + 1]; + rgbaData[dest + 2] = data[src]; + rgbaData[dest + 3] = data[src + 3]; } } @@ -158,11 +151,8 @@ namespace OpenRA.Graphics var cursorSprite = sprites[cursor.Name][frame]; var cursorOffset = cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2(); - - renderer.SetPalette(hardwarePalette); - renderer.SpriteRenderer.DrawSprite(cursorSprite, + renderer.RgbaSpriteRenderer.DrawSprite(cursorSprite, lockedPosition - cursorOffset, - paletteReferences[cursorSequence.Palette], cursorSprite.Size); } diff --git a/OpenRA.Game/Graphics/SoftwareCursor.cs b/OpenRA.Game/Graphics/SoftwareCursor.cs index 7791203b7e..ae85b1cf44 100644 --- a/OpenRA.Game/Graphics/SoftwareCursor.cs +++ b/OpenRA.Game/Graphics/SoftwareCursor.cs @@ -12,7 +12,6 @@ using System; using System.Collections.Generic; using System.Linq; -using OpenRA.Primitives; namespace OpenRA.Graphics { @@ -27,11 +26,9 @@ namespace OpenRA.Graphics public sealed class SoftwareCursor : ICursor { - readonly HardwarePalette palette = new HardwarePalette(); - readonly Cache paletteReferences; readonly Dictionary sprites = new Dictionary(); readonly CursorProvider cursorProvider; - readonly SheetBuilder sheetBuilder; + SheetBuilder sheetBuilder; bool isLocked = false; int2 lockedPosition; @@ -40,16 +37,13 @@ namespace OpenRA.Graphics { this.cursorProvider = cursorProvider; - paletteReferences = new Cache(CreatePaletteReference); - foreach (var p in cursorProvider.Palettes) - palette.AddPalette(p.Key, p.Value, false); + sheetBuilder = new SheetBuilder(SheetType.BGRA, 1024); - palette.Initialize(); - - sheetBuilder = new SheetBuilder(SheetType.Indexed); foreach (var kv in cursorProvider.Cursors) { - var s = kv.Value.Frames.Select(a => sheetBuilder.Add(a)).ToArray(); + var palette = !string.IsNullOrEmpty(kv.Value.Palette) ? cursorProvider.Palettes[kv.Value.Palette] : null; + var s = kv.Value.Frames.Select(f => sheetBuilder.Add(FrameToBGRA(kv.Key, f, palette), f.Size, 0, f.Offset)).ToArray(); + sprites.Add(kv.Key, s); } @@ -58,10 +52,38 @@ namespace OpenRA.Graphics Game.Renderer.Window.SetHardwareCursor(null); } - PaletteReference CreatePaletteReference(string name) + public static byte[] FrameToBGRA(string name, ISpriteFrame frame, ImmutablePalette palette) { - var pal = palette.GetPalette(name); - return new PaletteReference(name, palette.GetPaletteIndex(name), pal, palette); + // Data is already in BGRA format + if (frame.Type == SpriteFrameType.BGRA) + return frame.Data; + + // Cursors may be either native BGRA or Indexed. + // Indexed sprites are converted to BGRA using the referenced palette. + // All palettes must be explicitly referenced, even if they are embedded in the sprite. + if (frame.Type == SpriteFrameType.Indexed && palette == null) + throw new InvalidOperationException("Cursor sequence `{0}` attempted to load an indexed sprite but does not define Palette".F(name)); + + var width = frame.Size.Width; + var height = frame.Size.Height; + var data = new byte[4 * width * height]; + for (var j = 0; j < height; j++) + { + for (var i = 0; i < width; i++) + { + var bytes = BitConverter.GetBytes(palette[frame.Data[j * width + i]]); + var c = palette[frame.Data[j * width + i]]; + var k = 4 * (j * width + i); + + // Convert RGBA to BGRA + data[k] = bytes[2]; + data[k + 1] = bytes[1]; + data[k + 2] = bytes[0]; + data[k + 3] = bytes[3]; + } + } + + return data; } string cursorName; @@ -90,11 +112,10 @@ namespace OpenRA.Graphics (2 * cursorSequence.Hotspot) + cursorSprite.Size.XY.ToInt2() : cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2(); - renderer.SetPalette(palette); var mousePos = isLocked ? lockedPosition : Viewport.LastMousePos; - renderer.SpriteRenderer.DrawSprite(cursorSprite, + + renderer.RgbaSpriteRenderer.DrawSprite(cursorSprite, mousePos - cursorOffset, - paletteReferences[cursorSequence.Palette], cursorSize); } @@ -113,7 +134,6 @@ namespace OpenRA.Graphics public void Dispose() { - palette.Dispose(); sheetBuilder.Dispose(); } }