From 75c2b117d09f861a80cb987b556f53d8a4aace80 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 23 Dec 2016 22:09:26 +0000 Subject: [PATCH 1/7] Update to new SDL2-CS snapshot. --- thirdparty/fetch-thirdparty-deps.ps1 | 2 +- thirdparty/fetch-thirdparty-deps.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/thirdparty/fetch-thirdparty-deps.ps1 b/thirdparty/fetch-thirdparty-deps.ps1 index d06a114070..3dad43438a 100644 --- a/thirdparty/fetch-thirdparty-deps.ps1 +++ b/thirdparty/fetch-thirdparty-deps.ps1 @@ -130,7 +130,7 @@ if (!(Test-Path "SDL2-CS.dll")) { echo "Fetching SDL2-CS from GitHub." $target = Join-Path $pwd.ToString() "SDL2-CS.dll" - (New-Object System.Net.WebClient).DownloadFile("https://github.com/OpenRA/SDL2-CS/releases/download/20151227/SDL2-CS.dll", $target) + (New-Object System.Net.WebClient).DownloadFile("https://github.com/OpenRA/SDL2-CS/releases/download/20161223/SDL2-CS.dll", $target) } if (!(Test-Path "OpenAL-CS.dll")) diff --git a/thirdparty/fetch-thirdparty-deps.sh b/thirdparty/fetch-thirdparty-deps.sh index d5735063ab..ca4414b2d9 100755 --- a/thirdparty/fetch-thirdparty-deps.sh +++ b/thirdparty/fetch-thirdparty-deps.sh @@ -100,8 +100,8 @@ fi if [ ! -f SDL2-CS.dll -o ! -f SDL2-CS.dll.config ]; then echo "Fetching SDL2-CS from GitHub." - curl -s -L -O https://github.com/OpenRA/SDL2-CS/releases/download/20151227/SDL2-CS.dll - curl -s -L -O https://github.com/OpenRA/SDL2-CS/releases/download/20151227/SDL2-CS.dll.config + curl -s -L -O https://github.com/OpenRA/SDL2-CS/releases/download/20161223/SDL2-CS.dll + curl -s -L -O https://github.com/OpenRA/SDL2-CS/releases/download/20161223/SDL2-CS.dll.config fi if [ ! -f OpenAL-CS.dll -o ! -f OpenAL-CS.dll.config ]; then From 951ce92fccdd18b998c8e850d329aedacf6d9f67 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 23 Dec 2016 22:19:56 +0000 Subject: [PATCH 2/7] Update to new OSX launcher snapshot. --- packaging/osx/buildpackage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/osx/buildpackage.sh b/packaging/osx/buildpackage.sh index 465dad5135..64430e8b10 100755 --- a/packaging/osx/buildpackage.sh +++ b/packaging/osx/buildpackage.sh @@ -1,7 +1,7 @@ #!/bin/bash # OpenRA packaging script for Mac OSX -LAUNCHER_TAG="osx-launcher-20160824" +LAUNCHER_TAG="osx-launcher-20161223" if [ $# -ne "3" ]; then echo "Usage: `basename $0` tag files-dir outputdir" From 6599aeb0355925f6e211e50dd95304160090ecc2 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 23 Dec 2016 16:27:01 +0000 Subject: [PATCH 3/7] Add support for retina displays on OSX. --- OpenRA.Game/Graphics/PlatformInterfaces.cs | 2 + OpenRA.Game/Renderer.cs | 2 +- .../Sdl2GraphicsDevice.cs | 62 +++++++++++++++++-- OpenRA.Platforms.Default/Sdl2Input.cs | 7 ++- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index 436e445384..4a889ec1a0 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -43,6 +43,8 @@ namespace OpenRA IShader CreateShader(string name); Size WindowSize { get; } + float WindowScale { get; } + event Action OnWindowScaleChanged; void Clear(); void Present(); diff --git a/OpenRA.Game/Renderer.cs b/OpenRA.Game/Renderer.cs index 35296e475a..db29ea8543 100644 --- a/OpenRA.Game/Renderer.cs +++ b/OpenRA.Game/Renderer.cs @@ -86,7 +86,7 @@ namespace OpenRA fontSheetBuilder.Dispose(); fontSheetBuilder = new SheetBuilder(SheetType.BGRA); Fonts = modData.Manifest.Fonts.ToDictionary(x => x.Key, - x => new SpriteFont(x.Value.First, modData.DefaultFileSystem.Open(x.Value.First).ReadAllBytes(), x.Value.Second, fontSheetBuilder)).AsReadOnly(); + x => new SpriteFont(x.Value.First, modData.DefaultFileSystem.Open(x.Value.First).ReadAllBytes(), x.Value.Second, Device.WindowScale, fontSheetBuilder)).AsReadOnly(); } } diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs index 19812ccd2f..ae368bb51b 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs @@ -21,10 +21,15 @@ namespace OpenRA.Platforms.Default sealed class Sdl2GraphicsDevice : ThreadAffine, IGraphicsDevice { readonly Sdl2Input input; + IntPtr context, window; bool disposed; public Size WindowSize { get; private set; } + public float WindowScale { get; private set; } + + internal Size SurfaceSize { get; private set; } + public event Action OnWindowScaleChanged = (before, after) => { }; public Sdl2GraphicsDevice(Size windowSize, WindowMode windowMode) { @@ -50,8 +55,28 @@ namespace OpenRA.Platforms.Default Console.WriteLine("Using resolution: {0}x{1}", WindowSize.Width, WindowSize.Height); + var windowFlags = SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL; + if (Platform.CurrentPlatform == PlatformType.OSX) + windowFlags |= SDL.SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI; + window = SDL.SDL_CreateWindow("OpenRA", SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED, - WindowSize.Width, WindowSize.Height, SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL); + WindowSize.Width, WindowSize.Height, windowFlags); + + SurfaceSize = WindowSize; + WindowScale = 1; + + // Enable high resolution rendering for Retina displays + if (Platform.CurrentPlatform == PlatformType.OSX) + { + // OSX defines the window size in "points", with a device-dependent number of pixels per point. + // The window scale is simply the ratio of GL pixels / window points. + int width, height; + SDL.SDL_GL_GetDrawableSize(window, out width, out height); + SurfaceSize = new Size(width, height); + WindowScale = width * 1f / WindowSize.Width; + } + + Console.WriteLine("Using window scale {0:F2}", WindowScale); if (Game.Settings.Game.LockMouseWindow) GrabWindowMouseFocus(); @@ -114,6 +139,26 @@ namespace OpenRA.Platforms.Default } } + internal void WindowSizeChanged() + { + // The ratio between pixels and points can change when moving between displays in OSX + // We need to recalculate our scale to account for the potential change in the actual rendered area + if (Platform.CurrentPlatform == PlatformType.OSX) + { + int width, height; + SDL.SDL_GL_GetDrawableSize(window, out width, out height); + + if (width != SurfaceSize.Width || height != SurfaceSize.Height) + { + var oldScale = WindowScale; + SurfaceSize = new Size(width, height); + WindowScale = width * 1f / WindowSize.Width; + + OnWindowScaleChanged(oldScale, WindowScale); + } + } + } + sealed class SDL2HardwareCursor : IHardwareCursor { public IntPtr Cursor { get; private set; } @@ -315,7 +360,16 @@ namespace OpenRA.Platforms.Default if (height < 0) height = 0; - OpenGL.glScissor(left, WindowSize.Height - (top + height), width, height); + var bottom = WindowSize.Height - (top + height); + if (WindowSize != SurfaceSize) + { + left = (int)Math.Round(WindowScale * left); + bottom = (int)Math.Round(WindowScale * bottom); + width = (int)Math.Round(WindowScale * width); + height = (int)Math.Round(WindowScale * height); + } + + OpenGL.glScissor(left, bottom, width, height); OpenGL.CheckGLError(); OpenGL.glEnable(OpenGL.GL_SCISSOR_TEST); OpenGL.CheckGLError(); @@ -330,7 +384,7 @@ namespace OpenRA.Platforms.Default public Bitmap TakeScreenshot() { - var rect = new Rectangle(Point.Empty, WindowSize); + var rect = new Rectangle(Point.Empty, SurfaceSize); var bitmap = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var data = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); @@ -362,7 +416,7 @@ namespace OpenRA.Platforms.Default public void PumpInput(IInputHandler inputHandler) { VerifyThreadAffinity(); - input.PumpInput(inputHandler); + input.PumpInput(this, inputHandler); } public string GetClipboardText() diff --git a/OpenRA.Platforms.Default/Sdl2Input.cs b/OpenRA.Platforms.Default/Sdl2Input.cs index 012685892d..dd19d34467 100644 --- a/OpenRA.Platforms.Default/Sdl2Input.cs +++ b/OpenRA.Platforms.Default/Sdl2Input.cs @@ -40,7 +40,7 @@ namespace OpenRA.Platforms.Default | ((raw & (int)SDL.SDL_Keymod.KMOD_SHIFT) != 0 ? Modifiers.Shift : 0); } - public void PumpInput(IInputHandler inputHandler) + public void PumpInput(Sdl2GraphicsDevice device, IInputHandler inputHandler) { var mods = MakeModifiers((int)SDL.SDL_GetModState()); var scrollDelta = 0; @@ -67,6 +67,11 @@ namespace OpenRA.Platforms.Default case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED: Game.HasInputFocus = true; break; + + // Triggered when moving between displays with different DPI settings + case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED: + device.WindowSizeChanged(); + break; } break; From 5e7c816257324cefffb018a15119d2f13a813825 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 23 Dec 2016 20:41:52 +0000 Subject: [PATCH 4/7] Add support for font scaling. --- OpenRA.Game/Graphics/SpriteFont.cs | 31 ++++++++++++++++++++---------- OpenRA.Game/Primitives/Cache.cs | 2 ++ OpenRA.Game/Renderer.cs | 9 ++++++++- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/OpenRA.Game/Graphics/SpriteFont.cs b/OpenRA.Game/Graphics/SpriteFont.cs index 77dda6180c..5a67268cc3 100644 --- a/OpenRA.Game/Graphics/SpriteFont.cs +++ b/OpenRA.Game/Graphics/SpriteFont.cs @@ -29,27 +29,37 @@ namespace OpenRA.Graphics readonly Face face; readonly Cache, GlyphInfo> glyphs; - public SpriteFont(string name, byte[] data, int size, SheetBuilder builder) + float deviceScale; + + public SpriteFont(string name, byte[] data, int size, float scale, SheetBuilder builder) { if (builder.Type != SheetType.BGRA) throw new ArgumentException("The sheet builder must create BGRA sheets.", "builder"); + deviceScale = scale; this.size = size; this.builder = builder; face = new Face(Library, data, 0); - face.SetPixelSizes((uint)size, (uint)size); + face.SetPixelSizes((uint)(size * deviceScale), (uint)(size * deviceScale)); glyphs = new Cache, GlyphInfo>(CreateGlyph, Pair.EqualityComparer); // PERF: Cache these delegates for Measure calls. Func characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance; - lineWidth = line => line.Sum(characterWidth); + lineWidth = line => line.Sum(characterWidth) / deviceScale; PrecacheColor(Color.White, name); PrecacheColor(Color.Red, name); } + public void SetScale(float scale) + { + deviceScale = scale; + face.SetPixelSizes((uint)(size * deviceScale), (uint)(size * deviceScale)); + glyphs.Clear(); + } + void PrecacheColor(Color c, string name) { using (new PerfTimer("PrecacheColor {0} {1}px {2}".F(name, size, c.Name))) @@ -75,9 +85,10 @@ namespace OpenRA.Graphics var g = glyphs[Pair.New(s, c)]; Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite, new float2( - (int)Math.Round(p.X + g.Offset.X, 0), - p.Y + g.Offset.Y)); - p.X += g.Advance; + (int)Math.Round(p.X * deviceScale + g.Offset.X, 0) / deviceScale, + p.Y + g.Offset.Y / deviceScale), + g.Sprite.Size / deviceScale); + p.X += g.Advance / deviceScale; } } @@ -85,10 +96,10 @@ namespace OpenRA.Graphics { if (offset > 0) { - DrawText(text, location + new float2(-offset, 0), bg); - DrawText(text, location + new float2(offset, 0), bg); - DrawText(text, location + new float2(0, -offset), bg); - DrawText(text, location + new float2(0, offset), bg); + DrawText(text, location + new float2(-offset / deviceScale, 0), bg); + DrawText(text, location + new float2(offset / deviceScale, 0), bg); + DrawText(text, location + new float2(0, -offset / deviceScale), bg); + DrawText(text, location + new float2(0, offset / deviceScale), bg); } DrawText(text, location, fg); diff --git a/OpenRA.Game/Primitives/Cache.cs b/OpenRA.Game/Primitives/Cache.cs index 700991cc73..d162374b80 100644 --- a/OpenRA.Game/Primitives/Cache.cs +++ b/OpenRA.Game/Primitives/Cache.cs @@ -39,6 +39,8 @@ namespace OpenRA.Primitives public bool ContainsKey(T key) { return cache.ContainsKey(key); } public bool TryGetValue(T key, out U value) { return cache.TryGetValue(key, out value); } public int Count { get { return cache.Count; } } + public void Clear() { cache.Clear(); } + public ICollection Keys { get { return cache.Keys; } } public ICollection Values { get { return cache.Values; } } public IEnumerator> GetEnumerator() { return cache.GetEnumerator(); } diff --git a/OpenRA.Game/Renderer.cs b/OpenRA.Game/Renderer.cs index db29ea8543..fdddfbd0b2 100644 --- a/OpenRA.Game/Renderer.cs +++ b/OpenRA.Game/Renderer.cs @@ -86,8 +86,15 @@ namespace OpenRA fontSheetBuilder.Dispose(); fontSheetBuilder = new SheetBuilder(SheetType.BGRA); Fonts = modData.Manifest.Fonts.ToDictionary(x => x.Key, - x => new SpriteFont(x.Value.First, modData.DefaultFileSystem.Open(x.Value.First).ReadAllBytes(), x.Value.Second, Device.WindowScale, fontSheetBuilder)).AsReadOnly(); + x => new SpriteFont(x.Value.First, modData.DefaultFileSystem.Open(x.Value.First).ReadAllBytes(), + x.Value.Second, Device.WindowScale, fontSheetBuilder)).AsReadOnly(); } + + Device.OnWindowScaleChanged += (before, after) => + { + foreach (var f in Fonts) + f.Value.SetScale(after); + }; } public void InitializeDepthBuffer(MapGrid mapGrid) From 87b894ad8e03bfd5906e5ea2d9a93688117d5dbd Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 24 Dec 2016 01:11:10 +0000 Subject: [PATCH 5/7] Add support for the windows DPI scaling. --- .../Sdl2GraphicsDevice.cs | 21 +++++++++++++++---- OpenRA.Platforms.Default/Sdl2Input.cs | 18 ++++++++++++---- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs index ae368bb51b..51b51f6293 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs @@ -31,11 +31,18 @@ namespace OpenRA.Platforms.Default internal Size SurfaceSize { get; private set; } public event Action OnWindowScaleChanged = (before, after) => { }; + [DllImport("user32.dll")] + static extern bool SetProcessDPIAware(); + public Sdl2GraphicsDevice(Size windowSize, WindowMode windowMode) { Console.WriteLine("Using SDL 2 with OpenGL renderer"); WindowSize = windowSize; + // Disable legacy scaling on Windows + if (Platform.CurrentPlatform == PlatformType.Windows) + SetProcessDPIAware(); + SDL.SDL_Init(SDL.SDL_INIT_NOPARACHUTE | SDL.SDL_INIT_VIDEO); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1); SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_RED_SIZE, 8); @@ -55,10 +62,7 @@ namespace OpenRA.Platforms.Default Console.WriteLine("Using resolution: {0}x{1}", WindowSize.Width, WindowSize.Height); - var windowFlags = SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL; - if (Platform.CurrentPlatform == PlatformType.OSX) - windowFlags |= SDL.SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI; - + var windowFlags = SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL.SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI; window = SDL.SDL_CreateWindow("OpenRA", SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED, WindowSize.Width, WindowSize.Height, windowFlags); @@ -75,6 +79,15 @@ namespace OpenRA.Platforms.Default SurfaceSize = new Size(width, height); WindowScale = width * 1f / WindowSize.Width; } + else if (Platform.CurrentPlatform == PlatformType.Windows) + { + float ddpi, hdpi, vdpi; + if (SDL.SDL_GetDisplayDPI(0, out ddpi, out hdpi, out vdpi) == 0) + { + WindowScale = ddpi / 96; + WindowSize = new Size((int)(SurfaceSize.Width / WindowScale), (int)(SurfaceSize.Height / WindowScale)); + } + } Console.WriteLine("Using window scale {0:F2}", WindowScale); diff --git a/OpenRA.Platforms.Default/Sdl2Input.cs b/OpenRA.Platforms.Default/Sdl2Input.cs index dd19d34467..f69b5d76a8 100644 --- a/OpenRA.Platforms.Default/Sdl2Input.cs +++ b/OpenRA.Platforms.Default/Sdl2Input.cs @@ -40,6 +40,16 @@ namespace OpenRA.Platforms.Default | ((raw & (int)SDL.SDL_Keymod.KMOD_SHIFT) != 0 ? Modifiers.Shift : 0); } + int2 EventPosition(Sdl2GraphicsDevice device, int x, int y) + { + // On Windows and Linux (X11) events are given in surface coordinates + // These must be scaled to our effective window coordinates + if (Platform.CurrentPlatform != PlatformType.OSX && device.WindowSize != device.SurfaceSize) + return new int2((int)(x / device.WindowScale), (int)(y / device.WindowScale)); + + return new int2(x, y); + } + public void PumpInput(Sdl2GraphicsDevice device, IInputHandler inputHandler) { var mods = MakeModifiers((int)SDL.SDL_GetModState()); @@ -88,8 +98,7 @@ namespace OpenRA.Platforms.Default var button = MakeButton(e.button.button); lastButtonBits |= button; - var pos = new int2(e.button.x, e.button.y); - + var pos = EventPosition(device, e.button.x, e.button.y); inputHandler.OnMouseInput(new MouseInput( MouseInputEvent.Down, button, scrollDelta, pos, mods, MultiTapDetection.DetectFromMouse(e.button.button, pos))); @@ -108,7 +117,7 @@ namespace OpenRA.Platforms.Default var button = MakeButton(e.button.button); lastButtonBits &= ~button; - var pos = new int2(e.button.x, e.button.y); + var pos = EventPosition(device, e.button.x, e.button.y); inputHandler.OnMouseInput(new MouseInput( MouseInputEvent.Up, button, scrollDelta, pos, mods, MultiTapDetection.InfoFromMouse(e.button.button))); @@ -118,9 +127,10 @@ namespace OpenRA.Platforms.Default case SDL.SDL_EventType.SDL_MOUSEMOTION: { + var pos = EventPosition(device, e.motion.x, e.motion.y); pendingMotion = new MouseInput( MouseInputEvent.Move, lastButtonBits, scrollDelta, - new int2(e.motion.x, e.motion.y), mods, 0); + pos, mods, 0); break; } From fb451fb20374fdf5a3d636e75116daf41aeeecbc Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 24 Dec 2016 01:41:59 +0000 Subject: [PATCH 6/7] Add an OPENRA_DISPLAY_SCALE environment variable for linux. --- OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs index 51b51f6293..7b743e2aaa 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs @@ -88,6 +88,16 @@ namespace OpenRA.Platforms.Default WindowSize = new Size((int)(SurfaceSize.Width / WindowScale), (int)(SurfaceSize.Height / WindowScale)); } } + else + { + float scale = 1; + var scaleVariable = Environment.GetEnvironmentVariable("OPENRA_DISPLAY_SCALE"); + if (scaleVariable != null && float.TryParse(scaleVariable, out scale)) + { + WindowScale = scale; + WindowSize = new Size((int)(SurfaceSize.Width / WindowScale), (int)(SurfaceSize.Height / WindowScale)); + } + } Console.WriteLine("Using window scale {0:F2}", WindowScale); From 18d2454306bb1be5196b86888da6b45608f5ddf1 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 24 Dec 2016 01:33:29 +0000 Subject: [PATCH 7/7] Pixel double hardware cursors on non-OSX. --- .../Sdl2GraphicsDevice.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs index 7b743e2aaa..01466c0c79 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs @@ -141,6 +141,29 @@ namespace OpenRA.Platforms.Default VerifyThreadAffinity(); try { + // Pixel double the cursor on non-OSX if the window scale is large enough + // OSX does this for us automatically + if (Platform.CurrentPlatform != PlatformType.OSX && WindowScale > 1.5) + { + var scaledData = new byte[4 * data.Length]; + for (var y = 0; y < size.Height * 4; y += 4) + { + for (var x = 0; x < size.Width * 4; x += 4) + { + var a = 4 * (y * size.Width + x); + var b = 4 * ((y + 1) * size.Width + x); + for (var i = 0; i < 4; i++) + { + scaledData[2 * a + i] = scaledData[2 * a + 4 + i] = data[a + i]; + scaledData[2 * b + i] = scaledData[2 * b + 4 + i] = data[b + i]; + } + } + } + + size = new Size(2 * size.Width, 2 * size.Height); + data = scaledData; + } + return new SDL2HardwareCursor(size, data, hotspot); } catch (Exception ex)