drawLine function
Draw a line into image
.
If antialias
is true then the line is drawn with smooth edges.
thickness
determines how thick the line should be drawn, in pixels.
Implementation
Image drawLine(Image image,
{required int x1,
required int y1,
required int x2,
required int y2,
required Color color,
bool antialias = false,
num thickness = 1,
Image? mask,
Channel maskChannel = Channel.luminance}) {
final line = [x1, y1, x2, y2];
if (!clipLine(line, [0, 0, image.width - 1, image.height - 1])) {
return image;
}
x1 = line[0];
y1 = line[1];
x2 = line[2];
y2 = line[3];
var dx = x2 - x1;
var dy = y2 - y1;
final radius = (thickness / 2.0).floor();
// Drawing a single point.
if (dx == 0 && dy == 0) {
thickness == 1
? drawPixel(image, x1, y1, color, mask: mask, maskChannel: maskChannel)
: fillCircle(image,
x: x1,
y: y1,
radius: radius,
color: color,
mask: mask,
maskChannel: maskChannel);
return image;
}
// Axis-aligned lines
if (dx == 0) {
if (dy < 0) {
for (var y = y2; y <= y1; ++y) {
if (thickness <= 1) {
drawPixel(image, x1, y, color, mask: mask, maskChannel: maskChannel);
} else {
for (var i = 0; i < thickness; i++) {
drawPixel(image, x1 - radius + i, y, color,
mask: mask, maskChannel: maskChannel);
}
}
}
} else {
for (var y = y1; y <= y2; ++y) {
if (thickness <= 1) {
drawPixel(image, x1, y, color, mask: mask, maskChannel: maskChannel);
} else {
for (var i = 0; i < thickness; i++) {
drawPixel(image, x1 - radius + i, y, color,
mask: mask, maskChannel: maskChannel);
}
}
}
}
return image;
} else if (dy == 0) {
if (dx < 0) {
for (var x = x2; x <= x1; ++x) {
if (thickness <= 1) {
drawPixel(image, x, y1, color, mask: mask, maskChannel: maskChannel);
} else {
for (var i = 0; i < thickness; i++) {
drawPixel(image, x, y1 - radius + i, color,
mask: mask, maskChannel: maskChannel);
}
}
}
} else {
for (var x = x1; x <= x2; ++x) {
if (thickness <= 1) {
drawPixel(image, x, y1, color, mask: mask, maskChannel: maskChannel);
} else {
for (var i = 0; i < thickness; i++) {
drawPixel(image, x, y1 - radius + i, color,
mask: mask, maskChannel: maskChannel);
}
}
}
}
return image;
}
// 16-bit unsigned int xor.
int xor(int n) => (~n + 0x10000) & 0xffff;
if (!antialias) {
dx = dx.abs();
dy = dy.abs();
if (dy <= dx) {
// More-or-less horizontal. use wid for vertical stroke
final num ac = cos(atan2(dy, dx));
int wid;
if (ac != 0) {
wid = thickness ~/ ac;
} else {
wid = 1;
}
if (wid == 0) {
wid = 1;
}
var d = 2 * dy - dx;
final incr1 = 2 * dy;
final incr2 = 2 * (dy - dx);
int x, y;
int ydirflag;
int xend;
if (x1 > x2) {
x = x2;
y = y2;
ydirflag = -1;
xend = x1;
} else {
x = x1;
y = y1;
ydirflag = 1;
xend = x2;
}
// Set up line thickness
var wstart = (y - wid / 2).toInt();
for (var w = wstart; w < wstart + wid; w++) {
drawPixel(image, x, w, color, mask: mask, maskChannel: maskChannel);
}
if (((y2 - y1) * ydirflag) > 0) {
while (x < xend) {
x++;
if (d < 0) {
d += incr1;
} else {
y++;
d += incr2;
}
wstart = (y - wid / 2).toInt();
for (var w = wstart; w < wstart + wid; w++) {
drawPixel(image, x, w, color, mask: mask, maskChannel: maskChannel);
}
}
} else {
while (x < xend) {
x++;
if (d < 0) {
d += incr1;
} else {
y--;
d += incr2;
}
wstart = (y - wid / 2).toInt();
for (var w = wstart; w < wstart + wid; w++) {
drawPixel(image, x, w, color, mask: mask, maskChannel: maskChannel);
}
}
}
} else {
// More-or-less vertical. use wid for horizontal stroke
final as = sin(atan2(dy, dx));
int wid;
if (as != 0) {
wid = thickness ~/ as;
} else {
wid = 1;
}
if (wid == 0) {
wid = 1;
}
var d = 2 * dx - dy;
final incr1 = 2 * dx;
final incr2 = 2 * (dx - dy);
int x, y;
int yend;
int xdirflag;
if (y1 > y2) {
y = y2;
x = x2;
yend = y1;
xdirflag = -1;
} else {
y = y1;
x = x1;
yend = y2;
xdirflag = 1;
}
// Set up line thickness
var wstart = (x - wid / 2).toInt();
for (var w = wstart; w < wstart + wid; w++) {
drawPixel(image, w, y, color, mask: mask, maskChannel: maskChannel);
}
if (((x2 - x1) * xdirflag) > 0) {
while (y < yend) {
y++;
if (d < 0) {
d += incr1;
} else {
x++;
d += incr2;
}
wstart = (x - wid / 2).toInt();
for (var w = wstart; w < wstart + wid; w++) {
drawPixel(image, w, y, color, mask: mask, maskChannel: maskChannel);
}
}
} else {
while (y < yend) {
y++;
if (d < 0) {
d += incr1;
} else {
x--;
d += incr2;
}
wstart = (x - wid / 2).toInt();
for (var w = wstart; w < wstart + wid; w++) {
drawPixel(image, w, y, color, mask: mask, maskChannel: maskChannel);
}
}
}
}
return image;
}
// Antialias Line
if (thickness == 1) {
return _drawLineWu(image, x1: x1, y1: y1, x2: x2, y2: y2, color: color);
}
final ag = (dy.abs() < dx.abs()) ? cos(atan2(dy, dx)) : sin(atan2(dy, dx));
int wid;
if (ag != 0.0) {
wid = (thickness / ag).abs().toInt();
} else {
wid = 1;
}
if (wid == 0) {
wid = 1;
}
if (dx.abs() > dy.abs()) {
if (dx < 0) {
var tmp = x1;
x1 = x2;
x2 = tmp;
tmp = y1;
y1 = y2;
y2 = tmp;
dx = x2 - x1;
dy = y2 - y1;
}
var y = y1;
final inc = (dy * 65536) ~/ dx;
var frac = 0;
for (var x = x1; x <= x2; x++) {
final wstart = y - wid ~/ 2;
for (var w = wstart; w < wstart + wid; w++) {
drawPixel(image, x, w, color,
alpha: ((frac >> 8) & 0xff) / 255,
mask: mask,
maskChannel: maskChannel);
drawPixel(image, x, w + 1, color,
alpha: ((xor(frac) >> 8) & 0xff) / 255,
mask: mask,
maskChannel: maskChannel);
}
frac += inc;
if (frac >= 65536) {
frac -= 65536;
y++;
} else if (frac < 0) {
frac += 65536;
y--;
}
}
} else {
if (dy < 0) {
var tmp = x1;
x1 = x2;
x2 = tmp;
tmp = y1;
y1 = y2;
y2 = tmp;
dx = x2 - x1;
dy = y2 - y1;
}
var x = x1;
final inc = (dx * 65536) ~/ dy;
var frac = 0;
for (var y = y1; y <= y2; y++) {
final wstart = x - wid ~/ 2;
for (var w = wstart; w < wstart + wid; w++) {
drawPixel(image, w, y, color,
alpha: ((frac >> 8) & 0xff) / 255,
mask: mask,
maskChannel: maskChannel);
drawPixel(image, w + 1, y, color,
alpha: ((xor(frac) >> 8) & 0xff) / 255,
mask: mask,
maskChannel: maskChannel);
}
frac += inc;
if (frac >= 65536) {
frac -= 65536;
x++;
} else if (frac < 0) {
frac += 65536;
x--;
}
}
}
return image;
}