2019-07-03 00:02:40 +02:00
|
|
|
|
//https://github.com/maxartz15/MA_TextureUtils
|
|
|
|
|
|
|
|
|
|
//References:
|
|
|
|
|
//http://www.gamasutra.com/blogs/JoshSutphin/20131007/201829/Adding_to_Unitys_BuiltIn_Classes_Using_Extension_Methods.php
|
|
|
|
|
//https://forum.unity3d.com/threads/contribution-texture2d-blur-in-c.185694/
|
|
|
|
|
//http://orbcreation.com/orbcreation/page.orb?1180
|
|
|
|
|
//https://support.unity3d.com/hc/en-us/articles/206486626-How-can-I-get-pixels-from-unreadable-textures-
|
2019-08-30 01:11:33 +02:00
|
|
|
|
//https://github.com/maxartz15/MA_TextureAtlasser/commit/9f5240967a51692fa2a17a6b3c8d124dd5dc60f9
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-05-04 13:29:02 +02:00
|
|
|
|
#if UNITY_EDITOR
|
2018-08-27 11:55:21 +02:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEditor;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
2019-08-30 01:11:33 +02:00
|
|
|
|
using System;
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
|
|
|
|
namespace MA_Texture
|
|
|
|
|
{
|
|
|
|
|
public static class MA_TextureUtils
|
|
|
|
|
{
|
|
|
|
|
public static Texture ConvertToReadableTexture(Texture texture)
|
|
|
|
|
{
|
|
|
|
|
if (texture == null)
|
|
|
|
|
return texture;
|
|
|
|
|
// Create a temporary RenderTexture of the same size as the texture
|
|
|
|
|
RenderTexture tmp = RenderTexture.GetTemporary(
|
|
|
|
|
texture.width,
|
|
|
|
|
texture.height,
|
|
|
|
|
0,
|
|
|
|
|
RenderTextureFormat.Default,
|
|
|
|
|
RenderTextureReadWrite.Linear);
|
|
|
|
|
|
|
|
|
|
// Blit the pixels on texture to the RenderTexture
|
|
|
|
|
Graphics.Blit(texture, tmp);
|
|
|
|
|
|
|
|
|
|
// Backup the currently set RenderTexture
|
|
|
|
|
RenderTexture previous = RenderTexture.active;
|
|
|
|
|
|
|
|
|
|
// Set the current RenderTexture to the temporary one we created
|
|
|
|
|
RenderTexture.active = tmp;
|
|
|
|
|
|
|
|
|
|
// Create a new readable Texture2D to copy the pixels to it
|
|
|
|
|
Texture2D myTexture2D = new Texture2D(texture.width, texture.width);
|
|
|
|
|
|
|
|
|
|
// Copy the pixels from the RenderTexture to the new Texture
|
|
|
|
|
myTexture2D.ReadPixels(new Rect(0, 0, tmp.width, tmp.height), 0, 0);
|
|
|
|
|
myTexture2D.Apply();
|
2019-07-26 14:52:40 +02:00
|
|
|
|
myTexture2D.name = texture.name;
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
|
|
|
|
// Reset the active RenderTexture
|
|
|
|
|
RenderTexture.active = previous;
|
|
|
|
|
|
|
|
|
|
// Release the temporary RenderTexture
|
|
|
|
|
RenderTexture.ReleaseTemporary(tmp);
|
|
|
|
|
// "myTexture2D" now has the same pixels from "texture" and it's readable.
|
|
|
|
|
|
|
|
|
|
return myTexture2D;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Save
|
|
|
|
|
public static Texture2D MA_Save2D(this Texture2D texture, string textureName, string savePath)
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(savePath))
|
|
|
|
|
Directory.CreateDirectory(savePath);
|
|
|
|
|
|
|
|
|
|
FileStream fs = new FileStream(savePath + "/" + textureName + ".png", FileMode.Create);
|
|
|
|
|
BinaryWriter bw = new BinaryWriter(fs);
|
|
|
|
|
|
|
|
|
|
bw.Write(texture.EncodeToPNG());
|
|
|
|
|
bw.Close();
|
|
|
|
|
fs.Close();
|
|
|
|
|
|
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
|
|
|
|
|
|
return texture;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Texture MA_Save(this Texture texture, string name, string savePath)
|
|
|
|
|
{
|
|
|
|
|
Texture2D texture2D = (Texture2D)MA_TextureUtils.ConvertToReadableTexture(texture);
|
|
|
|
|
|
|
|
|
|
texture2D.MA_Save2D(name, savePath);
|
|
|
|
|
|
|
|
|
|
texture = texture2D;
|
|
|
|
|
|
|
|
|
|
return texture;
|
|
|
|
|
}
|
2019-08-30 01:11:33 +02:00
|
|
|
|
#endregion
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
#region Scale
|
|
|
|
|
public enum TextureScaleMode
|
|
|
|
|
{
|
|
|
|
|
Bilinear,
|
|
|
|
|
Point
|
|
|
|
|
}
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
public static Texture MA_Scale(this Texture texture, int width, int height, TextureScaleMode scaleMode)
|
2018-08-27 11:55:21 +02:00
|
|
|
|
{
|
2019-08-30 01:11:33 +02:00
|
|
|
|
Texture2D texture2D = (Texture2D)MA_TextureUtils.ConvertToReadableTexture(texture);
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
texture2D.MA_Scale2D(width, height, scaleMode);
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
texture = texture2D;
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
|
|
|
|
return texture;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
public static Texture2D MA_Scale2D(this Texture2D texture, int newWidth, int newHeight, TextureScaleMode scaleMode)
|
|
|
|
|
{
|
|
|
|
|
Color[] curColors = texture.GetPixels();
|
|
|
|
|
Color[] newColors = new Color[newWidth * newHeight];
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
switch (scaleMode)
|
|
|
|
|
{
|
|
|
|
|
case TextureScaleMode.Bilinear:
|
|
|
|
|
newColors = MA_BilinearScale(curColors, texture.width, texture.height, newWidth, newHeight);
|
|
|
|
|
break;
|
|
|
|
|
case TextureScaleMode.Point:
|
|
|
|
|
newColors = MA_PointScale(curColors, texture.width, texture.height, newWidth, newHeight);
|
|
|
|
|
break;
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
}
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
texture.Resize(newWidth, newHeight);
|
|
|
|
|
texture.SetPixels(newColors);
|
|
|
|
|
texture.Apply();
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
return texture;
|
|
|
|
|
}
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
private static Color[] MA_BilinearScale(Color[] curColors, int curWidth, int curHeight, int newWidth, int newHeight)
|
|
|
|
|
{
|
|
|
|
|
Color[] newColors = new Color[newWidth * newHeight];
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
float ratioX = 1.0f / ((float)newWidth / (curWidth - 1));
|
|
|
|
|
float ratioY = 1.0f / ((float)newHeight / (curHeight - 1));
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
for (int y = 0; y < newHeight; y++)
|
|
|
|
|
{
|
|
|
|
|
int yFloor = Mathf.FloorToInt(y * ratioY);
|
|
|
|
|
var y1 = yFloor * curWidth;
|
|
|
|
|
var y2 = (yFloor + 1) * curWidth;
|
|
|
|
|
var yw = y * newWidth;
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
for (int x = 0; x < newWidth; x++)
|
|
|
|
|
{
|
|
|
|
|
int xFloor = Mathf.FloorToInt(x * ratioX);
|
|
|
|
|
var xLerp = x * ratioX - xFloor;
|
|
|
|
|
|
|
|
|
|
newColors[yw + x] = ColorLerpUnclamped(ColorLerpUnclamped(curColors[y1 + xFloor], curColors[y1 + xFloor + 1], xLerp),
|
|
|
|
|
ColorLerpUnclamped(curColors[y2 + xFloor], curColors[y2 + xFloor + 1], xLerp),
|
|
|
|
|
y * ratioY - yFloor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newColors;
|
|
|
|
|
}
|
2019-07-16 14:12:32 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
private static Color[] MA_PointScale(Color[] curColors, int curWidth, int curHeight, int newWidth, int newHeight)
|
2019-07-16 14:12:32 +02:00
|
|
|
|
{
|
2019-08-30 01:11:33 +02:00
|
|
|
|
Color[] newColors = new Color[newWidth * newHeight];
|
2019-07-16 14:12:32 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
float ratioX = ((float)curWidth) / newWidth;
|
|
|
|
|
float ratioY = ((float)curHeight) / newHeight;
|
2019-07-16 14:12:32 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
for (int y = 0; y < newHeight; y++)
|
2019-07-16 14:12:32 +02:00
|
|
|
|
{
|
2019-08-30 01:11:33 +02:00
|
|
|
|
var thisY = Mathf.RoundToInt((ratioY * y) * curWidth);
|
|
|
|
|
var yw = y * newWidth;
|
|
|
|
|
|
|
|
|
|
for (int x = 0; x < newWidth; x++)
|
2019-07-16 14:12:32 +02:00
|
|
|
|
{
|
2019-08-30 01:11:33 +02:00
|
|
|
|
newColors[yw + x] = curColors[Mathf.RoundToInt(thisY + ratioX * x)];
|
2019-07-16 14:12:32 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
return newColors;
|
|
|
|
|
}
|
2019-07-16 14:12:32 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
private static Color ColorLerpUnclamped(Color c1, Color c2, float value)
|
|
|
|
|
{
|
|
|
|
|
return new Color(c1.r + (c2.r - c1.r) * value,
|
|
|
|
|
c1.g + (c2.g - c1.g) * value,
|
|
|
|
|
c1.b + (c2.b - c1.b) * value,
|
|
|
|
|
c1.a + (c2.a - c1.a) * value);
|
2019-07-16 14:12:32 +02:00
|
|
|
|
}
|
2019-07-26 14:52:40 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
#endregion
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
2019-08-30 01:11:33 +02:00
|
|
|
|
#region combine
|
|
|
|
|
public static Texture2D MA_Combine2D(this Texture2D texture, Texture2D combineTexture, int offsetX, int offsetY, bool flipY = true)
|
2018-08-27 11:55:21 +02:00
|
|
|
|
{
|
|
|
|
|
for (int x = 0; x < combineTexture.width; x++)
|
|
|
|
|
{
|
|
|
|
|
if(flipY)
|
|
|
|
|
{
|
|
|
|
|
//Y is 'flipped' because textures are made from left to right, bottom to top. We want to draw from left to right and top to bottom.
|
|
|
|
|
for (int y = combineTexture.height; y > 0; y--)
|
|
|
|
|
{
|
2019-07-23 22:48:36 +02:00
|
|
|
|
texture.SetPixel(x + offsetX, y + (texture.height - offsetY - combineTexture.height), combineTexture.GetPixel(x, y));
|
2018-08-27 11:55:21 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (int y = 0; y < combineTexture.height; y++)
|
|
|
|
|
{
|
|
|
|
|
texture.SetPixel(x + offsetX, y + offsetY, combineTexture.GetPixel(x, y));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
texture.Apply();
|
|
|
|
|
|
|
|
|
|
return texture;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Texture MA_Combine(this Texture texture, Texture combineTexture, int offsetX, int offsetY)
|
|
|
|
|
{
|
|
|
|
|
Texture2D texture2D = (Texture2D)MA_TextureUtils.ConvertToReadableTexture(texture);
|
2019-11-06 18:08:01 +01:00
|
|
|
|
Texture2D combineTexture2D = (Texture2D)MA_TextureUtils.ConvertToReadableTexture(combineTexture);
|
2018-08-27 11:55:21 +02:00
|
|
|
|
|
|
|
|
|
texture = texture2D.MA_Combine2D(combineTexture2D, offsetX, offsetY);
|
|
|
|
|
|
|
|
|
|
return texture;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
2019-05-04 13:29:02 +02:00
|
|
|
|
}
|
|
|
|
|
#endif
|