Skip to content

Instantly share code, notes, and snippets.

@Shubhra22
Last active April 25, 2024 12:45
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Shubhra22/bab1052cd90b9f4b89b3 to your computer and use it in GitHub Desktop.
Save Shubhra22/bab1052cd90b9f4b89b3 to your computer and use it in GitHub Desktop.
Screen Capture of particular UI elements
using UnityEngine;
using System.Collections;
using System.IO;
using UnityEngine.UI;
public class ScreenshotNow : MonoBehaviour
{
public RectTransform rectT; // Assign the UI element which you wanna capture
public Image img;
int width; // width of the object to capture
int height; // height of the object to capture
// Use this for initialization
void Start ()
{
width = System.Convert.ToInt32(rectT.rect.width);
height = System.Convert.ToInt32(rectT.rect.height);
}
// Update is called once per frame
void Update ()
{
if (Input.GetMouseButtonDown (0))
{
//StartCoroutine(takeScreenShot ()); // screenshot of a particular UI Element.
}
if (Input.GetKeyDown (KeyCode.A))
{
Application.CaptureScreenshot ("FullPageScreenShot.png");
}
}
public IEnumerator takeScreenShot()
{
yield return new WaitForEndOfFrame (); // it must be a coroutine
Vector2 temp = rectT.transform.position;
var startX = temp.x - width/2;
var startY = temp.y - height/2;
var tex = new Texture2D (width, height, TextureFormat.RGB24, false);
tex.ReadPixels (new Rect(startX, startY, width, height), 0, 0);
tex.Apply ();
// Encode texture into PNG
var bytes = tex.EncodeToPNG();
Destroy(tex);
//File.WriteAllBytes(Application.dataPath + "ScreenShot.png", bytes);
string imgsrc = System.Convert.ToBase64String(bytes);
Texture2D scrnShot = new Texture2D(1, 1, TextureFormat.ARGB32, false);
scrnShot.LoadImage(System.Convert.FromBase64String(imgsrc));
Sprite sprite = Sprite.Create(scrnShot, new Rect(0, 0, scrnShot.width, scrnShot.height), new Vector2(.5f, .5f));
img.sprite = sprite;
}
public void Capture()
{
StartCoroutine(takeScreenShot()); // screenshot of a particular UI Element.
}
}
@Straafe
Copy link

Straafe commented Jan 22, 2019

I'm getting these errors when trying to use this script:

[d3d11] attempting to ReadPixels outside of RenderTexture bounds!
and
Thread is not attached to scripting runtime

or
Trying to read pixels out of bounds

EDIT:
I think Unity has changed since this example was made. The dimensions were about 50% too large. I got it to work in Unity 2018 again, but changed the startX/startY and width/height to below:

       Vector3[] corners = new Vector3[4];
        rectT.GetWorldCorners(corners);
        var startX = corners[0].x;
        var startY = corners[0].y;

        int width = (int)corners[3].x - (int)corners[0].x;
        int height = (int)corners[1].y - (int)corners[0].y;

@CLOUDFIRE91
Copy link

Hi Strrafe i am using above code for screen shot the particular area. But the error is coming is "trying to read pixels out of bounds" . I am using your above code Vector3 line code. But it is not working.

@JohnDesley
Copy link

@Straafe You are amazing! This solved the scaling issue!

@matthias2279
Copy link

Thanks for this! You are amazing!

@cyberjj999
Copy link

Jesus THANK YOU SO MUCH FOR THIS! I have been trying to find a solution for AGES!

@gangstageek
Copy link

gangstageek commented Mar 2, 2021

Hi, I looked around for a few days on this. Pieced this together for this to work. I kept getting an error with the previous code. Also make sure your rect window is not larger than the actual screen size. You'll get a render texture error.

    Rect rect = RectTransformUtility.PixelAdjustRect(rectT, canvas); //public vars
    int textWidth = System.Convert.ToInt32(rect.width); // width of the object to capture
    int textHeight = System.Convert.ToInt32(rect.height); // height of the object to capture
    var startX = System.Convert.ToInt32(rect.x) + Screen.width / 2; // offset X
    var startY = System.Convert.ToInt32(rect.y) + Screen.height / 2; // offset Y

    RenderTexture rt = new RenderTexture(Screen.width , Screen.height , 24);
    Camera.main.targetTexture = rt;
    Texture2D screenShot = new Texture2D(textWidth , textHeight , textForm, false);
    Camera.main.Render();
    RenderTexture.active = rt;
    screenShot.ReadPixels(new Rect(startX, startY, textWidth, textHeight), 0, 0);
    Camera.main.targetTexture = null;
    RenderTexture.active = null;
    Destroy(rt);

    byte[] bytes = screenShot.EncodeToPNG();
    string filename = ScreenshotName("iOS+", (textWidth ).ToString(), (textHeight).ToString());
    if (!Directory.Exists(Application.persistentDataPath + "/screenshots/"))
        Directory.CreateDirectory(Application.persistentDataPath + "/screenshots/");
    System.IO.File.WriteAllBytes(filename, bytes);

