Sunday, October 6, 2013

Задача 7: Получение размера PNG текстуры до ее полной загрузки


Практическое использование движка Unity3d.





Задача 7: Получение размера PNG текстуры до ее полной загрузки


  Задача следующая: необходимо определит размер текстуры до ее полной загрузки. Дело в том что при загрузке большой текстуры в память, Unity3d приложение может «упасть». Для того чтоб предотвратить такое поведение приложения необходимо узнать размер загружаемой текстуры до ее полной загрузки.
  Теперь я расскажу, как решить эту задачу. Каждая текстура имеет свой файловый формат. Зная формат, мы знаем где в текстуре располагаются ячейки данных отвечающие за размер текстуры. Таким образом, даже не создавая объект Texture2D мы можем узнать размер текстуры, только по бинарным данным. Более того, большинство форматов текстур располагают данные о размере в начале файла. Таким образом достаточно загрузить только начальную часть файла текстуры, и мы сразу сможем узнать размер текстуры.
  Мы будем рассматривать формат PNG. Подробнее про формат можно прочесть здесь http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html. Текстуру в программу мы будем загружать через класс WWW в Unity3d. Начнем анализировать текстуру загрузив первые 30 байт. А до определения размера текстуры определим действительно ли это PNG файл. Для этого нужно всего лишь проверить сигнатуру файла. После проверки сигнатуры файла, мы будем считывать размер файла.

Теперь посмотрим на исходный код получения размера текстуры:

private void GetPNGTextureSize(byte[] bytes, out float width, out float height)
{
    width = -1.0f;
    height = -1.0f;

    // check only png tex!!! // http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
    byte[] png_signature = { 137, 80, 78, 71, 13, 10, 26, 10 };
    
    const int cMinDownloadedBytes = 30;
    byte[] buf = bytes;
    if (buf.Length > cMinDownloadedBytes)
    {
        // now we can check png format
        for (int i = 0; i < png_signature.Length; i++)
        {
            if (buf[i] != png_signature[i])
            {
                Debug.LogWarning("Error! Texture os NOT png format!");
                return; // this is NOT png file!
            }
        }
        
        // now get width and height of texture
        width = buf[16] << 24 | buf[17] << 16 | buf[18] << 8 | buf[19];
        height = buf[20] << 24 | buf[21] << 16 | buf[22] << 8 | buf[23];
        
        Debug.Log("Loaded texture size: width = " + width + "; height = " + height);
        return;
    }
}

А также исходный код загрузки текстуры:

public Texture2D LoadSyncPNGTexture2D(string path)
{
    string resPath = cLocalPathPrefix + path;   
    Debug.Log("Try to load texture at: " + resPath);
    WWW www = new WWW(resPath);

    if (!string.IsNullOrEmpty(www.error))
    {
    Debug.LogWarning("Error! '" + www.error + "'");
    return null;
    }

    bool texChecked = false;
    while (!www.isDone)
    {
        if (!texChecked)
        {
            float texWidth = 0;
            float texHeight = 0;
            GetPNGTextureSize(www.bytes, out texWidth, out texHeight);

            if (texWidth < 0 || texHeight < 0)
            {
                continue;
            }

            texChecked = true;
        }
    }

    if (!string.IsNullOrEmpty(www.error))
    {
        Debug.LogWarning("Error! '" + www.error + "'");
        return null;
    }

    // get texture
    Texture2D tex = www.texture;
    return tex;

}

Если Вам нужно более детальное описание работы, пожалуйста, пишите в комментариях.

Демонстрацию работы можно скачать здесь:
https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson7/Build/Build.zip


Исходные коды работы, как всегда бесплатно, можно скачать здесь:
https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson7/Sources/Sources.zip
https://github.com/den-potapenko/Unity3dArticles/tree/master/Lesson7

2 comments:

  1. Здравствуйте. Подскажите, есть такой же метод для JPG?

    ReplyDelete
    Replies
    1. Здравствуйте!
      Я уверен что есть. Суть этого кода была в том, чтоб прочитать хедер до полной загрузки тела файла. Вы сможете найти хедер для JPG здесь: http://www.fastgraph.com/help/jpeg_header_format.html
      Используя байты, которые определяют формат файла, и его размер - Вы сможете точно сказать размер загружаемого файла.

      Delete