Uses it in the artboard title but also updates the flutter runtime massively to support FFI & WASM C++ Rive Text. PRing to let the tests run.

Diffs=
3be5ff0d8 Text (#4372)
90245a5e1 Fix the Android debug build
0a0f3c267 Fix for missing animation in a blend state. (#4415)
440512dca Add simd::if_then_else (#4403)
ec9fb5bfc Revert "Update SIMD booleans to use bitwise logic operators"
701d8dee2 Update SIMD booleans to use bitwise logic operators
e98b93a61 Add SIMD fallbacks for missing builtins
466f68e3a Add some more core math and SIMD functions
This commit is contained in:
luigi-rosso
2022-11-15 23:07:11 +00:00
parent 0418b7310c
commit e618def5cd
101 changed files with 11860 additions and 30 deletions

View File

@ -5,7 +5,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: [self-hosted, macOS, ARM64]
steps:
- uses: actions/checkout@v2
@ -24,5 +24,15 @@ jobs:
flutter channel stable
flutter doctor
- name: Build WASM
run: |
cd wasm
./build_wasm.sh release
- name: Build Shared Lib
run: |
cd shared_lib
./build_shared.sh
- name: Run tests
run: flutter test
run: flutter test

16
.gitignore vendored
View File

@ -74,3 +74,19 @@ pubspec.lock
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
ios/rive-cpp
ios/harfbuzz
macos/rive-cpp
macos/harfbuzz
macos/SheenBidi
wasm/bin/
wasm/Makefile
wasm/render_font.make
node_modules
wasm/*.make
wasm/dependencies
shared_lib/dependencies
shared_lib/bin
*.make
Makefile

32
.lua-format Normal file
View File

@ -0,0 +1,32 @@
column_limit: 80
indent_width: 4
use_tab: false
tab_width: 4
continuation_indent_width: 4
spaces_before_call: 1
keep_simple_control_block_one_line: true
keep_simple_function_one_line: true
align_args: true
break_after_functioncall_lp: false
break_before_functioncall_rp: false
spaces_inside_functioncall_parens: false
spaces_inside_functiondef_parens: false
align_parameter: true
chop_down_parameter: false
break_after_functiondef_lp: false
break_before_functiondef_rp: false
align_table_field: true
break_after_table_lb: true
break_before_table_rb: true
chop_down_table: true
chop_down_kv_table: true
table_sep: ","
column_table_limit: column_limit
extra_sep_at_table_end: false
spaces_inside_table_braces: false
break_after_operator: true
double_quote_to_single_quote: false
single_quote_to_double_quote: false
spaces_around_equals_in_field: true
line_breaks_after_function_body: 1
line_separator: input

View File

@ -1,10 +1,42 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
# This file should be version controlled.
version:
revision: b9a56b9f48462d897101284f5f57b8568ef683c6
channel: master
revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
channel: unknown
project_type: package
project_type: plugin
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
- platform: android
create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
- platform: ios
create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
- platform: macos
create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
- platform: web
create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
- platform: windows
create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -1 +1 @@
f15304279962053360702d3ef8443d110d444893
3be5ff0d873280d2bcec300fb764f016ac96a08d

View File

@ -16,6 +16,9 @@ dependencies:
rive: ^0.9.0
```
## Platform Considerations
Read some [platform specific considerations](platform_considerations.md) with regards to the Rive Flutter package.
## Quick Start
Play an animation from a Rive file over HTTP:

9
android/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx

79
android/CMakeLists.txt Normal file
View File

@ -0,0 +1,79 @@
cmake_minimum_required(VERSION 3.4.1) # for example
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -DWITH_RIVE_TEXT -DHAVE_OT -DHB_NO_FALLBACK_SHAPE -DHB_NO_WIN1256 -std=c++17"
)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSB_CONFIG_UNITY")
add_library(
rive_text
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
../ios/rive_text/rive_text.cpp
../ios/rive-cpp/src/math/raw_path.cpp
../ios/rive-cpp/src/math/mat2d.cpp
../ios/rive-cpp/src/rive_counter.cpp
../ios/rive-cpp/src/renderer.cpp
../ios/rive-cpp/src/text/font_hb.cpp
../ios/rive-cpp/src/text/line_breaker.cpp
../ios/harfbuzz/src/hb-aat-layout.cc
../ios/harfbuzz/src/hb-aat-map.cc
../ios/harfbuzz/src/hb-blob.cc
../ios/harfbuzz/src/hb-buffer-serialize.cc
../ios/harfbuzz/src/hb-buffer-verify.cc
../ios/harfbuzz/src/hb-buffer.cc
../ios/harfbuzz/src/hb-common.cc
../ios/harfbuzz/src/hb-draw.cc
../ios/harfbuzz/src/hb-face.cc
../ios/harfbuzz/src/hb-font.cc
../ios/harfbuzz/src/hb-map.cc
../ios/harfbuzz/src/hb-number.cc
../ios/harfbuzz/src/hb-ot-cff1-table.cc
../ios/harfbuzz/src/hb-ot-cff2-table.cc
../ios/harfbuzz/src/hb-ot-color.cc
../ios/harfbuzz/src/hb-ot-face.cc
../ios/harfbuzz/src/hb-ot-font.cc
../ios/harfbuzz/src/hb-ot-layout.cc
../ios/harfbuzz/src/hb-ot-map.cc
../ios/harfbuzz/src/hb-ot-math.cc
../ios/harfbuzz/src/hb-ot-meta.cc
../ios/harfbuzz/src/hb-ot-metrics.cc
../ios/harfbuzz/src/hb-ot-name.cc
../ios/harfbuzz/src/hb-ot-shape-complex-arabic.cc
../ios/harfbuzz/src/hb-ot-shape-complex-default.cc
../ios/harfbuzz/src/hb-ot-shape-complex-hangul.cc
../ios/harfbuzz/src/hb-ot-shape-complex-hebrew.cc
../ios/harfbuzz/src/hb-ot-shape-complex-indic-table.cc
../ios/harfbuzz/src/hb-ot-shape-complex-indic.cc
../ios/harfbuzz/src/hb-ot-shape-complex-khmer.cc
../ios/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
../ios/harfbuzz/src/hb-ot-shape-complex-syllabic.cc
../ios/harfbuzz/src/hb-ot-shape-complex-thai.cc
../ios/harfbuzz/src/hb-ot-shape-complex-use.cc
../ios/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc
../ios/harfbuzz/src/hb-ot-shape-fallback.cc
../ios/harfbuzz/src/hb-ot-shape-normalize.cc
../ios/harfbuzz/src/hb-ot-shape.cc
../ios/harfbuzz/src/hb-ot-tag.cc
../ios/harfbuzz/src/hb-ot-var.cc
../ios/harfbuzz/src/hb-set.cc
../ios/harfbuzz/src/hb-shape-plan.cc
../ios/harfbuzz/src/hb-shape.cc
../ios/harfbuzz/src/hb-shaper.cc
../ios/harfbuzz/src/hb-static.cc
../ios/harfbuzz/src/hb-subset-cff-common.cc
../ios/harfbuzz/src/hb-subset-cff1.cc
../ios/harfbuzz/src/hb-subset-cff2.cc
../ios/harfbuzz/src/hb-subset-input.cc
../ios/harfbuzz/src/hb-subset-plan.cc
../ios/harfbuzz/src/hb-subset-repacker.cc
../ios/harfbuzz/src/hb-subset.cc
../ios/harfbuzz/src/hb-ucd.cc
../ios/harfbuzz/src/hb-unicode.cc
../ios/SheenBidi/Source/SheenBidi.c)
target_include_directories(
rive_text PRIVATE ../ios/harfbuzz/src ../ios/rive-cpp/skia/renderer/include
../ios/rive-cpp/include ../ios/SheenBidi/Headers)

56
android/build.gradle Normal file
View File

@ -0,0 +1,56 @@
group 'app.rive.rive'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.6.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 31
ndkVersion "25.1.8937393"
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 16
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

1
android/settings.gradle Normal file
View File

@ -0,0 +1 @@
rootProject.name = 'rive'

View File

@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.rive.rive">
</manifest>

View File

@ -0,0 +1,35 @@
package app.rive.rive
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/** RivePlugin */
class RivePlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "rive")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}

View File

@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -27,6 +27,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 31
ndkVersion "25.1.8937393"
sourceSets {
main.java.srcDirs += 'src/main/kotlin'

View File

@ -0,0 +1,6 @@
package app.rive.rive_example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
<string>11.0</string>
</dict>
</plist>

View File

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

41
example/ios/Podfile Normal file
View File

@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

22
example/ios/Podfile.lock Normal file
View File

@ -0,0 +1,22 @@
PODS:
- Flutter (1.0.0)
- rive (0.0.1):
- Flutter
DEPENDENCIES:
- Flutter (from `Flutter`)
- rive (from `.symlinks/plugins/rive/ios`)
EXTERNAL SOURCES:
Flutter:
:path: Flutter
rive:
:path: ".symlinks/plugins/rive/ios"
SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
rive: 75e8ef88dfbec24b5dac39121b8df26efafe5097
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.11.3

View File

@ -3,12 +3,13 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
57BFAF227DA797F2DC6C76E7 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 994CA1ABA4A9C2999F2DEB4C /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
@ -31,7 +32,10 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3621BEEC2FEB1765CC1955B6 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3EE3885D8C1E4061691A4E9C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
6F08542C2AA5F268C6BCDAA6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@ -42,6 +46,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
994CA1ABA4A9C2999F2DEB4C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -49,12 +54,21 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
57BFAF227DA797F2DC6C76E7 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
82EBFB8F4C6F19875EADADDE /* Frameworks */ = {
isa = PBXGroup;
children = (
994CA1ABA4A9C2999F2DEB4C /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@ -72,6 +86,8 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
D3FB9C997ED1EAC1563A8534 /* Pods */,
82EBFB8F4C6F19875EADADDE /* Frameworks */,
);
sourceTree = "<group>";
};
@ -106,6 +122,16 @@
name = "Supporting Files";
sourceTree = "<group>";
};
D3FB9C997ED1EAC1563A8534 /* Pods */ = {
isa = PBXGroup;
children = (
6F08542C2AA5F268C6BCDAA6 /* Pods-Runner.debug.xcconfig */,
3621BEEC2FEB1765CC1955B6 /* Pods-Runner.release.xcconfig */,
3EE3885D8C1E4061691A4E9C /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -113,12 +139,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9C42EB10E73B1A6E7577C992 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
54AC55EE59C86AF00C4FB9C1 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -135,7 +163,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@ -191,6 +219,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
54AC55EE59C86AF00C4FB9C1 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -205,6 +250,28 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
9C42EB10E73B1A6E7577C992 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -280,7 +347,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -296,13 +363,17 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = NJ3JMFUNS9;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@ -362,7 +433,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -411,7 +482,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -428,13 +499,17 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = NJ3JMFUNS9;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@ -455,13 +530,17 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = NJ3JMFUNS9;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",

View File

@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -4,4 +4,7 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,6 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : FlutterAppDelegate
@end

View File

@ -0,0 +1,13 @@
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end

View File

@ -41,5 +41,7 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,9 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View File

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"

View File

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"

View File

@ -5,6 +5,8 @@
import FlutterMacOS
import Foundation
import rive
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin"))
}

40
example/macos/Podfile Normal file
View File

@ -0,0 +1,40 @@
platform :osx, '10.11'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_macos_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
end
end

View File

@ -0,0 +1,22 @@
PODS:
- FlutterMacOS (1.0.0)
- rive (0.0.1):
- FlutterMacOS
DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`)
- rive (from `Flutter/ephemeral/.symlinks/plugins/rive/macos`)
EXTERNAL SOURCES:
FlutterMacOS:
:path: Flutter/ephemeral
rive:
:path: Flutter/ephemeral/.symlinks/plugins/rive/macos
SPEC CHECKSUMS:
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
rive: 8bf1bb49b46a4c3045424aa7c1243fe8942bb576
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
COCOAPODS: 1.11.3

View File

@ -26,6 +26,7 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
3C0FB406882FB59B3F797C42 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF6C7F17DBDC2D7E326330D2 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -52,9 +53,10 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
2D87453FF79CB62E45B9D168 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@ -68,6 +70,9 @@
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
D803C4DFFE53D1FFAB2CF942 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
DF6C7F17DBDC2D7E326330D2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E32AFBBD7A837D065523019F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -75,12 +80,24 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3C0FB406882FB59B3F797C42 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
22BEA6C90E8F99148A5C1825 /* Pods */ = {
isa = PBXGroup;
children = (
2D87453FF79CB62E45B9D168 /* Pods-Runner.debug.xcconfig */,
E32AFBBD7A837D065523019F /* Pods-Runner.release.xcconfig */,
D803C4DFFE53D1FFAB2CF942 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
33BA886A226E78AF003329D5 /* Configs */ = {
isa = PBXGroup;
children = (
@ -99,6 +116,7 @@
33CEB47122A05771004F2AC0 /* Flutter */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
22BEA6C90E8F99148A5C1825 /* Pods */,
);
sourceTree = "<group>";
};
@ -148,6 +166,7 @@
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
DF6C7F17DBDC2D7E326330D2 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -159,11 +178,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
6CAF1097CBE73A2DBD900610 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
1BB3133E7507708F9822CB2F /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -233,6 +254,23 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
1BB3133E7507708F9822CB2F /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -270,6 +308,28 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
6CAF1097CBE73A2DBD900610 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */

View File

@ -36,8 +36,8 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@ -59,8 +59,6 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"

View File

@ -4,4 +4,7 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

17
example/windows/.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
flutter/ephemeral/
# Visual Studio user-specific files.
*.suo
*.user
*.userosscache
*.sln.docstates
# Visual Studio build-related files.
x64/
x86/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

View File

@ -0,0 +1,101 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.14)
project(example LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "example")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# Define build configuration option.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG)
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
CACHE STRING "" FORCE)
else()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
endif()
# Define settings for the Profile build mode.
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
# Use Unicode for all projects.
add_definitions(-DUNICODE -D_UNICODE)
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
target_compile_options(${TARGET} PRIVATE /EHsc)
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# Support files are copied into place next to the executable, so that it can
# run in place. This is done instead of making a separate bundle (as on Linux)
# so that building and running from within Visual Studio will work.
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# Make the "install" step default, as it's required to run.
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
CONFIGURATIONS Profile;Release
COMPONENT Runtime)

View File

@ -0,0 +1,104 @@
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.14)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"flutter_export.h"
"flutter_windows.h"
"flutter_messenger.h"
"flutter_plugin_registrar.h"
"flutter_texture_registrar.h"
)
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
add_dependencies(flutter flutter_assemble)
# === Wrapper ===
list(APPEND CPP_WRAPPER_SOURCES_CORE
"core_implementations.cc"
"standard_codec.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
"plugin_registrar.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_APP
"flutter_engine.cc"
"flutter_view_controller.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
# Wrapper sources needed for a plugin.
add_library(flutter_wrapper_plugin STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
)
apply_standard_settings(flutter_wrapper_plugin)
set_target_properties(flutter_wrapper_plugin PROPERTIES
POSITION_INDEPENDENT_CODE ON)
set_target_properties(flutter_wrapper_plugin PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
target_include_directories(flutter_wrapper_plugin PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_plugin flutter_assemble)
# Wrapper sources needed for the runner.
add_library(flutter_wrapper_app STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_APP}
)
apply_standard_settings(flutter_wrapper_app)
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
target_include_directories(flutter_wrapper_app PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_app flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
${PHONY_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)

View File

@ -0,0 +1,14 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <rive/rive_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
RivePluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("RivePlugin"));
}

View File

@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter/plugin_registry.h>
// Registers Flutter plugins.
void RegisterPlugins(flutter::PluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@ -0,0 +1,24 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
rive
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.14)
project(runner LANGUAGES CXX)
# Define the application target. To change its name, change BINARY_NAME in the
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
# work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
"utils.cpp"
"win32_window.cpp"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
"Runner.rc"
"runner.exe.manifest"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add preprocessor definitions for the build version.
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
# Disable Windows macros that collide with C++ standard library functions.
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
# Add dependency libraries and include directories. Add any application-specific
# dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)

View File

@ -0,0 +1,121 @@
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APP_ICON ICON "resources\\app_icon.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else
#define VERSION_AS_NUMBER 1,0,0,0
#endif
#if defined(FLUTTER_VERSION)
#define VERSION_AS_STRING FLUTTER_VERSION
#else
#define VERSION_AS_STRING "1.0.0"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_AS_NUMBER
PRODUCTVERSION VERSION_AS_NUMBER
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "FileDescription", "example" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "example" "\0"
VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0"
VALUE "OriginalFilename", "example.exe" "\0"
VALUE "ProductName", "example" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -0,0 +1,70 @@
#include "flutter_window.h"
#include <optional>
#include "flutter/generated_plugin_registrant.h"
FlutterWindow::FlutterWindow(const flutter::DartProject& project) : project_(project) {}
FlutterWindow::~FlutterWindow() {}
bool FlutterWindow::OnCreate()
{
if (!Win32Window::OnCreate())
{
return false;
}
RECT frame = GetClientArea();
// The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(frame.right - frame.left,
frame.bottom - frame.top,
project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view())
{
return false;
}
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
return true;
}
void FlutterWindow::OnDestroy()
{
if (flutter_controller_)
{
flutter_controller_ = nullptr;
}
Win32Window::OnDestroy();
}
LRESULT
FlutterWindow::MessageHandler(HWND hwnd,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept
{
// Give Flutter, including plugins, an opportunity to handle window messages.
if (flutter_controller_)
{
std::optional<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, lparam);
if (result)
{
return *result;
}
}
switch (message)
{
case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts();
break;
}
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
}

View File

@ -0,0 +1,36 @@
#ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <memory>
#include "win32_window.h"
// A window that does nothing but host a Flutter view.
class FlutterWindow : public Win32Window
{
public:
// Creates a new FlutterWindow hosting a Flutter view running |project|.
explicit FlutterWindow(const flutter::DartProject& project);
virtual ~FlutterWindow();
protected:
// Win32Window:
bool OnCreate() override;
void OnDestroy() override;
LRESULT MessageHandler(HWND window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept override;
private:
// The project to run.
flutter::DartProject project_;
// The Flutter instance hosted by this window.
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
};
#endif // RUNNER_FLUTTER_WINDOW_H_

View File

@ -0,0 +1,48 @@
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
#include "flutter_window.h"
#include "utils.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance,
_In_opt_ HINSTANCE prev,
_In_ wchar_t* command_line,
_In_ int show_command)
{
// Attach to console when present (e.g., 'flutter run') or create a
// new console when running with a debugger.
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent())
{
CreateAndAttachConsole();
}
// Initialize COM, so that it is available for use in the library and/or
// plugins.
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
flutter::DartProject project(L"data");
std::vector<std::string> command_line_arguments = GetCommandLineArguments();
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"example", origin, size))
{
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::CoUninitialize();
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,16 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Runner.rc
//
#define IDI_APP_ICON 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>

View File

@ -0,0 +1,84 @@
#include "utils.h"
#include <flutter_windows.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>
#include <iostream>
void CreateAndAttachConsole()
{
if (::AllocConsole())
{
FILE* unused;
if (freopen_s(&unused, "CONOUT$", "w", stdout))
{
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr))
{
_dup2(_fileno(stdout), 2);
}
std::ios::sync_with_stdio();
FlutterDesktopResyncOutputStreams();
}
}
std::vector<std::string> GetCommandLineArguments()
{
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
int argc;
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
if (argv == nullptr)
{
return std::vector<std::string>();
}
std::vector<std::string> command_line_arguments;
// Skip the first argument as it's the binary name.
for (int i = 1; i < argc; i++)
{
command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
}
::LocalFree(argv);
return command_line_arguments;
}
std::string Utf8FromUtf16(const wchar_t* utf16_string)
{
if (utf16_string == nullptr)
{
return std::string();
}
int target_length = ::WideCharToMultiByte(CP_UTF8,
WC_ERR_INVALID_CHARS,
utf16_string,
-1,
nullptr,
0,
nullptr,
nullptr);
std::string utf8_string;
if (target_length == 0 || target_length > utf8_string.max_size())
{
return utf8_string;
}
utf8_string.resize(target_length);
int converted_length = ::WideCharToMultiByte(CP_UTF8,
WC_ERR_INVALID_CHARS,
utf16_string,
-1,
utf8_string.data(),
target_length,
nullptr,
nullptr);
if (converted_length == 0)
{
return std::string();
}
return utf8_string;
}

View File

@ -0,0 +1,19 @@
#ifndef RUNNER_UTILS_H_
#define RUNNER_UTILS_H_
#include <string>
#include <vector>
// Creates a console for the process, and redirects stdout and stderr to
// it for both the runner and the Flutter library.
void CreateAndAttachConsole();
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
// encoded in UTF-8. Returns an empty std::string on failure.
std::string Utf8FromUtf16(const wchar_t* utf16_string);
// Gets the command line arguments passed in as a std::vector<std::string>,
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments();
#endif // RUNNER_UTILS_H_

View File

@ -0,0 +1,282 @@
#include "win32_window.h"
#include <flutter_windows.h>
#include "resource.h"
namespace
{
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
// Scale helper to convert logical scaler values to physical using passed in
// scale factor
int Scale(int source, double scale_factor) { return static_cast<int>(source * scale_factor); }
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
// This API is only needed for PerMonitor V1 awareness mode.
void EnableFullDpiSupportIfAvailable(HWND hwnd)
{
HMODULE user32_module = LoadLibraryA("User32.dll");
if (!user32_module)
{
return;
}
auto enable_non_client_dpi_scaling = reinterpret_cast<EnableNonClientDpiScaling*>(
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr)
{
enable_non_client_dpi_scaling(hwnd);
FreeLibrary(user32_module);
}
}
} // namespace
// Manages the Win32Window's window class registration.
class WindowClassRegistrar
{
public:
~WindowClassRegistrar() = default;
// Returns the singleton registar instance.
static WindowClassRegistrar* GetInstance()
{
if (!instance_)
{
instance_ = new WindowClassRegistrar();
}
return instance_;
}
// Returns the name of the window class, registering the class if it hasn't
// previously been registered.
const wchar_t* GetWindowClass();
// Unregisters the window class. Should only be called if there are no
// instances of the window.
void UnregisterWindowClass();
private:
WindowClassRegistrar() = default;
static WindowClassRegistrar* instance_;
bool class_registered_ = false;
};
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
const wchar_t* WindowClassRegistrar::GetWindowClass()
{
if (!class_registered_)
{
WNDCLASS window_class{};
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = kWindowClassName;
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon = LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc;
RegisterClass(&window_class);
class_registered_ = true;
}
return kWindowClassName;
}
void WindowClassRegistrar::UnregisterWindowClass()
{
UnregisterClass(kWindowClassName, nullptr);
class_registered_ = false;
}
Win32Window::Win32Window() { ++g_active_window_count; }
Win32Window::~Win32Window()
{
--g_active_window_count;
Destroy();
}
bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, const Size& size)
{
Destroy();
const wchar_t* window_class = WindowClassRegistrar::GetInstance()->GetWindowClass();
const POINT target_point = {static_cast<LONG>(origin.x), static_cast<LONG>(origin.y)};
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
double scale_factor = dpi / 96.0;
HWND window = CreateWindow(window_class,
title.c_str(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
Scale(origin.x, scale_factor),
Scale(origin.y, scale_factor),
Scale(size.width, scale_factor),
Scale(size.height, scale_factor),
nullptr,
nullptr,
GetModuleHandle(nullptr),
this);
if (!window)
{
return false;
}
return OnCreate();
}
// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept
{
if (message == WM_NCCREATE)
{
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window,
GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
EnableFullDpiSupportIfAvailable(window);
that->window_handle_ = window;
}
else if (Win32Window* that = GetThisFromHandle(window))
{
return that->MessageHandler(window, message, wparam, lparam);
}
return DefWindowProc(window, message, wparam, lparam);
}
LRESULT
Win32Window::MessageHandler(HWND hwnd,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept
{
switch (message)
{
case WM_DESTROY:
window_handle_ = nullptr;
Destroy();
if (quit_on_close_)
{
PostQuitMessage(0);
}
return 0;
case WM_DPICHANGED:
{
auto newRectSize = reinterpret_cast<RECT*>(lparam);
LONG newWidth = newRectSize->right - newRectSize->left;
LONG newHeight = newRectSize->bottom - newRectSize->top;
SetWindowPos(hwnd,
nullptr,
newRectSize->left,
newRectSize->top,
newWidth,
newHeight,
SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
case WM_SIZE:
{
RECT rect = GetClientArea();
if (child_content_ != nullptr)
{
// Size and position the child window.
MoveWindow(child_content_,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
TRUE);
}
return 0;
}
case WM_ACTIVATE:
if (child_content_ != nullptr)
{
SetFocus(child_content_);
}
return 0;
}
return DefWindowProc(window_handle_, message, wparam, lparam);
}
void Win32Window::Destroy()
{
OnDestroy();
if (window_handle_)
{
DestroyWindow(window_handle_);
window_handle_ = nullptr;
}
if (g_active_window_count == 0)
{
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
}
}
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept
{
return reinterpret_cast<Win32Window*>(GetWindowLongPtr(window, GWLP_USERDATA));
}
void Win32Window::SetChildContent(HWND content)
{
child_content_ = content;
SetParent(content, window_handle_);
RECT frame = GetClientArea();
MoveWindow(content,
frame.left,
frame.top,
frame.right - frame.left,
frame.bottom - frame.top,
true);
SetFocus(child_content_);
}
RECT Win32Window::GetClientArea()
{
RECT frame;
GetClientRect(window_handle_, &frame);
return frame;
}
HWND Win32Window::GetHandle() { return window_handle_; }
void Win32Window::SetQuitOnClose(bool quit_on_close) { quit_on_close_ = quit_on_close; }
bool Win32Window::OnCreate()
{
// No-op; provided for subclasses.
return true;
}
void Win32Window::OnDestroy()
{
// No-op; provided for subclasses.
}

View File

@ -0,0 +1,98 @@
#ifndef RUNNER_WIN32_WINDOW_H_
#define RUNNER_WIN32_WINDOW_H_
#include <windows.h>
#include <functional>
#include <memory>
#include <string>
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
// inherited from by classes that wish to specialize with custom
// rendering and input handling
class Win32Window
{
public:
struct Point
{
unsigned int x;
unsigned int y;
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
};
struct Size
{
unsigned int width;
unsigned int height;
Size(unsigned int width, unsigned int height) : width(width), height(height) {}
};
Win32Window();
virtual ~Win32Window();
// Creates and shows a win32 window with |title| and position and size using
// |origin| and |size|. New windows are created on the default monitor. Window
// sizes are specified to the OS in physical pixels, hence to ensure a
// consistent size to will treat the width height passed in to this function
// as logical pixels and scale to appropriate for the default monitor. Returns
// true if the window was created successfully.
bool CreateAndShow(const std::wstring& title, const Point& origin, const Size& size);
// Release OS resources associated with window.
void Destroy();
// Inserts |content| into the window tree.
void SetChildContent(HWND content);
// Returns the backing Window handle to enable clients to set icon and other
// window properties. Returns nullptr if the window has been destroyed.
HWND GetHandle();
// If true, closing this window will quit the application.
void SetQuitOnClose(bool quit_on_close);
// Return a RECT representing the bounds of the current client area.
RECT GetClientArea();
protected:
// Processes and route salient window messages for mouse handling,
// size change and DPI. Delegates handling of these to member overloads that
// inheriting classes can handle.
virtual LRESULT MessageHandler(HWND window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Called when CreateAndShow is called, allowing subclass window-related
// setup. Subclasses should return false if setup fails.
virtual bool OnCreate();
// Called when Destroy is called.
virtual void OnDestroy();
private:
friend class WindowClassRegistrar;
// OS callback called by message pump. Handles the WM_NCCREATE message which
// is passed when the non-client area is being created and enables automatic
// non-client DPI scaling so that the non-client area automatically
// responsponds to changes in DPI. All other messages are handled by
// MessageHandler.
static LRESULT CALLBACK WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
bool quit_on_close_ = false;
// window handle for top level window.
HWND window_handle_ = nullptr;
// window handle for hosted content.
HWND child_content_ = nullptr;
};
#endif // RUNNER_WIN32_WINDOW_H_

38
ios/.gitignore vendored Normal file
View File

@ -0,0 +1,38 @@
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/ephemeral/
/Flutter/flutter_export_environment.sh

0
ios/Assets/.gitkeep Normal file
View File

4
ios/Classes/RivePlugin.h Normal file
View File

@ -0,0 +1,4 @@
#import <Flutter/Flutter.h>
@interface RivePlugin : NSObject <FlutterPlugin>
@end

15
ios/Classes/RivePlugin.m Normal file
View File

@ -0,0 +1,15 @@
#import "RivePlugin.h"
#if __has_include(<rive/rive-Swift.h>)
#import <rive/rive-Swift.h>
#else
// Support project import fallback if the generated compatibility header
// is not copied when this plugin is created as a library.
// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
#import "rive-Swift.h"
#endif
@implementation RivePlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[SwiftRivePlugin registerWithRegistrar:registrar];
}
@end

View File

@ -0,0 +1,14 @@
import Flutter
import UIKit
public class SwiftRivePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "rive", binaryMessenger: registrar.messenger())
let instance = SwiftRivePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion)
}
}

96
ios/rive.podspec Normal file
View File

@ -0,0 +1,96 @@
Pod::Spec.new do |s|
s.name = "rive"
s.version = "0.0.1"
s.summary = "Rive font abstraction."
s.description = <<-DESC
Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app.
DESC
s.homepage = "https://rive.app"
s.license = { :file => "../LICENSE" }
s.author = { "Rive" => "hello@rive.app" }
s.source = { :path => "." }
s.source_files = [
"Classes/**/*",
"rive_text/**/*.{cpp,hpp,c,h}",
"rive-cpp/src/math/raw_path.cpp",
"rive-cpp/src/math/mat2d.cpp",
"rive-cpp/src/rive_counter.cpp",
"rive-cpp/src/renderer.cpp",
"rive-cpp/src/text/font_hb.cpp",
"rive-cpp/src/text/line_breaker.cpp",
"harfbuzz/src/hb-aat-layout.cc",
"harfbuzz/src/hb-aat-map.cc",
"harfbuzz/src/hb-blob.cc",
"harfbuzz/src/hb-buffer-serialize.cc",
"harfbuzz/src/hb-buffer-verify.cc",
"harfbuzz/src/hb-buffer.cc",
"harfbuzz/src/hb-common.cc",
"harfbuzz/src/hb-draw.cc",
"harfbuzz/src/hb-face.cc",
"harfbuzz/src/hb-font.cc",
"harfbuzz/src/hb-map.cc",
"harfbuzz/src/hb-number.cc",
"harfbuzz/src/hb-ot-cff1-table.cc",
"harfbuzz/src/hb-ot-cff2-table.cc",
"harfbuzz/src/hb-ot-color.cc",
"harfbuzz/src/hb-ot-face.cc",
"harfbuzz/src/hb-ot-font.cc",
"harfbuzz/src/hb-ot-layout.cc",
"harfbuzz/src/hb-ot-map.cc",
"harfbuzz/src/hb-ot-math.cc",
"harfbuzz/src/hb-ot-meta.cc",
"harfbuzz/src/hb-ot-metrics.cc",
"harfbuzz/src/hb-ot-name.cc",
"harfbuzz/src/hb-ot-shape-complex-arabic.cc",
"harfbuzz/src/hb-ot-shape-complex-default.cc",
"harfbuzz/src/hb-ot-shape-complex-hangul.cc",
"harfbuzz/src/hb-ot-shape-complex-hebrew.cc",
"harfbuzz/src/hb-ot-shape-complex-indic-table.cc",
"harfbuzz/src/hb-ot-shape-complex-indic.cc",
"harfbuzz/src/hb-ot-shape-complex-khmer.cc",
"harfbuzz/src/hb-ot-shape-complex-myanmar.cc",
"harfbuzz/src/hb-ot-shape-complex-syllabic.cc",
"harfbuzz/src/hb-ot-shape-complex-thai.cc",
"harfbuzz/src/hb-ot-shape-complex-use.cc",
"harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc",
"harfbuzz/src/hb-ot-shape-fallback.cc",
"harfbuzz/src/hb-ot-shape-normalize.cc",
"harfbuzz/src/hb-ot-shape.cc",
"harfbuzz/src/hb-ot-tag.cc",
"harfbuzz/src/hb-ot-var.cc",
"harfbuzz/src/hb-set.cc",
"harfbuzz/src/hb-shape-plan.cc",
"harfbuzz/src/hb-shape.cc",
"harfbuzz/src/hb-shaper.cc",
"harfbuzz/src/hb-static.cc",
"harfbuzz/src/hb-subset-cff-common.cc",
"harfbuzz/src/hb-subset-cff1.cc",
"harfbuzz/src/hb-subset-cff2.cc",
"harfbuzz/src/hb-subset-input.cc",
"harfbuzz/src/hb-subset-plan.cc",
"harfbuzz/src/hb-subset-repacker.cc",
"harfbuzz/src/hb-subset.cc",
"harfbuzz/src/hb-ucd.cc",
"harfbuzz/src/hb-unicode.cc",
"SheenBidi/Headers/*.h",
"SheenBidi/Source/SheenBidi.c",
]
s.dependency "Flutter"
s.platform = :ios, "9.0"
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = {
"DEFINES_MODULE" => "YES",
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" => "i386",
"OTHER_CFLAGS" => "-DSB_CONFIG_UNITY -DWITH_RIVE_TEXT -DHAVE_OT -DHB_NO_FALLBACK_SHAPE -DHB_NO_WIN1256 -Wno-documentation -Wno-comma -Wno-unreachable-code -Wno-shorten-64-to-32",
"OTHER_CPLUSPLUSFLAGS" => "-DWITH_RIVE_TEXT -DHAVE_OT -DHB_NO_FALLBACK_SHAPE -DHB_NO_WIN1256 -Wno-conditional-uninitialized -Wno-documentation -Wno-comma -Wno-unreachable-code -Wno-shorten-64-to-32 -std=c++17",
"USER_HEADER_SEARCH_PATHS" => '"$(PODS_TARGET_SRCROOT)/SheenBidi/Headers" "$(PODS_TARGET_SRCROOT)/harfbuzz/src" "$(PODS_TARGET_SRCROOT)/rive-cpp/include" "$(PODS_TARGET_SRCROOT)/rive-cpp/skia/renderer/include"',
"OTHER_CPLUSPLUSFLAGS[config=Release]" => "-DNDEBUG -DWITH_RIVE_TEXT -DHAVE_OT -DHB_NO_FALLBACK_SHAPE -DHB_NO_WIN1256 -Wno-conditional-uninitialized -Wno-documentation -Wno-comma -Wno-unreachable-code -Wno-shorten-64-to-32 -std=c++17",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
"CLANG_CXX_LIBRARY" => "libc++",
}
s.swift_version = "5.0"
end

139
ios/rive_text/rive_text.cpp Normal file
View File

@ -0,0 +1,139 @@
#include <stdint.h>
#include <stdio.h>
#include "rive/text/font_hb.hpp"
#if defined(_MSC_VER)
#define EXPORT extern "C" __declspec(dllexport)
#else
#define EXPORT extern "C" __attribute__((visibility("default"))) __attribute__((used))
#endif
EXPORT
rive::Font* makeFont(const uint8_t* bytes, uint64_t length)
{
auto result = HBFont::Decode(rive::Span<const uint8_t>(bytes, length));
if (result)
{
auto ptr = result.release();
return ptr;
}
return nullptr;
}
EXPORT void deleteFont(rive::Font* font) { delete font; }
struct GlyphPath
{
rive::RawPath* rawPath;
rive::Vec2D* points;
rive::PathVerb* verbs;
uint16_t verbCount;
};
EXPORT
GlyphPath makeGlyphPath(rive::Font* font, rive::GlyphID id)
{
rive::RawPath* path = new rive::RawPath(font->getPath(id));
return {
.rawPath = path,
.points = path->points().data(),
.verbs = path->verbs().data(),
.verbCount = (uint16_t)path->verbs().size(),
};
}
EXPORT void deleteGlyphPath(rive::RawPath* rawPath) { delete rawPath; }
EXPORT
rive::SimpleArray<rive::Paragraph>*
shapeText(const uint32_t* text, uint64_t length, rive::TextRun* runs, uint64_t runsLength)
{
if (runsLength == 0 || length == 0)
{
return nullptr;
}
return new rive::SimpleArray<rive::Paragraph>(
runs[0].font->shapeText(rive::Span(text, length), rive::Span(runs, runsLength)));
}
EXPORT void deleteShapeResult(rive::SimpleArray<rive::Paragraph>* shapeResult)
{
delete shapeResult;
}
EXPORT rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>*
breakLines(rive::SimpleArray<rive::Paragraph>* paragraphs, float width, uint8_t align)
{
bool autoWidth = width == -1.0f;
float paragraphWidth = width;
rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>* lines =
new rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>(paragraphs->size());
rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>& linesRef = *lines;
size_t paragraphIndex = 0;
for (auto& para : *paragraphs)
{
linesRef[paragraphIndex] =
rive::GlyphLine::BreakLines(para.runs, autoWidth ? -1.0f : width);
if (autoWidth)
{
paragraphWidth =
std::max(paragraphWidth,
rive::GlyphLine::ComputeMaxWidth(linesRef[paragraphIndex], para.runs));
}
paragraphIndex++;
}
paragraphIndex = 0;
for (auto& para : *paragraphs)
{
rive::GlyphLine::ComputeLineSpacing(linesRef[paragraphIndex++],
para.runs,
paragraphWidth,
(rive::TextAlign)align);
}
return lines;
}
EXPORT void deleteLines(rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>* result)
{
delete result;
}
std::vector<rive::Font*> fallbackFonts;
EXPORT
void setFallbackFonts(rive::Font** fonts, uint64_t fontsLength)
{
if (fontsLength == 0)
{
fallbackFonts = std::vector<rive::Font*>();
return;
}
fallbackFonts = std::vector<rive::Font*>(fonts, fonts + fontsLength);
}
static rive::rcp<rive::Font> pickFallbackFont(rive::Span<const rive::Unichar> missing)
{
size_t length = fallbackFonts.size();
for (size_t i = 0; i < length; i++)
{
HBFont* font = static_cast<HBFont*>(fallbackFonts[i]);
if (i == length - 1 || font->hasGlyph(missing))
{
rive::rcp<rive::Font> rcFont = rive::rcp<rive::Font>(font);
// because the font was released at load time, we need to give it an
// extra ref whenever we bump it to a reference counted pointer.
rcFont->ref();
return rcFont;
}
}
return nullptr;
}
EXPORT
void init()
{
fallbackFonts.clear();
HBFont::gFallbackProc = pickFallbackFont;
}

15
lib/rive_web.dart Normal file
View File

@ -0,0 +1,15 @@
// In order to *not* need this ignore, consider extracting the "web" version
// of your plugin as a separate package, instead of inlining it in the same
// package as the core of your plugin.
// ignore: avoid_web_libraries_in_flutter
import 'dart:html' as html show window;
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
/// A web implementation of the RivePlatform of the Rive plugin.
class RivePlugin {
/// Constructs a RiveWeb
RivePlugin();
static void registerWith(Registrar registrar) {}
}

6
lib/src/platform.dart Normal file
View File

@ -0,0 +1,6 @@
import 'platform_native.dart' if (dart.library.html) 'platform_web.dart';
abstract class Platform {
bool get isTesting;
static final Platform instance = makePlatform();
}

View File

@ -0,0 +1,10 @@
import 'dart:io' as io show Platform;
import 'platform.dart';
Platform makePlatform() => PlatformNative();
class PlatformNative extends Platform {
@override
bool get isTesting => io.Platform.environment.containsKey('FLUTTER_TEST');
}

View File

@ -0,0 +1,8 @@
import 'platform.dart';
Platform makePlatform() => PlatformWeb();
class PlatformWeb extends Platform {
@override
bool get isTesting => false;
}

View File

@ -234,6 +234,12 @@ class Shape extends ShapeBase with ShapePaintContainer {
@override
void draw(ui.Canvas canvas) {
// canvas.draw(path, ui.Paint()..color = ui.Color(0xFFFF0000));
// canvas.drawCircle(
// ui.Offset.zero, 15, ui.Paint()..color = ui.Color(0xFFFF0000));
// shader = ui.Gradient.radial(ui.Offset.zero, 15.0,
// [ui.Color(0xFFFF0000), ui.Color(0x00FF0000)], [0.0, 1.0]));
// return;
bool clipped = clip(canvas);
var path = pathComposer.fillPath;
if (!_fillInWorld) {
@ -243,6 +249,7 @@ class Shape extends ShapeBase with ShapePaintContainer {
for (final fill in fills) {
fill.draw(canvas, path);
}
if (!_fillInWorld) {
canvas.restore();
}

319
lib/src/rive_text.dart Normal file
View File

@ -0,0 +1,319 @@
import 'dart:collection';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:rive/math.dart';
import 'rive_text_ffi.dart' if (dart.library.html) 'rive_text_wasm.dart';
enum RawPathVerb { move, line, quad, cubic, close }
abstract class RawPathCommand {
final RawPathVerb verb;
RawPathCommand(this.verb);
Vec2D point(int index);
}
abstract class RawPath with IterableMixin<RawPathCommand> {
void dispose();
void issueCommands(ui.Path path) {
for (final command in this) {
switch (command.verb) {
case RawPathVerb.move:
var p = command.point(0);
path.moveTo(p.x, p.y);
break;
case RawPathVerb.line:
var p = command.point(1);
path.lineTo(p.x, p.y);
break;
case RawPathVerb.quad:
var p1 = command.point(1);
var p2 = command.point(2);
path.quadraticBezierTo(p1.x, p1.y, p2.x, p2.y);
break;
case RawPathVerb.cubic:
var p1 = command.point(1);
var p2 = command.point(2);
var p3 = command.point(3);
path.cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
break;
case RawPathVerb.close:
path.close();
break;
}
}
}
}
enum TextDirection { ltr, rtl }
enum TextAlign { left, right, center }
abstract class Paragraph {
TextDirection get direction;
List<GlyphRun> get runs;
List<GlyphRun> get logicalRuns => runs;
List<GlyphRun> orderVisually(List<GlyphRun> glyphRuns) {
if (direction == TextDirection.ltr) {
return glyphRuns;
}
var visualOrder = <GlyphRun>[];
if (glyphRuns.isNotEmpty) {
var reversed = glyphRuns.reversed;
GlyphRun previous = reversed.first;
visualOrder.add(previous);
int ltrIndex = 0;
for (final run in reversed.skip(1)) {
if (run.direction == TextDirection.ltr &&
previous.direction == run.direction) {
visualOrder.insert(ltrIndex, run);
} else {
if (run.direction == TextDirection.ltr) {
ltrIndex = visualOrder.length;
}
visualOrder.add(run);
}
previous = run;
}
}
return visualOrder;
}
List<GlyphRun> get visualRuns => orderVisually(runs);
}
abstract class GlyphRun {
Font get font;
double get fontSize;
int get styleId;
int get glyphCount;
TextDirection get direction;
int glyphIdAt(int index);
int textIndexAt(int index);
double advanceAt(int index);
}
class LineRunGlyph {
final GlyphRun run;
final int index;
LineRunGlyph(this.run, this.index);
Float64List renderTransform(double x, double y) {
var scale = run.fontSize;
return Float64List.fromList([
scale,
0.0,
0.0,
0.0,
0.0,
scale,
0.0,
0.0,
0.0,
0.0,
1.0,
0.0,
x,
y,
0.0,
1.0
]);
}
@override
bool operator ==(Object other) =>
other is LineRunGlyph && other.run == run && other.index == index;
}
abstract class GlyphLine {
int get startRun;
int get startIndex;
int get endRun;
int get endIndex;
double get startX;
double get top;
double get baseline;
double get bottom;
/// Returns an iterator that traverses the glyphs in a line in visual order
/// taking into account both the paragraph's runs bidi order and the
/// individual glyphs bidi order within a run.
Iterable<LineRunGlyph> glyphs(Paragraph paragraph) sync* {
var displayRuns = <GlyphRun>[];
var glyphRuns = paragraph.runs;
int runIndex, bidiEndRun, runInc;
// if (paragraph.direction == TextDirection.rtl) {
// runIndex = endRun;
// bidiEndRun = startRun - 1;
// runInc = -1;
// } else {
runIndex = startRun;
bidiEndRun = endRun + 1;
runInc = 1;
// }
while (runIndex != bidiEndRun) {
var run = glyphRuns[runIndex];
displayRuns.add(run);
// int startGIndex = runIndex == startRun ? startIndex : 0;
// int endGIndex = runIndex == endRun ? endIndex : run.glyphCount;
// int j, end, inc;
// if (run.direction == TextDirection.rtl) {
// j = endGIndex - 1;
// end = startGIndex - 1;
// inc = -1;
// } else {
// j = startGIndex;
// end = endGIndex;
// inc = 1;
// }
// while (j != end) {
// yield LineRunGlyph(run, j);
// startGIndex = 0;
// j += inc;
// }
runIndex += runInc;
}
var startRunRef = displayRuns.first;
var endRunRef = displayRuns.last;
for (final run in paragraph.orderVisually(displayRuns)) {
int startGIndex = startRunRef == run ? startIndex : 0;
int endGIndex = endRunRef == run ? endIndex : run.glyphCount;
int j, end, inc;
if (run.direction == TextDirection.rtl) {
j = endGIndex - 1;
end = startGIndex - 1;
inc = -1;
} else {
j = startGIndex;
end = endGIndex;
inc = 1;
}
while (j != end) {
yield LineRunGlyph(run, j);
startGIndex = 0;
j += inc;
}
}
// var glyphRuns = paragraph.visualRuns;
// int runIndex, bidiEndRun, runInc;
// if (paragraph.direction == TextDirection.rtl) {
// runIndex = endRun;
// bidiEndRun = startRun - 1;
// runInc = -1;
// } else {
// runIndex = startRun;
// bidiEndRun = endRun + 1;
// runInc = 1;
// }
// while (runIndex != bidiEndRun) {
// var run = glyphRuns[runIndex];
// int startGIndex = runIndex == startRun ? startIndex : 0;
// int endGIndex = runIndex == endRun ? endIndex : run.glyphCount;
// int j, end, inc;
// if (run.direction == TextDirection.rtl) {
// j = endGIndex - 1;
// end = startGIndex - 1;
// inc = -1;
// } else {
// j = startGIndex;
// end = endGIndex;
// inc = 1;
// }
// while (j != end) {
// yield LineRunGlyph(run, j);
// startGIndex = 0;
// j += inc;
// }
// runIndex += runInc;
// }
}
}
abstract class BreakLinesResult extends ListBase<List<GlyphLine>> {
void dispose();
}
abstract class TextShapeResult {
List<Paragraph> get paragraphs;
void dispose();
BreakLinesResult breakLines(double width, TextAlign alignment);
}
/// A representation of a styled section of text in Rive.
class TextRun {
final Font font;
final double fontSize;
final int unicharCount;
final int styleId;
TextRun({
required this.font,
required this.fontSize,
required this.unicharCount,
this.styleId = 0,
});
TextRun copy({required int copyUnicharCount}) => TextRun(
font: font,
fontSize: fontSize,
unicharCount: copyUnicharCount,
styleId: styleId,
);
@override
String toString() => 'TextRun($fontSize:$unicharCount:$styleId)';
@override
bool operator ==(Object other) =>
other is TextRun &&
other.font == font &&
other.fontSize == fontSize &&
other.unicharCount == unicharCount &&
other.styleId == styleId;
@override
int get hashCode => Object.hash(font, fontSize, unicharCount, styleId);
}
abstract class Font {
static Future<void> initialize() => initFont();
static Font? decode(Uint8List bytes) {
return decodeFont(bytes);
}
static void setFallbacks(List<Font> fonts) {
setFallbackFonts(fonts);
}
RawPath getPath(int glyphId);
void dispose();
TextShapeResult shape(String text, List<TextRun> runs);
final HashMap<int, ui.Path> _glyphPaths = HashMap<int, ui.Path>();
ui.Path getUiPath(int glyphId) {
var glyphPath = _glyphPaths[glyphId];
if (glyphPath != null) {
return glyphPath;
}
var path = ui.Path();
var rawPath = getPath(glyphId);
rawPath.issueCommands(path);
rawPath.dispose();
_glyphPaths[glyphId] = path;
return path;
}
}

605
lib/src/rive_text_ffi.dart Normal file
View File

@ -0,0 +1,605 @@
import 'dart:collection';
import 'dart:ffi';
import 'dart:io';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:rive/math.dart';
import 'package:rive/src/platform.dart' as rive;
import 'package:rive/src/rive_text.dart';
final DynamicLibrary nativeLib = _loadLibrary();
DynamicLibrary _loadLibrary() {
if (rive.Platform.instance.isTesting) {
var paths = [
'',
'../../packages/rive_flutter/',
];
if (Platform.isMacOS) {
for (final path in paths) {
try {
return DynamicLibrary.open(
'${path}shared_lib/build/bin/debug/librive_text.dylib',
);
// ignore: avoid_catching_errors
} on ArgumentError catch (_) {}
}
} else if (Platform.isLinux) {
for (final path in paths) {
try {
return DynamicLibrary.open(
'${path}shared_lib/build/bin/debug/librive_text.so',
);
// ignore: avoid_catching_errors
} on ArgumentError catch (_) {}
}
}
}
if (Platform.isAndroid) {
return DynamicLibrary.open('librive_text.so');
} else if (Platform.isWindows) {
return DynamicLibrary.open('rive_plugin.dll');
}
return DynamicLibrary.process();
}
class PathPoint extends Struct {
@Float()
external double x;
@Float()
external double y;
@override
String toString() => '[$x, $y]';
}
class GlyphPathStruct extends Struct {
external Pointer<Void> rawPath;
external Pointer<PathPoint> points;
external Pointer<Uint8> verbs;
@Uint16()
external int verbCount;
}
class GlyphLineNative extends Struct {
@Uint32()
external int startRun;
@Uint32()
external int startIndex;
@Uint32()
external int endRun;
@Uint32()
external int endIndex;
@Float()
external double startX;
@Float()
external double top;
@Float()
external double baseline;
@Float()
external double bottom;
}
class GlyphLineFFI extends GlyphLine {
final GlyphLineNative nativeLine;
GlyphLineFFI(this.nativeLine);
@override
double get baseline => nativeLine.baseline;
@override
double get bottom => nativeLine.bottom;
@override
int get endIndex => nativeLine.endIndex;
@override
int get endRun => nativeLine.endRun;
@override
int get startIndex => nativeLine.startIndex;
@override
int get startRun => nativeLine.startRun;
@override
double get startX => nativeLine.startX;
@override
double get top => nativeLine.top;
}
class SimpleLineList extends Struct {
external Pointer<GlyphLineNative> data;
@Uint64()
external int size;
}
class SimpleLineDoubleList extends Struct {
external Pointer<SimpleLineList> data;
@Uint64()
external int size;
}
class SimpleUint16Array extends Struct {
external Pointer<Uint16> data;
@Uint64()
external int size;
}
class SimpleUint32Array extends Struct {
external Pointer<Uint32> data;
@Uint64()
external int size;
}
class SimpleFloatArray extends Struct {
external Pointer<Float> data;
@Uint64()
external int size;
}
class TextRunNative extends Struct {
external Pointer<Void> font;
@Float()
external double size;
@Uint32()
external int unicharCount;
@Uint32()
external int script;
@Uint16()
external int styleId;
@Uint8()
external int dir;
}
class SimpleGlyphRunArray extends Struct {
external Pointer<GlyphRunNative> data;
@Uint64()
external int size;
List<GlyphRunNative> toList() {
var list = <GlyphRunNative>[];
for (int i = 0; i < size; i++) {
list.add(data.elementAt(i).ref);
}
return list;
}
}
class GlyphRunNative extends Struct implements GlyphRun {
external Pointer<Void> fontPtr;
@Float()
external double size;
external SimpleUint16Array glyphs;
external SimpleUint32Array textIndices;
external SimpleFloatArray advances;
external SimpleFloatArray xpos;
external SimpleUint32Array breaks;
@override
@Uint16()
external int styleId;
@Uint8()
external int dir;
@override
double get fontSize => size;
@override
int get glyphCount => glyphs.size;
@override
int glyphIdAt(int index) => glyphs.data.elementAt(index).value;
@override
Font get font => FontFFI(fontPtr);
@override
int textIndexAt(int index) => textIndices.data.elementAt(index).value;
@override
double advanceAt(int index) => advances.data.elementAt(index).value;
@override
TextDirection get direction => TextDirection.values[dir];
}
class DynamicTextRunArray extends Struct {
external Pointer<GlyphRunNative> data;
@Uint64()
external int size;
}
class ParagraphNative extends Struct {
external SimpleGlyphRunArray runs;
@Uint8()
external int direction;
}
class SimpleParagraphArray extends Struct {
external Pointer<ParagraphNative> data;
@Uint64()
external int size;
List<Paragraph> toList() {
var list = <Paragraph>[];
for (int i = 0; i < size; i++) {
list.add(ParagraphFFI(data.elementAt(i).ref));
}
return list;
}
}
class ParagraphFFI extends Paragraph {
final ParagraphNative nativeParagraph;
@override
TextDirection get direction =>
TextDirection.values[nativeParagraph.direction];
@override
final List<GlyphRun> runs;
ParagraphFFI(this.nativeParagraph) : runs = nativeParagraph.runs.toList();
}
class ParagraphsListFFI extends ListBase<ParagraphFFI> {
final SimpleParagraphArray nativeList;
@override
int get length => nativeList.size;
ParagraphsListFFI._(this.nativeList);
@override
ParagraphFFI operator [](int index) =>
ParagraphFFI(nativeList.data.elementAt(index).ref);
@override
void operator []=(int index, ParagraphFFI value) {
throw UnsupportedError('Cannot set Paragraph on ParagraphList');
}
@override
set length(int newLength) {
throw UnsupportedError('Cannot set length on ParagraphList');
}
}
class LineList extends ListBase<GlyphLine> {
final SimpleLineList nativeList;
LineList(this.nativeList);
@override
int get length => nativeList.size;
@override
GlyphLine operator [](int index) =>
GlyphLineFFI(nativeList.data.elementAt(index).ref);
@override
void operator []=(int index, GlyphLine value) {
throw UnsupportedError('Cannot set glyphline on LineList');
}
@override
set length(int newLength) {
throw UnsupportedError('Cannot set length on LineList');
}
}
class LineDoubleList extends BreakLinesResult {
final Pointer<SimpleLineDoubleList> nativeDoubleListPtr;
final SimpleLineDoubleList nativeDoubleList;
@override
int get length => nativeDoubleList.size;
LineDoubleList(this.nativeDoubleListPtr)
: nativeDoubleList = nativeDoubleListPtr.ref;
@override
List<GlyphLine> operator [](int index) =>
LineList(nativeDoubleList.data.elementAt(index).ref);
@override
void operator []=(int index, List<GlyphLine> value) {
throw UnsupportedError('Cannot set list on LineDoubleList');
}
@override
set length(int newLength) {
throw UnsupportedError('Cannot set length on LineDoubleList');
}
@override
void dispose() => deleteLines(nativeDoubleListPtr);
}
class TextShapeResultFFI extends TextShapeResult {
final Pointer<SimpleParagraphArray> nativeResult;
TextShapeResultFFI(this.nativeResult)
: paragraphs = nativeResult.ref.toList();
@override
void dispose() {
deleteShapeResult(nativeResult);
}
@override
BreakLinesResult breakLines(double width, TextAlign alignment) {
return LineDoubleList(
breakLinesNative(nativeResult, width, alignment.index));
}
@override
final List<Paragraph> paragraphs;
}
final Pointer<SimpleParagraphArray> Function(Pointer<Uint32> text,
int textLength, Pointer<TextRunNative> runs, int runsLength) shapeText =
nativeLib
.lookup<
NativeFunction<
Pointer<SimpleParagraphArray> Function(Pointer<Uint32>, Uint64,
Pointer<TextRunNative>, Uint64)>>('shapeText')
.asFunction();
final void Function(Pointer<SimpleParagraphArray> font) deleteShapeResult =
nativeLib
.lookup<NativeFunction<Void Function(Pointer<SimpleParagraphArray>)>>(
'deleteShapeResult')
.asFunction();
final Pointer<SimpleLineDoubleList> Function(
Pointer<SimpleParagraphArray>, double width, int align)
breakLinesNative = nativeLib
.lookup<
NativeFunction<
Pointer<SimpleLineDoubleList> Function(
Pointer<SimpleParagraphArray>, Float, Uint8)>>('breakLines')
.asFunction();
final void Function(Pointer<SimpleLineDoubleList>) deleteLines = nativeLib
.lookup<NativeFunction<Void Function(Pointer<SimpleLineDoubleList>)>>(
'deleteLines')
.asFunction();
final Pointer<Void> Function(Pointer<Uint8> bytes, int count) makeFont =
nativeLib
.lookup<NativeFunction<Pointer<Void> Function(Pointer<Uint8>, Uint64)>>(
'makeFont')
.asFunction();
final void Function(Pointer<Void> font) deleteFont = nativeLib
.lookup<NativeFunction<Void Function(Pointer<Void>)>>('deleteFont')
.asFunction();
final GlyphPathStruct Function(
Pointer<Void> font,
int
glyphId) makeGlyphPath = nativeLib
.lookup<NativeFunction<GlyphPathStruct Function(Pointer<Void>, Uint16)>>(
'makeGlyphPath')
.asFunction();
final void Function(Pointer<Void> font) deleteGlyphPath = nativeLib
.lookup<NativeFunction<Void Function(Pointer<Void>)>>('deleteGlyphPath')
.asFunction();
final void Function() init =
nativeLib.lookup<NativeFunction<Void Function()>>('init').asFunction();
final Pointer<SimpleParagraphArray> Function(
Pointer<Pointer<Void>> fonts, int fontsLength) setFallbackFontsNative =
nativeLib
.lookup<
NativeFunction<
Pointer<SimpleParagraphArray> Function(
Pointer<Pointer<Void>>, Uint64)>>('setFallbackFonts')
.asFunction();
class RawPathCommandWasm extends RawPathCommand {
final Pointer<PathPoint> _points;
RawPathCommandWasm._(
RawPathVerb verb,
this._points,
) : super(verb);
@override
Vec2D point(int index) {
var ref = _points.elementAt(index).ref;
return Vec2D.fromValues(ref.x, ref.y);
}
}
RawPathVerb _verbFromNative(int nativeVerb) {
switch (nativeVerb) {
case 0:
return RawPathVerb.move;
case 1:
return RawPathVerb.line;
case 2:
return RawPathVerb.quad;
case 4:
return RawPathVerb.cubic;
case 5:
return RawPathVerb.close;
default:
throw Exception('Unexpected nativeVerb: $nativeVerb');
}
}
int _ptsAdvanceAfterVerb(RawPathVerb verb) {
switch (verb) {
case RawPathVerb.move:
return 1;
case RawPathVerb.line:
return 1;
case RawPathVerb.quad:
return 2;
case RawPathVerb.cubic:
return 3;
case RawPathVerb.close:
return 0;
default:
throw Exception('Unexpected nativeVerb: $verb');
}
}
int _ptsBacksetForVerb(RawPathVerb verb) {
switch (verb) {
case RawPathVerb.move:
return 0;
case RawPathVerb.line:
return -1;
case RawPathVerb.quad:
return -1;
case RawPathVerb.cubic:
return -1;
case RawPathVerb.close:
return -1;
default:
throw Exception('Unexpected nativeVerb: $verb');
}
}
class RawPathIterator extends Iterator<RawPathCommand> {
final GlyphPathStruct _native;
int _verbIndex = -1;
int _ptIndex = -1;
RawPathVerb _verb = RawPathVerb.move;
RawPathIterator._(this._native);
@override
RawPathCommand get current => RawPathCommandWasm._(
_verb,
_native.points.elementAt(_ptIndex + _ptsBacksetForVerb(_verb)),
);
@override
bool moveNext() {
if (++_verbIndex < _native.verbCount) {
_ptIndex += _ptsAdvanceAfterVerb(_verb);
_verb = _verbFromNative(_native.verbs.elementAt(_verbIndex).value);
return true;
}
return false;
}
}
class RawPathFFI extends RawPath {
final GlyphPathStruct _native;
RawPathFFI._(this._native);
@override
Iterator<RawPathCommand> get iterator => RawPathIterator._(_native);
@override
void dispose() => deleteGlyphPath(_native.rawPath);
}
/// A Font created and owned by Dart code. User is expected to call
/// dispose to release the font when they are done with it.
class StrongFontFFI extends FontFFI {
StrongFontFFI(Pointer<Void> ptr) : super(ptr);
@override
void dispose() => deleteFont(fontPtr);
}
/// A Font reference that should not be explicitly disposed by the user.
/// Returned while shaping.
class FontFFI extends Font {
Pointer<Void> fontPtr;
FontFFI(this.fontPtr);
@override
RawPath getPath(int glyphId) {
var glyphPath = makeGlyphPath(fontPtr, glyphId);
return RawPathFFI._(glyphPath);
}
@override
void dispose() {}
@override
TextShapeResult shape(String text, List<TextRun> runs) {
var textUni = text.codeUnits;
// Allocate and copy to runs memory.
var runsMemory =
calloc.allocate<TextRunNative>(runs.length * sizeOf<TextRunNative>());
int runIndex = 0;
for (final run in runs) {
runsMemory[runIndex++]
..font = (run.font as FontFFI).fontPtr
..size = run.fontSize
..script = 0
..unicharCount = run.unicharCount
..styleId = run.styleId
..dir = 0;
}
// Allocate and copy to text buffer.
var textBuffer = calloc.allocate<Uint32>(textUni.length * sizeOf<Uint32>());
for (int i = 0; i < textUni.length; i++) {
textBuffer[i] = textUni[i];
}
var shapeResult =
shapeText(textBuffer, textUni.length, runsMemory, runs.length);
// Free memory for structs passed into native that we no longer need.
calloc.free(textBuffer);
calloc.free(runsMemory);
return TextShapeResultFFI(shapeResult);
}
}
Font? decodeFont(Uint8List bytes) {
// Copy them to the native heap.
var pointer = calloc.allocate<Uint8>(bytes.length);
for (int i = 0; i < bytes.length; i++) {
pointer[i] = bytes[i];
}
// Pass the pointer in to a native method.
var result = makeFont(pointer, bytes.length);
calloc.free(pointer);
return FontFFI(result);
}
Future<void> initFont() async {
init();
}
void setFallbackFonts(List<Font> fonts) {
// Allocate and copy to fonts list memory.
var fontListMemory =
calloc.allocate<Pointer<Void>>(fonts.length * sizeOf<Pointer<Void>>());
int fontIndex = 0;
for (final font in fonts) {
fontListMemory[fontIndex++] = (font as FontFFI).fontPtr;
}
setFallbackFontsNative(fontListMemory, fonts.length);
// Free memory for structs passed into native that we no longer need.
calloc.free(fontListMemory);
}

551
lib/src/rive_text_wasm.dart Normal file
View File

@ -0,0 +1,551 @@
// ignore: avoid_web_libraries_in_flutter
import 'dart:async';
import 'dart:collection';
import 'dart:html' as html;
import 'dart:js' as js;
import 'dart:typed_data';
import 'package:rive/math.dart';
import 'package:rive/src/rive_text.dart';
import 'package:rive/src/rive_text_wasm_version.dart';
import 'package:rive/src/utilities/binary_buffer/binary_reader.dart';
import 'package:rive/src/utilities/binary_buffer/binary_writer.dart';
late js.JsFunction _makeFont;
late js.JsFunction _deleteFont;
late js.JsFunction _makeGlyphPath;
late js.JsFunction _deleteGlyphPath;
late js.JsFunction _shapeText;
late js.JsFunction _setFallbackFonts;
late js.JsFunction _deleteShapeResult;
late js.JsFunction _breakLines;
late js.JsFunction _deleteLines;
class RawPathWasm extends RawPath {
final Uint8List verbs;
final Float32List points;
RawPathWasm({
required this.verbs,
required this.points,
});
@override
void dispose() {}
@override
Iterator<RawPathCommand> get iterator => RawPathIterator._(verbs, points);
}
class RawPathCommandWasm extends RawPathCommand {
final Float32List _points;
final int _pointsOffset;
RawPathCommandWasm._(
RawPathVerb verb,
this._points,
this._pointsOffset,
) : super(verb);
@override
Vec2D point(int index) {
var base = _pointsOffset + index * 2;
return Vec2D.fromValues(_points[base], _points[base + 1]);
}
}
RawPathVerb _verbFromNative(int nativeVerb) {
switch (nativeVerb) {
case 0:
return RawPathVerb.move;
case 1:
return RawPathVerb.line;
case 2:
return RawPathVerb.quad;
case 4:
return RawPathVerb.cubic;
case 5:
return RawPathVerb.close;
default:
throw Exception('Unexpected nativeVerb: $nativeVerb');
}
}
int _ptsAdvanceAfterVerb(RawPathVerb verb) {
switch (verb) {
case RawPathVerb.move:
return 1;
case RawPathVerb.line:
return 1;
case RawPathVerb.quad:
return 2;
case RawPathVerb.cubic:
return 3;
case RawPathVerb.close:
return 0;
default:
throw Exception('Unexpected nativeVerb: $verb');
}
}
int _ptsBacksetForVerb(RawPathVerb verb) {
switch (verb) {
case RawPathVerb.move:
return 0;
case RawPathVerb.line:
return -1;
case RawPathVerb.quad:
return -1;
case RawPathVerb.cubic:
return -1;
case RawPathVerb.close:
return -1;
default:
throw Exception('Unexpected nativeVerb: $verb');
}
}
class RawPathIterator extends Iterator<RawPathCommand> {
final Uint8List verbs;
final Float32List points;
int _verbIndex = -1;
int _ptIndex = -1;
RawPathVerb _verb = RawPathVerb.move;
RawPathIterator._(this.verbs, this.points);
@override
RawPathCommand get current => RawPathCommandWasm._(
_verb,
points,
(_ptIndex + _ptsBacksetForVerb(_verb)) * 2,
);
@override
bool moveNext() {
if (++_verbIndex < verbs.length) {
_ptIndex += _ptsAdvanceAfterVerb(_verb);
_verb = _verbFromNative(verbs[_verbIndex]);
return true;
}
return false;
}
}
class GlyphLineWasm extends GlyphLine {
@override
final int startRun;
@override
final int startIndex;
@override
final int endRun;
@override
final int endIndex;
@override
final double startX;
@override
final double top;
@override
final double baseline;
@override
final double bottom;
GlyphLineWasm(ByteData data)
: startRun = data.getUint32(0, Endian.little),
startIndex = data.getUint32(4, Endian.little),
endRun = data.getUint32(8, Endian.little),
endIndex = data.getUint32(12, Endian.little),
startX = data.getFloat32(16, Endian.little),
top = data.getFloat32(20, Endian.little),
baseline = data.getFloat32(24, Endian.little),
bottom = data.getFloat32(28, Endian.little);
@override
String toString() {
return "GlyphLineWasm $startRun $startIndex $endRun $endIndex $startX $top $baseline $bottom";
}
}
class BreakLinesResultFFI extends BreakLinesResult {
final List<List<GlyphLine>> list;
BreakLinesResultFFI(this.list);
@override
int get length => list.length;
@override
set length(int value) => list.length = value;
@override
List<GlyphLine> operator [](int index) => list[index];
@override
void operator []=(int index, List<GlyphLine> value) => list[index] = value;
@override
void dispose() {}
}
class TextShapeResultWasm extends TextShapeResult {
final int shapeResultPtr;
@override
final List<ParagraphWasm> paragraphs;
TextShapeResultWasm(this.shapeResultPtr, this.paragraphs);
@override
void dispose() => _deleteShapeResult.apply(<dynamic>[shapeResultPtr]);
@override
BreakLinesResult breakLines(double width, TextAlign alignment) {
var result = _breakLines.apply(
<dynamic>[
shapeResultPtr,
width,
alignment.index,
],
) as js.JsObject;
var rawResult = result['rawResult'] as int;
var results = result['results'] as Uint8List;
const lineSize = 32;
var paragraphsList = ByteData.view(results.buffer, results.offsetInBytes)
.readDynamicArray(0);
var paragraphsLines = <List<GlyphLine>>[];
var pointerEnd = paragraphsList.size * 8;
for (var pointer = 0; pointer < pointerEnd; pointer += 8) {
var sublist = paragraphsList.data.readDynamicArray(pointer);
var lines = <GlyphLine>[];
var end = sublist.data.offsetInBytes + sublist.size * lineSize;
for (var lineOffset = sublist.data.offsetInBytes;
lineOffset < end;
lineOffset += lineSize) {
lines.add(
GlyphLineWasm(
ByteData.view(
sublist.data.buffer,
lineOffset,
),
),
);
}
paragraphsLines.add(lines);
}
_deleteLines.apply(
<dynamic>[
rawResult,
],
);
return BreakLinesResultFFI(paragraphsLines);
}
}
extension ByteDataWasm on ByteData {
WasmDynamicArray readDynamicArray(int offset) {
return WasmDynamicArray(
ByteData.view(buffer, getUint32(offset, Endian.little)),
getUint32(
offset + 4,
Endian.little,
),
);
}
Uint16List readUint16List(int offset, {bool clone = true}) {
var array = readDynamicArray(offset);
var list =
array.data.buffer.asUint16List(array.data.offsetInBytes, array.size);
if (clone) {
return Uint16List.fromList(list);
}
return list;
}
Uint32List readUint32List(int offset, {bool clone = true}) {
var array = readDynamicArray(offset);
var list =
array.data.buffer.asUint32List(array.data.offsetInBytes, array.size);
if (clone) {
return Uint32List.fromList(list);
}
return list;
}
Float32List readFloat32List(int offset, {bool clone = true}) {
var array = readDynamicArray(offset);
var list =
array.data.buffer.asFloat32List(array.data.offsetInBytes, array.size);
if (clone) {
return Float32List.fromList(list);
}
return list;
}
}
class WasmDynamicArray {
final ByteData data;
final int size;
WasmDynamicArray(this.data, this.size);
}
class LinesWasm extends ListBase<GlyphLineWasm> {
final WasmDynamicArray wasmDynamicArray;
LinesWasm(this.wasmDynamicArray);
@override
int get length => wasmDynamicArray.size;
@override
GlyphLineWasm operator [](int index) {
const lineSize = 4 + //startRun
4 + // startIndex
4 + // endRun
4 + // endIndex
4 + // startX
4 + // top
4 + // baseline
4; // bottom
var data = wasmDynamicArray.data;
return GlyphLineWasm(
ByteData.view(
data.buffer,
data.offsetInBytes + index * lineSize,
),
);
}
@override
void operator []=(int index, GlyphLineWasm value) {
throw UnsupportedError('Cannot set Line on LinesWasm array');
}
@override
set length(int newLength) {
throw UnsupportedError('Cannot set Line count on LinesWasm array');
}
}
class ParagraphWasm extends Paragraph {
final ByteData data;
@override
final TextDirection direction;
@override
final List<GlyphRunWasm> runs = [];
ParagraphWasm(this.data)
: direction = TextDirection.values[data.getUint8(8)] {
const runSize =
52; // see rive_text_bindings.cpp assertSomeAssumptions for explanation
var runsPointer = data.getUint32(0, Endian.little);
var runsCount = data.getUint32(4, Endian.little);
for (int i = 0, runPointer = runsPointer;
i < runsCount;
i++, runPointer += runSize) {
runs.add(GlyphRunWasm(ByteData.view(data.buffer, runPointer)));
}
}
}
class GlyphRunWasm extends GlyphRun {
final ByteData byteData;
final Uint16List glyphs;
final Uint32List textIndices;
final Float32List xPositions;
@override
final TextDirection direction;
@override
final int styleId;
@override
final Font font;
@override
final double fontSize;
GlyphRunWasm(this.byteData)
: font = FontWasm(byteData.getUint32(0, Endian.little)),
fontSize = byteData.getFloat32(4, Endian.little),
glyphs = byteData.readUint16List(8),
textIndices = byteData.readUint32List(16),
xPositions = byteData.readFloat32List(24),
styleId = byteData.getUint16(48, Endian.little),
direction = TextDirection.values[byteData.getUint8(50)];
@override
int get glyphCount => glyphs.length;
@override
int glyphIdAt(int index) => glyphs[index];
@override
int textIndexAt(int index) => textIndices[index];
@override
double advanceAt(int index) => xPositions[index];
}
/// A Font reference that should not be explicitly disposed by the user.
/// Returned while shaping.
class FontWasm extends Font {
final int fontPtr;
FontWasm(this.fontPtr);
@override
String toString() {
return 'FontWasm $fontPtr';
}
@override
void dispose() {}
@override
RawPath getPath(int glyphId) {
var object =
_makeGlyphPath.apply(<dynamic>[fontPtr, glyphId]) as js.JsObject;
var rawPathPtr = object['rawPath'] as int;
// The buffer for these share the WASM heap buffer, which is efficient but
// could also be lost in-between calls to WASM.
var verbs = object['verbs'] as Uint8List;
var points = object['points'] as Float32List;
// We copy the verb and points structures so we don't have to worry about
// the references being lost if the WASM heap is re-allocated.
var rawPath = RawPathWasm(
verbs: Uint8List.fromList(verbs),
points: Float32List.fromList(points),
);
// Immediately delete the native glyph's raw path.
_deleteGlyphPath.apply(<dynamic>[rawPathPtr]);
return rawPath;
}
static const int sizeOfNativeTextRun = 20;
@override
TextShapeResult shape(String text, List<TextRun> runs) {
var writer = BinaryWriter(
alignment: runs.length * sizeOfNativeTextRun,
);
for (final run in runs) {
writer.writeUint32((run.font as FontWasm).fontPtr);
writer.writeFloat32(run.fontSize);
writer.writeUint32(run.unicharCount);
writer.writeUint32(0); // script (unknown at this point)
writer.writeUint16(run.styleId);
writer.writeUint8(0); // dir (unknown at this point)
writer.writeUint8(0); // padding to word align struct
}
var result = _shapeText.apply(
<dynamic>[
Uint32List.fromList(text.codeUnits),
writer.uint8Buffer,
],
) as js.JsObject;
var rawResult = result['rawResult'] as int;
var results = result['results'] as Uint8List;
var reader = BinaryReader.fromList(results);
var paragraphsPointer = reader.readUint32();
var paragraphsSize = reader.readUint32();
var paragraphList = <ParagraphWasm>[];
const paragraphSize = 12; // runs = 8, direction = 1, padding = 3
for (int i = 0;
i < paragraphsSize;
i++, paragraphsPointer += paragraphSize) {
paragraphList
.add(ParagraphWasm(ByteData.view(results.buffer, paragraphsPointer)));
}
return TextShapeResultWasm(rawResult, paragraphList);
}
}
/// A Font created and owned by Dart code. User is expected to call
/// dispose to release the font when they are done with it.
class StrongFontWasm extends FontWasm {
StrongFontWasm(int fontPtr) : super(fontPtr);
@override
void dispose() => _deleteFont.apply(<dynamic>[fontPtr]);
}
Font? decodeFont(Uint8List bytes) {
int ptr = _makeFont.apply(<dynamic>[bytes]) as int;
if (ptr == 0) {
return null;
}
return StrongFontWasm(ptr);
}
Future<void> initFont() async {
var script = html.ScriptElement()
..src = const bool.fromEnvironment(
'LOCAL_RIVE_FLUTTER_WASM',
defaultValue: false,
)
? 'http://localhost:8282/release/rive_text.js'
: 'https://unpkg.com/@rive-app/flutter-wasm@$wasmVersion/build/bin/release/rive_text.js'
..type = 'application/javascript'
..defer = true;
html.document.body!.append(script);
await script.onLoad.first;
var initWasm = js.context['RiveText'] as js.JsFunction;
var promise = initWasm.apply(<dynamic>[]) as js.JsObject;
var thenFunction = promise['then'] as js.JsFunction;
var completer = Completer<void>();
thenFunction.apply(
<dynamic>[
(js.JsObject module) {
var init = module['init'] as js.JsFunction;
init.apply(<dynamic>[]);
_makeFont = module['makeFont'] as js.JsFunction;
_deleteFont = module['deleteFont'] as js.JsFunction;
_makeGlyphPath = module['makeGlyphPath'] as js.JsFunction;
_deleteGlyphPath = module['deleteGlyphPath'] as js.JsFunction;
_shapeText = module['shapeText'] as js.JsFunction;
_setFallbackFonts = module['setFallbackFonts'] as js.JsFunction;
_deleteShapeResult = module['deleteShapeResult'] as js.JsFunction;
_breakLines = module['breakLines'] as js.JsFunction;
_deleteLines = module['deleteLines'] as js.JsFunction;
completer.complete();
}
],
thisArg: promise,
);
return completer.future;
}
void setFallbackFonts(List<Font> fonts) {
_setFallbackFonts.apply(
<dynamic>[
Uint32List.fromList(
fonts
.cast<FontWasm>()
.map((font) => font.fontPtr)
.toList(growable: false),
),
],
);
}

View File

@ -0,0 +1 @@
const wasmVersion = '6.0.0';

View File

@ -0,0 +1,19 @@
import Cocoa
import FlutterMacOS
public class RivePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "rive", binaryMessenger: registrar.messenger)
let instance = RivePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString)
default:
result(FlutterMethodNotImplemented)
}
}
}

View File

@ -0,0 +1,10 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
}

94
macos/rive.podspec Normal file
View File

@ -0,0 +1,94 @@
Pod::Spec.new do |s|
s.name = "rive"
s.version = "0.0.1"
s.summary = "Rive text abstraction."
s.description = <<-DESC
Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app.
DESC
s.homepage = "https://rive.app"
s.license = { :file => "../LICENSE" }
s.author = { "Rive" => "hello@rive.app" }
s.source = { :path => "." }
s.source_files = [
"Classes/**/*",
"rive_text/**/*.{cpp,hpp,c,h}",
"rive-cpp/src/math/raw_path.cpp",
"rive-cpp/src/math/mat2d.cpp",
"rive-cpp/src/rive_counter.cpp",
"rive-cpp/src/renderer.cpp",
"rive-cpp/src/text/font_hb.cpp",
"rive-cpp/src/text/line_breaker.cpp",
"harfbuzz/src/hb-aat-layout.cc",
"harfbuzz/src/hb-aat-map.cc",
"harfbuzz/src/hb-blob.cc",
"harfbuzz/src/hb-buffer-serialize.cc",
"harfbuzz/src/hb-buffer-verify.cc",
"harfbuzz/src/hb-buffer.cc",
"harfbuzz/src/hb-common.cc",
"harfbuzz/src/hb-draw.cc",
"harfbuzz/src/hb-face.cc",
"harfbuzz/src/hb-font.cc",
"harfbuzz/src/hb-map.cc",
"harfbuzz/src/hb-number.cc",
"harfbuzz/src/hb-ot-cff1-table.cc",
"harfbuzz/src/hb-ot-cff2-table.cc",
"harfbuzz/src/hb-ot-color.cc",
"harfbuzz/src/hb-ot-face.cc",
"harfbuzz/src/hb-ot-font.cc",
"harfbuzz/src/hb-ot-layout.cc",
"harfbuzz/src/hb-ot-map.cc",
"harfbuzz/src/hb-ot-math.cc",
"harfbuzz/src/hb-ot-meta.cc",
"harfbuzz/src/hb-ot-metrics.cc",
"harfbuzz/src/hb-ot-name.cc",
"harfbuzz/src/hb-ot-shape-complex-arabic.cc",
"harfbuzz/src/hb-ot-shape-complex-default.cc",
"harfbuzz/src/hb-ot-shape-complex-hangul.cc",
"harfbuzz/src/hb-ot-shape-complex-hebrew.cc",
"harfbuzz/src/hb-ot-shape-complex-indic-table.cc",
"harfbuzz/src/hb-ot-shape-complex-indic.cc",
"harfbuzz/src/hb-ot-shape-complex-khmer.cc",
"harfbuzz/src/hb-ot-shape-complex-myanmar.cc",
"harfbuzz/src/hb-ot-shape-complex-syllabic.cc",
"harfbuzz/src/hb-ot-shape-complex-thai.cc",
"harfbuzz/src/hb-ot-shape-complex-use.cc",
"harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc",
"harfbuzz/src/hb-ot-shape-fallback.cc",
"harfbuzz/src/hb-ot-shape-normalize.cc",
"harfbuzz/src/hb-ot-shape.cc",
"harfbuzz/src/hb-ot-tag.cc",
"harfbuzz/src/hb-ot-var.cc",
"harfbuzz/src/hb-set.cc",
"harfbuzz/src/hb-shape-plan.cc",
"harfbuzz/src/hb-shape.cc",
"harfbuzz/src/hb-shaper.cc",
"harfbuzz/src/hb-static.cc",
"harfbuzz/src/hb-subset-cff-common.cc",
"harfbuzz/src/hb-subset-cff1.cc",
"harfbuzz/src/hb-subset-cff2.cc",
"harfbuzz/src/hb-subset-input.cc",
"harfbuzz/src/hb-subset-plan.cc",
"harfbuzz/src/hb-subset-repacker.cc",
"harfbuzz/src/hb-subset.cc",
"harfbuzz/src/hb-ucd.cc",
"harfbuzz/src/hb-unicode.cc",
"SheenBidi/Headers/*.h",
"SheenBidi/Source/SheenBidi.c",
]
s.dependency "FlutterMacOS"
s.platform = :osx, "10.11"
s.pod_target_xcconfig = {
"DEFINES_MODULE" => "YES",
"OTHER_CFLAGS" => "-DSB_CONFIG_UNITY -DWITH_RIVE_TEXT -DHAVE_OT -DHB_NO_FALLBACK_SHAPE -DHB_NO_WIN1256 -Wno-documentation -Wno-comma -Wno-unreachable-code -Wno-shorten-64-to-32",
"OTHER_CPLUSPLUSFLAGS" => "-DWITH_RIVE_TEXT -DHAVE_OT -DHB_NO_FALLBACK_SHAPE -DHB_NO_WIN1256 -Wno-conditional-uninitialized -Wno-documentation -Wno-comma -Wno-unreachable-code -Wno-shorten-64-to-32 -std=c++17",
"USER_HEADER_SEARCH_PATHS" => '"$(PODS_TARGET_SRCROOT)/SheenBidi/Headers" "$(PODS_TARGET_SRCROOT)/harfbuzz/src" "$(PODS_TARGET_SRCROOT)/rive-cpp/include" "$(PODS_TARGET_SRCROOT)/rive-cpp/skia/renderer/include"',
"OTHER_CPLUSPLUSFLAGS[config=Release]" => "-DNDEBUG -DWITH_RIVE_TEXT -DHAVE_OT -DHB_NO_FALLBACK_SHAPE -DHB_NO_WIN1256 -Wno-conditional-uninitialized -Wno-documentation -Wno-comma -Wno-unreachable-code -Wno-shorten-64-to-32 -std=c++17",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
"CLANG_CXX_LIBRARY" => "libc++",
}
s.swift_version = "5.0"
end

View File

@ -0,0 +1,135 @@
#include <stdint.h>
#include <stdio.h>
#include "rive/text/font_hb.hpp"
#define EXPORT extern "C" __attribute__((visibility("default"))) __attribute__((used))
EXPORT
rive::Font* makeFont(const uint8_t* bytes, uint64_t length)
{
auto result = HBFont::Decode(rive::Span<const uint8_t>(bytes, length));
if (result)
{
auto ptr = result.release();
return ptr;
}
return nullptr;
}
EXPORT void deleteFont(rive::Font* font) { delete font; }
struct GlyphPath
{
rive::RawPath* rawPath;
rive::Vec2D* points;
rive::PathVerb* verbs;
uint16_t verbCount;
};
EXPORT
GlyphPath makeGlyphPath(rive::Font* font, rive::GlyphID id)
{
rive::RawPath* path = new rive::RawPath(font->getPath(id));
return {
.rawPath = path,
.points = path->points().data(),
.verbs = path->verbs().data(),
.verbCount = (uint16_t)path->verbs().size(),
};
}
EXPORT void deleteGlyphPath(rive::RawPath* rawPath) { delete rawPath; }
EXPORT
rive::SimpleArray<rive::Paragraph>*
shapeText(const uint32_t* text, uint64_t length, rive::TextRun* runs, uint64_t runsLength)
{
if (runsLength == 0 || length == 0)
{
return nullptr;
}
return new rive::SimpleArray<rive::Paragraph>(
runs[0].font->shapeText(rive::Span(text, length), rive::Span(runs, runsLength)));
}
EXPORT void deleteShapeResult(rive::SimpleArray<rive::Paragraph>* shapeResult)
{
delete shapeResult;
}
EXPORT rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>*
breakLines(rive::SimpleArray<rive::Paragraph>* paragraphs, float width, uint8_t align)
{
bool autoWidth = width == -1.0f;
float paragraphWidth = width;
rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>* lines =
new rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>(paragraphs->size());
rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>& linesRef = *lines;
size_t paragraphIndex = 0;
for (auto& para : *paragraphs)
{
linesRef[paragraphIndex] =
rive::GlyphLine::BreakLines(para.runs, autoWidth ? -1.0f : width);
if (autoWidth)
{
paragraphWidth =
std::max(paragraphWidth,
rive::GlyphLine::ComputeMaxWidth(linesRef[paragraphIndex], para.runs));
}
paragraphIndex++;
}
paragraphIndex = 0;
for (auto& para : *paragraphs)
{
rive::GlyphLine::ComputeLineSpacing(linesRef[paragraphIndex++],
para.runs,
paragraphWidth,
(rive::TextAlign)align);
}
return lines;
}
EXPORT void deleteLines(rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>* result)
{
delete result;
}
std::vector<rive::Font*> fallbackFonts;
EXPORT
void setFallbackFonts(rive::Font** fonts, uint64_t fontsLength)
{
if (fontsLength == 0)
{
fallbackFonts = std::vector<rive::Font*>();
return;
}
fallbackFonts = std::vector<rive::Font*>(fonts, fonts + fontsLength);
}
static rive::rcp<rive::Font> pickFallbackFont(rive::Span<const rive::Unichar> missing)
{
size_t length = fallbackFonts.size();
for (size_t i = 0; i < length; i++)
{
HBFont* font = static_cast<HBFont*>(fallbackFonts[i]);
if (i == length - 1 || font->hasGlyph(missing))
{
rive::rcp<rive::Font> rcFont = rive::rcp<rive::Font>(font);
// because the font was released at load time, we need to give it an
// extra ref whenever we bump it to a reference counted pointer.
rcFont->ref();
return rcFont;
}
}
return nullptr;
}
EXPORT
void init()
{
fallbackFonts.clear();
HBFont::gFallbackProc = pickFallbackFont;
}

View File

@ -0,0 +1,35 @@
# Platform Considerations
In order to support some of our more low level features, Rive brings some of its C++ runtime to Flutter.
| Platform | Technology | Dependencies |
| ------------- | ------------- | ------------- |
| iOS | FFI | statically linked |
| Android | FFI | rive_text.so |
| Windows | FFI | rive_plugin.dll |
| Mac | FFI | statically linked |
| Web | WASM | rive_text.js, rive_text.wasm |
## iOS & Mac
We use cocoapods to build and statically link to your project the portions of Rive's C++ runtime that are necessary for text features.
## Android
We use Gradle & CMake to build rive_text.so. Rive's runtime uses modern features that are only available on newer NDKs, for this reason we recommend updating your build.gradle to include ndkVersion 25.1.8937393
```
android {
compileSdkVersion 31
ndkVersion "25.1.8937393"
...
}
```
## Windows
We use CMake to build rive_plugin.dll. Note that Clang compiler is required, see here for how to enable it in your Visual Studio:
https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-170
## Web
We use emscripten to build a wasm and js file which are statically served via unpkg similarly to how Flutter delivers the CanvasKit wasm file.

340
premake5_rive_plugin.lua Normal file
View File

@ -0,0 +1,340 @@
local dependency = require 'dependency'
harfbuzz = dependency.github('harfbuzz/harfbuzz', '858570b1d9912a1b746ab39fbe62a646c4f7a5b1')
sheenbidi = dependency.github('Tehreer/SheenBidi', 'v2.6')
workspace 'rive_text'
configurations {'debug', 'release'}
source = os.isdir('../../packages/runtime') and '../../packages/runtime' or 'macos/rive-cpp'
project 'rive_sheenbidi'
do
kind 'StaticLib'
language 'C'
toolset 'clang'
targetdir 'shared_lib/build/bin/%{cfg.buildcfg}/'
objdir 'shared_lib/build/obj/%{cfg.buildcfg}/'
includedirs {
sheenbidi .. '/Headers'
}
filter {'options:arch=wasm'}
do
targetdir 'wasm/build/bin/%{cfg.buildcfg}/'
objdir 'wasm/build/obj/%{cfg.buildcfg}/'
end
filter 'configurations:debug'
do
files {
sheenbidi .. '/Source/BidiChain.c',
sheenbidi .. '/Source/BidiTypeLookup.c',
sheenbidi .. '/Source/BracketQueue.c',
sheenbidi .. '/Source/GeneralCategoryLookup.c',
sheenbidi .. '/Source/IsolatingRun.c',
sheenbidi .. '/Source/LevelRun.c',
sheenbidi .. '/Source/PairingLookup.c',
sheenbidi .. '/Source/RunQueue.c',
sheenbidi .. '/Source/SBAlgorithm.c',
sheenbidi .. '/Source/SBBase.c',
sheenbidi .. '/Source/SBCodepointSequence.c',
sheenbidi .. '/Source/SBLine.c',
sheenbidi .. '/Source/SBLog.c',
sheenbidi .. '/Source/SBMirrorLocator.c',
sheenbidi .. '/Source/SBParagraph.c',
sheenbidi .. '/Source/SBScriptLocator.c',
sheenbidi .. '/Source/ScriptLookup.c',
sheenbidi .. '/Source/ScriptStack.c',
sheenbidi .. '/Source/StatusStack.c'
}
end
filter 'configurations:release'
do
files {
sheenbidi .. '/Source/SheenBidi.c'
}
end
buildoptions {
'-Wall',
'-ansi',
'-pedantic',
'-DANSI_DECLARATORS'
}
buildoptions {
'-Wno-c++17-extensions',
'-fno-exceptions',
'-fno-rtti',
'-fno-unwind-tables',
'-Wno-deprecated-builtins'
}
filter {'options:arch=wasm'}
do
buildoptions {
'-s STRICT=1',
'-s DISABLE_EXCEPTION_CATCHING=1',
'-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0',
'--no-entry'
}
end
filter 'configurations:debug'
do
defines {'DEBUG'}
symbols 'On'
end
filter 'configurations:release'
do
defines {'RELEASE', 'NDEBUG', 'SB_CONFIG_UNITY'}
optimize 'On'
buildoptions {
'-Oz',
'-g0',
'-flto'
}
linkoptions {
'-Oz',
'-g0',
'-flto'
}
end
filter {'system:macosx', 'options:arch=arm64'}
do
buildoptions {'-target arm64-apple-macos11'}
linkoptions {'-target arm64-apple-macos11'}
end
filter {'system:macosx', 'options:arch=x86_64'}
do
buildoptions {'-target x86_64-apple-macos10.12'}
linkoptions {'-target x86_64-apple-macos10.12'}
end
end
project 'rive_text'
do
kind 'SharedLib'
language 'C++'
cppdialect 'C++17'
toolset 'clang'
targetdir('shared_lib/build/bin/%{cfg.buildcfg}')
objdir('shared_lib/build/obj/%{cfg.buildcfg}')
dependson {
'rive_sheenbidi'
}
defines {
'WITH_RIVE_TEXT',
'HAVE_OT',
'HB_NO_FALLBACK_SHAPE',
'HB_NO_WIN1256'
}
includedirs {
source .. '/include',
source .. '/skia/renderer/include',
harfbuzz .. '/src/',
sheenbidi .. '/Headers'
}
files {
source .. '/src/renderer.cpp',
source .. '/src/rive_counter.cpp',
source .. '/src/math/mat2d.cpp',
source .. '/src/math/raw_path.cpp',
source .. '/src/text/font_hb.cpp',
source .. '/src/text/line_breaker.cpp',
harfbuzz .. '/src/hb-aat-layout.cc',
harfbuzz .. '/src/hb-aat-map.cc',
harfbuzz .. '/src/hb-blob.cc',
harfbuzz .. '/src/hb-buffer-serialize.cc',
harfbuzz .. '/src/hb-buffer-verify.cc',
harfbuzz .. '/src/hb-buffer.cc',
harfbuzz .. '/src/hb-common.cc',
harfbuzz .. '/src/hb-draw.cc',
harfbuzz .. '/src/hb-face.cc',
harfbuzz .. '/src/hb-font.cc',
harfbuzz .. '/src/hb-map.cc',
harfbuzz .. '/src/hb-number.cc',
harfbuzz .. '/src/hb-ot-cff1-table.cc',
harfbuzz .. '/src/hb-ot-cff2-table.cc',
harfbuzz .. '/src/hb-ot-color.cc',
harfbuzz .. '/src/hb-ot-face.cc',
harfbuzz .. '/src/hb-ot-font.cc',
harfbuzz .. '/src/hb-ot-layout.cc',
harfbuzz .. '/src/hb-ot-map.cc',
harfbuzz .. '/src/hb-ot-math.cc',
harfbuzz .. '/src/hb-ot-meta.cc',
harfbuzz .. '/src/hb-ot-metrics.cc',
harfbuzz .. '/src/hb-ot-name.cc',
harfbuzz .. '/src/hb-ot-shape-complex-arabic.cc',
harfbuzz .. '/src/hb-ot-shape-complex-default.cc',
harfbuzz .. '/src/hb-ot-shape-complex-hangul.cc',
harfbuzz .. '/src/hb-ot-shape-complex-hebrew.cc',
harfbuzz .. '/src/hb-ot-shape-complex-indic-table.cc',
harfbuzz .. '/src/hb-ot-shape-complex-indic.cc',
harfbuzz .. '/src/hb-ot-shape-complex-khmer.cc',
harfbuzz .. '/src/hb-ot-shape-complex-myanmar.cc',
harfbuzz .. '/src/hb-ot-shape-complex-syllabic.cc',
harfbuzz .. '/src/hb-ot-shape-complex-thai.cc',
harfbuzz .. '/src/hb-ot-shape-complex-use.cc',
harfbuzz .. '/src/hb-ot-shape-complex-vowel-constraints.cc',
harfbuzz .. '/src/hb-ot-shape-fallback.cc',
harfbuzz .. '/src/hb-ot-shape-normalize.cc',
harfbuzz .. '/src/hb-ot-shape.cc',
harfbuzz .. '/src/hb-ot-tag.cc',
harfbuzz .. '/src/hb-ot-var.cc',
harfbuzz .. '/src/hb-set.cc',
harfbuzz .. '/src/hb-shape-plan.cc',
harfbuzz .. '/src/hb-shape.cc',
harfbuzz .. '/src/hb-shaper.cc',
harfbuzz .. '/src/hb-static.cc',
harfbuzz .. '/src/hb-subset-cff-common.cc',
harfbuzz .. '/src/hb-subset-cff1.cc',
harfbuzz .. '/src/hb-subset-cff2.cc',
harfbuzz .. '/src/hb-subset-input.cc',
harfbuzz .. '/src/hb-subset-plan.cc',
harfbuzz .. '/src/hb-subset-repacker.cc',
harfbuzz .. '/src/hb-subset.cc',
harfbuzz .. '/src/hb-ucd.cc',
harfbuzz .. '/src/hb-unicode.cc'
}
links {
'rive_sheenbidi'
}
buildoptions {
'-Wno-c++17-extensions',
'-fno-exceptions',
'-fno-rtti',
'-fno-unwind-tables',
'-Wno-deprecated-builtins',
'-DANSI_DECLARATORS'
}
filter {'not options:arch=wasm'}
do
files {
'macos/rive_text/rive_text.cpp'
}
end
filter {'options:arch=wasm'}
do
targetdir 'wasm/build/bin/%{cfg.buildcfg}/'
objdir 'wasm/build/obj/%{cfg.buildcfg}/'
kind 'ConsoleApp'
files {
'wasm/rive_text_bindings.cpp'
}
buildoptions {
'-s STRICT=1',
'-s DISABLE_EXCEPTION_CATCHING=1',
'-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0',
'--no-entry'
}
linkoptions {
'--closure 1',
'--closure-args="--externs ./wasm/js/externs.js"',
'--bind',
'-s FORCE_FILESYSTEM=0',
'-s MODULARIZE=1',
'-s NO_EXIT_RUNTIME=1',
'-s STRICT=1',
'-s ALLOW_MEMORY_GROWTH=1',
'-s DISABLE_EXCEPTION_CATCHING=1',
'-s WASM=1',
'-s USE_ES6_IMPORT_META=0',
'-s EXPORT_NAME="RiveText"',
'-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0',
'--no-entry',
'--pre-js ./wasm/js/rive_text.js'
}
end
linkoptions {
'-Wno-c++17-extensions',
'-fno-exceptions',
'-fno-rtti',
'-fno-unwind-tables'
}
filter {'options:arch=wasm', 'options:single_file'}
do
linkoptions {
'-o %{cfg.targetdir}/rive_text_single.js',
'-s SINGLE_FILE=1'
}
end
filter {'options:arch=wasm', 'options:not single_file'}
do
linkoptions {
'-o %{cfg.targetdir}/rive_text.js'
}
end
filter 'configurations:debug'
do
defines {'DEBUG'}
symbols 'On'
end
filter {'configurations:debug', 'options:arch=wasm'}
do
linkoptions {'-s ASSERTIONS=1'}
end
filter 'configurations:release'
do
defines {'RELEASE'}
defines {'NDEBUG'}
optimize 'On'
buildoptions {
'-Oz',
'-g0',
'-flto'
}
linkoptions {
'-Oz',
'-g0',
'-flto'
}
end
filter {'configurations:release', 'options:arch=wasm'}
do
linkoptions {'-s ASSERTIONS=0'}
end
filter {'system:macosx', 'options:arch=arm64'}
do
buildoptions {'-target arm64-apple-macos11'}
linkoptions {'-target arm64-apple-macos11'}
end
filter {'system:macosx', 'options:arch=x86_64'}
do
buildoptions {'-target x86_64-apple-macos10.12'}
linkoptions {'-target x86_64-apple-macos10.12'}
end
end
newoption {
trigger = 'single_file',
description = 'Set when the wasm should be packed in with the js code.'
}
newoption {
trigger = 'arch',
description = 'Architectures we can target',
allowed = {{'x86_64'}, {'arm64'}, {'wasm'}}
}

View File

@ -1,20 +1,37 @@
name: rive
description: Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app.
version: 0.9.1
repository: https://github.com/rive-app/rive-flutter
homepage: https://rive.app
description: Rive 2 Flutter Runtime. This package provides runtime functionality for playing back and interacting with animations built with the Rive editor available at https://rive.app.
repository: https://github.com/rive-app/rive-flutter
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=2.5.0"
dependencies:
collection: ^1.15.0
flutter:
sdk: flutter
ffi: ^2.0.1
graphs: ^2.0.0
http: ^0.13.3
meta: ^1.3.0
plugin_platform_interface: ^2.0.2
flutter:
sdk: flutter
flutter_web_plugins:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
plugin:
platforms:
android:
package: app.rive.rive
pluginClass: RivePlugin
macos:
pluginClass: RivePlugin
windows:
pluginClass: RivePlugin
ios:
pluginClass: RivePlugin
web:
pluginClass: RivePlugin
fileName: rive_web.dart

59
shared_lib/build_shared.sh Executable file
View File

@ -0,0 +1,59 @@
#!/bin/bash
set -e
CONFIG=debug
SINGLE=
for var in "$@"; do
if [[ $var = "release" ]]; then
CONFIG=release
fi
done
unameOut="$(uname -s)"
case "${unameOut}" in
Linux*) MACHINE=linux ;;
Darwin*) MACHINE=mac ;;
CYGWIN*) MACHINE=cygwin ;;
MINGW*) MACHINE=mingw ;;
*) MACHINE="UNKNOWN:${unameOut}" ;;
esac
pushd ../
./update_dependencies.sh
popd
if [[ ! -f "bin/premake5" ]]; then
mkdir -p bin
pushd bin
echo Downloading Premake5
if [ "$MACHINE" = 'mac' ]; then
PREMAKE_URL=https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-macosx.tar.gz
else
PREMAKE_URL=https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-linux.tar.gz
fi
curl $PREMAKE_URL -L -o premake.tar.gz
# Export premake5 into bin
tar -xvf premake.tar.gz 2>/dev/null
# Delete downloaded archive
rm premake.tar.gz
popd
fi
export PREMAKE=bin/premake5
$PREMAKE --scripts=../macos/rive-cpp/build --file=../premake5_rive_plugin.lua gmake2 --arch=x86_64
cd ..
for var in "$@"; do
if [[ $var = "clean" ]]; then
make clean
make config=release clean
fi
done
make config=$CONFIG -j$(($(sysctl -n hw.physicalcpu) + 1))
if [ "$MACHINE" = 'mac' ]; then
du -hs shared_lib/build/bin/debug/librive_text.dylib
else
du -hs shared_lib/build/bin/debug/librive_text.so
fi

BIN
test/assets/RobotoFlex.ttf Normal file

Binary file not shown.

44
test/text_test.dart Normal file
View File

@ -0,0 +1,44 @@
import 'package:flutter_test/flutter_test.dart';
// ignore: implementation_imports
import 'package:rive/src/rive_text.dart';
import 'src/utils.dart';
void main() {
test('text shaping works', () async {
await Font.initialize();
final bytes = loadFile('assets/RobotoFlex.ttf');
expect(bytes.lengthInBytes, 1654412);
var roboto = Font.decode(bytes.buffer.asUint8List());
expect(roboto, isNotNull);
var text = 'ffi test';
var runs = [
TextRun(
font: roboto!,
fontSize: 32.0,
unicharCount: text.length,
styleId: 0,
)
];
var result = runs.first.font.shape(text, runs);
expect(result.paragraphs.length, 1);
expect(result.paragraphs.first.runs.length, 1);
var glyphRun = result.paragraphs.first.runs.first;
// ffi gets ligated as a single glyph
expect(glyphRun.glyphCount, 6);
expect(glyphRun.textIndexAt(0), 0); // ffi
expect(glyphRun.textIndexAt(1), 3); // space after ffi
var breakLinesResult = result.breakLines(60, TextAlign.left);
expect(breakLinesResult.length, 1); // 1 paragraph
expect(breakLinesResult.first.length, 2); // 2 lines in the paragraph
// first line shows first glyph on the left
expect(breakLinesResult.first.first.startIndex, 0);
// second line shows third glyph on the left, which is the start of 'test'
expect(breakLinesResult.first[1].startIndex, 2);
});
}

81
update_dependencies.sh Executable file
View File

@ -0,0 +1,81 @@
#!/bin/bash
set -e
# silence push/pop
pushd() {
command pushd "$@" >/dev/null
}
popd() {
command popd "$@" >/dev/null
}
FORCE=false
for var in "$@"; do
if [[ $var = "force" ]]; then
FORCE=true
fi
done
function installRiveCpp {
if [ $FORCE == "true" ] || [ ! -d rive-cpp ]; then
rm -fR rive-cpp
if [ -d ../../runtime ]; then
echo "Getting rive-cpp from current repo."
export INSTALL_TO=$PWD
mkdir -p rive-cpp
# cp -fR ../../runtime rive-cpp
# git clone machine1:/path/to/project machine2:/target/path
pushd ../../runtime
function copyRepoFile {
mkdir -p $(dirname $INSTALL_TO/rive-cpp/$1)
cp $1 $INSTALL_TO/rive-cpp/$1
echo -en "\r\033[K$1"
}
export -f copyRepoFile
git ls-files | xargs -n1 bash -c 'copyRepoFile "$@"' _
echo -en "\r\033[K"
popd
else
echo "Cloning rive-cpp."
git clone https://github.com/rive-app/rive-cpp
fi
# TODO: Fix this so we build the rive.podspec file based on paths determined
# here (for harfbuzz and sheenbidi)
#
# install dependencies from rive-cpp
# pushd rive-cpp/dependencies/macosx
# source config_directories.sh
# popd
# pushd rive-cpp
# ./build.sh
# popd
fi
# For now just manually install the deps.
if [ $FORCE == "true" ] || [ ! -d harfbuzz ]; then
rm -fR harfbuzz
echo "Cloning Harfbuzz."
git clone https://github.com/harfbuzz/harfbuzz
pushd harfbuzz
git checkout 858570b1d9912a1b746ab39fbe62a646c4f7a5b1 .
popd
fi
if [ $FORCE == "true" ] || [ ! -d SheenBidi ]; then
rm -fR SheenBidi
echo "Cloning Harfbuzz."
git clone https://github.com/Tehreer/SheenBidi.git
pushd SheenBidi
git checkout v2.6 .
popd
fi
}
pushd macos
installRiveCpp
popd
pushd ios
installRiveCpp
popd

1
wasm/.npmrc Normal file
View File

@ -0,0 +1 @@
tag-version-prefix=""

30
wasm/README.md Normal file
View File

@ -0,0 +1,30 @@
# Rive Flutter WASM
This folder contains the WASM portion of the Rive Flutter runtime.
## Delivered via Unpkg
This is published to NPM so that Flutter Web projects can all benefit from common caching of the WASM file via unpkg (similar to Flutter's same strategy for CanvasKit).
## Local Development
For local development the Flutter Runtime can be configured to use a local dev server. Steps are as follows:
```
cd wasm
npm run serve
```
Run any project depending on rive-flutter with a `--dart-define` argument to instruct the runtime to look for the local dev server.
```
flutter run --dart-define=LOCAL_RIVE_FLUTTER_WASM=true -d chrome
```
As you make changes to the C++ codebase, recompile with:
```
./build_wasm.sh
```
Refresh or Hot Restart the Flutter Web project.

View File

@ -0,0 +1,70 @@
var RiveText = (() => {
var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
return (
function(RiveText) {
RiveText = RiveText || {};
var f;f||(f=typeof RiveText !== 'undefined' ? RiveText : {});var aa,r;f.ready=new Promise(function(a,b){aa=a;r=b});
RiveText.onRuntimeInitialized=function(){var a=RiveText.makeGlyphPath;RiveText.makeGlyphPath=function(d,e){d=a(d,e);e=d[1];var h=d[2];h=RiveText.HEAPU8.subarray(h,h+d[3]);let l=0;for(var g of h)switch(g){case 0:case 1:l++;break;case 2:l+=2;break;case 4:l+=3}g=e/4;return{rawPath:d[0],verbs:h,points:RiveText.HEAPF32.subarray(g,g+2*l)}};var b=RiveText.shapeText;RiveText.shapeText=function(d,e){d=b(d,e);return{rawResult:d,results:RiveText.HEAPU8.subarray(d)}};var c=RiveText.breakLines;RiveText.breakLines=
function(d,e,h){d=c(d,e,h);return{rawResult:d,results:RiveText.HEAPU8.subarray(d)}}};var ba=Object.assign({},f),da="./this.program",ea="object"==typeof window,t="function"==typeof importScripts,fa="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,u="",ha,v,w;
if(fa){u=t?require("path").dirname(u)+"/":__dirname+"/";var fs,ia;"function"===typeof require&&(fs=require("fs"),ia=require("path"));ha=(a,b)=>{a=ia.normalize(a);return fs.readFileSync(a,b?void 0:"utf8")};w=a=>{a=ha(a,!0);a.buffer||(a=new Uint8Array(a));return a};v=(a,b,c)=>{a=ia.normalize(a);fs.readFile(a,function(d,e){d?c(d):b(e.buffer)})};1<process.argv.length&&(da=process.argv[1].replace(/\\/g,"/"));process.argv.slice(2);process.on("uncaughtException",function(a){throw a;});process.on("unhandledRejection",
function(a){throw a;});f.inspect=function(){return"[Emscripten Module object]"}}else if(ea||t)t?u=self.location.href:"undefined"!=typeof document&&document.currentScript&&(u=document.currentScript.src),_scriptDir&&(u=_scriptDir),0!==u.indexOf("blob:")?u=u.substr(0,u.replace(/[?#].*/,"").lastIndexOf("/")+1):u="",ha=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.send(null);return b.responseText},t&&(w=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),
v=(a,b,c)=>{var d=new XMLHttpRequest;d.open("GET",a,!0);d.responseType="arraybuffer";d.onload=()=>{200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};var ja=f.print||console.log.bind(console),x=f.printErr||console.warn.bind(console);Object.assign(f,ba);ba=null;f.thisProgram&&(da=f.thisProgram);var y;f.wasmBinary&&(y=f.wasmBinary);var noExitRuntime=f.noExitRuntime||!0;"object"!=typeof WebAssembly&&C("no native wasm support detected");
var ka,la=!1,ma="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0;
function na(a,b,c){var d=b+c;for(c=b;a[c]&&!(c>=d);)++c;if(16<c-b&&a.buffer&&ma)return ma.decode(a.subarray(b,c));for(d="";b<c;){var e=a[b++];if(e&128){var h=a[b++]&63;if(192==(e&224))d+=String.fromCharCode((e&31)<<6|h);else{var l=a[b++]&63;e=224==(e&240)?(e&15)<<12|h<<6|l:(e&7)<<18|h<<12|l<<6|a[b++]&63;65536>e?d+=String.fromCharCode(e):(e-=65536,d+=String.fromCharCode(55296|e>>10,56320|e&1023))}}else d+=String.fromCharCode(e)}return d}var oa,D,F,G,H,I,J,pa,qa;
function ra(){var a=ka.buffer;oa=a;f.HEAP8=D=new Int8Array(a);f.HEAP16=G=new Int16Array(a);f.HEAP32=I=new Int32Array(a);f.HEAPU8=F=new Uint8Array(a);f.HEAPU16=H=new Uint16Array(a);f.HEAPU32=J=new Uint32Array(a);f.HEAPF32=pa=new Float32Array(a);f.HEAPF64=qa=new Float64Array(a)}var sa,ta=[],ua=[],va=[];function wa(){var a=f.preRun.shift();ta.unshift(a)}var K=0,xa=null,L=null;
function C(a){if(f.onAbort)f.onAbort(a);a="Aborted("+a+")";x(a);la=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");r(a);throw a;}function ya(){return M.startsWith("data:application/octet-stream;base64,")}var M;M="rive_text.wasm";if(!ya()){var za=M;M=f.locateFile?f.locateFile(za,u):u+za}function Aa(){var a=M;try{if(a==M&&y)return new Uint8Array(y);if(w)return w(a);throw"both async and sync fetching of the wasm failed";}catch(b){C(b)}}
function Ba(){if(!y&&(ea||t)){if("function"==typeof fetch&&!M.startsWith("file://"))return fetch(M,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+M+"'";return a.arrayBuffer()}).catch(function(){return Aa()});if(v)return new Promise(function(a,b){v(M,function(c){a(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return Aa()})}function Ca(a){for(;0<a.length;)a.shift()(f)}var N={};
function Da(a){for(;a.length;){var b=a.pop();a.pop()(b)}}function O(a){return this.fromWireType(I[a>>2])}var P={},Q={},Ea={};function Fa(a){if(void 0===a)return"_unknown";a=a.replace(/[^a-zA-Z0-9_]/g,"$");var b=a.charCodeAt(0);return 48<=b&&57>=b?"_"+a:a}function Ga(a,b){a=Fa(a);return(new Function("body","return function "+a+'() {\n "use strict"; return body.apply(this, arguments);\n};\n'))(b)}
function Ha(a){var b=Error,c=Ga(a,function(d){this.name=a;this.message=d;d=Error(d).stack;void 0!==d&&(this.stack=this.toString()+"\n"+d.replace(/^Error(:[^\n]*)?\n/,""))});c.prototype=Object.create(b.prototype);c.prototype.constructor=c;c.prototype.toString=function(){return void 0===this.message?this.name:this.name+": "+this.message};return c}var Ia=void 0;
function Ja(a,b,c){function d(g){g=c(g);if(g.length!==a.length)throw new Ia("Mismatched type converter count");for(var k=0;k<a.length;++k)R(a[k],g[k])}a.forEach(function(g){Ea[g]=b});var e=Array(b.length),h=[],l=0;b.forEach((g,k)=>{Q.hasOwnProperty(g)?e[k]=Q[g]:(h.push(g),P.hasOwnProperty(g)||(P[g]=[]),P[g].push(()=>{e[k]=Q[g];++l;l===h.length&&d(e)}))});0===h.length&&d(e)}
function Ka(a){switch(a){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+a);}}var La=void 0;function T(a){for(var b="";F[a];)b+=La[F[a++]];return b}var Ma=void 0;function U(a){throw new Ma(a);}
function R(a,b,c={}){if(!("argPackAdvance"in b))throw new TypeError("registerType registeredInstance requires argPackAdvance");var d=b.name;a||U('type "'+d+'" must have a positive integer typeid pointer');if(Q.hasOwnProperty(a)){if(c.T)return;U("Cannot register type '"+d+"' twice")}Q[a]=b;delete Ea[a];P.hasOwnProperty(a)&&(b=P[a],delete P[a],b.forEach(e=>e()))}var Na=[],V=[{},{value:void 0},{value:null},{value:!0},{value:!1}];function Oa(a){4<a&&0===--V[a].N&&(V[a]=void 0,Na.push(a))}
var W=a=>{a||U("Cannot use deleted val. handle = "+a);return V[a].value},X=a=>{switch(a){case void 0:return 1;case null:return 2;case !0:return 3;case !1:return 4;default:var b=Na.length?Na.pop():V.length;V[b]={N:1,value:a};return b}};function Pa(a,b){switch(b){case 2:return function(c){return this.fromWireType(pa[c>>2])};case 3:return function(c){return this.fromWireType(qa[c>>3])};default:throw new TypeError("Unknown float type: "+a);}}
function Qa(a){var b=Function;if(!(b instanceof Function))throw new TypeError("new_ called with constructor type "+typeof b+" which is not a function");var c=Ga(b.name||"unknownFunctionName",function(){});c.prototype=b.prototype;c=new c;a=b.apply(c,a);return a instanceof Object?a:c}
function Ra(a,b){var c=f;if(void 0===c[a].L){var d=c[a];c[a]=function(){c[a].L.hasOwnProperty(arguments.length)||U("Function '"+b+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+c[a].L+")!");return c[a].L[arguments.length].apply(this,arguments)};c[a].L=[];c[a].L[d.O]=d}}
function Sa(a,b,c){f.hasOwnProperty(a)?((void 0===c||void 0!==f[a].L&&void 0!==f[a].L[c])&&U("Cannot register public name '"+a+"' twice"),Ra(a,a),f.hasOwnProperty(c)&&U("Cannot register multiple overloads of a function with the same number of arguments ("+c+")!"),f[a].L[c]=b):(f[a]=b,void 0!==c&&(f[a].$=c))}function Ta(a,b){for(var c=[],d=0;d<a;d++)c.push(J[b+4*d>>2]);return c}
function Ua(a,b){var c=[];return function(){c.length=0;Object.assign(c,arguments);if(a.includes("j")){var d=f["dynCall_"+a];d=c&&c.length?d.apply(null,[b].concat(c)):d.call(null,b)}else d=sa.get(b).apply(null,c);return d}}function Y(a,b){a=T(a);var c=a.includes("j")?Ua(a,b):sa.get(b);"function"!=typeof c&&U("unknown function pointer with signature "+a+": "+b);return c}var Va=void 0;function Wa(a){a=Xa(a);var b=T(a);Z(a);return b}
function Ya(a,b){function c(h){e[h]||Q[h]||(Ea[h]?Ea[h].forEach(c):(d.push(h),e[h]=!0))}var d=[],e={};b.forEach(c);throw new Va(a+": "+d.map(Wa).join([", "]));}function Za(a,b,c){switch(b){case 0:return c?function(d){return D[d]}:function(d){return F[d]};case 1:return c?function(d){return G[d>>1]}:function(d){return H[d>>1]};case 2:return c?function(d){return I[d>>2]}:function(d){return J[d>>2]};default:throw new TypeError("Unknown integer type: "+a);}}
var $a="undefined"!=typeof TextDecoder?new TextDecoder("utf-16le"):void 0;function ab(a,b){var c=a>>1;for(var d=c+b/2;!(c>=d)&&H[c];)++c;c<<=1;if(32<c-a&&$a)return $a.decode(F.subarray(a,c));c="";for(d=0;!(d>=b/2);++d){var e=G[a+2*d>>1];if(0==e)break;c+=String.fromCharCode(e)}return c}function bb(a,b,c){void 0===c&&(c=2147483647);if(2>c)return 0;c-=2;var d=b;c=c<2*a.length?c/2:a.length;for(var e=0;e<c;++e)G[b>>1]=a.charCodeAt(e),b+=2;G[b>>1]=0;return b-d}function cb(a){return 2*a.length}
function db(a,b){for(var c=0,d="";!(c>=b/4);){var e=I[a+4*c>>2];if(0==e)break;++c;65536<=e?(e-=65536,d+=String.fromCharCode(55296|e>>10,56320|e&1023)):d+=String.fromCharCode(e)}return d}function eb(a,b,c){void 0===c&&(c=2147483647);if(4>c)return 0;var d=b;c=d+c-4;for(var e=0;e<a.length;++e){var h=a.charCodeAt(e);if(55296<=h&&57343>=h){var l=a.charCodeAt(++e);h=65536+((h&1023)<<10)|l&1023}I[b>>2]=h;b+=4;if(b+4>c)break}I[b>>2]=0;return b-d}
function fb(a){for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);55296<=d&&57343>=d&&++c;b+=4}return b}function gb(a,b){var c=Q[a];void 0===c&&U(b+" has unknown type "+Wa(a));return c}var hb={};function ib(a){var b=hb[a];return void 0===b?T(a):b}var jb=[];function kb(a){var b=jb.length;jb.push(a);return b}function lb(a,b){for(var c=Array(a),d=0;d<a;++d)c[d]=gb(J[b+4*d>>2],"parameter "+d);return c}var mb=[],nb={};
function ob(){if(!pb){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:da||"./this.program"},b;for(b in nb)void 0===nb[b]?delete a[b]:a[b]=nb[b];var c=[];for(b in a)c.push(b+"="+a[b]);pb=c}return pb}var pb,qb=[null,[],[]];Ia=f.InternalError=Ha("InternalError");for(var rb=Array(256),sb=0;256>sb;++sb)rb[sb]=String.fromCharCode(sb);La=rb;Ma=f.BindingError=Ha("BindingError");
f.count_emval_handles=function(){for(var a=0,b=5;b<V.length;++b)void 0!==V[b]&&++a;return a};f.get_first_emval=function(){for(var a=5;a<V.length;++a)if(void 0!==V[a])return V[a];return null};Va=f.UnboundTypeError=Ha("UnboundTypeError");
var ub={o:function(a){var b=N[a];delete N[a];var c=b.elements,d=c.length,e=c.map(function(g){return g.S}).concat(c.map(function(g){return g.Y})),h=b.V,l=b.W;Ja([a],e,function(g){c.forEach((k,m)=>{var n=g[m],q=k.P,z=k.R,A=g[m+d],p=k.X,ca=k.Z;k.read=B=>n.fromWireType(q(z,B));k.write=(B,E)=>{var S=[];p(ca,B,A.toWireType(S,E));Da(S)}});return[{name:b.name,fromWireType:function(k){for(var m=Array(d),n=0;n<d;++n)m[n]=c[n].read(k);l(k);return m},toWireType:function(k,m){if(d!==m.length)throw new TypeError("Incorrect number of tuple elements for "+
b.name+": expected="+d+", actual="+m.length);for(var n=h(),q=0;q<d;++q)c[q].write(n,m[q]);null!==k&&k.push(l,n);return n},argPackAdvance:8,readValueFromPointer:O,M:l}]})},r:function(){},y:function(a,b,c,d,e){var h=Ka(c);b=T(b);R(a,{name:b,fromWireType:function(l){return!!l},toWireType:function(l,g){return g?d:e},argPackAdvance:8,readValueFromPointer:function(l){if(1===c)var g=D;else if(2===c)g=G;else if(4===c)g=I;else throw new TypeError("Unknown boolean type size: "+b);return this.fromWireType(g[l>>
h])},M:null})},x:function(a,b){b=T(b);R(a,{name:b,fromWireType:function(c){var d=W(c);Oa(c);return d},toWireType:function(c,d){return X(d)},argPackAdvance:8,readValueFromPointer:O,M:null})},k:function(a,b,c){c=Ka(c);b=T(b);R(a,{name:b,fromWireType:function(d){return d},toWireType:function(d,e){return e},argPackAdvance:8,readValueFromPointer:Pa(b,c),M:null})},e:function(a,b,c,d,e,h){var l=Ta(b,c);a=T(a);e=Y(d,e);Sa(a,function(){Ya("Cannot call "+a+" due to unbound types",l)},b-1);Ja([],l,function(g){var k=
[g[0],null].concat(g.slice(1)),m=g=a,n=e,q=k.length;2>q&&U("argTypes array size mismatch! Must at least get return value and 'this' types!");for(var z=null!==k[1]&&!1,A=!1,p=1;p<k.length;++p)if(null!==k[p]&&void 0===k[p].M){A=!0;break}var ca="void"!==k[0].name,B="",E="";for(p=0;p<q-2;++p)B+=(0!==p?", ":"")+"arg"+p,E+=(0!==p?", ":"")+"arg"+p+"Wired";m="return function "+Fa(m)+"("+B+") {\nif (arguments.length !== "+(q-2)+") {\nthrowBindingError('function "+m+" called with ' + arguments.length + ' arguments, expected "+
(q-2)+" args!');\n}\n";A&&(m+="var destructors = [];\n");var S=A?"destructors":"null";B="throwBindingError invoker fn runDestructors retType classParam".split(" ");n=[U,n,h,Da,k[0],k[1]];z&&(m+="var thisWired = classParam.toWireType("+S+", this);\n");for(p=0;p<q-2;++p)m+="var arg"+p+"Wired = argType"+p+".toWireType("+S+", arg"+p+"); // "+k[p+2].name+"\n",B.push("argType"+p),n.push(k[p+2]);z&&(E="thisWired"+(0<E.length?", ":"")+E);m+=(ca?"var rv = ":"")+"invoker(fn"+(0<E.length?", ":"")+E+");\n";if(A)m+=
"runDestructors(destructors);\n";else for(p=z?1:2;p<k.length;++p)q=1===p?"thisWired":"arg"+(p-2)+"Wired",null!==k[p].M&&(m+=q+"_dtor("+q+"); // "+k[p].name+"\n",B.push(q+"_dtor"),n.push(k[p].M));ca&&(m+="var ret = retType.fromWireType(rv);\nreturn ret;\n");B.push(m+"}\n");k=Qa(B).apply(null,n);p=b-1;if(!f.hasOwnProperty(g))throw new Ia("Replacing nonexistant public symbol");void 0!==f[g].L&&void 0!==p?f[g].L[p]=k:(f[g]=k,f[g].O=p);return[]})},d:function(a,b,c,d,e){b=T(b);-1===e&&(e=4294967295);e=
Ka(c);var h=g=>g;if(0===d){var l=32-8*c;h=g=>g<<l>>>l}c=b.includes("unsigned")?function(g,k){return k>>>0}:function(g,k){return k};R(a,{name:b,fromWireType:h,toWireType:c,argPackAdvance:8,readValueFromPointer:Za(b,e,0!==d),M:null})},a:function(a,b,c){function d(h){h>>=2;var l=J;return new e(oa,l[h+1],l[h])}var e=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][b];c=T(c);R(a,{name:c,fromWireType:d,argPackAdvance:8,readValueFromPointer:d},{T:!0})},l:function(a,
b){b=T(b);var c="std::string"===b;R(a,{name:b,fromWireType:function(d){var e=J[d>>2],h=d+4;if(c)for(var l=h,g=0;g<=e;++g){var k=h+g;if(g==e||0==F[k]){l=l?na(F,l,k-l):"";if(void 0===m)var m=l;else m+=String.fromCharCode(0),m+=l;l=k+1}}else{m=Array(e);for(g=0;g<e;++g)m[g]=String.fromCharCode(F[h+g]);m=m.join("")}Z(d);return m},toWireType:function(d,e){e instanceof ArrayBuffer&&(e=new Uint8Array(e));var h,l="string"==typeof e;l||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Int8Array||
U("Cannot pass non-string to std::string");var g;if(c&&l)for(h=g=0;h<e.length;++h){var k=e.charCodeAt(h);127>=k?g++:2047>=k?g+=2:55296<=k&&57343>=k?(g+=4,++h):g+=3}else g=e.length;h=g;g=tb(4+h+1);k=g+4;J[g>>2]=h;if(c&&l){if(l=k,k=h+1,h=F,0<k){k=l+k-1;for(var m=0;m<e.length;++m){var n=e.charCodeAt(m);if(55296<=n&&57343>=n){var q=e.charCodeAt(++m);n=65536+((n&1023)<<10)|q&1023}if(127>=n){if(l>=k)break;h[l++]=n}else{if(2047>=n){if(l+1>=k)break;h[l++]=192|n>>6}else{if(65535>=n){if(l+2>=k)break;h[l++]=
224|n>>12}else{if(l+3>=k)break;h[l++]=240|n>>18;h[l++]=128|n>>12&63}h[l++]=128|n>>6&63}h[l++]=128|n&63}}h[l]=0}}else if(l)for(l=0;l<h;++l)m=e.charCodeAt(l),255<m&&(Z(k),U("String has UTF-16 code units that do not fit in 8 bits")),F[k+l]=m;else for(l=0;l<h;++l)F[k+l]=e[l];null!==d&&d.push(Z,g);return g},argPackAdvance:8,readValueFromPointer:O,M:function(d){Z(d)}})},f:function(a,b,c){c=T(c);if(2===b){var d=ab;var e=bb;var h=cb;var l=()=>H;var g=1}else 4===b&&(d=db,e=eb,h=fb,l=()=>J,g=2);R(a,{name:c,
fromWireType:function(k){for(var m=J[k>>2],n=l(),q,z=k+4,A=0;A<=m;++A){var p=k+4+A*b;if(A==m||0==n[p>>g])z=d(z,p-z),void 0===q?q=z:(q+=String.fromCharCode(0),q+=z),z=p+b}Z(k);return q},toWireType:function(k,m){"string"!=typeof m&&U("Cannot pass non-string to C++ string type "+c);var n=h(m),q=tb(4+n+b);J[q>>2]=n>>g;e(m,q+4,n+b);null!==k&&k.push(Z,q);return q},argPackAdvance:8,readValueFromPointer:O,M:function(k){Z(k)}})},p:function(a,b,c,d,e,h){N[a]={name:T(b),V:Y(c,d),W:Y(e,h),elements:[]}},j:function(a,
b,c,d,e,h,l,g,k){N[a].elements.push({S:b,P:Y(c,d),R:e,Y:h,X:Y(l,g),Z:k})},z:function(a,b){b=T(b);R(a,{U:!0,name:b,argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},n:function(a,b,c){a=W(a);b=gb(b,"emval::as");var d=[],e=X(d);J[c>>2]=e;return b.toWireType(d,a)},C:function(a,b,c,d){a=jb[a];b=W(b);c=ib(c);a(b,c,null,d)},b:Oa,B:function(a,b){var c=lb(a,b),d=c[0];b=d.name+"_$"+c.slice(1).map(function(n){return n.name}).join("_")+"$";var e=mb[b];if(void 0!==e)return e;e=["retType"];
for(var h=[d],l="",g=0;g<a-1;++g)l+=(0!==g?", ":"")+"arg"+g,e.push("argType"+g),h.push(c[1+g]);var k="return function "+Fa("methodCaller_"+b)+"(handle, name, destructors, args) {\n",m=0;for(g=0;g<a-1;++g)k+=" var arg"+g+" = argType"+g+".readValueFromPointer(args"+(m?"+"+m:"")+");\n",m+=c[g+1].argPackAdvance;k+=" var rv = handle[name]("+l+");\n";for(g=0;g<a-1;++g)c[g+1].deleteObject&&(k+=" argType"+g+".deleteObject(arg"+g+");\n");d.U||(k+=" return retType.toWireType(destructors, rv);\n");
e.push(k+"};\n");a=Qa(e).apply(null,h);e=kb(a);return mb[b]=e},i:function(a,b){a=W(a);b=W(b);return X(a[b])},A:function(a){4<a&&(V[a].N+=1)},h:function(a){return X(ib(a))},m:function(a){var b=W(a);Da(b);Oa(a)},g:function(a,b){a=gb(a,"_emval_take_value");a=a.readValueFromPointer(b);return X(a)},c:function(){C("")},s:function(a){var b=F.length;a>>>=0;if(2147483648<a)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);var e=Math;d=Math.max(a,d);e=e.min.call(e,2147483648,d+(65536-
d%65536)%65536);a:{try{ka.grow(e-oa.byteLength+65535>>>16);ra();var h=1;break a}catch(l){}h=void 0}if(h)return!0}return!1},t:function(a,b){var c=0;ob().forEach(function(d,e){var h=b+c;e=J[a+4*e>>2]=h;for(h=0;h<d.length;++h)D[e++>>0]=d.charCodeAt(h);D[e>>0]=0;c+=d.length+1});return 0},u:function(a,b){var c=ob();J[a>>2]=c.length;var d=0;c.forEach(function(e){d+=e.length+1});J[b>>2]=d;return 0},v:function(){return 52},q:function(){return 70},w:function(a,b,c,d){for(var e=0,h=0;h<c;h++){var l=J[b>>2],
g=J[b+4>>2];b+=8;for(var k=0;k<g;k++){var m=F[l+k],n=qb[a];0===m||10===m?((1===a?ja:x)(na(n,0)),n.length=0):n.push(m)}e+=g}J[d>>2]=e;return 0}};
(function(){function a(e){f.asm=e.exports;ka=f.asm.D;ra();sa=f.asm.J;ua.unshift(f.asm.E);K--;f.monitorRunDependencies&&f.monitorRunDependencies(K);0==K&&(null!==xa&&(clearInterval(xa),xa=null),L&&(e=L,L=null,e()))}function b(e){a(e.instance)}function c(e){return Ba().then(function(h){return WebAssembly.instantiate(h,d)}).then(function(h){return h}).then(e,function(h){x("failed to asynchronously prepare wasm: "+h);C(h)})}var d={a:ub};K++;f.monitorRunDependencies&&f.monitorRunDependencies(K);if(f.instantiateWasm)try{return f.instantiateWasm(d,
a)}catch(e){x("Module.instantiateWasm callback failed with error: "+e),r(e)}(function(){return y||"function"!=typeof WebAssembly.instantiateStreaming||ya()||M.startsWith("file://")||fa||"function"!=typeof fetch?c(b):fetch(M,{credentials:"same-origin"}).then(function(e){return WebAssembly.instantiateStreaming(e,d).then(b,function(h){x("wasm streaming compile failed: "+h);x("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(r);return{}})();
f.___wasm_call_ctors=function(){return(f.___wasm_call_ctors=f.asm.E).apply(null,arguments)};var tb=f._malloc=function(){return(tb=f._malloc=f.asm.F).apply(null,arguments)},Z=f._free=function(){return(Z=f._free=f.asm.G).apply(null,arguments)},Xa=f.___getTypeName=function(){return(Xa=f.___getTypeName=f.asm.H).apply(null,arguments)};f.__embind_initialize_bindings=function(){return(f.__embind_initialize_bindings=f.asm.I).apply(null,arguments)};
f.dynCall_jiji=function(){return(f.dynCall_jiji=f.asm.K).apply(null,arguments)};var vb;L=function wb(){vb||xb();vb||(L=wb)};
function xb(){function a(){if(!vb&&(vb=!0,f.calledRun=!0,!la)){Ca(ua);aa(f);if(f.onRuntimeInitialized)f.onRuntimeInitialized();if(f.postRun)for("function"==typeof f.postRun&&(f.postRun=[f.postRun]);f.postRun.length;){var b=f.postRun.shift();va.unshift(b)}Ca(va)}}if(!(0<K)){if(f.preRun)for("function"==typeof f.preRun&&(f.preRun=[f.preRun]);f.preRun.length;)wa();Ca(ta);0<K||(f.setStatus?(f.setStatus("Running..."),setTimeout(function(){setTimeout(function(){f.setStatus("")},1);a()},1)):a())}}
if(f.preInit)for("function"==typeof f.preInit&&(f.preInit=[f.preInit]);0<f.preInit.length;)f.preInit.pop()();xb();
return RiveText.ready
}
);
})();
if (typeof exports === 'object' && typeof module === 'object')
module.exports = RiveText;
else if (typeof define === 'function' && define['amd'])
define([], function() { return RiveText; });
else if (typeof exports === 'object')
exports["RiveText"] = RiveText;

Binary file not shown.

71
wasm/build_wasm.sh Executable file
View File

@ -0,0 +1,71 @@
#!/bin/bash
set -e
CONFIG=debug
SINGLE=
for var in "$@"; do
if [[ $var = "release" ]]; then
CONFIG=release
fi
if [[ $var = "single" ]]; then
SINGLE=--single_file
fi
done
pushd ../
./update_dependencies.sh
popd
unameOut="$(uname -s)"
case "${unameOut}" in
Linux*) MACHINE=linux ;;
Darwin*) MACHINE=mac ;;
CYGWIN*) MACHINE=cygwin ;;
MINGW*) MACHINE=mingw ;;
*) MACHINE="UNKNOWN:${unameOut}" ;;
esac
if [[ ! -f "bin/premake5" ]]; then
mkdir -p bin
pushd bin
echo Downloading Premake5
if [ "$MACHINE" = 'mac' ]; then
PREMAKE_URL=https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-macosx.tar.gz
else
PREMAKE_URL=https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-linux.tar.gz
fi
curl $PREMAKE_URL -L -o premake.tar.gz
# Export premake5 into bin
tar -xvf premake.tar.gz 2>/dev/null
# Delete downloaded archive
rm premake.tar.gz
popd
fi
if [[ ! -f "bin/emsdk/emsdk_env.sh" ]]; then
mkdir -p bin
pushd bin
git clone https://github.com/emscripten-core/emsdk.git
pushd emsdk
./emsdk install latest
./emsdk activate latest
popd
popd
fi
source ./bin/emsdk/emsdk_env.sh
export PREMAKE=bin/premake5
$PREMAKE --scripts=../macos/rive-cpp/build --file=../premake5_rive_plugin.lua gmake2 $SINGLE --arch=wasm
cd ..
for var in "$@"; do
if [[ $var = "clean" ]]; then
make clean
make config=release clean
fi
done
AR=emar CC=emcc CXX=em++ make config=$CONFIG -j$(($(sysctl -n hw.physicalcpu) + 1))
du -hs wasm/build/bin/$CONFIG/rive_text.wasm

1
wasm/js/externs.js Normal file
View File

@ -0,0 +1 @@
var RiveText = {};

61
wasm/js/rive_text.js Normal file
View File

@ -0,0 +1,61 @@
RiveText["onRuntimeInitialized"] = function () {
var nativeMakeGlyphPath = RiveText["makeGlyphPath"];
var move = 0;
var line = 1;
var quad = 2;
var cubic = 4;
var close = 5;
RiveText["makeGlyphPath"] = function (font, glyphId) {
var glyph = nativeMakeGlyphPath(font, glyphId);
var verbCount = glyph[3];
var ptsPtr = glyph[1];
var verbPtr = glyph[2];
var verbs = RiveText["HEAPU8"]["subarray"](verbPtr, verbPtr + verbCount);
let pointCount = 0;
for (var verb of verbs) {
switch (verb) {
case move:
case line:
pointCount++;
break;
case quad:
pointCount += 2;
break;
case cubic:
pointCount += 3;
break;
default:
break;
}
}
const ptsStart = ptsPtr / 4;
return {
"rawPath": glyph[0],
"verbs": verbs,
"points": RiveText["HEAPF32"]["subarray"](
ptsStart,
ptsStart + pointCount * 2
),
};
};
var nativeShapeText = RiveText["shapeText"];
RiveText["shapeText"] = function (codeUnits, runsList) {
var shapeResult = nativeShapeText(codeUnits, runsList);
return {
"rawResult": shapeResult,
"results": RiveText["HEAPU8"]["subarray"](shapeResult),
};
};
var nativeBreakLines = RiveText["breakLines"];
RiveText["breakLines"] = function (shape, width, align) {
var breakResult = nativeBreakLines(shape, width, align);
return {
"rawResult": breakResult,
"results": RiveText["HEAPU8"]["subarray"](breakResult),
};
};
};

6583
wasm/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

27
wasm/package.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "@rive-app/flutter-wasm",
"version": "6.0.0",
"description": "WASM portion of Rive's Flutter runtime.",
"homepage": "https://rive.app",
"private": false,
"repository": {
"type": "git",
"url": "https://github.com/rive-app/rive-flutter"
},
"author": "Rive",
"license": "MIT",
"scripts": {
"test": "jest --testPathPattern=$PWD/test/",
"bump-version": "VERSION=`npm version major` && echo \"const wasmVersion = '$VERSION';\" > ../lib/src/rive_text_wasm_version.dart",
"serve": "http-server build/bin/ -p 8282 --cors"
},
"devDependencies": {
"jest": "^29.0.3",
"http-server": "^14.1.1"
},
"keywords": [],
"files": [
"build/bin/release/rive_text.js",
"build/bin/release/rive_text.wasm"
]
}

16
wasm/publish_wasm.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
set -ex
# Build the wasm and js files that will be released.
./build_wasm.sh clean release
# Make sure tests succeed.
npm test
# Bump version in package.json and referenced by dart code. This very
# intentionally bumps major version.
npm run bump-version
# Publish to npm
npm publish --access public

230
wasm/rive_text_bindings.cpp Normal file
View File

@ -0,0 +1,230 @@
#include "hb-ot.h"
#include "hb.h"
#include "rive/text/font_hb.hpp"
#include <emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <stdint.h>
#include <stdio.h>
using namespace emscripten;
using WasmPtr = uint32_t;
WasmPtr makeFont(emscripten::val byteArray)
{
std::vector<unsigned char> bytes;
const auto l = byteArray["byteLength"].as<unsigned>();
bytes.resize(l);
emscripten::val memoryView{emscripten::typed_memory_view(l, bytes.data())};
memoryView.call<void>("set", byteArray);
auto result = HBFont::Decode(bytes);
if (result)
{
return (WasmPtr)result.release();
}
return (WasmPtr) nullptr;
}
void deleteFont(WasmPtr font) { reinterpret_cast<HBFont*>(font)->unref(); }
struct GlyphPath
{
WasmPtr rawPath;
WasmPtr points;
WasmPtr verbs;
uint16_t verbCount;
};
GlyphPath makeGlyphPath(WasmPtr fontPtr, rive::GlyphID id)
{
auto font = reinterpret_cast<HBFont*>(fontPtr);
rive::RawPath* path = new rive::RawPath(font->getPath(id));
return {
.rawPath = (WasmPtr)path,
.points = (WasmPtr)path->points().data(),
.verbs = (WasmPtr)path->verbs().data(),
.verbCount = (uint16_t)path->verbs().size(),
};
}
void deleteGlyphPath(WasmPtr rawPath) { delete reinterpret_cast<rive::RawPath*>(rawPath); }
void deleteShapeResult(WasmPtr shaperResult)
{
delete reinterpret_cast<rive::SimpleArray<rive::Paragraph>*>(shaperResult);
}
WasmPtr breakLines(WasmPtr paragraphsPtr, float width, uint8_t align)
{
bool autoWidth = width == -1.0f;
auto paragraphs = reinterpret_cast<rive::SimpleArray<rive::Paragraph>*>(paragraphsPtr);
float paragraphWidth = width;
rive::SimpleArrayBuilder<uint16_t> paragraphLines;
rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>* lines =
new rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>(paragraphs->size());
rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>& linesRef = *lines;
size_t paragraphIndex = 0;
for (auto& para : *paragraphs)
{
linesRef[paragraphIndex] =
rive::GlyphLine::BreakLines(para.runs, autoWidth ? -1.0f : width);
if (autoWidth)
{
paragraphWidth =
std::max(paragraphWidth,
rive::GlyphLine::ComputeMaxWidth(linesRef[paragraphIndex], para.runs));
}
paragraphIndex++;
}
paragraphIndex = 0;
for (auto& para : *paragraphs)
{
rive::GlyphLine::ComputeLineSpacing(linesRef[paragraphIndex++],
para.runs,
paragraphWidth,
(rive::TextAlign)align);
}
return (WasmPtr)lines;
}
void deleteLines(WasmPtr lines)
{
delete reinterpret_cast<rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>*>(lines);
}
std::vector<rive::Font*> fallbackFonts;
void setFallbackFonts(emscripten::val fontsList)
{
std::vector<int> fonts(fontsList["length"].as<unsigned>());
{
emscripten::val memoryView{emscripten::typed_memory_view(fonts.size(), fonts.data())};
memoryView.call<void>("set", fontsList);
}
fallbackFonts = std::vector<rive::Font*>();
for (auto fontPtr : fonts)
{
fallbackFonts.push_back(reinterpret_cast<rive::Font*>(fontPtr));
}
}
static rive::rcp<rive::Font> pickFallbackFont(rive::Span<const rive::Unichar> missing)
{
size_t length = fallbackFonts.size();
for (size_t i = 0; i < length; i++)
{
HBFont* font = static_cast<HBFont*>(fallbackFonts[i]);
if (i == length - 1 || font->hasGlyph(missing))
{
rive::rcp<rive::Font> rcFont = rive::rcp<rive::Font>(font);
// because the font was released at load time, we need to give it an
// extra ref whenever we bump it to a reference counted pointer.
rcFont->ref();
return rcFont;
}
}
return nullptr;
}
WasmPtr shapeText(emscripten::val codeUnits, emscripten::val runsList)
{
std::vector<uint8_t> runsBytes(runsList["byteLength"].as<unsigned>());
{
emscripten::val memoryView{
emscripten::typed_memory_view(runsBytes.size(), runsBytes.data())};
memoryView.call<void>("set", runsList);
}
std::vector<uint32_t> codeUnitArray(codeUnits["length"].as<unsigned>());
{
emscripten::val memoryView{
emscripten::typed_memory_view(codeUnitArray.size(), codeUnitArray.data())};
memoryView.call<void>("set", codeUnits);
}
auto runCount = runsBytes.size() / sizeof(rive::TextRun);
rive::TextRun* runs = reinterpret_cast<rive::TextRun*>(runsBytes.data());
if (runCount > 0)
{
auto result = (WasmPtr) new rive::SimpleArray<rive::Paragraph>(
runs[0].font->shapeText(codeUnitArray, rive::Span(runs, runCount)));
return result;
}
return {};
}
void init()
{
fallbackFonts.clear();
HBFont::gFallbackProc = pickFallbackFont;
}
#ifdef DEBUG
#define OFFSET_OF(type, member) ((int)(intptr_t) & (((type*)(void*)0)->member))
void assertSomeAssumptions()
{
// These assumptions are important as our rive_text_wasm.dart integration
// relies on knowing the exact offsets of these struct elements. When and if
// we ever move to the proposed Wasm64 (currently not a standard), we'll
// need to make adjustements here.
assert(sizeof(rive::TextRun) == 20);
assert(OFFSET_OF(rive::TextRun, font) == 0);
assert(OFFSET_OF(rive::TextRun, size) == 4);
assert(OFFSET_OF(rive::TextRun, unicharCount) == 8);
assert(OFFSET_OF(rive::TextRun, script) == 12);
assert(OFFSET_OF(rive::TextRun, styleId) == 16);
assert(OFFSET_OF(rive::TextRun, dir) == 18);
assert(sizeof(rive::Paragraph) == 12);
assert(OFFSET_OF(rive::Paragraph, runs) == 0);
assert(OFFSET_OF(rive::Paragraph, baseDirection) == 8);
assert(sizeof(rive::GlyphRun) == 52);
assert(OFFSET_OF(rive::GlyphRun, font) == 0);
assert(OFFSET_OF(rive::GlyphRun, size) == 4);
assert(OFFSET_OF(rive::GlyphRun, glyphs) == 8);
assert(OFFSET_OF(rive::GlyphRun, textIndices) == 16);
assert(OFFSET_OF(rive::GlyphRun, advances) == 24);
assert(OFFSET_OF(rive::GlyphRun, xpos) == 32);
assert(OFFSET_OF(rive::GlyphRun, breaks) == 40);
assert(OFFSET_OF(rive::GlyphRun, styleId) == 48);
assert(OFFSET_OF(rive::GlyphRun, dir) == 50);
assert(sizeof(rive::GlyphLine) == 32);
}
#endif
EMSCRIPTEN_BINDINGS(RiveText)
{
function("makeFont", &makeFont, allow_raw_pointers());
function("deleteFont", &deleteFont);
value_array<GlyphPath>("GlyphPath")
.element(&GlyphPath::rawPath)
.element(&GlyphPath::points)
.element(&GlyphPath::verbs)
.element(&GlyphPath::verbCount);
function("makeGlyphPath", &makeGlyphPath);
function("deleteGlyphPath", &deleteGlyphPath);
function("shapeText", &shapeText);
function("setFallbackFonts", &setFallbackFonts);
function("deleteShapeResult", &deleteShapeResult);
function("breakLines", &breakLines);
function("deleteLines", &deleteLines);
function("init", &init);
#ifdef DEBUG
function("assertSomeAssumptions", &assertSomeAssumptions);
#endif
}

153
wasm/test/load.test.js Normal file
View File

@ -0,0 +1,153 @@
const fs = require("fs");
const initRiveText = require("../build/bin/debug/rive_text.js");
test("load render font", async () => {
const riveText = await initRiveText();
const fontBytes = fs.readFileSync("../test/assets/RobotoFlex.ttf");
const font = riveText.makeFont(fontBytes);
expect(font).not.toBe(0);
riveText.deleteFont(font);
});
const move = 0;
const line = 1;
const quad = 2;
const cubic = 4;
const close = 5;
// function toGlyphBuffer(module, glyph) {
// const verbCount = glyph[3];
// const ptsPtr = glyph[1];
// const verbPtr = glyph[2];
// const verbs = module.HEAPU8.subarray(verbPtr, verbPtr + verbCount);
// let pointCount = 0;
// for (const verb of verbs) {
// switch (verb) {
// case move:
// case line:
// pointCount++;
// break;
// case quad:
// pointCount += 2;
// break;
// case cubic:
// pointCount += 3;
// break;
// default:
// break;
// }
// }
// const ptsStart = ptsPtr / 4;
// return {
// rawPath: glyph[0],
// verbs: verbs,
// points: module.HEAPF32.subarray(ptsStart, ptsStart + pointCount * 2),
// };
// }
test("sanity checks", async () => {
const riveText = await initRiveText();
riveText.assertSomeAssumptions();
});
test("load glyph from font", async () => {
const riveText = await initRiveText();
const fontBytes = fs.readFileSync("../test/assets/RobotoFlex.ttf");
const font = riveText.makeFont(fontBytes);
const glyph = riveText.makeGlyphPath(font, 222);
// const glyphBuffer = toGlyphBuffer(riveText, glyph);
const expectedVerbs = new Uint8Array([
move,
quad,
quad,
quad,
quad,
line,
line,
line,
line,
quad,
quad,
quad,
quad,
line,
quad,
quad,
quad,
quad,
line,
quad,
quad,
quad,
quad,
line,
close,
move,
line,
quad,
quad,
quad,
quad,
line,
quad,
quad,
quad,
quad,
close,
move,
quad,
quad,
quad,
quad,
line,
quad,
quad,
quad,
quad,
line,
close,
]);
expect(glyph.verbs).toStrictEqual(expectedVerbs);
const expectedPoints = new Float32Array([
0.060546875, 0.138671875, 0.09765625, 0.1708984375, 0.1513671875,
0.19189453125, 0.205078125, 0.212890625, 0.2685546875, 0.212890625,
0.37109375, 0.212890625, 0.43505859375, 0.1552734375, 0.4990234375,
0.09765625, 0.4990234375, -0.009765625, 0.4990234375, -0.513671875,
0.4169921875, -0.513671875, 0.4130859375, -0.44677734375, 0.41015625,
-0.44677734375, 0.390625, -0.478515625, 0.350341796875, -0.5009765625,
0.31005859375, -0.5234375, 0.25634765625, -0.5234375, 0.15869140625,
-0.5234375, 0.10009765625, -0.4501953125, 0.04150390625, -0.376953125,
0.04150390625, -0.2646484375, 0.04150390625, -0.2490234375, 0.04150390625,
-0.13671875, 0.10009765625, -0.0634765625, 0.15869140625, 0.009765625,
0.25634765625, 0.009765625, 0.31005859375, 0.009765625, 0.347900390625,
-0.009521484375, 0.3857421875, -0.02880859375, 0.408203125, -0.060546875,
0.408203125, -0.009765625, 0.408203125, 0.05859375, 0.3701171875,
0.09619140625, 0.33203125, 0.1337890625, 0.26611328125, 0.1337890625,
0.21728515625, 0.1337890625, 0.17822265625, 0.114501953125, 0.13916015625,
0.09521484375, 0.11767578125, 0.0703125, 0.060546875, 0.138671875,
0.13427734375, -0.2490234375, 0.13427734375, -0.2646484375, 0.13427734375,
-0.3427734375, 0.16845703125, -0.39599609375, 0.20263671875, -0.44921875,
0.27587890625, -0.44921875, 0.32470703125, -0.44921875, 0.35888671875,
-0.42236328125, 0.39306640625, -0.3955078125, 0.408203125, -0.361328125,
0.408203125, -0.15234375, 0.39306640625, -0.1181640625, 0.35888671875,
-0.09130859375, 0.32470703125, -0.064453125, 0.27587890625, -0.064453125,
0.20263671875, -0.064453125, 0.16845703125, -0.11767578125, 0.13427734375,
-0.1708984375, 0.13427734375, -0.2490234375, 0.404296875, -0.7412109375,
0.38671875, -0.71142578125, 0.357421875, -0.688232421875, 0.328125,
-0.6650390625, 0.287109375, -0.6650390625, 0.24853515625, -0.6650390625,
0.219482421875, -0.689208984375, 0.1904296875, -0.71337890625,
0.17333984375, -0.7412109375, 0.12255859375, -0.71533203125, 0.14306640625,
-0.65966796875, 0.187255859375, -0.622314453125, 0.2314453125,
-0.5849609375, 0.28662109375, -0.5849609375, 0.34814453125, -0.5849609375,
0.390869140625, -0.625732421875, 0.43359375, -0.66650390625, 0.45068359375,
-0.71484375, 0.404296875, -0.7412109375,
]);
expect(glyph.points).toStrictEqual(expectedPoints);
riveText.deleteGlyphPath(glyph.rawPath);
riveText.deleteFont(font);
});

17
windows/.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
flutter/
# Visual Studio user-specific files.
*.suo
*.user
*.userosscache
*.sln.docstates
# Visual Studio build-related files.
x64/
x86/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

125
windows/CMakeLists.txt Normal file
View File

@ -0,0 +1,125 @@
# The Flutter tooling requires that developers have a version of Visual Studio
# installed that includes CMake 3.14 or later. You should not increase this
# version, as doing so will cause the plugin to fail to compile for some
# customers of the plugin.
cmake_minimum_required(VERSION 3.14)
# Project-level configuration.
set(PROJECT_NAME "rive")
project(${PROJECT_NAME} LANGUAGES CXX C)
# This value is used when generating builds using this plugin, so it must
# not be changed
set(PLUGIN_NAME "rive_plugin")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-deprecated-copy-with-user-provided-dtor -Wno-documentation -Wno-documentation-pedantic -Wno-documentation-unknown-command -Wno-double-promotion -Wno-exit-time-destructors -Wno-float-equal -Wno-global-constructors -Wno-implicit-float-conversion -Wno-newline-eof -Wno-old-style-cast -Wno-reserved-identifier -Wno-shadow -Wno-sign-compare -Wno-sign-conversion -Wno-unused-function -Wno-unused-macros -Wno-unused-variable -Wno-unused-parameter -Wno-switch-enum -Wno-missing-field-initializers -DWITH_RIVE_TEXT -DHAVE_OT -DHB_NO_FALLBACK_SHAPE -DHB_NO_WIN1256 -DSB_CONFIG_UNITY"
)
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -DSB_CONFIG_UNITY -Wno-unused-function -Wno-undef -Wno-unused-variable -Wno-unused-parameter"
)
# Any new source files that you add to the plugin should be added here.
list(APPEND PLUGIN_SOURCES
../ios/rive_text/rive_text.cpp
../ios/rive-cpp/src/math/raw_path.cpp
../ios/rive-cpp/src/math/mat2d.cpp
../ios/rive-cpp/src/rive_counter.cpp
../ios/rive-cpp/src/renderer.cpp
../ios/rive-cpp/src/text/font_hb.cpp
../ios/rive-cpp/src/text/line_breaker.cpp
../ios/harfbuzz/src/hb-aat-layout.cc
../ios/harfbuzz/src/hb-aat-map.cc
../ios/harfbuzz/src/hb-blob.cc
../ios/harfbuzz/src/hb-buffer-serialize.cc
../ios/harfbuzz/src/hb-buffer-verify.cc
../ios/harfbuzz/src/hb-buffer.cc
../ios/harfbuzz/src/hb-common.cc
../ios/harfbuzz/src/hb-draw.cc
../ios/harfbuzz/src/hb-face.cc
../ios/harfbuzz/src/hb-font.cc
../ios/harfbuzz/src/hb-map.cc
../ios/harfbuzz/src/hb-number.cc
../ios/harfbuzz/src/hb-ot-cff1-table.cc
../ios/harfbuzz/src/hb-ot-cff2-table.cc
../ios/harfbuzz/src/hb-ot-color.cc
../ios/harfbuzz/src/hb-ot-face.cc
../ios/harfbuzz/src/hb-ot-font.cc
../ios/harfbuzz/src/hb-ot-layout.cc
../ios/harfbuzz/src/hb-ot-map.cc
../ios/harfbuzz/src/hb-ot-math.cc
../ios/harfbuzz/src/hb-ot-meta.cc
../ios/harfbuzz/src/hb-ot-metrics.cc
../ios/harfbuzz/src/hb-ot-name.cc
../ios/harfbuzz/src/hb-ot-shape-complex-arabic.cc
../ios/harfbuzz/src/hb-ot-shape-complex-default.cc
../ios/harfbuzz/src/hb-ot-shape-complex-hangul.cc
../ios/harfbuzz/src/hb-ot-shape-complex-hebrew.cc
../ios/harfbuzz/src/hb-ot-shape-complex-indic-table.cc
../ios/harfbuzz/src/hb-ot-shape-complex-indic.cc
../ios/harfbuzz/src/hb-ot-shape-complex-khmer.cc
../ios/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
../ios/harfbuzz/src/hb-ot-shape-complex-syllabic.cc
../ios/harfbuzz/src/hb-ot-shape-complex-thai.cc
../ios/harfbuzz/src/hb-ot-shape-complex-use.cc
../ios/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc
../ios/harfbuzz/src/hb-ot-shape-fallback.cc
../ios/harfbuzz/src/hb-ot-shape-normalize.cc
../ios/harfbuzz/src/hb-ot-shape.cc
../ios/harfbuzz/src/hb-ot-tag.cc
../ios/harfbuzz/src/hb-ot-var.cc
../ios/harfbuzz/src/hb-set.cc
../ios/harfbuzz/src/hb-shape-plan.cc
../ios/harfbuzz/src/hb-shape.cc
../ios/harfbuzz/src/hb-shaper.cc
../ios/harfbuzz/src/hb-static.cc
../ios/harfbuzz/src/hb-subset-cff-common.cc
../ios/harfbuzz/src/hb-subset-cff1.cc
../ios/harfbuzz/src/hb-subset-cff2.cc
../ios/harfbuzz/src/hb-subset-input.cc
../ios/harfbuzz/src/hb-subset-plan.cc
../ios/harfbuzz/src/hb-subset-repacker.cc
../ios/harfbuzz/src/hb-subset.cc
../ios/harfbuzz/src/hb-ucd.cc
../ios/harfbuzz/src/hb-unicode.cc
../ios/SheenBidi/Source/SheenBidi.c
)
# Define the plugin library target. Its name must not be changed (see comment
# on PLUGIN_NAME above).
add_library(${PLUGIN_NAME} SHARED
${PLUGIN_SOURCES}
"rive_plugin.cpp"
)
# Apply a standard set of build settings that are configured in the
# application-level CMakeLists.txt. This can be removed for plugins that want
# full control over build settings.
apply_standard_settings(${PLUGIN_NAME})
# Symbols are hidden by default to reduce the chance of accidental conflicts
# between plugins. This should not be removed; any symbols that should be
# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro.
set_target_properties(${PLUGIN_NAME} PROPERTIES
VS_PLATFORM_TOOLSET ClangCL
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
# Source include directories and library dependencies. Add any plugin-specific
# dependencies here.
target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include"
PRIVATE ../ios/harfbuzz/src ../ios/rive-cpp/skia/renderer/include
../ios/rive-cpp/include ../ios/SheenBidi/Headers)
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
target_compile_features(${PLUGIN_NAME} PRIVATE cxx_std_17)
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(rive_bundled_libraries
""
PARENT_SCOPE
)

View File

@ -0,0 +1,24 @@
#ifndef FLUTTER_PLUGIN_RIVE_FLUTTER_PLUGIN_C_API_H_
#define FLUTTER_PLUGIN_RIVE_FLUTTER_PLUGIN_C_API_H_
#include <flutter_plugin_registrar.h>
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
#endif
#if defined(__cplusplus)
extern "C"
{
#endif
FLUTTER_PLUGIN_EXPORT void
RivePluginRegisterWithRegistrar(FlutterDesktopPluginRegistrarRef registrar);
#if defined(__cplusplus)
} // extern "C"
#endif
#endif // FLUTTER_PLUGIN_RIVE_FLUTTER_PLUGIN_C_API_H_

Some files were not shown because too many files have changed in this diff Show More