217 lines
6.5 KiB
C#
217 lines
6.5 KiB
C#
// Initial version at: http://wiki.unity3d.com/index.php/TextureScale#TextureScale.cs
|
|
// Modified by ftc to work with raw texture data (it consumes less memory).
|
|
// Currently works only on RGB24 and RGBA32 textures.
|
|
|
|
using System;
|
|
using System.Threading;
|
|
using Unity.Collections;
|
|
using UnityEngine;
|
|
|
|
public class TextureScale
|
|
{
|
|
public class ThreadData
|
|
{
|
|
public int start;
|
|
public int end;
|
|
public ThreadData(int s, int e)
|
|
{
|
|
start = s;
|
|
end = e;
|
|
}
|
|
}
|
|
|
|
private static NativeArray<byte> texBytes;
|
|
private static TextureFormat texFormat;
|
|
private static Color32[] newColors;
|
|
private static int h;
|
|
private static int w;
|
|
private static float ratioX;
|
|
private static float ratioY;
|
|
private static int w2;
|
|
private static int finishCount;
|
|
private static Mutex mutex;
|
|
|
|
public static void Point(Texture2D tex, int newWidth, int newHeight)
|
|
{
|
|
ThreadedScale(tex, newWidth, newHeight, false);
|
|
}
|
|
|
|
public static void Bilinear(Texture2D tex, int newWidth, int newHeight)
|
|
{
|
|
ThreadedScale(tex, newWidth, newHeight, true);
|
|
}
|
|
|
|
private static void ThreadedScale(Texture2D tex, int newWidth, int newHeight, bool useBilinear)
|
|
{
|
|
texBytes = tex.GetRawTextureData<byte>();
|
|
texFormat = tex.format;
|
|
if (texFormat != TextureFormat.RGB24 && texFormat != TextureFormat.RGBA32)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
if (newColors == null || newColors.Length < newWidth * newHeight)
|
|
{
|
|
newColors = new Color32[newWidth * newHeight];
|
|
}
|
|
if (useBilinear)
|
|
{
|
|
ratioX = 1.0f / ((float)newWidth / (tex.width - 1));
|
|
ratioY = 1.0f / ((float)newHeight / (tex.height - 1));
|
|
}
|
|
else
|
|
{
|
|
ratioX = ((float)tex.width) / newWidth;
|
|
ratioY = ((float)tex.height) / newHeight;
|
|
}
|
|
w = tex.width;
|
|
h = tex.height;
|
|
w2 = newWidth;
|
|
var cores = Mathf.Min(SystemInfo.processorCount, newHeight);
|
|
var slice = newHeight / cores;
|
|
|
|
finishCount = 0;
|
|
if (mutex == null)
|
|
{
|
|
mutex = new Mutex(false);
|
|
}
|
|
if (cores > 1)
|
|
{
|
|
int i = 0;
|
|
ThreadData threadData;
|
|
for (i = 0; i < cores - 1; i++)
|
|
{
|
|
threadData = new ThreadData(slice * i, slice * (i + 1));
|
|
ParameterizedThreadStart ts = useBilinear ? new ParameterizedThreadStart(BilinearScale) : new ParameterizedThreadStart(PointScale);
|
|
Thread thread = new Thread(ts);
|
|
thread.Start(threadData);
|
|
}
|
|
threadData = new ThreadData(slice * i, newHeight);
|
|
if (useBilinear)
|
|
{
|
|
BilinearScale(threadData);
|
|
}
|
|
else
|
|
{
|
|
PointScale(threadData);
|
|
}
|
|
while (finishCount < cores)
|
|
{
|
|
Thread.Sleep(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ThreadData threadData = new ThreadData(0, newHeight);
|
|
if (useBilinear)
|
|
{
|
|
BilinearScale(threadData);
|
|
}
|
|
else
|
|
{
|
|
PointScale(threadData);
|
|
}
|
|
}
|
|
|
|
tex.Resize(newWidth, newHeight);
|
|
var newBytes = tex.GetRawTextureData<byte>();
|
|
for (int i = 0; i < newWidth * newHeight; i++)
|
|
{
|
|
var color = newColors[i];
|
|
if (tex.format == TextureFormat.RGB24)
|
|
{
|
|
int s = 3 * i;
|
|
newBytes[s] = color.r;
|
|
newBytes[s + 1] = color.g;
|
|
newBytes[s + 2] = color.b;
|
|
}
|
|
else if (tex.format == TextureFormat.RGBA32)
|
|
{
|
|
int s = 4 * i;
|
|
newBytes[s] = color.r;
|
|
newBytes[s + 1] = color.g;
|
|
newBytes[s + 2] = color.b;
|
|
newBytes[s + 3] = color.a;
|
|
}
|
|
}
|
|
tex.Apply();
|
|
|
|
//newColors = null;
|
|
}
|
|
|
|
private static Color32 GetColor(int position)
|
|
{
|
|
if (texFormat == TextureFormat.RGB24)
|
|
{
|
|
int s = (int)position * 3;
|
|
Color32 result = new Color32();
|
|
result.r = texBytes[s];
|
|
result.g = texBytes[s + 1];
|
|
result.b = texBytes[s + 2];
|
|
result.a = 0;
|
|
return result;
|
|
}
|
|
else if (texFormat == TextureFormat.RGBA32)
|
|
{
|
|
int s = (int)position * 4;
|
|
Color32 result = new Color32();
|
|
result.r = texBytes[s];
|
|
result.g = texBytes[s + 1];
|
|
result.b = texBytes[s + 2];
|
|
result.a = texBytes[s + 3];
|
|
return result;
|
|
}
|
|
else throw new NotImplementedException();
|
|
}
|
|
|
|
public static void BilinearScale(System.Object obj)
|
|
{
|
|
ThreadData threadData = (ThreadData)obj;
|
|
for (var y = threadData.start; y < threadData.end; y++)
|
|
{
|
|
int yFloor = (int)Mathf.Floor(y * ratioY);
|
|
while (yFloor + 1 >= h) yFloor--;
|
|
var y1 = yFloor * w;
|
|
var y2 = (yFloor + 1) * w;
|
|
var yw = y * w2;
|
|
|
|
for (var x = 0; x < w2; x++)
|
|
{
|
|
int xFloor = (int)Mathf.Floor(x * ratioX);
|
|
var xLerp = x * ratioX - xFloor;
|
|
var c1 = ColorLerpUnclamped(GetColor(y1 + xFloor), GetColor(y1 + xFloor + 1), xLerp);
|
|
var c2 = ColorLerpUnclamped(GetColor(y2 + xFloor), GetColor(y2 + xFloor + 1), xLerp);
|
|
newColors[yw + x] = ColorLerpUnclamped(c1, c2, y * ratioY - yFloor);
|
|
}
|
|
}
|
|
mutex.WaitOne();
|
|
finishCount++;
|
|
mutex.ReleaseMutex();
|
|
}
|
|
|
|
public static void PointScale(System.Object obj)
|
|
{
|
|
ThreadData threadData = (ThreadData)obj;
|
|
for (var y = threadData.start; y < threadData.end; y++)
|
|
{
|
|
var thisY = (int)(ratioY * y) * w;
|
|
var yw = y * w2;
|
|
for (var x = 0; x < w2; x++)
|
|
{
|
|
newColors[yw + x] = GetColor((int)(thisY + ratioX * x));
|
|
}
|
|
}
|
|
|
|
mutex.WaitOne();
|
|
finishCount++;
|
|
mutex.ReleaseMutex();
|
|
}
|
|
|
|
private static Color32 ColorLerpUnclamped(Color32 c1, Color32 c2, float value)
|
|
{
|
|
return new Color32((byte)(c1.r + (c2.r - c1.r) * value),
|
|
(byte)(c1.g + (c2.g - c1.g) * value),
|
|
(byte)(c1.b + (c2.b - c1.b) * value),
|
|
(byte)(c1.a + (c2.a - c1.a) * value));
|
|
}
|
|
}
|