blob: 3e1bfb4f6f87a617d6aacab96bd4ba7bca6ef5c7 [file] [log] [blame]
import 'dart:math';
import 'dart:typed_data';
import '../image.dart';
import 'neural_quantizer.dart';
// From http://jsbin.com/iXofIji/2/edit by PAEz
enum DitherKernel {
None,
FalseFloydSteinberg,
FloydSteinberg,
Stucki,
Atkinson
}
const _ditherKernels = [
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
// FalseFloydSteinberg
[
[3 / 8, 1, 0],
[3 / 8, 0, 1],
[2 / 8, 1, 1]
],
// FloydSteinberg
[
[7 / 16, 1, 0],
[3 / 16, -1, 1],
[5 / 16, 0, 1],
[1 / 16, 1, 1]
],
// Stucki
[
[8 / 42, 1, 0],
[4 / 42, 2, 0],
[2 / 42, -2, 1],
[4 / 42, -1, 1],
[8 / 42, 0, 1],
[4 / 42, 1, 1],
[2 / 42, 2, 1],
[1 / 42, -2, 2],
[2 / 42, -1, 2],
[4 / 42, 0, 2],
[2 / 42, 1, 2],
[1 / 42, 2, 2]
],
//Atkinson:
[
[1 / 8, 1, 0],
[1 / 8, 2, 0],
[1 / 8, -1, 1],
[1 / 8, 0, 1],
[1 / 8, 1, 1],
[1 / 8, 0, 2]
]
];
Uint8List ditherPixels(Image image, NeuralQuantizer quantizer,
DitherKernel kernel, bool serpentine) {
if (kernel == DitherKernel.None) {
return quantizer.getIndexMap(image);
}
final ds = _ditherKernels[kernel.index];
final height = image.height;
final width = image.width;
final data = Uint8List.fromList(image.getBytes());
var direction = serpentine ? -1 : 1;
final indexedPixels = Uint8List(width * height);
final colorMap = quantizer.colorMap;
var index = 0;
for (var y = 0; y < height; y++) {
if (serpentine) direction = direction * -1;
final x0 = direction == 1 ? 0 : width - 1;
final x1 = direction == 1 ? width : 0;
for (var x = x0; x != x1; x += direction, ++index) {
// Get original color
var idx = index * 4;
final r1 = data[idx];
final g1 = data[idx + 1];
final b1 = data[idx + 2];
// Get converted color
idx = quantizer.lookupRGB(r1, g1, b1);
indexedPixels[index] = idx;
idx *= 3;
final r2 = colorMap[idx];
final g2 = colorMap[idx + 1];
final b2 = colorMap[idx + 2];
final er = r1 - r2;
final eg = g1 - g2;
final eb = b1 - b2;
if (er == 0 && eg == 0 && eb == 0) {
continue;
}
final i0 = direction == 1 ? 0: ds.length - 1;
final i1 = direction == 1 ? ds.length : 0;
for (var i = i0; i != i1; i += direction) {
final x1 = ds[i][1].toInt();
final y1 = ds[i][2].toInt();
if (x1 + x >= 0 && x1 + x < width && y1 + y >= 0 && y1 + y < height) {
var d = ds[i][0];
idx = index + x1 + (y1 * width);
idx *= 4;
data[idx] = max(0, min(255, (data[idx] + er * d).toInt()));
data[idx + 1] = max(0, min(255, (data[idx + 1] + eg * d).toInt()));
data[idx + 2] = max(0, min(255, (data[idx + 2] + eb * d).toInt()));
}
}
}
}
return indexedPixels;
}