Практическое использование движка 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/Build/Build.zip
Исходные коды работы, как всегда бесплатно, можно скачать здесь:
https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson7/Sources/Sources.zip
https://github.com/den-potapenko/Unity3dArticles/tree/master/Lesson7
https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson7/Sources/Sources.zip
https://github.com/den-potapenko/Unity3dArticles/tree/master/Lesson7
Здравствуйте. Подскажите, есть такой же метод для JPG?
ReplyDeleteЗдравствуйте!
DeleteЯ уверен что есть. Суть этого кода была в том, чтоб прочитать хедер до полной загрузки тела файла. Вы сможете найти хедер для JPG здесь: http://www.fastgraph.com/help/jpeg_header_format.html
Используя байты, которые определяют формат файла, и его размер - Вы сможете точно сказать размер загружаемого файла.