DitherIt π¨
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 (full color)
Floyd-Steinberg Dithering
Floyd-Steinberg algorithm - Classic error diffusion
Ordered Dithering (Bayer Matrix)
Ordered dithering - Regular pattern-based approach
Riemersma Dithering
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:
- Bug Reports: Open an issue with detailed reproduction steps
- Feature Requests: Suggest new algorithms or improvements
- Code Contributions: Fork the repo and submit a pull request
- 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 ditheringCustom color palette supportPerformance optimizations with isolatesWeb demo applicationCLI tool for batch processing
Version History
See CHANGELOG.md for detailed version history.
π License
This project is licensed under the BSD-3-Clause 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
- π Issues: GitHub Issues
- π¬ Discussions: GitHub Discussions
- π§ Contact: Create an issue for any questions
Made with β€οΈ by Penfore
Star β this repository if you find it helpful!