mirror of
https://github.com/rive-app/rive-flutter
synced 2025-06-22 01:55:59 +00:00
Text
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:
14
.github/workflows/tests.yaml
vendored
14
.github/workflows/tests.yaml
vendored
@ -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
16
.gitignore
vendored
@ -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
32
.lua-format
Normal 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
|
40
.metadata
40
.metadata
@ -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'
|
||||
|
@ -1 +1 @@
|
||||
f15304279962053360702d3ef8443d110d444893
|
||||
3be5ff0d873280d2bcec300fb764f016ac96a08d
|
||||
|
@ -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
9
android/.gitignore
vendored
Normal 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
79
android/CMakeLists.txt
Normal 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
56
android/build.gradle
Normal 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
1
android/settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
rootProject.name = 'rive'
|
3
android/src/main/AndroidManifest.xml
Normal file
3
android/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="app.rive.rive">
|
||||
</manifest>
|
35
android/src/main/kotlin/app/rive/rive/RivePlugin.kt
Normal file
35
android/src/main/kotlin/app/rive/rive/RivePlugin.kt
Normal 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)
|
||||
}
|
||||
}
|
29
example/analysis_options.yaml
Normal file
29
example/analysis_options.yaml
Normal 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
|
@ -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'
|
||||
|
@ -0,0 +1,6 @@
|
||||
package app.rive.rive_example
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
}
|
@ -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>
|
18
example/android/app/src/main/res/values-night/styles.xml
Normal file
18
example/android/app/src/main/res/values-night/styles.xml
Normal 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>
|
@ -21,6 +21,6 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
<string>11.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -1 +1,2 @@
|
||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
@ -1 +1,2 @@
|
||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
41
example/ios/Podfile
Normal file
41
example/ios/Podfile
Normal 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
22
example/ios/Podfile.lock
Normal 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
|
@ -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",
|
||||
|
@ -2,6 +2,6 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1020"
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -4,4 +4,7 @@
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
6
example/ios/Runner/AppDelegate.h
Normal file
6
example/ios/Runner/AppDelegate.h
Normal file
@ -0,0 +1,6 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : FlutterAppDelegate
|
||||
|
||||
@end
|
13
example/ios/Runner/AppDelegate.m
Normal file
13
example/ios/Runner/AppDelegate.m
Normal 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
|
@ -41,5 +41,7 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
9
example/ios/Runner/main.m
Normal file
9
example/ios/Runner/main.m
Normal 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]));
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||
|
@ -1 +1,2 @@
|
||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||
|
@ -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
40
example/macos/Podfile
Normal 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
|
22
example/macos/Podfile.lock
Normal file
22
example/macos/Podfile.lock
Normal 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
|
@ -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 */
|
||||
|
@ -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"
|
||||
|
@ -4,4 +4,7 @@
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
BIN
example/web/icons/Icon-maskable-192.png
Normal file
BIN
example/web/icons/Icon-maskable-192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
BIN
example/web/icons/Icon-maskable-512.png
Normal file
BIN
example/web/icons/Icon-maskable-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
17
example/windows/.gitignore
vendored
Normal file
17
example/windows/.gitignore
vendored
Normal 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/
|
101
example/windows/CMakeLists.txt
Normal file
101
example/windows/CMakeLists.txt
Normal 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)
|
104
example/windows/flutter/CMakeLists.txt
Normal file
104
example/windows/flutter/CMakeLists.txt
Normal 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}
|
||||
)
|
14
example/windows/flutter/generated_plugin_registrant.cc
Normal file
14
example/windows/flutter/generated_plugin_registrant.cc
Normal 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"));
|
||||
}
|
15
example/windows/flutter/generated_plugin_registrant.h
Normal file
15
example/windows/flutter/generated_plugin_registrant.h
Normal 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_
|
24
example/windows/flutter/generated_plugins.cmake
Normal file
24
example/windows/flutter/generated_plugins.cmake
Normal 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)
|
39
example/windows/runner/CMakeLists.txt
Normal file
39
example/windows/runner/CMakeLists.txt
Normal 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)
|
121
example/windows/runner/Runner.rc
Normal file
121
example/windows/runner/Runner.rc
Normal 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
|
70
example/windows/runner/flutter_window.cpp
Normal file
70
example/windows/runner/flutter_window.cpp
Normal 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);
|
||||
}
|
36
example/windows/runner/flutter_window.h
Normal file
36
example/windows/runner/flutter_window.h
Normal 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_
|
48
example/windows/runner/main.cpp
Normal file
48
example/windows/runner/main.cpp
Normal 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;
|
||||
}
|
16
example/windows/runner/resource.h
Normal file
16
example/windows/runner/resource.h
Normal 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
|
BIN
example/windows/runner/resources/app_icon.ico
Normal file
BIN
example/windows/runner/resources/app_icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
20
example/windows/runner/runner.exe.manifest
Normal file
20
example/windows/runner/runner.exe.manifest
Normal 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>
|
84
example/windows/runner/utils.cpp
Normal file
84
example/windows/runner/utils.cpp
Normal 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;
|
||||
}
|
19
example/windows/runner/utils.h
Normal file
19
example/windows/runner/utils.h
Normal 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_
|
282
example/windows/runner/win32_window.cpp
Normal file
282
example/windows/runner/win32_window.cpp
Normal 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.
|
||||
}
|
98
example/windows/runner/win32_window.h
Normal file
98
example/windows/runner/win32_window.h
Normal 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
38
ios/.gitignore
vendored
Normal 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
0
ios/Assets/.gitkeep
Normal file
4
ios/Classes/RivePlugin.h
Normal file
4
ios/Classes/RivePlugin.h
Normal file
@ -0,0 +1,4 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface RivePlugin : NSObject <FlutterPlugin>
|
||||
@end
|
15
ios/Classes/RivePlugin.m
Normal file
15
ios/Classes/RivePlugin.m
Normal 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
|
14
ios/Classes/SwiftRivePlugin.swift
Normal file
14
ios/Classes/SwiftRivePlugin.swift
Normal 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
96
ios/rive.podspec
Normal 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
139
ios/rive_text/rive_text.cpp
Normal 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
15
lib/rive_web.dart
Normal 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
6
lib/src/platform.dart
Normal 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();
|
||||
}
|
10
lib/src/platform_native.dart
Normal file
10
lib/src/platform_native.dart
Normal 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');
|
||||
}
|
8
lib/src/platform_web.dart
Normal file
8
lib/src/platform_web.dart
Normal file
@ -0,0 +1,8 @@
|
||||
import 'platform.dart';
|
||||
|
||||
Platform makePlatform() => PlatformWeb();
|
||||
|
||||
class PlatformWeb extends Platform {
|
||||
@override
|
||||
bool get isTesting => false;
|
||||
}
|
@ -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
319
lib/src/rive_text.dart
Normal 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
605
lib/src/rive_text_ffi.dart
Normal 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
551
lib/src/rive_text_wasm.dart
Normal 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),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
1
lib/src/rive_text_wasm_version.dart
Normal file
1
lib/src/rive_text_wasm_version.dart
Normal file
@ -0,0 +1 @@
|
||||
const wasmVersion = '6.0.0';
|
19
macos/Classes/RivePlugin.swift
Normal file
19
macos/Classes/RivePlugin.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
10
macos/Flutter/GeneratedPluginRegistrant.swift
Normal file
10
macos/Flutter/GeneratedPluginRegistrant.swift
Normal file
@ -0,0 +1,10 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
}
|
94
macos/rive.podspec
Normal file
94
macos/rive.podspec
Normal 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
|
135
macos/rive_text/rive_text.cpp
Normal file
135
macos/rive_text/rive_text.cpp
Normal 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;
|
||||
}
|
35
platform_considerations.md
Normal file
35
platform_considerations.md
Normal 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
340
premake5_rive_plugin.lua
Normal 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'}}
|
||||
}
|
31
pubspec.yaml
31
pubspec.yaml
@ -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
59
shared_lib/build_shared.sh
Executable 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
BIN
test/assets/RobotoFlex.ttf
Normal file
Binary file not shown.
44
test/text_test.dart
Normal file
44
test/text_test.dart
Normal 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
81
update_dependencies.sh
Executable 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
1
wasm/.npmrc
Normal file
@ -0,0 +1 @@
|
||||
tag-version-prefix=""
|
30
wasm/README.md
Normal file
30
wasm/README.md
Normal 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.
|
70
wasm/build/bin/release/rive_text.js
Normal file
70
wasm/build/bin/release/rive_text.js
Normal 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;
|
BIN
wasm/build/bin/release/rive_text.wasm
Executable file
BIN
wasm/build/bin/release/rive_text.wasm
Executable file
Binary file not shown.
71
wasm/build_wasm.sh
Executable file
71
wasm/build_wasm.sh
Executable 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
1
wasm/js/externs.js
Normal file
@ -0,0 +1 @@
|
||||
var RiveText = {};
|
61
wasm/js/rive_text.js
Normal file
61
wasm/js/rive_text.js
Normal 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
6583
wasm/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
wasm/package.json
Normal file
27
wasm/package.json
Normal 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
16
wasm/publish_wasm.sh
Executable 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
230
wasm/rive_text_bindings.cpp
Normal 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
153
wasm/test/load.test.js
Normal 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
17
windows/.gitignore
vendored
Normal 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
125
windows/CMakeLists.txt
Normal 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
|
||||
)
|
24
windows/include/rive/rive_plugin.h
Normal file
24
windows/include/rive/rive_plugin.h
Normal 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
Reference in New Issue
Block a user