Skip to content

Commit

Permalink
fix: minor fixes
Browse files Browse the repository at this point in the history
feat: igdb image processor

fix: proto formatting

feat: handle image callback

fix: lint

fix: push
  • Loading branch information
TroyKomodo committed Jan 31, 2024
1 parent dbb55a3 commit 444ed70
Show file tree
Hide file tree
Showing 10 changed files with 706 additions and 79 deletions.
2 changes: 0 additions & 2 deletions platform/api/src/api/v1/gql/models/image_upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ pub struct ImageUpload<G: ApiGlobal> {
pub struct ImageUploadVariant {
pub width: u32,
pub height: u32,
pub scale: u32,
pub url: String,
pub format: ImageUploadFormat,
pub byte_size: u32,
Expand Down Expand Up @@ -76,7 +75,6 @@ impl From<pb::scuffle::platform::internal::types::ProcessedImageVariant> for Ima
Self {
width: value.width,
height: value.height,
scale: value.scale,
format: value.format().into(),
byte_size: value.byte_size,
url: value.path,
Expand Down
17 changes: 13 additions & 4 deletions platform/api/src/api/v1/upload/profile_picture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ use crate::global::ApiGlobal;
fn create_task(file_id: Ulid, input_path: &str, config: &ImageUploaderConfig, owner_id: Ulid) -> image_processor::Task {
image_processor::Task {
input_path: input_path.to_string(),
base_height: 128, // 128, 256, 384, 512
base_width: 128, // 128, 256, 384, 512
aspect_ratio: Some(image_processor::task::Ratio {
numerator: 1,
denominator: 1,
}),
clamp_aspect_ratio: true,
formats: vec![
ImageFormat::PngStatic as i32,
ImageFormat::AvifStatic as i32,
Expand All @@ -42,8 +45,14 @@ fn create_task(file_id: Ulid, input_path: &str, config: &ImageUploaderConfig, ow
max_processing_time_ms: 60 * 1000, // 60 seconds
}),
resize_algorithm: image_processor::task::ResizeAlgorithm::Lanczos3 as i32,
upscale: true, // For profile pictures we want to have a consistent size
scales: vec![1, 2, 3, 4],
upscale: image_processor::task::Upscale::NoPreserveSource as i32,
input_image_scaling: true,
scales: vec![
64,
128,
256,
384,
],
resize_method: image_processor::task::ResizeMethod::PadCenter as i32,
output_prefix: format!("{owner_id}/{file_id}"),
}
Expand Down
14 changes: 11 additions & 3 deletions platform/api/src/igdb_cron.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,9 +587,13 @@ fn create_task(
) -> image_processor::Task {
image_processor::Task {
callback_subject: config.callback_subject.clone(),
upscale: false,
upscale: image_processor::task::Upscale::NoPreserveSource as i32,
output_prefix: format!("categories/{category_id}/{id}"),
scales: vec![1],
scales: vec![
720,
1080,
],
input_image_scaling: true,
limits: Some(image_processor::task::Limits {
max_processing_time_ms: 60000,
..Default::default()
Expand All @@ -601,7 +605,11 @@ fn create_task(
],
input_path: path,
resize_method: image_processor::task::ResizeMethod::Fit as i32,
clamp_aspect_ratio: false,
aspect_ratio: Some(image_processor::task::Ratio {
numerator: 1,
denominator: 1,
}),
resize_algorithm: image_processor::task::ResizeAlgorithm::Lanczos3 as i32,
..Default::default()
}
}
4 changes: 2 additions & 2 deletions platform/api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ struct GlobalState {
video_room_client: VideoRoomClient,
video_playback_session_client: VideoPlaybackSessionClient,
video_events_client: VideoEventsClient,

redis: Arc<fred::clients::RedisPool>,

playback_private_key: Option<jwt_next::asymmetric::AsymmetricKeyWithDigest<jwt_next::asymmetric::SigningKey>>,
Expand Down Expand Up @@ -330,7 +330,7 @@ pub async fn main() {
r = api_future => r.context("api server stopped unexpectedly")?,
r = subscription_manager => r.context("subscription manager stopped unexpectedly")?,
r = video_event_handler => r.context("video event handler stopped unexpectedly")?,
r = image_upload_callback => r.context("image upload callback handler stopped unexpectedly")?,
r = image_upload_callback => r.context("image processor callback handler stopped unexpectedly")?,
r = igdb_cron => r.context("igdb cron stopped unexpectedly")?,
}

Expand Down
4 changes: 2 additions & 2 deletions platform/image_processor/src/processor/job/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub(crate) mod libwebp;
pub(crate) mod process;
pub(crate) mod resize;
pub(crate) mod smart_object;
pub(crate) mod scaling;

pub(crate) struct Job<'a, G: ImageProcessorGlobal> {
pub(crate) global: &'a Arc<G>,
Expand Down Expand Up @@ -230,11 +231,10 @@ impl<'a, G: ImageProcessorGlobal> Job<'a, G> {
.iter()
.map(|image| pb::scuffle::platform::internal::types::ProcessedImageVariant {
path: image.url(&self.job.task.output_prefix),
format: image.request.1.into(),
format: image.request.into(),
width: image.width as u32,
height: image.height as u32,
byte_size: image.data.len() as u32,
scale: image.request.0 as u32,
})
.collect(),
},
Expand Down
116 changes: 66 additions & 50 deletions platform/image_processor/src/processor/job/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::borrow::Cow;
use std::collections::{HashMap, HashSet};

use bytes::Bytes;
use pb::scuffle::platform::internal::image_processor::task;
use pb::scuffle::platform::internal::types::ImageFormat;
use rgb::ComponentBytes;
use sha2::Digest;
Expand All @@ -11,6 +12,7 @@ use super::encoder::{AnyEncoder, Encoder, EncoderFrontend, EncoderSettings};
use super::resize::{ImageResizer, ImageResizerTarget};
use crate::database::Job;
use crate::processor::error::{ProcessorError, Result};
use crate::processor::job::scaling::{ScalingOptions, Ratio};

#[derive(Debug)]
pub struct Image {
Expand All @@ -21,12 +23,12 @@ pub struct Image {
pub encoder: EncoderFrontend,
pub data: Bytes,
pub loop_count: LoopCount,
pub request: (usize, ImageFormat),
pub request: ImageFormat,
}

impl Image {
pub fn file_extension(&self) -> &'static str {
match self.request.1 {
match self.request {
ImageFormat::Avif | ImageFormat::AvifStatic => "avif",
ImageFormat::Webp | ImageFormat::WebpStatic => "webp",
ImageFormat::Gif => "gif",
Expand All @@ -35,7 +37,7 @@ impl Image {
}

pub fn content_type(&self) -> &'static str {
match self.request.1 {
match self.request {
ImageFormat::Avif | ImageFormat::AvifStatic => "image/avif",
ImageFormat::Webp | ImageFormat::WebpStatic => "image/webp",
ImageFormat::Gif => "image/gif",
Expand All @@ -45,18 +47,19 @@ impl Image {

pub fn is_static(&self) -> bool {
matches!(
self.request.1,
self.request,
ImageFormat::AvifStatic | ImageFormat::WebpStatic | ImageFormat::PngStatic
)
}

pub fn url(&self, prefix: &str) -> String {
format!(
"{}/{}{}x.{}",
prefix.trim_end_matches('/'),
self.is_static().then_some("static_").unwrap_or_default(),
self.request.0,
self.file_extension()
"{prefix}/{static_prefix}{width}x{height}.{ext}",
prefix = prefix.trim_end_matches('/'),
static_prefix = self.is_static().then_some("static_").unwrap_or_default(),
width = self.width,
height = self.height,
ext = self.file_extension()
)
}
}
Expand All @@ -72,7 +75,10 @@ pub fn process_job(backend: DecoderBackend, job: &Job, data: Cow<'_, [u8]>) -> R
let info = decoder.info();

let formats = job.task.formats().collect::<HashSet<_>>();
let scales = job.task.scales.iter().map(|s| *s as usize).collect::<HashSet<_>>();
let mut scales = job.task.scales.iter().cloned().map(|s| s as usize).collect::<Vec<_>>();

// Sorts the scales from smallest to largest.
scales.sort();

if formats.is_empty() || scales.is_empty() {
tracing::debug!("no formats or scales specified");
Expand Down Expand Up @@ -118,32 +124,50 @@ pub fn process_job(backend: DecoderBackend, job: &Job, data: Cow<'_, [u8]>) -> R
static_image: true,
};

let (base_width, base_height) = if job.task.upscale {
(job.task.base_width as f64, job.task.base_height as f64)
} else {
let largest_scale = scales.iter().max().copied().unwrap_or(1);

let width = info.width as f64 / largest_scale as f64;
let height = info.height as f64 / largest_scale as f64;

if width > job.task.base_width as f64 && height > job.task.base_height as f64 {
(job.task.base_width as f64, job.task.base_height as f64)
} else {
(width, height)
}
let (preserve_aspect_height, preserve_aspect_width) = match job.task.resize_method() {
task::ResizeMethod::Fit => (true, true),
task::ResizeMethod::Stretch => (false, false),
task::ResizeMethod::PadBottomLeft => (false, false),
task::ResizeMethod::PadBottomRight => (false, false),
task::ResizeMethod::PadTopLeft => (false, false),
task::ResizeMethod::PadTopRight => (false, false),
task::ResizeMethod::PadCenter => (false, false),
task::ResizeMethod::PadCenterLeft => (false, false),
task::ResizeMethod::PadCenterRight => (false, false),
task::ResizeMethod::PadTopCenter => (false, false),
task::ResizeMethod::PadBottomCenter => (false, false),
task::ResizeMethod::PadTop => (false, true),
task::ResizeMethod::PadBottom => (false, true),
task::ResizeMethod::PadLeft => (true, false),
task::ResizeMethod::PadRight => (true, false),
};


let upscale = job.task.upscale().into();

let scales = ScalingOptions {
input_height: info.height,
input_width: info.width,
input_image_scaling: job.task.input_image_scaling,
clamp_aspect_ratio: job.task.clamp_aspect_ratio,
scales,
aspect_ratio: job.task.aspect_ratio.as_ref().map(|r| Ratio::new(r.numerator as usize, r.denominator as usize)).unwrap_or(Ratio::ONE),
upscale,
preserve_aspect_height,
preserve_aspect_width,
}.compute();

// let base_width = input_width as f64 / job.task.aspect_width as f64;
let mut resizers = scales
.iter()
.map(|scale| {
(
*scale,
scale.clone(),
ImageResizer::new(ImageResizerTarget {
height: base_height.ceil() as usize * scale,
width: base_width.ceil() as usize * scale,
height: scale.height,
width: scale.width,
algorithm: job.task.resize_algorithm(),
method: job.task.resize_method(),
upscale: job.task.upscale,
upscale: upscale.is_yes(),
}),
Vec::with_capacity(info.frame_count),
)
Expand Down Expand Up @@ -185,16 +209,14 @@ pub fn process_job(backend: DecoderBackend, job: &Job, data: Cow<'_, [u8]>) -> R
drop(decoder);

struct Stack {
scale: usize,
static_encoders: Vec<AnyEncoder>,
animation_encoders: Vec<AnyEncoder>,
}

let mut stacks = resizers
.iter_mut()
.map(|(scale, _, frames)| {
.iter()
.map(|(_, _, frames)| {
Ok(Stack {
scale: *scale,
static_encoders: static_formats
.iter()
.map(|&frontend| frontend.build(static_settings))
Expand Down Expand Up @@ -242,15 +264,12 @@ pub fn process_job(backend: DecoderBackend, job: &Job, data: Cow<'_, [u8]>) -> R
encoder: info.frontend,
data: output.into(),
loop_count: info.loop_count,
request: (
stack.scale,
match info.frontend {
EncoderFrontend::Gifski => ImageFormat::Gif,
EncoderFrontend::LibAvif => ImageFormat::Avif,
EncoderFrontend::LibWebp => ImageFormat::Webp,
EncoderFrontend::Png => unreachable!(),
},
),
request: match info.frontend {
EncoderFrontend::Gifski => ImageFormat::Gif,
EncoderFrontend::LibAvif => ImageFormat::Avif,
EncoderFrontend::LibWebp => ImageFormat::Webp,
EncoderFrontend::Png => unreachable!(),
},
});
}

Expand All @@ -265,15 +284,12 @@ pub fn process_job(backend: DecoderBackend, job: &Job, data: Cow<'_, [u8]>) -> R
encoder: info.frontend,
data: output.into(),
loop_count: info.loop_count,
request: (
stack.scale,
match info.frontend {
EncoderFrontend::LibAvif => ImageFormat::AvifStatic,
EncoderFrontend::LibWebp => ImageFormat::WebpStatic,
EncoderFrontend::Png => ImageFormat::PngStatic,
EncoderFrontend::Gifski => unreachable!(),
},
),
request: match info.frontend {
EncoderFrontend::LibAvif => ImageFormat::AvifStatic,
EncoderFrontend::LibWebp => ImageFormat::WebpStatic,
EncoderFrontend::Png => ImageFormat::PngStatic,
EncoderFrontend::Gifski => unreachable!(),
},
});
}
}
Expand Down
4 changes: 2 additions & 2 deletions platform/image_processor/src/processor/job/resize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl ImageResizer {
pub fn resize(&mut self, frame: &Frame) -> Result<Frame> {
let _abort_guard = utils::task::AbortGuard::new();

let (width, height) = if self.target.method == ResizeMethod::Exact {
let (width, height) = if self.target.method == ResizeMethod::Stretch {
(self.target.width, self.target.height)
} else {
let (mut width, mut height) = if frame.image.width() > frame.image.height() {
Expand Down Expand Up @@ -117,7 +117,7 @@ impl ImageResizer {
ResizeMethod::PadBottom => (0, height_delta, 0, 0),
ResizeMethod::PadLeft => (0, 0, width_delta, 0),
ResizeMethod::PadRight => (0, 0, 0, width_delta),
ResizeMethod::Exact => unreachable!(),
ResizeMethod::Stretch => unreachable!(),
ResizeMethod::Fit => unreachable!(),
};

Expand Down
Loading

0 comments on commit 444ed70

Please sign in to comment.