blurhashx 1.0.0 copy "blurhashx: ^1.0.0" to clipboard
blurhashx: ^1.0.0 copied to clipboard

A high-performance Flutter implementation of the BlurHash algorithm with native Skia image decoding. Built with ❤️ for the Tethxr app.

BlurHashX for Flutter & Dart #

pub package License: MIT

A high-performance implementation of the BlurHash algorithm with native hardware-accelerated image decoding and 100% NPM specification compatibility. Built with ❤️ for the Tethxr app.

Blurhash transforms images into compact, human-readable strings (typically 20–30 characters) that decode into smooth, beautiful blurred placeholder gradients while waiting for full images to load.

BlurHashX Overview


⚡ The Speed Breakthrough: How BlurHashX is 300x Faster #

Traditional Dart BlurHash packages rely on pure-Dart software image decoders and naive $O(cx \times cy \times W \times H)$ discrete cosine transforms (DCT), taking 1.5 – 5.0 seconds per photo on mobile devices and causing noticeable UI hangs or ANRs (Application Not Responding).

BlurHashX solves this with a multi-tiered performance architecture:

  1. Native C++ Hardware-Accelerated Decoding (dart:ui.ImageDescriptor): Instead of decoding multi-megapixel JPEGs in pure Dart bytecode, BlurHash.encodeImageBytes leverages the native Flutter engine / Skia C++ decoder (libjpeg-turbo with SIMD acceleration). It decodes and downsamples the image directly to the target DCT thumbnail dimension in ~2–5ms (compared to ~1,400ms in software decoders).
  2. Separable 2D Discrete Cosine Transform: Replaces the naive 4-nested-loop 2D DCT with a separable 2D DCT: a row pass ($O(cx \cdot W \cdot H)$) followed by a column pass ($O(cx \cdot cy \cdot H)$ on the compact intermediate matrix).
  3. Linear-Light Box Downsampling: Area-samples and linearizes pixels into target buckets in a single pass, ensuring diffuse colors accurately represent the image without gamma-skew.
  4. Precomputed Look-Up Tables (LUTs):
    • sRGB-to-Linear Table: 256-entry precomputed table eliminates all per-pixel pow() calculations during encoding.
    • 1D Cosine Tables: Pre-indexed cosine values eliminate expensive trigonometric calculations inside inner loops.
  5. Background Isolate & Concurrency Support: Zero-copy typed data transfer across isolate boundaries via encodeAsync.

Performance Comparison #

Operation Standard Dart Packages BlurHashX Speedup
Full Image Decode (12MP JPEG) ~1,400 ms (Pure Dart) ~3 - 5 ms (Native C++ Skia) ~300x Faster
2D DCT Calculation ~180 ms (Naive 4-loop) ~1 - 2 ms (Separable DCT + LUT) ~100x Faster
Total Encoding Time ~1,600 ms ~5 - 8 ms ~250x Faster
5 Images Parallel Batch ~13,000 - 24,000 ms (ANR risk) ~350 - 450 ms ~30x - 50x Faster

🌐 Ecosystem & Specification Compatibility #

Hashes produced by BlurHashX are 100% mathematically and bit-for-bit compatible with the official Wolt specification and all major ecosystem libraries:

  • JavaScript / TypeScript: blurhash (NPM)
  • iOS / macOS: BlurHash (Swift / Objective-C)
  • Android: blurhash (Kotlin / Java)
  • Backend Ports: Go (bbrks/go-blurhash), Python (blurhash-python), Rust (blurhash-rs)

Any hash generated on the client can be verified, stored in your database, or decoded on the web/backend and vice versa.


📦 Installation #

Add blurhashx to your pubspec.yaml:

dependencies:
  blurhashx: ^1.0.0

Or run:

flutter pub add blurhashx

Import the package in your Dart code:

import 'package:blurhashx/blurhashx.dart';

🚀 Usage Guide #

Pass raw file bytes (Uint8List) from JPEG, PNG, WebP, GIF, or BMP files. The package handles native C++ hardware decoding, aspect-ratio preservation, downscaling, and BlurHash encoding:

import 'dart:typed_data';
import 'package:blurhashx/blurhashx.dart';

