binaryFilterWithDithering static method

Future<Uint8List> binaryFilterWithDithering(
  1. Uint8List imageBytes, {
  2. double? blackTolerance,
  3. double? ditheringTolerance,
})

A method that applies a binary filter with dithering to an image, converting it to black and white while using dithering to represent colors that are not too dark. Parameters:

-imageBytes A Uint8List representing the image.

-blackTolerance A double representing the tolerance level for using black color. The default value is 0.34.

-ditheringTolerance A double representing the tolerance for using dithering to represent colors. The default value is 0.67.

Returns: A Uint8List with the filtered image.

Implementation

static Future<Uint8List> binaryFilterWithDithering(Uint8List imageBytes,
    {double? blackTolerance, double? ditheringTolerance}) async {
  final ui.Image image =
      (await (await ui.instantiateImageCodec(imageBytes)).getNextFrame())
          .image;

  final Uint8List pixels =
      (await image.toByteData(format: ui.ImageByteFormat.rawRgba))!
          .buffer
          .asUint8List();
  final Uint8List outputPixels = Uint8List(pixels.length);

  blackTolerance = blackTolerance ?? 0.34;
  ditheringTolerance = ditheringTolerance ?? 0.67;
  assert(blackTolerance >= 0.0 && blackTolerance <= 1.0);
  assert(ditheringTolerance >= 0.0 && ditheringTolerance <= 1.0);

  final int blackThreshold = (blackTolerance * 255).toInt();
  final int ditheringThreshold = (ditheringTolerance * 255).toInt();

  for (int y = 0; y < image.height; y++) {
    for (int x = 0; x < image.width; x++) {
      final int index = (y * image.width + x) * 4;

      final int r = pixels[index];
      final int g = pixels[index + 1];
      final int b = pixels[index + 2];
      final int a = pixels[index + 3];

      if (a == 0) {
        outputPixels[index] = 255;
        outputPixels[index + 1] = 255;
        outputPixels[index + 2] = 255;
        outputPixels[index + 3] = 255;
        continue;
      }

      final int gray = (0.3 * r + 0.59 * g + 0.11 * b).toInt();

      if (gray <= blackThreshold) {
        outputPixels[index] = 0;
        outputPixels[index + 1] = 0;
        outputPixels[index + 2] = 0;
        outputPixels[index + 3] = 255;
      } else {
        final int threshold = ((x + y) % 2) == 0
            ? ditheringThreshold
            : 255 - ditheringThreshold;
        if (gray <= threshold) {
          outputPixels[index] = 0;
          outputPixels[index + 1] = 0;
          outputPixels[index + 2] = 0;
          outputPixels[index + 3] = 255;
        } else {
          outputPixels[index] = 255;
          outputPixels[index + 1] = 255;
          outputPixels[index + 2] = 255;
          outputPixels[index + 3] = 255;
        }
      }
    }
  }

  final Completer<ui.Image> completer = Completer();
  ui.decodeImageFromPixels(
      outputPixels, image.width, image.height, ui.PixelFormat.rgba8888,
      (ui.Image outputImage) {
    completer.complete(outputImage);
  });

  return (await (await completer.future)
          .toByteData(format: ui.ImageByteFormat.png))!
      .buffer
      .asUint8List();
}