applyFloydSteinberg static method

Image applyFloydSteinberg(
  1. Image image
)

Converte a imagem fornecida para uma imagem preto e branco utilizando o algoritmo de dithering Floyd-Steinberg, que distribui o erro garantindo uma textura de meio-tom (ideal para impressoras térmicas).

Implementation

static img.Image applyFloydSteinberg(img.Image image) {
  final width = image.width;
  final height = image.height;

  // Converte a imagem para uma matriz 2D de luminâncias (escala de cinza 0-255)
  final doubleMatrix = List.generate(
    height,
    (y) => List.filled(width, 0.0),
  );

  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      final pixel = image.getPixel(x, y);
      // Calcula a luminância perceptiva
      final r = pixel.r;
      final g = pixel.g;
      final b = pixel.b;
      double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b);
      doubleMatrix[y][x] = luminance;
    }
  }

  // Aplica o espalhamento de erros de Floyd-Steinberg
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      final oldPixel = doubleMatrix[y][x];
      // Threshold em 128 para preto (0) ou branco (255)
      final newPixel = oldPixel < 128.0 ? 0.0 : 255.0;
      doubleMatrix[y][x] = newPixel;

      final error = oldPixel - newPixel;

      // Distribui o erro pros vizinhos:
      // direita (7/16)
      if (x + 1 < width) {
        doubleMatrix[y][x + 1] += error * 7.0 / 16.0;
      }
      // baixo-esquerda (3/16)
      if (x - 1 >= 0 && y + 1 < height) {
        doubleMatrix[y + 1][x - 1] += error * 3.0 / 16.0;
      }
      // baixo (5/16)
      if (y + 1 < height) {
        doubleMatrix[y + 1][x] += error * 5.0 / 16.0;
      }
      // baixo-direita (1/16)
      if (x + 1 < width && y + 1 < height) {
        doubleMatrix[y + 1][x + 1] += error * 1.0 / 16.0;
      }
    }
  }

  // Reconstrói a imagem em P&B com o novo mapa pontilhado
  final resultImage = img.Image(width: width, height: height, numChannels: 3);
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      // Se for 0.0, é preto. Se for 255.0, é branco.
      final v = doubleMatrix[y][x] < 128.0 ? 0 : 255;
      resultImage.setPixelRgb(x, y, v, v, v);
    }
  }

  return resultImage;
}