How can I convert images to textures? - c#

I'm using this script but I'm not sure how to convert the info array to textures and assign the info images to the textures array.
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
public class StreamVideo : MonoBehaviour
{
public Texture[] frames; // array of textures
public float framesPerSecond = 2.0f; // delay between frames
public RawImage image;
void Start()
{
DirectoryInfo dir = new DirectoryInfo(#"C:\tmp");
string[] extensions = new[] { ".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG", ".ogg", ".OGG" };
FileInfo[] info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
if (image == null)
{
//Get Raw Image Reference
image = gameObject.GetComponent<RawImage>();
}
}
void Update()
{
int index = (int)(Time.time * framesPerSecond) % frames.Length;
image.texture = frames[index]; //Change The Image
}
}
Then I copied the images from the tmp folder to my project new folder but if I select all the images in the assets I can't drag them to the inspector of the script I can only drag them one by one and it will take time so how can I do it with the script ?

Well you would need to read the according file e.g. using File.ReadAllBytes
Then you could use ImageConversion.LoadImage
I would then do the entire loading process already on app start and later only exchange the texture like e.g.
public class StreamVideo : MonoBehaviour
{
public Texture[] frames; // array of textures
public float framesPerSecond = 2.0f; // delay between frames
public RawImage image;
void Start()
{
DirectoryInfo dir = new DirectoryInfo(#"C:\tmp");
// since you use ToLower() the capitalized version are quite redundant btw ;)
string[] extensions = new[] { ".jpg", ".jpeg", ".png", ".ogg" };
FileInfo[] info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
if (!image)
{
//Get Raw Image Reference
image = gameObject.GetComponent<RawImage>();
}
frames = GetTextures(infos);
}
private Texture[] GetTextures(FileInfo[] fileInfos)
{
var output = new Texture[fileInfos.Length];
for(var i = 0; i < fileInfos.Length; i++)
{
var bytes = File.ReadAllBytes(fileInfos[i].fullName);
output[i] = new Texture2D(1,1);
if(!ImageConversion.LoadImage(output[i], bytes, false))
{
Debug.LogError($"Could not load image from {fileInfos.Length}!", this);
}
}
}
void Update()
{
int index = (int)(Time.time * framesPerSecond) % frames.Length;
image.texture = frames[index]; //Change The Image
}
}
Note: Typed on smartphone but I hope the idea gets clear

You can drag multiple selected images.
First you need to lock your inspector view with a padlock (not possible if you skip that).
Then select all the images, and drop then onto the name_of_the_array (not, into the field). This is just a matter of getting the drag mechanic right, nothing else.
Also, fileinfo reference in your question is a bit confusing, as you are not doing anything with the result anyway.

Have not done that myself yet, but I believe that should work with:
https://docs.unity3d.com/ScriptReference/ImageConversion.LoadImage.html
In the code example there, insteed of "imageAsset.bytes", apply this function to your files (calling FullPath on your FileInfo instances):
https://docs.unity3d.com/ScriptReference/Windows.File.ReadAllBytes.html

Related

Path and gallery

I am having a problem with my gallery code, my Android does not recognize the right folder with the .jpg images on the line 20 and 30. It´s a lot different that a PC code cause of the disk details, if someone could help me, i will be a lot gratefull
public class Gallery : MonoBehaviour {
public List<Sprite> gallery = new List<Sprite>();
public Image displayImage;
public Button nextImg;
public Button prevImg;
public int i = 0;
void Start ()
{
// var Images = Directory.GetFiles("C:/Users/Bandeira/Downloads/Menu Start/Assets/Sprite/JPG/","*.jpg");
var Images = Directory.GetFiles("file:///" + "/unitypictures/","*.jpg");
Debug.Log(Images);
StartCoroutine(LoadImages(Images));
}
IEnumerator LoadImages(string[] Images)
{
foreach (var path in Images)
{
Debug.Log(path);
using (var uwr = UnityWebRequestTexture.GetTexture("file:///" + path))
// using (var uwr = UnityWebRequestTexture.GetTexture(path))
{
Debug.Log(path);
yield return uwr.SendWebRequest();
if (uwr.result != UnityWebRequest.Result.Success)
{
Debug.Log(uwr.error);
yield break;
}
var tex = DownloadHandlerTexture.GetContent(uwr);
var sprite = Sprite.Create(tex, new Rect(0f, 0f, tex.width, tex.height), new Vector2(0.5f, 0.5f), 50f, 0, SpriteMeshType.FullRect);
gallery.Add(sprite);
uwr.Dispose();
}
yield return null;
}
}
public void BtnNext (){
if(i + 1 < gallery.Count){
i++;
}
displayImage.sprite = gallery[i];
}
public void BtnPrev () {
if (i - 1 > 0){
i--;
}
displayImage.sprite = gallery[i];
}
}
Dude, I already told you yesterday.
You are using absolute paths, and they cannot work, because for each operating system they are different.
If you connect your Android phone to your computer, or install a file manager, you can see that there should be no folder called "UnityPictures", and even if there is you can't get there easily because I believe they are protected by the device.
The answer is always the same: you can use Persistent data path to save the images and resume them later.
For example you can save them in the Directory (Application.PersistentDataPath + "UnityPictures") and get them back from the same path.
So to be clear: you have to create the folder and insert those images, if you then want to download them.
Alternatively you can also download them from a server. So you could edit them, add more, or remove them without the need for updates, and it would be usable for all devices.

How can I make a sprite from a byte array?

I have a Unity game where the player gets one's inventory from the database after starting the game.
I got the pictures of items stored in Firebase Storage, and I can download them.
However, I don't know how to make sprites from the byte[] (fileContents) I get by downloading.
(I'd load these sprites in the player's inventory later)
I got the following C# script:
private void getPlayersInventory()
{
FirebaseDatabase.DefaultInstance.GetReference("users").Child(auth.CurrentUser.UserId)
.Child("inventory").GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
print("unable to get snapshot for the inventory");
}
else if (task.IsCompleted)
{
snapshot = task.Result;
if(snapshot.HasChildren)
{
foreach(var current in snapshot.Children)
{
string currentName = current.Value.ToString();
print(currentName.ToLower() + ".png");
const long maxAllowedSize = 10 * 1024 * 1024;
storageReference.Child(currentName.ToLower() + ".png")
.GetBytesAsync(maxAllowedSize)
.ContinueWith((Task<byte[]> task_) =>
{
if (task_.IsFaulted || task_.IsCanceled)
{
Debug.Log(task_.Exception.ToString());
}
else
{
byte[] fileContents = task_.Result;
Debug.Log("Finished downloading!");
}
});
}
}
}
});
}
I believe that you're looking for ImageConversion.LoadImage
Your full code will look something like:
var tex = new Texture(1,1); // note that the size is overridden
tex.LoadImage(task_.Result);
var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(tex.width/2, tex.height/2));
You can also pack textures at runtime if that's your eventual goal. Once you get through the tex.LoadImage chunk, you should be able to work out Texture2D.PackTextures.
--Patrick

