我的游戏从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 解码。
/// 返回 null 当字节无法解码时,调用者可以避免缓存或显示一个破损的纹理。
///

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会将其作为默认选项。