Search Unity

TGA Loader for Unity3D

Discussion in 'Assets and Asset Store' started by ZO5KmUG6R, Mar 1, 2013.

  1. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    490
    Hey guys! This is my TGA Loader, It supports uncompressed 32-bit or 24-bit True Color images

    Here is the code :)
    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections;
    4. using UnityEngine;
    5. using System.IO;
    6. using System.Collections.Generic;
    7. public Texture2D LoadTGA(string TGAFile){
    8.     using (BinaryReader r = new BinaryReader(File.Open(TGAFile, FileMode.Open))) {
    9.             byte IDLength = r.ReadByte();
    10. byte ColorMapType = r.ReadByte();
    11. byte ImageType = r.ReadByte();
    12. Int16 CMapStart = r.ReadInt16();
    13. Int16 CMapLength = r.ReadInt16();
    14. byte CMapDepth = r.ReadByte();
    15. Int16 XOffset = r.ReadInt16();
    16. Int16 YOffset = r.ReadInt16();
    17. Int16 Width = r.ReadInt16();
    18. Int16 Height = r.ReadInt16();
    19. byte PixelDepth = r.ReadByte();
    20. byte ImageDescriptor = r.ReadByte();
    21. if (ImageType == 0) {
    22. Debug.Log("Unsupported TGA file! No image data");
    23. } else if (ImageType == 3 | ImageType == 11) {
    24. Debug.Log("Unsupported TGA file! Not truecolor");
    25. } else if (ImageType == 9 | ImageType == 10) {
    26. Debug.Log("Unsupported TGA file! Colormapped");
    27.  
    28. }
    29. //     MsgBox("Dimensions are "  Width  ","  Height)
    30. Texture2D b = new Texture2D(Width, Height);
    31. for (int y = 0; y <= b.height - 1; y++) {
    32.     for (int x = 0; x <= b.width - 1; x++) {
    33.  
    34.         if (PixelDepth == 32) {
    35.          
    36.          float red = Convert.ToSingle(r.ReadByte());
    37.          float green = Convert.ToSingle(r.ReadByte());
    38.          float blue = Convert.ToSingle(r.ReadByte());
    39.          float alpha = Convert.ToSingle(r.ReadByte());
    40.          alpha /=255;
    41.          green /= 255;
    42.          blue /= 255;
    43.          red /= 255;
    44.         Color cl = new Color(blue ,green ,red ,alpha );
    45.             b.SetPixel(x, y, cl);
    46.                        
    47.                        
    48.         } else {
    49.          
    50.          float red = Convert.ToSingle(r.ReadByte());
    51.          float green = Convert.ToSingle(r.ReadByte());
    52.          float blue = Convert.ToSingle(r.ReadByte());
    53.          
    54.          
    55.          green /= 255;
    56.          blue /= 255;
    57.          red /= 255;
    58.         Color cl = new Color(blue ,green ,red ,1 );
    59.             b.SetPixel(x, y, cl);
    60.                        
    61.            
    62.         }
    63.  
    64.     }
    65. }
    66.             b.Apply();
    67.  
    68. return b;
    69.         }
    70.     }
    71.    
    72.  

    Remember to give credit if you use it :)
     
    Last edited: Mar 1, 2013
  2. SevenBits

    SevenBits

    Joined:
    Dec 26, 2011
    Posts:
    1,953
    Cool. Code looks okay on first glance, though formatting appears to be an issue...
     
  3. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    490
    Yeah, I made this quickly for a project I am doing.
     
  4. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    I would advise using Color32 instead of Color. Also SetPixels32 instead of SetPixel.

    --Eric
     
  5. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    490
    What's the difference?
     
  6. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Using SetPixels is faster than calling SetPixel repeatedly, and Color32 is faster than Color (since there's no need to convert ints to normalized floats) and uses 4X less RAM.

    --Eric
     
  7. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    490
    Ah alright, I'll use this in my BMP loader which I am working on
     
  8. kenshin

    kenshin

    Joined:
    Apr 21, 2010
    Posts:
    940
    Thanks for sharing aaro4130! :)
     
  9. Deleted User

    Deleted User

    Guest

    I slimmed this down and optimized it a bit. I use it for loading TGA Quake 3 textures, but you made it, so I feel I should share my alterations.

    I use an array to store Color32 objects (instead of Color) and work with plain bytes, which avoids all the bitwise and Convert. stuff. I also use SetPixels32 instead of hitting each pixel one by one. It's a bit faster. It is also a complete static class that can be passed either a file name as a string, or a raw stream. I'm using it with SharpZipLib to pull Quake 3 textures directly from .pk3 files, and it works perfectly. The previous design also checked the bitdepth in between every pixel, which is kind of crazy.

    Code (csharp):
    1. // This was made by aaro4130 on the Unity forums.  Thanks boss!
    2. // It's been optimized and slimmed down for the purpose of loading Quake 3 TGA textures from memory streams.
    3.  
    4. using System;
    5. using System.IO;
    6. using UnityEngine;
    7.  
    8. public static class TGALoader
    9. {
    10.  
    11.     public static Texture2D LoadTGA(string fileName)
    12.     {
    13.         using (var imageFile = File.OpenRead(fileName))
    14.         {
    15.             return LoadTGA(imageFile);
    16.         }
    17.     }
    18.  
    19.     public static Texture2D LoadTGA(Stream TGAStream)
    20.     {
    21.    
    22.         using (BinaryReader r = new BinaryReader(TGAStream))
    23.         {
    24.             // Skip some header info we don't care about.
    25.             // Even if we did care, we have to move the stream seek point to the beginning,
    26.             // as the previous method in the workflow left it at the end.
    27.             r.BaseStream.Seek(12, SeekOrigin.Begin);
    28.  
    29.             short width = r.ReadInt16();
    30.             short height = r.ReadInt16();
    31.             int bitDepth = r.ReadByte();
    32.  
    33.             // Skip a byte of header information we don't care about.
    34.             r.BaseStream.Seek(1, SeekOrigin.Current);
    35.  
    36.             Texture2D tex = new Texture2D(width, height);
    37.             Color32[] pulledColors = new Color32[width * height];
    38.  
    39.             if (bitDepth == 32)
    40.             {
    41.                 for (int i = 0; i < width * height; i++)
    42.                 {
    43.                     byte red = r.ReadByte();
    44.                     byte green = r.ReadByte();
    45.                     byte blue = r.ReadByte();
    46.                     byte alpha = r.ReadByte();
    47.  
    48.                     pulledColors [i] = new Color32(blue, green, red, alpha);
    49.                 }
    50.             } else if (bitDepth == 24)
    51.             {
    52.                 for (int i = 0; i < width * height; i++)
    53.                 {
    54.                     byte red = r.ReadByte();
    55.                     byte green = r.ReadByte();
    56.                     byte blue = r.ReadByte();
    57.                    
    58.                     pulledColors [i] = new Color32(blue, green, red, 1);
    59.                 }
    60.             } else
    61.             {
    62.                 throw new Exception("TGA texture had non 32/24 bit depth.");
    63.             }
    64.  
    65.             tex.SetPixels32(pulledColors);
    66.             tex.Apply();
    67.             return tex;
    68.  
    69.         }
    70.     }
    71. }
     
    Last edited by a moderator: Apr 9, 2014
    ZO5KmUG6R likes this.
  10. ZO5KmUG6R

    ZO5KmUG6R

    Joined:
    Jul 15, 2010
    Posts:
    490
    Wow. Thanks !

    Glad people are using it.
     
    Last edited: May 31, 2018
  11. liaojiangzheng

    liaojiangzheng

    Joined:
    Dec 25, 2017
    Posts:
    2
    at System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean anonymous, FileOptions options) [0x00000] in <filename unknown>:0
    at System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share) [0x00000] in <filename unknown>:0
    at System.IO.File.OpenRead (System.String path) [0x00000] in <filename unknown>:0
    at TGALoader.LoadTGA (System.String fileName) [0x00000] in <filename unknown>:0
    at XLua.CSObjectWrap.TGALoaderWrap._m_LoadTGA_xlua_st_ (IntPtr L) [0x00000] in <filename unknown>:0
    stack traceback:
    [C]: in field 'LoadTGA'
     
  12. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    328
    Fast loader for 32-bit uncompressed TGA files:

    Code (CSharp):
    1. using System;
    2. using System.IO;
    3. using UnityEngine;
    4. public static class TGALoader
    5. {
    6.     // Loads 32-bit (RGBA) uncompressed TGA. Actually, due to TARGA file structure, BGRA32 is good option...
    7.     // Disabled mipmaps. Disabled read/write option, to release texture memory copy.
    8.     public static Texture2D LoadTGA(string fileName)
    9.     {
    10.         try
    11.         {
    12.             BinaryReader reader = new BinaryReader(File.OpenRead(fileName));
    13.             reader.BaseStream.Seek(12, SeekOrigin.Begin);    
    14.             short width = reader.ReadInt16();
    15.             short height = reader.ReadInt16();
    16.             reader.BaseStream.Seek(2, SeekOrigin.Current);
    17.             byte[] source = reader.ReadBytes(width * height * 4);
    18.             reader.Close();
    19.             Texture2D texture = new Texture2D(width, height, TextureFormat.BGRA32, false);
    20.             texture.LoadRawTextureData(source);
    21.             texture.name = Path.GetFileName(fileName);
    22.             texture.Apply(false, true);
    23.             return texture;
    24.         }
    25.         catch (Exception)
    26.         {
    27.             return Texture2D.blackTexture;
    28.         }
    29.     }
    30. }
     
    ktagawa likes this.
  13. ktagawa

    ktagawa

    Joined:
    Dec 20, 2015
    Posts:
    1
  14. hippogames

    hippogames

    Joined:
    Feb 5, 2015
    Posts:
    234
    Hi! The code doesn't work for all tga samples I've found here: https://people.math.sc.edu/Burkardt/data/tga/tga.html

    "TGA texture had non 32/24 bit depth." or "Unable to read beyond the end of the stream."
     
    Last edited: Jan 18, 2022
  15. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    328
    Yes, because I wrote "Loads 32-bit (RGBA) uncompressed TGA." So it doesn't work for TGA with lossless RLE compression. Anyway, for game development I recommend to use DDS file format, because it is the best in quality and performance ratio, and it is easy to use compression and mipmaps. Examples:

    Standard DDS loader:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.IO;
    3. using System;
    4.  
    5. public static class LoadDDS
    6. {
    7.     public static Texture2D Load(string path)
    8.     {
    9.         try
    10.         {
    11.             BinaryReader reader = new BinaryReader(File.OpenRead(path));
    12.             long length = new FileInfo(path).Length;
    13.             byte[] header = reader.ReadBytes(128);
    14.             int height = header[13] * 256 + header[12];
    15.             int width = header[17] * 256 + header[16];
    16.             bool mipmaps = header[28] > 0;
    17.             TextureFormat textureFormat = header[87] == 49 ? TextureFormat.DXT1 : TextureFormat.DXT5;
    18.             byte[] source = reader.ReadBytes(Convert.ToInt32(length) - 128);
    19.             reader.Close();
    20.             Texture2D texture = new Texture2D(width, height, textureFormat, mipmaps);
    21.             texture.LoadRawTextureData(source);
    22.             texture.name = Path.GetFileName(path);
    23.             texture.Apply(false, true);
    24.             return texture;
    25.         }
    26.         catch (Exception)
    27.         {
    28.             return Texture2D.blackTexture;
    29.         }
    30.     }
    31. }
    Async DDS loader:

    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3. using System.IO;
    4.  
    5. public class LoadTextureAsyncFromDDS : MonoBehaviour
    6. {
    7.     async void Load (Material material, string property, string filepath)
    8.     {
    9.         FileStream stream = File.Open(filepath, FileMode.Open);
    10.         long length = stream.Length;
    11.         byte[] header = new byte[128];
    12.         await stream.ReadAsync(header, 0, 128);
    13.         int height = header[13] * 256 + header[12];
    14.         int width = header[17] * 256 + header[16];
    15.         bool mipmaps = header[28] > 0;
    16.         TextureFormat textureFormat = header[87] == 49 ? TextureFormat.DXT1 : TextureFormat.DXT5;
    17.         byte[] source = new byte[Convert.ToInt32(length) - 128];
    18.         await stream.ReadAsync(source, 0, Convert.ToInt32(length) - 128);
    19.         stream.Close();
    20.         Texture2D texture = new Texture2D(width, height, textureFormat, mipmaps);
    21.         texture.LoadRawTextureData(source);
    22.         texture.name = Path.GetFileName(filepath);
    23.         texture.Apply(false, true);
    24.         material.SetTexture(property, texture);
    25.     }
    26.  
    27.     void Delete (Material material, string property)
    28.     {
    29.         Destroy(material.GetTexture(property));
    30.         material.SetTexture(property, null);
    31.     }
    32.  
    33.     void Update()
    34.     {
    35.         if (Input.GetKeyDown(KeyCode.L))
    36.             Load(GetComponent<Renderer>().material, "_MainTex", System.IO.Path.Combine(Application.streamingAssetsPath, "plasma.dds"));
    37.         if (Input.GetKeyDown(KeyCode.Space))
    38.             Delete(GetComponent<Renderer>().material, "_MainTex");
    39.     }
    40. }
    Or use PNG , this is async PNG loader:

    Code (CSharp):
    1. using System.Threading.Tasks;
    2. using UnityEngine;
    3. using UnityEngine.Networking;
    4. using System;
    5. using System.Runtime.CompilerServices;
    6.  
    7. public class UnityWebRequestAwaiter : INotifyCompletion
    8. {
    9.     UnityWebRequestAsyncOperation request;
    10.     Action action;
    11.  
    12.     public UnityWebRequestAwaiter(UnityWebRequestAsyncOperation request)
    13.     {
    14.         this.request = request;
    15.         request.completed += OnRequestCompleted;
    16.     }
    17.  
    18.     public bool IsCompleted { get { return request.isDone; } }
    19.  
    20.     public void GetResult() { }
    21.  
    22.     public void OnCompleted(Action action)
    23.     {
    24.         this.action = action;
    25.     }
    26.  
    27.     private void OnRequestCompleted(AsyncOperation command)
    28.     {
    29.         action();
    30.     }
    31. }
    32.  
    33. public static class ExtensionMethods
    34. {
    35.     public static UnityWebRequestAwaiter GetAwaiter(this UnityWebRequestAsyncOperation request)
    36.     {
    37.         return new UnityWebRequestAwaiter(request);
    38.     }
    39. }
    40.  
    41. public class LoadTextureAsync : MonoBehaviour
    42. {
    43.     async void Load (Material material, string property, string filepath)
    44.     {
    45.         UnityWebRequest request = UnityWebRequestTexture.GetTexture(filepath);
    46.         await request.SendWebRequest();
    47.         material.SetTexture(property, ((DownloadHandlerTexture)request.downloadHandler).texture);
    48.     }
    49.  
    50.     void Update()
    51.     {
    52.         if (Input.GetKeyDown(KeyCode.Space))
    53.             Load(GetComponent<Renderer>().material, "_MainTex", System.IO.Path.Combine(Application.streamingAssetsPath, "plasma.png"));
    54.     }
    55. }
     
    Play_Edu likes this.
  16. hippogames

    hippogames

    Joined:
    Feb 5, 2015
    Posts:
    234
    Thanks for replying! I was asking in context of an image editor. Anyway, I've found another solution to load any types of TGA.
     
  17. JudahMantell

    JudahMantell

    Joined:
    Feb 28, 2017
    Posts:
    476
    I know it's been a while since you posted this, but would you mind sharing your TGA loading solution?
    Thanks!