Sunday, October 6, 2013

Task #7: Getting the size of PNG textures before its full loading

Practical use of Unity3d engine.



Task #7: Getting the size of PNG textures before its full loading

  The next task we will solve today is get Texture2D size before its full loading.
  Unity3d is able to load PNG textures as Texture2D, but when size of the texture is too large, Unity3d application may crash. Therefore, the solution for this issue is to get texture size before its full loading.
  Now, let us solve this task. Each texture has its own file format. When we know the file format of the texture, we able to know the bytes that are responsible for the texture size. In such a way, we can determine size of texture, loading only the first bytes of the texture and even do not create Texture2d object.
  In this article, we will load PNG texture. See PNG format description here: http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html.
  Texture will be loaded via WWW class in Unity3d. We will analyze size of the texture after loading first 30 bytes. Before determining texture size application will check PNG texture file signature. It’s needed because we should be sure that file is PNG file.
  Let’s take a look to source code of retrieving texture size from bytes:


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;
    }
}

And source code of texture loading:

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;

}

  If you need more detailed article description, please write it in comments.

  Demo of the application you can download here:
https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson7/Build/Build.zip
  Free source codes you can download here:
https://dl.dropboxusercontent.com/u/20023505/Articles/Unity3d/Lesson7/Sources/Sources.zip
https://github.com/den-potapenko/Unity3dArticles/tree/master/Lesson7



2 comments:

  1. Hi Denis!

    Love these tutorials. I posted a question on #6, and I'm worried you won't see it because it's old. Sorry if this is spamming you, I just really could use some help, if you have time to check it out.

    Thanks,
    Jonathan

    ReplyDelete
    Replies
    1. Hi Jonathan,

      It's ok :) Really i can see all the posted comments, so i will answer to you in post #6.

      Denis

      Delete