Implement /pgen/1
[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 DimensionMismatch
47 }
48
49 impl Display for CanvasError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(f, "Canvas Error: see error_response() for more details")
52 }
53 }
54
55 impl ResponseError for CanvasError {
56 fn status_code(&self) -> StatusCode {
57 match &self {
58 CanvasError::NotExists => StatusCode::NOT_FOUND,
59 CanvasError::DimensionMismatch => StatusCode::BAD_REQUEST
60 }
61 }
62
63 fn error_response(&self) -> HttpResponse<BoxBody> {
64 match &self {
65 CanvasError::NotExists => HttpResponse::NotFound().body("Canvas does not exist."),
66 CanvasError::DimensionMismatch => HttpResponse::BadRequest().body("Supplied dimensions don't much the dimensions of existing canvas.")
67 }
68 }
69 }
70
71 struct ToRgbIter<'a, T> where T: AsPrimitive<u8> {
72 iter: Box<&'a mut dyn Iterator<Item = T>>
73 }
74
75 impl<'a, T: AsPrimitive<u8>> ToRgbIter<'a, T> {
76 fn new(data: &'a mut (dyn Iterator<Item = T>)) -> ToRgbIter<T> {
77 ToRgbIter { iter: Box::new(data) }
78 }
79 }
80
81 impl<T: AsPrimitive<u8> + Zero> Iterator for ToRgbIter<'_, T> {
82 type Item = Rgb<u8>;
83 fn next(&mut self) -> Option<Rgb<u8>> {
84 if let Some(r) = self.iter.next() {
85 let g = self.iter.next().unwrap_or(T::zero());
86 let b = self.iter.next().unwrap_or(T::zero());
87 Some(Rgb([r.as_(), g.as_(), b.as_()]))
88 }
89 else {
90 None
91 }
92 }
93 }
94
95 fn to_imageresult<T> (result: Result<T, image::ImageError>) -> Result<T, ImageError> {
96 match result {
97 Ok(v) => Ok(v),
98 Err(e) => Err(ImageError(e))
99 }
100 }
101
102 struct Canvas {
103 pen: usize,
104 img: RgbImage
105 }
106
107 impl Canvas {
108 fn new(width: u32, height: u32) -> Canvas {
109 Canvas { pen: 0, img: ImageBuffer::new(width, height) }
110 }
111
112 fn from_image(img: RgbImage) -> Canvas {
113 Canvas { pen: 0, img }
114 }
115
116 /*fn replace(&self, img: RgbImage, pen: usize) -> Canvas {
117 self.img = img;
118 self.pen = pen;
119 self
120 }*/
121
122 fn advance_pen(mut self, offset: usize) -> Canvas {
123 //Canvas { pen: self.pen + offset, img: self.img }
124 self.pen += offset;
125 self.pen %= self.img.width() as usize * self.img.height() as usize;
126 self
127 }
128 }
129
130 #[get("/hello/{name}")]
131 async fn greet(name: web::Path<String>, req: HttpRequest) -> impl Responder {
132 println!("{:?}", req);
133 format!("Hello {name}!")
134 }
135
136 fn rgb_encode_to_canvas(mut canvas: Canvas, data: &mut dyn Iterator<Item = u8>) -> Result<Canvas, ImageError> {
137 let mut pixels = canvas.img.pixels_mut().skip(canvas.pen);
138 let mut counter = 0;
139
140 for sp in ToRgbIter::new(data) {
141 let mut dp = match pixels.next() {
142 Some(pixel) => pixel,
143 None => {
144 pixels = canvas.img.pixels_mut().skip(0);
145 pixels.next().ok_or(ImageError::dimension_error())?
146 }
147 };
148 println!("{:?}", sp);
149 dp.0 = sp.0;
150 counter += 1;
151 }
152
153 Ok(canvas.advance_pen(counter))
154 }
155
156 fn rgb_encode(img: RgbImage, data: &mut dyn Iterator<Item = u8>) -> Result<RgbImage, ImageError> {
157 Ok(rgb_encode_to_canvas(Canvas::from_image(img), data)?.img)
158 }
159
160 fn make_png(dim_x: u32, dim_y: u32, scale: u32, data: &mut dyn Iterator<Item = u8>) -> Result<Cursor<Vec<u8>>, ImageError> {
161 // Image must not be larger than 1 megapixel
162 if dim_x * dim_y * scale > 1048576 {
163 return Err(ImageError::dimension_error())
164 }
165
166 let img: RgbImage = rgb_encode(ImageBuffer::new(dim_x, dim_y), data)?;
167
168 let tdim_x = dim_x * scale;
169 let tdim_y = dim_y * scale;
170 let img = resize(&img, tdim_x, tdim_y, FilterType::Nearest);
171 let mut cursor = Cursor::new(Vec::new());
172 to_imageresult(write_buffer_with_format(&mut cursor, &img, tdim_x, tdim_y, ColorType::Rgb8, Png))?;
173 Ok(cursor)
174 }
175
176 #[get("/gen/0/{data}")]
177 async fn img_gen0(req: HttpRequest) -> Result<impl Responder> {
178 let data = req.uri().path().split("/").skip(3).next().unwrap();
179 let cursor = make_png(32, 32, 16, percent_decode_str(&data).into_iter().borrow_mut())?;
180 Ok(HttpResponse::build(StatusCode::OK)
181 .content_type("image/png")
182 .body(cursor.into_inner()))
183 }
184
185 #[get("/gen/1/{dim_x}/{dim_y}/{scale}/{data}")]
186 async fn img_gen1(req: HttpRequest, path: web::Path<(u32, u32, u32)>) -> Result<impl Responder> {
187 let (dim_x, dim_y, scale) = path.into_inner();
188 let data = req.uri().path().split("/").skip(6).next().unwrap();
189 let cursor = make_png(dim_x, dim_y, scale, percent_decode_str(&data).into_iter().borrow_mut())?;
190 Ok(HttpResponse::build(StatusCode::OK)
191 .content_type("image/png")
192 .body(cursor.into_inner()))
193 }
194
195 #[get("/pgen/0/{data}")]
196 async fn img_pgen0(req: HttpRequest, canvas0: web::Data<Arc<Mutex<Option<Canvas>>>>) -> Result<impl Responder> {
197 let data = req.uri().path().split("/").skip(3).next().unwrap();
198 let mut cursor = Cursor::new(Vec::new());
199 let scale = 16;
200 let img: RgbImage = {
201 let canvas_option = &mut *canvas0.lock().unwrap();
202 let canvas = canvas_option.take().ok_or(CanvasError::NotExists)?;
203 let canvas = rgb_encode_to_canvas(canvas, percent_decode_str(&data).into_iter().borrow_mut())?;
204 canvas_option.insert(canvas).img.clone()
205 };
206 let (tdim_x, tdim_y) = img.dimensions();
207 let (tdim_x, tdim_y) = (tdim_x * scale, tdim_y * scale);
208 let img = resize(&img, tdim_x, tdim_y, FilterType::Nearest);
209 to_imageresult(write_buffer_with_format(&mut cursor, &img, tdim_x, tdim_y, ColorType::Rgb8, Png))?;
210 Ok(HttpResponse::build(StatusCode::OK)
211 .content_type("image/png")
212 .body(cursor.into_inner()))
213 }
214
215 #[get("/pgen/1/{dim_x}/{dim_y}/{scale}/{data}")]
216 async fn img_pgen1(req: HttpRequest, path: web::Path<(u32, u32, u32)>, canvas1: web::Data<Arc<Mutex<Option<Canvas>>>>) -> Result<impl Responder> {
217 let (dim_x, dim_y, scale) = path.into_inner();
218 let data = req.uri().path().split("/").skip(6).next().unwrap();
219 let img: RgbImage = {
220 let canvas_option = &mut *canvas1.lock().unwrap();
221 let canvas = canvas_option.take().unwrap_or_else( || Canvas::new(dim_x, dim_y) );
222 if (dim_x, dim_y) == canvas.img.dimensions() {
223 let canvas = rgb_encode_to_canvas(canvas, percent_decode_str(&data).into_iter().borrow_mut())?;
224 canvas_option.insert(canvas).img.clone()
225 }
226 else {
227 canvas_option.insert(canvas);
228 drop(canvas_option);
229 return Err(CanvasError::DimensionMismatch)?
230 }
231 };
232 let (tdim_x, tdim_y) = img.dimensions();
233 let (tdim_x, tdim_y) = (tdim_x * scale, tdim_y * scale);
234 let img = resize(&img, tdim_x, tdim_y, FilterType::Nearest);
235 let mut cursor = Cursor::new(Vec::new());
236 to_imageresult(write_buffer_with_format(&mut cursor, &img, tdim_x, tdim_y, ColorType::Rgb8, Png))?;
237 Ok(HttpResponse::build(StatusCode::OK)
238 .content_type("image/png")
239 .body(cursor.into_inner()))
240 }
241
242
243 #[actix_web::main] // or #[tokio::main]
244 async fn main() -> std::io::Result<()> {
245 env_logger::init();
246 let canvas0 = Arc::new(Mutex::new(Some(Canvas::new(32, 32))));
247 let canvas1 = Arc::new(Mutex::new(None as Option<Canvas>));
248 HttpServer::new(move || {
249 App::new()
250 .service(greet)
251 .service(img_gen0)
252 .service(img_gen1)
253 .service(img_pgen0)
254 .service(img_pgen1)
255 .app_data(web::Data::new(canvas0.clone()))
256 .app_data(web::Data::new(canvas1.clone()))
257 })
258 .bind(("127.0.0.1", 8080))?
259 .run()
260 .await
261 }