Rework multi-resolution sprite handling:

- Sprite.Bounds now refers to rectangles in the source image.
  Use this when copying pixels, etc.
- Sprite.Size now refers to sizes in effective pixel coordinates.
  Use this when rendering.
- Sheet.DPIScale has been removed.
- "Density" term is introduced to refer to the number of artwork
  pixels per effective pixel.
This commit is contained in:
Paul Chote
2020-02-11 21:29:15 +00:00
committed by abcdefg30
parent c0ece00c4b
commit de4a7cecf0
15 changed files with 106 additions and 91 deletions

View File

@@ -54,10 +54,10 @@ namespace OpenRA.Graphics
public static IReadOnlyDictionary<string, Collection> Collections { get; private set; } public static IReadOnlyDictionary<string, Collection> Collections { get; private set; }
static Dictionary<string, Collection> collections; static Dictionary<string, Collection> collections;
static Dictionary<string, Sheet> cachedSheets; static Dictionary<string, Pair<Sheet, int>> cachedSheets;
static Dictionary<string, Dictionary<string, Sprite>> cachedSprites; static Dictionary<string, Dictionary<string, Sprite>> cachedSprites;
static Dictionary<string, Sprite[]> cachedPanelSprites; static Dictionary<string, Sprite[]> cachedPanelSprites;
static Dictionary<Collection, Sheet> cachedCollectionSheets; static Dictionary<Collection, Pair<Sheet, int>> cachedCollectionSheets;
static IReadOnlyFileSystem fileSystem; static IReadOnlyFileSystem fileSystem;
static float dpiScale = 1; static float dpiScale = 1;
@@ -72,10 +72,10 @@ namespace OpenRA.Graphics
fileSystem = modData.DefaultFileSystem; fileSystem = modData.DefaultFileSystem;
collections = new Dictionary<string, Collection>(); collections = new Dictionary<string, Collection>();
cachedSheets = new Dictionary<string, Sheet>(); cachedSheets = new Dictionary<string, Pair<Sheet, int>>();
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>(); cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
cachedPanelSprites = new Dictionary<string, Sprite[]>(); cachedPanelSprites = new Dictionary<string, Sprite[]>();
cachedCollectionSheets = new Dictionary<Collection, Sheet>(); cachedCollectionSheets = new Dictionary<Collection, Pair<Sheet, int>>();
Collections = new ReadOnlyDictionary<string, Collection>(collections); Collections = new ReadOnlyDictionary<string, Collection>(collections);
@@ -91,7 +91,7 @@ namespace OpenRA.Graphics
{ {
if (cachedSheets != null) if (cachedSheets != null)
foreach (var sheet in cachedSheets.Values) foreach (var sheet in cachedSheets.Values)
sheet.Dispose(); sheet.First.Dispose();
collections = null; collections = null;
cachedSheets = null; cachedSheets = null;
@@ -108,47 +108,43 @@ namespace OpenRA.Graphics
collections.Add(name, FieldLoader.Load<Collection>(yaml)); collections.Add(name, FieldLoader.Load<Collection>(yaml));
} }
static Sheet SheetForCollection(Collection c) static Pair<Sheet, int> SheetForCollection(Collection c)
{ {
Sheet sheet; Pair<Sheet, int> sheetDensity;
// Outer cache avoids recalculating image names // Outer cache avoids recalculating image names
if (!cachedCollectionSheets.TryGetValue(c, out sheet)) if (!cachedCollectionSheets.TryGetValue(c, out sheetDensity))
{ {
string sheetImage; var image = c.Image;
float sheetScale; var density = 1;
if (dpiScale > 2 && !string.IsNullOrEmpty(c.Image3x)) if (dpiScale > 2 && !string.IsNullOrEmpty(c.Image3x))
{ {
sheetImage = c.Image3x; image = c.Image3x;
sheetScale = 3; density = 3;
} }
else if (dpiScale > 1 && !string.IsNullOrEmpty(c.Image2x)) else if (dpiScale > 1 && !string.IsNullOrEmpty(c.Image2x))
{ {
sheetImage = c.Image2x; image = c.Image2x;
sheetScale = 2; density = 2;
}
else
{
sheetImage = c.Image;
sheetScale = 1;
} }
// Inner cache makes sure we share sheets between collections // Inner cache makes sure we share sheets between collections
if (!cachedSheets.TryGetValue(sheetImage, out sheet)) if (!cachedSheets.TryGetValue(image, out sheetDensity))
{ {
using (var stream = fileSystem.Open(sheetImage)) Sheet sheet;
using (var stream = fileSystem.Open(image))
sheet = new Sheet(SheetType.BGRA, stream); sheet = new Sheet(SheetType.BGRA, stream);
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear; sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
sheet.DPIScale = sheetScale;
cachedSheets.Add(sheetImage, sheet); sheetDensity = Pair.New(sheet, density);
cachedSheets.Add(image, sheetDensity);
} }
cachedCollectionSheets.Add(c, sheet); cachedCollectionSheets.Add(c, sheetDensity);
} }
return sheet; return sheetDensity;
} }
public static Sprite GetImage(string collectionName, string imageName) public static Sprite GetImage(string collectionName, string imageName)
@@ -174,14 +170,14 @@ namespace OpenRA.Graphics
return null; return null;
// Cache the sprite // Cache the sprite
var sheet = SheetForCollection(collection); var sheetDensity = SheetForCollection(collection);
if (cachedCollection == null) if (cachedCollection == null)
{ {
cachedCollection = new Dictionary<string, Sprite>(); cachedCollection = new Dictionary<string, Sprite>();
cachedSprites.Add(collectionName, cachedCollection); cachedSprites.Add(collectionName, cachedCollection);
} }
var image = new Sprite(sheet, mi, TextureChannel.RGBA); var image = new Sprite(sheetDensity.First, sheetDensity.Second * mi, TextureChannel.RGBA, 1f / sheetDensity.Second);
cachedCollection.Add(imageName, image); cachedCollection.Add(imageName, image);
return image; return image;
@@ -214,7 +210,7 @@ namespace OpenRA.Graphics
} }
// Cache the sprites // Cache the sprites
var sheet = SheetForCollection(collection); var sheetDensity = SheetForCollection(collection);
var pr = collection.PanelRegion; var pr = collection.PanelRegion;
var ps = collection.PanelSides; var ps = collection.PanelSides;
@@ -231,7 +227,7 @@ namespace OpenRA.Graphics
Pair.New(PanelSides.Bottom | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3] + pr[5], pr[6], pr[7])) Pair.New(PanelSides.Bottom | PanelSides.Right, new Rectangle(pr[0] + pr[2] + pr[4], pr[1] + pr[3] + pr[5], pr[6], pr[7]))
}; };
sprites = sides.Select(x => ps.HasSide(x.First) ? new Sprite(sheet, x.Second, TextureChannel.RGBA) : null) sprites = sides.Select(x => ps.HasSide(x.First) ? new Sprite(sheetDensity.First, sheetDensity.Second * x.Second, TextureChannel.RGBA, 1f / sheetDensity.Second) : null)
.ToArray(); .ToArray();
} }
else else

