Add solar wallpaper support

main
Johannes Wünsche 2021-04-19 13:33:59 +02:00
parent f2853d4513
commit 79470ca392
Signed by: johannes
GPG Key ID: 2511CF80B7C283AB
7 changed files with 237 additions and 119 deletions

24
Cargo.lock generated
View File

@ -17,9 +17,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.39"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767"
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
[[package]]
name = "atty"
@ -210,9 +210,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.88"
version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "libheif-rs"
@ -310,9 +310,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.24"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
"unicode-xid",
]
@ -359,18 +359,18 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "serde"
version = "1.0.124"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f"
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.124"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b"
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2",
"quote",
@ -385,9 +385,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.64"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
dependencies = [
"proc-macro2",
"quote",

118
src/image.rs Normal file
View File

@ -0,0 +1,118 @@
use std::path::Path;
use libheif_rs::HeifContext;
use anyhow::Result;
use crate::util::png;
use crate::schema::xml::{Background, Image::{Static, Transition}};
use colored::*;
use crate::serializer::GnomeXMLBackgroundSerializer;
use crate::DAY_SECS;
use std::io::BufWriter;
pub struct ImagePoint<'a> {
pub image_ctx: &'a HeifContext,
pub img_id: u32,
pub index: usize,
pub background: &'a mut Background,
pub parent_directory: &'a Path,
pub start_time: f32,
pub time: f32,
pub next_time: f32,
}
pub fn process_img(pt: ImagePoint) -> Result<()> {
let prim_image = pt.image_ctx.image_handle(pt.img_id).unwrap();
let number_of_images = pt.image_ctx.number_of_top_level_images();
png::write_png(
format!("{}/{}.png", pt.parent_directory.to_string_lossy(), pt.index).as_str(),
prim_image,
)?;
// Add to Background Structure
pt.background.images.push(Static {
duration: 1 as f32,
file: format!("{}/{}.png", pt.parent_directory.to_string_lossy(), pt.index),
idx: pt.index,
});
pt.background.images.push(Transition {
kind: "overlay".to_string(),
duration: {
if pt.index < number_of_images - 1 {
(pt.time - pt.next_time).abs() * DAY_SECS - 1.0
} else {
(((pt.time - 1.0).abs() + pt.start_time) * DAY_SECS - 1.0).ceil()
}
},
from: format!("{}/{}.png", pt.parent_directory.to_string_lossy(), pt.index),
to: format!("{}/{}.png", pt.parent_directory.to_string_lossy(), {
if pt.index < number_of_images - 1 {
pt.index + 1
} else {
0
}
}),
idx: pt.index,
});
Ok(())
}
pub fn save_xml(xml: &mut Background, parent_directory: &Path) -> Result<()> {
println!("{}: {}", "Conversion".yellow(), "Done!");
println!("{}: {}", "Conversion".green(), "Creating xml description for new wallpaper");
xml.images.sort_by(|a, b| match (a, b) {
(
Static {
idx: static_idx, ..
},
Transition {
idx: transition_idx,
..
},
) => static_idx.cmp(&transition_idx),
(
Static {
idx: static_idx, ..
},
Static {
idx: transition_idx,
..
},
) => static_idx.cmp(&transition_idx),
(
Transition {
idx: static_idx, ..
},
Static {
idx: transition_idx,
..
},
) => static_idx.cmp(&transition_idx),
(
Transition {
idx: static_idx, ..
},
Transition {
idx: transition_idx,
..
},
) => static_idx.cmp(&transition_idx),
});
println!("{}: {}", "Conversion".green(), "Writing wallpaper description");
let result_file = std::fs::OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(format!(
"{}/default.xml",
parent_directory.to_string_lossy()
))?;
let mut result = BufWriter::new(result_file);
let mut ser = GnomeXMLBackgroundSerializer::new(&mut result);
ser.serialize(&xml)?;
println!("{}", "Done".green());
Ok(())
}

View File

@ -8,10 +8,13 @@ mod metadata;
mod schema;
mod serializer;
mod timebased;
mod solar;
mod util;
mod image;
const INPUT: &str = "IMAGE";
const DIR: &str = "DIR";
const DAY_SECS: f32 = 86400.0;
fn main() -> Result<()> {
let matches = App::new("heic-to-dynamic-gnome-wallpaper")
@ -67,10 +70,9 @@ fn main() -> Result<()> {
println!("{}: {}", "Preparation".bright_blue(), "Detected time-based wallpaper");
timebased::compute_time_based_wallpaper(image_ctx, content, &parent_directory)
}
metadata::WallPaperMode::Solar(_content) => {
metadata::WallPaperMode::Solar(content) => {
println!("{}: {}", "Preparation".bright_blue(), "Detected solar-based wallpaper");
eprintln!("Solar is not supported at the moment, please use wallpapers only with time based changes.");
std::process::exit(1)
solar::compute_solar_based_wallpaper(image_ctx, content, &&parent_directory)
}
}
}

