Cache the paths generated from hour name texts
Rendering text along a textPath is very CPU intensive, especially since we’re doing it every second.
This commit is contained in:
parent
7e8f62b811
commit
2fd9d4e8de
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -855,6 +855,7 @@ dependencies = [
|
||||
"toml",
|
||||
"usvg",
|
||||
"xdg",
|
||||
"xmlwriter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -15,3 +15,4 @@ toml = "0.5"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
xdg = "2.4"
|
||||
calloop = "0.9"
|
||||
xmlwriter = "0.1"
|
79
src/main.rs
79
src/main.rs
@ -12,16 +12,15 @@ use sctk::shm::AutoMemPool;
|
||||
use sctk::window::{Event as WEvent, FallbackFrame};
|
||||
use serde::Deserialize;
|
||||
use svg::node::element::path::Data as PathData;
|
||||
use svg::node::element::{
|
||||
Circle, Definitions, Group, Line, Path, Rectangle, Style, Text, TextPath,
|
||||
};
|
||||
use svg::node::element::{Circle, Group, Line, Path, Rectangle, Style, Text};
|
||||
use svg::node::Text as TextNode;
|
||||
use svg::Document;
|
||||
|
||||
mod svg_clock;
|
||||
|
||||
use svg_clock::{
|
||||
hour_name_path, svg_to_usvg, HOUR_NAMES, HOUR_NAME_FONT_SIZE, IMAGE_WIDTH, OUTER_R, RING_WIDTH,
|
||||
cache_hour_name_paths, svg_to_usvg, HOUR_NAMES, HOUR_NAME_FONT_SIZE, IMAGE_WIDTH, OUTER_R,
|
||||
RING_WIDTH,
|
||||
};
|
||||
|
||||
sctk::default_environment!(SeasonalClock, desktop);
|
||||
@ -69,7 +68,12 @@ fn time_to_degrees(timestamp: i32, // should be time/timestamp
|
||||
seconds_to_degrees(timestamp)
|
||||
}
|
||||
|
||||
fn hour_marker(hour: i32, is_current_hour: bool, utc_hour_font_size: f32) -> Group {
|
||||
fn hour_marker(
|
||||
hour: i32,
|
||||
hour_name_path_data: &PathData,
|
||||
is_current_hour: bool,
|
||||
utc_hour_font_size: f32,
|
||||
) -> Group {
|
||||
let season = match hour {
|
||||
0..=5 => Season::Winter,
|
||||
6..=11 => Season::Spring,
|
||||
@ -106,16 +110,10 @@ fn hour_marker(hour: i32, is_current_hour: bool, utc_hour_font_size: f32) -> Gro
|
||||
0,
|
||||
))
|
||||
.close();
|
||||
let path = Path::new().set("d", path_data);
|
||||
let hour_name_text_path = TextPath::new()
|
||||
.set("xlink:href", "#hour-name-path")
|
||||
.set("startOffset", "50%")
|
||||
.add(TextNode::new(HOUR_NAMES[hour as usize]));
|
||||
let hour_name_text = Text::new()
|
||||
.set("text-anchor", "middle")
|
||||
.set("dominant-baseline", "mathematical")
|
||||
.set("font-size", HOUR_NAME_FONT_SIZE)
|
||||
.add(hour_name_text_path);
|
||||
let path = Path::new().set("class", "hour-outline").set("d", path_data);
|
||||
let hour_name_path = Path::new()
|
||||
.set("class", "hour-name")
|
||||
.set("d", hour_name_path_data.clone());
|
||||
|
||||
let utc_hour_text = Text::new()
|
||||
.set("class", "utc")
|
||||
@ -148,7 +146,7 @@ fn hour_marker(hour: i32, is_current_hour: bool, utc_hour_font_size: f32) -> Gro
|
||||
),
|
||||
)
|
||||
.add(path)
|
||||
.add(hour_name_text)
|
||||
.add(hour_name_path)
|
||||
.add(utc_hour_text)
|
||||
}
|
||||
|
||||
@ -222,7 +220,7 @@ fn get_moon_path(radius: f32, moon_phase: f64) -> Path {
|
||||
Path::new().set("class", "moon").set("d", path_data)
|
||||
}
|
||||
|
||||
fn gen_svg(config: &Option<Config>) -> Document {
|
||||
fn gen_svg(config: &Option<Config>, hour_name_path_cache: &[PathData; 24]) -> Document {
|
||||
let local_timestamp = Local::now();
|
||||
let utc_hour = local_timestamp.with_timezone(&Utc).time().hour();
|
||||
let local_hour = local_timestamp.time().hour();
|
||||
@ -253,17 +251,17 @@ fn gen_svg(config: &Option<Config>) -> Document {
|
||||
let stylesheet = Style::new(
|
||||
"\
|
||||
#border {stroke: none; fill: rgb(19, 17, 30); }
|
||||
.hour path {stroke: rgb(0, 0, 0); stroke-width: 2px;}
|
||||
.hour text {stroke: none; fill: rgb(238, 187, 85);}
|
||||
.hour path.hour-outline {stroke: rgb(0, 0, 0); stroke-width: 2px;}
|
||||
.hour path.hour-name {stroke: none; fill: rgb(238, 187, 85);}
|
||||
.hour text.utc {stroke: none; fill: rgb(91, 68, 38);}
|
||||
.winter path {fill: rgb(70, 62, 108);}
|
||||
.active.winter path {fill: rgb(100, 92, 138);}
|
||||
.active.winter path.hour-outline {fill: rgb(100, 92, 138);}
|
||||
.spring path {fill: rgb(55, 87, 55);}
|
||||
.active.spring path {fill: rgb(85, 117, 85);}
|
||||
.active.spring path.hour-outline {fill: rgb(85, 117, 85);}
|
||||
.summer path {fill: rgb(113, 92, 43);}
|
||||
.active.summer path {fill: rgb(143, 122, 73);}
|
||||
.active.summer path.hour-outline {fill: rgb(143, 122, 73);}
|
||||
.autumn path {fill: rgb(108, 68, 44);}
|
||||
.active.autumn path {fill: rgb(138, 98, 74);}
|
||||
.active.autumn.hour-outline path {fill: rgb(138, 98, 74);}
|
||||
.local-hour {stroke: none; fill: rgb(238, 187, 85);}
|
||||
.night-time {stroke: none; fill: rgb(19, 17, 30);}
|
||||
.blue-hour {stroke: none; fill: rgb(9, 1, 119);}
|
||||
@ -279,7 +277,6 @@ fn gen_svg(config: &Option<Config>) -> Document {
|
||||
#current-hour-name {font-weight: bold;}",
|
||||
);
|
||||
|
||||
let definitions = Definitions::new().add(hour_name_path());
|
||||
let mut local_clock = Group::new().set("id", "local-clock");
|
||||
|
||||
for hour in 0i32..24 {
|
||||
@ -317,6 +314,7 @@ fn gen_svg(config: &Option<Config>) -> Document {
|
||||
for hour in 0i32..24 {
|
||||
seasonal_clock = seasonal_clock.add(hour_marker(
|
||||
hour,
|
||||
&hour_name_path_cache[hour as usize],
|
||||
hour == utc_hour as i32,
|
||||
utc_hour_font_size,
|
||||
));
|
||||
@ -559,7 +557,6 @@ fn gen_svg(config: &Option<Config>) -> Document {
|
||||
.set("height", 700i32)
|
||||
.set("xmlns:xlink", "http://www.w3.org/1999/xlink")
|
||||
.add(stylesheet)
|
||||
.add(definitions)
|
||||
.add(border)
|
||||
.add(local_clock);
|
||||
|
||||
@ -628,8 +625,17 @@ fn main() {
|
||||
let mut need_redraw = false;
|
||||
let mut dimensions = (700, 700);
|
||||
|
||||
let hour_name_path_cache = cache_hour_name_paths();
|
||||
|
||||
if !env.get_shell().unwrap().needs_configure() {
|
||||
redraw(&mut pool, window.surface(), dimensions, &config).expect("Failed to draw");
|
||||
redraw(
|
||||
&mut pool,
|
||||
window.surface(),
|
||||
dimensions,
|
||||
&config,
|
||||
&hour_name_path_cache,
|
||||
)
|
||||
.expect("Failed to draw");
|
||||
window.refresh()
|
||||
}
|
||||
|
||||
@ -657,7 +663,14 @@ fn main() {
|
||||
match next_action.take() {
|
||||
Some(WEvent::Close) => break,
|
||||
Some(WEvent::Refresh) => {
|
||||
redraw(&mut pool, window.surface(), dimensions, &config).expect("Failed to draw");
|
||||
redraw(
|
||||
&mut pool,
|
||||
window.surface(),
|
||||
dimensions,
|
||||
&config,
|
||||
&hour_name_path_cache,
|
||||
)
|
||||
.expect("Failed to draw");
|
||||
window.refresh();
|
||||
window.surface().commit();
|
||||
}
|
||||
@ -680,7 +693,14 @@ fn main() {
|
||||
if need_redraw {
|
||||
need_redraw = false;
|
||||
|
||||
redraw(&mut pool, window.surface(), dimensions, &config).expect("Failed to draw")
|
||||
redraw(
|
||||
&mut pool,
|
||||
window.surface(),
|
||||
dimensions,
|
||||
&config,
|
||||
&hour_name_path_cache,
|
||||
)
|
||||
.expect("Failed to draw")
|
||||
}
|
||||
|
||||
if let Err(e) = display.flush() {
|
||||
@ -698,8 +718,9 @@ fn redraw(
|
||||
surface: &wl_surface::WlSurface,
|
||||
(buf_x, buf_y): (u32, u32),
|
||||
config: &Option<Config>,
|
||||
hour_name_path_cache: &[PathData; 24],
|
||||
) -> Result<(), ::std::io::Error> {
|
||||
let document = gen_svg(config);
|
||||
let document = gen_svg(config, hour_name_path_cache);
|
||||
let svg_tree = svg_to_usvg(document);
|
||||
|
||||
let (canvas, new_buffer) = pool.buffer(
|
||||
|
@ -1,5 +1,8 @@
|
||||
use svg::{
|
||||
node::element::{path::Data as PathData, Path},
|
||||
node::{
|
||||
element::{path::Data as PathData, Definitions, Path, Text, TextPath},
|
||||
Text as TextNode,
|
||||
},
|
||||
Document,
|
||||
};
|
||||
use usvg::Tree;
|
||||
@ -24,7 +27,7 @@ pub fn svg_to_usvg(document: Document) -> Tree {
|
||||
Tree::from_str(&doc_str, &opt.to_ref()).unwrap()
|
||||
}
|
||||
|
||||
pub fn hour_name_path() -> Path {
|
||||
fn hour_name_path() -> Path {
|
||||
let radius = OUTER_R - RING_WIDTH / 2.0;
|
||||
let delta_x = radius * (15.0_f32.to_radians() / 2.0).sin();
|
||||
let delta_y = radius * (1.0 - (15.0_f32.to_radians() / 2.0).cos());
|
||||
@ -43,3 +46,94 @@ pub fn hour_name_path() -> Path {
|
||||
|
||||
Path::new().set("id", "hour-name-path").set("d", path_data)
|
||||
}
|
||||
|
||||
fn create_temp_document(hour_name: &str) -> Document {
|
||||
let definitions = Definitions::new().add(hour_name_path());
|
||||
|
||||
let hour_name_text_path = TextPath::new()
|
||||
.set("xlink:href", "#hour-name-path")
|
||||
.set("startOffset", "50%")
|
||||
.add(TextNode::new(hour_name));
|
||||
let hour_name_text = Text::new()
|
||||
.set("id", "hour-name")
|
||||
.set("text-anchor", "middle")
|
||||
.set("dominant-baseline", "mathematical")
|
||||
.set("font-size", HOUR_NAME_FONT_SIZE)
|
||||
.add(hour_name_text_path);
|
||||
|
||||
Document::new()
|
||||
.set("viewBox", (0i32, 0i32, 700i32, 700i32))
|
||||
.set("width", 700i32)
|
||||
.set("height", 700i32)
|
||||
.set("xmlns:xlink", "http://www.w3.org/1999/xlink")
|
||||
.add(definitions)
|
||||
.add(hour_name_text)
|
||||
}
|
||||
|
||||
fn cache_hour_name_path(hour_name: &str) -> PathData {
|
||||
let tree = svg_to_usvg(create_temp_document(hour_name));
|
||||
let mut svg_path_data: PathData = PathData::new();
|
||||
let text_node = tree.node_by_id("hour-name").unwrap();
|
||||
match *text_node.borrow() {
|
||||
usvg::NodeKind::Path(ref path) => {
|
||||
let path_data = &path.data;
|
||||
for segment in path_data.0.iter() {
|
||||
match segment {
|
||||
usvg::PathSegment::MoveTo { x, y } => {
|
||||
svg_path_data = svg_path_data.move_to((*x, *y));
|
||||
}
|
||||
usvg::PathSegment::LineTo { x, y } => {
|
||||
svg_path_data = svg_path_data.line_to((*x, *y));
|
||||
}
|
||||
usvg::PathSegment::CurveTo {
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
x,
|
||||
y,
|
||||
} => {
|
||||
svg_path_data = svg_path_data.cubic_curve_to((*x1, *y1, *x2, *y2, *x, *y));
|
||||
}
|
||||
usvg::PathSegment::ClosePath => {
|
||||
svg_path_data = svg_path_data.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
svg_path_data
|
||||
}
|
||||
|
||||
pub fn cache_hour_name_paths() -> [PathData; 24] {
|
||||
[
|
||||
cache_hour_name_path(HOUR_NAMES[0]),
|
||||
cache_hour_name_path(HOUR_NAMES[1]),
|
||||
cache_hour_name_path(HOUR_NAMES[2]),
|
||||
cache_hour_name_path(HOUR_NAMES[3]),
|
||||
cache_hour_name_path(HOUR_NAMES[4]),
|
||||
cache_hour_name_path(HOUR_NAMES[5]),
|
||||
cache_hour_name_path(HOUR_NAMES[6]),
|
||||
cache_hour_name_path(HOUR_NAMES[7]),
|
||||
cache_hour_name_path(HOUR_NAMES[8]),
|
||||
cache_hour_name_path(HOUR_NAMES[9]),
|
||||
cache_hour_name_path(HOUR_NAMES[10]),
|
||||
cache_hour_name_path(HOUR_NAMES[11]),
|
||||
cache_hour_name_path(HOUR_NAMES[12]),
|
||||
cache_hour_name_path(HOUR_NAMES[13]),
|
||||
cache_hour_name_path(HOUR_NAMES[14]),
|
||||
cache_hour_name_path(HOUR_NAMES[15]),
|
||||
cache_hour_name_path(HOUR_NAMES[16]),
|
||||
cache_hour_name_path(HOUR_NAMES[17]),
|
||||
cache_hour_name_path(HOUR_NAMES[18]),
|
||||
cache_hour_name_path(HOUR_NAMES[19]),
|
||||
cache_hour_name_path(HOUR_NAMES[20]),
|
||||
cache_hour_name_path(HOUR_NAMES[21]),
|
||||
cache_hour_name_path(HOUR_NAMES[22]),
|
||||
cache_hour_name_path(HOUR_NAMES[23]),
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user