use std::sync::{Arc, OnceLock};
use tiny_skia::Pixmap;
use crate::render::{AvatarFormat, Renderers, png_writer::to_png};
use crate::svg::{SvgDocument, SvgParser, SvgRasterizer};
use super::avatar_options::AvatarOptions;
#[derive(Debug, Clone)]
pub struct Avatar {
input: String,
options: AvatarOptions,
svg: String,
doc: OnceLock<Arc<SvgDocument>>,
}
impl Avatar {
pub(crate) fn new(input: String, options: AvatarOptions, svg: String) -> Avatar {
Avatar {
input,
options,
svg,
doc: OnceLock::new(),
}
}
pub fn input(&self) -> &str {
&self.input
}
pub fn options(&self) -> &AvatarOptions {
&self.options
}
pub fn svg(&self) -> &str {
&self.svg
}
pub fn document(&self) -> Arc<SvgDocument> {
self.doc
.get_or_init(|| Arc::new(SvgParser::parse(&self.svg)))
.clone()
}
pub fn is_empty(&self) -> bool {
self.svg.is_empty()
}
pub fn to_image(&self) -> Option<Pixmap> {
self.to_image_sized(self.options.size())
}
pub fn to_image_sized(&self, size: u32) -> Option<Pixmap> {
if self.is_empty() {
return None;
}
let doc = self.document();
SvgRasterizer::rasterize(&doc, size)
}
pub fn png(&self) -> Vec<u8> {
self.png_sized(self.options.size())
}
pub fn png_sized(&self, size: u32) -> Vec<u8> {
match self.to_image_sized(size) {
Some(pixmap) => to_png(&pixmap),
None => Vec::new(),
}
}
pub fn jpg(&self) -> Vec<u8> {
self.jpg_sized(self.options.size())
}
pub fn jpg_sized(&self, size: u32) -> Vec<u8> {
if self.is_empty() {
return Vec::new();
}
let renderer = Renderers::create(AvatarFormat::Jpg);
renderer.render(&self.svg, size)
}
pub fn data_uri(&self) -> String {
self.data_uri_sized(self.options.size())
}
pub fn data_uri_sized(&self, size: u32) -> String {
let png = self.png_sized(size);
if png.is_empty() {
return String::new();
}
format!(
"data:image/png;base64,{}",
crate::util::base64::encode(&png)
)
}
}