mirror of
https://git.asonix.dog/asonix/pict-rs
synced 2024-12-22 19:31:35 +00:00
Add new resize options, update README with current APIs
This commit is contained in:
parent
9db057fdc5
commit
85f20bbe0e
2 changed files with 136 additions and 7 deletions
23
README.md
23
README.md
|
@ -306,6 +306,19 @@ pict-rs offers the following endpoints:
|
||||||
square using raw pixel sampling
|
square using raw pixel sampling
|
||||||
- `resize={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}` square
|
- `resize={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}` square
|
||||||
using a Lanczos2 filter. This is slower than sampling but looks a bit better in some cases
|
using a Lanczos2 filter. This is slower than sampling but looks a bit better in some cases
|
||||||
|
- `resize={filter}.(a){int}`: produce a thumbnail of the image fitting inside an `{int}` by
|
||||||
|
`{int}` square, or when `(a)` is present, produce a thumbnail whose area is smaller than
|
||||||
|
`{int}`. `{filter}` is optional, and indicates what filter to use when resizing the image.
|
||||||
|
Available filters are `Lanczos`, `Lanczos2`, `LanczosSharp`, `Lanczos2Sharp`, `Mitchell`,
|
||||||
|
and `RobidouxSharp`.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- `resize=300`: Produce an image fitting inside a 300x300 px square
|
||||||
|
- `reizie=.a10000`: Produce an image whose area is at most 10000 px
|
||||||
|
- `resize=Mitchell.200`: Produce an image fitting inside a 200x200 px square using the
|
||||||
|
Mitchell filter
|
||||||
|
- `resize=RobidouxSharp.a40000`: Produce an image whose area is at most 40000 px using the
|
||||||
|
RobidouxSharp filter
|
||||||
- `crop={int-w}x{int-h}`: produce a cropped version of the image with an `{int-w}` by `{int-h}`
|
- `crop={int-w}x{int-h}`: produce a cropped version of the image with an `{int-w}` by `{int-h}`
|
||||||
aspect ratio. The resulting crop will be centered on the image. Either the width or height
|
aspect ratio. The resulting crop will be centered on the image. Either the width or height
|
||||||
of the image will remain full-size, depending on the image's aspect ratio and the requested
|
of the image will remain full-size, depending on the image's aspect ratio and the requested
|
||||||
|
@ -344,12 +357,20 @@ A secure API key can be generated by any password generator.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
- `GET /internal/aliases?alias={alias}` Get the aliases for a file by it's alias
|
- `GET /internal/aliases?alias={alias}` Get the aliases for a file by it's alias
|
||||||
- `?alias={alias}` get aliases by alias
|
|
||||||
|
|
||||||
This endpiont returns the same JSON as the purge endpoint
|
This endpiont returns the same JSON as the purge endpoint
|
||||||
- `DELETE /internal/variants` Queue a cleanup for generated variants of uploaded images.
|
- `DELETE /internal/variants` Queue a cleanup for generated variants of uploaded images.
|
||||||
|
|
||||||
If any of the cleaned variants are fetched again, they will be re-generated.
|
If any of the cleaned variants are fetched again, they will be re-generated.
|
||||||
|
- `GET /internal/identifier` Get the image identifier (file path or object path) for a given alias
|
||||||
|
|
||||||
|
On success, the returned json should look like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msg": "ok",
|
||||||
|
"identifier": "/path/to/object"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Additionally, all endpoints support setting deadlines, after which the request will cease
|
Additionally, all endpoints support setting deadlines, after which the request will cease
|
||||||
processing. To enable deadlines for your requests, you can set the `X-Request-Deadline` header to an
|
processing. To enable deadlines for your requests, you can set the `X-Request-Deadline` header to an
|
||||||
|
|
120
src/processor.rs
120
src/processor.rs
|
@ -15,10 +15,79 @@ pub(crate) trait Processor {
|
||||||
|
|
||||||
pub(crate) struct Identity;
|
pub(crate) struct Identity;
|
||||||
pub(crate) struct Thumbnail(usize);
|
pub(crate) struct Thumbnail(usize);
|
||||||
pub(crate) struct Resize(usize);
|
pub(crate) struct Resize {
|
||||||
|
filter: Option<ResizeFilter>,
|
||||||
|
kind: ResizeKind,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub(crate) enum ResizeFilter {
|
||||||
|
Lanczos,
|
||||||
|
LanczosSharp,
|
||||||
|
Lanczos2,
|
||||||
|
Lanczos2Sharp,
|
||||||
|
Mitchell,
|
||||||
|
RobidouxSharp,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub(crate) enum ResizeKind {
|
||||||
|
Bounds(usize),
|
||||||
|
Area(usize),
|
||||||
|
}
|
||||||
pub(crate) struct Crop(usize, usize);
|
pub(crate) struct Crop(usize, usize);
|
||||||
pub(crate) struct Blur(f64);
|
pub(crate) struct Blur(f64);
|
||||||
|
|
||||||
|
impl ResizeFilter {
|
||||||
|
fn from_str(s: &str) -> Option<Self> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"lanczos" => Some(Self::Lanczos),
|
||||||
|
"lanczossharp" => Some(Self::LanczosSharp),
|
||||||
|
"lanczos2" => Some(Self::Lanczos2),
|
||||||
|
"lanczos2sharp" => Some(Self::Lanczos2Sharp),
|
||||||
|
"mitchell" => Some(Self::Mitchell),
|
||||||
|
"robidouxsharp" => Some(Self::RobidouxSharp),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_magick_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Lanczos => "Lanczos",
|
||||||
|
Self::LanczosSharp => "LanczosSharp",
|
||||||
|
Self::Lanczos2 => "Lanczos2",
|
||||||
|
Self::Lanczos2Sharp => "Lanczos2Sharp",
|
||||||
|
Self::Mitchell => "Mitchell",
|
||||||
|
Self::RobidouxSharp => "RobidouxSharp",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ResizeFilter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Lanczos2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResizeKind {
|
||||||
|
fn from_str(s: &str) -> Option<Self> {
|
||||||
|
let kind = if s.starts_with('a') {
|
||||||
|
let size = s.trim_start_matches('a').parse().ok()?;
|
||||||
|
ResizeKind::Area(size)
|
||||||
|
} else {
|
||||||
|
let size = s.parse().ok()?;
|
||||||
|
ResizeKind::Bounds(size)
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_magick_string(self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Area(size) => format!("{}@>", size),
|
||||||
|
Self::Bounds(size) => format!("{}x{}>", size, size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument]
|
#[instrument]
|
||||||
pub(crate) fn build_chain(
|
pub(crate) fn build_chain(
|
||||||
args: &[(String, String)],
|
args: &[(String, String)],
|
||||||
|
@ -111,22 +180,61 @@ impl Processor for Resize {
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
let size = v.parse().ok()?;
|
if let Some((first, second)) = v.split_once('.') {
|
||||||
Some(Resize(size))
|
let filter = ResizeFilter::from_str(first);
|
||||||
|
|
||||||
|
let kind = ResizeKind::from_str(second)?;
|
||||||
|
|
||||||
|
Some(Resize { filter, kind })
|
||||||
|
} else {
|
||||||
|
let size = v.parse().ok()?;
|
||||||
|
Some(Resize {
|
||||||
|
filter: None,
|
||||||
|
kind: ResizeKind::Bounds(size),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path(&self, mut path: PathBuf) -> PathBuf {
|
fn path(&self, mut path: PathBuf) -> PathBuf {
|
||||||
path.push(Self::NAME);
|
path.push(Self::NAME);
|
||||||
path.push(self.0.to_string());
|
match self {
|
||||||
|
Resize {
|
||||||
|
filter: None,
|
||||||
|
kind: ResizeKind::Bounds(size),
|
||||||
|
} => {
|
||||||
|
path.push(size.to_string());
|
||||||
|
}
|
||||||
|
Resize {
|
||||||
|
filter: None,
|
||||||
|
kind: ResizeKind::Area(size),
|
||||||
|
} => {
|
||||||
|
let node = format!(".a{}", size);
|
||||||
|
path.push(node);
|
||||||
|
}
|
||||||
|
Resize {
|
||||||
|
filter: Some(filter),
|
||||||
|
kind: ResizeKind::Bounds(size),
|
||||||
|
} => {
|
||||||
|
let node = format!("{}.{}", filter.to_magick_str(), size);
|
||||||
|
path.push(node);
|
||||||
|
}
|
||||||
|
Resize {
|
||||||
|
filter: Some(filter),
|
||||||
|
kind: ResizeKind::Area(size),
|
||||||
|
} => {
|
||||||
|
let node = format!("{}.a{}", filter.to_magick_str(), size);
|
||||||
|
path.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command(&self, mut args: Vec<String>) -> Vec<String> {
|
fn command(&self, mut args: Vec<String>) -> Vec<String> {
|
||||||
args.extend([
|
args.extend([
|
||||||
"-filter".to_string(),
|
"-filter".to_string(),
|
||||||
"Lanczos2".to_string(),
|
self.filter.unwrap_or_default().to_magick_str().to_string(),
|
||||||
"-resize".to_string(),
|
"-resize".to_string(),
|
||||||
format!("{}x{}>", self.0, self.0),
|
self.kind.to_magick_string(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
args
|
args
|
||||||
|
|
Loading…
Reference in a new issue