Skip to content

Penfore/dither_it

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

31 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

DitherIt 🎨

License GitHub Stars Dart SDK

DitherIt is a comprehensive Dart library that implements various dithering algorithms for image processing. Transform your images with professional-grade dithering techniques to reduce color depth while maintaining visual quality.

πŸ“Έ Visual Examples

See the difference dithering makes! Below are examples of each algorithm applied to the same source image:

Original Image

Original Image
Original image (full color)

Floyd-Steinberg Dithering

Floyd-Steinberg Result
Floyd-Steinberg algorithm - Classic error diffusion

Ordered Dithering (Bayer Matrix)

Ordered Dithering Result
Ordered dithering - Regular pattern-based approach

Riemersma Dithering

Riemersma Result
Riemersma algorithm - Hilbert curve-based natural diffusion

πŸ“· Image Credits

Example image: "Small Copper Butterfly" by Illuvis from Pixabay Used under the Pixabay Content License

See example/IMAGE_CREDITS.md for full attribution details.


✨ Features

  • Floyd-Steinberg Dithering: Classic error diffusion algorithm with fine-grained results
  • Ordered Dithering: Bayer matrix-based dithering with configurable matrix sizes (2x2, 4x4, 8x8)
  • Riemersma Dithering: Hilbert curve-based error diffusion for natural-looking results
  • High Performance: Optimized algorithms for fast processing
  • Easy to Use: Simple API with comprehensive documentation
  • Well Tested: Extensive test coverage ensuring reliability
  • Pure Dart: No native dependencies, works on all platforms

πŸš€ Getting Started

Installation

Add dither_it to your pubspec.yaml:

dependencies:
  image: ^4.2.0  # Required for image processing
  dither_it:
    git:
      url: https://github.com/Penfore/dither_it.git
      ref: main

Then run flutter pub get or dart pub get to install the package.

Run:

dart pub get

Quick Example

import 'package:dither_it/dither_it.dart';
import 'package:image/image.dart';
import 'dart:io';

void main() async {
  // Load an image
  final bytes = await File('input.jpg').readAsBytes();
  final originalImage = decodeImage(bytes)!;

  // Apply Floyd-Steinberg dithering
  final ditheredImage = DitherIt.floydSteinberg(image: originalImage);

  // Save the result
  await File('output_floyd.png').writeAsBytes(encodePng(ditheredImage));

  print('Dithering complete!');
}

🎯 Algorithms

Floyd-Steinberg Dithering

The Floyd-Steinberg algorithm distributes quantization error to neighboring pixels using a specific pattern:

final result = DitherIt.floydSteinberg(image: myImage);

Best for: General purpose dithering, photographs, smooth gradients

Ordered Dithering (Bayer Matrix)

Uses predefined threshold matrices for consistent, pattern-based dithering:

// 2x2 matrix (fastest, more visible pattern)
final result2x2 = DitherIt.ordered(image: myImage, matrixSize: 2);

// 4x4 matrix (balanced quality/performance)
final result4x4 = DitherIt.ordered(image: myImage, matrixSize: 4);

// 8x8 matrix (finest pattern, slower)
final result8x8 = DitherIt.ordered(image: myImage, matrixSize: 8);

Best for: Animations, real-time processing, consistent patterns

Riemersma Dithering

Uses a Hilbert curve pattern for more natural-looking error distribution:

// Default history size (16)
final result = DitherIt.riemersma(image: myImage);

// Custom history size for fine-tuning
final resultCustom = DitherIt.riemersma(image: myImage, historySize: 32);

Best for: Natural textures, organic images, artistic effects

πŸ“Š Algorithm Comparison

Algorithm Speed Quality Pattern Best Use Case
Floyd-Steinberg Medium High Irregular General purpose
Ordered (2x2) Fast Medium Regular Real-time, animations
Ordered (4x4) Fast Good Regular Balanced performance
Ordered (8x8) Medium High Fine High-quality output
Riemersma Slow Very High Organic Artistic, natural images

πŸ”§ Advanced Usage

Processing Multiple Images

Future<void> processImageBatch(List<String> imagePaths) async {
  for (final path in imagePaths) {
    final bytes = await File(path).readAsBytes();
    final image = decodeImage(bytes)!;

    // Apply different algorithms
    final floyd = DitherIt.floydSteinberg(image: image);
    final ordered = DitherIt.ordered(image: image, matrixSize: 4);
    final riemersma = DitherIt.riemersma(image: image, historySize: 24);

    // Save results
    await File('${path}_floyd.png').writeAsBytes(encodePng(floyd));
    await File('${path}_ordered.png').writeAsBytes(encodePng(ordered));
    await File('${path}_riemersma.png').writeAsBytes(encodePng(riemersma));
  }
}

🎨 Examples

Check out our example code to get started quickly:

