Support loading sprites with pre-multiplied alpha.
This commit is contained in:
@@ -82,16 +82,16 @@ namespace OpenRA.Graphics
|
|||||||
this.margin = margin;
|
this.margin = margin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Type, frame.Size, 0, frame.Offset); }
|
public Sprite Add(ISpriteFrame frame, bool premultiplied = false) { return Add(frame.Data, frame.Type, frame.Size, 0, frame.Offset, premultiplied); }
|
||||||
public Sprite Add(byte[] src, SpriteFrameType type, Size size) { return Add(src, type, size, 0, float3.Zero); }
|
public Sprite Add(byte[] src, SpriteFrameType type, Size size, bool premultiplied = false) { return Add(src, type, size, 0, float3.Zero, premultiplied); }
|
||||||
public Sprite Add(byte[] src, SpriteFrameType type, Size size, float zRamp, in float3 spriteOffset)
|
public Sprite Add(byte[] src, SpriteFrameType type, Size size, float zRamp, in float3 spriteOffset, bool premultiplied = false)
|
||||||
{
|
{
|
||||||
// Don't bother allocating empty sprites
|
// Don't bother allocating empty sprites
|
||||||
if (size.Width == 0 || size.Height == 0)
|
if (size.Width == 0 || size.Height == 0)
|
||||||
return new Sprite(Current, Rectangle.Empty, 0, spriteOffset, CurrentChannel, BlendMode.Alpha);
|
return new Sprite(Current, Rectangle.Empty, 0, spriteOffset, CurrentChannel, BlendMode.Alpha);
|
||||||
|
|
||||||
var rect = Allocate(size, zRamp, spriteOffset);
|
var rect = Allocate(size, zRamp, spriteOffset);
|
||||||
Util.FastCopyIntoChannel(rect, src, type);
|
Util.FastCopyIntoChannel(rect, src, type, premultiplied);
|
||||||
Current.CommitBufferedData();
|
Current.CommitBufferedData();
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace OpenRA.Graphics
|
|||||||
readonly ISpriteLoader[] loaders;
|
readonly ISpriteLoader[] loaders;
|
||||||
readonly IReadOnlyFileSystem fileSystem;
|
readonly IReadOnlyFileSystem fileSystem;
|
||||||
|
|
||||||
readonly Dictionary<int, (int[] Frames, MiniYamlNode.SourceLocation Location)> spriteReservations = new();
|
readonly Dictionary<int, (int[] Frames, MiniYamlNode.SourceLocation Location, bool Premultiplied)> spriteReservations = new();
|
||||||
readonly Dictionary<int, (int[] Frames, MiniYamlNode.SourceLocation Location)> frameReservations = new();
|
readonly Dictionary<int, (int[] Frames, MiniYamlNode.SourceLocation Location)> frameReservations = new();
|
||||||
readonly Dictionary<string, List<int>> reservationsByFilename = new();
|
readonly Dictionary<string, List<int>> reservationsByFilename = new();
|
||||||
|
|
||||||
@@ -47,10 +47,10 @@ namespace OpenRA.Graphics
|
|||||||
this.loaders = loaders;
|
this.loaders = loaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ReserveSprites(string filename, IEnumerable<int> frames, MiniYamlNode.SourceLocation location)
|
public int ReserveSprites(string filename, IEnumerable<int> frames, MiniYamlNode.SourceLocation location, bool premultiplied = false)
|
||||||
{
|
{
|
||||||
var token = nextReservationToken++;
|
var token = nextReservationToken++;
|
||||||
spriteReservations[token] = (frames?.ToArray(), location);
|
spriteReservations[token] = (frames?.ToArray(), location, premultiplied);
|
||||||
reservationsByFilename.GetOrAdd(filename, _ => new List<int>()).Add(token);
|
reservationsByFilename.GetOrAdd(filename, _ => new List<int>()).Add(token);
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -91,14 +91,14 @@ namespace OpenRA.Graphics
|
|||||||
var loadedFrames = GetFrames(fileSystem, filename, loaders, out _);
|
var loadedFrames = GetFrames(fileSystem, filename, loaders, out _);
|
||||||
foreach (var token in tokens)
|
foreach (var token in tokens)
|
||||||
{
|
{
|
||||||
if (frameReservations.TryGetValue(token, out var r))
|
if (frameReservations.TryGetValue(token, out var rf))
|
||||||
{
|
{
|
||||||
if (loadedFrames != null)
|
if (loadedFrames != null)
|
||||||
{
|
{
|
||||||
if (r.Frames != null)
|
if (rf.Frames != null)
|
||||||
{
|
{
|
||||||
var resolved = new ISpriteFrame[loadedFrames.Length];
|
var resolved = new ISpriteFrame[loadedFrames.Length];
|
||||||
foreach (var i in r.Frames)
|
foreach (var i in rf.Frames)
|
||||||
resolved[i] = loadedFrames[i];
|
resolved[i] = loadedFrames[i];
|
||||||
resolvedFrames[token] = resolved;
|
resolvedFrames[token] = resolved;
|
||||||
}
|
}
|
||||||
@@ -108,26 +108,32 @@ namespace OpenRA.Graphics
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
resolvedFrames[token] = null;
|
resolvedFrames[token] = null;
|
||||||
missingFiles[token] = (filename, r.Location);
|
missingFiles[token] = (filename, rf.Location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spriteReservations.TryGetValue(token, out r))
|
if (spriteReservations.TryGetValue(token, out var rs))
|
||||||
{
|
{
|
||||||
if (loadedFrames != null)
|
if (loadedFrames != null)
|
||||||
{
|
{
|
||||||
var resolved = new Sprite[loadedFrames.Length];
|
var resolved = new Sprite[loadedFrames.Length];
|
||||||
var frames = r.Frames ?? Enumerable.Range(0, loadedFrames.Length);
|
var frames = rs.Frames ?? Enumerable.Range(0, loadedFrames.Length);
|
||||||
|
|
||||||
|
// Premultiplied and non-premultiplied sprites must be cached separately
|
||||||
|
// to cover the case where the same image is requested in both versions.
|
||||||
|
// The premultiplied sprites are stored with an index offset for efficiency
|
||||||
|
// rather than allocating a second dictionary.
|
||||||
|
var di = rs.Premultiplied ? loadedFrames.Length : 0;
|
||||||
foreach (var i in frames)
|
foreach (var i in frames)
|
||||||
resolved[i] = spriteCache.GetOrAdd(i,
|
resolved[i] = spriteCache.GetOrAdd(i + di,
|
||||||
f => SheetBuilders[SheetBuilder.FrameTypeToSheetType(loadedFrames[f].Type)].Add(loadedFrames[f]));
|
f => SheetBuilders[SheetBuilder.FrameTypeToSheetType(loadedFrames[f - di].Type)].Add(loadedFrames[f - di], rs.Premultiplied));
|
||||||
|
|
||||||
resolvedSprites[token] = resolved;
|
resolvedSprites[token] = resolved;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resolvedSprites[token] = null;
|
resolvedSprites[token] = null;
|
||||||
missingFiles[token] = (filename, r.Location);
|
missingFiles[token] = (filename, rs.Location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ namespace OpenRA.Graphics
|
|||||||
vertices[nv + 3] = new Vertex(d, r.Left, r.Bottom, sl, sb, uAttribC, tint, alpha);
|
vertices[nv + 3] = new Vertex(d, r.Left, r.Bottom, sl, sb, uAttribC, tint, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void FastCopyIntoChannel(Sprite dest, byte[] src, SpriteFrameType srcType)
|
public static void FastCopyIntoChannel(Sprite dest, byte[] src, SpriteFrameType srcType, bool premultiplied = false)
|
||||||
{
|
{
|
||||||
var destData = dest.Sheet.GetData();
|
var destData = dest.Sheet.GetData();
|
||||||
var width = dest.Bounds.Width;
|
var width = dest.Bounds.Width;
|
||||||
@@ -154,6 +154,9 @@ namespace OpenRA.Graphics
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cc = Color.FromArgb(a, r, g, b);
|
var cc = Color.FromArgb(a, r, g, b);
|
||||||
|
if (premultiplied)
|
||||||
|
data[(y + j) * destStride + x + i] = cc.ToArgb();
|
||||||
|
else
|
||||||
data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb();
|
data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user