Skip to content

Commit

Permalink
Support fetching Scribbles as PNGs
Browse files Browse the repository at this point in the history
  • Loading branch information
FrostyCoolSlug committed Mar 1, 2024
1 parent a35aeba commit 1a5fa00
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 11 deletions.
72 changes: 72 additions & 0 deletions daemon/src/servers/http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use anyhow::{anyhow, Result};
use include_dir::{include_dir, Dir};
use jsonpath_rust::JsonPathQuery;
use log::{debug, error, info, warn};
use mime_guess::mime::IMAGE_PNG;
use mime_guess::MimeGuess;
use serde_json::Value;
use tokio::sync::broadcast::Sender as BroadcastSender;
Expand All @@ -30,6 +31,8 @@ use crate::PatchEvent;
use goxlr_ipc::{
DaemonRequest, DaemonResponse, DaemonStatus, HttpSettings, WebsocketRequest, WebsocketResponse,
};
use goxlr_scribbles::get_scribble_png;
use goxlr_types::FaderName;

use crate::primary_worker::DeviceSender;
use crate::servers::server_packet::handle_packet;
Expand Down Expand Up @@ -225,6 +228,7 @@ pub async fn spawn_http_server(
.service(execute_command)
.service(get_devices)
.service(get_sample)
.service(get_scribble)
.service(get_path)
.service(websocket)
.default_service(web::to(default))
Expand Down Expand Up @@ -334,8 +338,76 @@ async fn get_path(app_data: Data<Mutex<AppData>>, req: HttpRequest) -> HttpRespo
HttpResponse::InternalServerError().finish()
}

#[get("/files/scribble/{serial}/{fader}.png")]
async fn get_scribble(
path: web::Path<(String, FaderName)>,
app_data: Data<Mutex<AppData>>,
req: HttpRequest,
) -> HttpResponse {
let serial = &path.0;
let fader = path.1;

let params = web::Query::<HashMap<String, String>>::from_query(req.query_string());
let mut final_width = 128;
let mut final_height = 64;

// Try and Parse the width and height out the request (?width=X&height=Y)
if let Ok(params) = params {
if let Some(width) = params.get("width") {
if let Ok(width_numeric) = width.parse() {
final_width = width_numeric;
}
}
if let Some(height) = params.get("height") {
if let Ok(height_numeric) = height.parse() {
final_height = height_numeric;
}
}
}

// Now we need to grab the DaemonResponse to get the layout of the scribble..
let mut guard = app_data.lock().await;
let sender = guard.deref_mut();
let request = DaemonRequest::GetStatus;

if let Ok(DaemonResponse::Status(status)) = handle_packet(request, &mut sender.usb_tx).await {
let scribble_path = status.paths.icons_directory;

if let Some(mixer) = status.mixers.get(serial) {
// Locate the Scribble..
if let Some(scribble) = &mixer.fader_status[fader].scribble {
let mut icon_path = None;
if let Some(file) = &scribble.file_name {
icon_path = Some(scribble_path.join(file));
}

// We have access to the Scribble package, so generate and throw out..
let png = get_scribble_png(
icon_path,
scribble.bottom_text.clone(),
scribble.left_text.clone(),
scribble.inverted,
final_width,
final_height,
);
debug!("Creating Image {}x{}", final_width, final_height);

let mime_type = ContentType(IMAGE_PNG);
let mut builder = HttpResponse::Ok();
builder.insert_header(mime_type);
return builder.body(png.unwrap());
}
}
}

debug!("Unable to Build Image: {} - {}", serial, fader);
HttpResponse::NotFound().finish()
}

#[get("/files/samples/{sample}")]
async fn get_sample(sample: web::Path<String>, app_data: Data<Mutex<AppData>>) -> HttpResponse {
debug!("Err?");

// Get the Base Samples Path..
let mut guard = app_data.lock().await;
let sender = guard.deref_mut();
Expand Down
75 changes: 64 additions & 11 deletions scribbles/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,79 @@
use anyhow::{bail, Result};
use image::imageops::{dither, overlay, BiLevel, FilterType};
use image::ImageOutputFormat::Png;
use image::{ColorType, DynamicImage, GenericImage, GenericImageView, GrayImage, Luma, Rgba};
use imageproc::drawing::{draw_text_mut, text_size};
use log::warn;
use rusttype::{Font, Scale};
use std::borrow::BorrowMut;
use std::io::Cursor;
use std::path::PathBuf;

static FONT: &[u8] = include_bytes!("../fonts/Play-Bold.ttf");

pub fn get_scribble(
path: Option<PathBuf>,
bottom_text: Option<String>,
top_right: Option<String>,
bottom: Option<String>,
top: Option<String>,
invert: bool,
) -> [u8; 1024] {
let image = get_scribble_base(path, bottom, top);

if let Ok(image) = to_goxlr(image, invert) {
image
} else {
[0; 1024]
}
}

pub fn get_scribble_png(
path: Option<PathBuf>,
bottom: Option<String>,
top: Option<String>,
invert: bool,
width: u32,
height: u32,
) -> Result<Vec<u8>> {
// First, get the GrayScale version..
let mut image = get_scribble_base(path, bottom, top);

let white = Luma::from([255_u8]);
let black = Luma::from([0_u8]);

// Do we need to invert this?
if invert {
image.pixels_mut().for_each(|f| {
*f = if *f == white { black } else { white };
})
}

// Now, we convert it into a dynamic image and resize..
let mut image: DynamicImage = DynamicImage::from(image);
image = image.resize_exact(width, height, FilterType::Nearest);

// Next step, is to reintroduce transparency, and correctly set the pixels..
let white = Rgba::from([255, 255, 255, 255]);
let transparent = Rgba::from([255, 255, 255, 0]);

let mut image: DynamicImage = image.to_rgba8().into();
for (x, y, pixel) in image.clone().pixels() {
if pixel == white {
image.put_pixel(x, y, transparent);
}
}

// Finally, return a PNG..
let mut bytes = Vec::new();
image.write_to(&mut Cursor::new(&mut bytes), Png)?;

Ok(bytes)
}

pub fn get_scribble_base(
path: Option<PathBuf>,
bottom: Option<String>,
top: Option<String>,
) -> GrayImage {
let mut processed_image = None;
let mut bottom_image = None;
let mut top_right_image = None;
Expand All @@ -25,25 +84,19 @@ pub fn get_scribble(
}
}

if let Some(text) = bottom_text {
if let Some(text) = bottom {
if let Ok(image) = create_text_image(&text) {
bottom_image = Some(image);
}
}

if let Some(text) = top_right {
if let Some(text) = top {
if let Ok(image) = create_text_image(&text) {
top_right_image = Some(image);
}
}

let image = create_final_image(processed_image, bottom_image, top_right_image);

if let Ok(result) = to_goxlr(image, invert) {
result
} else {
[0; 1024]
}
create_final_image(processed_image, bottom_image, top_right_image)
}

fn load_grayscale_image(path: PathBuf) -> Result<DynamicImage> {
Expand Down

0 comments on commit 1a5fa00

Please sign in to comment.