Unity3D中使用SkiaSharp处理Texture2D (中篇)
大约 2 分钟
这一篇主要讲一种 Texture2D 和 SKBitmap 之间更快捷的转换方式。
看本片文章之前,请先阅读我的上一篇文章。
直接填充数据
我们再思考一个问题,能否不去填充像素点的颜色,而是直接填充数据,这样转换效率会更高。
当 SKBitmap 的 ColorType 与 Texture2D 的 TextureFormat,对应时,内存数据一致,调整数据后直接填充进去就可以了。
我给出下面的示例代码,可以看一下。
using System;
using System.Buffers;
using SkiaSharp;
using UnityEngine;
using UnityEngine.UI;
public class Example2 : MonoBehaviour
{
[SerializeField] private Texture2D tex;
private RawImage _rawImage;
private void Start()
{
_rawImage = GetComponent<RawImage>();
// 以下代码都是 已知 tex 的 TextureFormat 为 RGBA32 的情况下
var bitmap = new SKBitmap(tex.width, tex.height, SKColorType.Rgba8888, SKAlphaType.Opaque);
var data = tex.GetPixelData<byte>(0).AsReadOnlySpan();
var writer = new ArrayBufferWriter<byte>(tex.width * tex.height * 4);
for (var i = tex.height - 1; i >= 0; i--) writer.Write(data.Slice(i * tex.width * 4, tex.width * 4));
var span = writer.WrittenSpan;
unsafe
{
fixed (byte* ptr = span)
{
bitmap.SetPixels((IntPtr)ptr);
}
}
var tex0 = new Texture2D(bitmap.Width, bitmap.Height, TextureFormat.RGBA32, false);
var data0 = bitmap.GetPixelSpan();
var writer0 = new ArrayBufferWriter<byte>(bitmap.Width * bitmap.Height * 4);
for (var i = bitmap.Height - 1; i >= 0; i--) writer0.Write(data0.Slice(i * bitmap.Width * 4, bitmap.Width * 4));
tex0.SetPixelData(writer0.WrittenSpan.ToArray(), 0);
tex0.Apply();
_rawImage.texture = tex0;
_rawImage.GetComponent<RectTransform>().sizeDelta = new Vector2(tex0.width, tex0.height);
}
}

从 GPU 读取数据
前面的所有代码,都是基于 Texture 数据在 CPU 内存中编写的,即 texture2d.isReadable = true
,如果数据不在 CPU 内存中,则需要在显卡中读取数据。
/// <summary>
/// 从GUP读取贴图
/// </summary>
/// <param name="texture"></param>
/// <returns></returns>
public static Texture2D GetTextureFromGpu(this Texture texture)
{
var data = GetTextureDataFromGpu(texture);
var tex2 = new Texture2D(texture.width, texture.height, texture.graphicsFormat, TextureCreationFlags.None);
tex2.SetPixelData(data, 0);
return tex2;
}
/// <summary>
/// 从GPU读取数据
/// </summary>
/// <param name="texture"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static NativeArray<byte> GetTextureDataFromGpu(this Texture texture)
{
var request = AsyncGPUReadback.Request(texture, 0, texture.graphicsFormat);
request.WaitForCompletion();
if (request.hasError) throw new Exception("");
return request.GetData<byte>();
}
/// <summary>
/// 从 GPU 获取数据
/// </summary>
/// <param name="texture2D"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private static async ValueTask<NativeArray<byte>> GetTextureDataFromGpuAsync(this Texture2D texture2D)
{
var request = await AsyncGPUReadback.RequestAsync(texture2D, 0, texture2D.graphicsFormat);
if (request.hasError) throw new Exception("");
return request.GetData<byte>();
}
然后修改前面所写的代码,判断是否可读,如果可读就按前面所写的做,如果不可读,就改为从 GPU 获取数据。