I made a simple app for android devices that uses mobile's camera to take a photo and save it in internal storage folder /mnt/sdcard/DCIM/Camerizeman/
Photos are saved correctly but the problem that i am faceing is that i can't see the photos from mobile's gallery. I can see them correctly if i use a file manager or reboot me device. i am searchig 10 days now and the problem is that i have to refresh the gallery after saving the image.
I didn't found any working solution.
my code is bellow:
RenderTexture renderTex;
Texture2D screenshot;
Texture2D LoadScreenshot;
int width = Screen.width; // for Taking Picture
int height = Screen.height; // for Taking Picture
string fileName;
string myScreenshotLocation;
string screenShotName = "MyImage_AR_" + System.DateTime.Now.ToString("yyyy-MM-dd-HHmmss") + ".png";
public void Snapshot ()
{
StartCoroutine (CaptureScreen ());
}
public IEnumerator CaptureScreen ()
{
yield return null; // Wait till the last possible moment before screen rendering to hide the UI
//GameObject.FindGameObjectWithTag("Snapshoot").SetActive(false);
yield return new WaitForEndOfFrame (); // Wait for screen rendering to complete
if (Screen.orientation == ScreenOrientation.Portrait || Screen.orientation == ScreenOrientation.PortraitUpsideDown) {
mainCamera = Camera.main.GetComponent<Camera> (); // for Taking Picture
renderTex = new RenderTexture (width, height, 24);
mainCamera.targetTexture = renderTex;
RenderTexture.active = renderTex;
mainCamera.Render ();
screenshot = new Texture2D (width, height, TextureFormat.RGB24, false);
screenshot.ReadPixels (new Rect (0, 0, width, height), 0, 0);
screenshot.Apply (); //false
RenderTexture.active = null;
mainCamera.targetTexture = null;
}
if (Screen.orientation == ScreenOrientation.LandscapeLeft || Screen.orientation == ScreenOrientation.LandscapeRight) {
mainCamera = Camera.main.GetComponent<Camera> (); // for Taking Picture
renderTex = new RenderTexture (height, width, 24);
mainCamera.targetTexture = renderTex;
RenderTexture.active = renderTex;
mainCamera.Render ();
screenshot = new Texture2D (height, width, TextureFormat.RGB24, false);
screenshot.ReadPixels (new Rect (0, 0, height, width), 0, 0);
screenshot.Apply (); //false
RenderTexture.active = null;
mainCamera.targetTexture = null;
}
myScreenshotLocation = myFolderLocation + screenShotName;
File.WriteAllBytes (myFolderLocation + screenShotName, screenshot.EncodeToPNG ());
}
Please help!
The second working solution is:
using (AndroidJavaClass jcUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (AndroidJavaObject joActivity = jcUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
using (AndroidJavaObject joContext = joActivity.Call<AndroidJavaObject>("getApplicationContext"))
using (AndroidJavaClass jcMediaScannerConnection = new AndroidJavaClass("android.media.MediaScannerConnection"))
using (AndroidJavaClass jcEnvironment = new AndroidJavaClass("android.os.Environment"))
using (AndroidJavaObject joExDir = jcEnvironment.CallStatic<AndroidJavaObject>("getExternalStorageDirectory"))
{
jcMediaScannerConnection.CallStatic("scanFile", joContext, new string[] { YOURFULL IMAGE PATH}, null, null);
}
Use MediaScannerConnection. You need the image "scanned into" the gallery.
Native Java Code goes something like:
MediaScannerConnection.scanFile(unityPlayerActivity, new String[]{externalImagePath}, null, null);
May Create plugin - Unity Android plugin tutorial (1/3) Fundamentals -, or use AndroidJavaClass.CallStatic to invoke a MediaScannerConnection.scanFile.
Related
I am fairly new to Unity but am trying to take a photo from the camera and save it. Taking a screen capture is not an option. When I take the photo it appears clear in the center of the photo but gets further distorted towards the edges and I am not sure why. The main camera is linked to the realCamera object in Unity.
The screenshot code, which calls on update is:
DirectoryInfo screenshotDirectory = Directory.CreateDirectory(directoryName);
fileO = original + fileNameEnd + count.ToString() + fileType;
string fullPathO = Path.Combine(screenshotDirectory.FullName, fileO);
RenderTexture rt = new RenderTexture(resWidth, resHeight, 24);
Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false);
byte[] bytes;
realCamera.targetTexture = rt;
realCamera.Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
realCamera.targetTexture = null;
RenderTexture.active = null;
Destroy(rt);
bytes = screenShot.EncodeToPNG();
System.IO.File.WriteAllBytes(fullPathO, bytes);
using UnityEngine;
using System.Collections;
public class HiResScreenshots : MonoBehaviour
{
public Camera camera;
public int resWidth = 2550;
public int resHeight = 3300;
private bool takeHiResShot = false;
public static string ScreenShotName(int width, int height)
{
return string.Format("{0}/screenshots/screen_{1}x{2}_{3}.png",
Application.dataPath,
width, height,
System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
}
public void TakeHiResShot()
{
takeHiResShot = true;
}
void LateUpdate()
{
takeHiResShot |= Input.GetKeyDown("k");
if (takeHiResShot)
{
RenderTexture rt = new RenderTexture(resWidth, resHeight, 24);
camera.targetTexture = rt;
Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false);
camera.Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
camera.targetTexture = null;
RenderTexture.active = null; // JC: added to avoid errors
Destroy(rt);
byte[] bytes = screenShot.EncodeToPNG();
string filename = ScreenShotName(resWidth, resHeight);
System.IO.File.WriteAllBytes(filename, bytes);
Debug.Log(string.Format("Took screenshot to: {0}", filename));
takeHiResShot = false;
}
}
}
I'm getting exception DirectoryNotFoundException and when pressing once the k key it's taking screenshots non stop and I want it to take one screenshot each time when pressing the k key.
I'm getting exception DirectoryNotFoundException
You can use Path.GetDirectoryName in order to get the full directory path but without the filename from the filename.
And then you can simply use Directory.CreateDirectory
Creates all directories and subdirectories in the specified path unless they already exist.
So.you don't even have to check whether it exists. (Of course you still need the permission to do so)
when pressing once the k key it's taking screenshots non stop and I want it to take one screenshot each time when pressing the k key.
This is a follow up error. Since you get an exception you never reach the line
takeHiResShot = false;
In general rather use a Coroutine here like e.g.
public void TakeHiResShot()
{
StartCoroutine (ScreenshotRoutine ());
}
void Update()
{
if(Input.GetKeyDown("k"))
{
StartCoroutine (ScreenshotRoutine ());
}
}
private IEnumerator ScreenshotRoutine ()
{
yield return new WaitForEndOfFrame();
var rt = new RenderTexture(resWidth, resHeight, 24);
camera.targetTexture = rt;
var screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false);
camera.Render();
RenderTexture.active = rt;
screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);
camera.targetTexture = null;
RenderTexture.active = null; // JC: added to avoid errors
Destroy(rt);
var bytes = screenShot.EncodeToPNG();
var filename = ScreenShotName(resWidth, resHeight);
var directory = Path.GetDirectoryName(filename);
Directory.CreateDirectory(directory);
System.IO.File.WriteAllBytes(filename, bytes);
Debug.Log($"Took screenshot to: /"{filename}/"", this);
}
This makes it imposible to land in an endless loop in case there was an error
This is in relation to this post Save AcquireCameraImageBytes() from Unity ARCore to storage as an image
I tried the steps mentioned by
#JordanRobinson I am having a similar issue of seeing just a gray square. I keep re-reading his update, and I am not clear how step 2 (creating a texture reader) ties to step 3. I added the update function to call Frame.CameraImage.AcquireCameraImageBytes. I think missing something.
I feel I am close as it is saving an image (just a gray nothing image :-) Any help you can offer will be greatly appreciated
Here is my code
private Texture2D m_TextureRender;
private TextureReader m_CachedTextureReader;
void Start ()
{
m_CachedTextureReader = GetComponent<TextureReader>();
m_CachedTextureReader.OnImageAvailableCallback += OnImageAvailable;
QuitOnConnectionErrors ();
}
void Update () {
Screen.sleepTimeout = SleepTimeout.NeverSleep;
using (var image = Frame.CameraImage.AcquireCameraImageBytes())
{
if (!image.IsAvailable)
{
return;
}
OnImageAvailable(TextureReaderApi.ImageFormatType.ImageFormatColor,
image.Width, image.Height, image.Y, 0);
}
}
private void OnImageAvailable(TextureReaderApi.ImageFormatType format, int width, int height, System.IntPtr pixelBuffer, int bufferSize)
{
if (format != TextureReaderApi.ImageFormatType.ImageFormatColor)
{
Debug.Log("No edge detected due to incorrect image format.");
return;
}
if (m_TextureRender == null || m_EdgeDetectionResultImage == null || m_TextureRender.width != width || m_TextureRender.height != height)
{
m_TextureRender = new Texture2D(width, height, TextureFormat.RGBA32, false, false);
m_EdgeDetectionResultImage = new byte[width * height * 4];
m_TextureRender.width = width;
m_TextureRender.height = height;
}
System.Runtime.InteropServices.Marshal.Copy(pixelBuffer, m_EdgeDetectionResultImage, 0, bufferSize);
// Update the rendering texture with the sampled image.
m_TextureRender.LoadRawTextureData(m_EdgeDetectionResultImage);
m_TextureRender.Apply();
var encodedJpg = m_TextureRender.EncodeToJPG();
var path = Application.persistentDataPath;
File.WriteAllBytes(path + "/test2.jpg", encodedJpg);
}
My game has a baseline memory usage of around 315 MB. However calling the following functions leads to a sharp rise in memory usage, leveling out at around 480 MB while reaching spikes of 580 MB and more, accompanied by memory warnings and even crashes.
What happens: First the TakeScreenshot IEnum is called three times in a row, wich is the max. count for screenshots in one session. Second the function SendEmailTask is called showing all three pictures for the user to choose one. By choosing picture "#1" the function SendImage1 is triggered.
Maybe someone can point me to where and how I can get some of that memory back, that would be really great!
All relevant code should be here:
public class Picture : MonoBehaviour {
private int ssCount = 0;
private Sprite cachedImage1sprite;
private Sprite cachedImage2sprite;
private Sprite cachedImage3sprite;
private Texture2D cachedImage1;
private Texture2D cachedImage2;
private Texture2D cachedImage3;
private Texture2D JPGtex1;
private Texture2D JPGtex2;
private Texture2D JPGtex3;
private Texture2D tex;
void Awake () {
}
// Use this for initialization
void Start () {
JPGtex1 = new Texture2D (2, 2, TextureFormat.RGB24, false );
JPGtex2 = new Texture2D (2, 2, TextureFormat.RGB24, false );
JPGtex3 = new Texture2D (2, 2, TextureFormat.RGB24, false );
// Create a texture the size of the screen, RGB24 format
int width = Screen.width;
int height = Screen.height;
tex = new Texture2D( width, height, TextureFormat.RGB24, false );
}
// Update is called once per frame
void Update () {
if (ssCount == 0) {
SendEmail.interactable = false;
TakePhoto.interactable = true;
} else if (ssCount == 1) {
SendEmail.interactable = true;
} else if (ssCount == 3) {
TakePhoto.interactable = false;
}
//Debug.Log (ssCount);
}
void SendEmailTask(){
if (ssCount == 3) {
cachedImage1 = SA.IOSNative.Storage.AppCache.GetTexture ("IMAGE_1");
cachedImage2 = SA.IOSNative.Storage.AppCache.GetTexture ("IMAGE_2");
cachedImage3 = SA.IOSNative.Storage.AppCache.GetTexture ("IMAGE_3");
ImagePicker.SetActive (true);
//Image1
Rect rec1 = new Rect(0, 0, cachedImage1.width, cachedImage1.height);
cachedImage1sprite = Sprite.Create(cachedImage1, rec1, new Vector2(0,0),1);
Image1.image.sprite = cachedImage1sprite;
//Image2
Rect rec2 = new Rect(0, 0, cachedImage2.width, cachedImage2.height);
cachedImage2sprite = Sprite.Create(cachedImage2, rec2, new Vector2(0,0),1);
Image2.image.sprite = cachedImage2sprite;
//Image3
Rect rec3 = new Rect(0, 0, cachedImage3.width, cachedImage3.height);
cachedImage3sprite = Sprite.Create(cachedImage3, rec3, new Vector2(0,0),1);
Image3.image.sprite = cachedImage3sprite;
SA.IOSNative.Storage.AppCache.Remove ("IMAGE_1");
SA.IOSNative.Storage.AppCache.Remove ("IMAGE_2");
SA.IOSNative.Storage.AppCache.Remove ("IMAGE_3");
}
}
IEnumerator TakeScreenshot() {
// Wait till the last possible moment before screen rendering to hide the UI
yield return null;
GameObject.Find("Buttons").GetComponent<Canvas>().enabled = false;
FlashImage();
// Wait for screen rendering to complete
yield return new WaitForEndOfFrame();
// Create a texture the size of the screen, RGB24 format
int width = Screen.width;
int height = Screen.height;
// Read screen contents into the texture
tex.ReadPixels( new Rect(0, 0, width, height), 0, 0 );
tex.Apply();
//byte[] screenshot = tex.EncodeToPNG();
print("Size is " + tex.width + " by " + tex.height);
if (ssCount == 0) {
SA.IOSNative.Storage.AppCache.Save ("IMAGE_1", tex);
ssCount++;
} else if (ssCount == 1) {
SA.IOSNative.Storage.AppCache.Save ("IMAGE_2", tex);
ssCount++;
} else if (ssCount == 2) {
SA.IOSNative.Storage.AppCache.Save ("IMAGE_3", tex);
ssCount++;
}
IOSCamera.Instance.SaveTextureToCameraRoll(tex); //Save to Cameraroll
// Show UI after we're done
GameObject.Find("Buttons").GetComponent<Canvas>().enabled = true;
}
public void SendImage1() {
byte[] screenshot1;
screenshot1 = cachedImage1.EncodeToJPG ();
if (Facebook == false) {
JPGtex1.LoadImage (screenshot1);
TextureScale.Bilinear (JPGtex1, 1200, 900);
IOSSocialManager.Instance.SendMail (SubjectText, EmailText, "", JPGtex1);
} else {
StartCoroutine(UploadToPage(screenshot1));
}
backToGame ();
}
public void backToGame() {
Destroy (cachedImage1sprite);
Destroy (cachedImage2sprite);
Destroy (cachedImage3sprite);
SA.IOSNative.Storage.AppCache.Remove ("IMAGE_1");
SA.IOSNative.Storage.AppCache.Remove ("IMAGE_2");
SA.IOSNative.Storage.AppCache.Remove ("IMAGE_3");
Destroy(cachedImage1);
Destroy(cachedImage2);
Destroy(cachedImage3);
cachedImage1 = null;
cachedImage2 = null;
cachedImage3 = null;
Image3Obj.SetActive (true);
ImagePicker.SetActive (false);
}
}
EDIT
Detailed memory profiler after going thru the routine twice:
Xcode memory profiler after going thru the routine twice:
From your profiler report;
You're using a big chunk of memory on Meshes and Texture2D Assets - that'd suggest to me you're possibly drawing over them/hiding them from the user rather than actually removing them from memory. 50+ MB in Texture2D Assets is a little odd considering you've got a further 120+ MB of Meshes being loaded in. I'm assuming from that that it's a 3D game, but has a 2D UI? If so, then 50MB is quite a lot to be spending on Texture2D Assets.
"Objects" are what you think they are - they're Objects. Instantiated classes, or GameObjects containing attached components. So if you made a Player GameObject, and attached to that is a "playerStats" object with a couple of variables for health, speed, stamina etc etc, then that'd count as 2 objects.
80MB isn't too worrying for Objects. Your Texture2D and Meshes use is what would strike me as being pretty high for a game that's targeting iOS. Make sure you're using mobile-friendly models, and textures that aren't too high in resolution.
I had a similar problem with audio files in iOS, I don't know if this could be your case.
I loaded big audio files of 20mb+ on memory to process them and then release the memory, the thing is that the memory kept going up in the xcode profiler. This is caused by memory fragmentation, you can read more about it here: https://stackoverflow.com/a/3770593/4024219
My solution was to load the files in little chunks and reuse my arrays instead of creating and destroying new ones.
I'm building a web based app that takes a screenshot of a play area and then posts it to a web server to be called up in a image gallery for other to view. Currently, when running in the editor I can take the screenshot and save it locally but that won't work once it's deployed. I don't know how to take that screenshot and save it to a texture (rather than to disk) to then upload to my server. How do I do this? I'm new at this and especially new at Render Texture functionality. Can someone help me sort this out?
I have found this Snippet on a forum here. But not tested by myself on WebPlayer.
using UnityEngine;
using System.Collections;
public class Main : MonoBehaviour
{
private string _data = string.Empty;
public Texture2D bg;
void OnGUI()
{
if (GUI.Button(new Rect(Screen.width*0.5f-32,32,64,32),"Save"))
StartCoroutine(ScreeAndSave());
}
IEnumerator ScreeAndSave()
{
yield return new WaitForEndOfFrame();
var newTexture = ScreenShoot(Camera.main, bg.width, bg.height);
LerpTexture(bg, ref newTexture);
_data = System.Convert.ToBase64String(newTexture.EncodeToPNG());
Application.ExternalEval("document.location.href='data:octet-stream;base64," + _data + "'");
}
private static Texture2D ScreenShoot(Camera srcCamera, int width, int height)
{
var renderTexture = new RenderTexture(width, height, 0);
var targetTexture = new Texture2D(width, height, TextureFormat.RGB24, false);
srcCamera.targetTexture = renderTexture;
srcCamera.Render();
RenderTexture.active = renderTexture;
targetTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
targetTexture.Apply();
srcCamera.targetTexture = null;
RenderTexture.active = null;
srcCamera.ResetAspect();
return targetTexture;
}
private static void LerpTexture(Texture2D alphaTexture, ref Texture2D texture)
{
var bgColors = alphaTexture.GetPixels();
var tarCols = texture.GetPixels();
for (var i = 0; i < tarCols.Length; i++)
tarCols[i] = bgColors[i].a > 0.99f ? bgColors[i] : Color.Lerp(tarCols[i], bgColors[i], bgColors[i].a);
texture.SetPixels(tarCols);
texture.Apply();
}
}
Reference Link