Fix texture decoding for Texture2DArray and CubemapArray

* Resolves #1501
This commit is contained in:
ds5678 2025-01-05 17:54:19 -08:00
parent 5bba24ccf0
commit 3c26685bf7
6 changed files with 227 additions and 12 deletions

View File

@ -11,6 +11,7 @@ public abstract class DirectBitmap
public Span<byte> Bits => new Span<byte>(Data, 0, ByteSize);
public abstract int PixelSize { get; }
protected byte[] Data { get; }
public bool IsEmpty => Width == 0 || Height == 0 || Depth == 0;
protected DirectBitmap(int width, int height, int depth)
{

View File

@ -82,12 +82,18 @@ namespace AssetRipper.Export.Modules.Textures
return false;
}
if (!TryGetTextureFormat(texture, out TextureFormat format))
{
bitmap = DirectBitmap.Empty;
return false;
}
if (!TryConvertToBitmap(
texture.FormatE,
format,
texture.Width,
texture.Height,
texture.Depth,
(int)texture.DataSize,
texture.GetCompleteImageSize(),
texture.Collection.Version,
buffer,
out bitmap))
@ -98,6 +104,20 @@ namespace AssetRipper.Export.Modules.Textures
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)
@ -112,9 +132,9 @@ namespace AssetRipper.Export.Modules.Textures
if (!TryConvertToBitmap(
texture.FormatE,
texture.Width,
texture.Width,//Not sure if this is correct
texture.CubemapCount * 6,//Not sure if this is correct
(int)texture.DataSize,
texture.GetHeight(),
texture.GetDepth(),
texture.GetCompleteImageSize(),
texture.Collection.Version,
buffer,
out bitmap))
@ -122,7 +142,7 @@ namespace AssetRipper.Export.Modules.Textures
return false;
}
bitmap.FlipY();
bitmap.FlipY();// Maybe not needed?
return true;
}

View File

