Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

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:
    936
    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:
    314
    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:
    228
    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:
    314
    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:
    228
    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. SceneForgeStudio

    SceneForgeStudio

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