Skip to content

Commit a72a99a

Browse files
WIP on GIF decoder
1 parent 9110cdb commit a72a99a

File tree

11 files changed

+243
-38
lines changed

11 files changed

+243
-38
lines changed

MCGalaxy/Commands/other/CmdSummon.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ static void SummonPlayer(Player p, string message, CommandData data) {
8080

8181
static bool CheckVisitPerm(Player p, Player target, bool confirmed) {
8282
AccessResult result = p.level.VisitAccess.Check(target.name, target.Rank);
83-
if (result == AccessResult.Allowed) return true;
83+
if (result == AccessResult.Accepted) return true;
8484
if (result == AccessResult.Whitelisted) return true;
8585
if (result == AccessResult.AboveMaxRank && confirmed) return true;
8686
if (result == AccessResult.BelowMinRank && confirmed) return true;

MCGalaxy/Drawing/CopyState.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,9 @@ public void LoadFromOld(Stream stream, Stream underlying) {
235235
CalculateBounds(raw);
236236
for (int i = 0; i < raw.Length; i += 7)
237237
{
238-
ushort x = BitConverter.ToUInt16(raw, i + 0);
239-
ushort y = BitConverter.ToUInt16(raw, i + 2);
240-
ushort z = BitConverter.ToUInt16(raw, i + 4);
238+
ushort x = MemUtils.ReadU16_LE(raw, i + 0);
239+
ushort y = MemUtils.ReadU16_LE(raw, i + 2);
240+
ushort z = MemUtils.ReadU16_LE(raw, i + 4);
241241

242242
byte rawBlock = raw[i + 6];
243243
Set(rawBlock, x - X, y - Y, z - Z);
@@ -252,9 +252,9 @@ void CalculateBounds(byte[] raw) {
252252

253253
for (int i = 0; i < raw.Length; i += 7)
254254
{
255-
ushort x = BitConverter.ToUInt16(raw, i + 0);
256-
ushort y = BitConverter.ToUInt16(raw, i + 2);
257-
ushort z = BitConverter.ToUInt16(raw, i + 4);
255+
ushort x = MemUtils.ReadU16_LE(raw, i + 0);
256+
ushort y = MemUtils.ReadU16_LE(raw, i + 2);
257+
ushort z = MemUtils.ReadU16_LE(raw, i + 4);
258258

259259
minX = Math.Min(x, minX); maxX = Math.Max(x, maxX);
260260
minY = Math.Min(y, minY); maxY = Math.Max(y, maxY);

MCGalaxy/Levels/AccessController.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public void CloneAccess(AccessController source) {
5151

5252
public bool CheckAllowed(Player p) {
5353
AccessResult access = Check(p.name, p.Rank);
54-
return access == AccessResult.Allowed || access == AccessResult.Whitelisted;
54+
return access == AccessResult.Accepted || access == AccessResult.Whitelisted;
5555
}
5656

5757
public AccessResult Check(string name, LevelPermission rank) {
@@ -62,13 +62,13 @@ public AccessResult Check(string name, LevelPermission rank) {
6262
if (rank > Max && MaxCmd != null && !CommandExtraPerms.Find(MaxCmd, 1).UsableBy(rank)) {
6363
return AccessResult.AboveMaxRank;
6464
}
65-
return AccessResult.Allowed;
65+
return AccessResult.Accepted;
6666
}
6767

6868
public bool CheckDetailed(Player p) { return CheckDetailed(p, p.Rank); }
6969
public bool CheckDetailed(Player p, LevelPermission plRank) {
7070
AccessResult access = Check(p.name, plRank);
71-
if (access == AccessResult.Allowed) return true;
71+
if (access == AccessResult.Accepted) return true;
7272
if (access == AccessResult.Whitelisted) return true;
7373

7474
if (access == AccessResult.Blacklisted) {
@@ -293,7 +293,7 @@ public enum AccessResult {
293293
/// <summary> The player is blacklisted and never allowed. </summary>
294294
Blacklisted,
295295
/// <summary> The player is allowed (by their rank) </summary>
296-
Allowed,
296+
Accepted,
297297
/// <summary> The player's rank is below the minimum rank allowed. </summary>
298298
BelowMinRank,
299299
/// <summary> The player's rank is above the maximum rank allowed. </summary>

MCGalaxy/Levels/IO/Importers/LvlImporter.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ public override Level Read(Stream src, string name, bool metadata) {
4444
Vec3U16 dims = ReadHeader(gs, header);
4545

4646
Level lvl = new Level(name, dims.X, dims.Y, dims.Z);
47-
lvl.spawnx = BitConverter.ToUInt16(header, 8);
48-
lvl.spawnz = BitConverter.ToUInt16(header, 10);
49-
lvl.spawny = BitConverter.ToUInt16(header, 12);
47+
lvl.spawnx = MemUtils.ReadU16_LE(header, 8);
48+
lvl.spawnz = MemUtils.ReadU16_LE(header, 10);
49+
lvl.spawny = MemUtils.ReadU16_LE(header, 12);
5050
lvl.rotx = header[14];
5151
lvl.roty = header[15];
5252
// pervisit/perbuild permission bytes ignored
@@ -71,14 +71,14 @@ public override Level Read(Stream src, string name, bool metadata) {
7171

7272
static Vec3U16 ReadHeader(Stream gs, byte[] header) {
7373
StreamUtils.ReadFully(gs, header, 0, HEADER_SIZE);
74-
int signature = BitConverter.ToUInt16(header, 0);
74+
int signature = MemUtils.ReadU16_LE(header, 0);
7575
if (signature != 1874)
7676
throw new InvalidDataException("Invalid .lvl map signature");
7777

7878
Vec3U16 dims;
79-
dims.X = BitConverter.ToUInt16(header, 2);
80-
dims.Z = BitConverter.ToUInt16(header, 4);
81-
dims.Y = BitConverter.ToUInt16(header, 6);
79+
dims.X = MemUtils.ReadU16_LE(header, 2);
80+
dims.Z = MemUtils.ReadU16_LE(header, 4);
81+
dims.Y = MemUtils.ReadU16_LE(header, 6);
8282
return dims;
8383
}
8484

MCGalaxy/Levels/IO/Importers/McfImporter.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ public override Level Read(Stream src, string name, bool metadata) {
4343
Vec3U16 dims = ReadHeader(header, gs);
4444

4545
Level lvl = new Level(name, dims.X, dims.Y, dims.Z);
46-
lvl.spawnx = BitConverter.ToUInt16(header, 6);
47-
lvl.spawnz = BitConverter.ToUInt16(header, 8);
48-
lvl.spawny = BitConverter.ToUInt16(header, 10);
46+
lvl.spawnx = MemUtils.ReadU16_LE(header, 6);
47+
lvl.spawnz = MemUtils.ReadU16_LE(header, 8);
48+
lvl.spawny = MemUtils.ReadU16_LE(header, 10);
4949
lvl.rotx = header[12]; lvl.roty = header[13];
5050
// 2 bytes for perbuild and pervisit
5151

@@ -59,14 +59,14 @@ public override Level Read(Stream src, string name, bool metadata) {
5959

6060
static Vec3U16 ReadHeader(byte[] header, Stream gs) {
6161
StreamUtils.ReadFully(gs, header, 0, 2);
62-
if (BitConverter.ToUInt16(header, 0) != 1874)
62+
if (MemUtils.ReadU16_LE(header, 0) != 1874)
6363
throw new InvalidDataException(".mcf files must have a version of 1874");
6464

6565
StreamUtils.ReadFully(gs, header, 0, 16);
6666
Vec3U16 dims;
67-
dims.X = BitConverter.ToUInt16(header, 0);
68-
dims.Z = BitConverter.ToUInt16(header, 2);
69-
dims.Y = BitConverter.ToUInt16(header, 4);
67+
dims.X = MemUtils.ReadU16_LE(header, 0);
68+
dims.Z = MemUtils.ReadU16_LE(header, 2);
69+
dims.Y = MemUtils.ReadU16_LE(header, 4);
7070
return dims;
7171
}
7272
}

MCGalaxy/Levels/Level.Blocks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ public AccessController CanAffect(Player p, ushort x, ushort y, ushort z) {
245245
Zone zn = zones[i];
246246
if (x < zn.MinX || x > zn.MaxX || y < zn.MinY || y > zn.MaxY || z < zn.MinZ || z > zn.MaxZ) continue;
247247
AccessResult access = zn.Access.Check(p.name, p.Rank);
248-
if (access == AccessResult.Allowed || access == AccessResult.Whitelisted) continue;
248+
if (access == AccessResult.Accepted || access == AccessResult.Whitelisted) continue;
249249

250250
return zn.Access;
251251
}

MCGalaxy/util/Imaging/Bitmap2D.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,15 @@ public struct Pixel
3737
{
3838
public byte A, R, G, B;
3939

40-
public Pixel(byte r, byte g, byte b, byte a)
41-
{
40+
public Pixel(byte r, byte g, byte b, byte a) {
4241
R = r; G = g; B = b; A = a;
4342
}
43+
44+
public static Pixel BLACK = new Pixel(0, 0, 0, 255);
45+
46+
public override string ToString() {
47+
return string.Format("R={0}, G={1}, B={2}, A={3}", R, G, B, A);
48+
}
4449
}
4550

4651
/// <summary> Represents a 2D image </summary>

MCGalaxy/util/Imaging/GifDecoder.cs

Lines changed: 193 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,206 @@ namespace MCGalaxy.Util.Imaging
2424
{
2525
public class GifDecoder : ImageDecoder
2626
{
27-
static byte[] gifSig = new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }; // "GIF89a"
27+
static byte[] gif87Sig = new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }; // "GIF87a"
28+
static byte[] gif89Sig = new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }; // "GIF89a"
29+
30+
Pixel[] globalPal;
31+
Pixel bgColor;
2832

2933
public static bool DetectHeader(byte[] data) {
30-
return MatchesSignature(data, gifSig);
34+
return MatchesSignature(data, gif87Sig)
35+
|| MatchesSignature(data, gif89Sig);
3136
}
3237

3338
public override SimpleBitmap Decode(byte[] src) {
39+
SetBuffer(src);
40+
if (!DetectHeader(src)) Fail("sig invalid");
41+
AdvanceOffset(gif87Sig.Length);
42+
43+
ReadGlobalHeader(src);
44+
ReadMarkers(src);
3445
Fail("GIF decoder unfinished");
3546
return null;
3647
}
48+
49+
const int LOGICAL_DESC_SIZE = 7;
50+
void ReadGlobalHeader(byte[] src) {
51+
// read logical screen descriptor
52+
int offset = AdvanceOffset(LOGICAL_DESC_SIZE);
53+
54+
ushort width = MemUtils.ReadU16_LE(src, offset + 0);
55+
ushort height = MemUtils.ReadU16_LE(src, offset + 2);
56+
57+
byte flags = src[offset + 4];
58+
byte bgIndex = src[offset + 5];
59+
// src[offset + 6] is pixel aspect ratio - not used
60+
61+
bool hasGlobalPal = (flags & 0x80) != 0;
62+
int globalPalSize = 1 << ((flags & 0x7) + 1);
63+
if (hasGlobalPal) ReadGlobalPalette(src, globalPalSize);
64+
65+
if (hasGlobalPal && bgIndex < globalPalSize)
66+
bgColor = globalPal[bgIndex];
67+
}
68+
69+
void ReadGlobalPalette(byte[] src, int size) {
70+
Pixel[] pal = new Pixel[size];
71+
int offset = AdvanceOffset(3 * size);
72+
73+
for (int i = 0; i < pal.Length; i++)
74+
{
75+
pal[i].R = src[offset++];
76+
pal[i].G = src[offset++];
77+
pal[i].B = src[offset++];
78+
pal[i].A = 255;
79+
}
80+
globalPal = pal;
81+
}
82+
83+
84+
const byte MARKER_EXTENSION = 0x21;
85+
const byte MARKER_IMAGE_END = 0x3B;
86+
const byte MARKER_IMAGE_BEG = 0x2C;
87+
88+
void ReadMarkers(byte[] src) {
89+
for (;;)
90+
{
91+
int offset = AdvanceOffset(1);
92+
byte marker = src[offset];
93+
switch (marker)
94+
{
95+
case MARKER_EXTENSION:
96+
ReadExtension(src);
97+
break;
98+
case MARKER_IMAGE_BEG:
99+
ReadImage(src);
100+
return; // NOTE: stops reading at first frame
101+
case MARKER_IMAGE_END:
102+
return;
103+
default:
104+
Fail("unknown marker");
105+
break;
106+
}
107+
}
108+
}
109+
110+
const byte EXT_GRAPHICS_CONTROL = 0xF9;
111+
void ReadExtension(byte[] src) {
112+
int offset = AdvanceOffset(1);
113+
byte type = src[offset++];
114+
115+
if (type == EXT_GRAPHICS_CONTROL) {
116+
ReadGraphicsControl(src);
117+
} else {
118+
SkipSubBlocks(src);
119+
}
120+
}
121+
122+
// Always a simple sub block
123+
void ReadGraphicsControl(byte[] src) {
124+
int offset = AdvanceOffset(1);
125+
byte length = src[offset];
126+
if (length < 4) Fail("graphics control extension too short");
127+
128+
// Look for transparent colour index
129+
offset = AdvanceOffset(length);
130+
bool hasTrans = (src[offset + 0] & 0x01) != 0;
131+
byte tcIndex = src[offset + 3];
132+
133+
Pixel[] pal = globalPal;
134+
if (hasTrans && pal != null && tcIndex < pal.Length)
135+
pal[tcIndex].A = 0;
136+
137+
// should only be one sub block
138+
offset = AdvanceOffset(1);
139+
length = src[offset];
140+
if (length != 0) Fail("graphics control should be one sub block");
141+
}
142+
143+
void SkipSubBlocks(byte[] src) {
144+
for (;;)
145+
{
146+
int offset = AdvanceOffset(1);
147+
byte length = src[offset++];
148+
149+
if (length == 0) return;
150+
AdvanceOffset(length);
151+
}
152+
}
153+
154+
155+
void ReadImage(byte[] src) {
156+
// Read image descriptor header
157+
int offset = AdvanceOffset(2 + 2 + 2 + 2 + 1);
158+
159+
ushort imageX = MemUtils.ReadU16_LE(src, offset + 0);
160+
ushort imageY = MemUtils.ReadU16_LE(src, offset + 2);
161+
ushort imageW = MemUtils.ReadU16_LE(src, offset + 4);
162+
ushort imageH = MemUtils.ReadU16_LE(src, offset + 6);
163+
byte flags = src[offset + 8];
164+
165+
// Read image data
166+
offset = AdvanceOffset(1);
167+
byte minCodeSize = src[offset];
168+
169+
// Init LZW variables
170+
int codeLen = minCodeSize + 1;
171+
int codeMask = (1 << codeLen) - 1;
172+
int clearCode = (1 << minCodeSize) + 0;
173+
int stopCode = (1 << minCodeSize) + 1;
174+
int dictEnd;
175+
DictEntry[] dict = new DictEntry[1 << codeLen];
176+
177+
// Bit buffer state
178+
uint bufVal = 0;
179+
int bufLen = 0;
180+
181+
// Spec says clear code _should_ be sent first, but not required
182+
for (dictEnd = 0; dictEnd < (1 << minCodeSize); dictEnd++)
183+
{
184+
dict[dictEnd].value = (byte)dictEnd;
185+
dict[dictEnd].prev = -1;
186+
dict[dictEnd].len = 1;
187+
}
188+
dictEnd += 2; // "clear code" and "stop code" entries
189+
190+
for (;;)
191+
{
192+
int code = 0;
193+
194+
if (bufLen < codeLen) {
195+
196+
}
197+
198+
code = (int)(bufVal & codeMask);
199+
bufVal >>= codeMask;
200+
bufLen -= codeLen;
201+
202+
if (code == clearCode) {
203+
codeLen = minCodeSize + 1;
204+
codeMask = (1 << codeLen) - 1;
205+
206+
// Clear dictionary
207+
for (dictEnd = 0; dictEnd < (1 << minCodeSize); dictEnd++)
208+
{
209+
dict[dictEnd].value = (byte)dictEnd;
210+
dict[dictEnd].prev = -1;
211+
dict[dictEnd].len = 1;
212+
}
213+
dictEnd += 2; // "clear code" and "stop code" entries
214+
} else if (code == stopCode) {
215+
break;
216+
}
217+
}
218+
219+
//SkipSubBlocks(src);
220+
Fail("GIF decoder unfinished");
221+
}
222+
223+
struct DictEntry
224+
{
225+
public byte value;
226+
public short prev, len;
227+
}
37228
}
38229
}

MCGalaxy/util/Imaging/ImageDecoder.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public abstract class ImageDecoder
3333
protected byte[] buf_data;
3434
protected int buf_offset, buf_length;
3535

36+
/// <summary> Attempts to advance next read offset by 'amount', then returns current read offset </summary>
3637
protected int AdvanceOffset(int amount) {
3738
int offset = buf_offset;
3839

@@ -42,6 +43,13 @@ protected int AdvanceOffset(int amount) {
4243
return offset;
4344
}
4445

46+
protected void SetBuffer(byte[] src) {
47+
buf_data = src;
48+
buf_offset = 0;
49+
buf_length = src.Length;
50+
}
51+
52+
4553
protected static void Fail(string reason) {
4654
throw new InvalidDataException(reason);
4755
}

0 commit comments

Comments
 (0)