Add RGBA cursor support.
This commit is contained in:
@@ -37,6 +37,7 @@ namespace OpenRA.Graphics
|
|||||||
pals[p.Palette] = p;
|
pals[p.Palette] = p;
|
||||||
|
|
||||||
Palettes = nodesDict["Cursors"].Nodes.Select(n => n.Value.Value)
|
Palettes = nodesDict["Cursors"].Nodes.Select(n => n.Value.Value)
|
||||||
|
.Where(p => p != null)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToDictionary(p => p, p => pals[p].ReadPalette(modData.DefaultFileSystem))
|
.ToDictionary(p => p, p => pals[p].ReadPalette(modData.DefaultFileSystem))
|
||||||
.AsReadOnly();
|
.AsReadOnly();
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ namespace OpenRA.Graphics
|
|||||||
readonly CursorProvider cursorProvider;
|
readonly CursorProvider cursorProvider;
|
||||||
readonly Dictionary<string, Sprite[]> sprites = new Dictionary<string, Sprite[]>();
|
readonly Dictionary<string, Sprite[]> sprites = new Dictionary<string, Sprite[]>();
|
||||||
readonly SheetBuilder sheetBuilder;
|
readonly SheetBuilder sheetBuilder;
|
||||||
readonly HardwarePalette hardwarePalette = new HardwarePalette();
|
|
||||||
readonly Cache<string, PaletteReference> paletteReferences;
|
|
||||||
|
|
||||||
CursorSequence cursor;
|
CursorSequence cursor;
|
||||||
bool isLocked = false;
|
bool isLocked = false;
|
||||||
@@ -32,16 +30,11 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
this.cursorProvider = cursorProvider;
|
this.cursorProvider = cursorProvider;
|
||||||
|
|
||||||
paletteReferences = new Cache<string, PaletteReference>(CreatePaletteReference);
|
|
||||||
foreach (var p in cursorProvider.Palettes)
|
|
||||||
hardwarePalette.AddPalette(p.Key, p.Value, false);
|
|
||||||
|
|
||||||
hardwarePalette.Initialize();
|
|
||||||
sheetBuilder = new SheetBuilder(SheetType.Indexed);
|
sheetBuilder = new SheetBuilder(SheetType.Indexed);
|
||||||
foreach (var kv in cursorProvider.Cursors)
|
foreach (var kv in cursorProvider.Cursors)
|
||||||
{
|
{
|
||||||
var frames = kv.Value.Frames;
|
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.
|
// Hardware cursors have a number of odd platform-specific bugs/limitations.
|
||||||
// Reduce the number of edge cases by padding the individual frames such that:
|
// 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++)
|
for (var i = 0; i < frames.Length; i++)
|
||||||
{
|
{
|
||||||
// Software rendering is used when the cursor is locked
|
// 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
|
// Calculate the padding to position the frame within sequenceBounds
|
||||||
var paddingTL = -(sequenceBounds.Location + frameHotspots[i]);
|
var paddingTL = -(sequenceBounds.Location + frameHotspots[i]);
|
||||||
var paddingBR = paddedSize - new int2(frames[i].Size) - paddingTL;
|
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);
|
hardwareCursors.Add(kv.Key, cursors);
|
||||||
@@ -84,26 +79,24 @@ namespace OpenRA.Graphics
|
|||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
PaletteReference CreatePaletteReference(string name)
|
IHardwareCursor CreateCursor(string name, byte[] data, Size size, int2 paddingTL, int2 paddingBR, int2 hotspot)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
// Pad the cursor and convert to RBGA
|
// Pad the cursor and convert to RBGA
|
||||||
var newWidth = paddingTL.X + frame.Size.Width + paddingBR.X;
|
var newWidth = paddingTL.X + size.Width + paddingBR.X;
|
||||||
var newHeight = paddingTL.Y + frame.Size.Height + paddingBR.Y;
|
var newHeight = paddingTL.Y + size.Height + paddingBR.Y;
|
||||||
var rgbaData = new byte[4 * newWidth * newHeight];
|
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 src = 4 * (j * size.Width + i);
|
||||||
var o = 4 * ((j + paddingTL.Y) * newWidth + i + paddingTL.X);
|
var dest = 4 * ((j + paddingTL.Y) * newWidth + i + paddingTL.X);
|
||||||
for (var k = 0; k < 4; k++)
|
|
||||||
rgbaData[o + k] = bytes[k];
|
// 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 cursorSprite = sprites[cursor.Name][frame];
|
||||||
|
|
||||||
var cursorOffset = cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2();
|
var cursorOffset = cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2();
|
||||||
|
renderer.RgbaSpriteRenderer.DrawSprite(cursorSprite,
|
||||||
renderer.SetPalette(hardwarePalette);
|
|
||||||
renderer.SpriteRenderer.DrawSprite(cursorSprite,
|
|
||||||
lockedPosition - cursorOffset,
|
lockedPosition - cursorOffset,
|
||||||
paletteReferences[cursorSequence.Palette],
|
|
||||||
cursorSprite.Size);
|
cursorSprite.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Primitives;
|
|
||||||
|
|
||||||
namespace OpenRA.Graphics
|
namespace OpenRA.Graphics
|
||||||
{
|
{
|
||||||
@@ -27,11 +26,9 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
public sealed class SoftwareCursor : ICursor
|
public sealed class SoftwareCursor : ICursor
|
||||||
{
|
{
|
||||||
readonly HardwarePalette palette = new HardwarePalette();
|
|
||||||
readonly Cache<string, PaletteReference> paletteReferences;
|
|
||||||
readonly Dictionary<string, Sprite[]> sprites = new Dictionary<string, Sprite[]>();
|
readonly Dictionary<string, Sprite[]> sprites = new Dictionary<string, Sprite[]>();
|
||||||
readonly CursorProvider cursorProvider;
|
readonly CursorProvider cursorProvider;
|
||||||
readonly SheetBuilder sheetBuilder;
|
SheetBuilder sheetBuilder;
|
||||||
|
|
||||||
bool isLocked = false;
|
bool isLocked = false;
|
||||||
int2 lockedPosition;
|
int2 lockedPosition;
|
||||||
@@ -40,16 +37,13 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
this.cursorProvider = cursorProvider;
|
this.cursorProvider = cursorProvider;
|
||||||
|
|
||||||
paletteReferences = new Cache<string, PaletteReference>(CreatePaletteReference);
|
sheetBuilder = new SheetBuilder(SheetType.BGRA, 1024);
|
||||||
foreach (var p in cursorProvider.Palettes)
|
|
||||||
palette.AddPalette(p.Key, p.Value, false);
|
|
||||||
|
|
||||||
palette.Initialize();
|
|
||||||
|
|
||||||
sheetBuilder = new SheetBuilder(SheetType.Indexed);
|
|
||||||
foreach (var kv in cursorProvider.Cursors)
|
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);
|
sprites.Add(kv.Key, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,10 +52,38 @@ namespace OpenRA.Graphics
|
|||||||
Game.Renderer.Window.SetHardwareCursor(null);
|
Game.Renderer.Window.SetHardwareCursor(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
PaletteReference CreatePaletteReference(string name)
|
public static byte[] FrameToBGRA(string name, ISpriteFrame frame, ImmutablePalette palette)
|
||||||
{
|
{
|
||||||
var pal = palette.GetPalette(name);
|
// Data is already in BGRA format
|
||||||
return new PaletteReference(name, palette.GetPaletteIndex(name), pal, palette);
|
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;
|
string cursorName;
|
||||||
@@ -90,11 +112,10 @@ namespace OpenRA.Graphics
|
|||||||
(2 * cursorSequence.Hotspot) + cursorSprite.Size.XY.ToInt2() :
|
(2 * cursorSequence.Hotspot) + cursorSprite.Size.XY.ToInt2() :
|
||||||
cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2();
|
cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2();
|
||||||
|
|
||||||
renderer.SetPalette(palette);
|
|
||||||
var mousePos = isLocked ? lockedPosition : Viewport.LastMousePos;
|
var mousePos = isLocked ? lockedPosition : Viewport.LastMousePos;
|
||||||
renderer.SpriteRenderer.DrawSprite(cursorSprite,
|
|
||||||
|
renderer.RgbaSpriteRenderer.DrawSprite(cursorSprite,
|
||||||
mousePos - cursorOffset,
|
mousePos - cursorOffset,
|
||||||
paletteReferences[cursorSequence.Palette],
|
|
||||||
cursorSize);
|
cursorSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +134,6 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
palette.Dispose();
|
|
||||||
sheetBuilder.Dispose();
|
sheetBuilder.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user