Search Unity

  1. Looking for a job or to hire someone for a project? Check out the re-opened job forums.
    Dismiss Notice
  2. The Burst compiler has its own forum section now.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Generating sprites dynamically from PNG or JPEG files in C#

Discussion in 'Scripting' started by c68, Jul 26, 2015.

  1. c68

    c68

    Joined:
    Jul 10, 2013
    Posts:
    4
    Hey everyone!

    I was searching the forums here (and some external sources) to find a way to load an external PNG file into a Unity-Sprite and display it on a UI.image element. It took me about a day to figure out how to do this! :-o

    But i finally managed to do it and thought its about time to give back something to the forum and share this little class with you.

    Hope this helps in any way in case you are looking for the same info as I did! :)

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.IO;
    4.  
    5. public class IMG2Sprite : MonoBehaviour {
    6.  
    7.    // This script loads a PNG or JPEG image from disk and returns it as a Sprite
    8.    // Drop it on any GameObject/Camera in your scene (singleton implementation)
    9.    //
    10.    // Usage from any other script:
    11.    // MySprite = IMG2Sprite.instance.LoadNewSprite(FilePath, [PixelsPerUnit (optional)])
    12.  
    13.    private static IMG2Sprite _instance;
    14.  
    15.    public static IMG2Sprite instance
    16.    {
    17.      get    
    18.      {
    19.        //If _instance hasn't been set yet, we grab it from the scene!
    20.        //This will only happen the first time this reference is used.
    21.  
    22.        if(_instance == null)
    23.          _instance = GameObject.FindObjectOfType<IMG2Sprite>();
    24.        return _instance;
    25.      }
    26.    }
    27.  
    28.    public Sprite LoadNewSprite(string FilePath, float PixelsPerUnit = 100.0f) {
    29.    
    30.      // Load a PNG or JPG image from disk to a Texture2D, assign this texture to a new sprite and return its reference
    31.      
    32.      Sprite NewSprite = new Sprite();
    33.      Texture2D SpriteTexture = LoadTexture(FilePath);
    34.      NewSprite = Sprite.Create(SpriteTexture, new Rect(0, 0, SpriteTexture.width, SpriteTexture.height),new Vector2(0,0), PixelsPerUnit);
    35.  
    36.      return NewSprite;
    37.    }
    38.  
    39.    public Texture2D LoadTexture(string FilePath) {
    40.  
    41.      // Load a PNG or JPG file from disk to a Texture2D
    42.      // Returns null if load fails
    43.  
    44.      Texture2D Tex2D;
    45.      byte[] FileData;
    46.  
    47.      if (File.Exists(FilePath)){
    48.        FileData = File.ReadAllBytes(FilePath);
    49.        Tex2D = new Texture2D(2, 2);           // Create new "empty" texture
    50.        if (Tex2D.LoadImage(FileData))           // Load the imagedata into the texture (size is set automatically)
    51.          return Tex2D;                 // If data = readable -> return texture
    52.      }  
    53.      return null;                     // Return null if load failed
    54.    }
    55. }
    Happy Coding!
     
  2. IKStreamIvo

    IKStreamIvo

    Joined:
    Nov 16, 2015
    Posts:
    3
    Many, many, many thanks!
     
    mutluadam likes this.
  3. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,108
    just fyi, LoadImage has a big "thunk" to it to decode either PNG or JPG for textures... you should really look into using native formats for the images like DDS or PVR and use LoadRawTextureData (with their respective headers removed from the bits).
     
  4. TutiBueno2

    TutiBueno2

    Joined:
    Oct 16, 2012
    Posts:
    3
    Thanks it helped a lot!
     
  5. Freznosis

    Freznosis

    Joined:
    Jul 16, 2014
    Posts:
    292
    I found it much more efficient (for my use at least) to drop it into a static tools class and be able to reference it anywhere without having to drop it in the scene constantly. I also added SpriteMeshType so that you can choose (optional) whether to use Tight or FullRect. Unity likes to load FullRect faster than Tight so it might be use to some people for testing or real world use purposes. Just drop this into any script file and it will work outright :)

    Edit 2/9/2018: I added a simple conversion method to the IMG2Sprite class since I needed to convert Texture2D's that are already loaded.

    Edit 4/11/2019: Updated the sprite creation method to avoid the new Sprite() error.


    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.IO;
    5.  
    6. public static class IMG2Sprite
    7. {
    8.  
    9.     //Static class instead of _instance
    10.     // Usage from any other script:
    11.     // MySprite = IMG2Sprite.LoadNewSprite(FilePath, [PixelsPerUnit (optional)], [spriteType(optional)])
    12.  
    13.     public static Sprite LoadNewSprite(string FilePath, float PixelsPerUnit = 100.0f, SpriteMeshType spriteType = SpriteMeshType.Tight)
    14.     {
    15.  
    16.         // Load a PNG or JPG image from disk to a Texture2D, assign this texture to a new sprite and return its reference
    17.  
    18.         Texture2D SpriteTexture = LoadTexture(FilePath);
    19.         Sprite  NewSprite = Sprite.Create(SpriteTexture, new Rect(0, 0, SpriteTexture.width, SpriteTexture.height), new Vector2(0, 0), PixelsPerUnit, 0 , spriteType);
    20.  
    21.         return NewSprite;
    22.     }
    23.  
    24.     public static Sprite ConvertTextureToSprite(Texture2D texture, float PixelsPerUnit = 100.0f, SpriteMeshType spriteType = SpriteMeshType.Tight)
    25.     {
    26.         // Converts a Texture2D to a sprite, assign this texture to a new sprite and return its reference
    27.  
    28.         Sprite NewSprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0, 0), PixelsPerUnit, 0, spriteType);
    29.  
    30.         return NewSprite;
    31.     }
    32.  
    33.     public static Texture2D LoadTexture(string FilePath)
    34.     {
    35.  
    36.         // Load a PNG or JPG file from disk to a Texture2D
    37.         // Returns null if load fails
    38.  
    39.         Texture2D Tex2D;
    40.         byte[] FileData;
    41.  
    42.         if (File.Exists(FilePath))
    43.         {
    44.             FileData = File.ReadAllBytes(FilePath);
    45.             Tex2D = new Texture2D(2, 2);           // Create new "empty" texture
    46.             if (Tex2D.LoadImage(FileData))           // Load the imagedata into the texture (size is set automatically)
    47.                 return Tex2D;                 // If data = readable -> return texture
    48.         }
    49.         return null;                     // Return null if load failed
    50.     }
    51. }
    52.  
     
    Last edited: Nov 5, 2019
  6. mischinab

    mischinab

    Joined:
    Nov 30, 2016
    Posts:
    1
    You can also use Resources.Load to create a sprite, if you have the image file in your Resources folder:

    string path = "filename"; // filename.png should be stored in your Assets/Resources folder
    Sprite sprite = Resources.Load<Sprite>(path);


    This is handy if you have a collection of images, and want to be able to choose one dynamically by filename at runtime.
     
    mbrostami likes this.
  7. Hamid-Gh

    Hamid-Gh

    Joined:
    Feb 16, 2018
    Posts:
    4
    can you show me how to use the script ?
     
  8. Ctnightmare

    Ctnightmare

    Joined:
    Jun 3, 2017
    Posts:
    1
    Hey, Great script. Only problem I'm having is that my image is off centered (up and to the right) and I cannot get it so the center of image is in the middle of the sprite render. Any thought


    ---Solved---
    Added a .5f, .5f pivot
     
    Last edited: Jul 25, 2018
  9. elilurie

    elilurie

    Joined:
    Aug 16, 2018
    Posts:
    1
    There was an error message in VS2017 editor of the script
    Sprite NewSprite = new Sprite();
    Sprite doesnt contain a constructor that takes 0 arguments
     
  10. daRedLoCo

    daRedLoCo

    Joined:
    Jan 20, 2013
    Posts:
    1
    Change:
    Code (CSharp):
    1.  
    2. public static Sprite LoadNewSprite(string FilePath, float PixelsPerUnit = 100.0f, SpriteMeshType spriteType = SpriteMeshType.Tight)
    3.     {
    4.         // Load a PNG or JPG image from disk to a Texture2D, assign this texture to a new sprite and return its reference
    5.         Sprite NewSprite = new Sprite();
    6.         Texture2D SpriteTexture = LoadTexture(FilePath);
    7.         NewSprite = Sprite.Create(SpriteTexture, new Rect(0, 0, SpriteTexture.width, SpriteTexture.height), new Vector2(0, 0), PixelsPerUnit, 0 , spriteType);
    8.         return NewSprite;
    9.     }
    to

    Code (CSharp):
    1.  
    2. public static Sprite LoadNewSprite(string FilePath, float PixelsPerUnit = 100.0f, SpriteMeshType spriteType = SpriteMeshType.Tight)
    3.     {
    4.         // Load a PNG or JPG image from disk to a Texture2D, assign this texture to a new sprite and return its reference
    5.         Texture2D SpriteTexture = LoadTexture(FilePath);
    6.         Sprite NewSprite = Sprite.Create(SpriteTexture, new Rect(0, 0, SpriteTexture.width, SpriteTexture.height), new Vector2(0, 0), PixelsPerUnit, 0 , spriteType);
    7.  
    8.         return NewSprite;
    9.     }
    10.  
     
  11. seyit25

    seyit25

    Joined:
    Sep 28, 2018
    Posts:
    4
    it does not work...
     
  12. dali_est

    dali_est

    Joined:
    Nov 14, 2018
    Posts:
    1
     
  13. tik_tikkers

    tik_tikkers

    Joined:
    Jan 5, 2018
    Posts:
    30
    After upgrading Unity to 2018 I got the error:

    Sprite does not contain a constructor

    This is the updated code that works:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.IO;
    4.  
    5.  
    6. public class IMG2Sprite : MonoBehaviour
    7. {
    8.  
    9.  
    10.     // This script loads a PNG or JPEG image from disk and returns it as a Sprite
    11.     // Drop it on any GameObject/Camera in your scene (singleton implementation)
    12.     //
    13.     // Usage from any other script:
    14.     // MySprite = IMG2Sprite.instance.LoadNewSprite(FilePath, [PixelsPerUnit (optional)])
    15.  
    16.     private static IMG2Sprite _instance;
    17.  
    18.     public static IMG2Sprite instance
    19.     {
    20.         get
    21.         {
    22.             //If _instance hasn't been set yet, we grab it from the scene!
    23.             //This will only happen the first time this reference is used.
    24.  
    25.             if (_instance == null)
    26.                 _instance = GameObject.FindObjectOfType<IMG2Sprite>();
    27.             return _instance;
    28.         }
    29.     }
    30.  
    31.     public Sprite LoadNewSprite(string FilePath, float PixelsPerUnit = 100.0f, SpriteMeshType spriteType = SpriteMeshType.Tight)
    32.     {      
    33.         // Load a PNG or JPG image from disk to a Texture2D, assign this texture to a new sprite and return its reference
    34.         Texture2D SpriteTexture = LoadTexture(FilePath);
    35.         Sprite NewSprite = Sprite.Create(SpriteTexture, new Rect(0, 0, SpriteTexture.width, SpriteTexture.height), new Vector2(0, 0), PixelsPerUnit, 0, spriteType);
    36.  
    37.         return NewSprite;
    38.     }
    39.  
    40.     public Texture2D LoadTexture(string FilePath)
    41.     {
    42.  
    43.         // Load a PNG or JPG file from disk to a Texture2D
    44.         // Returns null if load fails
    45.  
    46.         Texture2D Tex2D;
    47.         byte[] FileData;
    48.  
    49.         if (File.Exists(FilePath))
    50.         {
    51.             FileData = File.ReadAllBytes(FilePath);
    52.             Tex2D = new Texture2D(2, 2);           // Create new "empty" texture
    53.             if (Tex2D.LoadImage(FileData))           // Load the imagedata into the texture (size is set automatically)
    54.                 return Tex2D;                 // If data = readable -> return texture
    55.         }
    56.         return null;                     // Return null if load failed
    57.     }
    58. }
    59.  
    60.  
     
    urfx, minux, Redtail87 and 1 other person like this.
  14. rmgomez

    rmgomez

    Joined:
    Oct 2, 2014
    Posts:
    9

    I did some tests with your code and found out that if you set SpriteMeshType.FullRect instead of SpriteMeshType.Tight you increase the loading speed significantly.

    In case some is wondering why Sprite.Create is so slow.
     
  15. fadeway

    fadeway

    Joined:
    Apr 15, 2018
    Posts:
    9
    I tried tik_tikkers' latest snippet and the sprites it generates are noticably blurrier than ones created with the Editor and loaded as Resources. I tried decreasing PixelsPerUnit as well as both types of SpriteMeshType to no effect. Has anyone encountered this?

    (2019.4)
     
  16. ThomasKang

    ThomasKang

    Joined:
    Aug 19, 2013
    Posts:
    3
    Try settings Tex2d.FilterMode = FilterMode.Point

    Should remove the "blurriness" you're talking about.
     
    BinaryBanana and fadeway like this.
  17. fadeway

    fadeway

    Joined:
    Apr 15, 2018
    Posts:
    9
    Thanks for the tip Thomas! The Unity code I am trying to emulate was using FilterMode.Bilinear; setting my code to use that, as well as setting Tex2D.wrapMode = TextureWrapMode.Clamp; got things to look a lot closer to the inspector result.
     
    Last edited: Jan 12, 2021
  18. BinaryBanana

    BinaryBanana

    Joined:
    Mar 17, 2014
    Posts:
    68
    Thanks! I was looking for the solution for this for quite some time. You are the best!

    In my case I am loading map tiles, 138 jpgs with size 500px x 475px. If I drop it to Unity Editor each image is 0.7MB but if I keep it as ".bytes" in the Resources, it's only 50KBs each.

    It all works since but the edge of the image was blurred. With your suggestion is now nice and clean.

    This is how I load jpg from ".bytes" files, saving package size and loading it in the runtime. Seriously, Unity should have that option under sprite setup (slow load, no disk impact): :)
    Code (CSharp):
    1.  Texture2D texture = new Texture2D(500, 475, TextureFormat.RGB24, false);
    2. texture.filterMode = FilterMode.Point;
    3. TextAsset text = Resources.Load<TextAsset>($"MapTiles/Stage{stage}/{fileName}");
    4. texture.LoadImage(text.bytes);
    5.  
    6. Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100f, 1, SpriteMeshType.FullRect);
    Before:
    upload_2021-2-3_0-7-38.png

    After with FilterMode.Point:
    upload_2021-2-3_0-8-27.png

    JPGs in the Resource:
    upload_2021-2-3_0-13-16.png
     
unityunity