a391e4435bc4a20333bf6dde83e4e204a0fb11ac
[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 std::sync::{Arc, Mutex};
6 use num_traits::Zero;
7 use num_traits::cast::AsPrimitive;
8 use percent_encoding::percent_decode_str;
9 use actix_web::{get, web, App, HttpServer, HttpRequest, HttpResponse, Responder, ResponseError, Result};
10 use actix_web::body::BoxBody;
11 use actix_web::http::StatusCode;
12 use image::{ImageBuffer, ColorType, Rgb, RgbImage, write_buffer_with_format};
13 use image::ImageOutputFormat::Png;
14 use image::imageops::{FilterType, resize};
15 use image::error::{LimitError, LimitErrorKind};
16
17
18 #[derive(Debug)]
19 struct ImageError(image::ImageError);
20
21 impl ImageError {
22 fn dimension_error() -> ImageError {
23 ImageError(image::ImageError::Limits(LimitError::from_kind(LimitErrorKind::DimensionError)))
24 }
25 }
26
27 impl Display for ImageError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 write!(f, "{}", &self.0)
30 }
31 }
32
33 impl ResponseError for ImageError {
34 fn status_code(&self) -> StatusCode {
35 StatusCode::INTERNAL_SERVER_ERROR
36 }
37
38 fn error_response(&self) -> HttpResponse<BoxBody> {
39 HttpResponse::InternalServerError().body(format!("{}\n", &self))
40 }
41 }
42
43 #[derive(Debug)]
44 enum CanvasError {
45 NotExists
46 }
47
48 impl Display for CanvasError {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 write!(f, "Canvas Error: see error_response() for more details")
51 }
52 }
53
54 impl ResponseError for CanvasError {
55 fn status_code(&self) -> StatusCode {
56 match &self {
57 CanvasError::NotExists => StatusCode::NOT_FOUND
58 }
59 }
60
61 fn error_response(&self) -> HttpResponse<BoxBody> {
62 match &self {
63 CanvasError::NotExists => HttpResponse::NotFound().body("Canvas does not exist.")
64 }
65 }
66 }
67
68 struct ToRgbIter<'a, T> where T: AsPrimitive<u8> {
69 iter: Box<&'a mut dyn Iterator<Item = T>>
70 }
71
72 impl<'a, T: AsPrimitive<u8>> ToRgbIter<'a, T> {
73 fn new(data: &'a mut (dyn Iterator<Item = T>)) -> ToRgbIter<T> {
74 ToRgbIter { iter: Box::new(data) }
75 }
76 }
77
78 impl<T: AsPrimitive<u8> + Zero> Iterator for ToRgbIter<'_, T> {
79 type Item = Rgb<u8>;
80 fn next(&mut self) -> Option<Rgb<u8>> {
81 if let Some(r) = self.iter.next() {
82 let g = self.iter.next().unwrap_or(T::zero());
83 let b = self.iter.next().unwrap_or(T::zero());
84 Some(Rgb([r.as_(), g.as_(), b.as_()]))
85 }
86 else {
87 None
88 }
89 }
90 }
91
92 fn to_imageresult<T> (result: Result<T, image::ImageError>) -> Result<T, ImageError> {
93 match result {
94 Ok(v) => Ok(v),
95 Err(e) => Err(ImageError(e))
96 }
97 }
98
99 struct Canvas {
100 pen: usize,
101 img: RgbImage
102 }
103
104 impl Canvas {
105 fn new(width: u32, height: u32) -> Canvas {
106 Canvas { pen: 0, img: ImageBuffer::new(width, height) }
107 }
108
109 fn from_image(img: RgbImage) -> Canvas {
110 Canvas { pen: 0, img }
111 }
112
113 /*fn replace(&self, img: RgbImage, pen: usize) -> Canvas {
114 self.img = img;
115 self.pen = pen;
116 self
117 }*/
118
119 fn advance_pen(mut self, offset: usize) -> Canvas {
120 //Canvas { pen: self.pen + offset, img: self.img }
121 self.pen += offset;
122 self.pen %= self.img.width() as usize * self.img.height() as usize;
123 self
124 }
125 }
126
127 #[get("/hello/{name}")]
128 async fn greet(name: web::Path<String>, req: HttpRequest) -> impl Responder {
129 println!("{:?}", req);
130 format!("Hello {name}!")
131 }
132
133 fn rgb_encode_to_canvas(mut canvas: Canvas, data: &mut dyn Iterator<Item = u8>) -> Result<Canvas, ImageError> {
134 let mut pixels = canvas.img.pixels_mut().skip(canvas.pen);
135 let mut counter = 0;
136
137 for sp in ToRgbIter::new(data) {
138 let mut dp = match pixels.next() {
139 Some(pixel) => pixel,
140 None => {
141 pixels = canvas.img.pixels_mut().skip(0);
142 pixels.next().ok_or(ImageError::dimension_error())?
143 }
144 };
145 println!("{:?}", sp);
146 dp.0 = sp.0;
147 counter += 1;
148 }
149
150 Ok(canvas.advance_pen(counter))
151 }
152
153 fn rgb_encode(img: RgbImage, data: &mut dyn Iterator<Item = u8>) -> Result<RgbImage, ImageError> {
154 Ok(rgb_encode_to_canvas(Canvas::from_image(img), data)?.img)
155 }
156
157 fn make_png(dim_x: u32, dim_y: u32, scale: u32, data: &mut dyn Iterator<Item = u8>) -> Result<Cursor<Vec<u8>>, ImageError> {
158 // Image must not be larger than 1 megapixel
159 if dim_x * dim_y * scale > 1048576 {
160 return Err(ImageError::dimension_error())
161 }
162
163 let img: RgbImage = rgb_encode(ImageBuffer::new(dim_x, dim_y), data)?;
164
165 let tdim_x = dim_x * scale;
166 let tdim_y = dim_y * scale;
167 let img = resize(&img, tdim_x, tdim_y, FilterType::Nearest);
168 let mut cursor = Cursor::new(Vec::new());
169 to_imageresult(write_buffer_with_format(&mut cursor, &img, tdim_x, tdim_y, ColorType::Rgb8, Png))?;
170 Ok(cursor)
171 }
172
173 #[get("/gen/0/{data}")]
174 async fn img_gen0(req: HttpRequest) -> Result<impl Responder> {
175 let data = req.uri().path().split("/").skip(3).next().unwrap();
176 let cursor = make_png(32, 32, 16, percent_decode_str(&data).into_iter().borrow_mut())?;
177 Ok(HttpResponse::build(StatusCode::OK)
178 .content_type("image/png")
179 .body(cursor.into_inner()))
180 }
181
182 #[get("/gen/1/{dim_x}/{dim_y}/{scale}/{data}")]
183 async fn img_gen1(req: HttpRequest, path: web::Path<(u32, u32, u32)>) -> Result<impl Responder> {
184 let (dim_x, dim_y, scale) = path.into_inner();
185 let data = req.uri().path().split("/").skip(6).next().unwrap();
186 let cursor = make_png(dim_x, dim_y, scale, percent_decode_str(&data).into_iter().borrow_mut())?;
187 Ok(HttpResponse::build(StatusCode::OK)
188 .content_type("image/png")
189 .body(cursor.into_inner()))
190 }
191
192 #[get("/pgen/0/{data}")]
193 async fn img_pgen0(req: HttpRequest, canvas0: web::Data<Arc<Mutex<Option<Canvas>>>>) -> Result<impl Responder> {
194 let data = req.uri().path().split("/").skip(3).next().unwrap();
195 let mut cursor = Cursor::new(Vec::new());
196 let scale = 16;
197 let img: RgbImage = {
198 let canvas_option = &mut *canvas0.lock().unwrap();
199 let canvas = canvas_option.take().ok_or(CanvasError::NotExists)?;
200 let canvas = rgb_encode_to_canvas(canvas, percent_decode_str(&data).into_iter().borrow_mut())?;
201 canvas_option.insert(canvas).img.clone()
202 };
203 let (tdim_x, tdim_y) = img.dimensions();
204 let (tdim_x, tdim_y) = (tdim_x * scale, tdim_y * scale);
205 let img = resize(&img, tdim_x, tdim_y, FilterType::Nearest);
206 to_imageresult(write_buffer_with_format(&mut cursor, &img, tdim_x, tdim_y, ColorType::Rgb8, Png))?;
207 Ok(HttpResponse::build(StatusCode::OK)
208 .content_type("image/png")
209 .body(cursor.into_inner()))
210 }
211
212 #[actix_web::main] // or #[tokio::main]
213 async fn main() -> std::io::Result<()> {
214 env_logger::init();
215 let canvas0 = Arc::new(Mutex::new(Some(Canvas::new(32, 32))));
216 HttpServer::new(move || {
217 App::new()
218 .service(greet)
219 .service(img_gen0)
220 .service(img_gen1)
221 .service(img_pgen0)
222 .app_data(web::Data::new(canvas0.clone()))
223 })
224 .bind(("127.0.0.1", 8080))?
225 .run()
226 .await
227 }