refactor: restructure project into a workspace by introducing a plugin API crate and modularizing individual plugin definitions.
Release / check-release (push) Successful in 17s
Release / build (push) Skipped
Release / package (deb) (push) Skipped
Release / package (rpm) (push) Skipped
Release / publish (push) Skipped
CI / test (push) Failing after 2m6s

This commit is contained in:
Elias Wendland
2026-07-17 15:50:50 +02:00
parent 2ea317e117
commit eb76ed1937
43 changed files with 4243 additions and 2296 deletions
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "convertis-image-ascii"
build = "../../plugin-build.rs"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
[lib]
crate-type = ["cdylib"]
[dependencies]
convertis-plugin-api.workspace = true
image.workspace = true
+96
View File
@@ -0,0 +1,96 @@
// Copyright (C) 2026 Elias Wendland <eliaswendland@pm.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 exclusively.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use convertis_plugin_api::{Conversion, ConversionRequest, OptionSpec, Plugin, PluginMetadata};
const FORMATS: &[&str] = &["png", "jpeg", "gif", "webp", "bmp", "tiff", "ico"];
struct ImageAscii;
impl Plugin for ImageAscii {
fn metadata(&self) -> PluginMetadata {
PluginMetadata {
id: "image-ascii".into(),
package: "convertis-image-ascii".into(),
description: "Render images as ASCII text".into(),
conversions: FORMATS
.iter()
.map(|from| Conversion::file(from, "text", (255, 180, 230)))
.collect(),
options: vec![
OptionSpec {
name: "width".into(),
help: "Output width in characters".into(),
default: Some("80".into()),
},
OptionSpec {
name: "characters".into(),
help: "Dark-to-light character ramp".into(),
default: Some("@%#*+=-:. ".into()),
},
OptionSpec {
name: "invert".into(),
help: "Reverse the character ramp".into(),
default: Some("false".into()),
},
],
}
}
fn convert(&self, request: &ConversionRequest) -> Result<(), String> {
let image = image::open(&request.input).map_err(|error| error.to_string())?;
let width: u32 = request
.options
.get("width")
.map(String::as_str)
.unwrap_or("80")
.parse()
.map_err(|_| "width must be a positive integer")?;
if width == 0 {
return Err("width must be greater than zero".into());
}
let height = ((image.height() as f32 / image.width() as f32) * width as f32 * 0.5)
.round()
.max(1.0) as u32;
let grayscale = image
.resize_exact(width, height, image::imageops::FilterType::Triangle)
.to_luma8();
let mut ramp: Vec<char> = request
.options
.get("characters")
.map(String::as_str)
.unwrap_or("@%#*+=-:. ")
.chars()
.collect();
if ramp.len() < 2 {
return Err("characters must contain at least two characters".into());
}
if request
.options
.get("invert")
.is_some_and(|value| value == "true")
{
ramp.reverse();
}
let mut output = String::with_capacity((width as usize + 1) * height as usize);
for y in 0..height {
for x in 0..width {
let value = grayscale.get_pixel(x, y)[0] as usize;
output.push(ramp[value * (ramp.len() - 1) / 255]);
}
output.push('\n');
}
std::fs::write(&request.output, output).map_err(|error| error.to_string())
}
}
convertis_plugin_api::export_plugin!(ImageAscii, "image-ascii");