Initial repo

This commit is contained in:
Timofey Gelazoniya 2023-01-02 10:31:09 +03:00
commit daed1baafd
Signed by: zeldon
GPG Key ID: 047886915281DD2A
16 changed files with 768 additions and 0 deletions

197
.gitignore vendored Normal file
View File

@ -0,0 +1,197 @@
# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# End of https://www.toptal.com/developers/gitignore/api/node
# Created by https://www.toptal.com/developers/gitignore/api/macos
# Edit at https://www.toptal.com/developers/gitignore?templates=macos
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
# End of https://www.toptal.com/developers/gitignore/api/macos
# Created by https://www.toptal.com/developers/gitignore/api/windows
# Edit at https://www.toptal.com/developers/gitignore?templates=windows
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.toptal.com/developers/gitignore/api/windows
#Added by cargo
/target
Cargo.lock
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
*.node

13
.npmignore Normal file
View File

@ -0,0 +1,13 @@
target
Cargo.lock
.cargo
.github
npm
.eslintrc
.prettierignore
rustfmt.toml
yarn.lock
*.node
.yarn
__test__
renovate.json

2
Cargo.toml Normal file
View File

@ -0,0 +1,2 @@
[workspace]
members = ["crates/*"]

BIN
assets/XO_Thames_Nu.ttf Normal file

Binary file not shown.

View File

@ -0,0 +1,17 @@
[package]
edition = "2021"
name = "bindings"
version = "0.0.0"
[lib]
crate-type = ["cdylib"]
[dependencies]
napi = {version = "2", default-features = false, features = ["napi4"]}
napi-derive = "2"
[build-dependencies]
napi-build = "2"
[profile.release]
lto = true

5
crates/bindings/build.rs Normal file
View File

@ -0,0 +1,5 @@
extern crate napi_build;
fn main() {
napi_build::setup();
}

View File

@ -0,0 +1,8 @@
#![deny(clippy::all)]
use napi_derive::napi;
#[napi]
pub fn sum(a: i32, b: i32) -> i32 {
a + b
}

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
}

256
package-lock.json generated Normal file
View File

@ -0,0 +1,256 @@
{
"name": "node-demotivator-native",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "node-demotivator-native",
"version": "1.0.0",
"license": "MIT",
"devDependencies": {
"@napi-rs/cli": "^2.14.0",
"rimraf": "^3.0.2"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/cli": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.14.0.tgz",
"integrity": "sha512-hQW+gOTQ80nCoBAWA0hq49HM3QqyC7x879CdF/CEEFHeJNlHT8tgru8nbMQa6YqMP1XADfiudsYzy5V7TxBxCw==",
"dev": true,
"bin": {
"napi": "scripts/index.js"
},
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
}
},
"dependencies": {
"@napi-rs/cli": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.14.0.tgz",
"integrity": "sha512-hQW+gOTQ80nCoBAWA0hq49HM3QqyC7x879CdF/CEEFHeJNlHT8tgru8nbMQa6YqMP1XADfiudsYzy5V7TxBxCw==",
"dev": true
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
"glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true
},
"rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
}
}
}

29
package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "node-demotivator-native",
"version": "1.0.0",
"description": "",
"main": "index.js",
"types": "index.d.ts",
"license": "MIT",
"scripts": {
"build": "napi build ./dist --cargo-cwd crates/bindings --platform --release --js ./dist/index.js",
"build:debug": "napi build ./dist --cargo-cwd crates/bindings --platform --js ./dist/index.js",
"clean": "rimraf ./dist && rimraf ./target"
},
"napi": {
"name": "node-demotivator-native",
"defaults": true,
"triples": {
"additional": [
"aarch64-unknown-linux-gnu"
]
}
},
"engines": {
"node": ">= 10"
},
"devDependencies": {
"@napi-rs/cli": "^2.14.0",
"rimraf": "^3.0.2"
}
}

2
rustfmt.toml Normal file
View File

@ -0,0 +1,2 @@
edition = "2021"
tab_spaces = 4