Add support for hardware cursors.

This commit is contained in:
Paul Chote
2014-11-13 22:03:53 +13:00
committed by Paul Chote
parent 75b046ae2a
commit 1317101662
6 changed files with 194 additions and 1 deletions

View File

@@ -0,0 +1,138 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public class HardwareCursor : ICursor
{
readonly Dictionary<string, IHardwareCursor[]> hardwareCursors = new Dictionary<string, IHardwareCursor[]>();
readonly CursorProvider cursorProvider;
CursorSequence cursor;
public HardwareCursor(CursorProvider cursorProvider)
{
this.cursorProvider = cursorProvider;
foreach (var kv in cursorProvider.Cursors)
{
var palette = cursorProvider.Palettes[kv.Value.Palette];
var hc = kv.Value.Frames
.Select(f => CreateCursor(f, palette, kv.Key, kv.Value))
.ToArray();
hardwareCursors.Add(kv.Key, hc);
}
Update();
}
IHardwareCursor CreateCursor(ISpriteFrame f, ImmutablePalette palette, string name, CursorSequence sequence)
{
var hotspot = sequence.Hotspot - f.Offset.ToInt2() + new int2(f.Size) / 2;
// Expand the frame if required to include the hotspot
var frameWidth = f.Size.Width;
var dataWidth = f.Size.Width;
var dataX = 0;
if (hotspot.X < 0)
{
dataX = -hotspot.X;
dataWidth += dataX;
hotspot.X = 0;
}
else if (hotspot.X >= frameWidth)
dataWidth = hotspot.X + 1;
var frameHeight = f.Size.Height;
var dataHeight = f.Size.Height;
var dataY = 0;
if (hotspot.Y < 0)
{
dataY = -hotspot.Y;
dataHeight += dataY;
hotspot.Y = 0;
}
else if (hotspot.Y >= frameHeight)
dataHeight = hotspot.Y + 1;
var data = new byte[4 * dataWidth * dataHeight];
for (var j = 0; j < frameHeight; j++)
{
for (var i = 0; i < frameWidth; i++)
{
var bytes = BitConverter.GetBytes(palette[f.Data[j * frameWidth + i]]);
var start = 4 * ((j + dataY) * dataWidth + dataX + i);
for (var k = 0; k < 4; k++)
data[start + k] = bytes[k];
}
}
return Game.Renderer.Device.CreateHardwareCursor(name, new Size(dataWidth, dataHeight), data, hotspot);
}
public void SetCursor(string cursorName)
{
if ((cursorName == null && cursor == null) || (cursor != null && cursorName == cursor.Name))
return;
if (cursorName == null || !cursorProvider.Cursors.TryGetValue(cursorName, out cursor))
cursor = null;
Update();
}
int frame;
int ticks;
public void Tick()
{
if (cursor == null || cursor.Length == 1)
return;
if (++ticks > 2)
{
ticks -= 2;
frame++;
Update();
}
}
void Update()
{
if (cursor == null)
Game.Renderer.Device.SetHardwareCursor(null);
else
{
if (frame >= cursor.Length)
frame = frame % cursor.Length;
Game.Renderer.Device.SetHardwareCursor(hardwareCursors[cursor.Name][frame]);
}
}
public void Render(Renderer renderer) { }
public void Dispose()
{
foreach (var cursors in hardwareCursors)
foreach (var cursor in cursors.Value)
cursor.Dispose();
hardwareCursors.Clear();
}
}
}

View File

@@ -32,6 +32,8 @@ namespace OpenRA
IGraphicsDevice Create(Size size, WindowMode windowMode);
}
public interface IHardwareCursor : IDisposable { }
public enum BlendMode : byte { None, Alpha, Additive, Subtractive, Multiply }
public interface IGraphicsDevice : IDisposable
@@ -61,6 +63,9 @@ namespace OpenRA
void GrabWindowMouseFocus();
void ReleaseWindowMouseFocus();
IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot);
void SetHardwareCursor(IHardwareCursor cursor);
}
public interface IVertexBuffer<T> : IDisposable

View File

@@ -50,6 +50,8 @@ namespace OpenRA.Graphics
}
sheetBuilder.Current.ReleaseBuffer();
Game.Renderer.Device.SetHardwareCursor(null);
}
PaletteReference CreatePaletteReference(string name)

View File

@@ -253,6 +253,7 @@
<Compile Include="Graphics\UISpriteRenderable.cs" />
<Compile Include="GameRules\DamageWarhead.cs" />
<Compile Include="Graphics\SoftwareCursor.cs" />
<Compile Include="Graphics\HardwareCursor.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="FileSystem\D2kSoundResources.cs" />

View File

@@ -66,6 +66,9 @@ namespace OpenRA.Renderer.Null
public ITexture CreateTexture(Bitmap bitmap) { return new NullTexture(); }
public IFrameBuffer CreateFrameBuffer(Size s) { return new NullFrameBuffer(); }
public IShader CreateShader(string name) { return new NullShader(); }
public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot) { return null; }
public void SetHardwareCursor(IHardwareCursor cursor) { }
}
public class NullShader : IShader

View File

@@ -10,6 +10,8 @@
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using OpenRA;
using OpenRA.Graphics;
using OpenTK.Graphics.OpenGL;
@@ -80,7 +82,6 @@ namespace OpenRA.Renderer.Sdl2
SDL.SDL_SetHint(SDL.SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
}
SDL.SDL_ShowCursor(0);
context = SDL.SDL_GL_CreateContext(window);
SDL.SDL_GL_MakeCurrent(window, context);
GL.LoadAll();
@@ -103,10 +104,53 @@ namespace OpenRA.Renderer.Sdl2
input = new Sdl2Input();
}
public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot)
{
var c = new SDL2HardwareCursor(size, data, hotspot);
if (c.Cursor == IntPtr.Zero)
throw new InvalidDataException("Failed to create hardware cursor `{0}`: {1}".F(name, SDL.SDL_GetError()));
return c;
}
public void SetHardwareCursor(IHardwareCursor cursor)
{
var c = cursor as SDL2HardwareCursor;
if (c == null)
SDL.SDL_ShowCursor(0);
else
{
SDL.SDL_ShowCursor(1);
SDL.SDL_SetCursor(c.Cursor);
}
}
class SDL2HardwareCursor : IHardwareCursor
{
public readonly IntPtr Cursor;
readonly IntPtr surface;
public SDL2HardwareCursor(Size size, byte[] data, int2 hotspot)
{
surface = SDL.SDL_CreateRGBSurface(0, size.Width, size.Height, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
var sur = (SDL2.SDL.SDL_Surface)Marshal.PtrToStructure(surface, typeof(SDL2.SDL.SDL_Surface));
Marshal.Copy(data, 0, sur.pixels, data.Length);
Cursor = SDL.SDL_CreateColorCursor(surface, hotspot.X, hotspot.Y);
}
public void Dispose()
{
SDL.SDL_FreeCursor(Cursor);
SDL.SDL_FreeSurface(surface);
}
}
public void Dispose()
{
if (disposed)
return;
disposed = true;
if (context != IntPtr.Zero)
{