View File

@@ -25,7 +25,6 @@ namespace OpenRA.Graphics
public readonly Size Size; public readonly Size Size;
public readonly SheetType Type; public readonly SheetType Type;
public float DPIScale = 1f;
public byte[] GetData() public byte[] GetData()
{ {

View File

@@ -88,9 +88,9 @@ namespace OpenRA.Graphics
return rect; return rect;
} }
public Sprite Add(Png src) public Sprite Add(Png src, float scale = 1f)
{ {
var rect = Allocate(new Size(src.Width, src.Height)); var rect = Allocate(new Size(src.Width, src.Height), scale);
Util.FastCopyIntoSprite(rect, src); Util.FastCopyIntoSprite(rect, src);
current.CommitBufferedData(); current.CommitBufferedData();
return rect; return rect;
@@ -114,8 +114,8 @@ namespace OpenRA.Graphics
return (TextureChannel)nextChannel; return (TextureChannel)nextChannel;
} }
public Sprite Allocate(Size imageSize) { return Allocate(imageSize, 0, float3.Zero); } public Sprite Allocate(Size imageSize, float scale = 1f) { return Allocate(imageSize, 0, float3.Zero, scale); }
public Sprite Allocate(Size imageSize, float zRamp, float3 spriteOffset) public Sprite Allocate(Size imageSize, float zRamp, float3 spriteOffset, float scale = 1f)
{ {
if (imageSize.Width + p.X + margin > current.Size.Width) if (imageSize.Width + p.X + margin > current.Size.Width)
{ {
@@ -143,7 +143,7 @@ namespace OpenRA.Graphics
p = int2.Zero; p = int2.Zero;
} }
var rect = new Sprite(current, new Rectangle(p.X + margin, p.Y + margin, imageSize.Width, imageSize.Height), zRamp, spriteOffset, channel, BlendMode.Alpha); var rect = new Sprite(current, new Rectangle(p.X + margin, p.Y + margin, imageSize.Width, imageSize.Height), zRamp, spriteOffset, channel, BlendMode.Alpha, scale);
p += new int2(imageSize.Width + margin, 0); p += new int2(imageSize.Width + margin, 0);
return rect; return rect;

View File

@@ -26,25 +26,25 @@ namespace OpenRA.Graphics
public readonly float3 FractionalOffset; public readonly float3 FractionalOffset;
public readonly float Top, Left, Bottom, Right; public readonly float Top, Left, Bottom, Right;
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel) public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel, float scale = 1)
: this(sheet, bounds, 0, float2.Zero, channel) { } : this(sheet, bounds, 0, float2.Zero, channel, BlendMode.Alpha, scale) { }
public Sprite(Sheet sheet, Rectangle bounds, float zRamp, float3 offset, TextureChannel channel, BlendMode blendMode = BlendMode.Alpha) public Sprite(Sheet sheet, Rectangle bounds, float zRamp, float3 offset, TextureChannel channel, BlendMode blendMode = BlendMode.Alpha, float scale = 1f)
{ {
Sheet = sheet; Sheet = sheet;
Bounds = bounds; Bounds = bounds;
Offset = offset; Offset = offset;
ZRamp = zRamp; ZRamp = zRamp;
Channel = channel; Channel = channel;
Size = new float3(bounds.Size.Width, bounds.Size.Height, bounds.Size.Height * zRamp); Size = scale * new float3(bounds.Size.Width, bounds.Size.Height, bounds.Size.Height * zRamp);
BlendMode = blendMode; BlendMode = blendMode;
FractionalOffset = Size.Z != 0 ? offset / Size : FractionalOffset = Size.Z != 0 ? offset / Size :
new float3(offset.X / Size.X, offset.Y / Size.Y, 0); new float3(offset.X / Size.X, offset.Y / Size.Y, 0);
Left = (float)Math.Min(bounds.Left, bounds.Right) * sheet.DPIScale / sheet.Size.Width; Left = (float)Math.Min(bounds.Left, bounds.Right) / sheet.Size.Width;
Top = (float)Math.Min(bounds.Top, bounds.Bottom) * sheet.DPIScale / sheet.Size.Height; Top = (float)Math.Min(bounds.Top, bounds.Bottom) / sheet.Size.Height;
Right = (float)Math.Max(bounds.Left, bounds.Right) * sheet.DPIScale / sheet.Size.Width; Right = (float)Math.Max(bounds.Left, bounds.Right) / sheet.Size.Width;
Bottom = (float)Math.Max(bounds.Top, bounds.Bottom) * sheet.DPIScale / sheet.Size.Height; Bottom = (float)Math.Max(bounds.Top, bounds.Bottom) / sheet.Size.Height;
} }
} }
@@ -61,10 +61,10 @@ namespace OpenRA.Graphics
SecondarySheet = secondarySheet; SecondarySheet = secondarySheet;
SecondaryBounds = secondaryBounds; SecondaryBounds = secondaryBounds;
SecondaryChannel = secondaryChannel; SecondaryChannel = secondaryChannel;
SecondaryLeft = (float)Math.Min(secondaryBounds.Left, secondaryBounds.Right) * secondarySheet.DPIScale / s.Sheet.Size.Width; SecondaryLeft = (float)Math.Min(secondaryBounds.Left, secondaryBounds.Right) / s.Sheet.Size.Width;
SecondaryTop = (float)Math.Min(secondaryBounds.Top, secondaryBounds.Bottom) * secondarySheet.DPIScale / s.Sheet.Size.Height; SecondaryTop = (float)Math.Min(secondaryBounds.Top, secondaryBounds.Bottom) / s.Sheet.Size.Height;
SecondaryRight = (float)Math.Max(secondaryBounds.Left, secondaryBounds.Right) * secondarySheet.DPIScale / s.Sheet.Size.Width; SecondaryRight = (float)Math.Max(secondaryBounds.Left, secondaryBounds.Right) / s.Sheet.Size.Width;
SecondaryBottom = (float)Math.Max(secondaryBounds.Top, secondaryBounds.Bottom) * secondarySheet.DPIScale / s.Sheet.Size.Height; SecondaryBottom = (float)Math.Max(secondaryBounds.Top, secondaryBounds.Bottom) / s.Sheet.Size.Height;
} }
} }

