Add a glow effect to the cyberpunk-splash line drawing #48

Merged
savanni merged 2 commits from neon-glow into main 2023-07-26 13:59:22 +00:00
1 changed files with 179 additions and 88 deletions

View File

@ -143,6 +143,15 @@ impl SplashPrivate {
} }
fn redraw_background(&self) { fn redraw_background(&self) {
let pen = GlowPen::new(
*self.width.borrow(),
*self.height.borrow(),
2.,
8.,
8.,
(0.7, 0., 1.),
);
let background = let background =
ImageSurface::create(Format::Rgb24, *self.width.borrow(), *self.height.borrow()) ImageSurface::create(Format::Rgb24, *self.width.borrow(), *self.height.borrow())
.unwrap(); .unwrap();
@ -152,55 +161,89 @@ impl SplashPrivate {
let _ = context.paint(); let _ = context.paint();
context.select_font_face("Alegreya Sans SC", FontSlant::Normal, FontWeight::Bold); context.select_font_face("Alegreya Sans SC", FontSlant::Normal, FontWeight::Bold);
context.set_font_size(128.);
let center_x = *self.width.borrow() as f64 / 2.;
let center_y = *self.height.borrow() as f64 / 2.;
let title_extents = context.text_extents(&self.text.borrow()).unwrap();
let title_width = title_extents.width();
let title_height = title_extents.height();
{ {
let start_length = center_x - title_width / 2. - title_height - 20.; context.set_source_rgb(0.7, 0., 1.);
let title_cutout = AsymLineCutout { let hashtag = "#CodingTogether";
context.set_font_size(64.);
let extents = context.text_extents(hashtag).unwrap();
context.move_to(20., extents.height() + 40.);
let _ = context.show_text(hashtag);
AsymLine {
orientation: gtk::Orientation::Horizontal,
start_x: 10.,
start_y: extents.height() + 10.,
start_length: 0.,
height: extents.height() / 2.,
total_length: extents.width() + extents.height() / 2.,
invert: false,
}
.draw(&pen);
pen.stroke();
AsymLine {
orientation: gtk::Orientation::Horizontal, orientation: gtk::Orientation::Horizontal,
start_x: 20., start_x: 20.,
start_y: center_y - 20. - title_height / 2., start_y: extents.height() + 60.,
start_length, start_length: extents.width(),
total_length: *self.width.borrow() as f64 - 120., height: extents.height() / 2.,
cutout_length: title_width, total_length: extents.width() + extents.height() / 2.,
height: title_height,
invert: false, invert: false,
}; }
.draw(&pen);
context.set_line_cap(LineCap::Round); pen.stroke();
context.set_source_rgb(0.7, 0., 1.);
context.set_line_width(2.);
title_cutout.draw(&context);
let _ = context.stroke();
} }
{ {
let title_baseline_x = center_x - title_width / 2.; context.set_font_size(128.);
let title_baseline_y = center_y - 20.;
let gradient = LinearGradient::new( let center_x = *self.width.borrow() as f64 / 2.;
title_baseline_x, let center_y = *self.height.borrow() as f64 / 2.;
title_baseline_y - title_height,
title_baseline_x, let title_extents = context.text_extents(&self.text.borrow()).unwrap();
title_baseline_y, let title_width = title_extents.width();
); let title_height = title_extents.height();
gradient.add_color_stop_rgb(0.2, 0.7, 0.0, 1.0);
gradient.add_color_stop_rgb(0.8, 0.2, 0.0, 1.0); {
context.move_to(title_baseline_x, title_baseline_y); let start_length = center_x - title_width / 2. - title_height - 20.;
let _ = context.set_source(gradient);
let _ = context.show_text(&self.text.borrow()); let title_cutout = AsymLineCutout {
orientation: gtk::Orientation::Horizontal,
start_x: 20.,
start_y: center_y - 20. - title_height / 2.,
start_length,
total_length: *self.width.borrow() as f64 - 120.,
cutout_length: title_width,
height: title_height,
invert: false,
};
title_cutout.draw(&pen);
pen.stroke();
}
{
let title_baseline_x = center_x - title_width / 2.;
let title_baseline_y = center_y - 20.;
let gradient = LinearGradient::new(
title_baseline_x,
title_baseline_y - title_height,
title_baseline_x,
title_baseline_y,
);
gradient.add_color_stop_rgb(0.2, 0.7, 0.0, 1.0);
gradient.add_color_stop_rgb(0.8, 0.2, 0.0, 1.0);
context.move_to(title_baseline_x, title_baseline_y);
let _ = context.set_source(gradient);
let _ = context.show_text(&self.text.borrow());
}
} }
{ {
context.set_source_rgb(0.7, 0., 1.);
AsymLine { AsymLine {
orientation: gtk::Orientation::Horizontal, orientation: gtk::Orientation::Horizontal,
start_x: 100., start_x: 100.,
@ -210,8 +253,8 @@ impl SplashPrivate {
total_length: 650., total_length: 650.,
invert: true, invert: true,
} }
.draw(&context); .draw(&pen);
let _ = context.stroke(); pen.stroke();
} }
{ {
@ -225,10 +268,14 @@ impl SplashPrivate {
total_length: 650., total_length: 650.,
invert: false, invert: false,
} }
.draw(&context); .draw(&pen);
let _ = context.stroke(); pen.stroke();
} }
let tracery = pen.finish();
let _ = context.set_source(tracery);
let _ = context.paint();
let background = context.pop_group().unwrap(); let background = context.pop_group().unwrap();
*self.background.borrow_mut() = background; *self.background.borrow_mut() = background;
@ -394,7 +441,7 @@ struct AsymLineCutout {
} }
impl AsymLineCutout { impl AsymLineCutout {
fn draw(&self, context: &Context) { fn draw(&self, pen: &impl Pen) {
let dodge = if self.invert { let dodge = if self.invert {
self.height self.height
} else { } else {
@ -402,17 +449,17 @@ impl AsymLineCutout {
}; };
match self.orientation { match self.orientation {
gtk::Orientation::Horizontal => { gtk::Orientation::Horizontal => {
context.move_to(self.start_x, self.start_y); pen.move_to(self.start_x, self.start_y);
context.line_to(self.start_x + self.start_length, self.start_y); pen.line_to(self.start_x + self.start_length, self.start_y);
context.line_to( pen.line_to(
self.start_x + self.start_length + self.height, self.start_x + self.start_length + self.height,
self.start_y + dodge, self.start_y + dodge,
); );
context.line_to( pen.line_to(
self.start_x + self.start_length + self.height + self.cutout_length, self.start_x + self.start_length + self.height + self.cutout_length,
self.start_y + dodge, self.start_y + dodge,
); );
context.line_to( pen.line_to(
self.start_x self.start_x
+ self.start_length + self.start_length
+ self.height + self.height
@ -420,20 +467,20 @@ impl AsymLineCutout {
+ (self.height / 2.), + (self.height / 2.),
self.start_y + dodge / 2., self.start_y + dodge / 2.,
); );
context.line_to(self.total_length, self.start_y + dodge / 2.); pen.line_to(self.total_length, self.start_y + dodge / 2.);
} }
gtk::Orientation::Vertical => { gtk::Orientation::Vertical => {
context.move_to(self.start_x, self.start_y); pen.move_to(self.start_x, self.start_y);
context.line_to(self.start_x, self.start_y + self.start_length); pen.line_to(self.start_x, self.start_y + self.start_length);
context.line_to( pen.line_to(
self.start_x + dodge, self.start_x + dodge,
self.start_y + self.start_length + self.height, self.start_y + self.start_length + self.height,
); );
context.line_to( pen.line_to(
self.start_x + dodge, self.start_x + dodge,
self.start_y + self.start_length + self.height + self.cutout_length, self.start_y + self.start_length + self.height + self.cutout_length,
); );
context.line_to( pen.line_to(
self.start_x + dodge / 2., self.start_x + dodge / 2.,
self.start_y self.start_y
+ self.start_length + self.start_length
@ -441,7 +488,7 @@ impl AsymLineCutout {
+ self.cutout_length + self.cutout_length
+ (self.height / 2.), + (self.height / 2.),
); );
context.line_to(self.start_x + dodge / 2., self.total_length); pen.line_to(self.start_x + dodge / 2., self.total_length);
} }
_ => panic!("unknown orientation"), _ => panic!("unknown orientation"),
} }
@ -459,7 +506,7 @@ struct AsymLine {
} }
impl AsymLine { impl AsymLine {
fn draw(&self, context: &Context) { fn draw(&self, pen: &impl Pen) {
let dodge = if self.invert { let dodge = if self.invert {
self.height self.height
} else { } else {
@ -467,13 +514,13 @@ impl AsymLine {
}; };
match self.orientation { match self.orientation {
gtk::Orientation::Horizontal => { gtk::Orientation::Horizontal => {
context.move_to(self.start_x, self.start_y); pen.move_to(self.start_x, self.start_y);
context.line_to(self.start_x + self.start_length, self.start_y); pen.line_to(self.start_x + self.start_length, self.start_y);
context.line_to( pen.line_to(
self.start_x + self.start_length + self.height, self.start_x + self.start_length + self.height,
self.start_y + dodge, self.start_y + dodge,
); );
context.line_to(self.start_x + self.total_length, self.start_y + dodge); pen.line_to(self.start_x + self.total_length, self.start_y + dodge);
} }
gtk::Orientation::Vertical => {} gtk::Orientation::Vertical => {}
_ => panic!("unknown orientation"), _ => panic!("unknown orientation"),
@ -481,36 +528,6 @@ impl AsymLine {
} }
} }
struct RoundedRectangle {
x: f64,
y: f64,
width: f64,
height: f64,
}
impl RoundedRectangle {
fn draw(&self, context: &Context) {
context.arc(
self.x,
self.y - self.height / 2.,
self.height / 2.,
0.5 * std::f64::consts::PI,
1.5 * std::f64::consts::PI,
);
let _ = context.fill();
context.arc(
self.x + self.width,
self.y - self.height / 2.,
self.height / 2.,
1.5 * std::f64::consts::PI,
0.5 * std::f64::consts::PI,
);
let _ = context.fill();
context.rectangle(self.x, self.y, self.width, -self.height);
let _ = context.fill();
}
}
struct SlashMeter { struct SlashMeter {
orientation: gtk::Orientation, orientation: gtk::Orientation,
start_x: f64, start_x: f64,
@ -551,6 +568,80 @@ impl SlashMeter {
} }
} }
trait Pen {
fn move_to(&self, x: f64, y: f64);
fn line_to(&self, x: f64, y: f64);
fn stroke(&self);
fn finish(self) -> Pattern;
}
struct GlowPen {
blur_context: Context,
draw_context: Context,
line_width: f64,
blur_line_width: f64,
blur_size: f64,
}
impl GlowPen {
fn new(
width: i32,
height: i32,
line_width: f64,
blur_line_width: f64,
blur_size: f64,
color: (f64, f64, f64),
) -> Self {
let blur_context =
Context::new(ImageSurface::create(Format::Rgb24, width, height).unwrap()).unwrap();
blur_context.set_line_width(blur_line_width);
blur_context.set_source_rgba(color.0, color.1, color.2, 0.5);
blur_context.push_group();
blur_context.set_line_cap(LineCap::Round);
let draw_context =
Context::new(ImageSurface::create(Format::Rgb24, width, height).unwrap()).unwrap();
draw_context.set_line_width(line_width);
draw_context.set_source_rgb(color.0, color.1, color.2);
draw_context.push_group();
draw_context.set_line_cap(LineCap::Round);
Self {
blur_context,
draw_context,
line_width,
blur_line_width,
blur_size,
}
}
}
impl Pen for GlowPen {
fn move_to(&self, x: f64, y: f64) {
self.blur_context.move_to(x, y);
self.draw_context.move_to(x, y);
}
fn line_to(&self, x: f64, y: f64) {
self.blur_context.line_to(x, y);
self.draw_context.line_to(x, y);
}
fn stroke(&self) {
self.blur_context.stroke();
self.draw_context.stroke();
}
fn finish(self) -> Pattern {
let foreground = self.draw_context.pop_group().unwrap();
self.blur_context.set_source(foreground).unwrap();
self.blur_context.paint().unwrap();
self.blur_context.pop_group().unwrap()
}
}
fn main() { fn main() {
let app = gtk::Application::builder() let app = gtk::Application::builder()
.application_id("com.luminescent-dreams.cyberpunk-splash") .application_id("com.luminescent-dreams.cyberpunk-splash")