diff --git a/spine-flutter/example/lib/main.dart b/spine-flutter/example/lib/main.dart index efc83c841..261cc5e03 100644 --- a/spine-flutter/example/lib/main.dart +++ b/spine-flutter/example/lib/main.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; -import 'dart:async'; - import 'package:spine_flutter/spine_flutter.dart' as spine_flutter; +import 'package:flutter/services.dart' show rootBundle; void main() { runApp(const MyApp()); @@ -21,8 +20,9 @@ class _MyAppState extends State { @override void initState() { super.initState(); - majorVersion = spine_flutter.spine_major_version(); - minorVersion = spine_flutter.spine_minor_version(); + majorVersion = spine_flutter.majorVersion(); + minorVersion = spine_flutter.minorVersion(); + spine_flutter.loadAtlas(rootBundle, "assets/spineboy.atlas"); } @override @@ -31,20 +31,12 @@ class _MyAppState extends State { const spacerSmall = SizedBox(height: 10); return MaterialApp( home: Scaffold( - appBar: AppBar( - title: const Text('Native Packages'), - ), body: SingleChildScrollView( child: Container( padding: const EdgeInsets.all(10), child: Column( children: [ - const Text( - 'This calls a native function through FFI that is shipped as source in the package. ' - 'The native code is built as part of the Flutter Runner build.', - style: textStyle, - textAlign: TextAlign.center, - ), + const Image(image: AssetImage("assets/spineboy.png")), spacerSmall, Text( 'Spine version: $majorVersion.$minorVersion', diff --git a/spine-flutter/example/pubspec.lock b/spine-flutter/example/pubspec.lock index c2355bc37..7d084421d 100644 --- a/spine-flutter/example/pubspec.lock +++ b/spine-flutter/example/pubspec.lock @@ -57,6 +57,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" flutter: dependency: "direct main" description: flutter diff --git a/spine-flutter/lib/spine_flutter.dart b/spine-flutter/lib/spine_flutter.dart index e9fda816c..5755afa04 100644 --- a/spine-flutter/lib/spine_flutter.dart +++ b/spine-flutter/lib/spine_flutter.dart @@ -1,17 +1,41 @@ -import 'dart:async'; import 'dart:ffi'; import 'dart:io'; -import 'dart:isolate'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'spine_flutter_bindings_generated.dart'; +import 'package:path/path.dart' as Path; -int spine_major_version() => _bindings.spine_major_version(); -int spine_minor_version() => _bindings.spine_minor_version(); +int majorVersion() => _bindings.spine_major_version(); +int minorVersion() => _bindings.spine_minor_version(); + +void loadAtlas(AssetBundle assetBundle, String atlasFileName) async { + final atlasData = await assetBundle.loadString(atlasFileName); + final atlasDataNative = atlasData.toNativeUtf8(); + final atlas = _bindings.spine_atlas_load(atlasDataNative.cast()); + calloc.free(atlasDataNative); + if (atlas.ref.error.address != nullptr.address) { + final Pointer error = atlas.ref.error.cast(); + final message = error.toDartString(); + _bindings.spine_atlas_dispose(atlas); + throw Exception("Couldn't load atlas: " + message); + } + + final atlasDir = Path.dirname(atlasFileName); + List atlasPages = []; + for (int i = 0; i < atlas.ref.numImagePaths; i++) { + final Pointer atlasPageFile = atlas.ref.imagePaths[i].cast(); + final imagePath = Path.join(atlasDir, atlasPageFile.toDartString()); + atlasPages.add(await Image(image: AssetImage(imagePath))); + } + + _bindings.spine_atlas_dispose(atlas); +} const String _libName = 'spine_flutter'; -/// The dynamic library in which the symbols for [SpineFlutterBindings] can be found. final DynamicLibrary _dylib = () { if (Platform.isMacOS || Platform.isIOS) { return DynamicLibrary.open('$_libName.framework/$_libName'); @@ -25,5 +49,4 @@ final DynamicLibrary _dylib = () { throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}'); }(); -/// The bindings to the native functions in [_dylib]. final SpineFlutterBindings _bindings = SpineFlutterBindings(_dylib); \ No newline at end of file diff --git a/spine-flutter/lib/spine_flutter_bindings_generated.dart b/spine-flutter/lib/spine_flutter_bindings_generated.dart index 3772cf687..1440521e6 100644 --- a/spine-flutter/lib/spine_flutter_bindings_generated.dart +++ b/spine-flutter/lib/spine_flutter_bindings_generated.dart @@ -43,4 +43,44 @@ class SpineFlutterBindings { _lookup>('spine_minor_version'); late final _spine_minor_version = _spine_minor_versionPtr.asFunction(); + + ffi.Pointer spine_atlas_load( + ffi.Pointer atlasData, + ) { + return _spine_atlas_load( + atlasData, + ); + } + + late final _spine_atlas_loadPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('spine_atlas_load'); + late final _spine_atlas_load = _spine_atlas_loadPtr + .asFunction Function(ffi.Pointer)>(); + + void spine_atlas_dispose( + ffi.Pointer atlas, + ) { + return _spine_atlas_dispose( + atlas, + ); + } + + late final _spine_atlas_disposePtr = + _lookup)>>( + 'spine_atlas_dispose'); + late final _spine_atlas_dispose = _spine_atlas_disposePtr + .asFunction)>(); +} + +class spine_atlas extends ffi.Struct { + external ffi.Pointer atlas; + + external ffi.Pointer> imagePaths; + + @ffi.Int32() + external int numImagePaths; + + external ffi.Pointer error; } diff --git a/spine-flutter/pubspec.yaml b/spine-flutter/pubspec.yaml index af9ac32e5..47cbe7ec2 100644 --- a/spine-flutter/pubspec.yaml +++ b/spine-flutter/pubspec.yaml @@ -11,13 +11,14 @@ dependencies: flutter: sdk: flutter plugin_platform_interface: ^2.0.2 + ffi: ^1.1.2 dev_dependencies: - ffi: ^1.1.2 ffigen: ^4.1.2 flutter_test: sdk: flutter flutter_lints: ^2.0.0 + ffi: ^1.1.2 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/spine-flutter/src/spine_flutter.cpp b/spine-flutter/src/spine_flutter.cpp index eefc12e12..82c462bff 100644 --- a/spine-flutter/src/spine_flutter.cpp +++ b/spine-flutter/src/spine_flutter.cpp @@ -2,14 +2,39 @@ #include #include -int32_t spine_major_version() { +using namespace spine; + +FFI_PLUGIN_EXPORT int32_t spine_major_version() { return SPINE_MAJOR_VERSION; } -int32_t spine_minor_version() { +FFI_PLUGIN_EXPORT int32_t spine_minor_version() { return SPINE_MINOR_VERSION; } +FFI_PLUGIN_EXPORT spine_atlas* spine_atlas_load(const char *atlasData) { + int length = strlen(atlasData); + auto atlas = new Atlas(atlasData, length, "", (TextureLoader*)nullptr, false); + spine_atlas *result = SpineExtension::calloc(1, __FILE__, __LINE__); + result->atlas = atlas; + result->numImagePaths = atlas->getPages().size(); + result->imagePaths = SpineExtension::calloc(result->numImagePaths, __FILE__, __LINE__); + for (int i = 0; i < result->numImagePaths; i++) { + result->imagePaths[i] = strdup(atlas->getPages()[i]->texturePath.buffer()); + } + return result; +} + +FFI_PLUGIN_EXPORT void spine_atlas_dispose(spine_atlas *atlas) { + if (!atlas) return; + if (atlas->atlas) delete (Atlas*)atlas->atlas; + if (atlas->error) free(atlas->error); + for (int i = 0; i < atlas->numImagePaths; i++) { + free(atlas->imagePaths[i]); + } + SpineExtension::free(atlas, __FILE__, __LINE__); +} + spine::SpineExtension *spine::getDefaultExtension() { return new spine::DefaultSpineExtension(); } diff --git a/spine-flutter/src/spine_flutter.h b/spine-flutter/src/spine_flutter.h index a2194497b..5bf89e5b0 100644 --- a/spine-flutter/src/spine_flutter.h +++ b/spine-flutter/src/spine_flutter.h @@ -9,13 +9,30 @@ #include #endif +#ifdef __cplusplus #if _WIN32 #define FFI_PLUGIN_EXPORT extern "C" __declspec(dllexport) #else #define FFI_PLUGIN_EXPORT extern "C" #endif +#else +#if _WIN32 +#define FFI_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FFI_PLUGIN_EXPORT +#endif +#endif FFI_PLUGIN_EXPORT int32_t spine_major_version(); FFI_PLUGIN_EXPORT int32_t spine_minor_version(); +typedef struct spine_atlas { + void *atlas; + char **imagePaths; + int32_t numImagePaths; + char *error; +} spine_atlas; + +FFI_PLUGIN_EXPORT spine_atlas* spine_atlas_load(const char *atlasData); +FFI_PLUGIN_EXPORT void spine_atlas_dispose(spine_atlas *atlas);