Basic Example

See example/dither_it_example.dart for a complete working example that demonstrates all three algorithms:

import 'dart:io';
import 'package:dither_it/dither_it.dart';
import 'package:image/image.dart';

void main() async {
  // Load an image
  final bytes = await File('example/input.png').readAsBytes();
  final image = decodeImage(bytes)!;

  // Apply Floyd-Steinberg dithering
  final floyd = DitherIt.floydSteinberg(image: image);
  await File('example/output_floyd_steinberg.png').writeAsBytes(encodePng(floyd));

  // Apply Ordered dithering
  final ordered = DitherIt.ordered(image: image, matrixSize: 4);
  await File('example/output_ordered.png').writeAsBytes(encodePng(ordered));

  // Apply Riemersma dithering
  final riemersma = DitherIt.riemersma(image: image);
  await File('example/output_riemersma.png').writeAsBytes(encodePng(riemersma));

  print('βœ… Dithering complete! Check the output files.');
}

Visual Results

The visual examples above show the results of running this code. Compare how each algorithm handles the same input image differently!

Coming Soon

Additional examples in development:

  • comparison_example.dart - Side-by-side algorithm comparison with metrics (planned)
  • batch_processing.dart - Process multiple images efficiently (planned)
  • flutter_app_example/ - Complete Flutter app with UI (planned)

πŸ”¬ Technical Details

Algorithm Performance

Understanding the performance characteristics can help you choose the right algorithm for your use case:

Floyd-Steinberg

  • Processing Speed: Medium - processes each pixel sequentially
  • Memory Usage: Low - only needs to store error values for current and next row
  • Quality: High - excellent error diffusion creates smooth gradients
  • Best for: High-quality results when processing time is not critical

Ordered Dithering

  • Processing Speed: Fast - each pixel is processed independently
  • Memory Usage: Very Low - uses pre-computed threshold matrices
  • Quality: Good - creates consistent patterns, quality depends on matrix size
  • Best for: Real-time applications, animations, or when consistent patterns are desired

Riemersma

  • Processing Speed: Slower - maintains error history for each color channel
  • Memory Usage: Medium - stores error history (default: 16 values Γ— 3 colors = 48 values per pixel)
  • Quality: Very High - natural-looking results with organic error distribution
  • Best for: Artistic applications where quality is more important than speed

Memory Requirements

Here's what each algorithm needs in terms of additional memory:

  • Floyd-Steinberg: ~4KB extra for a 1920Γ—1080 image (stores one row of error values)
  • Ordered: No extra memory (uses static matrices)
  • Riemersma: ~300KB extra for a 1920Γ—1080 image with default history size

Processing Time Examples

Approximate processing times for a 1920Γ—1080 image on a modern CPU:

  • Ordered (2Γ—2): ~50ms
  • Ordered (4Γ—4): ~60ms
  • Ordered (8Γ—8): ~80ms
  • Floyd-Steinberg: ~200ms
  • Riemersma: ~400ms

Note: Times vary based on hardware and image complexity

Parallelization Support

  • Ordered Dithering: βœ… Fully parallelizable - each pixel can be processed independently
  • Floyd-Steinberg: ❌ Sequential processing required due to error propagation
  • Riemersma: ❌ Sequential processing required due to error history dependencies

🀝 Contributing

We welcome contributions! Here's how you can help:

  1. Bug Reports: Open an issue with detailed reproduction steps
  2. Feature Requests: Suggest new algorithms or improvements
  3. Code Contributions: Fork the repo and submit a pull request
  4. Documentation: Help improve our docs and examples

Development Setup

# Clone the repository
git clone https://github.com/Penfore/dither_it.git
cd dither_it

# Install dependencies
dart pub get

# Run tests
dart test

# Run example (coming soon)
# cd example
# dart run basic_example.dart

Coding Standards

  • Follow Dart style guide
  • Maintain 100% test coverage for new features
  • Document public APIs with DartDoc
  • Use meaningful commit messages

πŸ—ΊοΈ Roadmap

Upcoming Features

  • Additional error diffusion algorithms (Jarvis-Judice-Ninke, Stucki, etc.)
  • Blue noise dithering
  • Custom color palette support
  • Performance optimizations with isolates
  • Web demo application
  • CLI tool for batch processing

Version History

See CHANGELOG.md for detailed version history.

πŸ“ License

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

πŸ™ Acknowledgments

  • Robert Floyd and Louis Steinberg for the pioneering Floyd-Steinberg algorithm
  • Nico Riemersma for the Riemersma dithering technique
  • The Dart team for the excellent image processing foundation
  • Contributors who help improve this library

πŸ“ž Support


Made with ❀️ by Penfore

Star ⭐ this repository if you find it helpful!

About

DitherIt is a Dart library that implements various dithering algorithms.

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published

Languages