Changes ISpriteSource.Frames to be of type IReadOnlyList<ISpriteFrame>.

- Updated implementations to return a ReadOnlyList around an array (to reduce wasted memory from exposing lists or lazy enumerators around lists).
- Protect non-public ISpriteFrame classes by making them inner classes to prevent casting.
- Added an AsReadOnly extension method for lists.
This commit is contained in:
RoosterDragon
2014-06-27 23:38:34 +01:00
parent e9ee4a1e15
commit 19072775d4
12 changed files with 248 additions and 238 deletions

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -101,7 +101,7 @@ namespace OpenRA.Editor
} }
bitmap.UnlockBits(data); bitmap.UnlockBits(data);
return new ResourceTemplate { Bitmap = bitmap, Info = info, Value = shp.Frames.Count() - 1 }; return new ResourceTemplate { Bitmap = bitmap, Info = info, Value = shp.Frames.Count - 1 };
} }
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -20,9 +20,9 @@ namespace OpenRA.Editor
{ {
public class TileSetRenderer public class TileSetRenderer
{ {
public readonly int TileSize;
public TileSet TileSet; public TileSet TileSet;
Dictionary<ushort, List<byte[]>> templates; Dictionary<ushort, List<byte[]>> templates;
public readonly int TileSize;
// Extract a square tile that the editor can render // Extract a square tile that the editor can render
byte[] ExtractSquareTile(ISpriteFrame frame) byte[] ExtractSquareTile(ISpriteFrame frame)
@@ -61,7 +61,7 @@ namespace OpenRA.Editor
if (frames != null) if (frames != null)
{ {
var ret = new List<byte[]>(); var ret = new List<byte[]>();
var srcFrames = source.Frames.ToArray(); var srcFrames = source.Frames;
foreach (var i in frames) foreach (var i in frames)
ret.Add(ExtractSquareTile(srcFrames[i])); ret.Add(ExtractSquareTile(srcFrames[i]));

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -14,6 +14,8 @@ using System.IO;
using OpenRA.Graphics; using OpenRA.Graphics;
namespace OpenRA.FileFormats namespace OpenRA.FileFormats
{
public class R8Reader : ISpriteSource
{ {
class R8Image : ISpriteFrame class R8Image : ISpriteFrame
{ {
@@ -37,7 +39,8 @@ namespace OpenRA.FileFormats
Size = new Size(width, height); Size = new Size(width, height);
Offset = new int2(width / 2 - x, height / 2 - y); Offset = new int2(width / 2 - x, height / 2 - y);
/*var imageOffset = */s.ReadInt32(); /*var imageOffset = */
s.ReadInt32();
var paletteOffset = s.ReadInt32(); var paletteOffset = s.ReadInt32();
var bpp = s.ReadUInt8(); var bpp = s.ReadUInt8();
if (bpp != 8) if (bpp != 8)
@@ -58,20 +61,20 @@ namespace OpenRA.FileFormats
} }
} }
public class R8Reader : ISpriteSource public IReadOnlyList<ISpriteFrame> Frames { get; private set; }
{
readonly List<ISpriteFrame> frames = new List<ISpriteFrame>();
public IEnumerable<ISpriteFrame> Frames { get { return frames; } }
public bool CacheWhenLoadingTileset { get { return true; } } public bool CacheWhenLoadingTileset { get { return true; } }
public readonly int ImageCount; public readonly int ImageCount;
public R8Reader(Stream stream) public R8Reader(Stream stream)
{ {
var frames = new List<R8Image>();
while (stream.Position < stream.Length) while (stream.Position < stream.Length)
{ {
frames.Add(new R8Image(stream)); frames.Add(new R8Image(stream));
ImageCount++; ImageCount++;
} }
Frames = frames.ToArray().AsReadOnly();
} }
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -9,12 +9,13 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using OpenRA.Graphics; using OpenRA.Graphics;
namespace OpenRA.FileFormats namespace OpenRA.FileFormats
{
public class ShpD2Reader : ISpriteSource
{ {
[Flags] enum FormatFlags : int [Flags] enum FormatFlags : int
{ {
@@ -82,10 +83,7 @@ namespace OpenRA.FileFormats
} }
} }
public class ShpD2Reader : ISpriteSource public IReadOnlyList<ISpriteFrame> Frames { get; private set; }
{
readonly List<ISpriteFrame> frames = new List<ISpriteFrame>();
public IEnumerable<ISpriteFrame> Frames { get { return frames; } }
public bool CacheWhenLoadingTileset { get { return false; } } public bool CacheWhenLoadingTileset { get { return false; } }
public ShpD2Reader(Stream s) public ShpD2Reader(Stream s)
@@ -103,10 +101,12 @@ namespace OpenRA.FileFormats
for (var i = 0; i < imageCount + 1; i++) for (var i = 0; i < imageCount + 1; i++)
offsets[i] = (twoByteOffset ? s.ReadUInt16() : s.ReadUInt32()) + 2; offsets[i] = (twoByteOffset ? s.ReadUInt16() : s.ReadUInt32()) + 2;
for (var i = 0; i < imageCount; i++) var frames = new Frame[imageCount];
Frames = frames.AsReadOnly();
for (var i = 0; i < frames.Length; i++)
{ {
s.Position = offsets[i]; s.Position = offsets[i];
frames.Add(new Frame(s)); frames[i] = new Frame(s);
} }
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -16,6 +16,8 @@ using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
namespace OpenRA.FileFormats namespace OpenRA.FileFormats
{
public class ShpReader : ISpriteSource
{ {
enum Format { Format20 = 0x20, Format40 = 0x40, Format80 = 0x80 } enum Format { Format20 = 0x20, Format40 = 0x40, Format80 = 0x80 }
@@ -34,6 +36,7 @@ namespace OpenRA.FileFormats
public ImageHeader RefImage; public ImageHeader RefImage;
ShpReader reader; ShpReader reader;
// Used by ShpWriter // Used by ShpWriter
public ImageHeader() { } public ImageHeader() { }
@@ -56,11 +59,7 @@ namespace OpenRA.FileFormats
} }
} }
public class ShpReader : ISpriteSource public IReadOnlyList<ISpriteFrame> Frames { get; private set; }
{
readonly List<ImageHeader> headers = new List<ImageHeader>();
Lazy<IEnumerable<ISpriteFrame>> spriteFrames;
public IEnumerable<ISpriteFrame> Frames { get { return spriteFrames.Value; } }
public bool CacheWhenLoadingTileset { get { return false; } } public bool CacheWhenLoadingTileset { get { return false; } }
public readonly Size Size; public readonly Size Size;
@@ -79,8 +78,10 @@ namespace OpenRA.FileFormats
Size = new Size(width, height); Size = new Size(width, height);
stream.Position += 4; stream.Position += 4;
for (var i = 0; i < imageCount; i++) var headers = new ImageHeader[imageCount];
headers.Add(new ImageHeader(stream, this)); Frames = headers.AsReadOnly();
for (var i = 0; i < headers.Length; i++)
headers[i] = new ImageHeader(stream, this);
// Skip eof and zero headers // Skip eof and zero headers
stream.Position += 16; stream.Position += 16;
@@ -91,7 +92,6 @@ namespace OpenRA.FileFormats
var h = headers[i]; var h = headers[i];
if (h.Format == Format.Format20) if (h.Format == Format.Format20)
h.RefImage = headers[i - 1]; h.RefImage = headers[i - 1];
else if (h.Format == Format.Format40 && !offsets.TryGetValue(h.RefOffset, out h.RefImage)) else if (h.Format == Format.Format40 && !offsets.TryGetValue(h.RefOffset, out h.RefImage))
throw new InvalidDataException("Reference doesnt point to image data {0}->{1}".F(h.FileOffset, h.RefOffset)); throw new InvalidDataException("Reference doesnt point to image data {0}->{1}".F(h.FileOffset, h.RefOffset));
} }
@@ -101,8 +101,6 @@ namespace OpenRA.FileFormats
foreach (var h in headers) foreach (var h in headers)
Decompress(h); Decompress(h);
spriteFrames = Exts.Lazy(() => headers.Cast<ISpriteFrame>());
} }
void Decompress(ImageHeader h) void Decompress(ImageHeader h)

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -8,14 +8,13 @@
*/ */
#endregion #endregion
using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
namespace OpenRA.FileFormats namespace OpenRA.FileFormats
{
public class ShpTSReader : ISpriteSource
{ {
class FrameHeader : ISpriteFrame class FrameHeader : ISpriteFrame
{ {
@@ -44,11 +43,7 @@ namespace OpenRA.FileFormats
} }
} }
public class ShpTSReader : ISpriteSource public IReadOnlyList<ISpriteFrame> Frames { get; private set; }
{
readonly List<FrameHeader> frames = new List<FrameHeader>();
Lazy<IEnumerable<ISpriteFrame>> spriteFrames;
public IEnumerable<ISpriteFrame> Frames { get { return spriteFrames.Value; } }
public bool CacheWhenLoadingTileset { get { return false; } } public bool CacheWhenLoadingTileset { get { return false; } }
public ShpTSReader(Stream stream) public ShpTSReader(Stream stream)
@@ -59,8 +54,10 @@ namespace OpenRA.FileFormats
var size = new Size(width, height); var size = new Size(width, height);
var frameCount = stream.ReadUInt16(); var frameCount = stream.ReadUInt16();
for (var i = 0; i < frameCount; i++) var frames = new FrameHeader[frameCount];
frames.Add(new FrameHeader(stream, size)); Frames = frames.AsReadOnly();
for (var i = 0; i < frames.Length; i++)
frames[i] = new FrameHeader(stream, size);
for (var i = 0; i < frameCount; i++) for (var i = 0; i < frameCount; i++)
{ {
@@ -70,14 +67,16 @@ namespace OpenRA.FileFormats
stream.Position = f.FileOffset; stream.Position = f.FileOffset;
var frameSize = f.Size.Width * f.Size.Height;
// Uncompressed // Uncompressed
if (f.Format == 1 || f.Format == 0) if (f.Format == 1 || f.Format == 0)
f.Data = stream.ReadBytes(f.Size.Width * f.Size.Height); f.Data = stream.ReadBytes(frameSize);
// Uncompressed scanlines // Uncompressed scanlines
else if (f.Format == 2) else if (f.Format == 2)
{ {
f.Data = new byte[f.Size.Width * f.Size.Height]; f.Data = new byte[frameSize];
for (var j = 0; j < f.Size.Height; j++) for (var j = 0; j < f.Size.Height; j++)
{ {
var length = stream.ReadUInt16() - 2; var length = stream.ReadUInt16() - 2;
@@ -89,17 +88,15 @@ namespace OpenRA.FileFormats
// RLE-zero compressed scanlines // RLE-zero compressed scanlines
else if (f.Format == 3) else if (f.Format == 3)
{ {
f.Data = new byte[f.Size.Width * f.Size.Height]; f.Data = new byte[frameSize];
for (var j = 0; j < f.Size.Height; j++) for (var j = 0; j < f.Size.Height; j++)
{ {
var length = stream.ReadUInt16() - 2; var length = stream.ReadUInt16() - 2;
Format2.DecodeInto(stream.ReadBytes(length), f.Data, j * f.Size.Width); var offset = f.Size.Width * j;
Format2.DecodeInto(stream.ReadBytes(length), f.Data, offset);
} }
} }
} }
spriteFrames = Exts.Lazy(() => frames.Cast<ISpriteFrame>());
} }
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -8,7 +8,6 @@
*/ */
#endregion #endregion
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -17,8 +16,7 @@ namespace OpenRA.FileFormats
{ {
public class TmpRAReader : ISpriteSource public class TmpRAReader : ISpriteSource
{ {
readonly List<ISpriteFrame> tiles = new List<ISpriteFrame>(); public IReadOnlyList<ISpriteFrame> Frames { get; private set; }
public IEnumerable<ISpriteFrame> Frames { get { return tiles; } }
public bool CacheWhenLoadingTileset { get { return false; } } public bool CacheWhenLoadingTileset { get { return false; } }
public TmpRAReader(Stream s) public TmpRAReader(Stream s)
@@ -35,15 +33,19 @@ namespace OpenRA.FileFormats
var indexStart = s.ReadInt32(); var indexStart = s.ReadInt32();
s.Position = indexStart; s.Position = indexStart;
foreach (var b in s.ReadBytes(indexEnd - indexStart)) var count = indexEnd - indexStart;
var tiles = new TmpTile[count];
Frames = tiles.AsReadOnly();
var tilesIndex = 0;
foreach (var b in s.ReadBytes(count))
{ {
if (b != 255) if (b != 255)
{ {
s.Position = imgStart + b * width * height; s.Position = imgStart + b * width * height;
tiles.Add(new TmpTile(s.ReadBytes(width * height), size)); tiles[tilesIndex++] = new TmpTile(s.ReadBytes(width * height), size);
} }
else else
tiles.Add(new TmpTile(null, size)); tiles[tilesIndex++] = new TmpTile(null, size);
} }
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -8,7 +8,6 @@
*/ */
#endregion #endregion
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using OpenRA.Graphics; using OpenRA.Graphics;
@@ -36,8 +35,7 @@ namespace OpenRA.FileFormats
public class TmpTDReader : ISpriteSource public class TmpTDReader : ISpriteSource
{ {
readonly List<ISpriteFrame> tiles = new List<ISpriteFrame>(); public IReadOnlyList<ISpriteFrame> Frames { get; private set; }
public IEnumerable<ISpriteFrame> Frames { get { return tiles; } }
public bool CacheWhenLoadingTileset { get { return false; } } public bool CacheWhenLoadingTileset { get { return false; } }
public TmpTDReader(Stream s) public TmpTDReader(Stream s)
@@ -53,15 +51,19 @@ namespace OpenRA.FileFormats
var indexStart = s.ReadInt32(); var indexStart = s.ReadInt32();
s.Position = indexStart; s.Position = indexStart;
foreach (var b in s.ReadBytes(indexEnd - indexStart)) var count = indexEnd - indexStart;
var tiles = new TmpTile[count];
Frames = tiles.AsReadOnly();
var tilesIndex = 0;
foreach (var b in s.ReadBytes(count))
{ {
if (b != 255) if (b != 255)
{ {
s.Position = imgStart + b * width * height; s.Position = imgStart + b * width * height;
tiles.Add(new TmpTile(s.ReadBytes(width * height), size)); tiles[tilesIndex++] = new TmpTile(s.ReadBytes(width * height), size);
} }
else else
tiles.Add(new TmpTile(null, size)); tiles[tilesIndex++] = new TmpTile(null, size);
} }
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -49,8 +49,7 @@ namespace OpenRA.FileFormats
public class TmpTSReader : ISpriteSource public class TmpTSReader : ISpriteSource
{ {
readonly List<ISpriteFrame> tiles = new List<ISpriteFrame>(); public IReadOnlyList<ISpriteFrame> Frames { get; private set; }
public IEnumerable<ISpriteFrame> Frames { get { return tiles; } }
public bool CacheWhenLoadingTileset { get { return false; } } public bool CacheWhenLoadingTileset { get { return false; } }
public TmpTSReader(Stream s) public TmpTSReader(Stream s)
@@ -64,11 +63,14 @@ namespace OpenRA.FileFormats
for (var i = 0; i < offsets.Length; i++) for (var i = 0; i < offsets.Length; i++)
offsets[i] = s.ReadUInt32(); offsets[i] = s.ReadUInt32();
var tiles = new List<TmpTSTile>();
for (var i = 0; i < offsets.Length; i++) for (var i = 0; i < offsets.Length; i++)
{ {
s.Position = offsets[i]; s.Position = offsets[i];
tiles.Add(new TmpTSTile(s, size)); tiles.Add(new TmpTSTile(s, size));
} }
Frames = tiles.ToArray().AsReadOnly();
} }
} }
} }

View File

@@ -1,6 +1,6 @@
#region Copyright & License Information #region Copyright & License Information
/* /*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS) * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made * 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 * available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information, * as published by the Free Software Foundation. For more information,
@@ -8,7 +8,6 @@
*/ */
#endregion #endregion
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using OpenRA.FileFormats; using OpenRA.FileFormats;
@@ -25,8 +24,7 @@ namespace OpenRA.Graphics
public interface ISpriteSource public interface ISpriteSource
{ {
// TODO: Change this to IReadOnlyList so users don't need to call .ToArray() IReadOnlyList<ISpriteFrame> Frames { get; }
IEnumerable<ISpriteFrame> Frames { get; }
bool CacheWhenLoadingTileset { get; } bool CacheWhenLoadingTileset { get; }
} }

View File

@@ -21,12 +21,20 @@ namespace OpenRA
/// duplicate it but provide a compatible interface that can be replaced /// duplicate it but provide a compatible interface that can be replaced
/// when we switch to .NET 4.5 or higher. /// when we switch to .NET 4.5 or higher.
/// </remarks> /// </remarks>
public interface IReadOnlyList<T> : IEnumerable<T> public interface IReadOnlyList<out T> : IEnumerable<T>
{ {
int Count { get; } int Count { get; }
T this[int index] { get; } T this[int index] { get; }
} }
public static class ReadOnlyList
{
public static IReadOnlyList<T> AsReadOnly<T>(this IList<T> list)
{
return list as IReadOnlyList<T> ?? new ReadOnlyList<T>(list);
}
}
/// <summary> /// <summary>
/// A minimal read only list for .NET 4 implemented as a wrapper /// A minimal read only list for .NET 4 implemented as a wrapper
/// around an IList. /// around an IList.

View File

@@ -233,7 +233,7 @@ namespace OpenRA.Utility
{ {
var srcImage = ShpReader.Load(args[1]); var srcImage = ShpReader.Load(args[1]);
var srcFrames = srcImage.Frames.ToArray(); var srcFrames = srcImage.Frames;
var destFrames = srcImage.Frames.ToArray(); var destFrames = srcImage.Frames.ToArray();
for (var z = 3; z < args.Length - 2; z += 3) for (var z = 3; z < args.Length - 2; z += 3)