1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 05:47:34 +00:00

LibGfx: Improve AA stroke_path() line intersections a little

- Simplify both straight lines case
- Simplify rotation by constant 90 degrees
- Remove some unnecessary rounding and hacks
This commit is contained in:
MacDue 2022-12-26 22:05:40 +00:00 committed by Andreas Kling
parent d140d0f880
commit 2376385f0c

View file

@ -269,8 +269,11 @@ void AntiAliasingPainter::stroke_path(Path const& path, Color color, float thick
}
// Check if the figure was started and closed as line at the same position.
if (thickness > 1 && previous_was_line && path.segments().size() >= 2 && path.segments().first().point() == cursor && (path.segments().first().type() == Segment::Type::LineTo || (path.segments().first().type() == Segment::Type::MoveTo && path.segments()[1].type() == Segment::Type::LineTo)))
if (thickness > 1 && previous_was_line && path.segments().size() >= 2 && path.segments().first().point() == cursor
&& (path.segments().first().type() == Segment::Type::LineTo
|| (path.segments().first().type() == Segment::Type::MoveTo && path.segments()[1].type() == Segment::Type::LineTo))) {
stroke_segment_intersection(first_line.value().a(), first_line.value().b(), last_line, color, thickness);
}
}
void AntiAliasingPainter::draw_elliptical_arc(FloatPoint p1, FloatPoint p2, FloatPoint center, FloatPoint radii, float x_axis_rotation, float theta_1, float theta_delta, Color color, float thickness, Painter::LineStyle style)
@ -697,79 +700,67 @@ void AntiAliasingPainter::stroke_segment_intersection(FloatPoint current_line_a,
// Starting point of the current line is where the last line ended... this is an intersection.
auto intersection = current_line_a;
auto previous_line_b = (previous_line.a());
// If both are straight lines we can simply draw a rectangle at the intersection.
if ((current_line_a.x() == current_line_b.x() || current_line_a.y() == current_line_b.y()) && (previous_line.a().x() == previous_line.b().x() || previous_line.a().y() == previous_line.b().y())) {
intersection = m_transform.map(current_line_a);
// Adjust coordinates to handle rounding offsets.
auto intersection_rect = IntSize(thickness, thickness);
float drawing_edge_offset = fmodf(thickness, 2.0f) < 0.5f && thickness > 3 ? 1 : 0;
auto integer_part = [](float x) { return floorf(x); };
auto round = [&](float x) { return integer_part(x + 0.5f); };
if (thickness == 1)
drawing_edge_offset = -1;
if (current_line_a.x() == current_line_b.x() && previous_line.a().x() == previous_line.b().x()) {
intersection_rect.set_height(1);
}
if (current_line_a.y() == current_line_b.y() && previous_line.a().y() == previous_line.b().y()) {
intersection_rect.set_width(1);
drawing_edge_offset = thickness == 1 ? -1 : 0;
intersection.set_x(intersection.x() - 1 + (thickness == 1 ? 1 : 0));
intersection.set_y(intersection.y() + (thickness > 3 && fmodf(thickness, 2.0f) < 0.5f ? 1 : 0));
}
m_underlying_painter.fill_rect(IntRect::centered_on({ round(intersection.x()) + drawing_edge_offset, round(intersection.y()) + drawing_edge_offset }, intersection_rect), color);
// If both are straight lines we can simply draw a rectangle at the intersection (or nothing).
auto current_vertical = current_line_a.x() == current_line_b.x();
auto current_horizontal = current_line_a.y() == current_line_b.y();
auto previous_vertical = previous_line.a().x() == previous_line.b().x();
auto previous_horizontal = previous_line.a().y() == previous_line.b().y();
if (previous_horizontal && current_horizontal)
return;
if (previous_vertical && current_vertical)
return;
if ((previous_horizontal || previous_vertical) && (current_horizontal || current_vertical)) {
intersection = m_transform.map(current_line_a);
// Note: int_thickness used here to match behaviour of draw_line()
int int_thickness = AK::ceil(thickness);
return fill_rect(FloatRect(intersection, { thickness, thickness }).translated(-int_thickness / 2), color);
}
auto previous_line_a = previous_line.a();
float scale_to_move_current = (thickness / 2) / intersection.distance_from(current_line_b);
float scale_to_move_previous = (thickness / 2) / intersection.distance_from(previous_line_b);
float scale_to_move_previous = (thickness / 2) / intersection.distance_from(previous_line_a);
// Move the point on the line by half of the thickness.
double offset_current_edge_x = scale_to_move_current * (current_line_b.x() - intersection.x());
double offset_current_edge_y = scale_to_move_current * (current_line_b.y() - intersection.y());
double offset_prev_edge_x = scale_to_move_previous * (previous_line_b.x() - intersection.x());
double offset_prev_edge_y = scale_to_move_previous * (previous_line_b.y() - intersection.y());
float offset_current_edge_x = scale_to_move_current * (current_line_b.x() - intersection.x());
float offset_current_edge_y = scale_to_move_current * (current_line_b.y() - intersection.y());
float offset_prev_edge_x = scale_to_move_previous * (previous_line_a.x() - intersection.x());
float offset_prev_edge_y = scale_to_move_previous * (previous_line_a.y() - intersection.y());
// Rotate the point by 90 and 270 degrees to get the points for both edges.
double rad_90deg = 0.5 * M_PI;
FloatPoint current_rotated_90deg = { (offset_current_edge_x * cos(rad_90deg) - offset_current_edge_y * sin(rad_90deg)), (offset_current_edge_x * sin(rad_90deg) + offset_current_edge_y * cos(rad_90deg)) };
FloatPoint current_rotated_270deg = intersection - current_rotated_90deg;
FloatPoint previous_rotated_90deg = { (offset_prev_edge_x * cos(rad_90deg) - offset_prev_edge_y * sin(rad_90deg)), (offset_prev_edge_x * sin(rad_90deg) + offset_prev_edge_y * cos(rad_90deg)) };
FloatPoint previous_rotated_270deg = intersection - previous_rotated_90deg;
FloatPoint current_rotated_90deg(-offset_current_edge_y, offset_current_edge_x);
FloatPoint previous_rotated_90deg(-offset_prev_edge_y, offset_prev_edge_x);
auto current_rotated_270deg = intersection - current_rotated_90deg;
auto previous_rotated_270deg = intersection - previous_rotated_90deg;
// Translate coordinates to the intersection point.
current_rotated_90deg += intersection;
previous_rotated_90deg += intersection;
FloatLine outer_line_current_90 = FloatLine({ current_rotated_90deg, current_line_b - static_cast<FloatPoint>(intersection - current_rotated_90deg) });
FloatLine outer_line_current_270 = FloatLine({ current_rotated_270deg, current_line_b - static_cast<FloatPoint>(intersection - current_rotated_270deg) });
FloatLine outer_line_prev_270 = FloatLine({ previous_rotated_270deg, previous_line_b - static_cast<FloatPoint>(intersection - previous_rotated_270deg) });
FloatLine outer_line_prev_90 = FloatLine({ previous_rotated_90deg, previous_line_b - static_cast<FloatPoint>(intersection - previous_rotated_90deg) });
FloatLine outer_line_current_90(current_rotated_90deg, current_line_b - (intersection - current_rotated_90deg));
FloatLine outer_line_current_270(current_rotated_270deg, current_line_b - (intersection - current_rotated_270deg));
FloatLine outer_line_prev_270(previous_rotated_270deg, previous_line_a - (intersection - previous_rotated_270deg));
FloatLine outer_line_prev_90(previous_rotated_90deg, previous_line_a - (intersection - previous_rotated_90deg));
Optional<FloatPoint> edge_spike_90 = outer_line_current_90.intersected(outer_line_prev_270);
auto edge_spike_90 = outer_line_current_90.intersected(outer_line_prev_270);
Optional<FloatPoint> edge_spike_270;
if (edge_spike_90.has_value()) {
edge_spike_270 = intersection + (intersection - edge_spike_90.value());
edge_spike_270 = intersection + (intersection - *edge_spike_90);
} else {
edge_spike_270 = outer_line_current_270.intersected(outer_line_prev_90);
if (edge_spike_270.has_value()) {
edge_spike_90 = intersection + (intersection - edge_spike_270.value());
}
if (edge_spike_270.has_value())
edge_spike_90 = intersection + (intersection - *edge_spike_270);
}
Path intersection_edge_path;
intersection_edge_path.move_to(current_rotated_90deg);
if (edge_spike_90.has_value())
intersection_edge_path.line_to(edge_spike_90.value());
intersection_edge_path.line_to(*edge_spike_90);
intersection_edge_path.line_to(previous_rotated_270deg);
intersection_edge_path.line_to(current_rotated_270deg);
if (edge_spike_270.has_value())
intersection_edge_path.line_to(edge_spike_270.value());
intersection_edge_path.line_to(*edge_spike_270);
intersection_edge_path.line_to(previous_rotated_90deg);
intersection_edge_path.close();
fill_path(intersection_edge_path, color);