소스 검색

added svg support

Hvrnbi 3 달 전
부모
커밋
c4384d1d5d
4개의 변경된 파일과 57개의 추가작업 그리고 3개의 파일을 삭제
  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);
 }