mirror of
https://github.com/AssetRipper/AssetRipper.git
synced 2025-12-11 20:15:29 +01:00
597 lines
21 KiB
C#
597 lines
21 KiB
C#
using AssetRipper.Export.UnityProjects.Textures;
|
|
using AssetRipper.Import.Logging;
|
|
using AssetRipper.SourceGenerated.Classes.ClassID_117;
|
|
using AssetRipper.SourceGenerated.Classes.ClassID_187;
|
|
using AssetRipper.SourceGenerated.Classes.ClassID_188;
|
|
using AssetRipper.SourceGenerated.Classes.ClassID_189;
|
|
using AssetRipper.SourceGenerated.Classes.ClassID_28;
|
|
using AssetRipper.SourceGenerated.Classes.ClassID_89;
|
|
using AssetRipper.SourceGenerated.Enums;
|
|
using AssetRipper.SourceGenerated.Extensions;
|
|
using AssetRipper.TextureDecoder.Astc;
|
|
using AssetRipper.TextureDecoder.Atc;
|
|
using AssetRipper.TextureDecoder.Bc;
|
|
using AssetRipper.TextureDecoder.Dxt;
|
|
using AssetRipper.TextureDecoder.Etc;
|
|
using AssetRipper.TextureDecoder.Pvrtc;
|
|
using AssetRipper.TextureDecoder.Rgb;
|
|
using AssetRipper.TextureDecoder.Rgb.Formats;
|
|
using AssetRipper.TextureDecoder.Yuy2;
|
|
|
|
namespace AssetRipper.Export.Modules.Textures
|
|
{
|
|
public static class TextureConverter
|
|
{
|
|
public static bool TryConvertToBitmap(IImageTexture texture, out DirectBitmap bitmap)
|
|
{
|
|
return texture switch
|
|
{
|
|
ICubemapArray cubemapArray => TryConvertToBitmap(cubemapArray, out bitmap),
|
|
ITexture2DArray texture2DArray => TryConvertToBitmap(texture2DArray, out bitmap),
|
|
ITexture3D texture3D => TryConvertToBitmap(texture3D, out bitmap),
|
|
ITexture2D texture2D => TryConvertToBitmap(texture2D, out bitmap),
|
|
_ => ReturnFalse(out bitmap),
|
|
};
|
|
|
|
static bool ReturnFalse(out DirectBitmap bitmap)
|
|
{
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static bool TryConvertToBitmap(ITexture3D texture, out DirectBitmap bitmap)
|
|
{
|
|
byte[] buffer = texture.GetImageData();
|
|
if (buffer.Length == 0)
|
|
{
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
|
|
if (!TryConvertToBitmap(
|
|
texture.GetTextureFormat(),
|
|
texture.Width,
|
|
texture.Height,
|
|
texture.ImageCount,
|
|
texture.GetCompleteImageSize(),
|
|
texture.Collection.Version,
|
|
buffer,
|
|
out bitmap))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bitmap.FlipY();
|
|
|
|
// despite the name, this packing works for different formats
|
|
if (texture.LightmapFormatE == TextureUsageMode.NormalmapDXT5nm)
|
|
{
|
|
UnpackNormal(bitmap.Bits);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool TryConvertToBitmap(ITexture2DArray texture, out DirectBitmap bitmap)
|
|
{
|
|
byte[] buffer = texture.GetImageData();
|
|
if (buffer.Length == 0)
|
|
{
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
|
|
if (!TryGetTextureFormat(texture, out TextureFormat format))
|
|
{
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
|
|
if (!TryConvertToBitmap(
|
|
format,
|
|
texture.Width,
|
|
texture.Height,
|
|
texture.Depth,
|
|
texture.GetCompleteImageSize(),
|
|
texture.Collection.Version,
|
|
buffer,
|
|
out bitmap))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bitmap.FlipY();
|
|
|
|
return true;
|
|
|
|
static bool TryGetTextureFormat(ITexture2DArray texture, out TextureFormat format)
|
|
{
|
|
try
|
|
{
|
|
format = ((GraphicsFormat)texture.Format).ToTextureFormat();
|
|
return true;
|
|
}
|
|
catch (NotSupportedException)
|
|
{
|
|
format = default;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool TryConvertToBitmap(ICubemapArray texture, out DirectBitmap bitmap)
|
|
{
|
|
byte[] buffer = texture.GetImageData();
|
|
if (buffer.Length == 0)
|
|
{
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
|
|
if (!TryConvertToBitmap(
|
|
texture.FormatE,
|
|
texture.Width,
|
|
texture.GetHeight(),
|
|
texture.GetDepth(),
|
|
texture.GetCompleteImageSize(),
|
|
texture.Collection.Version,
|
|
buffer,
|
|
out bitmap))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bitmap.FlipY();// Maybe not needed?
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool TryConvertToBitmap(ITexture2D texture, out DirectBitmap bitmap)
|
|
{
|
|
byte[] buffer = texture.GetImageData();
|
|
if (buffer.Length == 0)
|
|
{
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
|
|
if (!TryConvertToBitmap(
|
|
texture.Format_C28E,
|
|
texture.Width_C28,
|
|
texture.Height_C28,
|
|
texture.ImageCount_C28,
|
|
texture.GetCompleteImageSize(),
|
|
texture.Collection.Version,
|
|
buffer,
|
|
out bitmap))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// cubemaps dont need flipping, for some reason
|
|
if (texture is not ICubemap)
|
|
{
|
|
bitmap.FlipY();
|
|
}
|
|
|
|
// despite the name, this packing works for different formats
|
|
if (texture.LightmapFormat_C28E == TextureUsageMode.NormalmapDXT5nm)
|
|
{
|
|
UnpackNormal(bitmap.Bits);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool TryConvertToBitmap(
|
|
TextureFormat textureFormat,
|
|
int width,
|
|
int height,
|
|
int depth,
|
|
int imageSize,
|
|
UnityVersion version,
|
|
byte[] data,
|
|
out DirectBitmap bitmap)
|
|
{
|
|
return textureFormat switch
|
|
{
|
|
TextureFormat.Alpha8 => TryConvertToBitmap<ColorA<byte>, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.ARGB4444 => TryConvertToBitmap<ColorARGB16, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGB24 => TryConvertToBitmap<ColorRGB<byte>, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGBA32 => TryConvertToBitmap<ColorRGBA<byte>, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.ARGB32 => TryConvertToBitmap<ColorARGB32, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGB565 => TryConvertToBitmap<ColorRGB16, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.R16 => TryConvertToBitmap<ColorR<ushort>, ushort>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGBA4444 => TryConvertToBitmap<ColorRGBA16, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.BGRA32_14 or TextureFormat.BGRA32_37 => TryConvertToBitmap<ColorBGRA32, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RHalf => TryConvertToBitmap<ColorR<Half>, Half>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGHalf => TryConvertToBitmap<ColorRG<Half>, Half>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGBAHalf => TryConvertToBitmap<ColorRGBA<Half>, Half>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RFloat => TryConvertToBitmap<ColorR<float>, float>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGFloat => TryConvertToBitmap<ColorRG<float>, float>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGBAFloat => TryConvertToBitmap<ColorRGBA<float>, float>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGB9e5Float => TryConvertToBitmap<ColorRGB9e5, double>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RG16 => TryConvertToBitmap<ColorRG<byte>, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.R8 => TryConvertToBitmap<ColorR<byte>, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RG32 => TryConvertToBitmap<ColorRG<ushort>, ushort>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGB48 => TryConvertToBitmap<ColorRGB<ushort>, ushort>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGBA64 => TryConvertToBitmap<ColorRGBA<ushort>, ushort>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.R8_SIGNED => TryConvertToBitmap<ColorR<sbyte>, sbyte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RG16_SIGNED => TryConvertToBitmap<ColorRG<sbyte>, sbyte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGB24_SIGNED => TryConvertToBitmap<ColorRGB<sbyte>, sbyte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGBA32_SIGNED => TryConvertToBitmap<ColorRGBA<sbyte>, sbyte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.R16_SIGNED => TryConvertToBitmap<ColorR<short>, short>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RG32_SIGNED => TryConvertToBitmap<ColorRG<short>, short>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGB48_SIGNED => TryConvertToBitmap<ColorRGB<short>, short>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
TextureFormat.RGBA64_SIGNED => TryConvertToBitmap<ColorRGBA<short>, short>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
_ => TryConvertToBitmap<ColorBGRA32, byte>(textureFormat, width, height, depth, imageSize, version, data, out bitmap),
|
|
};
|
|
}
|
|
|
|
private static bool TryConvertToBitmap<TColor, TChannelValue>(
|
|
TextureFormat textureFormat,
|
|
int width,
|
|
int height,
|
|
int depth,
|
|
int imageSize,
|
|
UnityVersion version,
|
|
byte[] data,
|
|
out DirectBitmap bitmap)
|
|
where TColor : unmanaged, IColor<TChannelValue>
|
|
where TChannelValue : unmanaged
|
|
{
|
|
if (width <= 0 || height <= 0 || depth <= 0)
|
|
{
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
|
|
if (data.Length < (long)imageSize * depth)
|
|
{
|
|
Logger.Log(LogType.Error, LogCategory.Export, $"Image data length {data.Length} is less than expected {(long)imageSize * depth}. Width: {width}, Height: {height}, Depth: {depth}, Image Size: {imageSize}, Format {textureFormat}.");
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
|
|
bitmap = new DirectBitmap<TColor, TChannelValue>(width, height, depth);
|
|
int outputSize = width * height * bitmap.PixelSize;
|
|
for (int i = 0; i < depth; i++)
|
|
{
|
|
ReadOnlySpan<byte> inputSpan = new ReadOnlySpan<byte>(data, i * imageSize, imageSize);
|
|
ReadOnlySpan<byte> uncompressedSpan;
|
|
if (textureFormat.IsCrunched())
|
|
{
|
|
if (CrunchHandler.DecompressCrunch(textureFormat, version, inputSpan, out byte[]? decompressedData))
|
|
{
|
|
uncompressedSpan = decompressedData;
|
|
}
|
|
else
|
|
{
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uncompressedSpan = inputSpan;
|
|
}
|
|
Span<byte> outputSpan = bitmap.Bits.Slice(i * outputSize, outputSize);
|
|
|
|
if (typeof(TColor) == typeof(ColorBGRA32))
|
|
{
|
|
if (!TryDecodeTexture(textureFormat, width, height, uncompressedSpan, outputSpan))
|
|
{
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!TryDecodeTexture<TColor, TChannelValue>(textureFormat, width, height, uncompressedSpan, outputSpan))
|
|
{
|
|
bitmap = DirectBitmap.Empty;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static bool TryDecodeTexture(TextureFormat textureFormat, int width, int height, ReadOnlySpan<byte> inputSpan, Span<byte> outputSpan)
|
|
{
|
|
switch (textureFormat)
|
|
{
|
|
//ASTC
|
|
case TextureFormat.ASTC_RGB_4x4:
|
|
case TextureFormat.ASTC_RGBA_4x4:
|
|
AstcDecoder.DecodeASTC(inputSpan, width, height, 4, 4, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ASTC_RGB_5x5:
|
|
case TextureFormat.ASTC_RGBA_5x5:
|
|
AstcDecoder.DecodeASTC(inputSpan, width, height, 5, 5, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ASTC_RGB_6x6:
|
|
case TextureFormat.ASTC_RGBA_6x6:
|
|
AstcDecoder.DecodeASTC(inputSpan, width, height, 6, 6, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ASTC_RGB_8x8:
|
|
case TextureFormat.ASTC_RGBA_8x8:
|
|
AstcDecoder.DecodeASTC(inputSpan, width, height, 8, 8, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ASTC_RGB_10x10:
|
|
case TextureFormat.ASTC_RGBA_10x10:
|
|
AstcDecoder.DecodeASTC(inputSpan, width, height, 10, 10, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ASTC_RGB_12x12:
|
|
case TextureFormat.ASTC_RGBA_12x12:
|
|
AstcDecoder.DecodeASTC(inputSpan, width, height, 12, 12, outputSpan);
|
|
return true;
|
|
|
|
//ATC
|
|
case TextureFormat.ATC_RGB4:
|
|
AtcDecoder.DecompressAtcRgb4(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ATC_RGBA8:
|
|
AtcDecoder.DecompressAtcRgba8(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
//BC
|
|
case TextureFormat.BC4:
|
|
case TextureFormat.BC5:
|
|
case TextureFormat.BC6H:
|
|
case TextureFormat.BC7:
|
|
return DecodeBC(inputSpan, textureFormat, width, height, outputSpan);
|
|
|
|
//ETC
|
|
case TextureFormat.ETC_RGB4:
|
|
case TextureFormat.ETC_RGB4_3DS:
|
|
case TextureFormat.ETC_RGB4Crunched:
|
|
EtcDecoder.DecompressETC(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.EAC_R:
|
|
EtcDecoder.DecompressEACRUnsigned(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.EAC_R_SIGNED:
|
|
EtcDecoder.DecompressEACRSigned(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.EAC_RG:
|
|
EtcDecoder.DecompressEACRGUnsigned(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.EAC_RG_SIGNED:
|
|
EtcDecoder.DecompressEACRGSigned(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ETC2_RGB:
|
|
EtcDecoder.DecompressETC2(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ETC2_RGBA1:
|
|
EtcDecoder.DecompressETC2A1(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ETC2_RGBA8:
|
|
case TextureFormat.ETC_RGBA8_3DS:
|
|
case TextureFormat.ETC2_RGBA8Crunched:
|
|
EtcDecoder.DecompressETC2A8(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
//PVRTC
|
|
case TextureFormat.PVRTC_RGB2:
|
|
case TextureFormat.PVRTC_RGBA2:
|
|
PvrtcDecoder.DecompressPVRTC(inputSpan, width, height, true, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.PVRTC_RGB4:
|
|
case TextureFormat.PVRTC_RGBA4:
|
|
PvrtcDecoder.DecompressPVRTC(inputSpan, width, height, false, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.BGRA32_14:
|
|
case TextureFormat.BGRA32_37:
|
|
//This needs sliced because the inputSpan can have mips.
|
|
inputSpan[..outputSpan.Length].CopyTo(outputSpan);
|
|
return true;
|
|
|
|
default:
|
|
return TryDecodeTexture<ColorBGRA32, byte>(textureFormat, width, height, inputSpan, outputSpan);
|
|
}
|
|
}
|
|
|
|
private static bool TryDecodeTexture<TColor, TChannelValue>(TextureFormat textureFormat, int width, int height, ReadOnlySpan<byte> inputSpan, Span<byte> outputSpan)
|
|
where TColor : unmanaged, IColor<TChannelValue>
|
|
where TChannelValue : unmanaged
|
|
{
|
|
switch (textureFormat)
|
|
{
|
|
//DXT
|
|
case TextureFormat.DXT1:
|
|
case TextureFormat.DXT1Crunched:
|
|
DxtDecoder.DecompressDXT1<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.DXT3:
|
|
DxtDecoder.DecompressDXT3<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.DXT5:
|
|
case TextureFormat.DXT5Crunched:
|
|
DxtDecoder.DecompressDXT5<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
//YUY2
|
|
case TextureFormat.YUY2:
|
|
Yuy2Decoder.DecompressYUY2<TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
//RGB
|
|
case TextureFormat.Alpha8:
|
|
RgbConverter.Convert<ColorA<byte>, byte, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ARGB4444:
|
|
RgbConverter.Convert<ColorARGB16, byte, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGBA4444:
|
|
RgbConverter.Convert<ColorRGBA16, byte, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGB565:
|
|
RgbConverter.Convert<ColorRGB16, byte, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.R8:
|
|
RgbConverter.Convert<ColorR<byte>, byte, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RG16:
|
|
RgbConverter.Convert<ColorRG<byte>, byte, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGB24:
|
|
RgbConverter.Convert<ColorRGB<byte>, byte, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGBA32:
|
|
RgbConverter.Convert<ColorRGBA<byte>, byte, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.ARGB32:
|
|
RgbConverter.Convert<ColorARGB32, byte, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.BGRA32_14:
|
|
case TextureFormat.BGRA32_37:
|
|
RgbConverter.Convert<ColorBGRA32, byte, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.R16:
|
|
RgbConverter.Convert<ColorR<ushort>, ushort, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RG32:
|
|
RgbConverter.Convert<ColorRG<ushort>, ushort, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGB48:
|
|
RgbConverter.Convert<ColorRGB<ushort>, ushort, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGBA64:
|
|
RgbConverter.Convert<ColorRGBA<ushort>, ushort, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RHalf:
|
|
RgbConverter.Convert<ColorR<Half>, Half, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGHalf:
|
|
RgbConverter.Convert<ColorRG<Half>, Half, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGBAHalf:
|
|
RgbConverter.Convert<ColorRGBA<Half>, Half, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RFloat:
|
|
RgbConverter.Convert<ColorR<float>, float, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGFloat:
|
|
RgbConverter.Convert<ColorRG<float>, float, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGBAFloat:
|
|
RgbConverter.Convert<ColorRGBA<float>, float, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
case TextureFormat.RGB9e5Float:
|
|
RgbConverter.Convert<ColorRGB9e5, double, TColor, TChannelValue>(inputSpan, width, height, outputSpan);
|
|
return true;
|
|
|
|
default:
|
|
Logger.Log(LogType.Error, LogCategory.Export, $"Unsupported texture format '{textureFormat}'");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static bool DecodeBC(ReadOnlySpan<byte> inputData, TextureFormat textureFormat, int width, int height, Span<byte> outputData)
|
|
{
|
|
if (width % 4 != 0 || height % 4 != 0) //Managed code doesn't currently handle partial block sizes well.
|
|
{
|
|
return NativeDecodeBC(inputData, textureFormat, width, height, outputData);
|
|
}
|
|
else
|
|
{
|
|
return ManagedDecodeBC(inputData, textureFormat, width, height, outputData);
|
|
}
|
|
}
|
|
|
|
private static bool NativeDecodeBC(ReadOnlySpan<byte> inputData, TextureFormat textureFormat, int width, int height, Span<byte> outputData)
|
|
{
|
|
Logger.Info(LogCategory.Export, $"Performing alternate decoding for {textureFormat}");
|
|
|
|
return textureFormat switch
|
|
{
|
|
TextureFormat.BC4 => Texture2DDecoder.TextureDecoder.DecodeBC4(inputData.ToArray(), width, height, outputData),
|
|
TextureFormat.BC5 => Texture2DDecoder.TextureDecoder.DecodeBC5(inputData.ToArray(), width, height, outputData),
|
|
TextureFormat.BC6H => Texture2DDecoder.TextureDecoder.DecodeBC6(inputData.ToArray(), width, height, outputData),
|
|
TextureFormat.BC7 => Texture2DDecoder.TextureDecoder.DecodeBC7(inputData.ToArray(), width, height, outputData),
|
|
_ => false,
|
|
};
|
|
}
|
|
|
|
private static bool ManagedDecodeBC(ReadOnlySpan<byte> inputData, TextureFormat textureFormat, int width, int height, Span<byte> outputData)
|
|
{
|
|
switch (textureFormat)
|
|
{
|
|
case TextureFormat.BC4:
|
|
Bc4.Decompress(inputData, width, height, outputData);
|
|
return true;
|
|
case TextureFormat.BC5:
|
|
Bc5.Decompress(inputData, width, height, outputData);
|
|
return true;
|
|
case TextureFormat.BC6H:
|
|
Bc6h.Decompress(inputData, width, height, false, outputData);
|
|
return true;
|
|
case TextureFormat.BC7:
|
|
Bc7.Decompress(inputData, width, height, outputData);
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static void UnpackNormal(Span<byte> data)
|
|
{
|
|
for (int i = 0; i < data.Length; i += 4)
|
|
{
|
|
Span<byte> pixelSpan = data.Slice(i, 4);
|
|
byte r = pixelSpan[3];
|
|
byte g = pixelSpan[1];
|
|
byte a = pixelSpan[2];
|
|
pixelSpan[2] = r;
|
|
pixelSpan[3] = a;
|
|
|
|
const double MagnitudeSqr = 255 * 255;
|
|
double vr = r * 2.0 - 255.0;
|
|
double vg = g * 2.0 - 255.0;
|
|
double hypotenuseSqr = Math.Min(vr * vr + vg * vg, MagnitudeSqr);
|
|
double b = (Math.Sqrt(MagnitudeSqr - hypotenuseSqr) + 255.0) / 2.0;
|
|
pixelSpan[0] = (byte)b;
|
|
}
|
|
}
|
|
}
|
|
}
|