Our SpriteFrameType names refer to the byte channel order rather than the bit order, meaning that SpriteFrameType.BGRA corresponds to the standard Color.ToArgb() etc byte order when the (little-endian) integer is read as 4 individual bytes. The previous code did not account for the fact that non-indexed Png uses big-endian storage for its RGBA colours, and that SheetBuilder had the color channels incorrectly swapped to match and cancel this out. New SpriteFrameType enums are introduced to distinguish between BGRA (little-endian) and RGBA (big-endian) formats, and also for 24bit data without alpha. The channel swizzling / alpha creation is now handled when copying into the texture atlas, removing the need for non-png ISpriteLoader implementations to allocate an additional temporary array and reorder the channels during load.
131 lines
3.1 KiB
C#
131 lines
3.1 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2020 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, either version 3 of
|
|
* the License, or (at your option) any later version. For more
|
|
* information, see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using OpenRA.Graphics;
|
|
using OpenRA.Mods.Common.Graphics;
|
|
using OpenRA.Primitives;
|
|
|
|
namespace OpenRA.Mods.D2k.SpriteLoaders
|
|
{
|
|
public class R8Loader : ISpriteLoader
|
|
{
|
|
class R8Frame : ISpriteFrame
|
|
{
|
|
public SpriteFrameType Type { get { return SpriteFrameType.Indexed; } }
|
|
public Size Size { get; private set; }
|
|
public Size FrameSize { get; private set; }
|
|
public float2 Offset { get; private set; }
|
|
public byte[] Data { get; set; }
|
|
public bool DisableExportPadding { get { return true; } }
|
|
|
|
public readonly uint[] Palette = null;
|
|
|
|
public R8Frame(Stream s)
|
|
{
|
|
// Scan forward until we find some data
|
|
var type = s.ReadUInt8();
|
|
while (type == 0)
|
|
type = s.ReadUInt8();
|
|
|
|
var width = s.ReadInt32();
|
|
var height = s.ReadInt32();
|
|
var x = s.ReadInt32();
|
|
var y = s.ReadInt32();
|
|
|
|
Size = new Size(width, height);
|
|
Offset = new int2(width / 2 - x, height / 2 - y);
|
|
|
|
/*var imageOffset = */
|
|
s.ReadInt32();
|
|
var paletteOffset = s.ReadInt32();
|
|
var bpp = s.ReadUInt8();
|
|
if (bpp != 8)
|
|
throw new InvalidDataException("Error: {0} bits per pixel are not supported.".F(bpp));
|
|
|
|
var frameHeight = s.ReadUInt8();
|
|
var frameWidth = s.ReadUInt8();
|
|
FrameSize = new Size(frameWidth, frameHeight);
|
|
|
|
// Skip alignment byte
|
|
s.ReadUInt8();
|
|
|
|
Data = s.ReadBytes(width * height);
|
|
|
|
// Read palette
|
|
if (type == 1 && paletteOffset != 0)
|
|
{
|
|
// Skip header
|
|
s.ReadUInt32();
|
|
s.ReadUInt32();
|
|
|
|
Palette = new uint[256];
|
|
for (var i = 0; i < 256; i++)
|
|
{
|
|
var packed = s.ReadUInt16();
|
|
Palette[i] = (uint)((255 << 24) | ((packed & 0xF800) << 8) | ((packed & 0x7E0) << 5) | ((packed & 0x1f) << 3));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IsR8(Stream s)
|
|
{
|
|
var start = s.Position;
|
|
|
|
// First byte is nonzero
|
|
if (s.ReadUInt8() == 0)
|
|
{
|
|
s.Position = start;
|
|
return false;
|
|
}
|
|
|
|
// Check the format of the first frame
|
|
s.Position = start + 25;
|
|
var d = s.ReadUInt8();
|
|
|
|
s.Position = start;
|
|
return d == 8;
|
|
}
|
|
|
|
public bool TryParseSprite(Stream s, out ISpriteFrame[] frames, out TypeDictionary metadata)
|
|
{
|
|
metadata = null;
|
|
if (!IsR8(s))
|
|
{
|
|
frames = null;
|
|
return false;
|
|
}
|
|
|
|
var start = s.Position;
|
|
var tmp = new List<R8Frame>();
|
|
var palettes = new Dictionary<int, uint[]>();
|
|
while (s.Position < s.Length)
|
|
{
|
|
var f = new R8Frame(s);
|
|
if (f.Palette != null)
|
|
palettes.Add(tmp.Count, f.Palette);
|
|
tmp.Add(f);
|
|
}
|
|
|
|
s.Position = start;
|
|
|
|
frames = tmp.ToArray();
|
|
if (palettes.Any())
|
|
metadata = new TypeDictionary { new EmbeddedSpritePalette(framePalettes: palettes) };
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|