View File

@ -2,7 +2,7 @@ use anyhow::Result;
use libheif_rs::HeifContext;
use quick_xml::{events::Event, Reader};
use crate::schema::plist::WallpaperMetaTime;
use crate::schema::plist::{WallpaperMetaSun, WallpaperMetaTime};
pub enum WallPaperMode {
H24(String),
@ -68,3 +68,9 @@ pub fn get_time_plist_from_base64(input: &String) -> Result<WallpaperMetaTime> {
let plist = plist::from_bytes(&decoded)?;
Ok(plist)
}
pub fn get_solar_plist_from_base64(input: &String) -> Result<WallpaperMetaSun> {
let decoded = base64::decode(input)?;
let plist = plist::from_bytes(&decoded)?;
Ok(plist)
}

View File

@ -28,8 +28,8 @@ pub struct TimeSlice {
pub struct WallpaperMetaSun {
#[serde(rename = "si")]
pub solar_slices: Vec<SolarSlice>,
#[serde(rename = "ap")]
pub appearance: Appearance,
// #[serde(rename = "ap")]
// pub appearance: Appearance,
}
#[derive(Deserialize, Debug)]
@ -38,8 +38,8 @@ pub struct SolarSlice {
pub altitude: f32,
#[serde(rename = "i")]
pub idx: usize,
#[serde(rename = "o")]
pub light_mode: usize,
// #[serde(rename = "o")]
// pub light_mode: usize,
#[serde(rename = "z")]
pub azimuth: f32,
}

69
src/solar.rs Normal file
View File

@ -0,0 +1,69 @@
use std::path::Path;
use crate::{image::process_img, metadata};
use anyhow::Result;
use libheif_rs::HeifContext;
use std::cmp::Ordering;
use crate::image::ImagePoint;
use crate::schema::xml::{Background, StartTime};
use crate::util::time;
use crate::DAY_SECS;
use indicatif::{ProgressIterator, ProgressBar, ProgressStyle};
#[derive(Debug)]
struct SolarToHourSlice {
time: f32,
index: usize,
}
const HOUR_PER_DEGREE:f32 = 24f32/360f32;
pub fn compute_solar_based_wallpaper(
image_ctx: HeifContext,
content: String,
parent_directory: &Path,
) -> Result<()> {
let mut plist = metadata::get_solar_plist_from_base64(&content)?;
plist.solar_slices.sort_by(|x, y| x.azimuth.partial_cmp(&y.azimuth).unwrap_or(Ordering::Equal));
let time_slices: Vec<SolarToHourSlice> = plist.solar_slices.iter().map(|elem|{
SolarToHourSlice {
time: elem.azimuth / 360f32,
index: elem.idx,
}
}).collect();
let mut img_ids = vec![0; image_ctx.number_of_top_level_images()];
image_ctx.top_level_image_ids(&mut img_ids);
let start_time = time_slices.get(0).expect("No image has been found").time;
let start_seconds = (start_time * DAY_SECS) as u16;
let mut background_definition = Background {
starttime: StartTime {
year: 2011,
month: 10,
day: 1,
hour: time::to_rem_hours(start_seconds),
minute: time::to_rem_min(start_seconds),
second: time::to_rem_sec(start_seconds),
},
images: vec!(),
};
let pb = ProgressBar::new(image_ctx.number_of_top_level_images() as u64).with_style(ProgressStyle::default_bar()
.template("Conversion: {wide_bar} {pos}/{len} [ETA: {eta_precise}]")
.progress_chars("## "));
for (idx, SolarToHourSlice { time, index }) in time_slices.iter().enumerate().progress_with(pb) {
let img_id = img_ids[*index];
let pt = ImagePoint {
image_ctx: &image_ctx,
img_id,
index: idx,
background: &mut background_definition,
parent_directory,
start_time,
time: *time,
next_time: time_slices.get(idx + 1).map(|elem| elem.time).unwrap_or(0f32),
};
process_img(pt)?;
}
crate::image::save_xml(&mut background_definition, parent_directory)
}

View File

@ -1,20 +1,20 @@
use std::{io::BufWriter, path::Path};
use std::path::Path;
use crate::metadata;
use crate::schema::plist::TimeSlice;
use crate::schema::xml::{
Background,
Image::{Static, Transition},
StartTime,
};
use crate::serializer::GnomeXMLBackgroundSerializer;
use crate::util::{png, time};
use crate::DAY_SECS;
use crate::image::{ImagePoint, process_img, save_xml};
use crate::util::time;
use anyhow::Result;
use libheif_rs::HeifContext;
use indicatif::{ProgressIterator, ProgressBar, ProgressStyle};
use colored::*;
const DAY_SECS: f32 = 86400.0;
pub fn compute_time_based_wallpaper(
image_ctx: HeifContext,
@ -27,22 +27,22 @@ pub fn compute_time_based_wallpaper(
plist
.time_slices
.sort_by(|a, b| a.time.partial_cmp(&b.time).unwrap());
let first_time = plist.time_slices.get(0).unwrap().time as u16;
let start_time = plist.time_slices.get(0).unwrap().time;
let start_seconds = (start_time * DAY_SECS) as u16;
let mut xml_background = Background {
images: Vec::new(),
starttime: StartTime {
year: 2011,
month: 10,
day: 1,
hour: time::to_rem_hours(first_time),
minute: time::to_rem_min(first_time),
second: time::to_rem_sec(first_time),
hour: time::to_rem_hours(start_seconds),
minute: time::to_rem_min(start_seconds),
second: time::to_rem_sec(start_seconds),
},
};
let number_of_images = image_ctx.number_of_top_level_images();
println!("{}: {} {} {}", "Preparation".bright_blue(), "Found", number_of_images, "images");
// Has to be initialized
let mut image_ids = vec![0u32; number_of_images];
image_ctx.top_level_image_ids(&mut image_ids);
println!("{}: {}", "Conversion".yellow(), "Converting embedded images to png format");
@ -52,95 +52,18 @@ pub fn compute_time_based_wallpaper(
for (time_idx, TimeSlice{time, idx}) in plist.time_slices.iter().enumerate().progress_with(pb) {
let img_id = *image_ids.get(*idx).expect("Could not fetch image id described in metadata");
//println!("Image ID: {:?}", img_id);
let prim_image = image_ctx.image_handle(img_id).unwrap();
png::write_png(
format!("{}/{}.png", parent_directory.to_string_lossy(), time_idx).as_str(),
prim_image,
)?;
// Add to Background Structure
xml_background.images.push(Static {
duration: 1 as f32,
file: format!("{}/{}.png", parent_directory.to_string_lossy(), time_idx),
idx: time_idx,
});
xml_background.images.push(Transition {
kind: "overlay".to_string(),
duration: {
if time_idx < number_of_images - 1 {
(time - plist.time_slices.get(time_idx + 1).unwrap().time).abs() * DAY_SECS
- 1.0
} else {
let first_time = plist.time_slices.get(0).unwrap().time;
(((time - 1.0).abs() + first_time) * DAY_SECS - 1.0).ceil()
}
},
from: format!("{}/{}.png", parent_directory.to_string_lossy(), time_idx),
to: format!("{}/{}.png", parent_directory.to_string_lossy(), {
if time_idx < number_of_images - 1 {
time_idx + 1
} else {
0
}
}),
idx: time_idx,
});
let pt = ImagePoint{
image_ctx: &image_ctx,
img_id,
index: time_idx,
background: &mut xml_background,
parent_directory,
start_time,
time: *time,
next_time: plist.time_slices.get(time_idx + 1).map(|elem| elem.time).unwrap_or(0f32),
};
process_img(pt)?;
}
println!("{}: {}", "Conversion".yellow(), "Done!");
println!("{}: {}", "Conversion".green(), "Creating xml description for new wallpaper");
xml_background.images.sort_by(|a, b| match (a, b) {
(
Static {
idx: static_idx, ..
},
Transition {
idx: transition_idx,
..
},
) => static_idx.cmp(&transition_idx),
(
Static {
idx: static_idx, ..
},
Static {
idx: transition_idx,
..
},
) => static_idx.cmp(&transition_idx),
(
Transition {
idx: static_idx, ..
},
Static {
idx: transition_idx,
..
},
) => static_idx.cmp(&transition_idx),
(
Transition {
idx: static_idx, ..
},
Transition {
idx: transition_idx,
..
},
) => static_idx.cmp(&transition_idx),
});
println!("{}: {}", "Conversion".green(), "Writing wallpaper description");
let result_file = std::fs::OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(format!(
"{}/default.xml",
parent_directory.to_string_lossy()
))?;
let mut result = BufWriter::new(result_file);
let mut ser = GnomeXMLBackgroundSerializer::new(&mut result);
ser.serialize(&xml_background)?;
println!("{}", "Done".green());
Ok(())
save_xml(&mut xml_background, parent_directory)
}