Unity 2019 - How can I save mixer audio output to an AudioClip or load a newly saved .wav to an audioclip at runtime?

I'm making a game with musical instruments and a recording device in-game. I want the user to be able to play the instruments and record them to an audioclip - and to a wav file - which they can then play back as accompaniment to themselves playing another instrument, the output again recorded, allowing them to make tracks with combined instruments. So far I have everything working except I can't figure out how to get the audioclip - I have .wav files being saved to the asset folder but I don't understand the code enough to figure out where to set the audioclip's data to the data being written.
I'm using code from Olkor here to save the recording as wav, which is working great: https://unitylist.com/p/za/Output-Audio-Recorder
My two options to save an audioclip from this seem to be a) save to an audioclip at the same time as saving to disk (I can't figure this out) or save it to disk and then load it into the game as an audioclip - using Unity Web Request which I have tried to do but I get an error either cannot access the .audioclip property of an aborted DownloadHandlerAudioClip or if I invoke the function with a delay to load the file, there is an error decoding the audio. Either way I have a saved .wav audio file in my assets folder but no audioclip.
InvalidOperationException: Cannot access the .audioClip property of an aborted DownloadHandlerAudioClip
UnityEngine.Networking.DownloadHandlerAudioClip.GetContent
(UnityEngine.Networking.UnityWebRequest www) (at
C:/buildslave/unity/build/Modules/UnityWebRequestAudio/Public/DownloadHandler
Audio.bindings.cs:49)
OutputAudioRecorder+<GetAudioClip>d__27.MoveNext () (at
Assets/Scripts/OutputAudioRecorder.cs:202)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator
enumerator, System.IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)
The script below that I'm using is attached to the audiolistener and so it's picking up all the output of audio from the game. I have looked at doing something with audioListener GetOutputData and passing it to audioClip SetData but I haven't been able to figure it out and I admit that this is beyond my current ability - I'd really appreciate some insight into how to approach this problem in any of the ways possible.
using System;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class OutputAudioRecorder : MonoBehaviour
{
public const string DEFAULT_FILENAME = "record";
public const string FILE_EXTENSION = ".wav";
public bool IsRecording { get { return recOutput; } }
private int bufferSize;
private int numBuffers;
private int outputRate;
private int headerSize = 44; //default for uncompressed wav
private String fileName;
private bool recOutput = false;
private AudioClip newClip;
private FileStream fileStream;
private AudioClip[] audioClips;
private AudioSource[] audioSources;
public int currentSlot;
float[] tempDataSource;
void Awake()
{
outputRate = AudioSettings.outputSampleRate;
}
void Start()
{
AudioSettings.GetDSPBufferSize(out bufferSize, out numBuffers);
audioSources = new AudioSource[3];
audioSources[0] = GameObject.FindWithTag("RecSlot1").GetComponent<AudioSource>();
audioSources[1] = GameObject.FindWithTag("RecSlot2").GetComponent<AudioSource>();
audioSources[2] = GameObject.FindWithTag("RecSlot3").GetComponent<AudioSource>();
}
public void StartRecording(string recordFileName)
{
fileName = Path.GetFileNameWithoutExtension(recordFileName) + FILE_EXTENSION;
if (!recOutput)
{
StartWriting(fileName);
recOutput = true;
}
else
{
Debug.LogError("Recording is in progress already");
}
}
public void StopRecording()
{
recOutput = false;
WriteHeader();
UpdateClip();
}
private void StartWriting(String name)
{
fileStream = new FileStream(Application.dataPath + "/" + name, FileMode.Create);
var emptyByte = new byte();
for (int i = 0; i < headerSize; i++) //preparing the header
{
fileStream.WriteByte(emptyByte);
}
}
private void OnAudioFilterRead(float[] data, int channels)
{
if (recOutput)
{
ConvertAndWrite(data); //audio data is interlaced
}
}
private void ConvertAndWrite(float[] dataSource)
{
var intData = new Int16[dataSource.Length];
//converting in 2 steps : float[] to Int16[], //then Int16[] to Byte[]
var bytesData = new Byte[dataSource.Length * 2];
//bytesData array is twice the size of
//dataSource array because a float converted in Int16 is 2 bytes.
var rescaleFactor = 32767; //to convert float to Int16
for (var i = 0; i < dataSource.Length; i++)
{
intData[i] = (Int16)(dataSource[i] * rescaleFactor);
var byteArr = new Byte[2];
byteArr = BitConverter.GetBytes(intData[i]);
byteArr.CopyTo(bytesData, i * 2);
}
fileStream.Write(bytesData, 0, bytesData.Length);
tempDataSource = new float[dataSource.Length];
tempDataSource = dataSource;
}
private void WriteHeader()
{
fileStream.Seek(0, SeekOrigin.Begin);
var riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
fileStream.Write(riff, 0, 4);
var chunkSize = BitConverter.GetBytes(fileStream.Length - 8);
fileStream.Write(chunkSize, 0, 4);
var wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
fileStream.Write(wave, 0, 4);
var fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
fileStream.Write(fmt, 0, 4);
var subChunk1 = BitConverter.GetBytes(16);
fileStream.Write(subChunk1, 0, 4);
UInt16 two = 2;
UInt16 one = 1;
var audioFormat = BitConverter.GetBytes(one);
fileStream.Write(audioFormat, 0, 2);
var numChannels = BitConverter.GetBytes(two);
fileStream.Write(numChannels, 0, 2);
var sampleRate = BitConverter.GetBytes(outputRate);
fileStream.Write(sampleRate, 0, 4);
var byteRate = BitConverter.GetBytes(outputRate * 4);
fileStream.Write(byteRate, 0, 4);
UInt16 four = 4;
var blockAlign = BitConverter.GetBytes(four);
fileStream.Write(blockAlign, 0, 2);
UInt16 sixteen = 16;
var bitsPerSample = BitConverter.GetBytes(sixteen);
fileStream.Write(bitsPerSample, 0, 2);
var dataString = System.Text.Encoding.UTF8.GetBytes("data");
fileStream.Write(dataString, 0, 4);
var subChunk2 = BitConverter.GetBytes(fileStream.Length - headerSize);
fileStream.Write(subChunk2, 0, 4);
fileStream.Close();
}
void UpdateClip()
{
StartCoroutine(GetAudioClip());
}
IEnumerator GetAudioClip()
{
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file://" + Application.dataPath + "myRecord1.wav", AudioType.WAV))
{
yield return www.Send();
if (www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
AudioClip newClip = DownloadHandlerAudioClip.GetContent(www);
Debug.Log(newClip.name + "name " + newClip.length);
keyboardScript.audioClip = newClip;
}
}
}
Try to load as audio clip using this:
private IENumerator LoadAudio(string url) {
WWW www;
www = new WWW("file:///" + url);
yield return www;
if (www != null && www.isDone)
{
AudioClip audioClip;
audioClip = www.GetAudioClip(true, false, AudioType.WAV);
}
}
That works for me at least.
Thought this was solution but still not working to load at runtime. (It just refers to the old copy of the clip until I reload the game. Didn't notice this at first.)
While I wasn't able to get the web request to work for me or get WWW to work either, I did get the effect I wanted
by creating a blank clip in the assets folder where the .wav saves, then dragging it in the properties window as a reference, setting the audio clip to streaming and then using the code above to write the wav, it automatically overwrites the clip with the data I recorded.
The audio has a bit of distortion on it (crackling) which I'll look into next.
This solution is I'm aware probably incredibly obvious (i guess I had to give unity something to refer to but didn't realise it) and doesn't solve the problem of loading files at runtime if they aren't predefined like mine are, but it fixed my problem at least.

Unity3d Delete current picture showing in gallery

I'm fairly new to C# and Unity so I'm having a little issue here. I'm working on some kind of "photobooth app", which makes the gallery a big part of it. However, I managed to sort out the part of taking screenshots and show them, my problem is that I can't seem to figure out how to delete the pictures from the gallery (I mean, inside the app).
So far, I'm working with this code (just to give it some sense), but this bit erases ALL of the taken pictures, not just the one currently showing.
tring path = Application.persistentDataPath;
DirectoryInfo dir = new DirectoryInfo(path);
FileInfo[] info = dir.GetFiles("*.png");
foreach (FileInfo f in info)
{
File.Delete(f.FullName);
}
I don't know if it'd help, but this is the code I'm using to take and save the screenshots:
yield return new WaitForEndOfFrame();
string timeStamp = System.DateTime.Now.ToString("dd-MM-yyyy-HH-mm-ss");
string fileName = "Screenshot" + timeStamp + ".png";
string pathToSave = fileName;
ScreenCapture.CaptureScreenshot(pathToSave);
yield return new WaitForEndOfFrame();
And, the one I'm using for showing them in the gallery:
public class ScreenShotPreview : MonoBehaviour
{
[SerializeField]
GameObject Panel;
[SerializeField]
string sceneName;
string[] files = null;
int whichScreenShotIsShown = 0;
void Start()
{
files = Directory.GetFiles(Application.persistentDataPath + "/", "*.png");
if (files.Length > 0)
{
GetPictureAndShowIt();
}
}
void GetPictureAndShowIt()
{
string pathToFile = files[whichScreenShotIsShown];
Texture2D texture = GetScreenshotImage(pathToFile);
Sprite sp = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
Panel.GetComponent<Image>().sprite = sp;
}
Texture2D GetScreenshotImage(string filePath)
{
Texture2D texture = null;
byte[] fileBytes;
if (File.Exists(filePath))
{
fileBytes = File.ReadAllBytes(filePath);
texture = new Texture2D(2, 2, TextureFormat.RGB24, false);
texture.LoadImage(fileBytes);
}
return texture;
}
public void NextPicture()
{
if (files.Length > 0)
{
whichScreenShotIsShown += 1;
if (whichScreenShotIsShown > files.Length - 1)
whichScreenShotIsShown = 0;
GetPictureAndShowIt();
}
}
public void PreviousPicture()
{
if (files.Length > 0)
{
whichScreenShotIsShown -= 1;
if (whichScreenShotIsShown < 0)
whichScreenShotIsShown = files.Length - 1;
GetPictureAndShowIt();
}
}
I hope it makes sense? Thank you in advance!
TLDR; Can't figure out how to delete current picture showing in the gallery.
Your file paths are stored in the string[] files variable. The whichScreenShotIsShown variable is the current index that determines which path is currently being displayed. These two variables are declared inside the ScreenShotPreview script.
Therefore to delete the current file, you would do something like this:
string currentFile = files[whichScreenShotIsShown];
File.Delete(currentFile );

Unity System.Drawing Internal Buffer Overflow Exception

Goal
Get a gif working in unity from a URL, I am currently using the WWW class. I am currently getting a byte[] and converting it to a System.Drawing.Image. This works in the editor but not any build
Error:
"Type Load Exception: Could not load type" System.IO.InternalBufferOverflowException from the assembly "System.Drawing.Image" at line 111
Why?
It has to do with the System.Drawing.Image.FromStream built in method, Unity for some reason doesn't like it. The other options are .FromFile and .FromHBitMap, I dont know how to use HBitMap but going back to my original plan, .FromFile is unusable to me.
Entire Code
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using UnityEngine;
using System.IO;
using UnityEngine.UI;
using System.Collections;
public class AnimatedGifDrawerBack : MonoBehaviour
{
public string loadingGifPath;
public float speed = 1;
public Vector2 drawPosition;
public string pName;
public float width;
public float height;
public float percentage;
public GameObject positionPlaceHolderGO;
public Vector2 positionPlaceHolder;
public Text debugText;
private SpriteImageArray sia;
private string url;
private WWW www;
public bool finishedWWW = false;
public bool hasWWW = false;
public bool canOnGUI = false;
List<Texture2D> gifFrames = new List<Texture2D>();
void Start()
{
percentage = 1.3f;
positionPlaceHolderGO = GameObject.FindGameObjectWithTag("PBLPlace");
positionPlaceHolder = positionPlaceHolderGO.transform.position;
}
void Update()
{
while (hasWWW == false)
{
Debug.Log("in while loop");
if (this.GetComponent<PokemonCreatorBack>().name == "")
{
}
else
{
debugText.text = "Name Found";
url = "www.pkparaiso.com/imagenes/xy/sprites/animados-espalda/" + this.GetComponent<PokemonCreatorBack>().PokemonName.ToLower() + ".gif";
StartCoroutine(WaitForRequest(positionPlaceHolderGO, url));
hasWWW = true;
debugText.text = "hawWWW = true";
}
}
}
void OnGUI()
{
height = (float)Screen.height - 80f / percentage;
//GUI.DrawTexture (new Rect (Screen.width-width, Screen.height - height, gifFrames [0].width * percentage, gifFrames [0].height * percentage), gifFrames [(int)(Time.frameCount * speed) % gifFrames.Count]);
if (canOnGUI)
GUI.DrawTexture(new Rect(positionPlaceHolder.x, positionPlaceHolder.y, gifFrames[0].width * percentage, gifFrames[0].height * percentage), gifFrames[(int)(Time.frameCount * speed) % gifFrames.Count]);
}
IEnumerator WaitForRequest(GameObject go, string url)
{
www = new WWW(url);
yield return www;
if (www.error == null)
{
Debug.Log("WWW Ok!: " + www.texture.name);
}
else
{
Debug.Log("WWW Error: " + www.error);
}
debugText.text = "finishedWWW = true";
finishedWWW = true;
}
public System.Drawing.Image ByteArrayToImage(byte[] byteArrayIn)
{
if (finishedWWW == false)
{
Debug.Log("Called too early");
}
if (byteArrayIn == null)
{
Debug.Log("Null byte array");
return null;
}
Debug.Log("Bytra array in length: " + byteArrayIn.GetLongLength(0));
MemoryStream ms = new MemoryStream(byteArrayIn);
System.Drawing.Image returnImage = System.Drawing.Image.FromStream(ms); //MAIN SOURCE OF ERROR HERE
finishedWWW = true;
debugText.text = "System.Image Created";
return returnImage;
}
public void loadImage()
{
Debug.Log("Called Load Image BACK");
debugText.text = "Called Load Image BACK";
System.Drawing.Image gifImage = ByteArrayToImage(www.bytes);
FrameDimension dimension = new FrameDimension(gifImage.FrameDimensionsList[0]);
int frameCount = gifImage.GetFrameCount(dimension);
for (int i = 0; i < frameCount; i++)
{
gifImage.SelectActiveFrame(dimension, i);
Bitmap frame = new Bitmap(gifImage.Width, gifImage.Height);
System.Drawing.Graphics.FromImage(frame).DrawImage(gifImage, Point.Empty);
Texture2D frameTexture = new Texture2D(frame.Width, frame.Height);
for (int x = 0; x < frame.Width; x++)
for (int y = 0; y < frame.Height; y++)
{
System.Drawing.Color sourceColor = frame.GetPixel(x, y);
frameTexture.SetPixel(frame.Width - 1 + x, -y, new Color32(sourceColor.R, sourceColor.G, sourceColor.B, sourceColor.A)); // for some reason, x is flipped
}
frameTexture.Apply();
gifFrames.Add(frameTexture);
}
Debug.Log("Starting ON GUI!");
debugText.text = "Starting OnGUI";
canOnGUI = true;
}
}
Thoughts
byteArrayIn.GetLongLength(0)
returns 80,000 at most.
The last debug statement coming through is Called Image Loading BACK.
I will write my own fire streamer if necessary, and if it is necessary can someone point me in the write direction for that.
I think the main workaround is dealing with the Image.FromStream().
There are two of these in the scene.
All thoughts or solutions are welcome, I really just wish I knew how to tackle this error so that I could share it more with the Unity Community.
We faced the same problem this morning.
The app is not finding a type in the System.IO namespace required by System.Drawing.Image.
The missing type has apparently been stripped from the system.dll that is packed during the build process.
To fix this, you need to copy and replace the unity generated System.dll with the original mono System.dll.
In your build, replace projectName_Data\Managed\System.dll with the System.dll found in Unity's mono installation folder:
Editor\Data\Mono\lib\mono\2.0 (relative to the root of the Unity installation folder).
Hope it helps!

Categories

Resources