4 use std
::borrow
::BorrowMut
;
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
, Rgb
, RgbImage
, write_buffer_with_format
};
12 use image
::ImageOutputFormat
::Png
;
13 use image
::imageops
::{FilterType
, resize
};
14 use image
::error
::{LimitError
, LimitErrorKind
};
18 struct ImageError(image
::ImageError
);
21 fn dimension_error() -> ImageError
{
22 ImageError(image
::ImageError
::Limits(LimitError
::from_kind(LimitErrorKind
::DimensionError
)))
26 impl Display
for ImageError
{
27 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
28 write
!(f
, "{}", &self.0)
32 impl ResponseError
for ImageError
{
33 fn status_code(&self) -> StatusCode
{
34 StatusCode
::INTERNAL_SERVER_ERROR
37 fn error_response(&self) -> HttpResponse
<BoxBody
> {
38 HttpResponse
::InternalServerError().body(format
!("{}\n", &self))
42 struct ToRgbIter
<'a
, T
> where T
: AsPrimitive
<u8> {
43 iter
: Box
<&'a
mut dyn Iterator
<Item
= T
>>
46 impl<'a
, T
: AsPrimitive
<u8>> ToRgbIter
<'a
, T
> {
47 fn new(data
: &'a
mut (dyn Iterator
<Item
= T
>)) -> ToRgbIter
<T
> {
48 ToRgbIter
{ iter
: Box
::new(data
) }
52 impl<T
: AsPrimitive
<u8> + Zero
> Iterator
for ToRgbIter
<'_
, T
> {
54 fn next(&mut self) -> Option
<Rgb
<u8>> {
55 if let Some(r
) = self.iter
.next() {
56 let g
= self.iter
.next().unwrap
_or
(T
::zero());
57 let b
= self.iter
.next().unwrap
_or
(T
::zero());
58 Some(Rgb([r
.as_(), g
.as_(), b
.as_()]))
66 fn to_imageresult
<T
> (result
: Result
<T
, image
::ImageError
>) -> Result
<T
, ImageError
> {
69 Err(e
) => Err(ImageError(e
))
73 #[get("/hello/{name}")]
74 async
fn greet(name
: web
::Path
<String
>, req
: HttpRequest
) -> impl Responder
{
75 println
!("{:?}", req
);
76 format
!("Hello {name}!")
79 fn make_png(dim_x
: u32, dim_y
: u32, scale
: u32, data
: &mut dyn Iterator
<Item
= u8>) -> Result
<Cursor
<Vec
<u8>>, ImageError
> {
80 // Image must not be larger than 1 megapixel
81 if dim_x
* dim_y
* scale
> 1048576 {
82 return Err(ImageError
::dimension_error())
85 let mut img
: RgbImage
= ImageBuffer
::new(dim_x
, dim_y
);
86 let mut pixels
= img
.pixels_mut();
88 for sp
in ToRgbIter
::new(data
) {
89 let mut dp
= pixels
.next().ok_or(ImageError
::dimension_error())?
;
94 let tdim_x
= dim_x
* scale
;
95 let tdim_y
= dim_y
* scale
;
96 let img
= resize(&img
, tdim_x
, tdim_y
, FilterType
::Nearest
);
97 let mut cursor
= Cursor
::new(Vec
::new());
98 to_imageresult(write_buffer_with_format(&mut cursor
, &img
, tdim_x
, tdim_y
, ColorType
::Rgb8
, Png
))?
;
102 #[get("/gen/0/{data}")]
103 async
fn img_gen0(req
: HttpRequest
) -> Result
<impl Responder
> {
104 let data
= req
.ur
i().path().split("/").skip(3).next().unwrap
();
105 let cursor
= make_png(32, 32, 16, percent_decode_str(&data
).into
_iter
().borrow_mut())?
;
106 Ok(HttpResponse
::build(StatusCode
::OK
)
107 .content_type("image/png")
108 .body(cursor
.into
_inner
()))
111 #[get("/gen/1/{dim_x}/{dim_y}/{scale}/{data}")]
112 async
fn img_gen1(req
: HttpRequest
, path
: web
::Path
<(u32, u32, u32)>) -> Result
<impl Responder
> {
113 let (dim_x
, dim_y
, scale
) = path
.into
_inner
();
114 let data
= req
.ur
i().path().split("/").skip(6).next().unwrap
();
115 let cursor
= make_png(dim_x
, dim_y
, scale
, percent_decode_str(&data
).into
_iter
().borrow_mut())?
;
116 Ok(HttpResponse
::build(StatusCode
::OK
)
117 .content_type("image/png")
118 .body(cursor
.into
_inner
()))
121 #[actix_web::main] // or #[tokio::main]
122 async
fn main() -> std
::io
::Result
<()> {
129 .bind(("127.0.0.1", 8080))?