Generic make_png function
[litoprism.git] / src / main.rs
1 use std::io::Cursor;
2 use std::fmt::Display;
3 use std::fmt;
4 use std::borrow::BorrowMut;
5 use num_traits::Zero;
6 use num_traits::cast::AsPrimitive;
7 use percent_encoding::percent_decode_str;
8 use actix_web::{get, web, App, HttpServer, HttpRequest, HttpResponse, Responder, ResponseError, Result};
9 use actix_web::body::BoxBody;
10 use actix_web::http::StatusCode;
11 use image::{ImageBuffer, ColorType, Pixel, Rgb, RgbImage, write_buffer_with_format};
12 use image::ImageOutputFormat::Png;
13 use image::imageops::{FilterType, resize};
14
15
16 #[derive(Debug)]
17 struct ImageError(image::ImageError);
18
19 impl Display for ImageError {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 write!(f, "{}", &self.0)
22 }
23 }
24
25 impl ResponseError for ImageError {
26 fn status_code(&self) -> StatusCode {
27 StatusCode::INTERNAL_SERVER_ERROR
28 }
29
30 fn error_response(&self) -> HttpResponse<BoxBody> {
31 HttpResponse::InternalServerError().finish()
32 }
33 }
34
35 struct ToRgbIter<'a, T> where T: AsPrimitive<u8> {
36 iter: Box<&'a mut dyn Iterator<Item = T>>
37 }
38
39 impl<'a, T: AsPrimitive<u8>> ToRgbIter<'a, T> {
40 fn new(data: &'a mut (dyn Iterator<Item = T>)) -> ToRgbIter<T> {
41 ToRgbIter { iter: Box::new(data) }
42 }
43 }
44
45 impl<T: AsPrimitive<u8> + Zero> Iterator for ToRgbIter<'_, T> {
46 type Item = Rgb<u8>;
47 fn next(&mut self) -> Option<Rgb<u8>> {
48 if let Some(r) = self.iter.next() {
49 let g = self.iter.next().unwrap_or(T::zero());
50 let b = self.iter.next().unwrap_or(T::zero());
51 println!("{} {} {}", r.as_(), g.as_(), b.as_());
52 Some(Rgb([r.as_(), g.as_(), b.as_()]))
53 }
54 else {
55 None
56 }
57 }
58 }
59
60 fn to_imageresult<T> (result: Result<T, image::ImageError>) -> Result<T, ImageError> {
61 match result {
62 Ok(v) => Ok(v),
63 Err(e) => Err(ImageError(e))
64 }
65 }
66
67 #[get("/hello/{name}")]
68 async fn greet(name: web::Path<String>, req: HttpRequest) -> impl Responder {
69 println!("{:?}", req);
70 format!("Hello {name}!")
71 }
72
73 fn make_png(dim_x: u32, dim_y: u32, scale: u32, data: &str) -> Result<Cursor<Vec<u8>>, ImageError> {
74 let mut img: RgbImage = ImageBuffer::new(dim_x, dim_y);
75 let mut pixels = img.pixels_mut();
76
77 for sp in ToRgbIter::new(percent_decode_str(&data).into_iter().borrow_mut()) {
78 let mut dp = pixels.next().unwrap();
79 println!("{:?}", sp);
80 dp.0 = sp.0;
81 }
82
83 let tdim_x = dim_x * scale;
84 let tdim_y = dim_y * scale;
85 let img = resize(&img, tdim_x, tdim_y, FilterType::Nearest);
86 let mut cursor = Cursor::new(Vec::new());
87 to_imageresult(write_buffer_with_format(&mut cursor, &img, tdim_x, tdim_y, ColorType::Rgb8, Png))?;
88 Ok(cursor)
89 }
90
91 #[get("/gen/0/{data}")]
92 async fn img_gen0(req: HttpRequest) -> Result<impl Responder> {
93 let data = req.uri().path().split("/").skip(3).next().unwrap();
94 let cursor = make_png(32, 32, 16, &data)?;
95 Ok(HttpResponse::build(StatusCode::OK)
96 .content_type("image/png")
97 .body(cursor.into_inner()))
98 }
99
100 #[get("/gen/1/{dim_x}/{dim_y}/{scale}/{data}")]
101 async fn img_gen1(req: HttpRequest, dim_x: web::Path<u16>, dim_y: web::Path<u16>, scale: web::Path<u8>) -> Result<impl Responder> {
102 Ok(HttpResponse::build(StatusCode::OK))
103 }
104
105 #[actix_web::main] // or #[tokio::main]
106 async fn main() -> std::io::Result<()> {
107 HttpServer::new(|| {
108 App::new()
109 .service(greet)
110 .service(img_gen0)
111 .service(img_gen1)
112 })
113 .bind(("127.0.0.1", 8080))?
114 .run()
115 .await
116 }