Print random easter egg
1
Cargo.lock
generated
|
|
@ -1388,6 +1388,7 @@ dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"serde",
|
"serde",
|
||||||
|
"showbits-assets",
|
||||||
"showbits-common",
|
"showbits-common",
|
||||||
"taffy",
|
"taffy",
|
||||||
"time",
|
"time",
|
||||||
|
|
|
||||||
BIN
showbits-assets/data/egg.xcf
Normal file
BIN
showbits-assets/data/egg_cover.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
showbits-assets/data/egg_pattern_0.png
Normal file
|
After Width: | Height: | Size: 673 B |
BIN
showbits-assets/data/egg_pattern_1.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
showbits-assets/data/egg_pattern_2.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
showbits-assets/data/egg_pattern_3.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
showbits-assets/data/egg_pattern_4.png
Normal file
|
After Width: | Height: | Size: 694 B |
BIN
showbits-assets/data/egg_pattern_5.png
Normal file
|
After Width: | Height: | Size: 901 B |
BIN
showbits-assets/data/egg_pattern_6.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -3,3 +3,14 @@ pub const UNIFONT_JP: &[u8] = include_bytes!("../data/unifont_jp-15.1.05.otf");
|
||||||
pub const UNIFONT_UPPER: &[u8] = include_bytes!("../data/unifont_upper-15.1.05.otf");
|
pub const UNIFONT_UPPER: &[u8] = include_bytes!("../data/unifont_upper-15.1.05.otf");
|
||||||
pub const UNIFONT_NAME: &str = "Unifont";
|
pub const UNIFONT_NAME: &str = "Unifont";
|
||||||
pub const UNIFONT_SIZE: f32 = 16.0;
|
pub const UNIFONT_SIZE: f32 = 16.0;
|
||||||
|
|
||||||
|
pub const EGG_COVER: &[u8] = include_bytes!("../data/egg_cover.png");
|
||||||
|
pub const EGG_PATTERNS: &[&[u8]] = &[
|
||||||
|
include_bytes!("../data/egg_pattern_0.png"),
|
||||||
|
include_bytes!("../data/egg_pattern_1.png"),
|
||||||
|
include_bytes!("../data/egg_pattern_2.png"),
|
||||||
|
include_bytes!("../data/egg_pattern_3.png"),
|
||||||
|
include_bytes!("../data/egg_pattern_4.png"),
|
||||||
|
include_bytes!("../data/egg_pattern_5.png"),
|
||||||
|
include_bytes!("../data/egg_pattern_6.png"),
|
||||||
|
];
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ palette.workspace = true
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rust-embed = "8.3.0"
|
rust-embed = "8.3.0"
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
|
showbits-assets.workspace = true
|
||||||
showbits-common.workspace = true
|
showbits-common.workspace = true
|
||||||
taffy.workspace = true
|
taffy.workspace = true
|
||||||
time = "0.3.34"
|
time = "0.3.34"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
mod calendar;
|
mod calendar;
|
||||||
mod cells;
|
mod cells;
|
||||||
mod chat_message;
|
mod chat_message;
|
||||||
|
mod egg;
|
||||||
mod image;
|
mod image;
|
||||||
mod photo;
|
mod photo;
|
||||||
mod text;
|
mod text;
|
||||||
|
|
@ -12,7 +13,7 @@ use crate::printer::Printer;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
calendar::CalendarDrawing, cells::CellsDrawing, chat_message::ChatMessageDrawing,
|
calendar::CalendarDrawing, cells::CellsDrawing, chat_message::ChatMessageDrawing,
|
||||||
image::ImageDrawing, photo::PhotoDrawing, text::TextDrawing,
|
egg::EggDrawing, image::ImageDrawing, photo::PhotoDrawing, text::TextDrawing,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
|
||||||
82
showbits-thermal-printer/src/drawer/egg.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
use image::{imageops, RgbaImage};
|
||||||
|
use rand::Rng;
|
||||||
|
use showbits_assets::{EGG_COVER, EGG_PATTERNS};
|
||||||
|
use showbits_common::{
|
||||||
|
color::{self, WHITE},
|
||||||
|
widgets::{Image, Text},
|
||||||
|
Node, Tree, WidgetExt,
|
||||||
|
};
|
||||||
|
use taffy::{style_helpers::percent, AlignItems, Display, FlexDirection};
|
||||||
|
|
||||||
|
use crate::printer::Printer;
|
||||||
|
|
||||||
|
use super::{Context, Drawing};
|
||||||
|
|
||||||
|
pub struct EggDrawing;
|
||||||
|
|
||||||
|
fn load_image(bytes: &[u8]) -> anyhow::Result<RgbaImage> {
|
||||||
|
Ok(image::load_from_memory(bytes)?.into_rgba8())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drawing for EggDrawing {
|
||||||
|
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
|
// Load image data from memory
|
||||||
|
let cover = load_image(EGG_COVER)?;
|
||||||
|
let mut patterns = vec![];
|
||||||
|
for pattern in EGG_PATTERNS {
|
||||||
|
patterns.push(load_image(pattern)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare egg image
|
||||||
|
let mut image =
|
||||||
|
RgbaImage::from_pixel(cover.width(), cover.height(), color::to_image_color(WHITE));
|
||||||
|
|
||||||
|
// Draw patterns onto egg
|
||||||
|
let mut last_idx = None;
|
||||||
|
let mut y = rand::thread_rng().gen_range(-100_i64..0);
|
||||||
|
while y < image.height().into() {
|
||||||
|
let idx = loop {
|
||||||
|
let idx = rand::thread_rng().gen_range(0..patterns.len());
|
||||||
|
if Some(idx) != last_idx {
|
||||||
|
break idx;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let paint = &patterns[idx];
|
||||||
|
imageops::overlay(&mut image, paint, 0, y);
|
||||||
|
y += <_ as Into<i64>>::into(paint.height());
|
||||||
|
last_idx = Some(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, draw the cover
|
||||||
|
imageops::overlay(&mut image, &cover, 0, 0);
|
||||||
|
|
||||||
|
let mut tree = Tree::<Context>::new(WHITE);
|
||||||
|
|
||||||
|
let image = Image::new(image)
|
||||||
|
.with_grow(false)
|
||||||
|
.with_shrink(false)
|
||||||
|
.node()
|
||||||
|
.register(&mut tree)?;
|
||||||
|
|
||||||
|
let text = Text::new()
|
||||||
|
.with_metrics(Text::default_metrics().scale(2.0))
|
||||||
|
.and_plain("Frohe Ostern!")
|
||||||
|
.widget(&mut ctx.font_stuff)
|
||||||
|
.node()
|
||||||
|
.register(&mut tree)?;
|
||||||
|
|
||||||
|
let root = Node::empty()
|
||||||
|
.with_size_width(percent(1.0))
|
||||||
|
.with_display(Display::Flex)
|
||||||
|
.with_flex_direction(FlexDirection::Column)
|
||||||
|
.with_align_items(Some(AlignItems::Center))
|
||||||
|
.and_child(image)
|
||||||
|
.and_child(text)
|
||||||
|
.register(&mut tree)?;
|
||||||
|
|
||||||
|
printer.print_tree(&mut tree, ctx, root)?;
|
||||||
|
printer.feed()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,8 +13,8 @@ use serde::Deserialize;
|
||||||
use tokio::{net::TcpListener, sync::mpsc};
|
use tokio::{net::TcpListener, sync::mpsc};
|
||||||
|
|
||||||
use crate::drawer::{
|
use crate::drawer::{
|
||||||
CalendarDrawing, CellsDrawing, ChatMessageDrawing, Command, ImageDrawing, PhotoDrawing,
|
CalendarDrawing, CellsDrawing, ChatMessageDrawing, Command, EggDrawing, ImageDrawing,
|
||||||
TextDrawing,
|
PhotoDrawing, TextDrawing,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{r#static::get_static_file, statuscode::status_code};
|
use self::{r#static::get_static_file, statuscode::status_code};
|
||||||
|
|
@ -29,6 +29,7 @@ pub async fn run(tx: mpsc::Sender<Command>, addr: String) -> anyhow::Result<()>
|
||||||
.route("/calendar", post(post_calendar))
|
.route("/calendar", post(post_calendar))
|
||||||
.route("/cells", post(post_cells))
|
.route("/cells", post(post_cells))
|
||||||
.route("/chat_message", post(post_chat_message))
|
.route("/chat_message", post(post_chat_message))
|
||||||
|
.route("/egg", post(post_egg))
|
||||||
.route("/image", post(post_image).fallback(get_static_file))
|
.route("/image", post(post_image).fallback(get_static_file))
|
||||||
.route("/photo", post(post_photo).fallback(get_static_file))
|
.route("/photo", post(post_photo).fallback(get_static_file))
|
||||||
.route("/text", post(post_text))
|
.route("/text", post(post_text))
|
||||||
|
|
@ -97,6 +98,12 @@ async fn post_chat_message(server: State<Server>, request: Form<PostChatMessageF
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /egg
|
||||||
|
|
||||||
|
async fn post_egg(server: State<Server>) {
|
||||||
|
let _ = server.tx.send(Command::draw(EggDrawing)).await;
|
||||||
|
}
|
||||||
|
|
||||||
// /image
|
// /image
|
||||||
|
|
||||||
async fn post_image(server: State<Server>, mut multipart: Multipart) -> somehow::Result<Response> {
|
async fn post_image(server: State<Server>, mut multipart: Multipart) -> somehow::Result<Response> {
|
||||||
|
|
|
||||||