Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaClient/ClientSource/Utils/TextureLoader.cs
2022-02-26 02:43:01 +09:00

246 lines
8.4 KiB
C#

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using Barotrauma.IO;
using System.Threading.Tasks;
using Lidgren.Network;
using Color = Microsoft.Xna.Framework.Color;
namespace Barotrauma
{
/// <summary>
/// Based on http://jakepoz.com/jake_poznanski__background_load_xna.html
/// </summary>
public static class TextureLoader
{
public static Texture2D PlaceHolderTexture
{
get;
private set;
}
private static volatile bool cancelAll = false;
public static void Init(GraphicsDevice graphicsDevice, bool needsBmp = false)
{
_graphicsDevice = graphicsDevice;
Color[] data = new Color[32 * 32];
for (int i = 0; i < 32 * 32; i++)
{
data[i] = Color.Magenta;
}
CrossThread.RequestExecutionOnMainThread(() =>
{
PlaceHolderTexture = new Texture2D(graphicsDevice, 32, 32);
PlaceHolderTexture.SetData(data);
});
}
public static void CancelAll()
{
cancelAll = true;
}
private static byte[] CompressDxt5(byte[] data, int width, int height)
{
using (System.IO.MemoryStream mstream = new System.IO.MemoryStream())
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
int offset = x * 4 + y * 4 * width;
CompressDxt5Block(data, offset, width, mstream);
}
}
return mstream.ToArray();
}
}
private static void CompressDxt5Block(byte[] data, int offset, int width, System.IO.Stream output)
{
int r1 = 255, g1 = 255, b1 = 255, a1 = 255;
int r2 = 0, g2 = 0, b2 = 0, a2 = 0;
//determine the two colors to interpolate between:
//color 1 represents lowest luma, color 2 represents highest luma
for (int i = 0; i < 16; i++)
{
int pixelOffset = offset + (4 * ((i % 4) + (width * (i >> 2))));
int r, g, b, a;
r = data[pixelOffset + 0];
g = data[pixelOffset + 1];
b = data[pixelOffset + 2];
a = data[pixelOffset + 3];
if (r * 299 + g * 587 + b * 114 < r1 * 299 + g1 * 587 + b1 * 114)
{
r1 = r; g1 = g; b1 = b;
}
if (r * 299 + g * 587 + b * 114 > r2 * 299 + g2 * 587 + b2 * 114)
{
r2 = r; g2 = g; b2 = b;
}
if (a < a1) { a1 = a; }
if (a > a2) { a2 = a; }
}
//convert the colors to rgb565 (16-bit rgb)
int r1_565 = (r1 * 0x1f) / 0xff; if (r1_565 > 0x1f) { r1_565 = 0x1f; }
int g1_565 = (g1 * 0x3f) / 0xff; if (g1_565 > 0x3f) { g1_565 = 0x3f; }
int b1_565 = (b1 * 0x1f) / 0xff; if (b1_565 > 0x1f) { b1_565 = 0x1f; }
int r2_565 = (r2 * 0x1f) / 0xff; if (r2_565 > 0x1f) { r2_565 = 0x1f; }
int g2_565 = (g2 * 0x3f) / 0xff; if (g2_565 > 0x3f) { g2_565 = 0x3f; }
int b2_565 = (b2 * 0x1f) / 0xff; if (b2_565 > 0x1f) { b2_565 = 0x1f; }
//luma is also used to determine which color on the palette
//most closely resembles each pixel to compress, so we
//calculate this here
int y1 = r1 * 299 + g1 * 587 + b1 * 114;
int y2 = r2 * 299 + g2 * 587 + b2 * 114;
byte[] newData = new byte[16];
for (int i = 0; i < 16; i++)
{
int pixelOffset = offset + (4 * ((i % 4) + (width * (i >> 2))));
int r, g, b, a;
r = data[pixelOffset + 0];
g = data[pixelOffset + 1];
b = data[pixelOffset + 2];
a = data[pixelOffset + 3];
if (a1 < a2)
{
a -= a1;
a = (a * 0x7) / (a2 - a1);
if (a > 0x7) { a = 0x7; }
switch (a)
{
case 0:
a = 1;
break;
case 1:
a = 7;
break;
case 2:
a = 6;
break;
case 3:
a = 5;
break;
case 4:
a = 4;
break;
case 5:
a = 3;
break;
case 6:
a = 2;
break;
case 7:
a = 0;
break;
}
}
else
{
a = 0;
}
NetBitWriter.WriteUInt32((uint)a, 3, newData, 16 + (i * 3));
int y = r * 299 + g * 587 + b * 114;
int max = y2 - y1;
int diffY = y - y1;
int paletteIndex;
if (diffY < max / 4)
{
paletteIndex = 0;
}
else if (diffY < max / 2)
{
paletteIndex = 2;
}
else if (diffY < max * 3 / 4)
{
paletteIndex = 3;
}
else
{
paletteIndex = 1;
}
newData[12 + (i / 4)] |= (byte)(paletteIndex << (2 * (i % 4)));
}
newData[0] = (byte)a2;
newData[1] = (byte)a1;
newData[9] = (byte)((r1_565 << 3) | (g1_565 >> 3));
newData[8] = (byte)((g1_565 << 5) | b1_565);
newData[11] = (byte)((r2_565 << 3) | (g2_565 >> 3));
newData[10] = (byte)((g2_565 << 5) | b2_565);
output.Write(newData, 0, 16);
}
public static Texture2D FromFile(string path, bool compress = true, bool mipmap = false)
{
using (FileStream fileStream = File.OpenRead(path))
{
return FromStream(fileStream, path, compress, mipmap);
}
}
public static Texture2D FromStream(System.IO.Stream stream, string path = null, bool compress = true, bool mipmap = false)
{
try
{
path = path.CleanUpPath();
byte[] textureData = null;
textureData = Texture2D.TextureDataFromStream(stream, out int width, out int height, out int channels);
SurfaceFormat format = SurfaceFormat.Color;
if (GameSettings.CurrentConfig.Graphics.CompressTextures && compress)
{
if (((width & 0x03) == 0) && ((height & 0x03) == 0))
{
textureData = CompressDxt5(textureData, width, height);
format = SurfaceFormat.Dxt5;
mipmap = false;
}
else
{
DebugConsole.AddWarning($"Could not compress a texture because the dimensions aren't a multiple of 4 (path: {path ?? "null"}, size: {width}x{height})");
}
}
Texture2D tex = null;
CrossThread.RequestExecutionOnMainThread(() =>
{
if (cancelAll) { return; }
tex = new Texture2D(_graphicsDevice, width, height, mipmap, format);
tex.SetData(textureData);
});
return tex;
}
catch (Exception e)
{
#if WINDOWS
if (e is SharpDX.SharpDXException) { throw; }
#endif
DebugConsole.ThrowError(string.IsNullOrEmpty(path) ? "Loading texture from stream failed!" :
"Loading texture \"" + path + "\" failed!", e);
return null;
}
}
private static GraphicsDevice _graphicsDevice;
}
}