我的游戏从CDN获取封面艺术,缓存它,制作精灵。几个月来都正常工作。但是,突然某些封面开始出现空白。不是总是同样的封面,这让我很烦恼。最后,我将一个损坏的字节流写入文件并查看它。它从RIFF开始,然后是WEBP几个字节内。CDN有时会提供WebP而不是JPEG。Unity的图像加载(UnityWebRequestTexture和Texture2D.LoadImage)只读PNG和JPEG。将WebP传递给它,它不会抛出错误。它只是给你一个破损的小占位符纹理,并继续。没有任何错误。我的缓存在写文件到磁盘之前就检查了是否解码,所以一旦WebP落在那里,它就每次加载时都变成破损。所以一些封面是永久空白,而其他的封面是正常的。修复方法是使用管理的WebP解码器(ImageSharp,因为它是纯C#,我不想为三个构建目标使用原生库)。现在我检查了前几个字节,如果是WebP我就解码它,否则Unity就像正常一样处理它。也停止缓存那些无法解码的文件。
///
/// 解码下载的图像字节为
/// Unity 的内置加载器只理解 PNG 和 JPEG,所以 WebP 包(某些 CDN 通过内容协商提供)将使用 ImageSharp 解码。
/// 返回
///
public static class ImageDecoder
{
public static Texture2D DecodeToTexture(byte[] bytes)
{
if (bytes == null || bytes.Length < 12) return null;
return IsWebP(bytes) ? DecodeWebP(bytes) : DecodeNative(bytes);
}
// RIFF....WEBP 容器签名。
private static bool IsWebP(byte[] b) => b[0] == 'R' && b[1] == 'I' && b[2] == 'F' && b[3] == 'F' && b[8] == 'W' && b[9] == 'E' && b[10] == 'B' && b[11] == 'P';
private static Texture2D DecodeNative(byte[] bytes)
{
var texture = new Texture2D(2, 2);
if (texture.LoadImage(bytes)) return texture;
Object.Destroy(texture);
return null;
}
private static Texture2D DecodeWebP(byte[] bytes)
{
try
{
using var image = Image.Load<Rgba32>(bytes);
var width = image.Width;
var height = image.Height;
var pixels = new Color32[width * height];
// ImageSharp 行从上到下;Unity 纹理从下到上,所以翻转。
for (var y = 0; y < height; y++)
{
var row = image.DangerousGetPixelRowMemory(y).Span;
var destRow = (height - 1 - y) * width;
for (var x = 0; x < width; x++)
{
var p = row[x];
pixels[destRow + x] = new Color32(p.R, p.G, p.B, p.A);
}
}
var texture = new Texture2D(width, height, TextureFormat.Rgba32, false);
texture.SetPixels32(pixels);
texture.Apply();
return texture;
}
catch (System.Exception exception)
{
Debug.LogError($"Failed to decode WebP image: {exception.Message}");
return null;
}
}
}
另外,LoadImage 返回一个布尔值告诉你它是否工作了,我之前一直忽略这个问题。 🤦
是否有其他人遇到WebP问题?感觉它很可能会再次攻击更多的人,因为CDN会将其作为默认选项。
评论 (0)