Client LuaCsForBarotrauma
TextureLoader.cs
1 using Microsoft.Xna.Framework.Graphics;
2 using System;
3 using System.Threading.Tasks;
4 using Barotrauma.IO;
5 using Lidgren.Network;
6 using Color = Microsoft.Xna.Framework.Color;
7 
8 namespace Barotrauma
9 {
13  public static class TextureLoader
14  {
15  public static Texture2D PlaceHolderTexture
16  {
17  get;
18  private set;
19  }
20 
21  private static volatile bool cancelAll = false;
22 
23  public static void Init(GraphicsDevice graphicsDevice, bool needsBmp = false)
24  {
25  _graphicsDevice = graphicsDevice;
26 
27  Color[] data = new Color[32 * 32];
28  for (int i = 0; i < 32 * 32; i++)
29  {
30  data[i] = Color.Magenta;
31  }
32 
33  CrossThread.RequestExecutionOnMainThread(() =>
34  {
35  PlaceHolderTexture = new Texture2D(graphicsDevice, 32, 32);
36  PlaceHolderTexture.SetData(data);
37  });
38  }
39 
40  public static void CancelAll()
41  {
42  cancelAll = true;
43  }
44 
45  private static byte[] CompressDxt5(byte[] data, int width, int height)
46  {
47  var output = new byte[width * height];
48  Parallel.For(
49  fromInclusive: 0,
50  toExclusive: width * height / 16,
51  i =>
52  {
53  int i4 = i * 4;
54  int inputOffset = (i4 % width + (i4 / width) * 4 * width) * 4;
55  int outputOffset = i * 16;
56  CompressDxt5Block(data, inputOffset, width, output, outputOffset);
57  });
58  return output;
59  }
60 
61  private static void CompressDxt5Block(byte[] data, int inputOffset, int width, byte[] output, int outputOffset)
62  {
63  int r1 = 255, g1 = 255, b1 = 255, a1 = 255;
64  int r2 = 0, g2 = 0, b2 = 0, a2 = 0;
65 
66  // Determine the two colors to interpolate between:
67  // color 1 represents lowest luma, color 2 represents highest luma.
68  // Luma is also used to determine which color on the palette
69  // most closely resembles each pixel to compress, so we
70  // cache our calculations here.
71  int y1 = 255000;
72  int y2 = 0;
73  for (int i = 0; i < 16; i++)
74  {
75  int pixelOffset = inputOffset + (4 * ((i % 4) + (width * (i >> 2))));
76  int r = data[pixelOffset + 0];
77  int g = data[pixelOffset + 1];
78  int b = data[pixelOffset + 2];
79  int a = data[pixelOffset + 3];
80  int y = r * 299 + g * 587 + b * 114;
81  if (y < y1)
82  {
83  r1 = r; g1 = g; b1 = b; y1 = y;
84  }
85  if (y > y2)
86  {
87  r2 = r; g2 = g; b2 = b; y2 = y;
88  }
89  if (a < a1) { a1 = a; }
90  if (a > a2) { a2 = a; }
91  }
92 
93  //convert the colors to rgb565 (16-bit rgb)
94  int r1_565 = r1 >> (8 - 5);
95  int g1_565 = g1 >> (8 - 6);
96  int b1_565 = b1 >> (8 - 5);
97 
98  int r2_565 = r2 >> (8 - 5);
99  int g2_565 = g2 >> (8 - 6);
100  int b2_565 = b2 >> (8 - 5);
101 
102  int y2y1Diff = y2 - y1;
103  if (y2y1Diff > 0 || a1 < a2)
104  {
105  for (int i = 0; i < 16; i++)
106  {
107  int pixelOffset = inputOffset + (4 * ((i % 4) + (width * (i >> 2))));
108  int r = data[pixelOffset + 0];
109  int g = data[pixelOffset + 1];
110  int b = data[pixelOffset + 2];
111 
112  if (a1 < a2)
113  {
114  int a = data[pixelOffset + 3];
115  a -= a1;
116  a = (a * 0x7) / (a2 - a1);
117  if (a < 0x7)
118  {
119  a = a switch
120  {
121  0 => 1,
122  1 => 7,
123  _ => 8 - a
124  };
125  NetBitWriter.WriteByte((byte)a, 3, output, (outputOffset * 8) + 16 + (i * 3));
126  }
127  }
128 
129  if (y2y1Diff <= 0) { continue; }
130 
131  int y = r * 299 + g * 587 + b * 114;
132  int diffY = y - y1;
133  int paletteIndex = (diffY * 4) / y2y1Diff;
134  paletteIndex = paletteIndex switch
135  {
136  0 => 0,
137  1 => 2,
138  2 => 3,
139  _ => 1
140  };
141  output[outputOffset + 12 + (i / 4)] |= (byte)(paletteIndex << (2 * (i % 4)));
142  }
143  }
144 
145  output[outputOffset + 0] = (byte)a2;
146  output[outputOffset + 1] = (byte)a1;
147 
148  output[outputOffset + 9] = (byte)((r1_565 << 3) | (g1_565 >> 3));
149  output[outputOffset + 8] = (byte)((g1_565 << 5) | b1_565);
150  output[outputOffset + 11] = (byte)((r2_565 << 3) | (g2_565 >> 3));
151  output[outputOffset + 10] = (byte)((g2_565 << 5) | b2_565);
152  }
153 
154  public static Texture2D FromFile(string path, bool compress = true, bool mipmap = false, ContentPackage contentPackage = null)
155  {
156  using FileStream fileStream = File.OpenRead(path);
157  return FromStream(fileStream, path, compress, mipmap, contentPackage);
158  }
159 
160  public static Texture2D FromStream(System.IO.Stream stream, string path = null, bool compress = true, bool mipmap = false, ContentPackage contentPackage = null)
161  {
162  try
163  {
164  path = path.CleanUpPath();
165  byte[] textureData = null;
166  textureData = Texture2D.TextureDataFromStream(stream, out int width, out int height, out int channels);
167 
168  SurfaceFormat format = SurfaceFormat.Color;
169  if (GameSettings.CurrentConfig.Graphics.CompressTextures && compress)
170  {
171  if (((width & 0x03) == 0) && ((height & 0x03) == 0))
172  {
173  textureData = CompressDxt5(textureData, width, height);
174  format = SurfaceFormat.Dxt5;
175  mipmap = false;
176  }
177  else
178  {
179  DebugConsole.AddWarning($"Could not compress a texture because the dimensions aren't a multiple of 4 (path: {path ?? "null"}, size: {width}x{height})",
180  contentPackage);
181  }
182  }
183 
184  Texture2D tex = null;
185  CrossThread.RequestExecutionOnMainThread(() =>
186  {
187  if (cancelAll) { return; }
188  tex = new Texture2D(_graphicsDevice, width, height, mipmap, format);
189  tex.SetData(textureData);
190  });
191  return tex;
192  }
193  catch (Exception e)
194  {
195 #if WINDOWS
196  if (e is SharpDX.SharpDXException) { throw; }
197 #endif
198 
199  DebugConsole.ThrowError(string.IsNullOrEmpty(path) ? "Loading texture from stream failed!" :
200  "Loading texture \"" + path + "\" failed!", e);
201  return null;
202  }
203  }
204 
205  private static GraphicsDevice _graphicsDevice;
206  }
207 }