Add new resize options, update README with current APIs

This commit is contained in:
asonix 2022-09-26 18:14:25 -05:00
parent 9db057fdc5
commit 85f20bbe0e
2 changed files with 136 additions and 7 deletions

View File

@ -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

View File

@ -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,
{ {
if let Some((first, second)) = v.split_once('.') {
let filter = ResizeFilter::from_str(first);
let kind = ResizeKind::from_str(second)?;
Some(Resize { filter, kind })
} else {
let size = v.parse().ok()?; let size = v.parse().ok()?;
Some(Resize(size)) 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