소스 검색

[flutter] Add SkeletonDrawable.renderToRawImageData(), clean-up dress up example.

Mario Zechner 2 년 전
부모
커밋
b0c63b8fa8

+ 13 - 34
spine-flutter/example/lib/dress_up.dart

@@ -16,8 +16,7 @@ class DressUp extends StatefulWidget {
 
 class DressUpState extends State<DressUp> {
   static const double thumbnailSize = 200;
-  late SpineWidgetController _controller;
-  SkeletonDrawable? _drawable;
+  late SkeletonDrawable _drawable;
   Skin? _customSkin;
   final Map<String, RawImageData> _skinImages = {};
   final Map<String, bool> _selectedSkins = {};
@@ -27,42 +26,18 @@ class DressUpState extends State<DressUp> {
     reportLeaks();
     super.initState();
     SkeletonDrawable.fromAsset("assets/mix-and-match.atlas", "assets/mix-and-match-pro.skel").then((drawable) async {
+      _drawable = drawable;
       for (var skin in drawable.skeletonData.getSkins()) {
         if (skin.getName() == "default") continue;
-
         var skeleton = drawable.skeleton;
         skeleton.setSkin(skin);
         skeleton.setToSetupPose();
         skeleton.updateWorldTransform();
-        var bounds = skeleton.getBounds();
-        var scale = 1 / (bounds.width > bounds.height ? bounds.width / thumbnailSize : bounds.height / thumbnailSize);
-
-        var recorder = ui.PictureRecorder();
-        var canvas = Canvas(recorder);
-        var bgColor = Random().nextInt(0xffffffff) | 0xff0000000;
-        var paint = Paint()
-          ..color = ui.Color(bgColor)
-          ..style = PaintingStyle.fill;
-        canvas.drawRect(const Rect.fromLTWH(0, 0, thumbnailSize, thumbnailSize), paint);
-        canvas.translate(thumbnailSize / 2, thumbnailSize / 2);
-        canvas.scale(scale, scale);
-        canvas.translate(-(bounds.x + bounds.width / 2), -(bounds.y + bounds.height / 2));
-        canvas.drawRect(Rect.fromLTRB(-5, -5, 5, -5), paint..color = Colors.red);
-        drawable.renderToCanvas(canvas);
-
-        var rawImageData = (await (await recorder.endRecording().toImage(thumbnailSize.toInt(), thumbnailSize.toInt())).toByteData(format: ui.ImageByteFormat.rawRgba))!.buffer.asUint8List();
-        _skinImages[skin.getName()] = (RawImageData(rawImageData, thumbnailSize.toInt(), thumbnailSize.toInt()));
+        _skinImages[skin.getName()] = await drawable.renderToRawImageData(thumbnailSize, thumbnailSize);
         _selectedSkins[skin.getName()] = false;
       }
-      _drawable = drawable;
-      _controller = SpineWidgetController(onInitialized: (controller) {
-        controller.animationState.setAnimationByName(0, "dance", true);
-      });
-      setState(() {
-        _selectedSkins["full-skins/girl"] = true;
-        drawable.skeleton.setSkinByName("full-skins/girl");
-        drawable.skeleton.setToSetupPose();
-      });
+      _toggleSkin("full-skins/girl");
+      setState(() {});
     });
   }
 
@@ -72,16 +47,20 @@ class DressUpState extends State<DressUp> {
     _customSkin = Skin("custom-skin");
     for (var skinName in _selectedSkins.keys) {
       if (_selectedSkins[skinName] == true) {
-        var skin = _controller.skeletonData.findSkin(skinName);
+        var skin = _drawable.skeletonData.findSkin(skinName);
         if (skin != null) _customSkin?.addSkin(skin);
       }
     }
-    _controller.skeleton.setSkin(_customSkin!);
-    _controller.skeleton.setToSetupPose();
+    _drawable.skeleton.setSkin(_customSkin!);
+    _drawable.skeleton.setToSetupPose();
   }
 
   @override
   Widget build(BuildContext context) {
+    final controller = SpineWidgetController(onInitialized: (controller) {
+      controller.animationState.setAnimationByName(0, "dance", true);
+    });
+
     return Scaffold(
         appBar: AppBar(title: const Text('Dress Up')),
         body: _skinImages.isEmpty
@@ -107,7 +86,7 @@ class DressUpState extends State<DressUp> {
                       ),
                     ),
                     Expanded(
-                      child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]),)
+                      child: SpineWidget.drawable(_drawable, controller, boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]),)
                     )
                   ]
               )

+ 9 - 3
spine-flutter/example/lib/flame_example.dart

