Workaround for Texture2D.GetData method

倾然丶 夕夏残阳落幕 提交于 2019-12-04 05:41:43

Serializing the data is actually a pretty clever workaround but XML is far to heavy for the job. What I would do is use a custom made, but simple, binary format so that you can control the size of the output.

Here are a couple of methods I whipped up that should work, but please note that I made them specifically to answer this question and they have not been tested.

To save the data..

    private void SaveTextureData(Texture2D texture, string filename)
    {
        int width = texture.Width;
        int height = texture.Height;
        Color[] data = new Color[width * height];
        texture.GetData<Color>(data, 0, data.Length);

        using (BinaryWriter writer = new BinaryWriter(File.Open(filename, FileMode.Create)))
        {
            writer.Write(width);
            writer.Write(height);
            writer.Write(data.Length);

            for (int i = 0; i < data.Length; i++)
            {
                writer.Write(data[i].R);
                writer.Write(data[i].G);
                writer.Write(data[i].B);
                writer.Write(data[i].A);
            }
        }
    }

And to load the data..

    private Texture2D LoadTextureData(string filename)
    {
        using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open)))
        {                
            var width = reader.ReadInt32();
            var height = reader.ReadInt32();
            var length = reader.ReadInt32();
            var data = new Color[length];

            for (int i = 0; i < data.Length; i++)
            {
                var r = reader.ReadByte();
                var g = reader.ReadByte();
                var b = reader.ReadByte();
                var a = reader.ReadByte();
                data[i] = new Color(r, g, b, a);
            }

            var texture = new Texture2D(GraphicsDevice, width, height);
            texture.SetData<Color>(data, 0, data.Length);
            return texture;
        }
    }

Binary data is far smaller than XML data. It's about as small as you're going to get without compression. Just be aware that binary formats are pretty rigid when it comes to change. If you need to change the format it'll be easier in most cases to write out new files.

If you need to make changes to the methods, be careful to keep them in sync. Every Write should be matched with an identical Read in the exact same order and data type.

I'm interested to know how much smaller the files become. Let me know how it goes?

I answer my own question, if someone will have the same problem.

Following craftworkgame's suggestion, I saved my data using byte streams, but due to the absence of File class, I had to use WinRT functions:

private async void SaveColorArray(string filename, Color[] array)
{
    StorageFile sampleFile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(filename + ".dat", CreationCollisionOption.ReplaceExisting);
    IRandomAccessStream writeStream = await sampleFile.OpenAsync(FileAccessMode.ReadWrite);
    IOutputStream outputSteam = writeStream.GetOutputStreamAt(0);
    DataWriter dataWriter = new DataWriter(outputSteam);
    for (int i = 0; i < array.Length; i++)
    {
        dataWriter.WriteByte(array[i].R);
        dataWriter.WriteByte(array[i].G);
        dataWriter.WriteByte(array[i].B);
        dataWriter.WriteByte(array[i].A);
    }

    await dataWriter.StoreAsync();
    await outputSteam.FlushAsync();
}

protected async Task<Color[]> LoadColorArray(string filename)
{
    StorageFile sampleFile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(filename + ".dat", CreationCollisionOption.OpenIfExists);
    IRandomAccessStream readStream = await sampleFile.OpenAsync(FileAccessMode.Read);
    IInputStream inputSteam = readStream.GetInputStreamAt(0);
    DataReader dataReader = new DataReader(inputSteam);
    await dataReader.LoadAsync((uint)readStream.Size);

    Color[] levelArray = new Color[dataReader.UnconsumedBufferLength / 4];
    int i = 0;
    while (dataReader.UnconsumedBufferLength > 0)
    {
        byte[] colorArray = new byte[4];
        dataReader.ReadBytes(colorArray);
        levelArray[i++] = new Color(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
    }

    return levelArray;
}

You can get Texture2D.GetData working on iOS if you're willing to build your own version of MonoGame. See the fix here: https://github.com/scottlerch/MonoGame/commit/24c1c3d5398f4ed9dbd9b60c0bbdbc096311632c).

It's from discussions about this same issue here: https://monogame.codeplex.com/discussions/442667

The fix has been confirmed to work on iPad1 v5.1.1+ and an iPhone4 v6.1+.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!