final Uint8List imageBytes = await file.readAsBytes();

// Automatically hardware-decodes and encodes in ~5ms
final String hash = await BlurHash.encodeImageBytes(
  imageBytes,
  componentX: 4, // Optional: defaults to aspect-ratio tuned values
  componentY: 3,
  maxDimension: 100, // Optional: long-edge downscale limit
);

print(hash); // e.g. "LEHV6nWB2yk8pyo0adR*.7kCMdnj"

2. Encode from Raw RGBA Pixel Buffers #

If you already have decoded raw RGBA pixel bytes (e.g. from a custom camera pipeline or Canvas):

import 'dart:typed_data';
import 'package:blurhashx/blurhashx.dart';

final Uint8List rgbaPixels = getRawRgbaBytes(); // width * height * 4
final int width = 320;
final int height = 240;

// Synchronous encoding
final String hash = BlurHash.encode(
  rgbaPixels,
  width,
  height,
  componentX: 4,
  componentY: 3,
);

// Or asynchronous (runs on background isolate)
final String asyncHash = await BlurHash.encodeAsync(
  rgbaPixels,
  width,
  height,
  componentX: 4,
  componentY: 3,
);

3. Decode a BlurHash to Raw RGBA Pixels #

Decode a BlurHash string into raw pixel bytes to render onto a texture, custom painter, or image canvas:

import 'dart:typed_data';
import 'package:blurhashx/blurhashx.dart';

const String hash = 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';

// Decode to target dimensions (e.g. 32x32 for placeholder preview)
final Uint8List rgbaPixels = BlurHash.decode(
  hash,
  width: 32,
  height: 32,
  punch: 1.0, // Optional: contrast factor (default is 1.0)
);

// rgbaPixels.length is width * height * 4 (32 * 32 * 4 = 4096 bytes)

4. Fast Average Color Extraction ($O(1)$) #

Extract the primary 24-bit sRGB color of an image instantly without decoding the entire hash:

final ({int r, int g, int b}) avgColor = BlurHash.decodeAverageColor(hash);

print('Average RGB: (${avgColor.r}, ${avgColor.g}, ${avgColor.b})');

5. Validation and Metadata Inspection #

// Quick boolean validity check
final bool isValid = BlurHash.isValid('LEHV6nWB2yk8pyo0adR*.7kCMdnj');

// Detailed validation diagnostics
final BlurHashValidationResult result = BlurHash.validate(hash);
if (!result.isValid) {
  print('Validation error: ${result.errorReason}');
}

// Extract component counts (X and Y components)
final ({int componentX, int componentY}) comps = BlurHash.components(hash);
print('Components: ${comps.componentX}x${comps.componentY}');

📐 BlurHash String Length Formula & Components #

A BlurHash string length is strictly determined by its components:

$$\text{Length} = 4 + 2 \times (\text{componentX} \times \text{componentY})$$

Components ($X \times Y$) Length Recommended Use Case
$1 \times 1$ 6 chars Single dominant flat color
$3 \times 3$ 22 chars Ultra-compact blur for small list items / avatars
$4 \times 3$ (Default) 28 chars Standard landscape photos ($4:3$ or $16:9$)
$3 \times 4$ 28 chars Standard portrait photos ($3:4$ or $9:16$)
$4 \times 4$ 36 chars Square profile pictures & album art
$5 \times 5$ 54 chars High-fidelity gradient previews

📱 Platform Support #

Android iOS macOS Windows Linux Web
✅ Yes ✅ Yes ✅ Yes ✅ Yes ✅ Yes ✅ Yes

🚀 Built for Tethxr #

BlurHashX was engineered and battle-tested for Tethxr to solve high-resolution mobile image hashing bottlenecks and completely eliminate UI jank and ANRs across iOS & Android.


📄 License #

This project is licensed under the MIT License - see the LICENSE file for details.

1
likes
160
points
93
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A high-performance Flutter implementation of the BlurHash algorithm with native Skia image decoding. Built with ❤️ for the Tethxr app.

Repository (GitHub)
View/report issues

Topics

#blurhash #placeholder #image #performance #graphics

License

MIT (license)

Dependencies

flutter

More

Packages that depend on blurhashx