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
873f29cdb0
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -855,6 +855,7 @@ dependencies = [
|
|||||||
"toml",
|
"toml",
|
||||||
"usvg",
|
"usvg",
|
||||||
"xdg",
|
"xdg",
|
||||||
|
"xmlwriter",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -15,3 +15,4 @@ toml = "0.5"
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
xdg = "2.4"
|
xdg = "2.4"
|
||||||
calloop = "0.9"
|
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 sctk::window::{Event as WEvent, FallbackFrame};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use svg::node::element::path::Data as PathData;
|
use svg::node::element::path::Data as PathData;
|
||||||
use svg::node::element::{
|
use svg::node::element::{Circle, Group, Line, Path, Rectangle, Style, Text};
|
||||||
Circle, Definitions, Group, Line, Path, Rectangle, Style, Text, TextPath,
|
|
||||||
};
|
|
||||||
use svg::node::Text as TextNode;
|
use svg::node::Text as TextNode;
|
||||||
use svg::Document;
|
use svg::Document;
|
||||||
|
|
||||||
mod svg_clock;
|
mod svg_clock;
|
||||||
|
|
||||||
use 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);
|
sctk::default_environment!(SeasonalClock, desktop);
|
||||||
@ -69,7 +68,12 @@ fn time_to_degrees(timestamp: i32, // should be time/timestamp
|
|||||||
seconds_to_degrees(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 {
|
let season = match hour {
|
||||||
0..=5 => Season::Winter,
|
0..=5 => Season::Winter,
|
||||||
6..=11 => Season::Spring,
|
6..=11 => Season::Spring,
|
||||||
@ -106,16 +110,10 @@ fn hour_marker(hour: i32, is_current_hour: bool, utc_hour_font_size: f32) -> Gro
|
|||||||
0,
|
0,
|
||||||
))
|
))
|
||||||
.close();
|
.close();
|
||||||
let path = Path::new().set("d", path_data);
|
let path = Path::new().set("class", "hour-outline").set("d", path_data);
|
||||||
let hour_name_text_path = TextPath::new()
|
let hour_name_path = Path::new()
|
||||||
.set("xlink:href", "#hour-name-path")
|
.set("class", "hour-name")
|
||||||
.set("startOffset", "50%")
|
.set("d", hour_name_path_data.clone());
|
||||||
.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 utc_hour_text = Text::new()
|
let utc_hour_text = Text::new()
|
||||||
.set("class", "utc")
|
.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(path)
|
||||||
.add(hour_name_text)
|
.add(hour_name_path)
|
||||||
.add(utc_hour_text)
|
.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)
|
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 local_timestamp = Local::now();
|
||||||
let utc_hour = local_timestamp.with_timezone(&Utc).time().hour();
|
let utc_hour = local_timestamp.with_timezone(&Utc).time().hour();
|
||||||
let local_hour = local_timestamp.time().hour();
|
let local_hour = local_timestamp.time().hour();
|
||||||
@ -253,17 +251,17 @@ fn gen_svg(config: &Option<Config>) -> Document {
|
|||||||
let stylesheet = Style::new(
|
let stylesheet = Style::new(
|
||||||
"\
|
"\
|
||||||
#border {stroke: none; fill: rgb(19, 17, 30); }
|
#border {stroke: none; fill: rgb(19, 17, 30); }
|
||||||
.hour path {stroke: rgb(0, 0, 0); stroke-width: 2px;}
|
.hour path.hour-outline {stroke: rgb(0, 0, 0); stroke-width: 2px;}
|
||||||
.hour text {stroke: none; fill: rgb(238, 187, 85);}
|
.hour path.hour-name {stroke: none; fill: rgb(238, 187, 85);}
|
||||||
.hour text.utc {stroke: none; fill: rgb(91, 68, 38);}
|
.hour text.utc {stroke: none; fill: rgb(91, 68, 38);}
|
||||||
.winter path {fill: rgb(70, 62, 108);}
|
.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);}
|
.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);}
|
.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);}
|
.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);}
|
.local-hour {stroke: none; fill: rgb(238, 187, 85);}
|
||||||
.night-time {stroke: none; fill: rgb(19, 17, 30);}
|
.night-time {stroke: none; fill: rgb(19, 17, 30);}
|
||||||
.blue-hour {stroke: none; fill: rgb(9, 1, 119);}
|
.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;}",
|
#current-hour-name {font-weight: bold;}",
|
||||||
);
|
);
|
||||||
|
|
||||||
let definitions = Definitions::new().add(hour_name_path());
|
|
||||||
let mut local_clock = Group::new().set("id", "local-clock");
|
let mut local_clock = Group::new().set("id", "local-clock");
|
||||||
|
|
||||||
for hour in 0i32..24 {
|
for hour in 0i32..24 {
|
||||||
@ -317,6 +314,7 @@ fn gen_svg(config: &Option<Config>) -> Document {
|
|||||||
for hour in 0i32..24 {
|
for hour in 0i32..24 {
|
||||||
seasonal_clock = seasonal_clock.add(hour_marker(
|
seasonal_clock = seasonal_clock.add(hour_marker(
|
||||||
hour,
|
hour,
|
||||||
|
&hour_name_path_cache[hour as usize],
|
||||||
hour == utc_hour as i32,
|
hour == utc_hour as i32,
|
||||||
utc_hour_font_size,
|
utc_hour_font_size,
|
||||||
));
|
));
|
||||||
@ -559,7 +557,6 @@ fn gen_svg(config: &Option<Config>) -> Document {
|
|||||||
.set("height", 700i32)
|
.set("height", 700i32)
|
||||||
.set("xmlns:xlink", "http://www.w3.org/1999/xlink")
|
.set("xmlns:xlink", "http://www.w3.org/1999/xlink")
|
||||||
.add(stylesheet)
|
.add(stylesheet)
|
||||||
.add(definitions)
|
|
||||||
.add(border)
|
.add(border)
|
||||||
.add(local_clock);
|
.add(local_clock);
|
||||||
|
|
||||||
@ -628,8 +625,17 @@ fn main() {
|
|||||||
let mut need_redraw = false;
|
let mut need_redraw = false;
|
||||||
let mut dimensions = (700, 700);
|
let mut dimensions = (700, 700);
|
||||||
|
|
||||||
|
let hour_name_path_cache = cache_hour_name_paths();
|
||||||
|
|
||||||
if !env.get_shell().unwrap().needs_configure() {
|
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()
|
window.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,7 +663,14 @@ fn main() {
|
|||||||
match next_action.take() {
|
match next_action.take() {
|
||||||
Some(WEvent::Close) => break,
|
Some(WEvent::Close) => break,
|
||||||
Some(WEvent::Refresh) => {
|
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.refresh();
|
||||||
window.surface().commit();
|
window.surface().commit();
|
||||||
}
|
}
|
||||||
@ -680,7 +693,14 @@ fn main() {
|
|||||||
if need_redraw {
|
if need_redraw {
|
||||||
need_redraw = false;
|
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() {
|
if let Err(e) = display.flush() {
|
||||||
@ -698,8 +718,9 @@ fn redraw(
|
|||||||
surface: &wl_surface::WlSurface,
|
surface: &wl_surface::WlSurface,
|
||||||
(buf_x, buf_y): (u32, u32),
|
(buf_x, buf_y): (u32, u32),
|
||||||
config: &Option<Config>,
|
config: &Option<Config>,
|
||||||
|
hour_name_path_cache: &[PathData; 24],
|
||||||
) -> Result<(), ::std::io::Error> {
|
) -> 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 svg_tree = svg_to_usvg(document);
|
||||||
|
|
||||||
let (canvas, new_buffer) = pool.buffer(
|
let (canvas, new_buffer) = pool.buffer(
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use svg::{
|
use svg::{
|
||||||
node::element::{path::Data as PathData, Path},
|
node::{
|
||||||
|
element::{path::Data as PathData, Definitions, Path, Text, TextPath},
|
||||||
|
Text as TextNode,
|
||||||
|
},
|
||||||
Document,
|
Document,
|
||||||
};
|
};
|
||||||
use usvg::Tree;
|
use usvg::Tree;
|
||||||
@ -24,7 +27,7 @@ pub fn svg_to_usvg(document: Document) -> Tree {
|
|||||||
Tree::from_str(&doc_str, &opt.to_ref()).unwrap()
|
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 radius = OUTER_R - RING_WIDTH / 2.0;
|
||||||
let delta_x = radius * (15.0_f32.to_radians() / 2.0).sin();
|
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());
|
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)
|
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