- Home /
How can I resize an image and save it to disk ?
Hi,
I need to save thumbnails and then be able to load them back in a GUI.
I figured the reading part Ok, using WWW for reading from a file, using a "file://" type of url.
Right now, I'm using the code from the doc to read from the screen and save to a png file, which I load back in the GUI later. http://unity3d.com/support/documentation/ScriptReference/Texture2D.EncodeToPNG.html
Now, to lighten storage and free some video memory, I'd like to resize those thumbnails prior to saving them.
I am trying the following, with no luck :
function SaveSmallThumbnail () { var path = "test.png"; if (path) { yield WaitForEndOfFrame();
// Create a texture the size of the screen, RGB24 format
var width = Screen.width;
var height = Screen.height;
var tex = new Texture2D (width, height, TextureFormat.RGB24, false);
// Read screen contents into the texture
tex.ReadPixels (Rect(0, 0, width, height), 0, 0);
tex.Apply ();
tex.Resize(tex.width/2,tex.height/2,TextureFormat.RGB24, false); // this screws the saved image, why ?
tex.Apply ();
// Encode texture into PNG
var bytes = tex.EncodeToPNG();
// Destroy (tex);
File.WriteAllBytes(path, bytes);
}
}
For a reason that I don't get, when I use the resize function, the saved image turns black, or pixel jam. I someone can provide at least an explanation, I'd be glad to read it.
If someone has a solution, even better. Btw, I'd like to keep the aspect ratio, and crop to the smallest dimension of the image. Something tells me I'll end writing a custom function... :)
Answer by ProFive · Jul 14, 2013 at 10:32 AM
Screen shot and resize texture
public void MyCapture(string filename){
Texture2D screenshot = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, true);
screenshot.ReadPixels(new Rect(0,0,Screen.width, Screen.height), 0, 0);
screenshot.Apply();
Texture2D newScreenshot =ScaleTexture(screenshot, 1024,576);
byte[] bytes = newScreenshot.EncodeToPNG();
File.WriteAllBytes(filename, bytes);
}
private Texture2D ScaleTexture(Texture2D source,int targetWidth,int targetHeight) {
Texture2D result=new Texture2D(targetWidth,targetHeight,source.format,true);
Color[] rpixels=result.GetPixels(0);
float incX=((float)1/source.width)*((float)source.width/targetWidth);
float incY=((float)1/source.height)*((float)source.height/targetHeight);
for(int px=0; px<rpixels.Length; px++) {
rpixels[px] = source.GetPixelBilinear(incX*((float)px%targetWidth),
incY*((float)Mathf.Floor(px/targetWidth)));
}
result.SetPixels(rpixels,0);
result.Apply();
return result;
}
I tried using the above (ProFive's solution) and it seems to work except that is there a memory leak going on? When I run my Unity Game build on my iPad Device, and I monitor the $$anonymous$$emory meter in Xcode, the memory usage of my app, there is a 20 $$anonymous$$B spike on the take screenshot and resize. It never returns back to prior level.
I write the screenshot to file with the following line:` System.IO.File.WriteAllBytes(fileNameWithPath, screenshot.EncodeToJPG(75.0f));`
And I follow after that with Destory(screenshot) but memory usage does not return to the level it was at prior.
Is there a memory leak?
Thanks, $$anonymous$$anny
Answer by inewland · May 22, 2013 at 07:13 PM
I ran into this problem as well and I wanted to share a unique solution I found from another source. It basically takes the mipmap level of the texture and uses that pixel data.
1) First, create a new texture and make sure mipmaps are set to TRUE (this takes the screenshot)
Texture2D screenshot = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, true);
screenshot.ReadPixels(new Rect(0,0,Screen.width, Screen.height), 0, 0);
screenshot.Apply();
2) Next we need to create a dummy texture - this will be used as our new texture (dividing by 2 cuts our texture in half) - Make sure the mipmap level is set to 1
Texture2D newScreenshot = new Texture2D(screenshot.width/2, screenshot.height/2);
newScreenshot.SetPixels(screenshot.GetPixels(1));
newScreenshot.Apply();
3) Now you can encode and save to disk or whatever...
byte[] bytes = newScreenshot.EncodeToPNG();
string localURL = Application.persistentDataPath + "/screenshot.png";
File.WriteAllBytes(localURL, bytes);
Answer by CgShady · Nov 15, 2011 at 01:07 AM
Ok, I got this to work, but not as expected. I turned around this using a RenderTexture the size of the thumbnail and rendering the camera to it, which is even better as it doesn't even pop like when taking a screenshot.
Answer by jonomf · Jan 12, 2012 at 08:07 PM
The saved image is black because Resize() clears the image (as the documentation says, "After resizing, texture pixels will be undefined"). I found the same problem trying to do a very similar thing, and Render To Texture was my answer too.
Answer by Streamfall · Feb 18, 2016 at 11:03 PM
Manny, had that same trouble. I've had some success using a rotating array of textures. So I create an array of Texture2D[] of some size, and each screenshot I increment the index in that array. Each of these textures is being converted into a thumbnail (this is the part im working on now). After I reach the last item in that array, I destroy each of them. Then call GC.Collect. This means if those textures are still being used, they'll turn black. So I am effectively going to have to limit the number of screenshots which can be captured. If you found a solution for this, please get back to me.
Thanks
Your answer
Follow this Question
Related Questions
Guilayout problems with image resize 0 Answers
How do I trigger GUI elements 1 Answer
How do I change the texture of a Raw image? 0 Answers
GUIUtiliy.RotateAroundPivot() doesn't work 0 Answers
GUI Button using .png image? 1 Answer