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.
See the difference dithering makes! Below are examples of each algorithm applied to the same source image:
Floyd-Steinberg algorithm - Classic error diffusion
Ordered dithering - Regular pattern-based approach
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.
- 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
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
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!');
}
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
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
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 | 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 |
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));
}
}
Check out our example code to get started quickly:
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.');
}
The visual examples above show the results of running this code. Compare how each algorithm handles the same input image differently!
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)
Understanding the performance characteristics can help you choose the right algorithm for your use case:
- 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
- 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
- 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
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
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
- 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
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
# 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
- Follow Dart style guide
- Maintain 100% test coverage for new features
- Document public APIs with DartDoc
- Use meaningful commit messages
- 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
See CHANGELOG.md for detailed version history.
This project is licensed under the MIT License - see the LICENSE file for details.
- 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
- π 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!