View File

@@ -117,6 +117,8 @@ namespace OpenRA.Primitives
return rect == Intersect(this, rect); return rect == Intersect(this, rect);
} }
public static Rectangle operator *(int a, Rectangle b) { return new Rectangle(a * b.X, a * b.Y, a * b.Width, a * b.Height); }
public override string ToString() public override string ToString()
{ {
return string.Format("{{X={0},Y={1},Width={2},Height={3}}}", X, Y, Width, Height); return string.Format("{{X={0},Y={1},Width={2},Height={3}}}", X, Y, Width, Height);

View File

@@ -31,6 +31,7 @@ namespace OpenRA.Mods.Cnc
float2 loadingPos, versionPos; float2 loadingPos, versionPos;
Sheet lastSheet; Sheet lastSheet;
int lastDensity;
Size lastResolution; Size lastResolution;
IReadOnlyDictionary<string, SpriteFont> lastFonts; IReadOnlyDictionary<string, SpriteFont> lastFonts;
@@ -41,31 +42,32 @@ namespace OpenRA.Mods.Cnc
versionText = modData.Manifest.Metadata.Version; versionText = modData.Manifest.Metadata.Version;
} }
public override void DisplayInner(Renderer r, Sheet s) public override void DisplayInner(Renderer r, Sheet s, int density)
{ {
if (s != lastSheet) if (s != lastSheet || density != lastDensity)
{ {
lastSheet = s; lastSheet = s;
lastDensity = density;
border = new[] border = new[]
{ {
new Sprite(s, new Rectangle(129, 129, 32, 32), TextureChannel.RGBA), CreateSprite(s, density, new Rectangle(129, 129, 32, 32)),
new Sprite(s, new Rectangle(161, 129, 62, 32), TextureChannel.RGBA), CreateSprite(s, density, new Rectangle(161, 129, 62, 32)),
new Sprite(s, new Rectangle(223, 129, 32, 32), TextureChannel.RGBA), CreateSprite(s, density, new Rectangle(223, 129, 32, 32)),
new Sprite(s, new Rectangle(129, 161, 32, 62), TextureChannel.RGBA), CreateSprite(s, density, new Rectangle(129, 161, 32, 62)),
null, null,
new Sprite(s, new Rectangle(223, 161, 32, 62), TextureChannel.RGBA), CreateSprite(s, density, new Rectangle(223, 161, 32, 62)),
new Sprite(s, new Rectangle(129, 223, 32, 32), TextureChannel.RGBA), CreateSprite(s, density, new Rectangle(129, 223, 32, 32)),
new Sprite(s, new Rectangle(161, 223, 62, 32), TextureChannel.RGBA), CreateSprite(s, density, new Rectangle(161, 223, 62, 32)),
new Sprite(s, new Rectangle(223, 223, 32, 32), TextureChannel.RGBA) CreateSprite(s, density, new Rectangle(223, 223, 32, 32))
}; };
nodLogo = new Sprite(s, new Rectangle(0, 256, 256, 256), TextureChannel.RGBA); nodLogo = CreateSprite(s, density, new Rectangle(0, 256, 256, 256));
gdiLogo = new Sprite(s, new Rectangle(256, 256, 256, 256), TextureChannel.RGBA); gdiLogo = CreateSprite(s, density, new Rectangle(256, 256, 256, 256));
evaLogo = new Sprite(s, new Rectangle(769, 320, 128, 64), TextureChannel.RGBA); evaLogo = CreateSprite(s, density, new Rectangle(769, 320, 128, 64));
brightBlock = new Sprite(s, new Rectangle(777, 385, 16, 35), TextureChannel.RGBA); brightBlock = CreateSprite(s, density, new Rectangle(777, 385, 16, 35));
dimBlock = new Sprite(s, new Rectangle(794, 385, 16, 35), TextureChannel.RGBA); dimBlock = CreateSprite(s, density, new Rectangle(794, 385, 16, 35));
} }
if (r.Resolution != lastResolution) if (r.Resolution != lastResolution)

View File

@@ -24,6 +24,7 @@ namespace OpenRA.Mods.Common.LoadScreens
Sprite stripe, logo; Sprite stripe, logo;
Sheet lastSheet; Sheet lastSheet;
int lastDensity;
Size lastResolution; Size lastResolution;
string[] messages = { "Loading..." }; string[] messages = { "Loading..." };
@@ -36,13 +37,14 @@ namespace OpenRA.Mods.Common.LoadScreens
messages = info["Text"].Split(','); messages = info["Text"].Split(',');
} }
public override void DisplayInner(Renderer r, Sheet s) public override void DisplayInner(Renderer r, Sheet s, int density)
{ {
if (s != lastSheet) if (s != lastSheet || density != lastDensity)
{ {
lastSheet = s; lastSheet = s;
logo = new Sprite(s, new Rectangle(0, 0, 256, 256), TextureChannel.RGBA); lastDensity = density;
stripe = new Sprite(s, new Rectangle(258, 0, 253, 256), TextureChannel.RGBA); logo = CreateSprite(s, density, new Rectangle(0, 0, 256, 256));
stripe = CreateSprite(s, density, new Rectangle(258, 0, 253, 256));
} }
if (r.Resolution != lastResolution) if (r.Resolution != lastResolution)

View File

@@ -25,14 +25,16 @@ namespace OpenRA.Mods.Common.LoadScreens
Rectangle bounds; Rectangle bounds;
Sheet lastSheet; Sheet lastSheet;
int lastDensity;
Size lastResolution; Size lastResolution;
public override void DisplayInner(Renderer r, Sheet s) public override void DisplayInner(Renderer r, Sheet s, int density)
{ {
if (s != lastSheet) if (s != lastSheet || density != lastDensity)
{ {
lastSheet = s; lastSheet = s;
sprite = new Sprite(s, new Rectangle(0, 0, 1024, 480), TextureChannel.RGBA); lastDensity = density;
sprite = CreateSprite(s, density, new Rectangle(0, 0, 1024, 480));
} }
if (r.Resolution != lastResolution) if (r.Resolution != lastResolution)

View File

@@ -12,6 +12,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Mods.Common.LoadScreens namespace OpenRA.Mods.Common.LoadScreens
{ {
@@ -21,7 +22,9 @@ namespace OpenRA.Mods.Common.LoadScreens
protected Dictionary<string, string> Info { get; private set; } protected Dictionary<string, string> Info { get; private set; }
float dpiScale = 1; float dpiScale = 1;
Sheet sheet; Sheet sheet;
int density;
public override void Init(ModData modData, Dictionary<string, string> info) public override void Init(ModData modData, Dictionary<string, string> info)
{ {
@@ -29,7 +32,7 @@ namespace OpenRA.Mods.Common.LoadScreens
Info = info; Info = info;
} }
public abstract void DisplayInner(Renderer r, Sheet s); public abstract void DisplayInner(Renderer r, Sheet s, int density);
public override void Display() public override void Display()
{ {
@@ -58,33 +61,37 @@ namespace OpenRA.Mods.Common.LoadScreens
if (sheet == null && Info.ContainsKey("Image")) if (sheet == null && Info.ContainsKey("Image"))
{ {
var key = "Image"; var key = "Image";
float sheetScale = 1; density = 1;
if (dpiScale > 2 && Info.ContainsKey("Image3x")) if (dpiScale > 2 && Info.ContainsKey("Image3x"))
{ {
key = "Image3x"; key = "Image3x";
sheetScale = 3; density = 3;
} }
else if (dpiScale > 1 && Info.ContainsKey("Image2x")) else if (dpiScale > 1 && Info.ContainsKey("Image2x"))
{ {
key = "Image2x"; key = "Image2x";
sheetScale = 2; density = 2;
} }
using (var stream = ModData.DefaultFileSystem.Open(Info[key])) using (var stream = ModData.DefaultFileSystem.Open(Info[key]))
{ {
sheet = new Sheet(SheetType.BGRA, stream); sheet = new Sheet(SheetType.BGRA, stream);
sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear; sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
sheet.DPIScale = sheetScale;
} }
} }
Game.Renderer.BeginUI(); Game.Renderer.BeginUI();
DisplayInner(Game.Renderer, sheet); DisplayInner(Game.Renderer, sheet, density);
Game.Renderer.EndFrame(new NullInputHandler()); Game.Renderer.EndFrame(new NullInputHandler());
lastUpdate.Restart(); lastUpdate.Restart();
} }
protected static Sprite CreateSprite(Sheet s, int density, Rectangle rect)
{
return new Sprite(s, density * rect, TextureChannel.RGBA, 1f / density);
}
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if (disposing && sheet != null) if (disposing && sheet != null)

View File

@@ -164,8 +164,8 @@ namespace OpenRA.Mods.Common.Widgets
Game.Renderer.RgbaSpriteRenderer.DrawSprite(mixerSprite, RenderOrigin, new float2(RenderBounds.Size)); Game.Renderer.RgbaSpriteRenderer.DrawSprite(mixerSprite, RenderOrigin, new float2(RenderBounds.Size));
var sprite = ChromeProvider.GetImage("lobby-bits", "colorpicker"); var sprite = ChromeProvider.GetImage("lobby-bits", "colorpicker");
var pos = RenderOrigin + PxFromValue() - new int2(sprite.Bounds.Width, sprite.Bounds.Height) / 2; var pos = RenderOrigin + PxFromValue() - new int2((int)sprite.Size.X, (int)sprite.Size.Y) / 2;
WidgetUtils.FillEllipseWithColor(new Rectangle(pos.X + 1, pos.Y + 1, sprite.Bounds.Width - 2, sprite.Bounds.Height - 2), Color); WidgetUtils.FillEllipseWithColor(new Rectangle(pos.X + 1, pos.Y + 1, (int)sprite.Size.X - 2, (int)sprite.Size.Y - 2), Color);
Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos); Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos);
} }

View File

@@ -48,10 +48,10 @@ namespace OpenRA.Mods.Common.Widgets
var image = ChromeProvider.GetImage("scrollbar", IsDisabled() ? "down_pressed" : "down_arrow"); var image = ChromeProvider.GetImage("scrollbar", IsDisabled() ? "down_pressed" : "down_arrow");
var rb = RenderBounds; var rb = RenderBounds;
WidgetUtils.DrawRGBA(image, stateOffset + new float2((rb.Right - (rb.Height + image.Bounds.Width) / 2), rb.Top + (rb.Height - image.Bounds.Height) / 2)); WidgetUtils.DrawRGBA(image, stateOffset + new float2(rb.Right - (rb.Height + image.Size.X) / 2, rb.Top + (rb.Height - image.Size.Y) / 2));
var separator = ChromeProvider.GetImage(SeparatorCollection, SeparatorImage); var separator = ChromeProvider.GetImage(SeparatorCollection, SeparatorImage);
WidgetUtils.DrawRGBA(separator, new float2(-3, 0) + new float2(rb.Right - rb.Height + 4, rb.Top + (rb.Height - separator.Bounds.Height) / 2)); WidgetUtils.DrawRGBA(separator, stateOffset + new float2(-3, 0) + new float2(rb.Right - rb.Height + 4, rb.Top + (rb.Height - separator.Size.Y) / 2));
} }
public override Widget Clone() { return new DropDownButtonWidget(this); } public override Widget Clone() { return new DropDownButtonWidget(this); }

