Parcourir la source

added svg support

Hvrnbi il y a 3 mois
Parent
commit
c4384d1d5d
4 fichiers modifiés avec 57 ajouts et 3 suppressions
  1. 7 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. 2 1
      README.md
  4. 47 2
      src/lib.rs

+ 7 - 0
Cargo.lock

@@ -694,6 +694,7 @@ dependencies = [
  "edge-detection",
  "fastrand",
  "image",
+ "svg",
 ]
 
 [[package]]
@@ -944,6 +945,12 @@ version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
+[[package]]
+name = "svg"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94afda9cd163c04f6bee8b4bf2501c91548deae308373c436f36aeff3cf3c4a3"
+
 [[package]]
 name = "syn"
 version = "2.0.118"

+ 1 - 0
Cargo.toml

@@ -15,3 +15,4 @@ clap = { version = "4.6.1", features = ["derive"] }
 edge-detection = "0.3.0"
 fastrand = "2.4.1"
 image = "0.25.10"
+svg = "0.18.0"

+ 2 - 1
README.md

@@ -43,7 +43,7 @@ fn main() {
 
 # Features
 
-- BMP, GIF, ICO, JPEG, PNG, WebP, and more format supported (SVG coming soon !)
+- SVG, BMP, GIF, ICO, JPEG, PNG, WebP, and more format supported
 - Custom output resolution supported
 - Custom number of nails supported
 
@@ -58,6 +58,7 @@ It's in french but may be translated.
 
 # Usage advices
 
+I recommend trying to set the output format to SVG, it produces cleaner images because it is vector-based.
 If the output lacks details, you may want to increase the nails count.
 It may take some time (a few minutes) if you try to obtain an image of more than 2000x2000 px and more than 600 nails.
 You can obtain nice results with smaller resolutions.

+ 47 - 2
src/lib.rs

@@ -1,4 +1,5 @@
 use image::{GrayImage, Luma, imageops::FilterType::Nearest};
+use svg::{Document, node::element::Path, node::element::path::Data, node::element::Rectangle};
 
 /** Import a picture, resize it, and convert it to Greyscale */
 fn import_picture(path: &String, new_size: [u32; 2], edge_detection: bool) -> GrayImage {
@@ -243,6 +244,17 @@ pub fn p2sa(src_path: String, output_path: String, new_size: [u32; 2], nails_cou
 
     let mut first_err_red: f32 = 0.;
 
+    let is_svg: bool;
+    let mut svg_data: Data = Data::new();
+
+    if &output_path[output_path.len() - 4..output_path.len()] == ".svg" {
+        is_svg = true;
+        svg_data = svg_data
+            .move_to((start[0], start[1]));
+    } else {
+        is_svg = false;
+    }
+
     while err_rising_cnt < 3 {
         let best_nail_and_err_red: ([u32; 2], f32) = find_best_line(&src_img, &new_img, start, nails.clone());
         let best_nail: [u32; 2] = best_nail_and_err_red.0;
@@ -261,11 +273,44 @@ pub fn p2sa(src_path: String, output_path: String, new_size: [u32; 2], nails_cou
 
         new_img = draw_line(new_img, start, best_nail);
 
+        if is_svg {
+            svg_data = svg_data.line_to((start[0], start[1]));
+        }
+
         start = best_nail;
         lines_cnt += 1;
     }
 
-    println!("{} lines drawn", lines_cnt);
+    svg_data = svg_data.close();
+
+    if is_svg {
+
+        let svg_bg = Rectangle::new()
+            .set("width", new_size[0])
+            .set("height", new_size[1])
+            .set("x", 0)
+            .set("y", 0)
+            .set("fill", "white")
+            .set("stroke", "none");
+
+        let svg_path = Path::new()
+            .set("fill", "none")
+            .set("stroke", "black")
+            .set("stroke-width", 1)
+            .set("stroke-opacity", 0.2)
+            .set("d", svg_data);
+
+        let svg_document = Document::new()
+            .set("viewbox", (0, 0, new_size[0], new_size[1]))
+            .set("width", new_size[0])
+            .set("height", new_size[1])
+            .add(svg_bg)
+            .add(svg_path);
+
+        let _ = svg::save(&output_path, &svg_document);
+    } else {
+        let _ = new_img.save(output_path);
+    }
 
-    let _ = new_img.save(output_path);
+    println!("{} lines drawn", lines_cnt);
 }