pub mod bezier_fitter;
pub mod edge_detector;
pub mod enhanced_quantizer;
pub mod enhanced_vectorizer;
pub mod despeckle;
pub mod centerline_tracer;
pub mod eps_generator;
pub mod logo_pipeline;
pub mod tune;
pub mod gradient_detector;
pub mod image_filters;
pub mod image_processor;
pub mod path_simplifier;
pub mod preprocessor;
pub mod region_extractor;
pub mod svg_generator;
pub mod vectorizer;
pub use anyhow::Result;
pub use enhanced_vectorizer::{
generate_enhanced_svg, generate_enhanced_svg_to, vectorize_enhanced, write_enhanced_svg,
EnhancedOptions, EnhancedPath, EnhancedVectorData,
};
pub use gradient_detector::{
detect_gradient, detect_linear_gradient, detect_radial_gradient, DetectedGradient,
LinearGradient, RadialGradient,
};
pub use image_filters::{
mean_shift_filter, median_filter, prewitt_edge_detection, quantize_from_labels,
roberts_edge_detection, slic_superpixels, watershed_segment, weighted_median_filter,
AdaptiveThreshold, AdaptiveToneMapper, CannyEdgeDetector, ColorOps, ColorTemperature,
EmbossFilter, GammaCorrection, HighBoostFilter, HistogramEqualizer, LaplacianSharpen,
MorphologyKind, MorphologyOp, NonLocalMeans, OtsuThreshold, PeronaMalik, Saturation,
SepiaFilter, SmartThreshold, UnsharpMask, VignetteFilter, VintageFilter, CLAHE, HSL, LAB,
};
pub use eps_generator::{
write_enhanced_ai, write_enhanced_eps, write_enhanced_output, write_enhanced_pdf,
write_vectorized_eps, EnhancedOutputFormat,
};
pub use image_processor::{
cmyk_to_rgb, detect_color_space, image_data_from_cmyk, load_animated_gif, load_image,
quantize_colors, ColorSpace, GifFrame, ImageData,
};
pub use despeckle::despeckle;
pub use svg_generator::{
generate_svg, generate_svg_advanced, generate_svg_to, write_multi_path_to, write_subpath_to,
};
pub use vectorizer::{vectorize, Curve, Point, VectorizedData};
#[derive(Debug, Clone)]
pub struct ConversionOptions {
pub num_colors: usize,
pub threshold: f64,
pub smooth_level: u8,
pub hierarchical: bool,
pub advanced: bool,
}
impl Default for ConversionOptions {
fn default() -> Self {
Self {
num_colors: 16,
threshold: 0.1,
smooth_level: 5,
hierarchical: false,
advanced: false,
}
}
}
pub fn convert(
input_path: &std::path::Path,
output_path: &std::path::Path,
options: &ConversionOptions,
) -> Result<()> {
let image_data = load_image(input_path)?;
let vectorized_data = vectorize(
&image_data,
options.num_colors,
options.threshold,
options.smooth_level,
options.hierarchical,
)?;
if options.advanced {
generate_svg_advanced(&vectorized_data, output_path)?;
} else {
generate_svg(&vectorized_data, output_path)?;
}
Ok(())
}
pub fn convert_to_svg_string(
image_data: &ImageData,
options: &ConversionOptions,
) -> Result<String> {
let vectorized_data = vectorize(
image_data,
options.num_colors,
options.threshold,
options.smooth_level,
options.hierarchical,
)?;
let mut buffer = Vec::new();
svg_generator::generate_svg_to(&vectorized_data, &mut buffer)?;
Ok(String::from_utf8(buffer)?)
}
pub fn convert_animated_gif(
frames: &[GifFrame],
options: &ConversionOptions,
) -> Result<(Vec<String>, Vec<u32>)> {
let mut svgs = Vec::with_capacity(frames.len());
let mut delays = Vec::with_capacity(frames.len());
for frame in frames {
let svg = convert_to_svg_string(&frame.image_data, options)?;
svgs.push(svg);
delays.push(frame.delay_ms);
}
Ok((svgs, delays))
}
pub fn is_supported_image(path: &std::path::Path) -> bool {
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
matches!(
ext.to_lowercase().as_str(),
"bmp" | "png" | "jpg" | "jpeg" | "gif" | "ico" | "tiff" | "tif" | "webp" | "pnm"
| "tga" | "dds" | "farbfeld"
)
} else {
false
}
}
#[derive(Debug, Clone)]
pub struct BatchConvertEntry {
pub input: std::path::PathBuf,
pub output: std::path::PathBuf,
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone)]
pub struct BatchConvertSummary {
pub total: usize,
pub converted: usize,
pub errors: usize,
pub entries: Vec<BatchConvertEntry>,
}
pub fn batch_convert_enhanced(
input_dir: &std::path::Path,
output_dir: &std::path::Path,
options: &EnhancedOptions,
format: EnhancedOutputFormat,
max_size: u32,
) -> Result<BatchConvertSummary> {
std::fs::create_dir_all(output_dir)?;
let entries: Vec<_> = std::fs::read_dir(input_dir)?
.filter_map(|e| e.ok())
.filter(|e| e.path().is_file() && is_supported_image(&e.path()))
.collect();
let total = entries.len();
let mut converted = 0usize;
let mut errors = 0usize;
let mut results = Vec::with_capacity(total);
for entry in entries {
let input_path = entry.path();
let stem = input_path
.file_stem()
.unwrap_or_default()
.to_string_lossy()
.into_owned();
let mut output_path = output_dir.join(&stem);
output_path.set_extension(format.extension());
let outcome = (|| -> Result<()> {
let image_data = load_image(&input_path)?;
let image_data = image_processor::resize_if_needed(image_data, max_size);
convert_enhanced_data(&image_data, &output_path, options, Some(format))?;
Ok(())
})();
match outcome {
Ok(()) => {
converted += 1;
results.push(BatchConvertEntry {
input: input_path,
output: output_path,
success: true,
error: None,
});
}
Err(e) => {
errors += 1;
results.push(BatchConvertEntry {
input: input_path,
output: output_path,
success: false,
error: Some(e.to_string()),
});
}
}
}
Ok(BatchConvertSummary {
total,
converted,
errors,
entries: results,
})
}
pub fn convert_enhanced_data(
image_data: &ImageData,
output_path: &std::path::Path,
options: &EnhancedOptions,
format: Option<EnhancedOutputFormat>,
) -> Result<EnhancedVectorData> {
let fmt = format.unwrap_or_else(|| EnhancedOutputFormat::from_path(output_path));
let data = vectorize_enhanced(image_data, options)?;
write_enhanced_output(&data, output_path, fmt)?;
Ok(data)
}
pub fn convert_enhanced(
input_path: &std::path::Path,
output_path: &std::path::Path,
options: &EnhancedOptions,
) -> Result<()> {
let image_data = load_image(input_path)?;
convert_enhanced_data(&image_data, output_path, options, None)?;
Ok(())
}
pub fn convert_enhanced_to_svg_string(
image_data: &ImageData,
options: &EnhancedOptions,
) -> Result<String> {
let data = vectorize_enhanced(image_data, options)?;
Ok(generate_enhanced_svg(&data))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_conversion_options_default() {
let options = ConversionOptions::default();
assert_eq!(options.num_colors, 16);
assert_eq!(options.threshold, 0.1);
assert_eq!(options.smooth_level, 5);
assert!(!options.hierarchical);
assert!(!options.advanced);
}
#[test]
fn test_is_supported_image() {
assert!(is_supported_image(std::path::Path::new("a.png")));
assert!(is_supported_image(std::path::Path::new("a.JPG")));
assert!(!is_supported_image(std::path::Path::new("readme.txt")));
}
#[test]
fn test_convert_enhanced_to_svg_string() {
let mut pixels = Vec::new();
for _ in 0..(20 * 20) {
pixels.push(rgb::RGBA8::new(200, 50, 50, 255));
}
let image = ImageData {
width: 20,
height: 20,
pixels,
};
let opts = EnhancedOptions {
num_colors: 4,
preprocess: false,
..Default::default()
};
let svg = convert_enhanced_to_svg_string(&image, &opts).expect("convert");
assert!(svg.contains("<svg"));
assert!(svg.contains("<path") || svg.contains("<rect"));
}
}