View File

@@ -47,7 +47,7 @@ namespace OpenRA.Mods.Common.Widgets
Game.Renderer.RgbaSpriteRenderer.DrawSprite(hueSprite, ro, new float2(rb.Size)); Game.Renderer.RgbaSpriteRenderer.DrawSprite(hueSprite, ro, new float2(rb.Size));
var sprite = ChromeProvider.GetImage("lobby-bits", "huepicker"); var sprite = ChromeProvider.GetImage("lobby-bits", "huepicker");
var pos = RenderOrigin + new int2(PxFromValue(Value).Clamp(0, rb.Width - 1) - sprite.Bounds.Width / 2, (rb.Height - sprite.Bounds.Height) / 2); var pos = RenderOrigin + new int2(PxFromValue(Value).Clamp(0, rb.Width - 1) - (int)sprite.Size.X / 2, (rb.Height - (int)sprite.Size.Y) / 2);
Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos); Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos);
} }
} }

View File

@@ -252,7 +252,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (!orderManager.LocalClient.IsObserver && orderManager.LocalClient.State == Session.ClientState.Ready) if (!orderManager.LocalClient.IsObserver && orderManager.LocalClient.State == Session.ClientState.Ready)
return; return;
var spawnSize = new float2(ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed").Bounds.Size); var spawnSize = ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed").Size.XY;
var selectedSpawn = preview.SpawnPoints var selectedSpawn = preview.SpawnPoints
.Select((sp, i) => Pair.New(mapPreview.ConvertToPreview(sp, preview.GridType), i)) .Select((sp, i) => Pair.New(mapPreview.ConvertToPreview(sp, preview.GridType), i))
.Where(a => ((a.First - mi.Location).ToFloat2() / spawnSize * 2).LengthSquared <= 1) .Where(a => ((a.First - mi.Location).ToFloat2() / spawnSize * 2).LengthSquared <= 1)

