Skip to content

feature: Native assets Rust implementation #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d4fa974
feat: Completed rust implementation for blurhash algorithm.
Feb 1, 2025
182cf63
refactor: Updated API & removed unnecessary methods and classes.
Feb 4, 2025
0b73760
fix: Migrated to static library approach in podspec.
Feb 8, 2025
7171058
feat: Full implementation for iOS target.
Feb 10, 2025
9a6e686
feat:
Feb 10, 2025
e894962
feat: Added supporting for macos platform.
Feb 11, 2025
c2c5702
fix: Updated doc.
Feb 12, 2025
8692ab0
feat: Added windows target
shigomany Feb 12, 2025
8dff48b
fix: Prepared to migrate to native assets
shigomany Feb 14, 2025
a25bae2
fix: Restored dependencies.
shigomany Feb 16, 2025
2a63da8
feat: Extracted to dart package with Native Assets feature.
Feb 18, 2025
7cc72be
feat: Updated test-case files.
shigomany Feb 18, 2025
0fb6cd8
fix: Removed dependency image fro dev deps.
shigomany Feb 18, 2025
9b5c0fa
fix: Fixed error.
shigomany Feb 18, 2025
9cd3860
feat: Created package for Flutter and added example.
shigomany Feb 18, 2025
2a7ceec
refactor: Removed unnecessary files.
Feb 18, 2025
d21ba7c
fix: Implemented for iOS builds
Feb 18, 2025
f8bdfda
fix: Fixed decode method on Rust.
Feb 20, 2025
02703d4
fix: Added codec.
Feb 20, 2025
d9fbfed
fix: Fixed builds for macos
Feb 20, 2025
a27669c
fix: Removed unnecessary import
shigomany Feb 21, 2025
c69a1a9
fix: Fixed for windows build.
shigomany Feb 21, 2025
9650639
docs: Updated readme.md
shigomany Apr 19, 2025
55cd172
Merge branch 'main' of https://github.com/shigomany/blurhash_ffi into…
shigomany Apr 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .fvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"flutter": "master"
}
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,11 @@ migrate_working_dir/
.dart_tool/
.packages
build/

# Rust related
rust/target/
rust/pkg/

# FVM Version Cache
.fvm/
.fvmrc/
42 changes: 0 additions & 42 deletions .metadata

This file was deleted.

23 changes: 3 additions & 20 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,22 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "blurhash_ffi",
"request": "launch",
"type": "dart"
},
{
"name": "blurhash_ffi (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "blurhash_ffi (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
},
{
"name": "example",
"cwd": "example",
"cwd": "packages/flutter_blurhash_ffi/example",
"request": "launch",
"type": "dart"
},
{
"name": "example (profile mode)",
"cwd": "example",
"cwd": "packages/flutter_blurhash_ffi/example",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "example (release mode)",
"cwd": "example",
"cwd": "packages/flutter_blurhash_ffi/example",
"request": "launch",
"type": "dart",
"flutterMode": "release"
Expand Down
12 changes: 10 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,13 @@
"editor.fontWeight": "normal",
"files.associations": {
"blurhash_ffi.h": "c"
}
}
},
"yaml.schemas": {
"https://taskfile.dev/schema.json": [
"**/Taskfile.yml"
]
},
"task": {
"outputTo": "terminal"
},
}
158 changes: 80 additions & 78 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,142 +1,144 @@
# blurhash_ffi

