Initial repo

This commit is contained in:
2023-01-02 10:31:09 +03:00
commit daed1baafd
16 changed files with 768 additions and 0 deletions

3
crates/meow_image/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
dem.jpg
font.ttf

View File

@ -0,0 +1,14 @@
[package]
name = "meow_image"
description = "Blazing fast 🚀 higher-level image proccessing library"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
image = "0.24"
imageproc = "0.23"
rusttype = "0.9"
once_cell = "1"

View File

@ -0,0 +1,12 @@
# Meow Image
Blazing fast 🚀 higher-level image proccessing library
# Building
1. **Move your font to the root of the project.**
2. Then run
```bash
cargo build --release
```

View File

@ -0,0 +1,189 @@
use crate::text_width::get_text_width;
use image::{GenericImage, ImageBuffer, Rgb, RgbImage};
use imageproc::drawing::draw_text_mut;
use once_cell::sync::Lazy;
use rusttype::{Font, Scale};
const WHITE: Rgb<u8> = Rgb([255, 255, 255]);
const BLACK: Rgb<u8> = Rgb([0, 0, 0]);
static FONT_DATA: &[u8] = include_bytes!("../../../assets/XO_Thames_Nu.ttf");
static FONT: Lazy<Font<'static>> = Lazy::new(|| Font::try_from_bytes(FONT_DATA).unwrap());
#[derive(Clone, Copy)]
struct Border {
pub top: u32,
pub left: u32,
pub bottom: u32,
pub right: u32,
}
/// Makes a demotivator meme
pub fn demotivate(image: &RgbImage, top_text: &str, bottom_text: &str) -> RgbImage {
let (width, height) = image.dimensions();
// Calculate a border
let border_size = (if width > height { width } else { height }) / 8;
let border = Border {
top: border_size,
left: border_size,
bottom: border_size * 2,
right: border_size,
};
// Add a black border
let mut image_with_border = add_border(image, BLACK, border);
let (width, height) = image_with_border.dimensions();
// Calculate the padding and the line width
let padding = border_size / 10;
let line_width = padding / 3;
// Add a white box next to the image
add_box(&mut image_with_border, WHITE, border, line_width, padding);
// Calculate the scale and position of the top text
{
let scale = Scale::uniform(border_size as f32);
let text_width = get_text_width(scale, &FONT, top_text);
let x_text_position = (width - text_width) / 2; // center by width
let y_text_position = height - (border.bottom - padding);
// Draw the top text
draw_text_mut(
&mut image_with_border,
WHITE,
x_text_position as i32,
y_text_position as i32,
scale,
&FONT,
top_text,
);
}
// Calculate the scale and position of the bottom text
{
let scale = Scale::uniform(border_size as f32 / 2f32);
let text_width = get_text_width(scale, &FONT, bottom_text);
let x_text_position = (width - text_width) / 2; // center by width
let y_text_position = height - (border.bottom - padding) / 2;
// Draw the bottom text
draw_text_mut(
&mut image_with_border,
WHITE,
x_text_position as i32,
y_text_position as i32,
scale,
&FONT,
bottom_text,
);
}
image_with_border
}
fn add_border(
image: &RgbImage,
color: Rgb<u8>,
Border {
top,
left,
bottom,
right,
}: Border,
) -> RgbImage {
// Get the dimensions of the image
let (width, height) = image.dimensions();
// Create a new image with a border
#[rustfmt::skip]
let mut out_image = ImageBuffer::from_fn(
width + left + right,
height + top + bottom,
|_, _| color
);
// Copy the original image into the center of the new image
out_image.copy_from(image, top, left).unwrap();
out_image
}
fn add_box(
image: &mut RgbImage,
color: Rgb<u8>,
Border {
mut top,
mut left,
mut bottom,
mut right,
}: Border,
line_width: u32,
padding: u32,
) {
top -= padding;
left -= padding;
bottom -= padding;
right -= padding;
// Get the dimensions of the image
let (width, height) = image.dimensions();
// Calculate the position and dimensions of the box
let x1 = left;
let y1 = top;
let x2 = width - (right + 1);
let y2 = height - (bottom + 1);
// Iterate over the pixels of the line and set the pixel values to color
for x in x1..=x2 {
for i in 0..line_width {
// top line
// +i to go bottom
image.put_pixel(x, y1 + i, color);
// bottom line
// -i to go top
image.put_pixel(x, y2 - i, color);
}
}
// Because we have already painted the corners
for y in (y1 + line_width)..=(y2 - line_width) {
for i in 0..line_width {
// left line
// +i to go right
image.put_pixel(x1 + i, y, color);
// right line
// -i to go left
image.put_pixel(x2 - i, y, color);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use image::io::Reader as ImageReader;
#[test]
fn demotivate_test() {
let image = ImageReader::open("./patterns/test.jpg")
.unwrap()
.decode()
.unwrap()
.into_rgb8();
let demotivated_image = demotivate(&image, "He is crazy!", "RUN");
demotivated_image.save("dem.jpg").unwrap();
}
}

View File

@ -0,0 +1,4 @@
mod demotivate;
mod text_width;
pub use demotivate::demotivate;

View File

@ -0,0 +1,17 @@
use rusttype::{point, Font, Scale};
pub fn get_text_width(scale: Scale, font: &Font, text: &str) -> u32 {
let mut layout = font.layout(text, scale, point(0.0, 0.0));
let min = layout
.next()
.map(|g| g.pixel_bounding_box().unwrap().min.x)
.unwrap();
let max = layout
.last()
.map(|g| g.pixel_bounding_box().unwrap().max.x)
.unwrap();
(max - min) as u32
}