1 |
using System; |
2 |
|
3 |
namespace Oni.Imaging |
4 |
{ |
5 |
internal static class Dxt1 |
6 |
{ |
7 |
public static Surface Decompress(Surface src, SurfaceFormat dstFormat) |
8 |
{ |
9 |
var dst = new Surface(src.Width, src.Height, dstFormat); |
10 |
var colors = new Color[4]; |
11 |
int srcOffset = 0; |
12 |
|
13 |
for (int y = 0; y < dst.Height; y += 4) |
14 |
{ |
15 |
for (int x = 0; x < dst.Width; x += 4) |
16 |
{ |
17 |
colors[0] = Color.ReadBgr565(src.Data, srcOffset); |
18 |
srcOffset += 2; |
19 |
|
20 |
colors[1] = Color.ReadBgr565(src.Data, srcOffset); |
21 |
srcOffset += 2; |
22 |
|
23 |
if (colors[0].ToBgr565() > colors[1].ToBgr565()) |
24 |
{ |
25 |
colors[2] = Color.Lerp(colors[0], colors[1], 1.0f / 3.0f); |
26 |
colors[3] = Color.Lerp(colors[0], colors[1], 2.0f / 3.0f); |
27 |
} |
28 |
else |
29 |
{ |
30 |
colors[2] = Color.Lerp(colors[0], colors[1], 0.5f); |
31 |
colors[3] = Color.Transparent; |
32 |
} |
33 |
|
34 |
for (int y2 = 0; y2 < 4; y2++) |
35 |
{ |
36 |
int packedLookup = src.Data[srcOffset++]; |
37 |
|
38 |
for (int x2 = 0; x2 < 4; x2++) |
39 |
{ |
40 |
dst[x + x2, y + y2] = colors[packedLookup & 3]; |
41 |
packedLookup >>= 2; |
42 |
} |
43 |
} |
44 |
} |
45 |
} |
46 |
|
47 |
return dst; |
48 |
} |
49 |
|
50 |
public static Surface Compress(Surface src) |
51 |
{ |
52 |
var dst = new Surface(Utils.Align4(src.Width), Utils.Align4(src.Height), SurfaceFormat.DXT1); |
53 |
|
54 |
var block = new Vector3[16]; |
55 |
var colors = new Vector3[4]; |
56 |
var lookup = new int[16]; |
57 |
int dstOffset = 0; |
58 |
int height = dst.Height; |
59 |
int width = dst.Width; |
60 |
|
61 |
for (int y = 0; y < height; y += 4) |
62 |
{ |
63 |
for (int x = 0; x < width; x += 4) |
64 |
{ |
65 |
for (int by = 0; by < 4; by++) |
66 |
{ |
67 |
for (int bx = 0; bx < 4; bx++) |
68 |
block[by * 4 + bx] = src[x + bx, y + by].ToVector3(); |
69 |
} |
70 |
|
71 |
CompressBlock(block, lookup, colors); |
72 |
|
73 |
Color.WriteBgr565(new Color(colors[0]), dst.Data, dstOffset); |
74 |
dstOffset += 2; |
75 |
|
76 |
Color.WriteBgr565(new Color(colors[1]), dst.Data, dstOffset); |
77 |
dstOffset += 2; |
78 |
|
79 |
for (int by = 0; by < 4; by++) |
80 |
{ |
81 |
int packedLookup = 0; |
82 |
|
83 |
for (int bx = 3; bx >= 0; bx--) |
84 |
packedLookup = (packedLookup << 2) | lookup[by * 4 + bx]; |
85 |
|
86 |
dst.Data[dstOffset++] = (byte)packedLookup; |
87 |
} |
88 |
} |
89 |
} |
90 |
|
91 |
return dst; |
92 |
} |
93 |
|
94 |
private static void CompressBlock(Vector3[] block, int[] lookup, Vector3[] colors) |
95 |
{ |
96 |
colors[0] = block[0]; |
97 |
colors[1] = block[0]; |
98 |
|
99 |
for (int i = 1; i < block.Length; i++) |
100 |
{ |
101 |
colors[0] = Vector3.Min(colors[0], block[i]); |
102 |
colors[1] = Vector3.Max(colors[1], block[i]); |
103 |
} |
104 |
|
105 |
int maxColor; |
106 |
|
107 |
if (new Color(colors[0]).ToBgr565() > new Color(colors[1]).ToBgr565()) |
108 |
{ |
109 |
colors[2] = Vector3.Lerp(colors[0], colors[1], 1.0f / 3.0f); |
110 |
colors[3] = Vector3.Lerp(colors[0], colors[1], 2.0f / 3.0f); |
111 |
maxColor = 4; |
112 |
} |
113 |
else |
114 |
{ |
115 |
colors[2] = Vector3.Lerp(colors[0], colors[1], 0.5f); |
116 |
maxColor = 3; |
117 |
} |
118 |
|
119 |
for (int i = 0; i < block.Length; i++) |
120 |
{ |
121 |
lookup[i] = LookupNearest(colors, block[i], maxColor); |
122 |
} |
123 |
} |
124 |
|
125 |
private static int LookupNearest(Vector3[] colors, Vector3 pixel, int maxColor) |
126 |
{ |
127 |
int index = 0; |
128 |
float ds = Vector3.DistanceSquared(pixel, colors[0]); |
129 |
|
130 |
for (int i = 1; i < maxColor; i++) |
131 |
{ |
132 |
float newDs = Vector3.DistanceSquared(pixel, colors[i]); |
133 |
|
134 |
if (newDs < ds) |
135 |
{ |
136 |
ds = newDs; |
137 |
index = i; |
138 |
} |
139 |
} |
140 |
|
141 |
return index; |
142 |
} |
143 |
} |
144 |
} |