EditorScreenshot.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEditor;
  6. //-----------------------------------------------------------------------------
  7. // Copyright 2012-2022 RenderHeads Ltd. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. namespace RenderHeads.Media.AVProMovieCapture.Editor
  10. {
  11. public class EditorScreenshot : MonoBehaviour
  12. {
  13. internal enum ImageFormat
  14. {
  15. PNG,
  16. JPG,
  17. TGA,
  18. EXR,
  19. }
  20. internal enum ExrPrecision
  21. {
  22. Half,
  23. Float,
  24. }
  25. internal enum ExrCompression
  26. {
  27. None,
  28. ZIP,
  29. RLE,
  30. PIZ,
  31. }
  32. [Serializable]
  33. internal class Options
  34. {
  35. [SerializeField, Range(1, 100)]
  36. internal int jpgQuality = 75;
  37. [SerializeField]
  38. internal ExrPrecision exrPrecision;
  39. [SerializeField]
  40. internal ExrCompression exrCompression;
  41. internal Texture2D.EXRFlags GetExrFlags()
  42. {
  43. Texture2D.EXRFlags result = Texture2D.EXRFlags.None;
  44. if (exrPrecision == ExrPrecision.Float) result |= Texture2D.EXRFlags.OutputAsFloat;
  45. if (exrCompression == ExrCompression.ZIP) result |= Texture2D.EXRFlags.CompressZIP;
  46. else if (exrCompression == ExrCompression.RLE) result |= Texture2D.EXRFlags.CompressRLE;
  47. else if (exrCompression == ExrCompression.PIZ) result |= Texture2D.EXRFlags.CompressPIZ;
  48. return result;
  49. }
  50. }
  51. private const string SceneCameraName = "SceneCamera";
  52. internal static RenderTexture GetSceneViewTexture()
  53. {
  54. RenderTexture result = null;
  55. Camera[] cameras = FindAllCameras();
  56. if (cameras != null)
  57. {
  58. Camera camera = FindCameraByName(cameras.Length, cameras, SceneCameraName);
  59. if (camera != null)
  60. {
  61. if (camera.targetTexture != null)
  62. {
  63. // Note we have to force a render
  64. camera.Render();
  65. result = camera.targetTexture;
  66. }
  67. }
  68. }
  69. return result;
  70. }
  71. private static RenderTexture GetSceneViewTexture2()
  72. {
  73. RenderTexture result = null;
  74. RenderTexture[] rts = Resources.FindObjectsOfTypeAll<RenderTexture>();
  75. foreach (RenderTexture rt in rts)
  76. {
  77. if (rt.name == "SceneView RT")
  78. {
  79. result = rt;
  80. break;
  81. }
  82. }
  83. return result;
  84. }
  85. internal static void SceneViewToFile(string fileNamePrefix, string folderPath, ImageFormat format, Options options)
  86. {
  87. RenderTexture cameraTexture = GetSceneViewTexture();
  88. if (cameraTexture != null)
  89. {
  90. Texture2D texture = GetReadableTexture(cameraTexture, format == ImageFormat.EXR);
  91. if (texture != null)
  92. {
  93. string filePath = EditorScreenshot.GenerateFilename(fileNamePrefix, format, texture.width, texture.height);
  94. filePath = GenerateFilePath(folderPath, filePath);
  95. TextureToFile(texture, filePath, format, options);
  96. if (Application.isPlaying)
  97. {
  98. Destroy(texture);
  99. }
  100. else
  101. {
  102. DestroyImmediate(texture);
  103. }
  104. }
  105. }
  106. else
  107. {
  108. Debug.LogError("SceneView texture isn't available, make sure the view is visible");
  109. }
  110. }
  111. internal static Texture2D GetReadableTexture(RenderTexture texture, bool supportHDR)
  112. {
  113. var oldRT = RenderTexture.active;
  114. TextureFormat format = TextureFormat.RGBA32;
  115. if (supportHDR)
  116. {
  117. format = TextureFormat.RGBAFloat;
  118. }
  119. Texture2D destTex = new Texture2D(texture.width, texture.height, format, false, supportHDR);
  120. RenderTexture.active = texture;
  121. destTex.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0);
  122. destTex.Apply();
  123. RenderTexture.active = oldRT;
  124. return destTex;
  125. }
  126. internal static bool SupportsTGA()
  127. {
  128. #if UNITY_2018_3_OR_NEWER
  129. return true;
  130. #else
  131. return false;
  132. #endif
  133. }
  134. internal static bool SupportsGameViewJPGTGAEXR()
  135. {
  136. #if UNITY_2017_3_OR_NEWER
  137. return Application.isPlaying;
  138. #else
  139. return false;
  140. #endif
  141. }
  142. internal static bool SupportsGameViewEXR()
  143. {
  144. #if UNITY_2019_1_OR_NEWER
  145. return Application.isPlaying;
  146. #else
  147. return false;
  148. #endif
  149. }
  150. internal static void TextureToFile(Texture2D texture, string filePath, ImageFormat format, Options options)
  151. {
  152. byte[] data = null;
  153. #if UNITY_2017_1_OR_NEWER
  154. switch (format)
  155. {
  156. case ImageFormat.PNG:
  157. data = ImageConversion.EncodeToPNG(texture);
  158. break;
  159. case ImageFormat.JPG:
  160. data = ImageConversion.EncodeToJPG(texture, options.jpgQuality);
  161. break;
  162. case ImageFormat.TGA:
  163. #if UNITY_2018_3_OR_NEWER
  164. data = ImageConversion.EncodeToTGA(texture);
  165. #endif
  166. break;
  167. case ImageFormat.EXR:
  168. data = ImageConversion.EncodeToEXR(texture, options.GetExrFlags());
  169. break;
  170. }
  171. #else
  172. switch (format)
  173. {
  174. case ImageFormat.PNG:
  175. data = texture.EncodeToPNG();
  176. break;
  177. case ImageFormat.JPG:
  178. data = texture.EncodeToJPG(options.jpgQuality);
  179. break;
  180. case ImageFormat.EXR:
  181. data = texture.EncodeToEXR(options.GetExrFlags());
  182. break;
  183. }
  184. #endif
  185. if (data != null)
  186. {
  187. System.IO.File.WriteAllBytes(filePath, data);
  188. OnFileWritten(filePath);
  189. }
  190. }
  191. internal static void GameViewToPNG(string filePath, int superSize = 1)
  192. {
  193. #if UNITY_2017_1_OR_NEWER
  194. ScreenCapture.CaptureScreenshot(filePath, superSize);
  195. #else
  196. Application.CaptureScreenshot(filePath, superSize);
  197. #endif
  198. // The screenshot will not be generated until the frame has finished (at least in Application.CaptureScreenshot())
  199. if (!Application.isPlaying)
  200. {
  201. UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
  202. }
  203. OnFileWritten(filePath);
  204. }
  205. internal static void OnFileWritten(string filePath)
  206. {
  207. Debug.Log("[AVProMovieCapture] File written: " + filePath);
  208. CaptureBase.LastFileSaved = filePath;
  209. }
  210. internal static void RenderTextureToFile(string filePath, ImageFormat format, Options options, RenderTexture rt)
  211. {
  212. Texture2D texture = GetReadableTexture(rt, format == ImageFormat.EXR);
  213. if (texture != null)
  214. {
  215. TextureToFile(texture, filePath, format, options);
  216. if (Application.isPlaying)
  217. {
  218. Destroy(texture);
  219. }
  220. else
  221. {
  222. DestroyImmediate(texture);
  223. }
  224. }
  225. }
  226. internal static void GameViewToFile(string filePath, ImageFormat format, Options options, int superSize = 1)
  227. {
  228. // Coroutines aren't supported in editor mode, so we fake it using a GameObject with EditorCoroutine component
  229. GameObject go = new GameObject("temp-screenshot");
  230. go.hideFlags = HideFlags.HideAndDontSave;
  231. EditorCoroutine co = go.AddComponent<EditorCoroutine>();
  232. co.RunCoroutine(EditorScreenshot.GameViewToFileCoroutine(filePath, format, options, go, superSize));
  233. }
  234. internal static IEnumerator GameViewToFileCoroutine(string filePath, ImageFormat format, Options options, GameObject go, int superSize = 1)
  235. {
  236. yield return new WaitForEndOfFrame();
  237. Texture2D texture = null;
  238. #if UNITY_2017_3_OR_NEWER
  239. if (format != ImageFormat.EXR)
  240. {
  241. texture = ScreenCapture.CaptureScreenshotAsTexture(superSize);
  242. }
  243. else
  244. {
  245. // For EXR we want floating point textures which CaptureScreenshotAsTexture() doesn't provide
  246. RenderTextureFormat rtFormat = (options.exrPrecision == ExrPrecision.Float) ? RenderTextureFormat.ARGBFloat : RenderTextureFormat.ARGBHalf;
  247. RenderTexture rt = new RenderTexture(Screen.width * superSize, Screen.height * superSize, 24, rtFormat);
  248. rt.Create();
  249. #if UNITY_2019_1_OR_NEWER
  250. ScreenCapture.CaptureScreenshotIntoRenderTexture(rt);
  251. #endif
  252. texture = GetReadableTexture(rt, true);
  253. Destroy(rt);
  254. }
  255. #endif
  256. if (texture != null)
  257. {
  258. TextureToFile(texture, filePath, format, options);
  259. if (Application.isPlaying)
  260. {
  261. Destroy(texture);
  262. Destroy(go);
  263. }
  264. else
  265. {
  266. DestroyImmediate(texture);
  267. DestroyImmediate(go);
  268. }
  269. }
  270. }
  271. internal static Camera[] FindAllCameras()
  272. {
  273. return Resources.FindObjectsOfTypeAll<Camera>();
  274. }
  275. static Camera FindCameraByName(int cameraCount, Camera[] cameras, string name)
  276. {
  277. Camera result = null;
  278. for (int i = 0; i < cameraCount; i++)
  279. {
  280. Camera c = cameras[i];
  281. if (c.name == name)
  282. {
  283. result = c;
  284. break;
  285. }
  286. }
  287. return result;
  288. }
  289. internal static string GetExtension(ImageFormat format)
  290. {
  291. switch (format)
  292. {
  293. case ImageFormat.PNG:
  294. return "png";
  295. case ImageFormat.JPG:
  296. return "jpg";
  297. case ImageFormat.TGA:
  298. return "tga";
  299. case ImageFormat.EXR:
  300. return "exr";
  301. }
  302. throw new Exception("Unknown image format");
  303. }
  304. internal static Vector2 GetGameViewSize()
  305. {
  306. Vector2 result = Vector2.zero;
  307. string[] res = UnityStats.screenRes.Split('x');
  308. if (res.Length == 2)
  309. {
  310. result.x = int.Parse(res[0]);
  311. result.y = int.Parse(res[1]);
  312. }
  313. return result;
  314. }
  315. internal static string GenerateFilename(string filenamePrefix, ImageFormat format, int width, int height)
  316. {
  317. string filenameExtension = GetExtension(format);
  318. string dateTime = DateTime.Now.ToString("yyyyMMdd-HHmmss");
  319. string filename = string.Format("{0}-{1}-{2}x{3}.{4}", filenamePrefix, dateTime, width, height, filenameExtension);
  320. return filename;
  321. }
  322. internal static string GenerateFilePath(string folderPath, string fileName)
  323. {
  324. if (!System.IO.Directory.Exists(folderPath))
  325. {
  326. System.IO.Directory.CreateDirectory(folderPath);
  327. }
  328. return System.IO.Path.Combine(folderPath, fileName);
  329. }
  330. }
  331. }