Skip to content

Commit

Permalink
Streamline image loading by using NSData for interop of the image byt…
Browse files Browse the repository at this point in the history
…es. (#18)

* Streamline image loading by using NSData for interop of the image bytes.
* Add trailing newlines.
* Fix some spacing issues called out in code review.
* Handle nil in Texture2D.CreateFromNSDataPtr()
* More spacing fixes.
  • Loading branch information
Adam Szofran authored and GitHub Enterprise committed Jan 16, 2024
1 parent d58d55b commit 469b3a5
Show file tree
Hide file tree
Showing 27 changed files with 302 additions and 120 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Runtime.InteropServices;

namespace Apple.Core.Runtime
{
/// <summary>
/// C# wrapper around NSData.
/// </summary>
public class NSData : NSObject
{
/// <summary>
/// Construct an NSData wrapper around an existing instance.
/// </summary>
/// <param name="pointer"></param>
public NSData(IntPtr pointer) : base(pointer)
{
}

/// <summary>
/// The number of bytes contained by the data object.
/// </summary>
public UInt64 Length => Interop.NSData_GetLength(Pointer, NSException.ThrowOnExceptionCallback);

/// <summary>
/// Return the object's contents as a byte array.
/// </summary>
public byte[] Bytes
{
get
{
byte[] bytes = null;

var length = (int)Length;
var bytePtr = Interop.NSData_GetBytes(Pointer, NSException.ThrowOnExceptionCallback);
if (length >= 0 && bytePtr != IntPtr.Zero)
{
bytes = new byte[length];
Marshal.Copy(bytePtr, bytes, 0, length);
}

return bytes;
}
}

private static class Interop
{
[DllImport(InteropUtility.DLLName)] public static extern UInt64 NSData_GetLength(IntPtr nsDataPtr, NSExceptionCallback onException);
[DllImport(InteropUtility.DLLName)] public static extern IntPtr NSData_GetBytes(IntPtr nsDataPtr, NSExceptionCallback onException);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class NSString : NSObject
/// Construct an NSString around a C# string.
/// </summary>
/// <param name="s"></param>
public NSString(string s) : this(Interop.NSString_StringWithUTF8String(s)) { }
public NSString(string s) : this(Interop.NSString_StringWithUtf8String(s)) { }

private static readonly NSString _empty = new NSString();
public static NSString Empty => _empty;
Expand All @@ -43,11 +43,25 @@ public class NSString : NSObject
/// <param name="s"></param>
public static implicit operator NSString(string s) => new NSString(s);

/// <summary>
/// Construct an NSString from UTF8 formatted NSData.
/// </summary>
/// <param name="data"></param>
public NSString(NSData data) : this(Interop.NSString_StringWithUtf8Data(data.Pointer)) { }

/// <summary>
/// Convert a C# string to UTF8 formatted NSData.
/// </summary>
public NSData Utf8Data => PointerCast<NSData>(Interop.NSString_Utf8Data(Pointer));
public static explicit operator NSData(NSString s) => s.Utf8Data;

private static class Interop
{
[DllImport(InteropUtility.DLLName)] public static extern IntPtr NSString_string();
[DllImport(InteropUtility.DLLName)] public static extern IntPtr NSString_StringWithUTF8String(string s);
[DllImport(InteropUtility.DLLName)] public static extern IntPtr NSString_StringWithUtf8String(string s);
[DllImport(InteropUtility.DLLName)] public static extern string NSString_Utf8String(IntPtr nsStringPtr);
[DllImport(InteropUtility.DLLName)] public static extern IntPtr NSString_StringWithUtf8Data(IntPtr nsDataPtr);
[DllImport(InteropUtility.DLLName)] public static extern IntPtr NSString_Utf8Data(IntPtr nsStringPtr);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using UnityEngine;

namespace Apple.Core.Runtime
{
public static class Texture2DExtensions
{
/// <summary>
/// Create a Texture2D object from the contents of an NSData object.
/// </summary>
/// <param name="nsData"></param>
/// <returns>The created texture if successful; null otherwise.</returns>
public static Texture2D CreateFromNSData(NSData nsData)
{
Texture2D texture = null;

if (nsData?.Length > 0)
{
texture = new Texture2D(1, 1);
texture.LoadImage(nsData.Bytes);
}

return texture;
}

/// <summary>
/// Create a Texture2D object from the contents of an NSData object referenced via IntPtr.
/// </summary>
/// <param name="nsDataPtr"></param>
/// <returns>The created texture if successful; null otherwise.</returns>
public static Texture2D CreateFromNSDataPtr(IntPtr nsDataPtr)
{
return (nsDataPtr != IntPtr.Zero) ? CreateFromNSData(InteropReference.PointerCast<NSData>(nsDataPtr)) : null;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using NUnit.Framework;

using Apple.Core.Runtime;
using System;

public class TestNSData
{
[Test]
public void TestBasicOperations()
{
NSData emptyData = NSString.Empty.Utf8Data;
Assert.AreEqual(emptyData.Length, 0);

NSString emptyString = new NSString(emptyData);
Assert.AreEqual(string.Empty, emptyString.ToString());

NSData testData = new NSString("test").Utf8Data;
Assert.AreEqual(testData.Length, 4);

NSString testString = new NSString(testData);
Assert.AreEqual("test", testString.ToString());
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<linker>
<assembly fullname="Apple.Core" preserve="all"/>
<assembly fullname="Apple.Core.Runtime" preserve="all"/>
<assembly fullname="System.Collections" preserve="all"/>
<assembly fullname="System.Collections.Generic" preserve="all"/>
<assembly fullname="System.Reflection" preserve="all"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"com.unity.2d.sprite": "1.0.0",
"com.unity.2d.tilemap": "1.0.0",
"com.unity.ai.navigation": "1.1.5",
"com.unity.ide.rider": "3.0.26",
"com.unity.ide.rider": "3.0.27",
"com.unity.ide.visualstudio": "2.0.22",
"com.unity.ide.vscode": "1.2.5",
"com.unity.test-framework": "1.1.33",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"url": "https://packages.unity.com"
},
"com.unity.ide.rider": {
"version": "3.0.26",
"version": "3.0.27",
"depth": 0,
"source": "registry",
"dependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
m_EditorVersion: 2022.3.14f1
m_EditorVersionWithRevision: 2022.3.14f1 (eff2de9070d8)
m_EditorVersion: 2022.3.16f1
m_EditorVersionWithRevision: 2022.3.16f1 (d2c21f0ef2f1)
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@
FB8694412AD47FFA009B8676 /* NSNull.m in Sources */ = {isa = PBXBuildFile; fileRef = FB8694402AD47FFA009B8676 /* NSNull.m */; };
FB8694422AD47FFA009B8676 /* NSNull.m in Sources */ = {isa = PBXBuildFile; fileRef = FB8694402AD47FFA009B8676 /* NSNull.m */; };
FB8694432AD47FFA009B8676 /* NSNull.m in Sources */ = {isa = PBXBuildFile; fileRef = FB8694402AD47FFA009B8676 /* NSNull.m */; };
FB8FF4922B338A1A0058B918 /* NSData.m in Sources */ = {isa = PBXBuildFile; fileRef = FB8FF4912B338A110058B918 /* NSData.m */; };
FB8FF4932B338A1A0058B918 /* NSData.m in Sources */ = {isa = PBXBuildFile; fileRef = FB8FF4912B338A110058B918 /* NSData.m */; };
FB8FF4942B338A1A0058B918 /* NSData.m in Sources */ = {isa = PBXBuildFile; fileRef = FB8FF4912B338A110058B918 /* NSData.m */; };
FBB2BEC02AC77D160040E5E0 /* NSNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB2BEBF2AC77D160040E5E0 /* NSNumber.m */; };
FBB2BEC12AC77D160040E5E0 /* NSNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB2BEBF2AC77D160040E5E0 /* NSNumber.m */; };
FBB2BEC22AC77D160040E5E0 /* NSNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = FBB2BEBF2AC77D160040E5E0 /* NSNumber.m */; };
Expand Down Expand Up @@ -148,6 +151,7 @@
FB6C73C22AFDA7800077714C /* NSException.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSException.m; sourceTree = "<group>"; };
FB86943C2ACF32FB009B8676 /* NSObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSObject.m; sourceTree = "<group>"; };
FB8694402AD47FFA009B8676 /* NSNull.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSNull.m; sourceTree = "<group>"; };
FB8FF4912B338A110058B918 /* NSData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSData.m; sourceTree = "<group>"; };
FBB2BEBF2AC77D160040E5E0 /* NSNumber.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSNumber.m; sourceTree = "<group>"; };
FBB2BEC32ACB2FDE0040E5E0 /* NSString.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSString.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -198,22 +202,23 @@
CFE5338E263B6CCC00078448 /* AppleCoreNative */ = {
isa = PBXGroup;
children = (
245B212627DEA521007479CE /* NSError.swift */,
245B212227DEA517007479CE /* AppleCoreRuntimeShared.swift */,
24B8F1882A73397C00914A67 /* ACRuntimeEnvironment.swift */,
244F33522A6F0A2B005099AE /* AppleCore_BridgingHeader.h */,
CFE5338F263B6CCC00078448 /* AppleCoreNative.h */,
245B212227DEA517007479CE /* AppleCoreRuntimeShared.swift */,
CFE53398263B6D1400078448 /* CoreUtilities.swift */,
CFE53442263B7A2A00078448 /* Info.plist */,
244F33522A6F0A2B005099AE /* AppleCore_BridgingHeader.h */,
24B8F1882A73397C00914A67 /* ACRuntimeEnvironment.swift */,
FBB2BEBF2AC77D160040E5E0 /* NSNumber.m */,
FBB2BEC32ACB2FDE0040E5E0 /* NSString.m */,
FB86943C2ACF32FB009B8676 /* NSObject.m */,
FB8694402AD47FFA009B8676 /* NSNull.m */,
FB32D8782AF96000006A8FE4 /* NSArray.m */,
FB32D87C2AF97A09006A8FE4 /* NSMutableArray.m */,
FB8FF4912B338A110058B918 /* NSData.m */,
FB32D8802AFAADAA006A8FE4 /* NSDictionary.m */,
FB32D8842AFADF1A006A8FE4 /* NSMutableDictionary.m */,
245B212627DEA521007479CE /* NSError.swift */,
FB6C73C22AFDA7800077714C /* NSException.m */,
FB32D87C2AF97A09006A8FE4 /* NSMutableArray.m */,
FB32D8842AFADF1A006A8FE4 /* NSMutableDictionary.m */,
FB8694402AD47FFA009B8676 /* NSNull.m */,
FBB2BEBF2AC77D160040E5E0 /* NSNumber.m */,
FB86943C2ACF32FB009B8676 /* NSObject.m */,
FBB2BEC32ACB2FDE0040E5E0 /* NSString.m */,
);
path = AppleCoreNative;
sourceTree = "<group>";
Expand Down Expand Up @@ -387,6 +392,7 @@
FBB2BEC42ACB2FDE0040E5E0 /* NSString.m in Sources */,
FB32D87D2AF97A09006A8FE4 /* NSMutableArray.m in Sources */,
245B212327DEA517007479CE /* AppleCoreRuntimeShared.swift in Sources */,
FB8FF4922B338A1A0058B918 /* NSData.m in Sources */,
FB8694412AD47FFA009B8676 /* NSNull.m in Sources */,
FB32D8812AFAADAA006A8FE4 /* NSDictionary.m in Sources */,
FB6C73C32AFDA7800077714C /* NSException.m in Sources */,
Expand All @@ -407,6 +413,7 @@
FBB2BEC52ACB2FDE0040E5E0 /* NSString.m in Sources */,
FB32D87E2AF97A09006A8FE4 /* NSMutableArray.m in Sources */,
245B212427DEA517007479CE /* AppleCoreRuntimeShared.swift in Sources */,
FB8FF4932B338A1A0058B918 /* NSData.m in Sources */,
FB8694422AD47FFA009B8676 /* NSNull.m in Sources */,
FB32D8822AFAADAA006A8FE4 /* NSDictionary.m in Sources */,
FB6C73C42AFDA7800077714C /* NSException.m in Sources */,
Expand All @@ -427,6 +434,7 @@
CFE5340C263B700A00078448 /* CoreUtilities.swift in Sources */,
FB32D87F2AF97A09006A8FE4 /* NSMutableArray.m in Sources */,
245B212527DEA517007479CE /* AppleCoreRuntimeShared.swift in Sources */,
FB8FF4942B338A1A0058B918 /* NSData.m in Sources */,
FB8694432AD47FFA009B8676 /* NSNull.m in Sources */,
FB32D8832AFAADAA006A8FE4 /* NSDictionary.m in Sources */,
FB6C73C52AFDA7800077714C /* NSException.m in Sources */,
Expand Down
32 changes: 32 additions & 0 deletions plug-ins/Apple.Core/Native/AppleCoreNative/NSData.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// NSData.m
// AppleCoreNative
//

#import <Foundation/Foundation.h>

NSUInteger NSData_GetLength(void * nsDataPtr, void (* exceptionCallback)(void * nsExceptionPtr)) {
@try {
if (nsDataPtr != NULL) {
NSData * nsData = (__bridge NSData *)nsDataPtr;
return nsData.length;
}
}
@catch (NSException * e) {
exceptionCallback((void *)CFBridgingRetain(e));
}
return 0;
}

const void * NSData_GetBytes(void * nsDataPtr, void (* exceptionCallback)(void * nsExceptionPtr)) {
@try {
if (nsDataPtr != NULL) {
NSData * nsData = (__bridge NSData *)nsDataPtr;
return nsData.bytes;
}
}
@catch (NSException * e) {
exceptionCallback((void *)CFBridgingRetain(e));
}
return 0;
}
11 changes: 10 additions & 1 deletion plug-ins/Apple.Core/Native/AppleCoreNative/NSString.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#import <Foundation/Foundation.h>

void * NSString_StringWithUTF8String(const char * nullTerminatedCString) {
void * NSString_StringWithUtf8String(const char * nullTerminatedCString) {
return (void *)CFBridgingRetain([NSString stringWithUTF8String:nullTerminatedCString]);
}

Expand All @@ -18,3 +18,12 @@
void * NSString_string(void) {
return (void *)CFBridgingRetain([NSString string]);
}


void * NSString_StringWithUtf8Data(void * nsDataPtr) {
return (void *)CFBridgingRetain([[NSString alloc] initWithData:(__bridge NSData *)nsDataPtr encoding:NSUTF8StringEncoding]);
}

void * NSString_Utf8Data(void * nsStringPtr) {
return (void *)CFBridgingRetain([(__bridge NSString *)nsStringPtr dataUsingEncoding:NSUTF8StringEncoding]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,25 +98,21 @@ private static void OnLoadAchievementDescriptionsError(long taskId, IntPtr error
public Task<Texture2D> LoadImage()
{
var tcs = InteropTasks.Create<Texture2D>(out var taskId);
Interop.GKAchievementDescription_LoadImage(Pointer, OnLoadImage, OnLoadImageError);
Interop.GKAchievementDescription_LoadImage(Pointer, taskId, OnLoadImage, OnLoadImageError);
return tcs.Task;
}

[MonoPInvokeCallback(typeof(SuccessTaskImageCallback))]
private static void OnLoadImage(long taskId, int width, int height, IntPtr data, int dataLength)
private static void OnLoadImage(long taskId, IntPtr nsDataPtr)
{
Texture2D texture = null;

if (dataLength > 0)
try
{
var image = new byte[dataLength];
Marshal.Copy(data, image, 0, dataLength);

texture = new Texture2D(width, height);
texture.LoadImage(image);
InteropTasks.TrySetResultAndRemove(taskId, Texture2DExtensions.CreateFromNSDataPtr(nsDataPtr));
}
catch (Exception ex)
{
InteropTasks.TrySetExceptionAndRemove<Texture2D>(taskId, ex);
}

InteropTasks.TrySetResultAndRemove(taskId, texture);
}

[MonoPInvokeCallback(typeof(NSErrorTaskCallback))]
Expand Down Expand Up @@ -149,7 +145,7 @@ private static class Interop
[DllImport(InteropUtility.DLLName)]
public static extern void GKAchievementDescription_LoadAchievementDescriptions(long taskId, SuccessTaskCallback<IntPtr> onSuccess, NSErrorTaskCallback onError);
[DllImport(InteropUtility.DLLName)]
public static extern void GKAchievementDescription_LoadImage(IntPtr pointer, SuccessTaskImageCallback onSuccess, NSErrorTaskCallback onError);
public static extern void GKAchievementDescription_LoadImage(IntPtr pointer, long taskId, SuccessTaskImageCallback onSuccess, NSErrorTaskCallback onError);
}
}
}

0 comments on commit 469b3a5

Please sign in to comment.