8653ec3d9da8d766f2c7ae43a6088fa52a9cfe2d
1 use std
::convert
::From
;
5 use std
::borrow
::BorrowMut
;
6 use std
::sync
::{Arc
, Mutex
};
8 use num_traits
::cast
::AsPrimitive
;
9 use percent_encoding
::percent_decode_str
;
10 use actix_web
::{get
, web
, App
, HttpServer
, HttpRequest
, HttpResponse
, Responder
, ResponseError
, Result
};
11 use actix_web
::body
::BoxBody
;
12 use actix_web
::http
::StatusCode
;
13 use image
::{ImageBuffer
, ColorType
, Rgb
, RgbImage
, write_buffer_with_format
};
14 use image
::ImageOutputFormat
::Png
;
15 use image
::imageops
::{FilterType
, overlay
, resize
};
16 use image
::error
::{LimitError
, LimitErrorKind
};
17 use actix_web
::cookie
::time
::OffsetDateTime
;
21 struct ImageError(image
::ImageError
);
24 fn dimension_error() -> ImageError
{
25 ImageError(image
::ImageError
::Limits(LimitError
::from_kind(LimitErrorKind
::DimensionError
)))
29 impl Display
for ImageError
{
30 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
31 write
!(f
, "{}", &self.0)
35 impl ResponseError
for ImageError
{
36 fn status_code(&self) -> StatusCode
{
37 StatusCode
::INTERNAL_SERVER_ERROR
40 fn error_response(&self) -> HttpResponse
<BoxBody
> {
41 HttpResponse
::InternalServerError().body(format
!("{}\n", &self))
48 DimensionMismatch((u32, u32)),
49 ImageError(ImageError
)
52 impl Display
for CanvasError
{
53 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
54 write
!(f
, "Canvas Error: see error_response() for more details")
58 impl ResponseError
for CanvasError
{
59 fn status_code(&self) -> StatusCode
{
61 CanvasError
::NotExists
=> StatusCode
::NOT_FOUND
,
62 CanvasError
::DimensionMismatch(_
) => StatusCode
::BAD_REQUEST
,
63 CanvasError
::ImageError(e
) => e
.status_code()
67 fn error_response(&self) -> HttpResponse
<BoxBody
> {
69 CanvasError
::NotExists
=> HttpResponse
::NotFound().body("Canvas does not exist."),
70 CanvasError
::DimensionMismatch(dim
) =>
71 HttpResponse
::BadRequest().body(format
!("Supplied dimensions don't match the dimensions of existing canvas {:?}.", dim
)),
72 CanvasError
::ImageError(e
) => e
.error_response()
77 impl From
<ImageError
> for CanvasError
{
78 fn from(e
: ImageError
) -> CanvasError
{
79 CanvasError
::ImageError(e
)
83 struct ToRgbIter
<'a
, T
> where T
: AsPrimitive
<u8> {
84 iter
: Box
<&'a
mut dyn Iterator
<Item
= T
>>
87 impl<'a
, T
: AsPrimitive
<u8>> ToRgbIter
<'a
, T
> {
88 fn new(data
: &'a
mut (dyn Iterator
<Item
= T
>)) -> ToRgbIter
<T
> {
89 ToRgbIter
{ iter
: Box
::new(data
) }
93 impl<T
: AsPrimitive
<u8> + Zero
> Iterator
for ToRgbIter
<'_
, T
> {
95 fn next(&mut self) -> Option
<Rgb
<u8>> {
96 if let Some(r
) = self.iter
.next() {
97 let g
= self.iter
.next().unwrap
_or
(T
::zero());
98 let b
= self.iter
.next().unwrap
_or
(T
::zero());
99 Some(Rgb([r
.as_(), g
.as_(), b
.as_()]))
107 fn to_imageresult
<T
> (result
: Result
<T
, image
::ImageError
>) -> Result
<T
, ImageError
> {
110 Err(e
) => Err(ImageError(e
))
114 fn to_canvasresult
<T
: std
::fmt
::Debug
> (result
: Result
<T
, ImageError
>) -> Result
<T
, CanvasError
> {
117 Err(e
) => Err(CanvasError
::ImageError(e
))
128 fn new(width
: u32, height
: u32) -> Canvas
{
129 Canvas
{ pen
: 0, img
: ImageBuffer
::new(width
, height
) }
132 fn from_image(img
: RgbImage
) -> Canvas
{
133 Canvas
{ pen
: 0, img
}
136 /*fn replace(&self, img: RgbImage, pen: usize) -> Canvas {
142 fn advance_pen(mut self, offset
: usize) -> Canvas
{
143 //Canvas { pen: self.pen + offset, img: self.img }
145 self.pen
%= self.img
.width() as usize * self.img
.height() as usize;
150 struct Canvas0(Option
<Canvas
>);
151 struct Canvas1(Option
<Canvas
>);
153 #[get("/hello/{name}")]
154 async
fn greet(name
: web
::Path
<String
>, req
: HttpRequest
) -> impl Responder
{
155 println
!("{:?}", req
);
156 format
!("Hello {name}!\n{:?}", req
.headers().get("user-agent"))
159 fn rgb_encode_to_canvas(mut canvas
: Canvas
, data
: &mut dyn Iterator
<Item
= u8>) -> Result
<Canvas
, ImageError
> {
160 let mut pixels
= canvas
.img
.pixels_mut().skip(canvas
.pen
);
163 for sp
in ToRgbIter
::new(data
) {
164 let mut dp
= match pixels
.next() {
165 Some(pixel
) => pixel
,
167 pixels
= canvas
.img
.pixels_mut().skip(0);
168 pixels
.next().ok_or(ImageError
::dimension_error())?
175 Ok(canvas
.advance_pen(counter
))
178 fn rgb_encode(img
: RgbImage
, data
: &mut dyn Iterator
<Item
= u8>) -> Result
<RgbImage
, ImageError
> {
179 Ok(rgb_encode_to_canvas(Canvas
::from_image(img
), data
)?
.img
)
182 fn apply_to_canvas
<'a
, F
>(f
: F
, canvas_option
: &'a
mut Option
<Canvas
>, dimensions
: Option
<(u32, u32)>, data
: &str) -> Result
<&'a
mut Canvas
, CanvasError
>
183 where F
: Fn(Canvas
, &mut dyn Iterator
<Item
= u8>) -> Result
<Canvas
, CanvasError
>
185 let canvas
= match canvas_option
.take() {
186 Some(canvas
) => match dimensions
{
187 Some(dimensions
) => {
188 if dimensions
== canvas
.img
.dimensions() {
192 Err(CanvasError
::DimensionMismatch(canvas_option
.insert
(canvas
).img
.dimensions()))
198 None
=> match dimensions
{
199 Some((dim_x
, dim_y
)) =>
200 Ok(Canvas
::new(dim_x
, dim_y
)),
202 Err(CanvasError
::NotExists
)
206 Ok(canvas_option
.insert
(f(canvas
, percent_decode_str(&data
).into
_iter
().borrow_mut())?
))
209 fn make_png(dim_x
: u32, dim_y
: u32, scale
: u32, data
: &mut dyn Iterator
<Item
= u8>) -> Result
<Cursor
<Vec
<u8>>, ImageError
> {
210 // Image must not be larger than 1 megapixel
211 if dim_x
* dim_y
* scale
> 1048576 {
212 return Err(ImageError
::dimension_error())
215 let img
: RgbImage
= rgb_encode(ImageBuffer
::new(dim_x
, dim_y
), data
)?
;
216 let cursor
= image_to_cursor(img
, scale
)?
;
220 fn image_to_cursor(img
: RgbImage
, scale
: u32) -> Result
<Cursor
<Vec
<u8>>, ImageError
> {
221 let (dim_x
, dim_y
) = img
.dimensions();
222 let (dim_x
, dim_y
) = (dim_x
* scale
, dim_y
* scale
);
224 // Image must not be larger than 1 megapixel
225 if dim_x
* dim_y
> 1048576 {
226 return Err(ImageError
::dimension_error())
229 let mut cursor
= Cursor
::new(Vec
::new());
230 let img
= resize(&img
, dim_x
, dim_y
, FilterType
::Nearest
);
231 to_imageresult(write_buffer_with_format(&mut cursor
, &img
, dim_x
, dim_y
, ColorType
::Rgb8
, Png
))?
;
236 #[get("/gen/0/{data}")]
237 async
fn img_gen0(req
: HttpRequest
) -> Result
<impl Responder
> {
238 let data
= req
.ur
i().path().split("/").skip(3).next().unwrap
();
239 let cursor
= make_png(32, 32, 16, percent_decode_str(&data
).into
_iter
().borrow_mut())?
;
240 Ok(HttpResponse
::build(StatusCode
::OK
)
241 .content_type("image/png")
242 .body(cursor
.into
_inner
()))
245 #[get("/gen/1/{dim_x}/{dim_y}/{scale}/{data}")]
246 async
fn img_gen1(req
: HttpRequest
, path
: web
::Path
<(u32, u32, u32)>) -> Result
<impl Responder
> {
247 let (dim_x
, dim_y
, scale
) = path
.into
_inner
();
248 let data
= req
.ur
i().path().split("/").skip(6).next().unwrap
();
249 let cursor
= make_png(dim_x
, dim_y
, scale
, percent_decode_str(&data
).into
_iter
().borrow_mut())?
;
250 Ok(HttpResponse
::build(StatusCode
::OK
)
251 .content_type("image/png")
252 .body(cursor
.into
_inner
()))
255 #[get("/gen/2/{dim_x}/{dim_y}/{scale}")]
256 async
fn img_gen2(req
: HttpRequest
, path
: web
::Path
<(u32, u32, u32)>) -> Result
<impl Responder
> {
257 let (dim_x
, dim_y
, scale
) = path
.into
_inner
();
258 let data
= match req
.headers().get("user-agent") {
259 Some(header
) => header
.to_str().unwrap
_or
(""),
262 let cursor
= make_png(dim_x
, dim_y
, scale
, &mut data
.bytes())?
;
263 Ok(HttpResponse
::build(StatusCode
::OK
)
264 .content_type("image/png")
265 .body(cursor
.into
_inner
()))
268 #[get("/gen/3/{scale}")]
269 async
fn img_gen3(req
: HttpRequest
, path
: web
::Path
<u32>) -> Result
<impl Responder
> {
270 let scale
= path
.into
_inner
();
271 let data
= match req
.headers().get("user-agent") {
272 Some(header
) => header
.to_str().unwrap
_or
(""),
275 let rgbimg
: RgbImage
= rgb_encode(ImageBuffer
::new(6, 6), &mut data
.bytes())?
;
276 let mut resimg
: RgbImage
= ImageBuffer
::new(60 * 6, 24 * 6);
277 let time
= OffsetDateTime
::now_utc().time();
278 overlay(&mut resimg
, &rgbimg
, (time
.minute() * 6).into
(), (time
.hour() * 6).into
());
279 let cursor
= image_to_cursor(resimg
, scale
)?
;
280 Ok(HttpResponse
::build(StatusCode
::OK
)
281 .content_type("image/png")
282 .body(cursor
.into
_inner
()))
285 #[get("/pgen/0/{data}")]
286 async
fn img_pgen0(req
: HttpRequest
, canvas0
: web
::Data
<Arc
<Mutex
<Canvas0
>>>) -> Result
<impl Responder
> {
287 let data
= req
.ur
i().path().split("/").skip(3).next().unwrap
();
289 let img
: RgbImage
= ({
290 let canvas_option
= &mut *canvas0
.lock().unwrap
();
291 Ok(apply_to_canvas( |c
, d
| to_canvasresult(rgb_encode_to_canvas(c
, d
)), &mut canvas_option
.0, None
, data
)?
.img
.clone())
292 } as Result
<RgbImage
, CanvasError
>)?
;
293 let cursor
= image_to_cursor(img
, scale
)?
;
294 Ok(HttpResponse
::build(StatusCode
::OK
)
295 .content_type("image/png")
296 .body(cursor
.into
_inner
()))
299 #[get("/pgen/1/{dim_x}/{dim_y}/{scale}/{data}")]
300 async
fn img_pgen1(req
: HttpRequest
, path
: web
::Path
<(u32, u32, u32)>, canvas1
: web
::Data
<Arc
<Mutex
<Canvas1
>>>) -> Result
<impl Responder
> {
301 let (dim_x
, dim_y
, scale
) = path
.into
_inner
();
302 let data
= req
.ur
i().path().split("/").skip(6).next().unwrap
();
303 let img
: RgbImage
= ({
304 let canvas_option
= &mut *canvas1
.lock().unwrap
();
305 Ok(apply_to_canvas( |c
, d
| to_canvasresult(rgb_encode_to_canvas(c
, d
)), &mut canvas_option
.0, Some((dim_x
, dim_y
)), data
)?
.img
.clone())
306 } as Result
<RgbImage
, CanvasError
>)?
;
307 let cursor
= image_to_cursor(img
, scale
)?
;
308 Ok(HttpResponse
::build(StatusCode
::OK
)
309 .content_type("image/png")
310 .body(cursor
.into
_inner
()))
313 #[get("/pgen/2/{dim_x}/{dim_y}/{scale}")]
314 async
fn img_pgen2(req
: HttpRequest
, path
: web
::Path
<(u32, u32, u32)>, canvas1
: web
::Data
<Arc
<Mutex
<Canvas1
>>>) -> Result
<impl Responder
> {
315 let (dim_x
, dim_y
, scale
) = path
.into
_inner
();
316 let data
= match req
.headers().get("user-agent") {
317 Some(header
) => header
.to_str().unwrap
_or
(""),
320 let img
: RgbImage
= ({
321 let canvas_option
= &mut *canvas1
.lock().unwrap
();
322 Ok(apply_to_canvas( |c
, d
| to_canvasresult(rgb_encode_to_canvas(c
, d
)), &mut canvas_option
.0, Some((dim_x
, dim_y
)), data
)?
.img
.clone())
323 } as Result
<RgbImage
, CanvasError
>)?
;
324 let cursor
= image_to_cursor(img
, scale
)?
;
325 Ok(HttpResponse
::build(StatusCode
::OK
)
326 .content_type("image/png")
327 .body(cursor
.into
_inner
()))
330 #[get("/pdrop/{canvas_id}")]
331 async
fn pdrop(canvas_id
: web
::Path
<u8>, canvas0
: web
::Data
<Arc
<Mutex
<Canvas0
>>>, canvas1
: web
::Data
<Arc
<Mutex
<Canvas1
>>>) -> Result
<impl Responder
> {
332 let canvas_id
= canvas_id
.into
_inner
();
334 0 => canvas0
.lock().unwrap
().0.take(),
335 1 => canvas1
.lock().unwrap
().0.take(),
337 }.ok_or_else( || CanvasError
::NotExists
)?
;
338 Ok(HttpResponse
::build(StatusCode
::OK
)
339 .body(format
!("Canvas {} dropped.", canvas_id
)))
343 #[actix_web::main] // or #[tokio::main]
344 async
fn main() -> std
::io
::Result
<()> {
346 let canvas0
= Arc
::new(Mutex
::new(Canvas0(Some(Canvas
::new(32, 32)))));
347 let canvas1
= Arc
::new(Mutex
::new(Canvas1(None
)));
348 HttpServer
::new(move || {
359 .app_data(web
::Data
::new(canvas0
.clone()))
360 .app_data(web
::Data
::new(canvas1
.clone()))
362 .bind(("127.0.0.1", 8080))?