Improve screen scaling behavior
This commit is contained in:
@@ -4,4 +4,6 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
femtovg = "0.19.3"
|
||||
image = { version = "0.25.9", default-features = false, features = ["png"] }
|
||||
nih_plug = { git = "https://github.com/robbert-vdh/nih-plug", version = "0.0.0", default-features = false }
|
||||
|
||||
187
ebu-dsp/src/gui/mod.rs
Normal file
187
ebu-dsp/src/gui/mod.rs
Normal file
@@ -0,0 +1,187 @@
|
||||
use std::sync::{Arc, atomic::Ordering};
|
||||
|
||||
use femtovg::{Canvas, ImageFlags, ImageId, Paint, Path, renderer::OpenGl};
|
||||
use nih_plug::prelude::AtomicF32;
|
||||
|
||||
use crate::{Rect, ScaledRect};
|
||||
|
||||
pub struct SpriteSheet {
|
||||
scale_factor: Arc<AtomicF32>,
|
||||
image: Result<ImageId, String>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
frame_width: usize,
|
||||
frame_height: usize,
|
||||
frames_x: usize,
|
||||
}
|
||||
|
||||
impl SpriteSheet {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
scale_factor: Arc::new(AtomicF32::new(1.0)),
|
||||
image: Err("No image loaded".to_owned()),
|
||||
width: 0,
|
||||
height: 0,
|
||||
frame_width: 0,
|
||||
frame_height: 0,
|
||||
frames_x: 0,
|
||||
}
|
||||
}
|
||||
pub fn new(
|
||||
canvas: &mut Canvas<OpenGl>,
|
||||
data: &[u8],
|
||||
scale_factor: Arc<AtomicF32>,
|
||||
frame_width: usize,
|
||||
frame_height: usize,
|
||||
frames: usize,
|
||||
) -> Self {
|
||||
let image = canvas
|
||||
.load_image_mem(data, ImageFlags::empty())
|
||||
.map_err(|e| format!("{e:?}"));
|
||||
if let Ok(image) = image {
|
||||
let (width, height) = canvas.image_size(image).unwrap_or_default();
|
||||
Self {
|
||||
scale_factor,
|
||||
image: Ok(image),
|
||||
width,
|
||||
height,
|
||||
frame_width,
|
||||
frame_height,
|
||||
frames_x: width / frame_width,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
scale_factor,
|
||||
image,
|
||||
width: 0,
|
||||
height: 0,
|
||||
frame_width,
|
||||
frame_height,
|
||||
frames_x: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, x: f32, y: f32, frame: usize) {
|
||||
let factor = self.scale_factor.load(Ordering::Relaxed);
|
||||
|
||||
let frame_x = frame % self.frames_x;
|
||||
let frame_y = frame / self.frames_x;
|
||||
|
||||
let screen_rect = Rect {
|
||||
x: x * factor,
|
||||
y: y * factor,
|
||||
width: self.frame_width as f32 * factor,
|
||||
height: self.frame_height as f32 * factor,
|
||||
};
|
||||
let image_rect = Rect {
|
||||
x: screen_rect.x - (frame_x * self.frame_width) as f32 * factor,
|
||||
y: screen_rect.y - (frame_y * self.frame_height) as f32 * factor,
|
||||
width: self.width as f32 * factor,
|
||||
height: self.height as f32 * factor,
|
||||
};
|
||||
|
||||
let mut screen_path = Path::new();
|
||||
screen_path.rect(
|
||||
screen_rect.x,
|
||||
screen_rect.y,
|
||||
screen_rect.width,
|
||||
screen_rect.height,
|
||||
);
|
||||
if let Ok(image) = self.image {
|
||||
canvas.fill_path(
|
||||
&screen_path,
|
||||
&Paint::image(
|
||||
image,
|
||||
image_rect.x,
|
||||
image_rect.y,
|
||||
image_rect.width,
|
||||
image_rect.height,
|
||||
0.0,
|
||||
1.0,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn screen_bounds(&self, x: f32, y: f32) -> ScaledRect {
|
||||
ScaledRect::new_from(
|
||||
self.scale_factor.clone(),
|
||||
(x, y, self.frame_width as f32, self.frame_height as f32),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Sprite {
|
||||
scale_factor: Arc<AtomicF32>,
|
||||
image: Result<ImageId, String>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
impl Sprite {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
scale_factor: Arc::new(AtomicF32::new(1.0)),
|
||||
image: Err("No image loaded".to_owned()),
|
||||
width: 0,
|
||||
height: 0,
|
||||
}
|
||||
}
|
||||
pub fn new(canvas: &mut Canvas<OpenGl>, data: &[u8], scale_factor: Arc<AtomicF32>) -> Self {
|
||||
let image = canvas
|
||||
.load_image_mem(data, ImageFlags::empty())
|
||||
.map_err(|e| format!("{e:?}"));
|
||||
if let Ok(image) = image {
|
||||
let (width, height) = canvas.image_size(image).unwrap_or_default();
|
||||
Self {
|
||||
scale_factor,
|
||||
image: Ok(image),
|
||||
width,
|
||||
height,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
scale_factor,
|
||||
image,
|
||||
width: 0,
|
||||
height: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, x: f32, y: f32, alpha: f32) {
|
||||
let factor = self.scale_factor.load(Ordering::Relaxed);
|
||||
|
||||
let screen_rect = Rect {
|
||||
x: x * factor,
|
||||
y: y * factor,
|
||||
width: self.width as f32 * factor,
|
||||
height: self.height as f32 * factor,
|
||||
};
|
||||
|
||||
let mut screen_path = Path::new();
|
||||
screen_path.rect(
|
||||
screen_rect.x,
|
||||
screen_rect.y,
|
||||
screen_rect.width,
|
||||
screen_rect.height,
|
||||
);
|
||||
if let Ok(image) = self.image {
|
||||
canvas.fill_path(
|
||||
&screen_path,
|
||||
&Paint::image(
|
||||
image,
|
||||
screen_rect.x,
|
||||
screen_rect.y,
|
||||
screen_rect.width,
|
||||
screen_rect.height,
|
||||
0.0,
|
||||
alpha,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn screen_bounds(&self, x: f32, y: f32) -> ScaledRect {
|
||||
ScaledRect::new_from(
|
||||
self.scale_factor.clone(),
|
||||
(x, y, self.width as f32, self.height as f32),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,16 @@ mod comp;
|
||||
mod decibel;
|
||||
mod eq;
|
||||
mod freq_split;
|
||||
mod gui;
|
||||
mod meter;
|
||||
mod ring_buffer;
|
||||
mod smoother;
|
||||
mod traits;
|
||||
|
||||
use std::{
|
||||
fmt::Debug, ops::Add, time::Duration
|
||||
fmt::Debug,
|
||||
sync::{Arc, atomic::Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
pub use amplitude::Amplitude;
|
||||
@@ -19,42 +22,159 @@ pub use comp::{Compressor, CompressorState};
|
||||
pub use decibel::Decibel;
|
||||
pub use eq::{Equalizer, EqualizerState};
|
||||
pub use freq_split::{FreqSplitter, FreqSplitterState};
|
||||
pub use gui::{SpriteSheet, Sprite};
|
||||
use nih_plug::prelude::AtomicF32;
|
||||
pub use traits::{FloatFormatter, IntFormatter, Lerp, Processor};
|
||||
|
||||
pub struct Rect<T> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct Point {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
impl<T: Debug> Debug for Rect<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Rect")
|
||||
.field("x", &self.x)
|
||||
.field("y", &self.y)
|
||||
.field("width", &self.width)
|
||||
.field("height", &self.height)
|
||||
.finish()
|
||||
impl Point {
|
||||
pub const fn new(x: f32, y: f32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
impl<T: PartialOrd + Add<Output = T> + Copy> Rect<T> {
|
||||
pub fn contains(&self, value: (T, T)) -> bool {
|
||||
value.0 >= self.x
|
||||
&& value.1 >= self.y
|
||||
&& value.0 < self.x + self.width
|
||||
&& value.1 < self.y + self.height
|
||||
impl From<(f32, f32)> for Point {
|
||||
fn from(value: (f32, f32)) -> Self {
|
||||
Point {
|
||||
x: value.0,
|
||||
y: value.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> Default for Rect<T>
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ScaledPoint {
|
||||
pub factor: Arc<AtomicF32>,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
impl PartialEq for ScaledPoint {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.factor.load(Ordering::Acquire) == other.factor.load(Ordering::Acquire)
|
||||
&& self.x == other.x
|
||||
&& self.y == other.y
|
||||
}
|
||||
}
|
||||
impl ScaledPoint {
|
||||
pub fn new(factor: Arc<AtomicF32>) -> Self {
|
||||
Self {
|
||||
x: Default::default(),
|
||||
y: Default::default(),
|
||||
width: Default::default(),
|
||||
height: Default::default(),
|
||||
factor,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn new_from<P>(factor: Arc<AtomicF32>, point: P) -> Self
|
||||
where
|
||||
P: Into<Point>,
|
||||
{
|
||||
let point = point.into();
|
||||
Self {
|
||||
factor,
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
}
|
||||
}
|
||||
pub fn set<P>(&mut self, point: P)
|
||||
where
|
||||
P: Into<Point>,
|
||||
{
|
||||
let point = point.into();
|
||||
self.x = point.x;
|
||||
self.y = point.y;
|
||||
}
|
||||
pub fn set_scaled<P>(&mut self, point: P)
|
||||
where
|
||||
P: Into<Point>,
|
||||
{
|
||||
let factor = self.factor.load(Ordering::Acquire);
|
||||
let point = point.into();
|
||||
self.x = point.x / factor;
|
||||
self.y = point.y / factor;
|
||||
}
|
||||
pub fn as_point(&self) -> Point {
|
||||
let factor = self.factor.load(Ordering::Acquire);
|
||||
Point {
|
||||
x: self.x * factor,
|
||||
y: self.y * factor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct Rect {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
impl Rect {
|
||||
pub fn contains<P>(&self, value: P) -> bool
|
||||
where
|
||||
P: Into<Point>,
|
||||
{
|
||||
let value = value.into();
|
||||
value.x >= self.x
|
||||
&& value.y >= self.y
|
||||
&& value.x < self.x + self.width
|
||||
&& value.y < self.y + self.height
|
||||
}
|
||||
}
|
||||
impl From<(f32, f32, f32, f32)> for Rect {
|
||||
fn from(value: (f32, f32, f32, f32)) -> Self {
|
||||
Rect {
|
||||
x: value.0,
|
||||
y: value.1,
|
||||
width: value.2,
|
||||
height: value.3,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ScaledRect {
|
||||
pub factor: Arc<AtomicF32>,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
impl PartialEq for ScaledRect {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.factor.load(Ordering::Acquire) == other.factor.load(Ordering::Acquire)
|
||||
&& self.x == other.x
|
||||
&& self.y == other.y
|
||||
&& self.width == other.width
|
||||
&& self.height == other.height
|
||||
}
|
||||
}
|
||||
impl ScaledRect {
|
||||
pub fn new(factor: Arc<AtomicF32>) -> Self {
|
||||
Self {
|
||||
factor,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn new_from<R>(factor: Arc<AtomicF32>, rect: R) -> Self
|
||||
where
|
||||
R: Into<Rect>,
|
||||
{
|
||||
let rect = rect.into();
|
||||
Self {
|
||||
factor,
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
}
|
||||
}
|
||||
pub fn as_rect(&self) -> Rect {
|
||||
let factor = self.factor.load(Ordering::Acquire);
|
||||
Rect {
|
||||
x: self.x * factor,
|
||||
y: self.y * factor,
|
||||
width: self.width * factor,
|
||||
height: self.height * factor,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,4 +314,3 @@ pub fn rms<const N: usize>(sample_buffer: &RingBuffer<f64, N>, last_rms: f64) ->
|
||||
last_rms.powf(2.0)
|
||||
+ (1.0 / N as f64) * (sample_buffer[0].powf(2.0) - sample_buffer[N - 1].powf(2.0))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user