b9b9dfe95db8994ed84fe8c6bb96c7c13b554690
1 use std
::convert
::From
;
6 use std
::borrow
::BorrowMut
;
7 use std
::sync
::{Arc
, Mutex
};
8 use log
::{debug
, warn
, LevelFilter
};
10 use num_traits
::cast
::AsPrimitive
;
11 use percent_encoding
::percent_decode_str
;
12 use actix_web
::{middleware
::Logger
, get
, web
, App
, HttpServer
, HttpRequest
, HttpResponse
, Responder
, ResponseError
, Result
};
13 use actix_web
::body
::BoxBody
;
14 use actix_web
::http
::StatusCode
;
15 use actix_web
::cookie
::time
::OffsetDateTime
;
16 use image
::{ImageBuffer
, ColorType
, Rgb
, RgbImage
, write_buffer_with_format
};
17 use image
::ImageOutputFormat
::Png
;
18 use image
::imageops
::{FilterType
, overlay
, resize
};
19 use image
::error
::{LimitError
, LimitErrorKind
};
22 const TIMEAVATAR_SIZE_U32
: u32 = 6;
23 const TIMEAVATAR_SIZE_I64
: i64 = TIMEAVATAR_SIZE_U32
as i64;
25 macro_rules
! response_image
{
27 HttpResponse
::build(StatusCode
::OK
)
28 .content_type("image/png")
29 .body($cursor
.into
_inner
())
33 macro_rules
! extract_data
{
34 ($req
:expr
,$n
:expr
) => {
35 $req
.ur
i().path().split('
/'
).nth($n
).unwrap
()
39 macro_rules
! extract_header
{
40 ($req
:expr
,$header
:expr
) => {
41 match $req
.headers().get($header
) {
42 Some(header
) => header
.to_str().unwrap
_or
(""),
49 struct ImageError(image
::ImageError
);
52 fn dimension_error() -> ImageError
{
53 ImageError(image
::ImageError
::Limits(LimitError
::from_kind(LimitErrorKind
::DimensionError
)))
57 impl Display
for ImageError
{
58 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
59 write
!(f
, "{}", &self.0)
63 impl ResponseError
for ImageError
{
64 fn status_code(&self) -> StatusCode
{
65 StatusCode
::INTERNAL_SERVER_ERROR
68 fn error_response(&self) -> HttpResponse
<BoxBody
> {
69 HttpResponse
::InternalServerError().body(format
!("{}\n", &self))
73 impl From
<image
::ImageError
> for ImageError
{
74 fn from(e
: image
::ImageError
) -> Self {
82 DimensionMismatch((u32, u32)),
83 ImageError(ImageError
)
86 impl Display
for CanvasError
{
87 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
88 write
!(f
, "Canvas Error: see error_response() for more details")
92 impl ResponseError
for CanvasError
{
93 fn status_code(&self) -> StatusCode
{
95 CanvasError
::NotExists
=> StatusCode
::NOT_FOUND
,
96 CanvasError
::DimensionMismatch(_
) => StatusCode
::BAD_REQUEST
,
97 CanvasError
::ImageError(e
) => e
.status_code()
101 fn error_response(&self) -> HttpResponse
<BoxBody
> {
103 CanvasError
::NotExists
=> HttpResponse
::NotFound().body("Canvas does not exist."),
104 CanvasError
::DimensionMismatch(dim
) =>
105 HttpResponse
::BadRequest().body(format
!("Supplied dimensions don't match the dimensions of existing canvas {:?}.", dim
)),
106 CanvasError
::ImageError(e
) => e
.error_response()
111 impl From
<ImageError
> for CanvasError
{
112 fn from(e
: ImageError
) -> CanvasError
{
113 CanvasError
::ImageError(e
)
117 struct ToRgbIter
<'a
, T
> where T
: AsPrimitive
<u8> {
118 iter
: &'a
mut dyn Iterator
<Item
= T
>
121 impl<'a
, T
: AsPrimitive
<u8>> ToRgbIter
<'a
, T
> {
122 fn new(iter
: &'a
mut (dyn Iterator
<Item
= T
>)) -> ToRgbIter
<T
> {
127 impl<T
: AsPrimitive
<u8> + Zero
> Iterator
for ToRgbIter
<'_
, T
> {
129 fn next(&mut self) -> Option
<Rgb
<u8>> {
130 if let Some(r
) = self.iter
.next() {
131 let g
= self.iter
.next().unwrap
_or
_else
(T
::zero
);
132 let b
= self.iter
.next().unwrap
_or
_else
(T
::zero
);
133 Some(Rgb([r
.as_(), g
.as_(), b
.as_()]))
148 fn new(width
: u32, height
: u32) -> Canvas
{
149 Canvas
{ pen
: 0, img
: ImageBuffer
::new(width
, height
) }
152 /*fn from_image(img: RgbImage) -> Canvas {
153 Canvas { pen: 0, img }
156 fn replace(&self, img: RgbImage, pen: usize) -> Canvas {
162 fn advance_pen(mut self, offset
: usize) -> Canvas
{
163 //Canvas { pen: self.pen + offset, img: self.img }
165 self.pen
%= self.img
.width() as usize * self.img
.height() as usize;
170 struct Canvas0(Option
<Canvas
>);
171 struct Canvas1(Option
<Canvas
>);
172 struct Canvas2(Option
<Canvas
>);
174 #[get("/hello/{name}")]
175 async
fn greet(name
: web
::Path
<String
>, req
: HttpRequest
) -> impl Responder
{
176 println
!("{:?}", req
);
177 format
!("Hello {name}!\n{:?}", req
.headers().get("user-agent"))
180 fn rgb_encode_with_pen(img
: &mut RgbImage
, pen
: usize, data
: &mut dyn Iterator
<Item
= u8>) -> Result
<usize, ImageError
> {
181 let mut pixels
= img
.pixels_mut().skip(pen
);
184 for sp
in ToRgbIter
::new(data
) {
185 let mut dp
= match pixels
.next() {
186 Some(pixel
) => pixel
,
188 pixels
= img
.pixels_mut().skip(0);
189 pixels
.next().ok_or_else(ImageError
::dimension_error
)?
199 fn rgb_encode_to_canvas(mut canvas
: Canvas
, data
: &mut dyn Iterator
<Item
= u8>) -> Result
<Canvas
, CanvasError
> {
200 let counter
= rgb_encode_with_pen(&mut canvas
.img
, canvas
.pen
, data
)?
;
201 Ok(canvas
.advance_pen(counter
))
204 fn rgb_encode(mut img
: RgbImage
, data
: &mut dyn Iterator
<Item
= u8>) -> Result
<RgbImage
, ImageError
> {
205 rgb_encode_with_pen(&mut img
, 0, data
)?
;
209 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
>
210 where F
: Fn(Canvas
, &mut dyn Iterator
<Item
= u8>) -> Result
<Canvas
, CanvasError
>
212 let canvas
= match canvas_option
.take() {
213 Some(canvas
) => match dimensions
{
214 Some(dimensions
) => {
215 if dimensions
== canvas
.img
.dimensions() {
219 Err(CanvasError
::DimensionMismatch(canvas_option
.insert
(canvas
).img
.dimensions()))
225 None
=> match dimensions
{
226 Some((dim_x
, dim_y
)) =>
227 Ok(Canvas
::new(dim_x
, dim_y
)),
229 Err(CanvasError
::NotExists
)
233 Ok(canvas_option
.insert
(f(canvas
, percent_decode_str(data
).into
_iter
().borrow_mut())?
))
236 fn make_png(dim_x
: u32, dim_y
: u32, scale
: u32, data
: &mut dyn Iterator
<Item
= u8>) -> Result
<Cursor
<Vec
<u8>>, ImageError
> {
237 // Image must not be larger than 1 megapixel
238 if dim_x
* dim_y
* scale
> 1048576 {
239 return Err(ImageError
::dimension_error())
242 let img
: RgbImage
= rgb_encode(ImageBuffer
::new(dim_x
, dim_y
), data
)?
;
243 let cursor
= image_to_cursor(img
, scale
)?
;
247 fn image_to_cursor(img
: RgbImage
, scale
: u32) -> Result
<Cursor
<Vec
<u8>>, ImageError
> {
248 let (dim_x
, dim_y
) = img
.dimensions();
249 let (dim_x
, dim_y
) = (dim_x
* scale
, dim_y
* scale
);
251 // Image must not be larger than 1 megapixel
252 if dim_x
* dim_y
> 1048576 {
253 return Err(ImageError
::dimension_error())
256 let mut cursor
= Cursor
::new(Vec
::new());
257 let img
= resize(&img
, dim_x
, dim_y
, FilterType
::Nearest
);
258 write_buffer_with_format(&mut cursor
, &img
, dim_x
, dim_y
, ColorType
::Rgb8
, Png
)?
;
263 #[get("/gen/0/{data}")]
264 async
fn img_gen0(req
: HttpRequest
) -> Result
<impl Responder
> {
265 let data
= extract_data
!(req
, 3);
266 let cursor
= make_png(32, 32, 16, percent_decode_str(data
).into
_iter
().borrow_mut())?
;
267 Ok(response_image
!(cursor
))
270 #[get("/gen/1/{dim_x}/{dim_y}/{scale}/{data}")]
271 async
fn img_gen1(req
: HttpRequest
, path
: web
::Path
<(u32, u32, u32)>) -> Result
<impl Responder
> {
272 let (dim_x
, dim_y
, scale
) = path
.into
_inner
();
273 let data
= extract_data
!(req
, 6);
274 let cursor
= make_png(dim_x
, dim_y
, scale
, percent_decode_str(data
).into
_iter
().borrow_mut())?
;
275 Ok(response_image
!(cursor
))
278 #[get("/gen/2/{dim_x}/{dim_y}/{scale}")]
279 async
fn img_gen2(req
: HttpRequest
, path
: web
::Path
<(u32, u32, u32)>) -> Result
<impl Responder
> {
280 let (dim_x
, dim_y
, scale
) = path
.into
_inner
();
281 let data
= extract_header
!(req
, "user-agent");
282 let cursor
= make_png(dim_x
, dim_y
, scale
, &mut data
.bytes())?
;
283 Ok(response_image
!(cursor
))
286 #[get("/gen/3/{scale}")]
287 async
fn img_gen3(req
: HttpRequest
, path
: web
::Path
<u32>) -> Result
<impl Responder
> {
288 let scale
= path
.into
_inner
();
289 let data
= extract_header
!(req
, "user-agent");
290 let rgbimg
: RgbImage
= rgb_encode(ImageBuffer
::new(TIMEAVATAR_SIZE_U32
, TIMEAVATAR_SIZE_U32
), &mut data
.bytes())?
;
291 let mut resimg
: RgbImage
= ImageBuffer
::new(60 * TIMEAVATAR_SIZE_U32
, 24 * TIMEAVATAR_SIZE_U32
);
292 let time
= OffsetDateTime
::now_utc().time();
293 let (hour
, minute
): (i64, i64) = (time
.hour().into
(), time
.minute().into
());
294 overlay(&mut resimg
, &rgbimg
, minute
* TIMEAVATAR_SIZE_I64
, hour
* TIMEAVATAR_SIZE_I64
);
295 let cursor
= image_to_cursor(resimg
, scale
)?
;
296 Ok(response_image
!(cursor
))
299 #[get("/gen/4/{scale}")]
300 async
fn img_gen4(req
: HttpRequest
, path
: web
::Path
<u32>) -> Result
<impl Responder
> {
301 let scale
= path
.into
_inner
();
302 let signal
= &mut extract_header
!(req
, "user-agent").bytes();
303 let mut resimg
: RgbImage
= ImageBuffer
::new(1600, 600);
304 let mut x
: f32 = 0.0;
305 let max_x
: f32 = (resimg
.width() - 1) as f32;
306 //let max_y: f32 = (resimg.height() - 1) as f32;
308 let amplitude
: f32 = 200.0;
309 let wave_offset
: f32 = (resimg
.height() / 2) as f32 + amplitude
/ 2.0 - 1.0;
310 let freq_base
: f32 = 50.0;
313 let s
= signal
.next().unwrap
_or
(0);
314 warn
!("Next char: {:>1} {:#02X}", s
as char, s
);
315 while ((x
- px
) < run
) && (x
< max_x
) {
317 (x
/ (freq_base
/ s
as f32)).sin() * amplitude
+ wave_offset
319 else { wave_offset
};
322 resimg
.put_pixel(x
as u32, y
as u32, Rgb([255, 255, 255]));
323 resimg
.put_pixel(x
as u32, s
.into
(), Rgb([255, 0, 0]));
326 let cursor
= image_to_cursor(resimg
, scale
)?
;
327 Ok(response_image
!(cursor
))
330 #[get("/pgen/0/{data}")]
331 async
fn img_pgen0(req
: HttpRequest
, canvas0
: web
::Data
<Arc
<Mutex
<Canvas0
>>>) -> Result
<impl Responder
> {
332 let data
= extract_data
!(req
, 3);
334 let img
: RgbImage
= ({
335 let canvas_option
= &mut *canvas0
.lock().unwrap
();
336 Ok(apply_to_canvas( |c
, d
| rgb_encode_to_canvas(c
, d
), &mut canvas_option
.0, None
, data
)?
.img
.clone())
337 } as Result
<RgbImage
, CanvasError
>)?
;
338 let cursor
= image_to_cursor(img
, scale
)?
;
339 Ok(response_image
!(cursor
))
342 #[get("/pgen/1/{dim_x}/{dim_y}/{scale}/{data}")]
343 async
fn img_pgen1(req
: HttpRequest
, path
: web
::Path
<(u32, u32, u32)>, canvas1
: web
::Data
<Arc
<Mutex
<Canvas1
>>>) -> Result
<impl Responder
> {
344 let (dim_x
, dim_y
, scale
) = path
.into
_inner
();
345 let data
= extract_data
!(req
, 6);
346 let img
: RgbImage
= ({
347 let canvas_option
= &mut *canvas1
.lock().unwrap
();
348 Ok(apply_to_canvas( |c
, d
| rgb_encode_to_canvas(c
, d
), &mut canvas_option
.0, Some((dim_x
, dim_y
)), data
)?
.img
.clone())
349 } as Result
<RgbImage
, CanvasError
>)?
;
350 let cursor
= image_to_cursor(img
, scale
)?
;
351 Ok(response_image
!(cursor
))
354 #[get("/pgen/2/{dim_x}/{dim_y}/{scale}")]
355 async
fn img_pgen2(req
: HttpRequest
, path
: web
::Path
<(u32, u32, u32)>, canvas1
: web
::Data
<Arc
<Mutex
<Canvas1
>>>) -> Result
<impl Responder
> {
356 let (dim_x
, dim_y
, scale
) = path
.into
_inner
();
357 let data
= extract_header
!(req
, "user-agent");
358 let img
: RgbImage
= ({
359 let canvas_option
= &mut *canvas1
.lock().unwrap
();
360 Ok(apply_to_canvas( |c
, d
| rgb_encode_to_canvas(c
, d
), &mut canvas_option
.0, Some((dim_x
, dim_y
)), data
)?
.img
.clone())
361 } as Result
<RgbImage
, CanvasError
>)?
;
362 let cursor
= image_to_cursor(img
, scale
)?
;
363 Ok(response_image
!(cursor
))
366 #[get("/pgen/3/{scale}")]
367 async
fn img_pgen3(req
: HttpRequest
, path
: web
::Path
<u32>, canvas2
: web
::Data
<Arc
<Mutex
<Canvas2
>>>) -> Result
<impl Responder
> {
368 let scale
= path
.into
_inner
();
369 let data
= extract_header
!(req
, "user-agent");
370 let time
= OffsetDateTime
::now_utc().time();
371 let (hour
, minute
): (i64, i64) = (time
.hour().into
(), time
.minute().into
());
372 let rgbimg
: RgbImage
= rgb_encode(ImageBuffer
::new(TIMEAVATAR_SIZE_U32
, TIMEAVATAR_SIZE_U32
), &mut data
.bytes())?
;
373 let resimg
: RgbImage
= ({
374 let canvas_option
= &mut *canvas2
.lock().unwrap
();
375 let mut canvas
= match canvas_option
.0.take() {
376 Some(canvas
) => canvas
,
377 None
=> Canvas
::new(60 * TIMEAVATAR_SIZE_U32
, 24 * TIMEAVATAR_SIZE_U32
)
379 overlay(&mut canvas
.img
, &rgbimg
, minute
* TIMEAVATAR_SIZE_I64
, hour
* TIMEAVATAR_SIZE_I64
);
380 Ok(canvas_option
.0.insert
(canvas
).img
.clone())
381 } as Result
<RgbImage
, CanvasError
>)?
;
382 let cursor
= image_to_cursor(resimg
, scale
)?
;
383 Ok(response_image
!(cursor
))
386 #[get("/pdrop/{canvas_id}")]
387 async
fn pdrop(canvas_id
: web
::Path
<u8>,
388 canvas0
: web
::Data
<Arc
<Mutex
<Canvas0
>>>,
389 canvas1
: web
::Data
<Arc
<Mutex
<Canvas1
>>>,
390 canvas2
: web
::Data
<Arc
<Mutex
<Canvas2
>>>) -> Result
<impl Responder
> {
392 let canvas_id
= canvas_id
.into
_inner
();
394 0 => canvas0
.lock().unwrap
().0.take(),
395 1 => canvas1
.lock().unwrap
().0.take(),
396 2 => canvas2
.lock().unwrap
().0.take(),
398 }.ok_or(CanvasError
::NotExists
)?
;
399 Ok(HttpResponse
::build(StatusCode
::OK
)
400 .body(format
!("Canvas {} dropped.", canvas_id
)))
404 #[actix_web::main] // or #[tokio::main]
405 async
fn main() -> std
::io
::Result
<()> {
406 env_logger
::builder()
407 .filter
_mod
ule
("actix_web", LevelFilter
::Info
)
411 let listenaddress
= env
::args().nth(1).unwrap
_or
_else
(|| "127.0.0.1:8080".to_string());
412 let canvas0
= Arc
::new(Mutex
::new(Canvas0(Some(Canvas
::new(32, 32)))));
413 let canvas1
= Arc
::new(Mutex
::new(Canvas1(None
)));
414 let canvas2
= Arc
::new(Mutex
::new(Canvas2(None
)));
416 HttpServer
::new(move || {
418 .wrap(Logger
::default())
430 .app_data(web
::Data
::new(canvas0
.clone()))
431 .app_data(web
::Data
::new(canvas1
.clone()))
432 .app_data(web
::Data
::new(canvas2
.clone()))
434 .bind(listenaddress
)?