of static method
The normal map of picture.
Implementation
static Pixels of(
Pixels picture, {
NormalMapOptions options = const NormalMapOptions(),
}) {
final w = picture.width, h = picture.height;
final height = Float32List(w * h);
final src = picture.rgba;
for (var i = 0; i < w * h; i++) {
final r = src[i * 4], g = src[i * 4 + 1], b = src[i * 4 + 2];
final a = src[i * 4 + 3] / 255;
var lum = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
if (options.invert) lum = 1 - lum;
height[i] = options.tiles ? lum : lum * a;
}
if (options.bevel > 0 && !options.tiles) {
final rounded = _bevel(picture, options.bevel);
for (var i = 0; i < w * h; i++) {
// Half the picture's own bumps, half the rounded edge.
height[i] = height[i] * 0.5 + rounded[i] * 0.5;
}
}
double at(int x, int y) {
if (options.tiles) {
x %= w;
y %= h;
if (x < 0) x += w;
if (y < 0) y += h;
} else {
x = x.clamp(0, w - 1);
y = y.clamp(0, h - 1);
}
return height[y * w + x];
}
final out = Pixels(w, h);
final dst = out.rgba;
final s = options.strength;
for (var y = 0; y < h; y++) {
for (var x = 0; x < w; x++) {
// Sobel: the slope across and down, smoothed.
final dx =
(at(x + 1, y - 1) + 2 * at(x + 1, y) + at(x + 1, y + 1)) -
(at(x - 1, y - 1) + 2 * at(x - 1, y) + at(x - 1, y + 1));
final dy =
(at(x - 1, y + 1) + 2 * at(x, y + 1) + at(x + 1, y + 1)) -
(at(x - 1, y - 1) + 2 * at(x, y - 1) + at(x + 1, y - 1));
// Up is against the picture's rows: higher below, the normal
// leans up.
var nx = -dx * s, ny = dy * s;
const nz = 1.0;
final len = math.sqrt(nx * nx + ny * ny + nz * nz);
nx /= len;
ny /= len;
final i = (y * w + x) * 4;
dst[i] = ((nx * 0.5 + 0.5) * 255).round();
dst[i + 1] = ((ny * 0.5 + 0.5) * 255).round();
dst[i + 2] = ((nz / len * 0.5 + 0.5) * 255).round();
dst[i + 3] = options.tiles ? 255 : src[i + 3];
}
}
return out;
}