From 84dd675977b099b1ff79fe1589007cc8103c07ea Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Sun, 8 Dec 2019 03:23:16 +0100 Subject: improve antialiasing of thick lines As it turns out, the algorithm for drawing convex polygons is very bad at handling aliasing, and the line height looked off as well. The new algorithm fixes the issue by first drawing a horizontal filled rectangle, then rotating the rectangle by the required angle (using bicubic filtering) and finally overlaying the rectangle onto the target. This improves both the looks of the line height and the aliasing effects. --- src/render.rs | 84 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/render.rs b/src/render.rs index 51f4ceb..53edd2c 100644 --- a/src/render.rs +++ b/src/render.rs @@ -4,9 +4,10 @@ use image::{ imageops, CatmullRom, DynamicImage, GenericImage, GenericImageView, ImageBuffer, Pixel, Primitive, Rgba, RgbaImage, }; -use imageproc::drawing::{self, Point}; +use imageproc::drawing; use num_traits::NumCast; use rusttype::{Font, Scale, SharedBytes}; +use std::cmp::min; quick_error! { #[derive(Debug)] @@ -175,44 +176,26 @@ impl<'r> Renderer<'r> { + (x_slice + self.options.trait_size) / 2 + self.options.traitline_x_offset; let end_x = start_x + x_slice - self.options.trait_size; - let start_y = (buffer.height() - self.options.line_height) / 2; + let start_y = buffer.height() / 2; let y_slice = buffer.height() / 3; - let end_y = y_slice * (choice as u32 - 1) + (y_slice - self.options.line_height) / 2; + let end_y = y_slice * (choice as u32 - 1) + y_slice / 2; - drawing::draw_convex_polygon_mut( + draw_thick_line( buffer, - &[ - Point::new(start_x as i32, start_y as i32), - Point::new( - start_x as i32, - start_y as i32 + self.options.line_height as i32, - ), - Point::new(end_x as i32, end_y as i32 + self.options.line_height as i32), - Point::new(end_x as i32, end_y as i32), - ], + (start_x, start_y), + (end_x, end_y), + self.options.line_height, self.options.line_color, ); if tier == 2 { return Ok(()); } - drawing::draw_convex_polygon_mut( + draw_thick_line( buffer, - &[ - Point::new( - 2 * x_slice as i32 + start_x as i32 - self.options.trait_size as i32, - start_y as i32, - ), - Point::new( - 2 * x_slice as i32 + start_x as i32 - self.options.trait_size as i32, - start_y as i32 + self.options.line_height as i32, - ), - Point::new( - self.options.trait_size as i32 + end_x as i32, - end_y as i32 + self.options.line_height as i32, - ), - Point::new(self.options.trait_size as i32 + end_x as i32, end_y as i32), - ], + (end_x + self.options.trait_size, end_y), + (end_x + x_slice, start_y), + self.options.line_height, self.options.line_color, ); @@ -396,3 +379,46 @@ where out } + +fn draw_thick_line( + image: &mut I, + start: (u32, u32), + end: (u32, u32), + thickness: u32, + color: I::Pixel, +) where + I: GenericImage>, +{ + let delta_x = end.0 as i32 - start.0 as i32; + let delta_y = end.1 as i32 - start.1 as i32; + let line_length = ((delta_x * delta_x + delta_y * delta_y) as f32) + .sqrt() + .round() as u32; + + let mut line_buffer: RgbaImage = ImageBuffer::new(line_length, line_length); + let halfway = (line_length - thickness) / 2; + + for i in 0..thickness { + for x in 0..line_length { + let y = halfway + i; + line_buffer.put_pixel(x, y, color); + } + } + + line_buffer = imageproc::geometric_transformations::rotate_about_center( + &line_buffer, + (delta_y as f32 / delta_x as f32).atan(), + imageproc::geometric_transformations::Interpolation::Bicubic, + Rgba([0, 0, 0, 0]), + ); + + let half_x = (start.0 as i32 + delta_x / 2) as u32; + let half_y = (start.1 as i32 + delta_y / 2) as u32; + + imageops::overlay( + image, + &line_buffer, + half_x - line_length / 2, + half_y - line_length / 2, + ); +} -- cgit v1.2.3