Think about any Unity.UI Canvas
you might have.
Imagine a typical Panel
on that Canvas. Say it happens to contain some images, maybe some Text and so on.
It would be very handy if you could turn that panel (only the panel) in to a screenshot: a Texture2D or PNG.
The only thing I can think of is just using ReadPixels
and figuring out the area of the Panel
in question on the screen (and actually that is pretty tricky); and that only works if the panel happens to be square and not rotated at an angle.
You'd think, there should be a way to render one Panel, or at least, one whole canvas? I can find nothing.
In the example, make the pink Panel a PNG image. Ouch.
(Obviously, if anyone has a solution that does merely "the whole Canvas", rather than one Panel, of course even that is admirable.)
The code below can take picture of a Canvas. Canvas must be attached the Object you are passing into it. The only function to call is void takeScreenShot(Canvas canvasPanel, SCREENSHOT_TYPE screenShotType = SCREENSHOT_TYPE.IMAGE_AND_TEXT, bool createNewInstance = true)
SCREENSHOT_TYPE.IMAGE_AND_TEXT
parameter will take picture of images and texts.
SCREENSHOT_TYPE.IMAGE_ONLY
parameter will only take picture of images. Every texts on the screen will be excluded. You can use this for security reason to remove texts and just show graphics only.
SCREENSHOT_TYPE.TEXT_ONLY
parameter will take picture of Texts only.
How to use. Create a GameObject, attach the CanvasScreenShot script to it. Subscribe to CanvasScreenShot.OnPictureTaken(byte[] pngArray)
;, then call screenShot.takeScreenShot(canvasToSreenShot, SCREENSHOT_TYPE.IMAGE_AND_TEXT, false);
Complete Code:
Your test.cs
script:
public class test : MonoBehaviour
{
public Canvas canvasToSreenShot;
// Use this for initialization
void Start()
{
//Subscribe
CanvasScreenShot.OnPictureTaken += receivePNGScreenShot;
CanvasScreenShot screenShot = GameObject.Find("GameObject").GetComponent<CanvasScreenShot>();
//take ScreenShot(Image and Text)
//screenShot.takeScreenShot(canvasToSreenShot, SCREENSHOT_TYPE.IMAGE_AND_TEXT, false);
//take ScreenShot(Image only)
screenShot.takeScreenShot(canvasToSreenShot, SCREENSHOT_TYPE.IMAGE_ONLY, false);
//take ScreenShot(Text only)
// screenShot.takeScreenShot(canvasToSreenShot, SCREENSHOT_TYPE.TEXT_ONLY, false);
}
public void OnEnable()
{
//Un-Subscribe
CanvasScreenShot.OnPictureTaken -= receivePNGScreenShot;
}
void receivePNGScreenShot(byte[] pngArray)
{
Debug.Log("Picture taken");
//Do Something With the Image (Save)
string path = Application.persistentDataPath + "/CanvasScreenShot.png";
System.IO.File.WriteAllBytes(path, pngArray);
Debug.Log(path);
}
}
The CanvasScreenShot.cs
script:
public class CanvasScreenShot : MonoBehaviour
{
/*
CanvasScreenShot by programmer.
http://stackoverflow.com/questions/36555521/unity3d-build-png-from-panel-of-a-unity-ui#36555521
http://stackoverflow.com/users/3785314/programmer
*/
//Events
public delegate void takePictureHandler(byte[] pngArray);
public static event takePictureHandler OnPictureTaken;
private GameObject duplicatedTargetUI;
private Image[] allImages;
private Text[] allTexts;
//Store all other canvas that will be disabled and re-anabled after screenShot
private Canvas[] allOtherCanvas;
//takes Screenshot
public void takeScreenShot(Canvas canvasPanel, SCREENSHOT_TYPE screenShotType = SCREENSHOT_TYPE.IMAGE_AND_TEXT, bool createNewInstance = true)
{
StartCoroutine(_takeScreenShot(canvasPanel, screenShotType, createNewInstance));
}
private IEnumerator _takeScreenShot(Canvas canvasPanel, SCREENSHOT_TYPE screenShotType = SCREENSHOT_TYPE.IMAGE_AND_TEXT, bool createNewInstance = true)
{
//Get Visible Canvas In the Scene
allOtherCanvas = getAllCanvasInScene(false);
//Hide all the other Visible Canvas except the one that is passed in as parameter(Canvas we want to take Picture of)
showCanvasExcept(allOtherCanvas, canvasPanel, false);
//Reset the position so that both UI will be in the-same place if we make the duplicate a child
resetPosAndRot(gameObject);
//Check if we should operate on the original image or make a duplicate of it
if (createNewInstance)
{
//Duplicate the Canvas we want to take Picture of
duplicatedTargetUI = duplicateUI(canvasPanel.gameObject, "ScreenShotUI");
//Make this game object the parent of the Canvas
duplicatedTargetUI.transform.SetParent(gameObject.transform);
//Hide the orginal Canvas we want to take Picture of
showCanvas(canvasPanel, false);
}
else
{
//No duplicate. Use original GameObject
//Make this game object the parent of the Canvas
canvasPanel.transform.SetParent(gameObject.transform);
}
RenderMode defaultRenderMode;
//Change the duplicated Canvas to RenderMode to overlay
Canvas duplicatedCanvas = null;
if (createNewInstance)
{
duplicatedCanvas = duplicatedTargetUI.GetComponent<Canvas>();
defaultRenderMode = duplicatedCanvas.renderMode;
duplicatedCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
}
else
{
defaultRenderMode = canvasPanel.renderMode;
canvasPanel.renderMode = RenderMode.ScreenSpaceOverlay;
}
if (screenShotType == SCREENSHOT_TYPE.IMAGE_AND_TEXT)
{
//No Action Needed
}
else if (screenShotType == SCREENSHOT_TYPE.IMAGE_ONLY)
{
if (createNewInstance)
{
//Get all images on the duplicated visible Canvas
allTexts = getAllTextsFromCanvas(duplicatedTargetUI, false);
//Hide those images
showTexts(allTexts, false);
}
else
{
//Get all images on the duplicated visible Canvas
allTexts = getAllTextsFromCanvas(canvasPanel.gameObject, false);
//Hide those images
showTexts(allTexts, false);
}
}
else if (screenShotType == SCREENSHOT_TYPE.TEXT_ONLY)
{
if (createNewInstance)
{
//Get all images on the duplicated visible Canvas
allImages = getAllImagesFromCanvas(duplicatedTargetUI, false);
//Hide those images
showImages(allImages, false);
}
else
{
//Get all images on the duplicated visible Canvas
allImages = getAllImagesFromCanvas(canvasPanel.gameObject, false);
//Hide those images
showImages(allImages, false);
}
}
//////////////////////////////////////Finally Take ScreenShot///////////////////////////////
yield return new WaitForEndOfFrame();
Texture2D screenImage = new Texture2D(Screen.width, Screen.height);
//Get Image from screen
screenImage.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
screenImage.Apply();
//Convert to png
byte[] pngBytes = screenImage.EncodeToPNG();
/*FOR TESTING/DEBUGGING PURPOSES ONLY. COMMENT THIS
string path = Application.persistentDataPath + "/CanvasScreenShot.png";
System.IO.File.WriteAllBytes(path, pngBytes);
Debug.Log(path);*/
//Notify functions that are subscribed to this event that picture is taken then pass in image bytes as png
if (OnPictureTaken != null)
{
OnPictureTaken(pngBytes);
}
///////////////////////////////////RE-ENABLE OBJECTS
//Change the duplicated Canvas RenderMode back to default Value
if (createNewInstance)
{
duplicatedCanvas.renderMode = defaultRenderMode;
}
else
{
canvasPanel.renderMode = defaultRenderMode;
}
//Un-Hide all the other Visible Canvas except the one that is passed in as parameter(Canvas we want to take Picture of)
showCanvas(allOtherCanvas, true);
if (screenShotType == SCREENSHOT_TYPE.IMAGE_AND_TEXT)
{
//No Action Needed
}
else if (screenShotType == SCREENSHOT_TYPE.IMAGE_ONLY)
{
//Un-Hide those images
showTexts(allTexts, true);
}
else if (screenShotType == SCREENSHOT_TYPE.TEXT_ONLY)
{
//Un-Hide those images
showImages(allImages, true);
}
//Un-hide the orginal Canvas we want to take Picture of
showCanvas(canvasPanel, true);
if (createNewInstance)
{
//Destroy the duplicated GameObject
Destroy(duplicatedTargetUI, 1f);
}
else
{
//Remove the Canvas as parent
canvasPanel.transform.SetParent(null);
}
}
private GameObject duplicateUI(GameObject parentUICanvasOrPanel, string newOBjectName)
{
GameObject tempObj = Instantiate(parentUICanvasOrPanel);
tempObj.name = newOBjectName;
return tempObj;
}
private Image[] getAllImagesFromCanvas(GameObject canvasParentGameObject, bool findDisabledCanvas = false)
{
Image[] tempImg = canvasParentGameObject.GetComponentsInChildren<Image>(findDisabledCanvas);
if (findDisabledCanvas)
{
return tempImg;
}
else
{
System.Collections.Generic.List<Image> canvasList = new System.Collections.Generic.List<Image>();
for (int i = 0; i < tempImg.Length; i++)
{
if (tempImg[i].enabled)
{
canvasList.Add(tempImg[i]);
}
}
return canvasList.ToArray();
}
}
private Text[] getAllTextsFromCanvas(GameObject canvasParentGameObject, bool findDisabledCanvas = false)
{
Text[] tempImg = canvasParentGameObject.GetComponentsInChildren<Text>(findDisabledCanvas);
if (findDisabledCanvas)
{
return tempImg;
}
else
{
System.Collections.Generic.List<Text> canvasList = new System.Collections.Generic.List<Text>();
for (int i = 0; i < tempImg.Length; i++)
{
if (tempImg[i].enabled)
{
canvasList.Add(tempImg[i]);
}
}
return canvasList.ToArray();
}
}
private Canvas[] getAllCanvasFromCanvas(Canvas canvasParentGameObject, bool findDisabledCanvas = false)
{
Canvas[] tempImg = canvasParentGameObject.GetComponentsInChildren<Canvas>(findDisabledCanvas);
if (findDisabledCanvas)
{
return tempImg;
}
else
{
System.Collections.Generic.List<Canvas> canvasList = new System.Collections.Generic.List<Canvas>();
for (int i = 0; i < tempImg.Length; i++)
{
if (tempImg[i].enabled)
{
canvasList.Add(tempImg[i]);
}
}
return canvasList.ToArray();
}
}
//Find Canvas.
private Canvas[] getAllCanvasInScene(bool findDisabledCanvas = false)
{
Canvas[] tempCanvas = GameObject.FindObjectsOfType<Canvas>();
if (findDisabledCanvas)
{
return tempCanvas;
}
else
{
System.Collections.Generic.List<Canvas> canvasList = new System.Collections.Generic.List<Canvas>();
for (int i = 0; i < tempCanvas.Length; i++)
{
if (tempCanvas[i].enabled)
{
canvasList.Add(tempCanvas[i]);
}
}
return canvasList.ToArray();
}
}
//Disable/Enable Images
private void showImages(Image[] imagesToDisable, bool enableImage = true)
{
for (int i = 0; i < imagesToDisable.Length; i++)
{
imagesToDisable[i].enabled = enableImage;
}
}
//Disable/Enable Texts
private void showTexts(Text[] imagesToDisable, bool enableTexts = true)
{
for (int i = 0; i < imagesToDisable.Length; i++)
{
imagesToDisable[i].enabled = enableTexts;
}
}
//Disable/Enable Canvas
private void showCanvas(Canvas[] canvasToDisable, bool enableCanvas = true)
{
for (int i = 0; i < canvasToDisable.Length; i++)
{
canvasToDisable[i].enabled = enableCanvas;
}
}
//Disable/Enable one canvas
private void showCanvas(Canvas canvasToDisable, bool enableCanvas = true)
{
canvasToDisable.enabled = enableCanvas;
}
//Disable/Enable Canvas Except
private void showCanvasExcept(Canvas[] canvasToDisable, Canvas ignoreCanvas, bool enableCanvas = true)
{
for (int i = 0; i < canvasToDisable.Length; i++)
{
if (!(canvasToDisable[i] == ignoreCanvas))
{
canvasToDisable[i].enabled = enableCanvas;
}
}
}
//Disable/Enable Canvas Except
private void showCanvasExcept(Canvas[] canvasToDisable, Canvas[] ignoreCanvas, bool enableCanvas = true)
{
for (int i = 0; i < canvasToDisable.Length; i++)
{
for (int j = 0; j < ignoreCanvas.Length; j++)
{
if (!(canvasToDisable[i] == ignoreCanvas[j]))
{
canvasToDisable[i].enabled = enableCanvas;
}
}
}
}
//Reset Position
private void resetPosAndRot(GameObject posToReset)
{
posToReset.transform.position = Vector3.zero;
posToReset.transform.rotation = Quaternion.Euler(Vector3.zero);
}
}
public enum SCREENSHOT_TYPE
{
IMAGE_AND_TEXT, IMAGE_ONLY, TEXT_ONLY
}