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… :slight_smile:

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 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);

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.

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.

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

There’s a TextureScale script on Unify that offers a threaded scaling using Bilinear or Point sampling.