A [Blurhash](https://blurha.sh) compact Image placeholder encoder and decoder FFI implementation for flutter in C, Supports Android, iOS, Linux, macOS and Windows.
A [Blurhash](https://blurha.sh) compact Image placeholder encoder and decoder FFI implementation for Flutter in Rust via experemental feature [Native Assets](https://github.com/flutter/flutter/issues/129757).

Matches the official [Blurhash](https://github.com/woltapp/blurhash) implementation in performance and quality.


![blurhash_ffi](https://firebasestorage.googleapis.com/v0/b/folksable-d4dc8.appspot.com/o/blurhash_ffi.png?alt=media&token=e6c7e81b-1798-403b-b055-68a1f767d21f)

# Supports

| Platform | Status | Description |
| -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Android | ✅ | - |
| iOS | ✅ | - |
| macOS | ⚠️ | Worked if merge this [PR](https://github.com/irondash/native_toolchain_rust/pull/26) |
| Linux | ⚠️ | Not tested |
| Windows | ⚠️ | Worked but exist problem: long path length error via compilation ([meta issue](https://github.com/rust-lang/cargo/issues/9770) and [issue](https://github.com/rust-lang/cargo/issues/13141) which describe this ) |
| Web | ❌ | Rust can compiled to WebAssembly, but no supported yet. This library compiled on standalone wasm module (not in Empscripten, AssemblyScript) that Dart can't be use non-JS enviroments. Also, Dart should use FFI bindings for call wasm module. Stopped issues: [comment](https://github.com/dart-lang/sdk/issues/37355#issuecomment-2135064545) |

# Installation

Add dependency to your `pubspec.yaml` file following the command below:


For dart projects:

```sh
dart pub add dart_blurhash_ffi
```

For flutter projects:

```sh
dart pub add flutter_blurhash_ffi
```

## Usage

To use this plugin, add `blurhash_ffi` as a dependency in your pubspec.yaml file

**One Step (both Encoding & Decoding) Usage**
**Flutter usage**

```dart
import 'package:blurhash_ffi/blurhash_ffi.dart';
import 'package:flutter_blurhash_ffi/flutter_blurhash_ffi.dart';

/// Encoding and Decoding all in One Step
///
/// `ImageProvider` in -> Send your Image to be encoded.
/// `ImageProvider` out -> Get your blurry image version.

class BlurhashMyImage extends StatelessWidget {
final String imageUrl;
const BlurhashMyImage({required this.imageUrl, super.key});

@override
Widget build(BuildContext context) {
return Image(
image: BlurhashTheImage(
image: BlurhashFfiImage.fromImageProvider(
NetworkImage(imageUrl), // you can use any image provider of your choice.
decodingHeight: 1920, decodingWidth: 1080),
decodingHeight: 1920,
decodingWidth: 1080,
),
alignment: Alignment.center,
fit: BoxFit.cover
);
}
}


class BlurhashMyImage2 extends StatelessWidget {
final String blurhash;
const BlurhashMyImage2({required this.blurhash, super.key});

@override
Widget build(BuildContext context) {
return Image(
image: BlurhashFfiImage(blurhash),
width: 200,
height: 200,
alignment: Alignment.center,
fit: BoxFit.cover
);
}
}

```

> [!WARNING]
> Not recommend use it (without _blurhash string param_), because in this case, encoding and decoding will be performed. This effect can be obtained almost through [ImageFiltered](https://api.flutter.dev/flutter/widgets/ImageFiltered-class.html).

**Encoding**

Encoding now uses sync interface (without `Future`).

<?code-excerpt "readme_excerpts.dart (Example)"?>

```dart
import 'package:blurhash_ffi/blurhash_ffi.dart';
import 'package:dart_blurhash_ffi/dart_blurhash_ffi.dart';

/// Encoding a blurhash from an image provider
///
/// You can use any ImageProvider you want, including NetworkImage, FileImage, MemoryImage, AssetImage, etc.
final imageProvider = NetworkImage('https://picsum.photos/512');
final imageProvider2 = AssetImage('assets/image.jpg');
final Uint8List blurhashImageBytes = File('assets/image.webp').readAsBytesSync();

/// Signature
/// static Future<String> encode(
/// ImageProvider imageProvider, {
/// static String encode(
/// Uint8List data, {
/// int componentX = 4,
/// int componentY = 3,
/// })
/// may throw `BlurhashFFIException` if encoding fails.
final String blurHash = await BlurhashFFI.encode(imageProvider);
final String blurHash = BlurhashFFI.encode(blurhashImageBytes);

// Or via codec

final String blurHashSame = BlurhashFfiCodec().encoder.convert(blurhashImageBytes);

```

**Decoding**
```dart
import 'package:blurhash_ffi/blurhash_ffi.dart';
import 'dart:ui' as ui;
/// You have 3 ways to decode a blurhash
///
/// 1. Using the `BlurhashFfi` widget
/// 2. Using the `BlurhashFfiImage` ImageProvider
/// 3. Using the `BlurhashFfi.decode` static method

/// 1. Using the `BlurhashFfi` widget (same constructor as flutter_blurhash's Blurhash widget)
class BlurHashApp extends StatelessWidget {
const BlurHashApp({Key? key}) : super(key: key);
Decoding now uses sync interface (without `Future`).

@override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("BlurHash")),
body: const SizedBox.expand(
child: Center(
child: AspectRatio(
aspectRatio: 1.6,
child: BlurhashFfi(hash: "L5H2EC=PM+yV0g-mq.wG9c010J}I"),
),
),
),
),
);
}

/// 2. Using the `BlurhashFfiImage` ImageProvider
final imageProvider = BlurhashFfiImage("L5H2EC=PM+yV0g-mq.wG9c010J}I");
class BlurHashApp2 extends StatelessWidget {
const BlurHashApp2({Key? key}) : super(key: key);
```dart
import 'package:dart_blurhash_ffi/dart_blurhash_ffi.dart';

@override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("BlurHash")),
body: const SizedBox.expand(
child: Center(
child: AspectRatio(
aspectRatio: 1.6,
child: Image(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
),
),
);
}
final String blurhash = 'LGFO~6Yk^6#M@-5c,1Ex@@or[j6o';

/// 3. Using the `BlurhashFfi.decode` static method which returns dart:ui.Image
/// Signature
/// static Future<ui.Image> decode(
/// String blurHash, {
/// Signature
///
/// static Uint8List decode(
/// String blurhash, {
/// int width = 32,
/// int height = 32,
/// int punch = 1,
/// })
/// may throw `BlurhashFFIException` if decoding fails.
final ui.Image image = await BlurhashFFI.decode("L5H2EC=PM+yV0g-mq.wG9c010J}I");
```
final decoded = BlurhashFFI.decode(blurhash);

**Release Isolate and it's memory**
// or via codec

do this only when you are done with encoding/decoding blurhashes
```dart
import 'package:blurhash_ffi/blurhash_ffi.dart';

BlurhashFFi.free();
final String decodedSame = BlurhashFfiCodec().decoder.convert(blurhash);

```
check the [example](./example/) for more details

Check the [example](./packages/flutter_blurhash_ffi/example/) for more details

**contributions in the form of PR's and Issues are a welcome**
9 changes: 0 additions & 9 deletions android/.gitignore

This file was deleted.

Loading