Feat: completely generate logo without relying on pngs
This commit is contained in:
parent
85c6d4741f
commit
9d89154b23
10 changed files with 484 additions and 67 deletions
|
@ -1,9 +1,14 @@
|
|||
use axum::{extract::Query, routing::get, Router};
|
||||
use cairo::{Context, Format, ImageSurface};
|
||||
use rust_embed::RustEmbed;
|
||||
use std::time::Instant;
|
||||
|
||||
use axum::{
|
||||
extract::{Query, State},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use cairo::{freetype::Face, Context, FontFace, Format, ImageSurface};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{polygon, SharedState};
|
||||
use crate::{color::Color, polygon, text::DrawableText, SharedState};
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
#[allow(non_camel_case_types)]
|
||||
|
@ -29,53 +34,23 @@ struct ImageProperties {
|
|||
orientation: LogoOrientation,
|
||||
}
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "artwork/logo"]
|
||||
#[include = "*_T.png"]
|
||||
struct ImageFiles;
|
||||
|
||||
fn get_surface_and_logo_coordiates(
|
||||
properties: &ImageProperties,
|
||||
) -> (ImageSurface, (f64, f64), f64, f64) {
|
||||
if let LogoVariant::NoText = properties.variant {
|
||||
return (
|
||||
ImageSurface::create(Format::ARgb32, 400, 400).unwrap(),
|
||||
(200.0, 200.0),
|
||||
148.0,
|
||||
67.0,
|
||||
);
|
||||
}
|
||||
|
||||
let background_image_path = match (&properties.variant, &properties.orientation) {
|
||||
(LogoVariant::DarkText, LogoOrientation::Landscape) => "landscape/4C_T.png",
|
||||
(LogoVariant::LightText, LogoOrientation::Landscape) => "landscape/W_T.png",
|
||||
(LogoVariant::DarkText, LogoOrientation::Portrait) => "portrait/4C_T.png",
|
||||
(LogoVariant::LightText, LogoOrientation::Portrait) => "portrait/W_T.png",
|
||||
_ => unreachable!(),
|
||||
fn create_surface(properties: &ImageProperties) -> ImageSurface {
|
||||
let (width, height) = match (&properties.variant, &properties.orientation) {
|
||||
(LogoVariant::NoText, _) => (400, 400),
|
||||
(_, LogoOrientation::Landscape) => (2127, 591),
|
||||
(_, LogoOrientation::Portrait) => (1654, 1654),
|
||||
};
|
||||
|
||||
let (coordinates, outer_radius, inner_radius) = match &properties.orientation {
|
||||
LogoOrientation::Landscape => ((412.0, 299.0), 209.0, 99.0),
|
||||
LogoOrientation::Portrait => ((828.0, 563.0), 253.0, 118.0),
|
||||
};
|
||||
|
||||
let image = ImageFiles::get(background_image_path).unwrap();
|
||||
(
|
||||
ImageSurface::create_from_png(&mut image.data.as_ref()).unwrap(),
|
||||
coordinates,
|
||||
outer_radius,
|
||||
inner_radius,
|
||||
)
|
||||
ImageSurface::create(Format::ARgb32, width, height).unwrap()
|
||||
}
|
||||
|
||||
async fn handler(Query(properties): Query<ImageProperties>) -> impl axum::response::IntoResponse {
|
||||
let (surface, logo_coordinates, logo_outer_radius, logo_inner_radius) =
|
||||
get_surface_and_logo_coordiates(&properties);
|
||||
let context = Context::new(&surface).unwrap();
|
||||
|
||||
context.set_source_rgba(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
context.paint().unwrap();
|
||||
fn draw_logo(context: &Context, properties: &ImageProperties) {
|
||||
let (logo_coordinates, logo_outer_radius, logo_inner_radius) =
|
||||
match (&properties.variant, &properties.orientation) {
|
||||
(LogoVariant::NoText, _) => ((200.0, 200.0), 148.0, 67.0),
|
||||
(_, LogoOrientation::Landscape) => ((412.0, 299.0), 209.0, 99.0),
|
||||
(_, LogoOrientation::Portrait) => ((828.0, 563.0), 253.0, 118.0),
|
||||
};
|
||||
|
||||
polygon::draw_polygon_of_segmented_polygons(
|
||||
logo_coordinates,
|
||||
|
@ -85,9 +60,101 @@ async fn handler(Query(properties): Query<ImageProperties>) -> impl axum::respon
|
|||
&context,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn draw_text(
|
||||
context: &Context,
|
||||
font_regular: &Face,
|
||||
font_light: &Face,
|
||||
properties: &ImageProperties,
|
||||
) {
|
||||
if let LogoVariant::NoText = properties.variant {
|
||||
return;
|
||||
}
|
||||
|
||||
let color = match &properties.variant {
|
||||
LogoVariant::DarkText => Color::from("#383F50"),
|
||||
LogoVariant::LightText => Color::from("#ffffff"),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
context.set_source_rgb(
|
||||
color.r as f64 / 255.0,
|
||||
color.g as f64 / 255.0,
|
||||
color.b as f64 / 255.0,
|
||||
);
|
||||
|
||||
let texts = match &properties.orientation {
|
||||
LogoOrientation::Landscape => (
|
||||
DrawableText {
|
||||
position: (760.0, 356.0),
|
||||
size: 228.0,
|
||||
spacing: 45.5,
|
||||
text: "makerlab",
|
||||
},
|
||||
DrawableText {
|
||||
position: (1495.0, 504.0),
|
||||
size: 110.0,
|
||||
spacing: 10.5,
|
||||
text: "MURNAU",
|
||||
},
|
||||
),
|
||||
LogoOrientation::Portrait => (
|
||||
DrawableText {
|
||||
position: (48.0, 1242.0),
|
||||
size: 280.0,
|
||||
spacing: 53.5,
|
||||
text: "makerlab",
|
||||
},
|
||||
DrawableText {
|
||||
position: (493.0, 1424.0),
|
||||
size: 135.0,
|
||||
spacing: 13.0,
|
||||
text: "MURNAU",
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
context.set_font_face(&FontFace::create_from_ft(font_regular).unwrap());
|
||||
texts.0.draw(&context);
|
||||
|
||||
context.set_font_face(&FontFace::create_from_ft(font_light).unwrap());
|
||||
texts.1.draw(&context);
|
||||
}
|
||||
|
||||
#[axum_macros::debug_handler]
|
||||
async fn handler(
|
||||
Query(properties): Query<ImageProperties>,
|
||||
State(state): State<SharedState>,
|
||||
) -> impl axum::response::IntoResponse {
|
||||
let start = Instant::now();
|
||||
let (font_regular, font_light) = state.fonts.get().await;
|
||||
println!("{:?}", start.elapsed());
|
||||
let start = Instant::now();
|
||||
|
||||
// cannot use await after this, because surface does not implement Send
|
||||
|
||||
let surface = create_surface(&properties);
|
||||
let context = Context::new(&surface).unwrap();
|
||||
|
||||
println!("{:?}", (surface.width(), surface.height()));
|
||||
|
||||
println!("{:?}", start.elapsed());
|
||||
let start = Instant::now();
|
||||
|
||||
draw_logo(&context, &properties);
|
||||
println!("{:?}", start.elapsed());
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
draw_text(&context, &font_regular, &font_light, &properties);
|
||||
|
||||
println!("{:?}", start.elapsed());
|
||||
let start = Instant::now();
|
||||
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
surface.write_to_png(&mut data).unwrap();
|
||||
println!("{:?}", start.elapsed());
|
||||
|
||||
(
|
||||
axum::response::AppendHeaders([(axum::http::header::CONTENT_TYPE, "image/png")]),
|
||||
|
@ -96,5 +163,5 @@ async fn handler(Query(properties): Query<ImageProperties>) -> impl axum::respon
|
|||
}
|
||||
|
||||
pub fn routes() -> Router<SharedState> {
|
||||
Router::new().route("/", get(handler))
|
||||
Router::<SharedState>::new().route("/", get(handler))
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue