Search code examples
c#unity-game-enginetexturestexture2d

How to create an image from byte array and display it?


I want to get data from an image file and display that information in a texture that Unity can read.

I am able to get the pixel information into a byte array, but nothing ever displays on the screen. How do I actually get the image to display?

     pcxFile = File.ReadAllBytes("Assets/5_ImageParser/bagit_icon.pcx");
     int startPoint = 128;
     int height = 152; 
     int width = 152; 
     target = new Texture2D(height, width);
     for (var y = 0; y < height; y++)
     {
         for (var x = 0; x < width; x++)
         {
             timesDone ++;
             pixels[x, y] = new Color(pcxFile[startPoint], pcxFile[startPoint+1], pcxFile[startPoint+2]);
             startPoint += 4;
             target.SetPixel(x, y, pixels[x, y]);
         }            
     }
     target.Apply();
     target.EncodeToJPG();

Solution

  • Well, (assuming you get the pixel data correctly) you have to asign that created texture to something...

    I'ld use e.g. a RawImage since it doesn't need a Sprite (as the UI.Image component would do - also see the RawImage Manual):

    // Reference this in the Inspector
    public RawImage image;
    
    //...
    
    pcxFile = File.ReadAllBytes("Assets/5_ImageParser/bagit_icon.pcx");
    int startPoint = 128;
    int height = 152; 
    int width = 152; 
    target = new Texture2D(height, width);
    for (var y = 0; y < height; y++)
    {
        for (var x = 0; x < width; x++)
        {
            timesDone ++;
            pixels[x, y] = new Color(pcxFile[startPoint], pcxFile[startPoint+1], pcxFile[startPoint+2]);
            startPoint += 4;
            target.SetPixel(x, y, pixels[x, y]);
        }            
    }
    target.Apply();
    // You don't need this. Only if you are also going to save it locally
    // as an actual *.jpg file or if you are going to
    // e.g. upload it later via http POST
    //
    // In this case however you would have to asign the result 
    // to a variable in order to use it later
    //var rawJpgBytes = target.EncodeToJPG();
    
    // Assign the texture to the RawImage component
    image.texture = target;
    

    Alternatively for using with normal Image component create a Sprite from your texture using Sprite.Create:

    // Reference this in the Inspector
    public Image image;
    
    // ...
    
    var sprite = Sprite.Create(target, new Rect(0.0f, 0.0f, target.width, target.height), new Vector2(0.5f, 0.5f), 100.0f);
    
    image.sprite = sprite;
    

    Hint 1
    In order to have the correct aspect ratio I used a little trick usually:

    1. Create a parent object for this RawImage
    2. Set the desired "maximal size" in the RectTransform of the parent
    3. Next to the RawImage/Image (on the child object) add an AspectRatioFitter (also see the AspectRatioFitter Manual) and set AspectMode to FitInParent.
    4. Now in the code adjust the aspect ratio (you get it from the texture):

      public RawImage image;
      public AspectRatioFitter fitter;
      
      //...
      
      image.texture = target;
      
      var ratio = target.width / target.height;
      fitter.aspectRatio = ratio;
      

    Hint 2
    It is "cheaper" to call SetPixels once for all pixels than calling SetPixel repeatedly:

    // ...
    
    startPoint = 0;
    pixels = new Color[width * height]();
    for(int i = 0; i < pixels.Length; i++)
    {
        pixels[i] = new Color(pcxFile[startPoint], pcxFile[startPoint+1], pcxFile[startPoint+2]);
        startPoint += 4;
    }
    
    target.SetPixels(pixels);
    target.Apply();
    
    // ...
    

    (I don't know how exactly your pcx format works but maybe you could even use LoadRawTextureData