@SimonDarksideJ
Copy link

SimonDarksideJ commented Oct 12, 2021

@gangstageek you don't need to render the camera to a RenderTexture, doing ".ReadPixels" will read directly from the current pixels of the screen at the end of a render frame automatically. That is if you want to grab what is on screen at the moment.

The only reason to use the RenderTexture method is if you are using an alternate camera to grab the UI from that is not currently in view.

@AnshuNair
Copy link

AnshuNair commented Jan 27, 2022

I'd like to highlight one requirement for Straafe's solution to work exactly as they have posted it. The rectTransform you are using must not be rotated. i.e. it must have a rotation of (0.0.0).

If it is rotated, you must use a different corner for your startX and startY values. Also, depending on the rotation, you may need to swap your width and height parameters. For example, my rectTransform was rotated -90 on the Z, so it would fit better on a phone in portrait mode.

This is the logic that worked for me:

    Vector3[] corners = new Vector3[4];
    rectTransform.GetWorldCorners(corners);
    int startX = (int)corners[3].x;
    int startY = (int)corners[3].y;
    int width = (int)corners[0].y - (int)corners[3].y;
    int height = (int)corners[1].x - (int)corners[0].x;
    var tex = new Texture2D(height, width, TextureFormat.RGB24, false);
    tex.ReadPixels(new Rect(startX, startY, height, width), 0, 0);
    tex.Apply();

startX and startY must be taken from the corner that is closest to the bottom left corner of your screen. So, in my case, it was corners[3], not corners[0].
I was skeptical if this solution would work with screen space overlay but it does, so that's great. Thanks for the solution, Straafe!

@Straafe
Copy link

Straafe commented Jan 27, 2022

Hey, no problem, I'm glad it helped people, and nice improvement and note about rotation. We could even edit it further so it finds the appropriate corners to use automatically by comparing their world positions to see which is closest to the true bottom left (although I imagine in almost all cases it would be either the corners you used or I used).

@SaumyaSaurav-RedAppleTech

This script takes screenshot of just the required UI object and saves to disk.

`using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class TakeScreenshotAndSave : MonoBehaviour
{
//Object To Screenshot
[SerializeField] private RectTransform _objToScreenshot;

//Assign the button to take screenshot on clicking
[SerializeField] private Button _takeScreenshotButton;

void Start()
{
    _takeScreenshotButton.onClick.AddListener(OnClickTakeScreenshotAndSaveButton);
}

private void OnClickTakeScreenshotAndSaveButton()
{
    StartCoroutine(TakeSnapShotAndSave());
}

//Using a Coroutine instead of normal method
public IEnumerator TakeSnapShotAndSave()
{
    //Code will throw error at runtime if this is removed
    yield return [new](http://www.google.com/search?q=new+msdn.microsoft.com) WaitForEndOfFrame();

    //Get the corners of RectTransform rect and store it in a array vector
    Vector3[] corners = [new](http://www.google.com/search?q=new+msdn.microsoft.com) Vector3[4];
    _objToScreenshot.GetWorldCorners(corners);

    //Remove 100 and you will get error
    int width = ((int)corners[3].x - (int)corners[0].x) - 100;
    int height = (int)corners[1].y - (int)corners[0].y;
    var startX = corners[0].x;
    var startY = corners[0].y;

    //Make a temporary texture and read pixels from it
    Texture2D ss = [new](http://www.google.com/search?q=new+msdn.microsoft.com) Texture2D(width, height, TextureFormat.RGB24, false);
    ss.ReadPixels([new](http://www.google.com/search?q=new+msdn.microsoft.com) Rect(startX, startY, width, height), 0, 0);
    ss.Apply();

    Debug.Log("Start X : " + startX + " Start Y : " + startY);
    Debug.Log("Screen Width : " + Screen.width + " Screen Height : " + Screen.height);
    Debug.Log("Texture Width : " + width + " Texture Height : " + height);

    //Save the screenshot to disk
    byte[] byteArray = ss.EncodeToPNG();
    string savePath = Application.persistentDataPath + "/ScreenshotSave.png";
    System.IO.File.WriteAllBytes(savePath, byteArray);
    Debug.Log("Screenshot Path : " + savePath);

    // Destroy texture to avoid memory leaks
    Destroy(ss);
}

}`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment