SW 중심대학 OSS GIT 서버 박건태, 이승준, 고기완, 이준호 새로운 배포
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

488 lines
21 KiB

4 years ago
  1. using AOT;
  2. using System;
  3. using System.Runtime.InteropServices;
  4. using Unity.Collections;
  5. using Unity.Collections.LowLevel.Unsafe;
  6. namespace UnityEngine.XR.ARSubsystems
  7. {
  8. /// <summary>
  9. /// Represents a single, raw image from a device camera. Provides access to the raw image plane data, as well as
  10. /// conversion methods to convert to color and grayscale formats. See <see cref="Convert"/> and
  11. /// <see cref="ConvertAsync(XRCameraImageConversionParams)"/>. Use
  12. /// <see cref="XRCameraSubsystem.TryGetLatestImage"/> to get a <c>XRCameraImage</c>.
  13. /// </summary>
  14. /// <remarks>
  15. /// Each <c>XRCameraImage</c> must be explicitly disposed. Failing to do so will leak resources and could prevent
  16. /// future camera image access.
  17. /// </remarks>
  18. public struct XRCameraImage : IDisposable, IEquatable<XRCameraImage>
  19. {
  20. int m_NativeHandle;
  21. XRCameraSubsystem m_CameraSubsystem;
  22. static XRCameraSubsystem.OnImageRequestCompleteDelegate s_OnAsyncConversionComplete;
  23. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  24. AtomicSafetyHandle m_SafetyHandle;
  25. #endif
  26. /// <summary>
  27. /// The dimensions (width and height) of the image.
  28. /// </summary>
  29. /// <value>
  30. /// The dimensions (width and height) of the image.
  31. /// </value>
  32. public Vector2Int dimensions { get; private set; }
  33. /// <summary>
  34. /// The image width.
  35. /// </summary>
  36. /// <value>
  37. /// The image width.
  38. /// </value>
  39. public int width { get { return dimensions.x; } }
  40. /// <summary>
  41. /// The image height.
  42. /// </summary>
  43. /// <value>
  44. /// The image height.
  45. /// </value>
  46. public int height { get { return dimensions.y; } }
  47. /// <summary>
  48. /// The number of image planes. A "plane" in this context refers to a channel in the raw video format,
  49. /// not a physical surface.
  50. /// </summary>
  51. /// <value>
  52. /// The number of image planes.
  53. /// </value>
  54. public int planeCount { get; private set; }
  55. /// <summary>
  56. /// The format used by the image planes. You will only need this if you plan to interpret the raw plane data.
  57. /// </summary>
  58. /// <value>
  59. /// The format used by the image planes.
  60. /// </value>
  61. public CameraImageFormat format { get; private set; }
  62. /// <summary>
  63. /// The timestamp, in seconds, associated with this camera image
  64. /// </summary>
  65. /// <value>
  66. /// The timestamp, in seconds, associated with this camera image
  67. /// </value>
  68. public double timestamp { get; private set; }
  69. /// <summary>
  70. /// Whether this <c>XRCameraImage</c> represents a valid image (i.e., not Disposed).
  71. /// </summary>
  72. /// <value>
  73. /// <c>true</c> if this <c>XRCameraImage</c> represents a valid image. Otherwise, <c>false</c>.
  74. /// </value>
  75. public bool valid
  76. {
  77. get { return (m_CameraSubsystem != null) && m_CameraSubsystem.NativeHandleValid(m_NativeHandle); }
  78. }
  79. /// <summary>
  80. /// Initialize the static callback for when the image request completes.
  81. /// </summary>
  82. static XRCameraImage()
  83. {
  84. s_OnAsyncConversionComplete = new XRCameraSubsystem.OnImageRequestCompleteDelegate(OnAsyncConversionComplete);
  85. }
  86. /// <summary>
  87. /// Construct the <c>XRCameraImage</c> with the given native image information.
  88. /// </summary>
  89. /// <param name="cameraSubsystem">The camera subsystem to use for interacting with the native image.</param>
  90. /// <param name="nativeHandle">The native image handle.</param>
  91. /// <param name="dimensions">The dimensions of the native image.</param>
  92. /// <param name="planeCount">The number of video planes in the native image.</param>
  93. /// <param name="timestamp">The timestamp for when the native image was captured.</param>
  94. /// <param name="format">The camera image format of the native image.</param>
  95. internal XRCameraImage(
  96. XRCameraSubsystem cameraSubsystem,
  97. int nativeHandle,
  98. Vector2Int dimensions,
  99. int planeCount,
  100. double timestamp,
  101. CameraImageFormat format)
  102. {
  103. m_CameraSubsystem = cameraSubsystem;
  104. m_NativeHandle = nativeHandle;
  105. this.dimensions = dimensions;
  106. this.planeCount = planeCount;
  107. this.timestamp = timestamp;
  108. this.format = format;
  109. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  110. m_SafetyHandle = AtomicSafetyHandle.Create();
  111. #endif
  112. }
  113. /// <summary>
  114. /// Determines whether the given <c>TextureFormat</c> is supported for conversion.
  115. /// </summary>
  116. /// <remarks>
  117. /// These texture formats are supported:
  118. /// <list type="bullet">
  119. /// <item><description><c>TextureFormat.R8</c></description></item>
  120. /// <item><description><c>TextureFormat.Alpha8</c></description></item>
  121. /// <item><description><c>TextureFormat.RGB24</c></description></item>
  122. /// <item><description><c>TextureFormat.RGBA32</c></description></item>
  123. /// <item><description><c>TextureFormat.ARGBA32</c></description></item>
  124. /// <item><description><c>TextureFormat.BGRA32</c></description></item>
  125. /// </list>
  126. /// </remarks>
  127. /// <param name="format">A <c>TextureFormat</c> to test.</param>
  128. /// <returns><c>true</c> if the format is supported by the various conversion methods.</returns>
  129. public static bool FormatSupported(TextureFormat format)
  130. {
  131. switch (format)
  132. {
  133. case TextureFormat.Alpha8:
  134. case TextureFormat.R8:
  135. case TextureFormat.RGB24:
  136. case TextureFormat.RGBA32:
  137. case TextureFormat.ARGB32:
  138. case TextureFormat.BGRA32:
  139. return true;
  140. default:
  141. return false;
  142. }
  143. }
  144. /// <summary>
  145. /// Get an image "plane". A "plane" in this context refers to a channel in the raw video format, not a physical
  146. /// surface.
  147. /// </summary>
  148. /// <param name="planeIndex">The index of the plane to get.</param>
  149. /// <returns>A <see cref="XRCameraImagePlane"/> describing the plane.</returns>
  150. /// <exception cref="System.ArgumentOutOfRangeException">Thrown if <paramref name="planeIndex"/> is not within
  151. /// the range [0, <see cref="planeCount"/>).</exception>
  152. /// <exception cref="System.InvalidOperationException">Thrown if the requested plane is not valid.</exception>
  153. public unsafe XRCameraImagePlane GetPlane(int planeIndex)
  154. {
  155. ValidateNativeHandleAndThrow();
  156. if (planeIndex < 0 || planeIndex >= planeCount)
  157. {
  158. throw new ArgumentOutOfRangeException("planeIndex",
  159. string.Format("planeIndex must be in the range 0 to {0}", planeCount - 1));
  160. }
  161. XRCameraSubsystem.CameraImagePlaneCinfo imagePlaneCinfo;
  162. if (!m_CameraSubsystem.TryGetPlane(m_NativeHandle, planeIndex, out imagePlaneCinfo))
  163. {
  164. throw new InvalidOperationException("The requested plane is not valid for this XRCameraImage.");
  165. }
  166. var data = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(
  167. (void*)imagePlaneCinfo.dataPtr, imagePlaneCinfo.dataLength, Allocator.None);
  168. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  169. NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref data, m_SafetyHandle);
  170. #endif
  171. return new XRCameraImagePlane
  172. {
  173. rowStride = imagePlaneCinfo.rowStride,
  174. pixelStride = imagePlaneCinfo.pixelStride,
  175. data = data
  176. };
  177. }
  178. /// <summary>
  179. /// Get the number of bytes required to store a converted image with the given parameters.
  180. /// </summary>
  181. /// <param name="dimensions">The desired dimensions of the converted image.</param>
  182. /// <param name="format">The desired <c>TextureFormat</c> for the converted image.</param>
  183. /// <returns>The number of bytes required to store the converted image.</returns>
  184. /// <exception cref="System.ArgumentException">Thrown if the desired <paramref name="format"/> is not
  185. /// supported.</exception>
  186. /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the desired <paramref name="dimensions"/>
  187. /// exceed the native image dimensions.</exception>
  188. /// <exception cref="System.InvalidOperationException">Thrown if the image is invalid.</exception>
  189. /// <seealso cref="FormatSupported"/>
  190. public int GetConvertedDataSize(Vector2Int dimensions, TextureFormat format)
  191. {
  192. ValidateNativeHandleAndThrow();
  193. if (dimensions.x > this.dimensions.x)
  194. {
  195. throw new ArgumentOutOfRangeException("width",
  196. string.Format("Converted image width must be less than or equal to native image width. {0} > {1}",
  197. dimensions.x, this.dimensions.x));
  198. }
  199. if (dimensions.y > this.dimensions.y)
  200. {
  201. throw new ArgumentOutOfRangeException("height",
  202. string.Format("Converted image height must be less than or equal to native image height. {0} > {1}",
  203. dimensions.y, this.dimensions.y));
  204. }
  205. if (!FormatSupported(format))
  206. {
  207. throw new ArgumentException("Invalid texture format.", "format");
  208. }
  209. int size;
  210. if (!m_CameraSubsystem.TryGetConvertedDataSize(m_NativeHandle, dimensions, format, out size))
  211. {
  212. throw new InvalidOperationException("XRCameraImage is not valid.");
  213. }
  214. return size;
  215. }
  216. /// <summary>
  217. /// Get the number of bytes required to store a converted image with the given parameters.
  218. /// </summary>
  219. /// <param name="conversionParams">The desired conversion parameters.</param>
  220. /// <returns>The number of bytes required to store the converted image.</returns>
  221. /// <exception cref="System.ArgumentException">Thrown if the desired format is not supported.</exception>
  222. /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the desired dimensions exceed the native
  223. /// image dimensions.</exception>
  224. /// <exception cref="System.InvalidOperationException">Thrown if the image is invalid.</exception>
  225. /// <seealso cref="FormatSupported"/>
  226. public int GetConvertedDataSize(XRCameraImageConversionParams conversionParams)
  227. {
  228. return GetConvertedDataSize(
  229. conversionParams.outputDimensions,
  230. conversionParams.outputFormat);
  231. }
  232. /// <summary>
  233. /// Convert the <c>XRCameraImage</c> to one of the supported formats using the specified
  234. /// <paramref name="conversionParams"/>.
  235. /// </summary>
  236. /// <param name="conversionParams">The parameters for the image conversion.</param>
  237. /// <param name="destinationBuffer">A pointer to memory to which to write the converted image.</param>
  238. /// <param name="bufferLength">The number of bytes pointed to by <paramref name="destinationBuffer"/>. Must be
  239. /// greater than or equal to the value returned by
  240. /// <see cref="GetConvertedDataSize(XRCameraImageConversionParams)"/>.</param>
  241. /// <exception cref="System.ArgumentException">Thrown if the <paramref name="bufferLength"/> is smaller than
  242. /// the data size required by the conversion.</exception>
  243. /// <exception cref="System.InvalidOperationException">Thrown if the conversion failed.</exception>
  244. /// <seealso cref="FormatSupported"/>
  245. public void Convert(XRCameraImageConversionParams conversionParams, IntPtr destinationBuffer, int bufferLength)
  246. {
  247. ValidateNativeHandleAndThrow();
  248. ValidateConversionParamsAndThrow(conversionParams);
  249. int requiredDataSize = GetConvertedDataSize(conversionParams);
  250. if (bufferLength < requiredDataSize)
  251. {
  252. throw new ArgumentException(string.Format(
  253. "Conversion requires {0} bytes but only provided {1} bytes.", requiredDataSize, bufferLength),
  254. "bufferLength");
  255. }
  256. if (!m_CameraSubsystem.TryConvert(m_NativeHandle, conversionParams, destinationBuffer, bufferLength))
  257. {
  258. throw new InvalidOperationException("Conversion failed.");
  259. }
  260. }
  261. /// <summary>
  262. /// Convert the <c>XRCameraImage</c> to one of the supported formats using the specified
  263. /// <paramref name="conversionParams"/>. The conversion is performed asynchronously. Use the returned
  264. /// <see cref="XRAsyncCameraImageConversion"/> to check for the status of the conversion, and retrieve the data
  265. /// when complete.
  266. /// </summary>
  267. /// <remarks>
  268. /// It is safe to <c>Dispose</c> the <c>XRCameraImage</c> before the asynchronous operation has completed.
  269. /// </remarks>
  270. /// <param name="conversionParams">The parameters to use for the conversion.</param>
  271. /// <returns>A <see cref="XRAsyncCameraImageConversion"/> which can be used to check the status of the
  272. /// conversion operation and get the resulting data.</returns>
  273. /// <seealso cref="FormatSupported"/>
  274. public XRAsyncCameraImageConversion ConvertAsync(XRCameraImageConversionParams conversionParams)
  275. {
  276. ValidateNativeHandleAndThrow();
  277. ValidateConversionParamsAndThrow(conversionParams);
  278. return new XRAsyncCameraImageConversion(m_CameraSubsystem, m_NativeHandle, conversionParams);
  279. }
  280. /// <summary>
  281. /// <para>Convert the <c>XRCameraImage</c> to one of the supported formats using the specified
  282. /// <paramref name="conversionParams"/>. The conversion is performed asynchronously, and
  283. /// <paramref name="onComplete"/> is invoked when the conversion is complete, whether successful or not.</para>
  284. /// <para>The <c>NativeArray</c> provided in the <paramref name="onComplete"/> delegate is only valid during
  285. /// the invocation and is disposed immediately upon return.</para>
  286. /// </summary>
  287. /// <param name="conversionParams">The parameters to use for the conversion.</param>
  288. /// <param name="onComplete">A delegate to invoke when the conversion operation completes. The delegate is
  289. /// always invoked.</param>
  290. /// <seealso cref="FormatSupported"/>
  291. public void ConvertAsync(
  292. XRCameraImageConversionParams conversionParams,
  293. Action<AsyncCameraImageConversionStatus, XRCameraImageConversionParams, NativeArray<byte>> onComplete)
  294. {
  295. ValidateNativeHandleAndThrow();
  296. ValidateConversionParamsAndThrow(conversionParams);
  297. var handle = GCHandle.Alloc(onComplete);
  298. var context = GCHandle.ToIntPtr(handle);
  299. m_CameraSubsystem.ConvertAsync(m_NativeHandle, conversionParams, s_OnAsyncConversionComplete, context);
  300. }
  301. /// <summary>
  302. /// Callback from native code for when the asychronous conversion is complete.
  303. /// </summary>
  304. /// <param name="status">The status of the conversion operation.</param>
  305. /// <param name="conversionParams">The parameters for the conversion.</param>
  306. /// <param name="dataPtr">The native pointer to the converted data.</param>
  307. /// <param name="dataLength">The memory size of the converted data.</param>
  308. /// <param name="context">The native context for the conversion operation.</param>
  309. [MonoPInvokeCallback(typeof(XRCameraSubsystem.OnImageRequestCompleteDelegate))]
  310. static unsafe void OnAsyncConversionComplete(
  311. AsyncCameraImageConversionStatus status, XRCameraImageConversionParams conversionParams, IntPtr dataPtr,
  312. int dataLength, IntPtr context)
  313. {
  314. var handle = GCHandle.FromIntPtr(context);
  315. var onComplete = (Action<AsyncCameraImageConversionStatus, XRCameraImageConversionParams, NativeArray<byte>>)handle.Target;
  316. if (onComplete != null)
  317. {
  318. var data = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(
  319. (void*)dataPtr, dataLength, Allocator.None);
  320. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  321. var safetyHandle = AtomicSafetyHandle.Create();
  322. NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref data, safetyHandle);
  323. #endif
  324. onComplete(status, conversionParams, data);
  325. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  326. AtomicSafetyHandle.Release(safetyHandle);
  327. #endif
  328. }
  329. handle.Free();
  330. }
  331. /// <summary>
  332. /// Ensure the image is valid.
  333. /// </summary>
  334. /// <exception cref="System.InvalidOperationException">Thrown if the image is invalid.</exception>
  335. void ValidateNativeHandleAndThrow()
  336. {
  337. if (!valid)
  338. {
  339. throw new InvalidOperationException("XRCameraImage is not valid.");
  340. }
  341. }
  342. /// <summary>
  343. /// Ensure the conversion parameters are valid.
  344. /// </summary>
  345. /// <param name="conversionParams">The conversion parameters to validate.</param>
  346. /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the input image rect exceeds the actual
  347. /// image dimensions or if the output dimensions exceed the input dimensions.</exception>
  348. /// <exception cref="System.ArgumentException">Thrown if the texture format is not suppported</exception>
  349. /// <seealso cref="FormatSupported"/>
  350. void ValidateConversionParamsAndThrow(XRCameraImageConversionParams conversionParams)
  351. {
  352. if ((conversionParams.inputRect.x + conversionParams.inputRect.width > width) ||
  353. (conversionParams.inputRect.y + conversionParams.inputRect.height > height))
  354. {
  355. throw new ArgumentOutOfRangeException(
  356. "conversionParams.inputRect",
  357. "Input rect must be completely within the original image.");
  358. }
  359. if ((conversionParams.outputDimensions.x > conversionParams.inputRect.width) ||
  360. (conversionParams.outputDimensions.y > conversionParams.inputRect.height))
  361. {
  362. throw new ArgumentOutOfRangeException(string.Format(
  363. "Output dimensions must be less than or equal to the inputRect's dimensions: ({0}x{1} > {2}x{3}).",
  364. conversionParams.outputDimensions.x, conversionParams.outputDimensions.y,
  365. conversionParams.inputRect.width, conversionParams.inputRect.height));
  366. }
  367. if (!FormatSupported(conversionParams.outputFormat))
  368. {
  369. throw new ArgumentException("TextureFormat not supported.", "conversionParams.format");
  370. }
  371. }
  372. /// <summary>
  373. /// Dispose native resources associated with this request, including the raw image data. Any
  374. /// <see cref="XRCameraImagePlane"/>s returned by <see cref="GetPlane"/> are invalidated immediately after
  375. /// calling <c>Dispose</c>.
  376. /// </summary>
  377. public void Dispose()
  378. {
  379. if (m_CameraSubsystem == null || m_NativeHandle == 0)
  380. {
  381. return;
  382. }
  383. m_CameraSubsystem.DisposeImage(m_NativeHandle);
  384. m_NativeHandle = 0;
  385. m_CameraSubsystem = null;
  386. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  387. AtomicSafetyHandle.Release(m_SafetyHandle);
  388. #endif
  389. }
  390. public override int GetHashCode()
  391. {
  392. unchecked
  393. {
  394. var hash = width.GetHashCode();
  395. hash = hash * 486187739 + height.GetHashCode();
  396. hash = hash * 486187739 + planeCount.GetHashCode();
  397. hash = hash * 486187739 + m_NativeHandle.GetHashCode();
  398. hash = hash * 486187739 + ((int)format).GetHashCode();
  399. if (m_CameraSubsystem != null)
  400. {
  401. hash = hash * 486187739 + m_CameraSubsystem.GetHashCode();
  402. }
  403. return hash;
  404. }
  405. }
  406. public override bool Equals(object obj)
  407. {
  408. return ((obj is XRCameraImage) && Equals((XRCameraImage)obj));
  409. }
  410. public bool Equals(XRCameraImage other)
  411. {
  412. return
  413. (width == other.width) &&
  414. (height == other.height) &&
  415. (planeCount == other.planeCount) &&
  416. (format == other.format) &&
  417. (m_NativeHandle == other.m_NativeHandle) &&
  418. (m_CameraSubsystem == other.m_CameraSubsystem);
  419. }
  420. public static bool operator ==(XRCameraImage lhs, XRCameraImage rhs)
  421. {
  422. return lhs.Equals(rhs);
  423. }
  424. public static bool operator !=(XRCameraImage lhs, XRCameraImage rhs)
  425. {
  426. return !lhs.Equals(rhs);
  427. }
  428. public override string ToString()
  429. {
  430. return string.Format(
  431. "(Width: {0}, Height: {1}, PlaneCount: {2}, Format: {3})",
  432. width, height, planeCount, format);
  433. }
  434. }
  435. }