@@ -37,9 +37,8 @@ class SpineComponent extends PositionComponent {
         Iterable<Component>? children,
         int? priority,
       }) async {
-    final drawable = await SkeletonDrawable.fromAsset(atlasFile, skeletonFile, bundle: bundle);
     return SpineComponent(
-        drawable,
+        await SkeletonDrawable.fromAsset(atlasFile, skeletonFile, bundle: bundle),
         ownsDrawable: true,
         boundsProvider: boundsProvider,
         position: position,
@@ -54,6 +53,11 @@ class SpineComponent extends PositionComponent {
     if (_ownsDrawable) {
       _drawable.dispose();
     }
+
+    @override
+    void onDetach() {
+
+    }
   }
   
   @override
@@ -133,9 +137,11 @@ class PreloadAndShareSpineDataExample extends FlameGame {
 
   @override
   void onDetach() {
-    // Dispose the pre-loaded atlas and skeleton data when the game/scene is removed
+    // Dispose the pre-loaded atlas and skeleton data when the game/scene is removed.
     cachedAtlas.dispose();
     cachedSkeletonData.dispose();
+
+    // Dispose each spineboy and its internal SkeletonDrawable.
     for (final spineboy in spineboys) {
       spineboy.dispose();
     }

+ 0 - 13
spine-flutter/example/lib/main.dart

@@ -6,7 +6,6 @@ import 'flame_example.dart';
 import 'simple_animation.dart';
 import 'animation_state_events.dart';
 import 'pause_play_animation.dart';
-import 'skins.dart';
 import 'dress_up.dart';
 import 'ik_following.dart';
 
@@ -70,18 +69,6 @@ class ExampleSelector extends StatelessWidget {
                 },
               ),
               spacer,
-              ElevatedButton(
-                child: const Text('Skins'),
-                onPressed: () {
-                  Navigator.push(
-                    context,
-                    MaterialPageRoute<void>(
-                      builder: (context) => const Skins(),
-                    ),
-                  );
-                },
-              ),
-              spacer,
               ElevatedButton(
                 child: const Text('Dress Up'),
                 onPressed: () {

+ 0 - 85
spine-flutter/example/lib/skins.dart

@@ -1,85 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:esotericsoftware_spine_flutter/spine_flutter.dart';
-
-class Skins extends StatefulWidget {
-  const Skins({Key? key}) : super(key: key);
-
-  @override
-  SkinsState createState() => SkinsState();
-}
-
-class SkinsState extends State<Skins> {
-  SkeletonDrawable? _drawable;
-  late SpineWidgetController _controller;
-  final Map<String, bool> _selectedSkins = {};
-  Skin? _customSkin;
-
-  @override
-  void initState() {
-    super.initState();
-    SkeletonDrawable.fromAsset("assets/mix-and-match.atlas", "assets/mix-and-match-pro.skel").then((drawable) {
-      for (var skin in drawable.skeletonData.getSkins()) {
-        _selectedSkins[skin.getName()] = false;
-      }
-      _controller = SpineWidgetController(onInitialized: (controller) {
-        controller.animationState.setAnimationByName(0, "walk", true);
-      });
-      drawable.skeleton.setSkinByName("full-skins/girl");
-      _selectedSkins["full-skins/girl"] = true;
-      _drawable = drawable;
-      setState(() {});
-    });
-  }
-
-  void _toggleSkin(String skinName) {
-    _selectedSkins[skinName] = !_selectedSkins[skinName]!;
-
-    if (_customSkin != null) _customSkin?.dispose();
-
-    _customSkin = Skin("custom-skin");
-    for (var skinName in _selectedSkins.keys) {
-      if (_selectedSkins[skinName] == true) {
-        var skin = _controller.skeletonData.findSkin(skinName);
-        if (skin != null) _customSkin?.addSkin(skin);
-      }
-    }
-    _controller.skeleton.setSkin(_customSkin!);
-    _controller.skeleton.setToSetupPose();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return Scaffold(
-        appBar: AppBar(title: const Text('Skins')),
-        body: _drawable == null
-            ? const SizedBox()
-            : Row(
-            children: [
-              Expanded(
-                  child:ListView(
-                      children: _selectedSkins.keys.map((skinName) {
-                        return CheckboxListTile(
-                          title: Text(skinName),
-                          value: _selectedSkins[skinName],
-                          onChanged: (bool? value) {
-                            _toggleSkin(skinName);
-                            setState(() => {});
-                          },
-                        );
-                      }).toList()
-                  )
-              ),
-              Expanded(
-                  child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]))
-              )
-            ]
-        )
-    );
-  }
-
-  @override
-  void dispose() {
-    super.dispose();
-    _drawable?.dispose();
-  }
-}

+ 1 - 8
spine-flutter/example/pubspec.lock

@@ -132,15 +132,8 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.8.2"
-  plugin_platform_interface:
-    dependency: transitive
-    description:
-      name: plugin_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.2"
   raw_image_provider:
-    dependency: "direct main"
+    dependency: transitive
     description:
       name: raw_image_provider
       url: "https://pub.dartlang.org"

+ 0 - 1
spine-flutter/example/pubspec.yaml

@@ -13,7 +13,6 @@ dependencies:
   esotericsoftware_spine_flutter:
     path: ../
   cupertino_icons: ^1.0.2
-  raw_image_provider: ^0.2.0
   flame: ^1.4.0
 
 dev_dependencies:

+ 22 - 1
spine-flutter/lib/spine_flutter.dart

@@ -1,5 +1,6 @@
 import 'dart:convert' as convert;
 import 'dart:io';
+import 'dart:math';
 import 'dart:typed_data';
 import 'dart:ui';
 
@@ -8,6 +9,7 @@ import 'package:flutter/rendering.dart' as rendering;
 import 'package:flutter/services.dart';
 import 'package:http/http.dart' as http;
 import 'package:path/path.dart' as path;
+import 'package:raw_image_provider/raw_image_provider.dart';
 
 import 'spine_flutter_bindings_generated.dart';
 import 'ffi_proxy.dart';
@@ -2595,7 +2597,6 @@ class Skeleton {
   }
 }
 
-// FIXME expose timelines and apply?
 class Animation {
   final spine_animation _animation;
 
@@ -3307,6 +3308,26 @@ class SkeletonDrawable {
     }
   }
 
+  Future<RawImageData> renderToRawImageData(double width, double height) async {
+    var bounds = skeleton.getBounds();
+    var scale = 1 / (bounds.width > bounds.height ? bounds.width / width : bounds.height / height);
+
+    var recorder = PictureRecorder();
+    var canvas = Canvas(recorder);
+    var bgColor = Random().nextInt(0xffffffff) | 0xff0000000;
+    var paint = Paint()
+      ..color = material.Color(bgColor)
+      ..style = PaintingStyle.fill;
+    canvas.drawRect(Rect.fromLTWH(0, 0, width, height), paint);
+    canvas.translate(width / 2, height / 2);
+    canvas.scale(scale, scale);
+    canvas.translate(-(bounds.x + bounds.width / 2), -(bounds.y + bounds.height / 2));
+    canvas.drawRect(const Rect.fromLTRB(-5, -5, 5, -5), paint..color = material.Colors.red);
+    renderToCanvas(canvas);
+    var rawImageData = (await (await recorder.endRecording().toImage(width.toInt(), height.toInt())).toByteData(format: ImageByteFormat.rawRgba))!.buffer.asUint8List();
+    return RawImageData(rawImageData, width.toInt(), height.toInt());
+  }
+
   void dispose() {
     if (_disposed) return;
     _disposed = true;

+ 7 - 5
spine-flutter/lib/spine_widget.dart

@@ -106,17 +106,18 @@ class SkinAndAnimationBounds extends BoundsProvider {
 
   @override
   Bounds computeBounds(SkeletonDrawable drawable) {
-    var data = drawable.skeletonData;
-    var customSkin = Skin("custom-skin");
-    for (var skinName in skins) {
-      var skin = data.findSkin(skinName);
+    final data = drawable.skeletonData;
+    final oldSkin = drawable.skeleton.getSkin();
+    final customSkin = Skin("custom-skin");
+    for (final skinName in skins) {
+      final skin = data.findSkin(skinName);
       if (skin == null) continue;
       customSkin.addSkin(skin);
     }
     drawable.skeleton.setSkin(customSkin);
     drawable.skeleton.setToSetupPose();
 
-    final animation = data.findAnimation(this.animation!);
+    final animation = this.animation != null ? data.findAnimation(this.animation!) : null;
     double minX = double.infinity;
     double minY = double.infinity;
     double maxX = double.negativeInfinity;
@@ -142,6 +143,7 @@ class SkinAndAnimationBounds extends BoundsProvider {
     customSkin.dispose();
     drawable.skeleton.setSkinByName("default");
     drawable.animationState.clearTracks();
+    if (oldSkin != null) drawable.skeleton.setSkin(oldSkin);
     drawable.skeleton.setToSetupPose();
     drawable.update(0);
     return Bounds(minX, minY, maxX - minX, maxY - minY);

+ 1 - 1
spine-flutter/pubspec.yaml

@@ -13,7 +13,6 @@ environment:
 dependencies:
   flutter:
     sdk: flutter
-  plugin_platform_interface: ^2.0.2
   ffi: ^2.0.1
   web_ffi_fork: ^0.7.4
   inject_js: ^2.0.0
@@ -21,6 +20,7 @@ dependencies:
   meta: ^1.3.0
   http: ^0.13.5
   path: ^1.8.2
+  raw_image_provider: ^0.2.0
 
 dev_dependencies:
   ffigen: ^6.1.2