@ -1,5 +1,4 @@
using AsmResolver.DotNet;
using AssetRipper.Assets;
using AssetRipper.Assets;
using AssetRipper.Export.Modules.Audio;
using AssetRipper.Export.Modules.Models;
using AssetRipper.Export.Modules.Shaders.IO;
@ -18,6 +17,7 @@ using AssetRipper.Processing.Textures;
using AssetRipper.SourceGenerated.Classes.ClassID_115;
using AssetRipper.SourceGenerated.Classes.ClassID_128;
using AssetRipper.SourceGenerated.Classes.ClassID_156;
using AssetRipper.SourceGenerated.Classes.ClassID_189;
using AssetRipper.SourceGenerated.Classes.ClassID_213;
using AssetRipper.SourceGenerated.Classes.ClassID_28;
using AssetRipper.SourceGenerated.Classes.ClassID_329;
@ -88,8 +88,14 @@ internal static class AssetAPI
if (TryGetImageExtensionFromQuery(context, out string? extension, out ImageExportFormat format))
{
DirectBitmap bitmap = GetImageBitmap(asset);
if (bitmap.IsEmpty)
{
return context.Response.NotFound("Image data could not be decoded.");
}
MemoryStream stream = new();
GetImageBitmap(asset).Save(stream, format);
bitmap.Save(stream, format);
return Results.Bytes(stream.ToArray(), $"image/{extension}").ExecuteAsync(context);
}
else
@ -100,7 +106,7 @@ internal static class AssetAPI
public static bool HasImageData(IUnityObjectBase asset) => asset switch
{
ITexture2D texture => texture.CheckAssetIntegrity(),
IImageTexture texture => texture.CheckAssetIntegrity(),
SpriteInformationObject spriteInformationObject => spriteInformationObject.Texture.CheckAssetIntegrity(),
ISprite sprite => sprite.TryGetTexture()?.CheckAssetIntegrity() ?? false,
ITerrainData terrainData => terrainData.Heightmap.Heights.Count > 0,
@ -111,14 +117,14 @@ internal static class AssetAPI
{
return asset switch
{
ITexture2D texture => TextureToBitmap(texture),
IImageTexture texture => TextureToBitmap(texture),
SpriteInformationObject spriteInformationObject => TextureToBitmap(spriteInformationObject.Texture),
ISprite sprite => SpriteToBitmap(sprite),
ITerrainData terrainData => TerrainHeatmap.GetBitmap(terrainData),
_ => DirectBitmap.Empty,
};
static DirectBitmap TextureToBitmap(ITexture2D texture)
static DirectBitmap TextureToBitmap(IImageTexture texture)
{
return TextureConverter.TryConvertToBitmap(texture, out DirectBitmap bitmap) ? bitmap : DirectBitmap.Empty;
}

View File

@ -4,6 +4,16 @@ namespace AssetRipper.SourceGenerated.Extensions;
public static class CubemapArrayExtensions
{
public static int GetHeight(this ICubemapArray texture)
{
return texture.Width;
}
public static int GetDepth(this ICubemapArray texture)
{
return texture.CubemapCount * 6;// Not sure about this
}
public static byte[] GetImageData(this ICubemapArray texture)
{
if (texture.ImageData.Length > 0)
@ -35,4 +45,10 @@ public static class CubemapArrayExtensions
return false;
}
}
public static int GetCompleteImageSize(this ICubemapArray texture)
{
int depth = texture.GetDepth();
return depth > 0 ? (int)texture.DataSize / depth : 0;
}
}

View File

@ -0,0 +1,167 @@
using AssetRipper.SourceGenerated.Enums;
namespace AssetRipper.SourceGenerated.Extensions;
public static class GraphicsFormatExtensions
{
/// <summary>
/// Converts <see cref="GraphicsFormat"/> to <see cref="TextureFormat"/>.
/// </summary>
/// <remarks>
/// Note: Some conversions are not entirely accurate. For example, SRGB and UNorm are treated as the same.
/// </remarks>
/// <param name="format">The <see cref="GraphicsFormat"/> to convert.</param>
/// <returns>The <see cref="TextureFormat"/> equivalent to the <paramref name="format"/> parameter.</returns>
/// <exception cref="NotSupportedException">There is no direct conversion for the format.</exception>
/// <exception cref="ArgumentOutOfRangeException">The format was an invalid value.</exception>
public static TextureFormat ToTextureFormat(this GraphicsFormat format) => format switch
{
GraphicsFormat.None => throw new NotSupportedException(),
GraphicsFormat.R8_SRGB => TextureFormat.R8,
GraphicsFormat.R8G8_SRGB => TextureFormat.RG16,
GraphicsFormat.R8G8B8_SRGB => TextureFormat.RGB24,
GraphicsFormat.R8G8B8A8_SRGB => TextureFormat.RGBA32,
GraphicsFormat.R8_UNorm => TextureFormat.R8,
GraphicsFormat.R8G8_UNorm => TextureFormat.RG16,
GraphicsFormat.R8G8B8_UNorm => TextureFormat.RGB24,
GraphicsFormat.R8G8B8A8_UNorm => TextureFormat.RGBA32,
GraphicsFormat.R8_SNorm => TextureFormat.R8_SIGNED,
GraphicsFormat.R8G8_SNorm => TextureFormat.RG16_SIGNED,
GraphicsFormat.R8G8B8_SNorm => TextureFormat.RGB24_SIGNED,
GraphicsFormat.R8G8B8A8_SNorm => TextureFormat.RGBA32_SIGNED,
GraphicsFormat.R8_UInt => TextureFormat.R8,
GraphicsFormat.R8G8_UInt => TextureFormat.RG16,
GraphicsFormat.R8G8B8_UInt => TextureFormat.RGB24,
GraphicsFormat.R8G8B8A8_UInt => TextureFormat.RGBA32,
GraphicsFormat.R8_SInt => TextureFormat.R8_SIGNED,
GraphicsFormat.R8G8_SInt => TextureFormat.RG16_SIGNED,
GraphicsFormat.R8G8B8_SInt => TextureFormat.RGB24_SIGNED,
GraphicsFormat.R8G8B8A8_SInt => TextureFormat.RGBA32_SIGNED,
GraphicsFormat.R16_UNorm => TextureFormat.R16,
GraphicsFormat.R16G16_UNorm => TextureFormat.RG32,
GraphicsFormat.R16G16B16_UNorm => TextureFormat.RGB48,
GraphicsFormat.R16G16B16A16_UNorm => TextureFormat.RGBA64,
GraphicsFormat.R16_SNorm => TextureFormat.R16_SIGNED,
GraphicsFormat.R16G16_SNorm => TextureFormat.RG32_SIGNED,
GraphicsFormat.R16G16B16_SNorm => TextureFormat.RGB48_SIGNED,
GraphicsFormat.R16G16B16A16_SNorm => TextureFormat.RGBA64_SIGNED,
GraphicsFormat.R16_UInt => TextureFormat.R16,
GraphicsFormat.R16G16_UInt => TextureFormat.RG32,
GraphicsFormat.R16G16B16_UInt => TextureFormat.RGB48,
GraphicsFormat.R16G16B16A16_UInt => TextureFormat.RGBA64,
GraphicsFormat.R16_SInt => TextureFormat.R16_SIGNED,
GraphicsFormat.R16G16_SInt => TextureFormat.RG32_SIGNED,
GraphicsFormat.R16G16B16_SInt => TextureFormat.RGB48_SIGNED,
GraphicsFormat.R16G16B16A16_SInt => TextureFormat.RGBA64_SIGNED,
GraphicsFormat.R32_UInt => throw new NotSupportedException(),
GraphicsFormat.R32G32_UInt => throw new NotSupportedException(),
GraphicsFormat.R32G32B32_UInt => throw new NotSupportedException(),
GraphicsFormat.R32G32B32A32_UInt => throw new NotSupportedException(),
GraphicsFormat.R32_SInt => throw new NotSupportedException(),
GraphicsFormat.R32G32_SInt => throw new NotSupportedException(),
GraphicsFormat.R32G32B32_SInt => throw new NotSupportedException(),
GraphicsFormat.R32G32B32A32_SInt => throw new NotSupportedException(),
GraphicsFormat.R16_SFloat => throw new NotSupportedException(),
GraphicsFormat.R16G16_SFloat => throw new NotSupportedException(),
GraphicsFormat.R16G16B16_SFloat => throw new NotSupportedException(),
GraphicsFormat.R16G16B16A16_SFloat => throw new NotSupportedException(),
GraphicsFormat.R32_SFloat => throw new NotSupportedException(),
GraphicsFormat.R32G32_SFloat => throw new NotSupportedException(),
GraphicsFormat.R32G32B32_SFloat => throw new NotSupportedException(),
GraphicsFormat.R32G32B32A32_SFloat => throw new NotSupportedException(),
GraphicsFormat.B8G8R8_SRGB => throw new NotSupportedException(),
GraphicsFormat.B8G8R8A8_SRGB => TextureFormat.BGRA32_14,
GraphicsFormat.B8G8R8_UNorm => throw new NotSupportedException(),
GraphicsFormat.B8G8R8A8_UNorm => TextureFormat.BGRA32_14,
GraphicsFormat.B8G8R8_SNorm => throw new NotSupportedException(),
GraphicsFormat.B8G8R8A8_SNorm => TextureFormat.BGRA32_14,
GraphicsFormat.B8G8R8_UInt => throw new NotSupportedException(),
GraphicsFormat.B8G8R8A8_UInt => TextureFormat.BGRA32_14,
GraphicsFormat.B8G8R8_SInt => throw new NotSupportedException(),
GraphicsFormat.B8G8R8A8_SInt => TextureFormat.BGRA32_14,
GraphicsFormat.R4G4B4A4_UNormPack16 => throw new NotSupportedException(),
GraphicsFormat.B4G4R4A4_UNormPack16 => throw new NotSupportedException(),
GraphicsFormat.R5G6B5_UNormPack16 => TextureFormat.RGB565,
GraphicsFormat.B5G6R5_UNormPack16 => throw new NotSupportedException(),
GraphicsFormat.R5G5B5A1_UNormPack16 => throw new NotSupportedException(),
GraphicsFormat.B5G5R5A1_UNormPack16 => throw new NotSupportedException(),
GraphicsFormat.A1R5G5B5_UNormPack16 => throw new NotSupportedException(),
GraphicsFormat.E5B9G9R9_UFloatPack32 => throw new NotSupportedException(),
GraphicsFormat.B10G11R11_UFloatPack32 => throw new NotSupportedException(),
GraphicsFormat.A2B10G10R10_UNormPack32 => throw new NotSupportedException(),
GraphicsFormat.A2B10G10R10_UIntPack32 => throw new NotSupportedException(),
GraphicsFormat.A2B10G10R10_SIntPack32 => throw new NotSupportedException(),
GraphicsFormat.A2R10G10B10_UNormPack32 => throw new NotSupportedException(),
GraphicsFormat.A2R10G10B10_UIntPack32 => throw new NotSupportedException(),
GraphicsFormat.A2R10G10B10_SIntPack32 => throw new NotSupportedException(),
GraphicsFormat.A2R10G10B10_XRSRGBPack32 => throw new NotSupportedException(),
GraphicsFormat.A2R10G10B10_XRUNormPack32 => throw new NotSupportedException(),
GraphicsFormat.R10G10B10_XRSRGBPack32 => throw new NotSupportedException(),
GraphicsFormat.R10G10B10_XRUNormPack32 => throw new NotSupportedException(),
GraphicsFormat.A10R10G10B10_XRSRGBPack32 => throw new NotSupportedException(),
GraphicsFormat.A10R10G10B10_XRUNormPack32 => throw new NotSupportedException(),
GraphicsFormat.D16_UNorm => throw new NotSupportedException(),
GraphicsFormat.D24_UNorm => throw new NotSupportedException(),
GraphicsFormat.D24_UNorm_S8_UInt => throw new NotSupportedException(),
GraphicsFormat.D32_SFloat => throw new NotSupportedException(),
GraphicsFormat.D32_SFloat_S8_Uint => throw new NotSupportedException(),
GraphicsFormat.S8_Uint => TextureFormat.Alpha8,
GraphicsFormat.RGB_DXT1_SRGB => TextureFormat.DXT1,
GraphicsFormat.RGB_DXT1_UNorm => TextureFormat.DXT1,
GraphicsFormat.RGBA_DXT3_SRGB => TextureFormat.DXT3,
GraphicsFormat.RGBA_DXT3_UNorm => TextureFormat.DXT3,
GraphicsFormat.RGBA_DXT5_SRGB => TextureFormat.DXT5,
GraphicsFormat.RGBA_DXT5_UNorm => TextureFormat.DXT5,
GraphicsFormat.R_BC4_UNorm => TextureFormat.BC4,
GraphicsFormat.R_BC4_SNorm => TextureFormat.BC4,
GraphicsFormat.RG_BC5_UNorm => TextureFormat.BC5,
GraphicsFormat.RG_BC5_SNorm => TextureFormat.BC5,
GraphicsFormat.RGB_BC6H_UFloat => TextureFormat.BC6H,
GraphicsFormat.RGB_BC6H_SFloat => TextureFormat.BC6H,
GraphicsFormat.RGBA_BC7_SRGB => TextureFormat.BC7,
GraphicsFormat.RGBA_BC7_UNorm => TextureFormat.BC7,
GraphicsFormat.RGB_PVRTC_2Bpp_SRGB => TextureFormat.PVRTC_RGB2,
GraphicsFormat.RGB_PVRTC_2Bpp_UNorm => TextureFormat.PVRTC_RGB2,
GraphicsFormat.RGB_PVRTC_4Bpp_SRGB => TextureFormat.PVRTC_RGB4,
GraphicsFormat.RGB_PVRTC_4Bpp_UNorm => TextureFormat.PVRTC_RGB4,
GraphicsFormat.RGBA_PVRTC_2Bpp_SRGB => TextureFormat.PVRTC_RGBA2,
GraphicsFormat.RGBA_PVRTC_2Bpp_UNorm => TextureFormat.PVRTC_RGBA2,
GraphicsFormat.RGBA_PVRTC_4Bpp_SRGB => TextureFormat.PVRTC_RGBA4,
GraphicsFormat.RGBA_PVRTC_4Bpp_UNorm => TextureFormat.PVRTC_RGBA4,
GraphicsFormat.RGB_ETC_UNorm => TextureFormat.ETC_RGB4,
GraphicsFormat.RGB_ETC2_SRGB => TextureFormat.ETC2_RGB,
GraphicsFormat.RGB_ETC2_UNorm => TextureFormat.ETC2_RGB,
GraphicsFormat.RGB_A1_ETC2_SRGB => TextureFormat.ETC2_RGBA1,
GraphicsFormat.RGB_A1_ETC2_UNorm => TextureFormat.ETC2_RGBA1,
GraphicsFormat.RGBA_ETC2_SRGB => TextureFormat.ETC2_RGBA8,
GraphicsFormat.RGBA_ETC2_UNorm => TextureFormat.ETC2_RGBA8,
GraphicsFormat.R_EAC_UNorm => TextureFormat.EAC_R,
GraphicsFormat.R_EAC_SNorm => TextureFormat.EAC_R,
GraphicsFormat.RG_EAC_UNorm => TextureFormat.EAC_RG,
GraphicsFormat.RG_EAC_SNorm => TextureFormat.EAC_RG,
GraphicsFormat.RGBA_ASTC4X4_SRGB => TextureFormat.ASTC_4x4,
GraphicsFormat.RGBA_ASTC4X4_UNorm => TextureFormat.ASTC_4x4,
GraphicsFormat.RGBA_ASTC5X5_SRGB => TextureFormat.ASTC_5x5,
GraphicsFormat.RGBA_ASTC5X5_UNorm => TextureFormat.ASTC_5x5,
GraphicsFormat.RGBA_ASTC6X6_SRGB => TextureFormat.ASTC_6x6,
GraphicsFormat.RGBA_ASTC6X6_UNorm => TextureFormat.ASTC_6x6,
GraphicsFormat.RGBA_ASTC8X8_SRGB => TextureFormat.ASTC_8x8,
GraphicsFormat.RGBA_ASTC8X8_UNorm => TextureFormat.ASTC_8x8,
GraphicsFormat.RGBA_ASTC10X10_SRGB => TextureFormat.ASTC_10x10,
GraphicsFormat.RGBA_ASTC10X10_UNorm => TextureFormat.ASTC_10x10,
GraphicsFormat.RGBA_ASTC12X12_SRGB => TextureFormat.ASTC_12x12,
GraphicsFormat.RGBA_ASTC12X12_UNorm => TextureFormat.ASTC_12x12,
GraphicsFormat.YUV2 => throw new NotSupportedException(),
GraphicsFormat.DepthAuto => throw new NotSupportedException(),
GraphicsFormat.ShadowAuto => throw new NotSupportedException(),
GraphicsFormat.VideoAuto => throw new NotSupportedException(),
GraphicsFormat.RGBA_ASTC4X4_UFloat => TextureFormat.ASTC_4x4,
GraphicsFormat.RGBA_ASTC5X5_UFloat => TextureFormat.ASTC_5x5,
GraphicsFormat.RGBA_ASTC6X6_UFloat => TextureFormat.ASTC_6x6,
GraphicsFormat.RGBA_ASTC8X8_UFloat => TextureFormat.ASTC_8x8,
GraphicsFormat.RGBA_ASTC10X10_UFloat => TextureFormat.ASTC_10x10,
GraphicsFormat.RGBA_ASTC12X12_UFloat => TextureFormat.ASTC_12x12,
GraphicsFormat.D16_UNorm_S8_UInt => throw new NotSupportedException(),
_ => throw new ArgumentOutOfRangeException(nameof(format), format, null),
};
}

View File

@ -35,5 +35,10 @@ namespace AssetRipper.SourceGenerated.Extensions
return false;
}
}
public static int GetCompleteImageSize(this ITexture2DArray texture)
{
return (int)texture.DataSize / (texture.Depth > 1 ? texture.Depth : 1);
}
}
}