View File

@@ -191,10 +191,10 @@ namespace OpenRA.Mods.Common.Widgets
var owned = colors.ContainsKey(p); var owned = colors.ContainsKey(p);
var pos = ConvertToPreview(p, gridType); var pos = ConvertToPreview(p, gridType);
var sprite = owned ? spawnClaimed : spawnUnclaimed; var sprite = owned ? spawnClaimed : spawnUnclaimed;
var offset = new int2(sprite.Bounds.Width, sprite.Bounds.Height) / 2; var offset = sprite.Size.XY.ToInt2() / 2;
if (owned) if (owned)
WidgetUtils.FillEllipseWithColor(new Rectangle(pos.X - offset.X + 1, pos.Y - offset.Y + 1, sprite.Bounds.Width - 2, sprite.Bounds.Height - 2), colors[p]); WidgetUtils.FillEllipseWithColor(new Rectangle(pos.X - offset.X + 1, pos.Y - offset.Y + 1, (int)sprite.Size.X - 2, (int)sprite.Size.Y - 2), colors[p]);
Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos - offset); Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos - offset);
var number = Convert.ToChar('A' + spawnPoints.IndexOf(p)).ToString(); var number = Convert.ToChar('A' + spawnPoints.IndexOf(p)).ToString();

View File

@@ -47,23 +47,28 @@ namespace OpenRA.Mods.Common.Widgets
public static void FillRectWithSprite(Rectangle r, Sprite s) public static void FillRectWithSprite(Rectangle r, Sprite s)
{ {
for (var x = r.Left; x < r.Right; x += (int)s.Size.X) var scale = s.Size.X / s.Bounds.Width;
for (var y = r.Top; y < r.Bottom; y += (int)s.Size.Y) for (var x = (float)r.Left; x < r.Right; x += s.Size.X)
{
for (var y = (float)r.Top; y < r.Bottom; y += s.Size.Y)
{ {
var ss = s; var ss = s;
var left = new int2(r.Right - x, r.Bottom - y); var dx = r.Right - x;
if (left.X < (int)s.Size.X || left.Y < (int)s.Size.Y) var dy = r.Bottom - y;
if (dx < s.Size.X || dy < s.Size.Y)
{ {
var rr = new Rectangle(s.Bounds.Left, var rr = new Rectangle(
s.Bounds.Left,
s.Bounds.Top, s.Bounds.Top,
Math.Min(left.X, (int)s.Size.X), Math.Min(s.Bounds.Width, (int)(dx / scale)),
Math.Min(left.Y, (int)s.Size.Y)); Math.Min(s.Bounds.Height, (int)(dy / scale)));
ss = new Sprite(s.Sheet, rr, s.Channel); ss = new Sprite(s.Sheet, rr, s.Channel, scale);
} }
DrawRGBA(ss, new float2(x, y)); DrawRGBA(ss, new float2(x, y));
} }
} }
}
public static void FillRectWithColor(Rectangle r, Color c) public static void FillRectWithColor(Rectangle r, Color c)
{ {