|
|
#region License
|
|
|
/* FNA - XNA4 Reimplementation for Desktop Platforms
|
|
|
* Copyright 2009-2020 Ethan Lee and the MonoGame Team
|
|
|
*
|
|
|
* Released under the Microsoft Public License.
|
|
|
* See LICENSE for details.
|
|
|
*/
|
|
|
#endregion
|
|
|
|
|
|
#region Using Statements
|
|
|
using System.IO;
|
|
|
#endregion
|
|
|
|
|
|
namespace Microsoft.Xna.Framework.Graphics
|
|
|
{
|
|
|
internal static class DxtUtil
|
|
|
{
|
|
|
#region Internal Static Methods
|
|
|
|
|
|
internal static byte[] DecompressDxt1(byte[] imageData, int width, int height)
|
|
|
{
|
|
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
|
|
{
|
|
|
return DecompressDxt1(imageStream, width, height);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
internal static byte[] DecompressDxt1(Stream imageStream, int width, int height)
|
|
|
{
|
|
|
byte[] imageData = new byte[width * height * 4];
|
|
|
|
|
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
|
|
{
|
|
|
int blockCountX = (width + 3) / 4;
|
|
|
int blockCountY = (height + 3) / 4;
|
|
|
|
|
|
for (int y = 0; y < blockCountY; y++)
|
|
|
{
|
|
|
for (int x = 0; x < blockCountX; x++)
|
|
|
{
|
|
|
DecompressDxt1Block(imageReader, x, y, blockCountX, width, height, imageData);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return imageData;
|
|
|
}
|
|
|
|
|
|
internal static byte[] DecompressDxt3(byte[] imageData, int width, int height)
|
|
|
{
|
|
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
|
|
{
|
|
|
return DecompressDxt3(imageStream, width, height);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
internal static byte[] DecompressDxt3(Stream imageStream, int width, int height)
|
|
|
{
|
|
|
byte[] imageData = new byte[width * height * 4];
|
|
|
|
|
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
|
|
{
|
|
|
int blockCountX = (width + 3) / 4;
|
|
|
int blockCountY = (height + 3) / 4;
|
|
|
|
|
|
for (int y = 0; y < blockCountY; y++)
|
|
|
{
|
|
|
for (int x = 0; x < blockCountX; x++)
|
|
|
{
|
|
|
DecompressDxt3Block(imageReader, x, y, blockCountX, width, height, imageData);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return imageData;
|
|
|
}
|
|
|
|
|
|
internal static byte[] DecompressDxt5(byte[] imageData, int width, int height)
|
|
|
{
|
|
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
|
|
{
|
|
|
return DecompressDxt5(imageStream, width, height);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
internal static byte[] DecompressDxt5(Stream imageStream, int width, int height)
|
|
|
{
|
|
|
byte[] imageData = new byte[width * height * 4];
|
|
|
|
|
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
|
|
{
|
|
|
int blockCountX = (width + 3) / 4;
|
|
|
int blockCountY = (height + 3) / 4;
|
|
|
|
|
|
for (int y = 0; y < blockCountY; y++)
|
|
|
{
|
|
|
for (int x = 0; x < blockCountX; x++)
|
|
|
{
|
|
|
DecompressDxt5Block(imageReader, x, y, blockCountX, width, height, imageData);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return imageData;
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region Private Static Methods
|
|
|
|
|
|
private static void DecompressDxt1Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
|
|
{
|
|
|
ushort c0 = imageReader.ReadUInt16();
|
|
|
ushort c1 = imageReader.ReadUInt16();
|
|
|
|
|
|
byte r0, g0, b0;
|
|
|
byte r1, g1, b1;
|
|
|
ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
|
|
|
ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);
|
|
|
|
|
|
uint lookupTable = imageReader.ReadUInt32();
|
|
|
|
|
|
for (int blockY = 0; blockY < 4; blockY++)
|
|
|
{
|
|
|
for (int blockX = 0; blockX < 4; blockX++)
|
|
|
{
|
|
|
byte r = 0, g = 0, b = 0, a = 255;
|
|
|
uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;
|
|
|
|
|
|
if (c0 > c1)
|
|
|
{
|
|
|
switch (index)
|
|
|
{
|
|
|
case 0:
|
|
|
r = r0;
|
|
|
g = g0;
|
|
|
b = b0;
|
|
|
break;
|
|
|
case 1:
|
|
|
r = r1;
|
|
|
g = g1;
|
|
|
b = b1;
|
|
|
break;
|
|
|
case 2:
|
|
|
r = (byte) ((2 * r0 + r1) / 3);
|
|
|
g = (byte) ((2 * g0 + g1) / 3);
|
|
|
b = (byte) ((2 * b0 + b1) / 3);
|
|
|
break;
|
|
|
case 3:
|
|
|
r = (byte) ((r0 + 2 * r1) / 3);
|
|
|
g = (byte) ((g0 + 2 * g1) / 3);
|
|
|
b = (byte) ((b0 + 2 * b1) / 3);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
switch (index)
|
|
|
{
|
|
|
case 0:
|
|
|
r = r0;
|
|
|
g = g0;
|
|
|
b = b0;
|
|
|
break;
|
|
|
case 1:
|
|
|
r = r1;
|
|
|
g = g1;
|
|
|
b = b1;
|
|
|
break;
|
|
|
case 2:
|
|
|
r = (byte) ((r0 + r1) / 2);
|
|
|
g = (byte) ((g0 + g1) / 2);
|
|
|
b = (byte) ((b0 + b1) / 2);
|
|
|
break;
|
|
|
case 3:
|
|
|
r = 0;
|
|
|
g = 0;
|
|
|
b = 0;
|
|
|
a = 0;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int px = (x << 2) + blockX;
|
|
|
int py = (y << 2) + blockY;
|
|
|
if ((px < width) && (py < height))
|
|
|
{
|
|
|
int offset = ((py * width) + px) << 2;
|
|
|
imageData[offset] = r;
|
|
|
imageData[offset + 1] = g;
|
|
|
imageData[offset + 2] = b;
|
|
|
imageData[offset + 3] = a;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private static void DecompressDxt3Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
|
|
{
|
|
|
byte a0 = imageReader.ReadByte();
|
|
|
byte a1 = imageReader.ReadByte();
|
|
|
byte a2 = imageReader.ReadByte();
|
|
|
byte a3 = imageReader.ReadByte();
|
|
|
byte a4 = imageReader.ReadByte();
|
|
|
byte a5 = imageReader.ReadByte();
|
|
|
byte a6 = imageReader.ReadByte();
|
|
|
byte a7 = imageReader.ReadByte();
|
|
|
|
|
|
ushort c0 = imageReader.ReadUInt16();
|
|
|
ushort c1 = imageReader.ReadUInt16();
|
|
|
|
|
|
byte r0, g0, b0;
|
|
|
byte r1, g1, b1;
|
|
|
ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
|
|
|
ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);
|
|
|
|
|
|
uint lookupTable = imageReader.ReadUInt32();
|
|
|
|
|
|
int alphaIndex = 0;
|
|
|
for (int blockY = 0; blockY < 4; blockY++)
|
|
|
{
|
|
|
for (int blockX = 0; blockX < 4; blockX++)
|
|
|
{
|
|
|
byte r = 0, g = 0, b = 0, a = 0;
|
|
|
|
|
|
uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;
|
|
|
|
|
|
switch (alphaIndex)
|
|
|
{
|
|
|
case 0:
|
|
|
a = (byte) ((a0 & 0x0F) | ((a0 & 0x0F) << 4));
|
|
|
break;
|
|
|
case 1:
|
|
|
a = (byte) ((a0 & 0xF0) | ((a0 & 0xF0) >> 4));
|
|
|
break;
|
|
|
case 2:
|
|
|
a = (byte) ((a1 & 0x0F) | ((a1 & 0x0F) << 4));
|
|
|
break;
|
|
|
case 3:
|
|
|
a = (byte) ((a1 & 0xF0) | ((a1 & 0xF0) >> 4));
|
|
|
break;
|
|
|
case 4:
|
|
|
a = (byte) ((a2 & 0x0F) | ((a2 & 0x0F) << 4));
|
|
|
break;
|
|
|
case 5:
|
|
|
a = (byte) ((a2 & 0xF0) | ((a2 & 0xF0) >> 4));
|
|
|
break;
|
|
|
case 6:
|
|
|
a = (byte) ((a3 & 0x0F) | ((a3 & 0x0F) << 4));
|
|
|
break;
|
|
|
case 7:
|
|
|
a = (byte) ((a3 & 0xF0) | ((a3 & 0xF0) >> 4));
|
|
|
break;
|
|
|
case 8:
|
|
|
a = (byte) ((a4 & 0x0F) | ((a4 & 0x0F) << 4));
|
|
|
break;
|
|
|
case 9:
|
|
|
a = (byte) ((a4 & 0xF0) | ((a4 & 0xF0) >> 4));
|
|
|
break;
|
|
|
case 10:
|
|
|
a = (byte) ((a5 & 0x0F) | ((a5 & 0x0F) << 4));
|
|
|
break;
|
|
|
case 11:
|
|
|
a = (byte) ((a5 & 0xF0) | ((a5 & 0xF0) >> 4));
|
|
|
break;
|
|
|
case 12:
|
|
|
a = (byte) ((a6 & 0x0F) | ((a6 & 0x0F) << 4));
|
|
|
break;
|
|
|
case 13:
|
|
|
a = (byte) ((a6 & 0xF0) | ((a6 & 0xF0) >> 4));
|
|
|
break;
|
|
|
case 14:
|
|
|
a = (byte) ((a7 & 0x0F) | ((a7 & 0x0F) << 4));
|
|
|
break;
|
|
|
case 15:
|
|
|
a = (byte) ((a7 & 0xF0) | ((a7 & 0xF0) >> 4));
|
|
|
break;
|
|
|
}
|
|
|
++alphaIndex;
|
|
|
|
|
|
switch (index)
|
|
|
{
|
|
|
case 0:
|
|
|
r = r0;
|
|
|
g = g0;
|
|
|
b = b0;
|
|
|
break;
|
|
|
case 1:
|
|
|
r = r1;
|
|
|
g = g1;
|
|
|
b = b1;
|
|
|
break;
|
|
|
case 2:
|
|
|
r = (byte) ((2 * r0 + r1) / 3);
|
|
|
g = (byte) ((2 * g0 + g1) / 3);
|
|
|
b = (byte) ((2 * b0 + b1) / 3);
|
|
|
break;
|
|
|
case 3:
|
|
|
r = (byte) ((r0 + 2 * r1) / 3);
|
|
|
g = (byte) ((g0 + 2 * g1) / 3);
|
|
|
b = (byte) ((b0 + 2 * b1) / 3);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
int px = (x << 2) + blockX;
|
|
|
int py = (y << 2) + blockY;
|
|
|
if ((px < width) && (py < height))
|
|
|
{
|
|
|
int offset = ((py * width) + px) << 2;
|
|
|
imageData[offset] = r;
|
|
|
imageData[offset + 1] = g;
|
|
|
imageData[offset + 2] = b;
|
|
|
imageData[offset + 3] = a;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private static void DecompressDxt5Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
|
|
{
|
|
|
byte alpha0 = imageReader.ReadByte();
|
|
|
byte alpha1 = imageReader.ReadByte();
|
|
|
|
|
|
ulong alphaMask = (ulong) imageReader.ReadByte();
|
|
|
alphaMask += (ulong) imageReader.ReadByte() << 8;
|
|
|
alphaMask += (ulong) imageReader.ReadByte() << 16;
|
|
|
alphaMask += (ulong) imageReader.ReadByte() << 24;
|
|
|
alphaMask += (ulong) imageReader.ReadByte() << 32;
|
|
|
alphaMask += (ulong) imageReader.ReadByte() << 40;
|
|
|
|
|
|
ushort c0 = imageReader.ReadUInt16();
|
|
|
ushort c1 = imageReader.ReadUInt16();
|
|
|
|
|
|
byte r0, g0, b0;
|
|
|
byte r1, g1, b1;
|
|
|
ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
|
|
|
ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);
|
|
|
|
|
|
uint lookupTable = imageReader.ReadUInt32();
|
|
|
|
|
|
for (int blockY = 0; blockY < 4; blockY++)
|
|
|
{
|
|
|
for (int blockX = 0; blockX < 4; blockX++)
|
|
|
{
|
|
|
byte r = 0, g = 0, b = 0, a = 255;
|
|
|
uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;
|
|
|
|
|
|
uint alphaIndex = (uint) ((alphaMask >> 3 * (4 * blockY + blockX)) & 0x07);
|
|
|
if (alphaIndex == 0)
|
|
|
{
|
|
|
a = alpha0;
|
|
|
}
|
|
|
else if (alphaIndex == 1)
|
|
|
{
|
|
|
a = alpha1;
|
|
|
}
|
|
|
else if (alpha0 > alpha1)
|
|
|
{
|
|
|
a = (byte) (((8 - alphaIndex) * alpha0 + (alphaIndex - 1) * alpha1) / 7);
|
|
|
}
|
|
|
else if (alphaIndex == 6)
|
|
|
{
|
|
|
a = 0;
|
|
|
}
|
|
|
else if (alphaIndex == 7)
|
|
|
{
|
|
|
a = 0xff;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
a = (byte) (((6 - alphaIndex) * alpha0 + (alphaIndex - 1) * alpha1) / 5);
|
|
|
}
|
|
|
|
|
|
switch (index)
|
|
|
{
|
|
|
case 0:
|
|
|
r = r0;
|
|
|
g = g0;
|
|
|
b = b0;
|
|
|
break;
|
|
|
case 1:
|
|
|
r = r1;
|
|
|
g = g1;
|
|
|
b = b1;
|
|
|
break;
|
|
|
case 2:
|
|
|
r = (byte) ((2 * r0 + r1) / 3);
|
|
|
g = (byte) ((2 * g0 + g1) / 3);
|
|
|
b = (byte) ((2 * b0 + b1) / 3);
|
|
|
break;
|
|
|
case 3:
|
|
|
r = (byte) ((r0 + 2 * r1) / 3);
|
|
|
g = (byte) ((g0 + 2 * g1) / 3);
|
|
|
b = (byte) ((b0 + 2 * b1) / 3);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
int px = (x << 2) + blockX;
|
|
|
int py = (y << 2) + blockY;
|
|
|
if ((px < width) && (py < height))
|
|
|
{
|
|
|
int offset = ((py * width) + px) << 2;
|
|
|
imageData[offset] = r;
|
|
|
imageData[offset + 1] = g;
|
|
|
imageData[offset + 2] = b;
|
|
|
imageData[offset + 3] = a;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private static void ConvertRgb565ToRgb888(ushort color, out byte r, out byte g, out byte b)
|
|
|
{
|
|
|
int temp;
|
|
|
|
|
|
temp = (color >> 11) * 255 + 16;
|
|
|
r = (byte) ((temp / 32 + temp) / 32);
|
|
|
temp = ((color & 0x07E0) >> 5) * 255 + 32;
|
|
|
g = (byte) ((temp / 64 + temp) / 64);
|
|
|
temp = (color & 0x001F) * 255 + 16;
|
|
|
b = (byte) ((temp / 32 + temp) / 32);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
}
|
|
|
}
|
|
|
|