├── rust ├── .gitignore ├── src │ ├── lib.rs │ └── api │ │ ├── mod.rs │ │ ├── constants.rs │ │ ├── encode.rs │ │ └── orientation.rs └── Cargo.toml ├── example ├── linux │ ├── .gitignore │ ├── runner │ │ ├── main.cc │ │ ├── my_application.h │ │ ├── CMakeLists.txt │ │ └── my_application.cc │ ├── flutter │ │ ├── generated_plugin_registrant.h │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugins.cmake │ │ └── CMakeLists.txt │ └── CMakeLists.txt ├── ios │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── RunnerTests │ │ └── RunnerTests.swift │ └── .gitignore ├── macos │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Runner │ │ ├── Configs │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ ├── Warnings.xcconfig │ │ │ └── AppInfo.xcconfig │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_512.png │ │ │ │ ├── app_icon_64.png │ │ │ │ ├── app_icon_1024.png │ │ │ │ └── Contents.json │ │ ├── Release.entitlements │ │ ├── AppDelegate.swift │ │ ├── DebugProfile.entitlements │ │ ├── MainFlutterWindow.swift │ │ └── Info.plist │ ├── .gitignore │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── RunnerTests │ │ └── RunnerTests.swift ├── windows │ ├── runner │ │ ├── resources │ │ │ └── app_icon.ico │ │ ├── resource.h │ │ ├── runner.exe.manifest │ │ ├── utils.h │ │ ├── flutter_window.h │ │ ├── main.cpp │ │ ├── CMakeLists.txt │ │ ├── utils.cpp │ │ ├── flutter_window.cpp │ │ ├── Runner.rc │ │ └── win32_window.h │ ├── .gitignore │ ├── flutter │ │ ├── generated_plugin_registrant.h │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugins.cmake │ │ └── CMakeLists.txt │ └── CMakeLists.txt ├── android │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── untitled_example │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── build.gradle.kts │ └── settings.gradle.kts ├── README.md ├── .gitignore ├── analysis_options.yaml └── pubspec.yaml ├── cargokit ├── .gitignore ├── build_tool │ ├── README.md │ ├── bin │ │ └── build_tool.dart │ ├── lib │ │ ├── build_tool.dart │ │ └── src │ │ │ ├── build_cmake.dart │ │ │ ├── logging.dart │ │ │ ├── cargo.dart │ │ │ ├── build_gradle.dart │ │ │ ├── environment.dart │ │ │ ├── verify_binaries.dart │ │ │ ├── build_pod.dart │ │ │ ├── crate_hash.dart │ │ │ ├── target.dart │ │ │ ├── rustup.dart │ │ │ └── util.dart │ ├── test │ │ ├── cargo_test.dart │ │ ├── builder_test.dart │ │ ├── rustup_test.dart │ │ └── options_test.dart │ ├── pubspec.yaml │ └── analysis_options.yaml ├── README ├── cmake │ ├── resolve_symlinks.ps1 │ └── cargokit.cmake ├── LICENSE ├── build_pod.sh ├── run_build_tool.cmd ├── run_build_tool.sh └── docs │ └── precompiled_binaries.md ├── android ├── settings.gradle ├── .gitignore ├── src │ └── main │ │ └── AndroidManifest.xml └── build.gradle ├── ios ├── Classes │ └── dummy_file.c └── simple_native_image_compress.podspec ├── macos ├── Classes │ └── dummy_file.c └── simple_native_image_compress.podspec ├── flutter_rust_bridge.yaml ├── test_driver └── integration_test.dart ├── analysis_options.yaml ├── lib ├── simple_native_image_compress.dart └── src │ └── rust │ └── api │ ├── constants.dart │ └── compress.dart ├── windows ├── .gitignore └── CMakeLists.txt ├── .gitignore ├── linux └── CMakeLists.txt ├── makefile ├── LICENSE ├── .metadata ├── pubspec.yaml ├── CHANGELOG.md └── README.md /rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /example/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod api; 2 | mod frb_generated; 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /cargokit/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .dart_tool 3 | *.iml 4 | !pubspec.lock 5 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'simple_native_image_compress' 2 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Classes/dummy_file.c: -------------------------------------------------------------------------------- 1 | // This is an empty file to force CocoaPods to create a framework. 2 | -------------------------------------------------------------------------------- /macos/Classes/dummy_file.c: -------------------------------------------------------------------------------- 1 | // This is an empty file to force CocoaPods to create a framework. 2 | -------------------------------------------------------------------------------- /rust/src/api/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod compress; 2 | pub mod constants; 3 | pub mod encode; 4 | mod orientation; 5 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /flutter_rust_bridge.yaml: -------------------------------------------------------------------------------- 1 | rust_input: crate::api 2 | rust_root: rust/ 3 | dart_output: lib/src/rust 4 | dart_entrypoint_class_name: NativeImageCompress -------------------------------------------------------------------------------- /test_driver/integration_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:integration_test/integration_test_driver.dart'; 2 | 3 | Future main() => integrationDriver(); 4 | -------------------------------------------------------------------------------- /cargokit/build_tool/README.md: -------------------------------------------------------------------------------- 1 | A sample command-line application with an entrypoint in `bin/`, library code 2 | in `lib/`, and example unit test in `test/`. 3 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .cxx 10 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /cargokit/build_tool/bin/build_tool.dart: -------------------------------------------------------------------------------- 1 | import 'package:build_tool/build_tool.dart' as build_tool; 2 | 3 | void main(List arguments) { 4 | build_tool.runMain(arguments); 5 | } 6 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/build_tool.dart: -------------------------------------------------------------------------------- 1 | import 'src/build_tool.dart' as build_tool; 2 | 3 | Future runMain(List args) async { 4 | return build_tool.runMain(args); 5 | } 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hwkim1127/simple_native_image_compress/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/linux/runner/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/simple_native_image_compress.dart: -------------------------------------------------------------------------------- 1 | library simple_native_image_compress; 2 | 3 | export 'src/rust/api/compress.dart'; 4 | export 'src/rust/api/constants.dart'; 5 | export 'src/rust/frb_generated.dart' show NativeImageCompress; 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/untitled_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.simple_native_image_compress 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity : FlutterActivity() 6 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip 6 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | .cxx/ 9 | .kotlin 10 | 11 | # Remember to never publicly share your keystore. 12 | # See https://flutter.dev/to/reference-keystore 13 | key.properties 14 | **/*.keystore 15 | **/*.jks 16 | -------------------------------------------------------------------------------- /cargokit/README: -------------------------------------------------------------------------------- 1 | Experimental repository to provide glue for seamlessly integrating cargo build 2 | with flutter plugins and packages. 3 | 4 | See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/ 5 | for a tutorial on how to use Cargokit. 6 | 7 | Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin. 8 | 9 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /example/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | 10 | override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { 11 | return true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/linux/runner/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simple_native_image_compress" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib", "staticlib"] 8 | 9 | [dependencies] 10 | anyhow = "1.0.98" 11 | flutter_rust_bridge = "=2.10.0" 12 | image = "0.25.6" 13 | kamadak-exif = "0.6.1" 14 | 15 | [profile.dev] 16 | lto = true 17 | panic = "abort" 18 | opt-level = "z" 19 | strip = "debuginfo" 20 | 21 | [profile.release] 22 | lto = true 23 | panic = "abort" 24 | opt-level = "z" 25 | strip = "debuginfo" 26 | -------------------------------------------------------------------------------- /example/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() 9 | rootProject.layout.buildDirectory.value(newBuildDir) 10 | 11 | subprojects { 12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) 13 | project.layout.buildDirectory.value(newSubprojectBuildDir) 14 | } 15 | subprojects { 16 | project.evaluationDependsOn(":app") 17 | } 18 | 19 | tasks.register("clean") { 20 | delete(rootProject.layout.buildDirectory) 21 | } 22 | -------------------------------------------------------------------------------- /cargokit/build_tool/test/cargo_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:build_tool/src/cargo.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | final _cargoToml = """ 5 | [workspace] 6 | 7 | [profile.release] 8 | lto = true 9 | panic = "abort" 10 | opt-level = "z" 11 | # strip = "symbols" 12 | 13 | [package] 14 | name = "super_native_extensions" 15 | version = "0.1.0" 16 | edition = "2021" 17 | resolver = "2" 18 | 19 | [lib] 20 | crate-type = ["cdylib", "staticlib"] 21 | """; 22 | 23 | void main() { 24 | test('parseCargoToml', () { 25 | final info = CrateInfo.parseManifest(_cargoToml); 26 | expect(info.packageName, 'super_native_extensions'); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # simple_native_image_compress 2 | 3 | Demonstrates how to use the untitled plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | .vscode 12 | migrate_working_dir/ 13 | 14 | # IntelliJ related 15 | *.iml 16 | *.ipr 17 | *.iws 18 | .idea/ 19 | .cxx/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 28 | /pubspec.lock 29 | **/doc/api/ 30 | .dart_tool/ 31 | build/ 32 | -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = simple_native_image_compress 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.untitledExample 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /cargokit/build_tool/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: build_tool 2 | description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build. 3 | publish_to: none 4 | version: 1.0.0 5 | 6 | environment: 7 | sdk: ">=3.0.0 <4.0.0" 8 | 9 | # Add regular dependencies here. 10 | dependencies: 11 | # these are pinned on purpose because the bundle_tool_runner doesn't have 12 | # pubspec.lock. See run_build_tool.sh 13 | logging: 1.2.0 14 | path: 1.8.0 15 | version: 3.0.0 16 | collection: 1.18.0 17 | ed25519_edwards: 0.3.1 18 | hex: 0.2.0 19 | yaml: 3.1.2 20 | source_span: 1.10.0 21 | github: 9.17.0 22 | args: 2.4.2 23 | crypto: 3.0.3 24 | convert: 3.1.1 25 | http: 1.1.0 26 | toml: 0.14.0 27 | 28 | dev_dependencies: 29 | lints: ^2.1.0 30 | test: ^1.24.0 31 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import file_picker 9 | import file_selector_macos 10 | import flutter_avif_macos 11 | import path_provider_foundation 12 | import sqflite_darwin 13 | 14 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 15 | FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) 16 | FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) 17 | FlutterAvifPlugin.register(with: registry.registrar(forPlugin: "FlutterAvifPlugin")) 18 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 19 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) 20 | } 21 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | void RegisterPlugins(flutter::PluginRegistry* registry) { 14 | FileSelectorWindowsRegisterWithRegistrar( 15 | registry->GetRegistrarForPlugin("FileSelectorWindows")); 16 | FlutterAvifWindowsPluginRegisterWithRegistrar( 17 | registry->GetRegistrarForPlugin("FlutterAvifWindowsPlugin")); 18 | PermissionHandlerWindowsPluginRegisterWithRegistrar( 19 | registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); 20 | } 21 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void fl_register_plugins(FlPluginRegistry* registry) { 13 | g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = 14 | fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); 15 | file_selector_plugin_register_with_registrar(file_selector_linux_registrar); 16 | g_autoptr(FlPluginRegistrar) flutter_avif_linux_registrar = 17 | fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAvifLinuxPlugin"); 18 | flutter_avif_linux_plugin_register_with_registrar(flutter_avif_linux_registrar); 19 | } 20 | -------------------------------------------------------------------------------- /example/android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | val flutterSdkPath = run { 3 | val properties = java.util.Properties() 4 | file("local.properties").inputStream().use { properties.load(it) } 5 | val flutterSdkPath = properties.getProperty("flutter.sdk") 6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } 7 | flutterSdkPath 8 | } 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id("dev.flutter.flutter-plugin-loader") version "1.0.0" 21 | id("com.android.application") version "8.7.3" apply false 22 | id("org.jetbrains.kotlin.android") version "2.1.0" apply false 23 | } 24 | 25 | include(":app") 26 | -------------------------------------------------------------------------------- /lib/src/rust/api/constants.dart: -------------------------------------------------------------------------------- 1 | // This file is automatically generated, so please do not edit it. 2 | // @generated by `flutter_rust_bridge`@ 2.10.0. 3 | 4 | // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import 5 | 6 | import '../frb_generated.dart'; 7 | import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; 8 | 9 | // These functions are ignored because they are not marked as `pub`: `convert_filter_type` 10 | // These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `eq`, `eq` 11 | 12 | enum CompressFormat { 13 | jpeg, 14 | webP, 15 | avif, 16 | ; 17 | } 18 | 19 | enum FilterType { 20 | nearest, 21 | triangle, 22 | catmullRom, 23 | gaussian, 24 | lanczos3, 25 | ; 26 | } 27 | -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The Flutter tooling requires that developers have CMake 3.10 or later 2 | # installed. You should not increase this version, as doing so will cause 3 | # the plugin to fail to compile for some customers of the plugin. 4 | cmake_minimum_required(VERSION 3.10) 5 | cmake_policy(SET CMP0079 NEW) 6 | # Project-level configuration. 7 | set(PROJECT_NAME "simple_native_image_compress") 8 | project(${PROJECT_NAME} LANGUAGES CXX) 9 | 10 | include("../cargokit/cmake/cargokit.cmake") 11 | apply_cargokit(${PROJECT_NAME} ../rust simple_native_image_compress "") 12 | 13 | # List of absolute paths to libraries that should be bundled with the plugin. 14 | # This list could contain prebuilt libraries, or libraries created by an 15 | # external build triggered from this build file. 16 | set(simple_native_image_compress_bundled_libraries 17 | "${${PROJECT_NAME}_cargokit_lib}" 18 | PARENT_SCOPE 19 | ) 20 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The Flutter tooling requires that developers have a version of Visual Studio 2 | # installed that includes CMake 3.14 or later. You should not increase this 3 | # version, as doing so will cause the plugin to fail to compile for some 4 | # customers of the plugin. 5 | cmake_minimum_required(VERSION 3.14) 6 | 7 | # Project-level configuration. 8 | set(PROJECT_NAME "simple_native_image_compress") 9 | project(${PROJECT_NAME} LANGUAGES CXX) 10 | 11 | include("../cargokit/cmake/cargokit.cmake") 12 | apply_cargokit(${PROJECT_NAME} ../rust simple_native_image_compress "") 13 | 14 | # List of absolute paths to libraries that should be bundled with the plugin. 15 | # This list could contain prebuilt libraries, or libraries created by an 16 | # external build triggered from this build file. 17 | set(simple_native_image_compress_bundled_libraries 18 | "${${PROJECT_NAME}_cargokit_lib}" 19 | PARENT_SCOPE 20 | ) 21 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | pubspec.lock 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | file_selector_linux 7 | flutter_avif_linux 8 | ) 9 | 10 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 11 | simple_native_image_compress 12 | ) 13 | 14 | set(PLUGIN_BUNDLED_LIBRARIES) 15 | 16 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 17 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 18 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 21 | endforeach(plugin) 22 | 23 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 24 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 26 | endforeach(ffi_plugin) 27 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | file_selector_windows 7 | flutter_avif_windows 8 | permission_handler_windows 9 | ) 10 | 11 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 12 | simple_native_image_compress 13 | ) 14 | 15 | set(PLUGIN_BUNDLED_LIBRARIES) 16 | 17 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 18 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 19 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 21 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 22 | endforeach(plugin) 23 | 24 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 25 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 26 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 27 | endforeach(ffi_plugin) 28 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | setup_apple: 2 | cargo install cargo-xcode 3 | cargo install cargo-lipo 4 | 5 | setup_android: 6 | cargo install cargo-ndk 7 | rustup target add \ 8 | aarch64-linux-android \ 9 | armv7-linux-androideabi \ 10 | x86_64-linux-android \ 11 | i686-linux-android 12 | 13 | setup_ios: 14 | rustup target add \ 15 | aarch64-apple-ios \ 16 | x86_64-apple-ios \ 17 | aarch64-apple-ios-sim 18 | 19 | # # From https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/ 20 | # install_cargokit: 21 | # git subtree add --prefix cargokit https://github.com/irondash/cargokit.git main --squash 22 | 23 | # # Unlike git modules, subtrees actually live in your repository and thus work with pub without any issues. 24 | # update_cargokit: 25 | # git subtree pull --prefix cargokit https://github.com/irondash/cargokit.git main --squash 26 | 27 | generate_bindings: 28 | flutter_rust_bridge_codegen generate 29 | 30 | check_plugin: 31 | flutter pub publish --dry-run 32 | 33 | publish: 34 | flutter pub publish -------------------------------------------------------------------------------- /cargokit/build_tool/test/builder_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:build_tool/src/builder.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('parseBuildConfiguration', () { 6 | var b = BuildEnvironment.parseBuildConfiguration('debug'); 7 | expect(b, BuildConfiguration.debug); 8 | 9 | b = BuildEnvironment.parseBuildConfiguration('profile'); 10 | expect(b, BuildConfiguration.profile); 11 | 12 | b = BuildEnvironment.parseBuildConfiguration('release'); 13 | expect(b, BuildConfiguration.release); 14 | 15 | b = BuildEnvironment.parseBuildConfiguration('debug-dev'); 16 | expect(b, BuildConfiguration.debug); 17 | 18 | b = BuildEnvironment.parseBuildConfiguration('profile'); 19 | expect(b, BuildConfiguration.profile); 20 | 21 | b = BuildEnvironment.parseBuildConfiguration('profile-prod'); 22 | expect(b, BuildConfiguration.profile); 23 | 24 | // fallback to release 25 | b = BuildEnvironment.parseBuildConfiguration('unknown'); 26 | expect(b, BuildConfiguration.release); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /cargokit/cmake/resolve_symlinks.ps1: -------------------------------------------------------------------------------- 1 | function Resolve-Symlinks { 2 | [CmdletBinding()] 3 | [OutputType([string])] 4 | param( 5 | [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 6 | [string] $Path 7 | ) 8 | 9 | [string] $separator = '/' 10 | [string[]] $parts = $Path.Split($separator) 11 | 12 | [string] $realPath = '' 13 | foreach ($part in $parts) { 14 | if ($realPath -and !$realPath.EndsWith($separator)) { 15 | $realPath += $separator 16 | } 17 | 18 | $realPath += $part.Replace('\', '/') 19 | 20 | # The slash is important when using Get-Item on Drive letters in pwsh. 21 | if (-not($realPath.Contains($separator)) -and $realPath.EndsWith(':')) { 22 | $realPath += '/' 23 | } 24 | 25 | $item = Get-Item $realPath 26 | if ($item.LinkTarget) { 27 | $realPath = $item.LinkTarget.Replace('\', '/') 28 | } 29 | } 30 | $realPath 31 | } 32 | 33 | $path = Resolve-Symlinks -Path $args[0] 34 | Write-Host $path 35 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/linux/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} 10 | "main.cc" 11 | "my_application.cc" 12 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 13 | ) 14 | 15 | # Apply the standard set of build settings. This can be removed for applications 16 | # that need different build settings. 17 | apply_standard_settings(${BINARY_NAME}) 18 | 19 | # Add preprocessor definitions for the application ID. 20 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 21 | 22 | # Add dependency libraries. Add any application-specific dependencies here. 23 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 24 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 25 | 26 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Hyunwoo Kim 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /rust/src/api/constants.rs: -------------------------------------------------------------------------------- 1 | use image::imageops; 2 | 3 | #[derive(PartialEq, Eq)] 4 | // #[cfg_attr(debug_assertions, derive(Debug))] 5 | pub enum CompressFormat { 6 | Jpeg, 7 | WebP, 8 | AVIF, 9 | } 10 | 11 | /* 12 | flutter rust bridge cannot import imageops::FilterType... so here we go... 13 | https://docs.rs/image/latest/image/imageops/enum.FilterType.html 14 | */ 15 | #[derive(PartialEq, Eq)] 16 | pub enum FilterType { 17 | Nearest, // Nearest Neighbor 18 | Triangle, // Linear Filter 19 | CatmullRom, // Cubic Filter 20 | Gaussian, // Gaussian Filter 21 | Lanczos3, // Lanczos with window 3 22 | } 23 | 24 | pub(crate) fn convert_filter_type(filter_type: FilterType) -> imageops::FilterType { 25 | match filter_type { 26 | FilterType::Nearest => imageops::FilterType::Nearest, 27 | // FilterType::Triangle => imageops::FilterType::Triangle, 28 | FilterType::CatmullRom => imageops::FilterType::CatmullRom, 29 | FilterType::Gaussian => imageops::FilterType::Gaussian, 30 | FilterType::Lanczos3 => imageops::FilterType::Lanczos3, 31 | _ => imageops::FilterType::Triangle, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/build_cmake.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path/path.dart' as path; 4 | 5 | import 'artifacts_provider.dart'; 6 | import 'builder.dart'; 7 | import 'environment.dart'; 8 | import 'options.dart'; 9 | import 'target.dart'; 10 | 11 | class BuildCMake { 12 | final CargokitUserOptions userOptions; 13 | 14 | BuildCMake({required this.userOptions}); 15 | 16 | Future build() async { 17 | final targetPlatform = Environment.targetPlatform; 18 | final target = Target.forFlutterName(Environment.targetPlatform); 19 | if (target == null) { 20 | throw Exception("Unknown target platform: $targetPlatform"); 21 | } 22 | 23 | final environment = BuildEnvironment.fromEnvironment(isAndroid: false); 24 | final provider = 25 | ArtifactProvider(environment: environment, userOptions: userOptions); 26 | final artifacts = await provider.getArtifacts([target]); 27 | 28 | final libs = artifacts[target]!; 29 | 30 | for (final lib in libs) { 31 | if (lib.type == AritifactType.dylib) { 32 | File(lib.path) 33 | .copySync(path.join(Environment.outputDir, lib.finalFileName)); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /cargokit/build_tool/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | linter: 19 | rules: 20 | - prefer_relative_imports 21 | - directives_ordering 22 | 23 | # analyzer: 24 | # exclude: 25 | # - path/to/excluded/files/** 26 | 27 | # For more information about the core and recommended set of lints, see 28 | # https://dart.dev/go/core-lints 29 | 30 | # For additional information about configuring this file, see 31 | # https://dart.dev/guides/language/analysis-options 32 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/logging.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:logging/logging.dart'; 4 | 5 | const String kSeparator = "--"; 6 | const String kDoubleSeparator = "=="; 7 | 8 | bool _lastMessageWasSeparator = false; 9 | 10 | void _log(LogRecord rec) { 11 | final prefix = '${rec.level.name}: '; 12 | final out = rec.level == Level.SEVERE ? stderr : stdout; 13 | if (rec.message == kSeparator) { 14 | if (!_lastMessageWasSeparator) { 15 | out.write(prefix); 16 | out.writeln('-' * 80); 17 | _lastMessageWasSeparator = true; 18 | } 19 | return; 20 | } else if (rec.message == kDoubleSeparator) { 21 | out.write(prefix); 22 | out.writeln('=' * 80); 23 | _lastMessageWasSeparator = true; 24 | return; 25 | } 26 | out.write(prefix); 27 | out.writeln(rec.message); 28 | _lastMessageWasSeparator = false; 29 | } 30 | 31 | void initLogging() { 32 | Logger.root.level = Level.INFO; 33 | Logger.root.onRecord.listen((LogRecord rec) { 34 | final lines = rec.message.split('\n'); 35 | for (final line in lines) { 36 | if (line.isNotEmpty || lines.length == 1 || line != lines.last) { 37 | _log(LogRecord( 38 | rec.level, 39 | line, 40 | rec.loggerName, 41 | )); 42 | } 43 | } 44 | }); 45 | } 46 | 47 | void enableVerboseLogging() { 48 | Logger.root.level = Level.ALL; 49 | } 50 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/cargo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path/path.dart' as path; 4 | import 'package:toml/toml.dart'; 5 | 6 | class ManifestException { 7 | ManifestException(this.message, {required this.fileName}); 8 | 9 | final String? fileName; 10 | final String message; 11 | 12 | @override 13 | String toString() { 14 | if (fileName != null) { 15 | return 'Failed to parse package manifest at $fileName: $message'; 16 | } else { 17 | return 'Failed to parse package manifest: $message'; 18 | } 19 | } 20 | } 21 | 22 | class CrateInfo { 23 | CrateInfo({required this.packageName}); 24 | 25 | final String packageName; 26 | 27 | static CrateInfo parseManifest(String manifest, {final String? fileName}) { 28 | final toml = TomlDocument.parse(manifest); 29 | final package = toml.toMap()['package']; 30 | if (package == null) { 31 | throw ManifestException('Missing package section', fileName: fileName); 32 | } 33 | final name = package['name']; 34 | if (name == null) { 35 | throw ManifestException('Missing package name', fileName: fileName); 36 | } 37 | return CrateInfo(packageName: name); 38 | } 39 | 40 | static CrateInfo load(String manifestDir) { 41 | final manifestFile = File(path.join(manifestDir, 'Cargo.toml')); 42 | final manifest = manifestFile.readAsStringSync(); 43 | return parseManifest(manifest, fileName: manifestFile.path); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"simple_native_image_compress", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/build_gradle.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:logging/logging.dart'; 4 | import 'package:path/path.dart' as path; 5 | 6 | import 'artifacts_provider.dart'; 7 | import 'builder.dart'; 8 | import 'environment.dart'; 9 | import 'options.dart'; 10 | import 'target.dart'; 11 | 12 | final log = Logger('build_gradle'); 13 | 14 | class BuildGradle { 15 | BuildGradle({required this.userOptions}); 16 | 17 | final CargokitUserOptions userOptions; 18 | 19 | Future build() async { 20 | final targets = Environment.targetPlatforms.map((arch) { 21 | final target = Target.forFlutterName(arch); 22 | if (target == null) { 23 | throw Exception( 24 | "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); 25 | } 26 | return target; 27 | }).toList(); 28 | 29 | final environment = BuildEnvironment.fromEnvironment(isAndroid: true); 30 | final provider = 31 | ArtifactProvider(environment: environment, userOptions: userOptions); 32 | final artifacts = await provider.getArtifacts(targets); 33 | 34 | for (final target in targets) { 35 | final libs = artifacts[target]!; 36 | final outputDir = path.join(Environment.outputDir, target.android!); 37 | Directory(outputDir).createSync(recursive: true); 38 | 39 | for (final lib in libs) { 40 | if (lib.type == AritifactType.dylib) { 41 | File(lib.path).copySync(path.join(outputDir, lib.finalFileName)); 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /example/android/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("kotlin-android") 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id("dev.flutter.flutter-gradle-plugin") 6 | } 7 | 8 | android { 9 | namespace = "com.example.simple_native_image_compress" 10 | compileSdk = flutter.compileSdkVersion 11 | ndkVersion = "27.0.12077973" 12 | 13 | compileOptions { 14 | sourceCompatibility = JavaVersion.VERSION_17 15 | targetCompatibility = JavaVersion.VERSION_17 16 | } 17 | 18 | kotlinOptions { 19 | jvmTarget = JavaVersion.VERSION_17.toString() 20 | } 21 | 22 | defaultConfig { 23 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 24 | applicationId = "com.example.simple_native_image_compress" 25 | // You can update the following values to match your application needs. 26 | // For more information, see: https://flutter.dev/to/review-gradle-config. 27 | minSdk = flutter.minSdkVersion 28 | targetSdk = flutter.targetSdkVersion 29 | versionCode = flutter.versionCode 30 | versionName = flutter.versionName 31 | } 32 | 33 | buildTypes { 34 | release { 35 | // TODO: Add your own signing config for the release build. 36 | // Signing with the debug keys for now, so `flutter run --release` works. 37 | signingConfig = signingConfigs.getByName("debug") 38 | } 39 | } 40 | } 41 | 42 | flutter { 43 | source = "../.." 44 | } 45 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // The Android Gradle Plugin builds the native code with the Android NDK. 2 | 3 | group 'com.flutter_rust_bridge.simple_native_image_compress' 4 | version '1.0' 5 | 6 | buildscript { 7 | repositories { 8 | google() 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | // The Android Gradle Plugin knows how to build native code with the NDK. 14 | classpath 'com.android.tools.build:gradle:8.7.3' 15 | } 16 | } 17 | 18 | rootProject.allprojects { 19 | repositories { 20 | google() 21 | mavenCentral() 22 | } 23 | } 24 | 25 | apply plugin: 'com.android.library' 26 | 27 | android { 28 | if (project.android.hasProperty("namespace")) { 29 | namespace 'com.flutter_rust_bridge.simple_native_image_compress' 30 | } 31 | 32 | // Bumping the plugin compileSdkVersion requires all clients of this plugin 33 | // to bump the version in their app. 34 | compileSdkVersion 35 35 | 36 | // Use the NDK version 37 | // declared in /android/app/build.gradle file of the Flutter project. 38 | // Replace it with a version number if this plugin requires a specfic NDK version. 39 | // (e.g. ndkVersion "23.1.7779620") 40 | ndkVersion android.ndkVersion 41 | 42 | compileOptions { 43 | sourceCompatibility JavaVersion.VERSION_17 44 | targetCompatibility JavaVersion.VERSION_17 45 | } 46 | 47 | defaultConfig { 48 | minSdkVersion 19 49 | } 50 | } 51 | 52 | apply from: "../cargokit/gradle/plugin.gradle" 53 | cargokit { 54 | manifestDir = "../rust" 55 | libname = "simple_native_image_compress" 56 | } 57 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "5874a72aa4c779a02553007c47dacbefba2374dc" 8 | channel: "stable" 9 | 10 | project_type: plugin_ffi 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 17 | base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 18 | - platform: android 19 | create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 20 | base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 21 | - platform: ios 22 | create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 23 | base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 24 | - platform: linux 25 | create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 26 | base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 27 | - platform: macos 28 | create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 29 | base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 30 | - platform: windows 31 | create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 32 | base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc 33 | 34 | # User provided section 35 | 36 | # List of Local paths (relative to this file) that should be 37 | # ignored by the migrate tool. 38 | # 39 | # Files that are not part of the templates will be ignored by default. 40 | unmanaged_files: 41 | - 'lib/main.dart' 42 | - 'ios/Runner.xcodeproj/project.pbxproj' 43 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Untitled 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | simple_native_image_compress 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /cargokit/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Matej Knopp 2 | 3 | ================================================================================ 4 | 5 | MIT LICENSE 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 20 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | ================================================================================ 25 | 26 | APACHE LICENSE, VERSION 2.0 27 | 28 | Licensed under the Apache License, Version 2.0 (the "License"); 29 | you may not use this file except in compliance with the License. 30 | You may obtain a copy of the License at 31 | 32 | http://www.apache.org/licenses/LICENSE-2.0 33 | 34 | Unless required by applicable law or agreed to in writing, software 35 | distributed under the License is distributed on an "AS IS" BASIS, 36 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 37 | See the License for the specific language governing permissions and 38 | limitations under the License. 39 | 40 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | unsigned int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length == 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /macos/simple_native_image_compress.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint simple_native_image_compress.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'simple_native_image_compress' 7 | s.version = '0.0.1' 8 | s.summary = 'A new Flutter FFI plugin project.' 9 | s.description = <<-DESC 10 | A new Flutter FFI plugin project. 11 | DESC 12 | s.homepage = 'https://hyunwookim.net' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'me@hyunwookim.net' } 15 | 16 | # This will ensure the source files in Classes/ are included in the native 17 | # builds of apps using this FFI plugin. Podspec does not support relative 18 | # paths, so Classes contains a forwarder C file that relatively imports 19 | # `../src/*` so that the C sources can be shared among all target platforms. 20 | s.source = { :path => '.' } 21 | s.source_files = 'Classes/**/*' 22 | s.dependency 'FlutterMacOS' 23 | 24 | s.platform = :osx, '10.11' 25 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } 26 | s.swift_version = '5.0' 27 | 28 | s.script_phase = { 29 | :name => 'Build Rust library', 30 | # First argument is relative path to the `rust` folder, second is name of rust library 31 | :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../rust simple_native_image_compress', 32 | :execution_position => :before_compile, 33 | :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], 34 | # Let XCode know that the static library referenced in -force_load below is 35 | # created by this build step. 36 | :output_files => ["${BUILT_PRODUCTS_DIR}/libsimple_native_image_compress.a"], 37 | } 38 | s.pod_target_xcconfig = { 39 | 'DEFINES_MODULE' => 'YES', 40 | # Flutter.framework does not contain a i386 slice. 41 | 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', 42 | 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/libsimple_native_image_compress.a', 43 | } 44 | end -------------------------------------------------------------------------------- /rust/src/api/encode.rs: -------------------------------------------------------------------------------- 1 | use image::{ 2 | codecs::{avif, jpeg::JpegEncoder, webp}, 3 | DynamicImage, 4 | }; 5 | 6 | use super::constants::CompressFormat; 7 | 8 | pub(crate) fn img_buffer_to_bytes_with_alpha( 9 | compress_format: CompressFormat, 10 | buffer: &image::ImageBuffer, Vec>, 11 | quality: u8, 12 | speed: Option, 13 | ) -> anyhow::Result> { 14 | let mut bytes: Vec = Vec::new(); 15 | if compress_format == CompressFormat::WebP { 16 | let encoder = webp::WebPEncoder::new_lossless(&mut bytes); 17 | buffer.write_with_encoder(encoder)?; 18 | } else { 19 | let encoder = 20 | avif::AvifEncoder::new_with_speed_quality(&mut bytes, speed.unwrap_or(10), quality); 21 | buffer.write_with_encoder(encoder)?; 22 | } 23 | 24 | return Ok(bytes); 25 | } 26 | 27 | pub(crate) fn img_buffer_to_bytes_without_alpha( 28 | buffer: &image::ImageBuffer, Vec>, 29 | quality: u8, 30 | ) -> anyhow::Result> { 31 | let mut bytes: Vec = Vec::new(); 32 | let encoder = JpegEncoder::new_with_quality(&mut bytes, quality); 33 | buffer.write_with_encoder(encoder)?; 34 | return Ok(bytes); 35 | } 36 | 37 | pub(crate) fn dyn_img_to_bytes( 38 | dyn_img: &DynamicImage, 39 | compress_format: CompressFormat, 40 | quality: u8, 41 | speed: Option, 42 | ) -> anyhow::Result> { 43 | let mut bytes: Vec = Vec::new(); 44 | match compress_format { 45 | CompressFormat::WebP => { 46 | let encoder = webp::WebPEncoder::new_lossless(&mut bytes); 47 | dyn_img.write_with_encoder(encoder)?; 48 | } 49 | CompressFormat::AVIF => { 50 | let encoder = 51 | avif::AvifEncoder::new_with_speed_quality(&mut bytes, speed.unwrap_or(10), quality); 52 | dyn_img.write_with_encoder(encoder)?; 53 | } 54 | _ => { 55 | let encoder = JpegEncoder::new_with_quality(&mut bytes, quality); 56 | dyn_img.to_rgb8().write_with_encoder(encoder)?; 57 | } 58 | } 59 | return Ok(bytes); 60 | } 61 | -------------------------------------------------------------------------------- /cargokit/build_pod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | BASEDIR=$(dirname "$0") 5 | 6 | # Workaround for https://github.com/dart-lang/pub/issues/4010 7 | BASEDIR=$(cd "$BASEDIR" ; pwd -P) 8 | 9 | # Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project 10 | NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"` 11 | 12 | export PATH=${NEW_PATH%?} # remove trailing : 13 | 14 | env 15 | 16 | # Platform name (macosx, iphoneos, iphonesimulator) 17 | export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME 18 | 19 | # Arctive architectures (arm64, armv7, x86_64), space separated. 20 | export CARGOKIT_DARWIN_ARCHS=$ARCHS 21 | 22 | # Current build configuration (Debug, Release) 23 | export CARGOKIT_CONFIGURATION=$CONFIGURATION 24 | 25 | # Path to directory containing Cargo.toml. 26 | export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1 27 | 28 | # Temporary directory for build artifacts. 29 | export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR 30 | 31 | # Output directory for final artifacts. 32 | export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME 33 | 34 | # Directory to store built tool artifacts. 35 | export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool 36 | 37 | # Directory inside root project. Not necessarily the top level directory of root project. 38 | export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT 39 | 40 | FLUTTER_EXPORT_BUILD_ENVIRONMENT=( 41 | "$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS 42 | "$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS 43 | ) 44 | 45 | for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}" 46 | do 47 | if [[ -f "$path" ]]; then 48 | source "$path" 49 | fi 50 | done 51 | 52 | sh "$BASEDIR/run_build_tool.sh" build-pod "$@" 53 | 54 | # Make a symlink from built framework to phony file, which will be used as input to 55 | # build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate 56 | # attribute on custom build phase) 57 | ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony" 58 | ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out" 59 | -------------------------------------------------------------------------------- /ios/simple_native_image_compress.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint simple_native_image_compress.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'simple_native_image_compress' 7 | s.version = '0.0.1' 8 | s.summary = 'A new Flutter FFI plugin project.' 9 | s.description = <<-DESC 10 | A new Flutter FFI plugin project. 11 | DESC 12 | s.homepage = 'https://hyunwookim.net' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'me@hyunwookim.net' } 15 | 16 | # This will ensure the source files in Classes/ are included in the native 17 | # builds of apps using this FFI plugin. Podspec does not support relative 18 | # paths, so Classes contains a forwarder C file that relatively imports 19 | # `../src/*` so that the C sources can be shared among all target platforms. 20 | s.source = { :path => '.' } 21 | s.source_files = 'Classes/**/*' 22 | s.dependency 'Flutter' 23 | s.platform = :ios, '11.0' 24 | 25 | # Flutter.framework does not contain a i386 slice. 26 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 27 | s.swift_version = '5.0' 28 | 29 | s.script_phase = { 30 | :name => 'Build Rust library', 31 | # First argument is relative path to the `rust` folder, second is name of rust library 32 | :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../rust simple_native_image_compress', 33 | :execution_position => :before_compile, 34 | :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], 35 | # Let XCode know that the static library referenced in -force_load below is 36 | # created by this build step. 37 | :output_files => ["${BUILT_PRODUCTS_DIR}/libsimple_native_image_compress.a"], 38 | } 39 | s.pod_target_xcconfig = { 40 | 'DEFINES_MODULE' => 'YES', 41 | # Flutter.framework does not contain a i386 slice. 42 | 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', 43 | 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/libsimple_native_image_compress.a', 44 | } 45 | end -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/environment.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | extension on String { 4 | String resolveSymlink() => File(this).resolveSymbolicLinksSync(); 5 | } 6 | 7 | class Environment { 8 | /// Current build configuration (debug or release). 9 | static String get configuration => 10 | _getEnv("CARGOKIT_CONFIGURATION").toLowerCase(); 11 | 12 | static bool get isDebug => configuration == 'debug'; 13 | static bool get isRelease => configuration == 'release'; 14 | 15 | /// Temporary directory where Rust build artifacts are placed. 16 | static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR"); 17 | 18 | /// Final output directory where the build artifacts are placed. 19 | static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR'); 20 | 21 | /// Path to the crate manifest (containing Cargo.toml). 22 | static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR'); 23 | 24 | /// Directory inside root project. Not necessarily root folder. Symlinks are 25 | /// not resolved on purpose. 26 | static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR'); 27 | 28 | // Pod 29 | 30 | /// Platform name (macosx, iphoneos, iphonesimulator). 31 | static String get darwinPlatformName => 32 | _getEnv("CARGOKIT_DARWIN_PLATFORM_NAME"); 33 | 34 | /// List of architectures to build for (arm64, armv7, x86_64). 35 | static List get darwinArchs => 36 | _getEnv("CARGOKIT_DARWIN_ARCHS").split(' '); 37 | 38 | // Gradle 39 | static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION"); 40 | static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION"); 41 | static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR"); 42 | static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME"); 43 | static List get targetPlatforms => 44 | _getEnv("CARGOKIT_TARGET_PLATFORMS").split(','); 45 | 46 | // CMAKE 47 | static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM"); 48 | 49 | static String _getEnv(String key) { 50 | final res = Platform.environment[key]; 51 | if (res == null) { 52 | throw Exception("Missing environment variable $key"); 53 | } 54 | return res; 55 | } 56 | 57 | static String _getEnvPath(String key) { 58 | final res = _getEnv(key); 59 | if (Directory(res).existsSync()) { 60 | return res.resolveSymlink(); 61 | } else { 62 | return res; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/verify_binaries.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:ed25519_edwards/ed25519_edwards.dart'; 4 | import 'package:http/http.dart'; 5 | 6 | import 'artifacts_provider.dart'; 7 | import 'cargo.dart'; 8 | import 'crate_hash.dart'; 9 | import 'options.dart'; 10 | import 'precompile_binaries.dart'; 11 | import 'target.dart'; 12 | 13 | class VerifyBinaries { 14 | VerifyBinaries({ 15 | required this.manifestDir, 16 | }); 17 | 18 | final String manifestDir; 19 | 20 | Future run() async { 21 | final crateInfo = CrateInfo.load(manifestDir); 22 | 23 | final config = CargokitCrateOptions.load(manifestDir: manifestDir); 24 | final precompiledBinaries = config.precompiledBinaries; 25 | if (precompiledBinaries == null) { 26 | stdout.writeln('Crate does not support precompiled binaries.'); 27 | } else { 28 | final crateHash = CrateHash.compute(manifestDir); 29 | stdout.writeln('Crate hash: $crateHash'); 30 | 31 | for (final target in Target.all) { 32 | final message = 'Checking ${target.rust}...'; 33 | stdout.write(message.padRight(40)); 34 | stdout.flush(); 35 | 36 | final artifacts = getArtifactNames( 37 | target: target, 38 | libraryName: crateInfo.packageName, 39 | remote: true, 40 | ); 41 | 42 | final prefix = precompiledBinaries.uriPrefix; 43 | 44 | bool ok = true; 45 | 46 | for (final artifact in artifacts) { 47 | final fileName = PrecompileBinaries.fileName(target, artifact); 48 | final signatureFileName = 49 | PrecompileBinaries.signatureFileName(target, artifact); 50 | 51 | final url = Uri.parse('$prefix$crateHash/$fileName'); 52 | final signatureUrl = 53 | Uri.parse('$prefix$crateHash/$signatureFileName'); 54 | 55 | final signature = await get(signatureUrl); 56 | if (signature.statusCode != 200) { 57 | stdout.writeln('MISSING'); 58 | ok = false; 59 | break; 60 | } 61 | final asset = await get(url); 62 | if (asset.statusCode != 200) { 63 | stdout.writeln('MISSING'); 64 | ok = false; 65 | break; 66 | } 67 | 68 | if (!verify(precompiledBinaries.publicKey, asset.bodyBytes, 69 | signature.bodyBytes)) { 70 | stdout.writeln('INVALID SIGNATURE'); 71 | ok = false; 72 | } 73 | } 74 | 75 | if (ok) { 76 | stdout.writeln('OK'); 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /cargokit/build_tool/test/rustup_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:build_tool/src/rustup.dart'; 2 | import 'package:build_tool/src/util.dart'; 3 | import 'package:test/test.dart'; 4 | 5 | void main() { 6 | test('rustup with no toolchains', () { 7 | bool didListToolchains = false; 8 | bool didInstallStable = false; 9 | bool didListTargets = false; 10 | testRunCommandOverride = (args) { 11 | expect(args.executable, 'rustup'); 12 | switch (args.arguments) { 13 | case ['toolchain', 'list']: 14 | didListToolchains = true; 15 | return TestRunCommandResult(stdout: 'no installed toolchains\n'); 16 | case ['toolchain', 'install', 'stable']: 17 | didInstallStable = true; 18 | return TestRunCommandResult(); 19 | case ['target', 'list', '--toolchain', 'stable', '--installed']: 20 | didListTargets = true; 21 | return TestRunCommandResult( 22 | stdout: 'x86_64-unknown-linux-gnu\nx86_64-apple-darwin\n'); 23 | default: 24 | throw Exception('Unexpected call: ${args.arguments}'); 25 | } 26 | }; 27 | final rustup = Rustup(); 28 | rustup.installToolchain('stable'); 29 | expect(didInstallStable, true); 30 | expect(didListToolchains, true); 31 | expect(didListTargets, true); 32 | expect(rustup.installedTargets('stable'), [ 33 | 'x86_64-unknown-linux-gnu', 34 | 'x86_64-apple-darwin', 35 | ]); 36 | testRunCommandOverride = null; 37 | }); 38 | 39 | test('rustup with esp toolchain', () { 40 | final targetsQueried = []; 41 | testRunCommandOverride = (args) { 42 | expect(args.executable, 'rustup'); 43 | switch (args.arguments) { 44 | case ['toolchain', 'list']: 45 | return TestRunCommandResult( 46 | stdout: 'stable-aarch64-apple-darwin (default)\n' 47 | 'nightly-aarch64-apple-darwin\n' 48 | 'esp\n'); 49 | case ['target', 'list', '--toolchain', String toolchain, '--installed']: 50 | targetsQueried.add(toolchain); 51 | return TestRunCommandResult(stdout: '$toolchain:target\n'); 52 | default: 53 | throw Exception('Unexpected call: ${args.arguments}'); 54 | } 55 | }; 56 | final rustup = Rustup(); 57 | expect(targetsQueried, [ 58 | 'stable-aarch64-apple-darwin', 59 | 'nightly-aarch64-apple-darwin', 60 | ]); 61 | expect(rustup.installedTargets('stable'), 62 | ['stable-aarch64-apple-darwin:target']); 63 | expect(rustup.installedTargets('nightly'), 64 | ['nightly-aarch64-apple-darwin:target']); 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /cargokit/build_tool/test/options_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:build_tool/src/builder.dart'; 2 | import 'package:build_tool/src/options.dart'; 3 | import 'package:hex/hex.dart'; 4 | import 'package:test/test.dart'; 5 | import 'package:yaml/yaml.dart'; 6 | 7 | void main() { 8 | test('parseCargoBuildOptions', () { 9 | final yaml = """ 10 | toolchain: nightly 11 | extra_flags: 12 | - -Z 13 | # Comment here 14 | - build-std=panic_abort,std 15 | """; 16 | final node = loadYamlNode(yaml); 17 | final options = CargoBuildOptions.parse(node); 18 | expect(options.toolchain, Toolchain.nightly); 19 | expect(options.flags, ['-Z', 'build-std=panic_abort,std']); 20 | }); 21 | 22 | test('parsePrecompiledBinaries', () { 23 | final yaml = """ 24 | url_prefix: https://url-prefix 25 | public_key: a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445 26 | """; 27 | final precompiledBinaries = PrecompiledBinaries.parse(loadYamlNode(yaml)); 28 | final key = HEX.decode( 29 | 'a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445'); 30 | expect(precompiledBinaries.uriPrefix, 'https://url-prefix'); 31 | expect(precompiledBinaries.publicKey.bytes, key); 32 | }); 33 | 34 | test('parseCargokitOptions', () { 35 | const yaml = ''' 36 | cargo: 37 | # For smalles binaries rebuilt the standard library with panic=abort 38 | debug: 39 | toolchain: nightly 40 | extra_flags: 41 | - -Z 42 | # Comment here 43 | - build-std=panic_abort,std 44 | release: 45 | toolchain: beta 46 | 47 | precompiled_binaries: 48 | url_prefix: https://url-prefix 49 | public_key: a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445 50 | '''; 51 | final options = CargokitCrateOptions.parse(loadYamlNode(yaml)); 52 | expect(options.precompiledBinaries?.uriPrefix, 'https://url-prefix'); 53 | final key = HEX.decode( 54 | 'a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445'); 55 | expect(options.precompiledBinaries?.publicKey.bytes, key); 56 | 57 | final debugOptions = options.cargo[BuildConfiguration.debug]!; 58 | expect(debugOptions.toolchain, Toolchain.nightly); 59 | expect(debugOptions.flags, ['-Z', 'build-std=panic_abort,std']); 60 | 61 | final releaseOptions = options.cargo[BuildConfiguration.release]!; 62 | expect(releaseOptions.toolchain, Toolchain.beta); 63 | expect(releaseOptions.flags, []); 64 | }); 65 | 66 | test('parseCargokitUserOptions', () { 67 | const yaml = ''' 68 | use_precompiled_binaries: false 69 | verbose_logging: true 70 | '''; 71 | final options = CargokitUserOptions.parse(loadYamlNode(yaml)); 72 | expect(options.usePrecompiledBinaries, false); 73 | expect(options.verboseLogging, true); 74 | }); 75 | } 76 | -------------------------------------------------------------------------------- /cargokit/run_build_tool.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | setlocal ENABLEDELAYEDEXPANSION 5 | 6 | SET BASEDIR=%~dp0 7 | 8 | if not exist "%CARGOKIT_TOOL_TEMP_DIR%" ( 9 | mkdir "%CARGOKIT_TOOL_TEMP_DIR%" 10 | ) 11 | cd /D "%CARGOKIT_TOOL_TEMP_DIR%" 12 | 13 | SET BUILD_TOOL_PKG_DIR=%BASEDIR%build_tool 14 | SET DART=%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dart 15 | 16 | set BUILD_TOOL_PKG_DIR_POSIX=%BUILD_TOOL_PKG_DIR:\=/% 17 | 18 | ( 19 | echo name: build_tool_runner 20 | echo version: 1.0.0 21 | echo publish_to: none 22 | echo. 23 | echo environment: 24 | echo sdk: '^>=3.0.0 ^<4.0.0' 25 | echo. 26 | echo dependencies: 27 | echo build_tool: 28 | echo path: %BUILD_TOOL_PKG_DIR_POSIX% 29 | ) >pubspec.yaml 30 | 31 | if not exist bin ( 32 | mkdir bin 33 | ) 34 | 35 | ( 36 | echo import 'package:build_tool/build_tool.dart' as build_tool; 37 | echo void main^(List^ args^) ^{ 38 | echo build_tool.runMain^(args^); 39 | echo ^} 40 | ) >bin\build_tool_runner.dart 41 | 42 | SET PRECOMPILED=bin\build_tool_runner.dill 43 | 44 | REM To detect changes in package we compare output of DIR /s (recursive) 45 | set PREV_PACKAGE_INFO=.dart_tool\package_info.prev 46 | set CUR_PACKAGE_INFO=.dart_tool\package_info.cur 47 | 48 | DIR "%BUILD_TOOL_PKG_DIR%" /s > "%CUR_PACKAGE_INFO%_orig" 49 | 50 | REM Last line in dir output is free space on harddrive. That is bound to 51 | REM change between invocation so we need to remove it 52 | ( 53 | Set "Line=" 54 | For /F "UseBackQ Delims=" %%A In ("%CUR_PACKAGE_INFO%_orig") Do ( 55 | SetLocal EnableDelayedExpansion 56 | If Defined Line Echo !Line! 57 | EndLocal 58 | Set "Line=%%A") 59 | ) >"%CUR_PACKAGE_INFO%" 60 | DEL "%CUR_PACKAGE_INFO%_orig" 61 | 62 | REM Compare current directory listing with previous 63 | FC /B "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" > nul 2>&1 64 | 65 | If %ERRORLEVEL% neq 0 ( 66 | REM Changed - copy current to previous and remove precompiled kernel 67 | if exist "%PREV_PACKAGE_INFO%" ( 68 | DEL "%PREV_PACKAGE_INFO%" 69 | ) 70 | MOVE /Y "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" 71 | if exist "%PRECOMPILED%" ( 72 | DEL "%PRECOMPILED%" 73 | ) 74 | ) 75 | 76 | REM There is no CUR_PACKAGE_INFO it was renamed in previous step to %PREV_PACKAGE_INFO% 77 | REM which means we need to do pub get and precompile 78 | if not exist "%PRECOMPILED%" ( 79 | echo Running pub get in "%cd%" 80 | "%DART%" pub get --no-precompile 81 | "%DART%" compile kernel bin/build_tool_runner.dart 82 | ) 83 | 84 | "%DART%" "%PRECOMPILED%" %* 85 | 86 | REM 253 means invalid snapshot version. 87 | If %ERRORLEVEL% equ 253 ( 88 | "%DART%" pub get --no-precompile 89 | "%DART%" compile kernel bin/build_tool_runner.dart 90 | "%DART%" "%PRECOMPILED%" %* 91 | ) 92 | -------------------------------------------------------------------------------- /cargokit/run_build_tool.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | BASEDIR=$(dirname "$0") 6 | 7 | mkdir -p "$CARGOKIT_TOOL_TEMP_DIR" 8 | 9 | cd "$CARGOKIT_TOOL_TEMP_DIR" 10 | 11 | # Write a very simple bin package in temp folder that depends on build_tool package 12 | # from Cargokit. This is done to ensure that we don't pollute Cargokit folder 13 | # with .dart_tool contents. 14 | 15 | BUILD_TOOL_PKG_DIR="$BASEDIR/build_tool" 16 | 17 | if [[ -z $FLUTTER_ROOT ]]; then # not defined 18 | DART=dart 19 | else 20 | DART="$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart" 21 | fi 22 | 23 | cat << EOF > "pubspec.yaml" 24 | name: build_tool_runner 25 | version: 1.0.0 26 | publish_to: none 27 | 28 | environment: 29 | sdk: '>=3.0.0 <4.0.0' 30 | 31 | dependencies: 32 | build_tool: 33 | path: "$BUILD_TOOL_PKG_DIR" 34 | EOF 35 | 36 | mkdir -p "bin" 37 | 38 | cat << EOF > "bin/build_tool_runner.dart" 39 | import 'package:build_tool/build_tool.dart' as build_tool; 40 | void main(List args) { 41 | build_tool.runMain(args); 42 | } 43 | EOF 44 | 45 | # Create alias for `shasum` if it does not exist and `sha1sum` exists 46 | if ! [ -x "$(command -v shasum)" ] && [ -x "$(command -v sha1sum)" ]; then 47 | shopt -s expand_aliases 48 | alias shasum="sha1sum" 49 | fi 50 | 51 | # Dart run will not cache any package that has a path dependency, which 52 | # is the case for our build_tool_runner. So instead we precompile the package 53 | # ourselves. 54 | # To invalidate the cached kernel we use the hash of ls -LR of the build_tool 55 | # package directory. This should be good enough, as the build_tool package 56 | # itself is not meant to have any path dependencies. 57 | 58 | if [[ "$OSTYPE" == "darwin"* ]]; then 59 | PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum) 60 | else 61 | PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum) 62 | fi 63 | 64 | PACKAGE_HASH_FILE=".package_hash" 65 | 66 | if [ -f "$PACKAGE_HASH_FILE" ]; then 67 | EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE") 68 | if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then 69 | rm "$PACKAGE_HASH_FILE" 70 | fi 71 | fi 72 | 73 | # Run pub get if needed. 74 | if [ ! -f "$PACKAGE_HASH_FILE" ]; then 75 | "$DART" pub get --no-precompile 76 | "$DART" compile kernel bin/build_tool_runner.dart 77 | echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE" 78 | fi 79 | 80 | # Rebuild the tool if it was deleted by Android Studio 81 | if [ ! -f "bin/build_tool_runner.dill" ]; then 82 | "$DART" compile kernel bin/build_tool_runner.dart 83 | fi 84 | 85 | set +e 86 | 87 | "$DART" bin/build_tool_runner.dill "$@" 88 | 89 | exit_code=$? 90 | 91 | # 253 means invalid snapshot version. 92 | if [ $exit_code == 253 ]; then 93 | "$DART" pub get --no-precompile 94 | "$DART" compile kernel bin/build_tool_runner.dart 95 | "$DART" bin/build_tool_runner.dill "$@" 96 | exit_code=$? 97 | fi 98 | 99 | exit $exit_code 100 | -------------------------------------------------------------------------------- /rust/src/api/orientation.rs: -------------------------------------------------------------------------------- 1 | use exif::{Exif, In, Tag}; 2 | use image::{imageops, DynamicImage}; 3 | 4 | pub(crate) fn check_from_filepath(path_str: &str) -> anyhow::Result { 5 | let file = std::fs::File::open(path_str)?; 6 | let mut buf_reader = std::io::BufReader::new(&file); 7 | let exif_reader = exif::Reader::new(); 8 | let exif: Exif = match exif_reader.read_from_container(&mut buf_reader) { 9 | Result::Ok(exif) => exif, 10 | Result::Err(error) => match error { 11 | exif::Error::NotFound(_) | exif::Error::BlankValue(_) => return Ok(1), 12 | _ => return Err(anyhow::anyhow!(error.to_string())), 13 | }, 14 | }; 15 | 16 | let orientation = match exif.get_field(Tag::Orientation, In::PRIMARY) { 17 | Some(orientation) => match orientation.value.get_uint(0) { 18 | Some(v @ 1..=8) => v, 19 | _ => 1, 20 | }, 21 | None => 1, 22 | }; 23 | return Ok(orientation); 24 | } 25 | 26 | // pub(crate) fn check_from_bytes(buf: Vec) -> anyhow::Result { 27 | // let exif_reader = exif::Reader::new(); 28 | // let exif: Exif = match exif_reader.read_raw(buf) { 29 | // Result::Ok(exif) => exif, 30 | // Result::Err(error) => match error { 31 | // exif::Error::NotFound(_) | exif::Error::BlankValue(_) => return Ok(1), 32 | // _ => return Err(anyhow::anyhow!(error.to_string())), 33 | // }, 34 | // }; 35 | 36 | // let orientation = match exif.get_field(Tag::Orientation, In::PRIMARY) { 37 | // Some(orientation) => match orientation.value.get_uint(0) { 38 | // Some(v @ 1..=8) => v, 39 | // _ => 1, 40 | // }, 41 | // None => 1, 42 | // }; 43 | // return Ok(orientation); 44 | // } 45 | 46 | /* 47 | https://magnushoff.com/articles/jpeg-orientation/ 48 | */ 49 | pub(crate) fn rotate(orientation: u32, dyn_img: DynamicImage) -> DynamicImage { 50 | match orientation { 51 | // filp Horizontally 52 | 2 | 4 => return DynamicImage::ImageRgba8(imageops::flip_horizontal(&dyn_img)), 53 | // rotate180 54 | 3 => return DynamicImage::ImageRgba8(imageops::rotate180(&dyn_img)), 55 | // rotate 90 & filp Horizontally 56 | 5 => { 57 | let temp_img = DynamicImage::ImageRgba8(imageops::rotate90(&dyn_img)); 58 | return DynamicImage::ImageRgba8(imageops::flip_horizontal(&temp_img)); 59 | } 60 | // rotate90 61 | 6 => return DynamicImage::ImageRgba8(imageops::rotate90(&dyn_img)), 62 | // filp Horizontally & rotate 270 63 | 7 => { 64 | let temp_img = DynamicImage::ImageRgba8(imageops::flip_horizontal(&dyn_img)); 65 | return DynamicImage::ImageRgba8(imageops::rotate270(&temp_img)); 66 | } 67 | // rotate 270 68 | 8 => return DynamicImage::ImageRgba8(imageops::rotate270(&dyn_img)), 69 | // don't do anything 70 | _ => return dyn_img, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/build_pod.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path/path.dart' as path; 4 | 5 | import 'artifacts_provider.dart'; 6 | import 'builder.dart'; 7 | import 'environment.dart'; 8 | import 'options.dart'; 9 | import 'target.dart'; 10 | import 'util.dart'; 11 | 12 | class BuildPod { 13 | BuildPod({required this.userOptions}); 14 | 15 | final CargokitUserOptions userOptions; 16 | 17 | Future build() async { 18 | final targets = Environment.darwinArchs.map((arch) { 19 | final target = Target.forDarwin( 20 | platformName: Environment.darwinPlatformName, darwinAarch: arch); 21 | if (target == null) { 22 | throw Exception( 23 | "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); 24 | } 25 | return target; 26 | }).toList(); 27 | 28 | final environment = BuildEnvironment.fromEnvironment(isAndroid: false); 29 | final provider = 30 | ArtifactProvider(environment: environment, userOptions: userOptions); 31 | final artifacts = await provider.getArtifacts(targets); 32 | 33 | void performLipo(String targetFile, Iterable sourceFiles) { 34 | runCommand("lipo", [ 35 | '-create', 36 | ...sourceFiles, 37 | '-output', 38 | targetFile, 39 | ]); 40 | } 41 | 42 | final outputDir = Environment.outputDir; 43 | 44 | Directory(outputDir).createSync(recursive: true); 45 | 46 | final staticLibs = artifacts.values 47 | .expand((element) => element) 48 | .where((element) => element.type == AritifactType.staticlib) 49 | .toList(); 50 | final dynamicLibs = artifacts.values 51 | .expand((element) => element) 52 | .where((element) => element.type == AritifactType.dylib) 53 | .toList(); 54 | 55 | final libName = environment.crateInfo.packageName; 56 | 57 | // If there is static lib, use it and link it with pod 58 | if (staticLibs.isNotEmpty) { 59 | final finalTargetFile = path.join(outputDir, "lib$libName.a"); 60 | performLipo(finalTargetFile, staticLibs.map((e) => e.path)); 61 | } else { 62 | // Otherwise try to replace bundle dylib with our dylib 63 | final bundlePaths = [ 64 | '$libName.framework/Versions/A/$libName', 65 | '$libName.framework/$libName', 66 | ]; 67 | 68 | for (final bundlePath in bundlePaths) { 69 | final targetFile = path.join(outputDir, bundlePath); 70 | if (File(targetFile).existsSync()) { 71 | performLipo(targetFile, dynamicLibs.map((e) => e.path)); 72 | 73 | // Replace absolute id with @rpath one so that it works properly 74 | // when moved to Frameworks. 75 | runCommand("install_name_tool", [ 76 | '-id', 77 | '@rpath/$bundlePath', 78 | targetFile, 79 | ]); 80 | return; 81 | } 82 | } 83 | throw Exception('Unable to find bundle for dynamic library'); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: simple_native_image_compress 2 | description: "A simple native image compression library for Flutter that supports Windows and Linux as well" 3 | version: 3.0.2 4 | homepage: https://www.hyunwookim.net 5 | repository: https://github.com/hwkim1127/simple_native_image_compress 6 | 7 | environment: 8 | sdk: ^3.5.1 9 | flutter: '>=3.3.0' 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | flutter_rust_bridge: 2.10.0 15 | plugin_platform_interface: ^2.1.8 16 | 17 | dev_dependencies: 18 | ffi: ^2.1.3 19 | ffigen: ^14.0.1 20 | flutter_test: 21 | sdk: flutter 22 | flutter_lints: ^4.0.0 23 | integration_test: 24 | sdk: flutter 25 | 26 | # For information on the generic Dart part of this file, see the 27 | # following page: https://dart.dev/tools/pub/pubspec 28 | 29 | # The following section is specific to Flutter packages. 30 | flutter: 31 | # This section identifies this Flutter project as a plugin project. 32 | # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) 33 | # which should be registered in the plugin registry. This is required for 34 | # using method channels. 35 | # The Android 'package' specifies package in which the registered class is. 36 | # This is required for using method channels on Android. 37 | # The 'ffiPlugin' specifies that native code should be built and bundled. 38 | # This is required for using `dart:ffi`. 39 | # All these are used by the tooling to maintain consistency when 40 | # adding or updating assets for this project. 41 | # 42 | # Please refer to README.md for a detailed explanation. 43 | plugin: 44 | platforms: 45 | android: 46 | ffiPlugin: true 47 | ios: 48 | ffiPlugin: true 49 | linux: 50 | ffiPlugin: true 51 | macos: 52 | ffiPlugin: true 53 | windows: 54 | ffiPlugin: true 55 | 56 | # To add assets to your plugin package, add an assets section, like this: 57 | # assets: 58 | # - images/a_dot_burr.jpeg 59 | # - images/a_dot_ham.jpeg 60 | # 61 | # For details regarding assets in packages, see 62 | # https://flutter.dev/to/asset-from-package 63 | # 64 | # An image asset can refer to one or more resolution-specific "variants", see 65 | # https://flutter.dev/to/resolution-aware-images 66 | 67 | # To add custom fonts to your plugin package, add a fonts section here, 68 | # in this "flutter" section. Each entry in this list should have a 69 | # "family" key with the font family name, and a "fonts" key with a 70 | # list giving the asset and other descriptors for the font. For 71 | # example: 72 | # fonts: 73 | # - family: Schyler 74 | # fonts: 75 | # - asset: fonts/Schyler-Regular.ttf 76 | # - asset: fonts/Schyler-Italic.ttf 77 | # style: italic 78 | # - family: Trajan Pro 79 | # fonts: 80 | # - asset: fonts/TrajanPro.ttf 81 | # - asset: fonts/TrajanPro_Bold.ttf 82 | # weight: 700 83 | # 84 | # For details regarding fonts in packages, see 85 | # https://flutter.dev/to/font-from-package 86 | -------------------------------------------------------------------------------- /example/linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 3.0.2 2 | * **FEAT**: Updated the `cargokit` dependency. 3 | * **DEPRECATED**: Deprecated `contain`, `fitWidth`, and `fitHeight` in favor of `containFromFilePath`, `fitWidthFromFilePath`, and `fitHeightFromFilePath`. 4 | 5 | ## 3.0.1 6 | * **RETRACTED**: Do not use. Replaced by version `3.0.2`. 7 | 8 | ## 3.0.0 9 | > Note: This release has breaking changes. and WILL NOT WORK FOR FLUTTER VERSION BELOW 3.32.0 10 | * **ANDROID BREAKING CHANGE**: 11 | - Flutter plugin name change for Gradle, since it seems like Flutter Team completely changed the methods of the FlutterPlugin class when porting from Groovy to Kotlin. 12 | * **DEPENDENCY UPDATE**: `flutter_rust_bridge` 2.9.0 -> 2.10.0 13 | 14 | ## 2.5.0 15 | * **DEPENDENCY UPDATE**: `flutter_rust_bridge` 2.8.0 -> 2.9.0 16 | 17 | ## 2.4.0 18 | * **DEPENDENCY UPDATE**: `flutter_rust_bridge` 2.7.0 -> 2.8.0 19 | * **ANDROID DEPENDENCIES UPDATE**: 20 | * org.jetbrains.kotlin.android: 1.7.10 -> 2.1.0 21 | * com.android.tools.build:gradle: 7.3.0 -> 8.7.3 22 | * Compile SDK Version: 33 -> 35 23 | 24 | ## 2.3.1 25 | * **FIX**: ignoring AVIF compression format for small images fixed. 26 | 27 | ## 2.3.0 - RETRACTED DUE TO AVIF FORMAT NOT USED FOR SMALL IMAGES 28 | * **FEAT**: added `CompressFormat.avif` 29 | * **ANDROID JAVA UPDATE**: JAVA 8 -> JAVA 17 30 | 31 | ## 2.2.0 32 | * **DEPENDENCY UPDATE**: `flutter_rust_bridge` 2.6.0 -> 2.7.0 33 | 34 | ## 2.1.0 35 | * Rust 1.60 or later is now required to build 36 | * **DEPENDENCY UPDATE**: `flutter_rust_bridge` 2.5.0 -> 2.6.0 and 37 | * **DEPENDENCY UPDATE**: `plugin_platform_interface` 2.0.2 -> 2.1.8 38 | 39 | ## 2.0.4 40 | * **DEPENDENCY UPDATE**: `flutter_rust_bridge` 2.4.0 -> 2.5.0 41 | 42 | ## 2.0.3 43 | * **FEAT**: now guesses image file format instead of using file extensions to figure out 44 | 45 | ## 2.0.2 - RETRACTED DUE TO CRLF LINE ENDING 46 | 47 | * **FEAT**: now guesses image file format instead of using file extensions to figure out 48 | 49 | 50 | ## 2.0.1 51 | > Note: This release has breaking changes. 52 | * **DEPENDENCY UPDATE**: `flutter_rust_bridge` 2.3.0 -> 2.4.0 53 | 54 | ## 2.0.0 55 | > Note: This release has breaking changes. 56 | * **DOCS**: README.md update 57 | * **FEAT**: bump flutter_rust_bridge to version 2.3.0 58 | * **BREAKING** **REFACTOR**: 59 | - 1. The library should not be called as `singleton`. Just add `await NativeImageCompress.init();` for initialization. 60 | - 2. Methods are now static with different class name. Therefore, the methods now should be called with `ImageCompress` class. i.e) `ImageCompress.contain` 61 | 62 | ## 1.1.1+2 63 | 64 | * README.md link fix 65 | 66 | ## 1.1.1+1 67 | 68 | * README.md update 69 | 70 | ## 1.1.1 71 | 72 | * README.md update 73 | * cargokit update 74 | 75 | ## 1.1.0 76 | 77 | * Now keeps the image orientation when converted 78 | * Sampling Filter can be set 79 | 80 | ## 1.0.2 81 | 82 | * FIX: fixed CR LF problem 83 | 84 | ## 1.0.1+6 85 | 86 | * podspec description update 87 | 88 | ## 1.0.1+5 89 | 90 | * Example and README.md updated to show a way to call the library as a singleton. 91 | 92 | ## 1.0.1+4 93 | 94 | * homepage update just to get pub.dev score 95 | * CHANGELOG.md update 96 | 97 | ## 1.0.1+2 98 | 99 | * Now panic when wrong image file path is given 100 | * ios info.plist for example updated with permission 101 | 102 | ## 1.0.1+1 103 | 104 | * Added missing Android Plugin 105 | * flutter debug mode now compiles optimized rust code 106 | 107 | ## 1.0.1 108 | 109 | * Lowered minimum Dart SDK version 110 | 111 | ## 1.0.0 112 | 113 | * Initial release. 114 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "simple_native_image_compress" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "simple_native_image_compress" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2025 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "simple_native_image_compress.exe" "\0" 98 | VALUE "ProductName", "simple_native_image_compress" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 64 | 66 | 72 | 73 | 74 | 75 | 81 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # Set fallback configurations for older versions of the flutter tool. 14 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 15 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 16 | endif() 17 | 18 | # === Flutter Library === 19 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 20 | 21 | # Published to parent scope for install step. 22 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 23 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 24 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 25 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 26 | 27 | list(APPEND FLUTTER_LIBRARY_HEADERS 28 | "flutter_export.h" 29 | "flutter_windows.h" 30 | "flutter_messenger.h" 31 | "flutter_plugin_registrar.h" 32 | "flutter_texture_registrar.h" 33 | ) 34 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 35 | add_library(flutter INTERFACE) 36 | target_include_directories(flutter INTERFACE 37 | "${EPHEMERAL_DIR}" 38 | ) 39 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 40 | add_dependencies(flutter flutter_assemble) 41 | 42 | # === Wrapper === 43 | list(APPEND CPP_WRAPPER_SOURCES_CORE 44 | "core_implementations.cc" 45 | "standard_codec.cc" 46 | ) 47 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 48 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 49 | "plugin_registrar.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 52 | list(APPEND CPP_WRAPPER_SOURCES_APP 53 | "flutter_engine.cc" 54 | "flutter_view_controller.cc" 55 | ) 56 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 57 | 58 | # Wrapper sources needed for a plugin. 59 | add_library(flutter_wrapper_plugin STATIC 60 | ${CPP_WRAPPER_SOURCES_CORE} 61 | ${CPP_WRAPPER_SOURCES_PLUGIN} 62 | ) 63 | apply_standard_settings(flutter_wrapper_plugin) 64 | set_target_properties(flutter_wrapper_plugin PROPERTIES 65 | POSITION_INDEPENDENT_CODE ON) 66 | set_target_properties(flutter_wrapper_plugin PROPERTIES 67 | CXX_VISIBILITY_PRESET hidden) 68 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 69 | target_include_directories(flutter_wrapper_plugin PUBLIC 70 | "${WRAPPER_ROOT}/include" 71 | ) 72 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 73 | 74 | # Wrapper sources needed for the runner. 75 | add_library(flutter_wrapper_app STATIC 76 | ${CPP_WRAPPER_SOURCES_CORE} 77 | ${CPP_WRAPPER_SOURCES_APP} 78 | ) 79 | apply_standard_settings(flutter_wrapper_app) 80 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 81 | target_include_directories(flutter_wrapper_app PUBLIC 82 | "${WRAPPER_ROOT}/include" 83 | ) 84 | add_dependencies(flutter_wrapper_app flutter_assemble) 85 | 86 | # === Flutter tool backend === 87 | # _phony_ is a non-existent file to force this command to run every time, 88 | # since currently there's no way to get a full input/output list from the 89 | # flutter tool. 90 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 91 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 92 | add_custom_command( 93 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 94 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 95 | ${CPP_WRAPPER_SOURCES_APP} 96 | ${PHONY_OUTPUT} 97 | COMMAND ${CMAKE_COMMAND} -E env 98 | ${FLUTTER_TOOL_ENVIRONMENT} 99 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 100 | ${FLUTTER_TARGET_PLATFORM} $ 101 | VERBATIM 102 | ) 103 | add_custom_target(flutter_assemble DEPENDS 104 | "${FLUTTER_LIBRARY}" 105 | ${FLUTTER_LIBRARY_HEADERS} 106 | ${CPP_WRAPPER_SOURCES_CORE} 107 | ${CPP_WRAPPER_SOURCES_PLUGIN} 108 | ${CPP_WRAPPER_SOURCES_APP} 109 | ) 110 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/crate_hash.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:collection/collection.dart'; 6 | import 'package:convert/convert.dart'; 7 | import 'package:crypto/crypto.dart'; 8 | import 'package:path/path.dart' as path; 9 | 10 | class CrateHash { 11 | /// Computes a hash uniquely identifying crate content. This takes into account 12 | /// content all all .rs files inside the src directory, as well as Cargo.toml, 13 | /// Cargo.lock, build.rs and cargokit.yaml. 14 | /// 15 | /// If [tempStorage] is provided, computed hash is stored in a file in that directory 16 | /// and reused on subsequent calls if the crate content hasn't changed. 17 | static String compute(String manifestDir, {String? tempStorage}) { 18 | return CrateHash._( 19 | manifestDir: manifestDir, 20 | tempStorage: tempStorage, 21 | )._compute(); 22 | } 23 | 24 | CrateHash._({ 25 | required this.manifestDir, 26 | required this.tempStorage, 27 | }); 28 | 29 | String _compute() { 30 | final files = getFiles(); 31 | final tempStorage = this.tempStorage; 32 | if (tempStorage != null) { 33 | final quickHash = _computeQuickHash(files); 34 | final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash')); 35 | quickHashFolder.createSync(recursive: true); 36 | final quickHashFile = File(path.join(quickHashFolder.path, quickHash)); 37 | if (quickHashFile.existsSync()) { 38 | return quickHashFile.readAsStringSync(); 39 | } 40 | final hash = _computeHash(files); 41 | quickHashFile.writeAsStringSync(hash); 42 | return hash; 43 | } else { 44 | return _computeHash(files); 45 | } 46 | } 47 | 48 | /// Computes a quick hash based on files stat (without reading contents). This 49 | /// is used to cache the real hash, which is slower to compute since it involves 50 | /// reading every single file. 51 | String _computeQuickHash(List files) { 52 | final output = AccumulatorSink(); 53 | final input = sha256.startChunkedConversion(output); 54 | 55 | final data = ByteData(8); 56 | for (final file in files) { 57 | input.add(utf8.encode(file.path)); 58 | final stat = file.statSync(); 59 | data.setUint64(0, stat.size); 60 | input.add(data.buffer.asUint8List()); 61 | data.setUint64(0, stat.modified.millisecondsSinceEpoch); 62 | input.add(data.buffer.asUint8List()); 63 | } 64 | 65 | input.close(); 66 | return base64Url.encode(output.events.single.bytes); 67 | } 68 | 69 | String _computeHash(List files) { 70 | final output = AccumulatorSink(); 71 | final input = sha256.startChunkedConversion(output); 72 | 73 | void addTextFile(File file) { 74 | // text Files are hashed by lines in case we're dealing with github checkout 75 | // that auto-converts line endings. 76 | final splitter = LineSplitter(); 77 | if (file.existsSync()) { 78 | final data = file.readAsStringSync(); 79 | final lines = splitter.convert(data); 80 | for (final line in lines) { 81 | input.add(utf8.encode(line)); 82 | } 83 | } 84 | } 85 | 86 | for (final file in files) { 87 | addTextFile(file); 88 | } 89 | 90 | input.close(); 91 | final res = output.events.single; 92 | 93 | // Truncate to 128bits. 94 | final hash = res.bytes.sublist(0, 16); 95 | return hex.encode(hash); 96 | } 97 | 98 | List getFiles() { 99 | final src = Directory(path.join(manifestDir, 'src')); 100 | final files = src 101 | .listSync(recursive: true, followLinks: false) 102 | .whereType() 103 | .toList(); 104 | files.sortBy((element) => element.path); 105 | void addFile(String relative) { 106 | final file = File(path.join(manifestDir, relative)); 107 | if (file.existsSync()) { 108 | files.add(file); 109 | } 110 | } 111 | 112 | addFile('Cargo.toml'); 113 | addFile('Cargo.lock'); 114 | addFile('build.rs'); 115 | addFile('cargokit.yaml'); 116 | return files; 117 | } 118 | 119 | final String manifestDir; 120 | final String? tempStorage; 121 | } 122 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 44 | 50 | 51 | 52 | 53 | 54 | 66 | 68 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/target.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:collection/collection.dart'; 4 | 5 | import 'util.dart'; 6 | 7 | class Target { 8 | Target({ 9 | required this.rust, 10 | this.flutter, 11 | this.android, 12 | this.androidMinSdkVersion, 13 | this.darwinPlatform, 14 | this.darwinArch, 15 | }); 16 | 17 | static final all = [ 18 | Target( 19 | rust: 'armv7-linux-androideabi', 20 | flutter: 'android-arm', 21 | android: 'armeabi-v7a', 22 | androidMinSdkVersion: 16, 23 | ), 24 | Target( 25 | rust: 'aarch64-linux-android', 26 | flutter: 'android-arm64', 27 | android: 'arm64-v8a', 28 | androidMinSdkVersion: 21, 29 | ), 30 | Target( 31 | rust: 'i686-linux-android', 32 | flutter: 'android-x86', 33 | android: 'x86', 34 | androidMinSdkVersion: 16, 35 | ), 36 | Target( 37 | rust: 'x86_64-linux-android', 38 | flutter: 'android-x64', 39 | android: 'x86_64', 40 | androidMinSdkVersion: 21, 41 | ), 42 | Target( 43 | rust: 'x86_64-pc-windows-msvc', 44 | flutter: 'windows-x64', 45 | ), 46 | Target( 47 | rust: 'aarch64-pc-windows-msvc', 48 | flutter: 'windows-arm64', 49 | ), 50 | Target( 51 | rust: 'x86_64-unknown-linux-gnu', 52 | flutter: 'linux-x64', 53 | ), 54 | Target( 55 | rust: 'aarch64-unknown-linux-gnu', 56 | flutter: 'linux-arm64', 57 | ), 58 | Target(rust: 'riscv64gc-unknown-linux-gnu', flutter: 'linux-riscv64'), 59 | Target( 60 | rust: 'x86_64-apple-darwin', 61 | darwinPlatform: 'macosx', 62 | darwinArch: 'x86_64', 63 | ), 64 | Target( 65 | rust: 'aarch64-apple-darwin', 66 | darwinPlatform: 'macosx', 67 | darwinArch: 'arm64', 68 | ), 69 | Target( 70 | rust: 'aarch64-apple-ios', 71 | darwinPlatform: 'iphoneos', 72 | darwinArch: 'arm64', 73 | ), 74 | Target( 75 | rust: 'aarch64-apple-ios-sim', 76 | darwinPlatform: 'iphonesimulator', 77 | darwinArch: 'arm64', 78 | ), 79 | Target( 80 | rust: 'x86_64-apple-ios', 81 | darwinPlatform: 'iphonesimulator', 82 | darwinArch: 'x86_64', 83 | ), 84 | ]; 85 | 86 | static Target? forFlutterName(String flutterName) { 87 | return all.firstWhereOrNull((element) => element.flutter == flutterName); 88 | } 89 | 90 | static Target? forDarwin({ 91 | required String platformName, 92 | required String darwinAarch, 93 | }) { 94 | return all.firstWhereOrNull((element) => // 95 | element.darwinPlatform == platformName && 96 | element.darwinArch == darwinAarch); 97 | } 98 | 99 | static Target? forRustTriple(String triple) { 100 | return all.firstWhereOrNull((element) => element.rust == triple); 101 | } 102 | 103 | static List androidTargets() { 104 | return all 105 | .where((element) => element.android != null) 106 | .toList(growable: false); 107 | } 108 | 109 | /// Returns buildable targets on current host platform ignoring Android targets. 110 | static List buildableTargets() { 111 | if (Platform.isLinux) { 112 | // Right now we don't support cross-compiling on Linux. So we just return 113 | // the host target. 114 | final arch = (runCommand('arch', []).stdout as String).trim(); 115 | if (arch == 'aarch64') { 116 | return [Target.forRustTriple('aarch64-unknown-linux-gnu')!]; 117 | } else if (arch == 'riscv64') { 118 | return [Target.forRustTriple('riscv64gc-unknown-linux-gnu')!]; 119 | } else { 120 | return [Target.forRustTriple('x86_64-unknown-linux-gnu')!]; 121 | } 122 | } 123 | return all.where((target) { 124 | if (Platform.isWindows) { 125 | return target.rust.contains('-windows-'); 126 | } else if (Platform.isMacOS) { 127 | return target.darwinPlatform != null; 128 | } 129 | return false; 130 | }).toList(growable: false); 131 | } 132 | 133 | @override 134 | String toString() { 135 | return rust; 136 | } 137 | 138 | final String? flutter; 139 | final String rust; 140 | final String? android; 141 | final int? androidMinSdkVersion; 142 | final String? darwinPlatform; 143 | final String? darwinArch; 144 | } 145 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/rustup.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:collection/collection.dart'; 4 | import 'package:path/path.dart' as path; 5 | 6 | import 'util.dart'; 7 | 8 | class _Toolchain { 9 | _Toolchain( 10 | this.name, 11 | this.targets, 12 | ); 13 | 14 | final String name; 15 | final List targets; 16 | } 17 | 18 | class Rustup { 19 | List? installedTargets(String toolchain) { 20 | final targets = _installedTargets(toolchain); 21 | return targets != null ? List.unmodifiable(targets) : null; 22 | } 23 | 24 | void installToolchain(String toolchain) { 25 | log.info("Installing Rust toolchain: $toolchain"); 26 | runCommand("rustup", ['toolchain', 'install', toolchain]); 27 | _installedToolchains 28 | .add(_Toolchain(toolchain, _getInstalledTargets(toolchain))); 29 | } 30 | 31 | void installTarget( 32 | String target, { 33 | required String toolchain, 34 | }) { 35 | log.info("Installing Rust target: $target"); 36 | runCommand("rustup", ['target', 'add', '--toolchain', toolchain, target]); 37 | _installedTargets(toolchain)?.add(target); 38 | } 39 | 40 | bool _didInstallZigBuild = false; 41 | 42 | void installZigBuild(String toolchain) { 43 | if (_didInstallZigBuild) { 44 | return; 45 | } 46 | 47 | log.info("Installing Zig build"); 48 | runCommand("rustup", [ 49 | 'run', 50 | toolchain, 51 | 'cargo', 52 | 'install', 53 | '--locked', 54 | 'cargo-zigbuild', 55 | ]); 56 | _didInstallZigBuild = true; 57 | } 58 | 59 | final List<_Toolchain> _installedToolchains; 60 | 61 | Rustup() : _installedToolchains = _getInstalledToolchains(); 62 | 63 | List? _installedTargets(String toolchain) => _installedToolchains 64 | .firstWhereOrNull( 65 | (e) => e.name == toolchain || e.name.startsWith('$toolchain-')) 66 | ?.targets; 67 | 68 | static List<_Toolchain> _getInstalledToolchains() { 69 | String extractToolchainName(String line) { 70 | // ignore (default) after toolchain name 71 | final parts = line.split(' '); 72 | return parts[0]; 73 | } 74 | 75 | final res = runCommand("rustup", ['toolchain', 'list']); 76 | 77 | // To list all non-custom toolchains, we need to filter out lines that 78 | // don't start with "stable", "beta", or "nightly". 79 | Pattern nonCustom = RegExp(r"^(stable|beta|nightly)"); 80 | final lines = res.stdout 81 | .toString() 82 | .split('\n') 83 | .where((e) => e.isNotEmpty && e.startsWith(nonCustom)) 84 | .map(extractToolchainName) 85 | .toList(growable: true); 86 | 87 | return lines 88 | .map( 89 | (name) => _Toolchain( 90 | name, 91 | _getInstalledTargets(name), 92 | ), 93 | ) 94 | .toList(growable: true); 95 | } 96 | 97 | static List _getInstalledTargets(String toolchain) { 98 | final res = runCommand("rustup", [ 99 | 'target', 100 | 'list', 101 | '--toolchain', 102 | toolchain, 103 | '--installed', 104 | ]); 105 | final lines = res.stdout 106 | .toString() 107 | .split('\n') 108 | .where((e) => e.isNotEmpty) 109 | .toList(growable: true); 110 | return lines; 111 | } 112 | 113 | bool _didInstallRustSrcForNightly = false; 114 | 115 | void installRustSrcForNightly() { 116 | if (_didInstallRustSrcForNightly) { 117 | return; 118 | } 119 | // Useful for -Z build-std 120 | runCommand( 121 | "rustup", 122 | ['component', 'add', 'rust-src', '--toolchain', 'nightly'], 123 | ); 124 | _didInstallRustSrcForNightly = true; 125 | } 126 | 127 | static String? executablePath() { 128 | final envPath = Platform.environment['PATH']; 129 | final envPathSeparator = Platform.isWindows ? ';' : ':'; 130 | final home = Platform.isWindows 131 | ? Platform.environment['USERPROFILE'] 132 | : Platform.environment['HOME']; 133 | final paths = [ 134 | if (home != null) path.join(home, '.cargo', 'bin'), 135 | if (envPath != null) ...envPath.split(envPathSeparator), 136 | ]; 137 | for (final p in paths) { 138 | final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup'; 139 | final rustupPath = path.join(p, rustup); 140 | if (File(rustupPath).existsSync()) { 141 | return rustupPath; 142 | } 143 | } 144 | return null; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /cargokit/cmake/cargokit.cmake: -------------------------------------------------------------------------------- 1 | SET(cargokit_cmake_root "${CMAKE_CURRENT_LIST_DIR}/..") 2 | 3 | # Workaround for https://github.com/dart-lang/pub/issues/4010 4 | get_filename_component(cargokit_cmake_root "${cargokit_cmake_root}" REALPATH) 5 | 6 | if(WIN32) 7 | # REALPATH does not properly resolve symlinks on windows :-/ 8 | execute_process(COMMAND powershell -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_LIST_DIR}/resolve_symlinks.ps1" "${cargokit_cmake_root}" OUTPUT_VARIABLE cargokit_cmake_root OUTPUT_STRIP_TRAILING_WHITESPACE) 9 | endif() 10 | 11 | # Arguments 12 | # - target: CMAKE target to which rust library is linked 13 | # - manifest_dir: relative path from current folder to directory containing cargo manifest 14 | # - lib_name: cargo package name 15 | # - any_symbol_name: name of any exported symbol from the library. 16 | # used on windows to force linking with library. 17 | function(apply_cargokit target manifest_dir lib_name any_symbol_name) 18 | 19 | set(CARGOKIT_LIB_NAME "${lib_name}") 20 | set(CARGOKIT_LIB_FULL_NAME "${CMAKE_SHARED_MODULE_PREFIX}${CARGOKIT_LIB_NAME}${CMAKE_SHARED_MODULE_SUFFIX}") 21 | if (CMAKE_CONFIGURATION_TYPES) 22 | set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$") 23 | set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$/${CARGOKIT_LIB_FULL_NAME}") 24 | else() 25 | set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") 26 | set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/${CARGOKIT_LIB_FULL_NAME}") 27 | endif() 28 | set(CARGOKIT_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargokit_build") 29 | 30 | if (FLUTTER_TARGET_PLATFORM) 31 | set(CARGOKIT_TARGET_PLATFORM "${FLUTTER_TARGET_PLATFORM}") 32 | else() 33 | set(CARGOKIT_TARGET_PLATFORM "windows-x64") 34 | endif() 35 | 36 | set(CARGOKIT_ENV 37 | "CARGOKIT_CMAKE=${CMAKE_COMMAND}" 38 | "CARGOKIT_CONFIGURATION=$" 39 | "CARGOKIT_MANIFEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${manifest_dir}" 40 | "CARGOKIT_TARGET_TEMP_DIR=${CARGOKIT_TEMP_DIR}" 41 | "CARGOKIT_OUTPUT_DIR=${CARGOKIT_OUTPUT_DIR}" 42 | "CARGOKIT_TARGET_PLATFORM=${CARGOKIT_TARGET_PLATFORM}" 43 | "CARGOKIT_TOOL_TEMP_DIR=${CARGOKIT_TEMP_DIR}/tool" 44 | "CARGOKIT_ROOT_PROJECT_DIR=${CMAKE_SOURCE_DIR}" 45 | ) 46 | 47 | if (WIN32) 48 | set(SCRIPT_EXTENSION ".cmd") 49 | set(IMPORT_LIB_EXTENSION ".lib") 50 | else() 51 | set(SCRIPT_EXTENSION ".sh") 52 | set(IMPORT_LIB_EXTENSION "") 53 | execute_process(COMMAND chmod +x "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}") 54 | endif() 55 | 56 | # Using generators in custom command is only supported in CMake 3.20+ 57 | if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0") 58 | foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) 59 | add_custom_command( 60 | OUTPUT 61 | "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}" 62 | "${CMAKE_CURRENT_BINARY_DIR}/_phony_" 63 | COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} 64 | "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake 65 | VERBATIM 66 | ) 67 | endforeach() 68 | else() 69 | add_custom_command( 70 | OUTPUT 71 | ${OUTPUT_LIB} 72 | "${CMAKE_CURRENT_BINARY_DIR}/_phony_" 73 | COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} 74 | "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake 75 | VERBATIM 76 | ) 77 | endif() 78 | 79 | 80 | set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE) 81 | 82 | if (TARGET ${target}) 83 | # If we have actual cmake target provided create target and make existing 84 | # target depend on it 85 | add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB}) 86 | add_dependencies("${target}" "${target}_cargokit") 87 | target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}") 88 | if(WIN32) 89 | target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}") 90 | endif() 91 | else() 92 | # Otherwise (FFI) just use ALL to force building always 93 | add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB}) 94 | endif() 95 | 96 | # Allow adding the output library to plugin bundled libraries 97 | set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE) 98 | 99 | endfunction() 100 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(simple_native_image_compress LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "simple_native_image_compress") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(VERSION 3.14...3.25) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | 56 | # Generated plugin build rules, which manage building the plugins and adding 57 | # them to the application. 58 | include(flutter/generated_plugins.cmake) 59 | 60 | 61 | # === Installation === 62 | # Support files are copied into place next to the executable, so that it can 63 | # run in place. This is done instead of making a separate bundle (as on Linux) 64 | # so that building and running from within Visual Studio will work. 65 | set(BUILD_BUNDLE_DIR "$") 66 | # Make the "install" step default, as it's required to run. 67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 68 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 70 | endif() 71 | 72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 74 | 75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 76 | COMPONENT Runtime) 77 | 78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 82 | COMPONENT Runtime) 83 | 84 | if(PLUGIN_BUNDLED_LIBRARIES) 85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 87 | COMPONENT Runtime) 88 | endif() 89 | 90 | # Copy the native assets provided by the build.dart from all packages. 91 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") 92 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 93 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 94 | COMPONENT Runtime) 95 | 96 | # Fully re-copy the assets directory on each build to avoid having stale files 97 | # from a previous install. 98 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 99 | install(CODE " 100 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 101 | " COMPONENT Runtime) 102 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 103 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 104 | 105 | # Install the AOT library on non-Debug builds only. 106 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 107 | CONFIGURATIONS Profile;Release 108 | COMPONENT Runtime) 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple_native_image_compress 2 | 3 | A simple native image compression library for Flutter written in rust using [flutter_rust_bridge][1], [image][2], [cargokit][3], [kamadak-exif][4] and [anyhow][5] 4 | 5 | ## Why? 6 | 7 | 1. For some reason, image compression in Dart is slow. Even with isolate. 8 | 2. There is no native libraries that supports WINDOWS & LINUX when it comes to image compression. 9 | 10 | ## What does it do? 11 | 12 | - If path for an image file is given, it will resize and return AVIF, Jpeg, or WebP image as Uint8List. 13 | 14 | ## What it does not do 15 | 16 | - Web Support with WASM. Since I use `Angular` for Web not `Flutter` 17 | 18 | ## Prerequisite 19 | 20 | 1. Rust 21 | 2. Android NDK for Android 22 | 23 | ## Setup 24 | 25 | 1. Follow the instructions [here][6] and install `Rust` 26 | 2. If installed already 27 | ```shell 28 | rustup update 29 | ``` 30 | 31 | #### Android 32 | 1. Install rust tools for Android 33 | ```shell 34 | cargo install cargo-ndk 35 | rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android i686-linux-android 36 | ``` 37 | 2. Since `simple_native_image_compress` requires Android NDK, install `Android NDK` via `Android Studio` and add ndkVersion in `android/app/build.gradle`. 38 | ```groovy 39 | android { 40 | // by default the project uses NDK version from flutter plugin. 41 | ndkVersion flutter.ndkVersion 42 | ``` 43 | 44 | #### Apple 45 | 1. Install rust tools for Apple 46 | ```shell 47 | cargo install cargo-xcode 48 | cargo install cargo-lipo 49 | ``` 50 | 2. If you are targeting iOS 51 | ```shell 52 | rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim 53 | ``` 54 | 55 | ## Supported Output Formats 56 | 57 | - Jpeg 58 | - WebP 59 | - lossless only 60 | - AVIF 61 | - lossy only 62 | - `speed` parameter can be given in the range 1-10, where 1 is the slowest and 10 is the fastest. Default is 10. 63 | 64 | 65 | ## Sampling Filter Types 66 | 67 | - 5 types of sampling filters are available and can be selected 68 | 1. Nearest, // Nearest Neighbor 69 | 2. Triangle, // Linear Filter (DEFAULT) 70 | 3. CatmullRom, // Cubic Filter 71 | 4. Gaussian, // Gaussian Filter 72 | 5. Lanczos3, // Lanczos with window 3 73 | - you can read more about sampling filters here [image crate doc][7] 74 | 75 | ## Example 76 | 77 | 1. Initialize Simple Native Image Compress in main function 78 | ```shell 79 | Future main() async { 80 | await NativeImageCompress.init(); 81 | runApp(const MaterialApp(home: MyApp())); 82 | } 83 | ``` 84 | 85 | 2. After initialization call static methods from ImageCompress class 86 | 87 | #### "contain" will make the image fit into the given max width/height. 88 | ```shell 89 | try{ 90 | final bytes = await ImageCompress.containFromFilepath( 91 | filePath: yourFilePath, 92 | compressFormat: CompressFormat.avif, 93 | speed: 3, // only for AVIF 94 | quality: 90, 95 | maxWidth: 512, 96 | maxHeight: 512, 97 | samplingFilter: FilterType.Lanczos3 98 | ); 99 | } catch (e) { 100 | print(e); 101 | } 102 | ``` 103 | 104 | #### "fitWidth" will make the image fit into the given max width. 105 | ```shell 106 | try{ 107 | final bytes = await ImageCompress.fitWidthFromFilepath( 108 | filePath: yourFilePath, 109 | compressFormat: CompressFormat.webP, 110 | maxWidth: 512, 111 | samplingFilter: FilterType.Lanczos3 112 | ); 113 | } catch (e) { 114 | print(e); 115 | } 116 | ``` 117 | 118 | #### "fitHeight" will make the image fit into the given max height. 119 | ```shell 120 | try{ 121 | final bytes = await ImageCompress.fitHeightFromFilepath( 122 | filePath: yourFilePath, 123 | compressFormat: CompressFormat.jpeg, 124 | maxHeight: 512, 125 | samplingFilter: FilterType.Lanczos3 126 | ); 127 | } catch (e) { 128 | print(e); 129 | } 130 | ``` 131 | 132 | ## Default values 133 | 134 | - Default value for width and/or height is `1024 px` 135 | - Default value for Jpeg/AVIF quality is `80` (For webP Quality does nothing) 136 | - Default value for samplingFilter is `FilterType.Triangle` 137 | - Default value for AVIF speed is `10` 138 | 139 | [1]: "flutter rust bridge github" 140 | [2]: "rust image crate github" 141 | [3]: "cargokit github" 142 | [4]: "exif-rs github" 143 | [5]: "Anyhow ¯\_(°ペ)_/¯" 144 | [6]: "rust install page" 145 | [7]: "sampling filters page" -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: "Demonstrates how to use the untitled plugin." 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 1.0.0+1 20 | 21 | environment: 22 | sdk: ^3.8.1 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | 34 | simple_native_image_compress: 35 | # When depending on this package from a real application you should use: 36 | # simple_native_image_compress: ^x.y.z 37 | # See https://dart.dev/tools/pub/dependencies#version-constraints 38 | # The example app is bundled with the plugin so we use a path dependency on 39 | # the parent directory to use the current plugin's version. 40 | path: ../ 41 | 42 | # The following adds the Cupertino Icons font to your application. 43 | # Use with the CupertinoIcons class for iOS style icons. 44 | cupertino_icons: ^1.0.8 45 | image_picker: 46 | file_picker: 47 | permission_handler: 48 | flutter_avif: ^3.0.0 49 | 50 | dev_dependencies: 51 | flutter_test: 52 | sdk: flutter 53 | 54 | # The "flutter_lints" package below contains a set of recommended lints to 55 | # encourage good coding practices. The lint set provided by the package is 56 | # activated in the `analysis_options.yaml` file located at the root of your 57 | # package. See that file for information about deactivating specific lint 58 | # rules and activating additional ones. 59 | flutter_lints: ^5.0.0 60 | 61 | # For information on the generic Dart part of this file, see the 62 | # following page: https://dart.dev/tools/pub/pubspec 63 | 64 | # The following section is specific to Flutter packages. 65 | flutter: 66 | 67 | # The following line ensures that the Material Icons font is 68 | # included with your application, so that you can use the icons in 69 | # the material Icons class. 70 | uses-material-design: true 71 | 72 | # To add assets to your application, add an assets section, like this: 73 | # assets: 74 | # - images/a_dot_burr.jpeg 75 | # - images/a_dot_ham.jpeg 76 | 77 | # An image asset can refer to one or more resolution-specific "variants", see 78 | # https://flutter.dev/to/resolution-aware-images 79 | 80 | # For details regarding adding assets from package dependencies, see 81 | # https://flutter.dev/to/asset-from-package 82 | 83 | # To add custom fonts to your application, add a fonts section here, 84 | # in this "flutter" section. Each entry in this list should have a 85 | # "family" key with the font family name, and a "fonts" key with a 86 | # list giving the asset and other descriptors for the font. For 87 | # example: 88 | # fonts: 89 | # - family: Schyler 90 | # fonts: 91 | # - asset: fonts/Schyler-Regular.ttf 92 | # - asset: fonts/Schyler-Italic.ttf 93 | # style: italic 94 | # - family: Trajan Pro 95 | # fonts: 96 | # - asset: fonts/TrajanPro.ttf 97 | # - asset: fonts/TrajanPro_Bold.ttf 98 | # weight: 700 99 | # 100 | # For details regarding fonts from package dependencies, 101 | # see https://flutter.dev/to/font-from-package 102 | -------------------------------------------------------------------------------- /cargokit/docs/precompiled_binaries.md: -------------------------------------------------------------------------------- 1 | # Precompiled Binaries 2 | 3 | Because Cargokit builds the Rust crate during Flutter build, it is inherently 4 | dependend on the Rust toolchain being installed on the developer's machine. 5 | 6 | To decrease the friction, it is possible for Cargokit to use precompiled binaries instead. 7 | 8 | This is how the process of using precompiled binaries looks from the perspective of the build on developer machine: 9 | 10 | 1. Cargokit checks if there is `cargokit_options.yaml` file in the root folder of target application. If there is one, it will be checked for `use_precompiled_binaries` options to see if user opted out of using precompiled binaries. In which case Cargokit will insist on building from source. Cargokit will also build from source if the configuration file is absent, but user has Rustup installed. 11 | 12 | 2. Cargokit checks if there is `cargokit.yaml` file placed in the Rust crate. If there is one, it will be checked for `precompiled_binaries` section to see if crate supports precompiled binaries. The configuration section must contain a public key and URL prefix. 13 | 14 | 3. Cargokit computes a `crate-hash`. This is a SHA256 hash value computed from all Rust files inside crate, `Cargo.toml`, `Cargo.lock` and `cargokit.yaml`. This uniquely identifies the crate and it is used to find the correct precompiled binaries. 15 | 16 | 4. Cargokit will attempt to download the precompiled binaries for target platform and `crate_hash` combination and a signature file for each downloaded binary. If download succeeds, the binary content will be verified against the signature and public key included in `cargokit.yaml` (which is part of Rust crate and thus part of published Flutter package). 17 | 18 | 5. If the verification succeeds, the precompiled binaries will be used. Otherwise the binary will be discarded and Cargokit will insist on building from source. 19 | 20 | ## Providing precompiled binaries 21 | 22 | Note that this assumes that precompiled binaries will be generated during github actions and deployed as github releases. 23 | 24 | ### Use `build_tool` to generate a key-pair: 25 | 26 | ``` 27 | dart run build_tool gen-key 28 | ``` 29 | 30 | This will print the private key and public key. Store the private key securely. It needs to be provided as a secret to github action. 31 | 32 | The public key should be included in `cargokit.yaml` file in the Rust crate. 33 | 34 | ### Provide a `cargokit.yaml` file in the Rust crate 35 | 36 | The file must be placed alongside Cargo.toml. 37 | 38 | ```yaml 39 | precompiled_binaries: 40 | # Uri prefix used when downloading precompiled binaries. 41 | url_prefix: https://github.com///releases/download/precompiled_ 42 | 43 | # Public key for verifying downloaded precompiled binaries. 44 | public_key: 45 | ``` 46 | 47 | ### Configure a github action to build and upload precompiled binaries. 48 | 49 | The github action should be run at every commit to main branch (and possibly other branches). 50 | 51 | The action needs two secrets - private key for signing binaries and GitHub token for uploading binaries as releases. Here is example action that precompiles and uploads binaries for all supported targets. 52 | 53 | ```yaml 54 | on: 55 | push: 56 | branches: [ main ] 57 | 58 | name: Precompile Binaries 59 | 60 | jobs: 61 | Precompile: 62 | runs-on: ${{ matrix.os }} 63 | strategy: 64 | fail-fast: false 65 | matrix: 66 | os: 67 | - ubuntu-latest 68 | - macOS-latest 69 | - windows-latest 70 | steps: 71 | - uses: actions/checkout@v2 72 | - uses: dart-lang/setup-dart@v1 73 | - name: Install GTK 74 | if: (matrix.os == 'ubuntu-latest') 75 | run: sudo apt-get update && sudo apt-get install libgtk-3-dev 76 | - name: Precompile 77 | if: (matrix.os == 'macOS-latest') || (matrix.os == 'windows-latest') 78 | run: dart run build_tool precompile-binaries -v --manifest-dir=../../rust --repository=superlistapp/super_native_extensions 79 | working-directory: super_native_extensions/cargokit/build_tool 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }} 82 | PRIVATE_KEY: ${{ secrets.RELEASE_PRIVATE_KEY }} 83 | - name: Precompile (with Android) 84 | if: (matrix.os == 'ubuntu-latest') 85 | run: dart run build_tool precompile-binaries -v --manifest-dir=../../rust --repository=superlistapp/super_native_extensions --android-sdk-location=/usr/local/lib/android/sdk --android-ndk-version=24.0.8215888 --android-min-sdk-version=23 86 | working-directory: super_native_extensions/cargokit/build_tool 87 | env: 88 | GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }} 89 | PRIVATE_KEY: ${{ secrets.RELEASE_PRIVATE_KEY }} 90 | ``` 91 | 92 | By default the `built_tool precompile-binaries` commands build and uploads the binaries for all targets buildable from current host. This can be overriden using the `--target ` argument. 93 | 94 | Android binaries will be built when `--android-sdk-location` and `--android-ndk-version` arguments are provided. 95 | 96 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:logging/logging.dart'; 5 | import 'package:path/path.dart' as path; 6 | 7 | import 'logging.dart'; 8 | import 'rustup.dart'; 9 | 10 | final log = Logger("process"); 11 | 12 | class CommandFailedException implements Exception { 13 | final String executable; 14 | final List arguments; 15 | final ProcessResult result; 16 | 17 | CommandFailedException({ 18 | required this.executable, 19 | required this.arguments, 20 | required this.result, 21 | }); 22 | 23 | @override 24 | String toString() { 25 | final stdout = result.stdout.toString().trim(); 26 | final stderr = result.stderr.toString().trim(); 27 | return [ 28 | "External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}", 29 | "Returned Exit Code: ${result.exitCode}", 30 | kSeparator, 31 | "STDOUT:", 32 | if (stdout.isNotEmpty) stdout, 33 | kSeparator, 34 | "STDERR:", 35 | if (stderr.isNotEmpty) stderr, 36 | ].join('\n'); 37 | } 38 | } 39 | 40 | class TestRunCommandArgs { 41 | final String executable; 42 | final List arguments; 43 | final String? workingDirectory; 44 | final Map? environment; 45 | final bool includeParentEnvironment; 46 | final bool runInShell; 47 | final Encoding? stdoutEncoding; 48 | final Encoding? stderrEncoding; 49 | 50 | TestRunCommandArgs({ 51 | required this.executable, 52 | required this.arguments, 53 | this.workingDirectory, 54 | this.environment, 55 | this.includeParentEnvironment = true, 56 | this.runInShell = false, 57 | this.stdoutEncoding, 58 | this.stderrEncoding, 59 | }); 60 | } 61 | 62 | class TestRunCommandResult { 63 | TestRunCommandResult({ 64 | this.pid = 1, 65 | this.exitCode = 0, 66 | this.stdout = '', 67 | this.stderr = '', 68 | }); 69 | 70 | final int pid; 71 | final int exitCode; 72 | final String stdout; 73 | final String stderr; 74 | } 75 | 76 | TestRunCommandResult Function(TestRunCommandArgs args)? testRunCommandOverride; 77 | 78 | ProcessResult runCommand( 79 | String executable, 80 | List arguments, { 81 | String? workingDirectory, 82 | Map? environment, 83 | bool includeParentEnvironment = true, 84 | bool runInShell = false, 85 | Encoding? stdoutEncoding = systemEncoding, 86 | Encoding? stderrEncoding = systemEncoding, 87 | }) { 88 | if (testRunCommandOverride != null) { 89 | final result = testRunCommandOverride!(TestRunCommandArgs( 90 | executable: executable, 91 | arguments: arguments, 92 | workingDirectory: workingDirectory, 93 | environment: environment, 94 | includeParentEnvironment: includeParentEnvironment, 95 | runInShell: runInShell, 96 | stdoutEncoding: stdoutEncoding, 97 | stderrEncoding: stderrEncoding, 98 | )); 99 | return ProcessResult( 100 | result.pid, 101 | result.exitCode, 102 | result.stdout, 103 | result.stderr, 104 | ); 105 | } 106 | log.finer('Running command $executable ${arguments.join(' ')}'); 107 | final res = Process.runSync( 108 | _resolveExecutable(executable), 109 | arguments, 110 | workingDirectory: workingDirectory, 111 | environment: environment, 112 | includeParentEnvironment: includeParentEnvironment, 113 | runInShell: runInShell, 114 | stderrEncoding: stderrEncoding, 115 | stdoutEncoding: stdoutEncoding, 116 | ); 117 | if (res.exitCode != 0) { 118 | throw CommandFailedException( 119 | executable: executable, 120 | arguments: arguments, 121 | result: res, 122 | ); 123 | } else { 124 | return res; 125 | } 126 | } 127 | 128 | class RustupNotFoundException implements Exception { 129 | @override 130 | String toString() { 131 | return [ 132 | ' ', 133 | 'rustup not found in PATH.', 134 | ' ', 135 | 'Maybe you need to install Rust? It only takes a minute:', 136 | ' ', 137 | if (Platform.isWindows) 'https://www.rust-lang.org/tools/install', 138 | if (hasHomebrewRustInPath()) ...[ 139 | '\$ brew unlink rust # Unlink homebrew Rust from PATH', 140 | ], 141 | if (!Platform.isWindows) 142 | "\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", 143 | ' ', 144 | ].join('\n'); 145 | } 146 | 147 | static bool hasHomebrewRustInPath() { 148 | if (!Platform.isMacOS) { 149 | return false; 150 | } 151 | final envPath = Platform.environment['PATH'] ?? ''; 152 | final paths = envPath.split(':'); 153 | return paths.any((p) { 154 | return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync(); 155 | }); 156 | } 157 | } 158 | 159 | String _resolveExecutable(String executable) { 160 | if (executable == 'rustup') { 161 | final resolved = Rustup.executablePath(); 162 | if (resolved != null) { 163 | return resolved; 164 | } 165 | throw RustupNotFoundException(); 166 | } else { 167 | return executable; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /example/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.13) 3 | project(runner LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "simple_native_image_compress") 8 | # The unique GTK application identifier for this application. See: 9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID 10 | set(APPLICATION_ID "com.example.untitled") 11 | 12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 13 | # versions of CMake. 14 | cmake_policy(SET CMP0063 NEW) 15 | 16 | # Load bundled libraries from the lib/ directory relative to the binary. 17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 18 | 19 | # Root filesystem for cross-building. 20 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 27 | endif() 28 | 29 | # Define build configuration options. 30 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 31 | set(CMAKE_BUILD_TYPE "Debug" CACHE 32 | STRING "Flutter build mode" FORCE) 33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 34 | "Debug" "Profile" "Release") 35 | endif() 36 | 37 | # Compilation settings that should be applied to most targets. 38 | # 39 | # Be cautious about adding new options here, as plugins use this function by 40 | # default. In most cases, you should add new options to specific targets instead 41 | # of modifying this function. 42 | function(APPLY_STANDARD_SETTINGS TARGET) 43 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 47 | endfunction() 48 | 49 | # Flutter library and tool build rules. 50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 51 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 52 | 53 | # System-level dependencies. 54 | find_package(PkgConfig REQUIRED) 55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 56 | 57 | # Application build; see runner/CMakeLists.txt. 58 | add_subdirectory("runner") 59 | 60 | # Run the Flutter tool portions of the build. This must not be removed. 61 | add_dependencies(${BINARY_NAME} flutter_assemble) 62 | 63 | # Only the install-generated bundle's copy of the executable will launch 64 | # correctly, since the resources must in the right relative locations. To avoid 65 | # people trying to run the unbundled copy, put it in a subdirectory instead of 66 | # the default top-level location. 67 | set_target_properties(${BINARY_NAME} 68 | PROPERTIES 69 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 70 | ) 71 | 72 | 73 | # Generated plugin build rules, which manage building the plugins and adding 74 | # them to the application. 75 | include(flutter/generated_plugins.cmake) 76 | 77 | 78 | # === Installation === 79 | # By default, "installing" just makes a relocatable bundle in the build 80 | # directory. 81 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 82 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 83 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 84 | endif() 85 | 86 | # Start with a clean build bundle directory every time. 87 | install(CODE " 88 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 89 | " COMPONENT Runtime) 90 | 91 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 92 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 93 | 94 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 95 | COMPONENT Runtime) 96 | 97 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 98 | COMPONENT Runtime) 99 | 100 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 101 | COMPONENT Runtime) 102 | 103 | foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) 104 | install(FILES "${bundled_library}" 105 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 106 | COMPONENT Runtime) 107 | endforeach(bundled_library) 108 | 109 | # Copy the native assets provided by the build.dart from all packages. 110 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") 111 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 112 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 113 | COMPONENT Runtime) 114 | 115 | # Fully re-copy the assets directory on each build to avoid having stale files 116 | # from a previous install. 117 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 118 | install(CODE " 119 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 120 | " COMPONENT Runtime) 121 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 122 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 123 | 124 | # Install the AOT library on non-Debug builds only. 125 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 126 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 127 | COMPONENT Runtime) 128 | endif() 129 | -------------------------------------------------------------------------------- /example/linux/runner/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "simple_native_image_compress"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "simple_native_image_compress"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GApplication::startup. 85 | static void my_application_startup(GApplication* application) { 86 | //MyApplication* self = MY_APPLICATION(object); 87 | 88 | // Perform any actions required at application startup. 89 | 90 | G_APPLICATION_CLASS(my_application_parent_class)->startup(application); 91 | } 92 | 93 | // Implements GApplication::shutdown. 94 | static void my_application_shutdown(GApplication* application) { 95 | //MyApplication* self = MY_APPLICATION(object); 96 | 97 | // Perform any actions required at application shutdown. 98 | 99 | G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); 100 | } 101 | 102 | // Implements GObject::dispose. 103 | static void my_application_dispose(GObject* object) { 104 | MyApplication* self = MY_APPLICATION(object); 105 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 106 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 107 | } 108 | 109 | static void my_application_class_init(MyApplicationClass* klass) { 110 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 111 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 112 | G_APPLICATION_CLASS(klass)->startup = my_application_startup; 113 | G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; 114 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 115 | } 116 | 117 | static void my_application_init(MyApplication* self) {} 118 | 119 | MyApplication* my_application_new() { 120 | // Set the program name to the application ID, which helps various systems 121 | // like GTK and desktop environments map this running application to its 122 | // corresponding .desktop file. This ensures better integration by allowing 123 | // the application to be recognized beyond its binary name. 124 | g_set_prgname(APPLICATION_ID); 125 | 126 | return MY_APPLICATION(g_object_new(my_application_get_type(), 127 | "application-id", APPLICATION_ID, 128 | "flags", G_APPLICATION_NON_UNIQUE, 129 | nullptr)); 130 | } 131 | -------------------------------------------------------------------------------- /lib/src/rust/api/compress.dart: -------------------------------------------------------------------------------- 1 | // This file is automatically generated, so please do not edit it. 2 | // @generated by `flutter_rust_bridge`@ 2.10.0. 3 | 4 | // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import 5 | 6 | import '../frb_generated.dart'; 7 | import 'constants.dart'; 8 | import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; 9 | 10 | // These functions are ignored because they are not marked as `pub`: `compress` 11 | 12 | class ImageCompress { 13 | const ImageCompress(); 14 | 15 | static Future contain( 16 | {required String filePath, 17 | CompressFormat? compressFormat, 18 | int? maxWidth, 19 | int? maxHeight, 20 | int? quality, 21 | FilterType? samplingFilter, 22 | int? speed}) => 23 | NativeImageCompress.instance.api.crateApiCompressImageCompressContain( 24 | filePath: filePath, 25 | compressFormat: compressFormat, 26 | maxWidth: maxWidth, 27 | maxHeight: maxHeight, 28 | quality: quality, 29 | samplingFilter: samplingFilter, 30 | speed: speed); 31 | 32 | static Future containFromBytes( 33 | {required List bytes, 34 | CompressFormat? compressFormat, 35 | int? maxWidth, 36 | int? maxHeight, 37 | int? quality, 38 | FilterType? samplingFilter, 39 | int? speed}) => 40 | NativeImageCompress.instance.api 41 | .crateApiCompressImageCompressContainFromBytes( 42 | bytes: bytes, 43 | compressFormat: compressFormat, 44 | maxWidth: maxWidth, 45 | maxHeight: maxHeight, 46 | quality: quality, 47 | samplingFilter: samplingFilter, 48 | speed: speed); 49 | 50 | static Future containFromFilepath( 51 | {required String filePath, 52 | CompressFormat? compressFormat, 53 | int? maxWidth, 54 | int? maxHeight, 55 | int? quality, 56 | FilterType? samplingFilter, 57 | int? speed}) => 58 | NativeImageCompress.instance.api 59 | .crateApiCompressImageCompressContainFromFilepath( 60 | filePath: filePath, 61 | compressFormat: compressFormat, 62 | maxWidth: maxWidth, 63 | maxHeight: maxHeight, 64 | quality: quality, 65 | samplingFilter: samplingFilter, 66 | speed: speed); 67 | 68 | static Future fitHeight( 69 | {required String filePath, 70 | CompressFormat? compressFormat, 71 | int? maxHeight, 72 | int? quality, 73 | FilterType? samplingFilter, 74 | int? speed}) => 75 | NativeImageCompress.instance.api.crateApiCompressImageCompressFitHeight( 76 | filePath: filePath, 77 | compressFormat: compressFormat, 78 | maxHeight: maxHeight, 79 | quality: quality, 80 | samplingFilter: samplingFilter, 81 | speed: speed); 82 | 83 | static Future fitHeightFromBytes( 84 | {required List bytes, 85 | CompressFormat? compressFormat, 86 | int? maxHeight, 87 | int? quality, 88 | FilterType? samplingFilter, 89 | int? speed}) => 90 | NativeImageCompress.instance.api 91 | .crateApiCompressImageCompressFitHeightFromBytes( 92 | bytes: bytes, 93 | compressFormat: compressFormat, 94 | maxHeight: maxHeight, 95 | quality: quality, 96 | samplingFilter: samplingFilter, 97 | speed: speed); 98 | 99 | static Future fitHeightFromFilepath( 100 | {required String filePath, 101 | CompressFormat? compressFormat, 102 | int? maxHeight, 103 | int? quality, 104 | FilterType? samplingFilter, 105 | int? speed}) => 106 | NativeImageCompress.instance.api 107 | .crateApiCompressImageCompressFitHeightFromFilepath( 108 | filePath: filePath, 109 | compressFormat: compressFormat, 110 | maxHeight: maxHeight, 111 | quality: quality, 112 | samplingFilter: samplingFilter, 113 | speed: speed); 114 | 115 | static Future fitWidth( 116 | {required String filePath, 117 | CompressFormat? compressFormat, 118 | int? maxWidth, 119 | int? quality, 120 | FilterType? samplingFilter, 121 | int? speed}) => 122 | NativeImageCompress.instance.api.crateApiCompressImageCompressFitWidth( 123 | filePath: filePath, 124 | compressFormat: compressFormat, 125 | maxWidth: maxWidth, 126 | quality: quality, 127 | samplingFilter: samplingFilter, 128 | speed: speed); 129 | 130 | static Future fitWidthFromBytes( 131 | {required List bytes, 132 | CompressFormat? compressFormat, 133 | int? maxWidth, 134 | int? quality, 135 | FilterType? samplingFilter, 136 | int? speed}) => 137 | NativeImageCompress.instance.api 138 | .crateApiCompressImageCompressFitWidthFromBytes( 139 | bytes: bytes, 140 | compressFormat: compressFormat, 141 | maxWidth: maxWidth, 142 | quality: quality, 143 | samplingFilter: samplingFilter, 144 | speed: speed); 145 | 146 | static Future fitWidthFromFilepath( 147 | {required String filePath, 148 | CompressFormat? compressFormat, 149 | int? maxWidth, 150 | int? quality, 151 | FilterType? samplingFilter, 152 | int? speed}) => 153 | NativeImageCompress.instance.api 154 | .crateApiCompressImageCompressFitWidthFromFilepath( 155 | filePath: filePath, 156 | compressFormat: compressFormat, 157 | maxWidth: maxWidth, 158 | quality: quality, 159 | samplingFilter: samplingFilter, 160 | speed: speed); 161 | 162 | @override 163 | int get hashCode => 0; 164 | 165 | @override 166 | bool operator ==(Object other) => 167 | identical(this, other) || 168 | other is ImageCompress && runtimeType == other.runtimeType; 169 | } 170 | --------------------------------------------------------------------------------