From d7664a97466b4f6eaa4829b5443cfe18d704f15f Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 21 Aug 2024 17:17:58 +0800 Subject: [PATCH] feat!: (web) (flutter) create canvas when createViewer is called (no longer need to manually add canvas element to web HTML) --- .../lib/thermion_dart/thermion_viewer.dart | 1 - .../lib/thermion/thermion_flutter_plugin.dart | 10 ++-- .../lib/thermion/widgets/thermion_widget.dart | 22 +++++++-- .../thermion/widgets/thermion_widget_web.dart | 3 ++ .../widgets/thermion_widget_web_impl.dart | 49 +++++++++++++++++++ .../widgets/thermion_widget_web_stub.dart | 7 +++ .../lib/thermion_flutter_web.dart | 21 +++++--- 7 files changed, 95 insertions(+), 18 deletions(-) create mode 100644 thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web.dart create mode 100644 thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web_impl.dart create mode 100644 thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web_stub.dart diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer.dart b/thermion_dart/lib/thermion_dart/thermion_viewer.dart index 49370879..c3569657 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer.dart @@ -85,7 +85,6 @@ abstract class ThermionViewer { /// Future render(); - /// /// Render a single frame to the viewport and copy the pixel buffer to [out]. /// diff --git a/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart b/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart index 3c784cc9..da02e615 100644 --- a/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart +++ b/thermion_flutter/thermion_flutter/lib/thermion/thermion_flutter_plugin.dart @@ -9,10 +9,8 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dar /// surface in a Flutter application and lifecycle listeners to pause rendering /// when the app is inactive or in the background. /// Call [createViewer] to create an instance of [ThermionViewer]. -/// This is a lightweight singleton that /// class ThermionFlutterPlugin { - ThermionFlutterPlugin._(); static AppLifecycleListener? _appLifecycleListener; @@ -92,8 +90,12 @@ class ThermionFlutterPlugin { } @override - static Future resizeTexture(ThermionFlutterTexture texture, - int width, int height, int offsetLeft, int offsetRight) async { + static Future resizeTexture( + ThermionFlutterTexture texture, + int width, + int height, + int offsetLeft, + int offsetRight) async { return ThermionFlutterPlatform.instance .resizeTexture(texture, width, height, offsetLeft, offsetRight); } diff --git a/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget.dart b/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget.dart index e69d2eb1..2f86d154 100644 --- a/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget.dart +++ b/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:thermion_flutter/thermion/widgets/thermion_widget_web.dart'; import 'dart:async'; import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; @@ -82,17 +83,27 @@ class _ThermionWidgetState extends State { @override Widget build(BuildContext context) { + if (kIsWeb) { + if (_texture == null || _resizing) { + return widget.initial ?? Container(color: Colors.red); + } + return ResizeObserver( + onResized: _resizeTexture, child: ThermionWidgetWeb()); + } + if (_texture?.usesBackingWindow == true) { return ResizeObserver( - onResized: _resizeTexture, - child: Stack(children: [ - Positioned.fill(child: CustomPaint(painter: TransparencyPainter())) - ])); + onResized: _resizeTexture, + child: Stack(children: [ + Positioned.fill(child: CustomPaint(painter: TransparencyPainter())) + ])); } if (_texture == null || _resizing) { return widget.initial ?? - Container(color: kIsWeb ? Colors.transparent : Colors.red); + Container( + color: + kIsWeb ? const Color.fromARGB(0, 170, 129, 129) : Colors.red); } var textureWidget = Texture( @@ -131,3 +142,4 @@ class TransparencyPainter extends CustomPainter { @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } + diff --git a/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web.dart b/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web.dart new file mode 100644 index 00000000..1b72c757 --- /dev/null +++ b/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web.dart @@ -0,0 +1,3 @@ +export 'thermion_widget_web_stub.dart' + if (dart.library.js_interop) 'thermion_widget_web_impl.dart'; + diff --git a/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web_impl.dart b/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web_impl.dart new file mode 100644 index 00000000..905d63a6 --- /dev/null +++ b/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web_impl.dart @@ -0,0 +1,49 @@ +import 'dart:js_util'; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import 'dart:ui' as ui; +import 'dart:ui_web' as ui_web; +import 'package:flutter/material.dart'; +import 'package:web/web.dart'; +import 'package:flutter/widgets.dart'; +import 'dart:html' as html; + +class ThermionWidgetWeb extends StatefulWidget { + @override + State createState() => ThermionWidgetWebState(); +} + +class ThermionWidgetWebState extends State { + ui.Image? _img; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + capture(); + }); + } + + Future capture() async { + try { + final ImageBitmap newSource = await promiseToFuture( + window.createImageBitmap( + document.getElementById("canvas") as HTMLCanvasElement)); + _img = await ui_web.createImageFromImageBitmap(newSource); + setState(() {}); + WidgetsBinding.instance.addPostFrameCallback((_) { + capture(); + }); + } catch (err) { + print(err); + } + } + + @override + Widget build(BuildContext context) { + if (_img == null) { + return Container(color: Colors.transparent); + } + return RawImage(image: _img!); + } +} diff --git a/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web_stub.dart b/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web_stub.dart new file mode 100644 index 00000000..528afe83 --- /dev/null +++ b/thermion_flutter/thermion_flutter/lib/thermion/widgets/thermion_widget_web_stub.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +class ThermionWidgetWeb extends StatefulWidget { + @override + State createState() => throw Exception(); +} diff --git a/thermion_flutter/thermion_flutter_web/lib/thermion_flutter_web.dart b/thermion_flutter/thermion_flutter_web/lib/thermion_flutter_web.dart index 765c5dc2..230a08b1 100644 --- a/thermion_flutter/thermion_flutter_web/lib/thermion_flutter_web.dart +++ b/thermion_flutter/thermion_flutter_web/lib/thermion_flutter_web.dart @@ -17,6 +17,14 @@ class ThermionFlutterWebPlugin extends ThermionFlutterPlatform { @override Future createTexture( int width, int height, int offsetLeft, int offsetRight) async { + await _viewer!.destroySwapChain(); + await _viewer!.createSwapChain(width, height); + _viewer!.updateViewportAndCameraProjection(width, height, 1.0); + + final canvas = document.getElementById("canvas") as HTMLCanvasElement; + canvas.width = width; + canvas.height = height; + return ThermionFlutterTexture(null, null, 0, 0, null); } @@ -39,15 +47,12 @@ class ThermionFlutterWebPlugin extends ThermionFlutterPlatform { } Future createViewer({String? uberArchivePath}) async { - final canvas = document.getElementById("canvas") as HTMLCanvasElement; - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - - var width = window.innerWidth; - var height = window.innerHeight; - _viewer = ThermionViewerWasm(assetPathPrefix: "/assets/"); - await _viewer!.initialize(width, height, uberArchivePath: uberArchivePath); + final canvas = document.createElement("canvas") as HTMLCanvasElement; + canvas.id = "canvas"; + document.body!.appendChild(canvas); + canvas.style.display = 'none'; + await _viewer!.initialize(0, 0, uberArchivePath:uberArchivePath); return _viewer!; } }