mc_map2png/
lib.rs

1//! Module for processing image files and creating images from color data.
2
3mod color_map_utils;
4
5use std::fs::File;
6use std::io::Read;
7use std::io::Cursor;
8use flate2::read::GzDecoder;
9use image::{ImageBuffer, Rgb, RgbImage};
10use color_map_utils::map_block_color;
11use color_map_utils::extract_colors_data;
12use wasm_bindgen::prelude::*;
13
14/// Processes an image file, extracts color data, and saves the resulting image.
15///
16/// # Arguments
17///
18/// * `input_path` - The path to the input image file.
19/// * `output_path` - The path where the resulting image will be saved.
20///
21/// # Returns
22///
23/// A `Result` indicating success or failure. If successful, returns `Ok(())`.
24/// If an error occurs, returns `Err` with an error message.
25pub fn process_image_file(input_path: &str, output_path: &str) -> Result<(), String> {
26    let file = File::open(input_path).map_err(|_| "Failed to open input file")?;
27    let mut decoder = GzDecoder::new(file);
28    let mut buffer = Vec::new();
29    decoder.read_to_end(&mut buffer).map_err(|_| "Failed to read compressed data")?;
30    let value = fastnbt::from_bytes(&buffer).map_err(|_| "Failed to parse NBT data")?;
31    println!("value: {:?}", value);
32    let color_data = extract_colors_data(&value).ok_or("No color data found in NBT")?;
33    create_and_save_image(&color_data, output_path)
34}
35
36/// Creates an image from color data and saves it to the specified output path.
37///
38/// # Arguments
39///
40/// * `color_data` - A slice of bytes representing the color data.
41/// * `output_path` - The path where the resulting image will be saved.
42///
43/// # Returns
44///
45/// A `Result` indicating success or failure. If successful, returns `Ok(())`.
46/// If an error occurs, returns `Err` with an error message.
47pub fn create_and_save_image(color_data: &[u8], output_path: &str) -> Result<(), String> {
48    let image_size = compute_image_size(color_data.len())?;
49    let image = build_image(color_data, image_size, image_size);
50    image.save(output_path).map_err(|_| "Failed to save image".to_string())
51}
52
53/// Computes the size of the image based on the length of the color data.
54///
55/// # Arguments
56///
57/// * `data_length` - The length of the color data.
58///
59/// # Returns
60///
61/// A `Result` containing the computed image size if the data length is a perfect square.
62/// If the data length is not a perfect square, returns an `Err` with an error message.
63fn compute_image_size(data_length: usize) -> Result<usize, String> {
64    let side_length = (data_length as f64).sqrt() as usize;
65    if side_length * side_length != data_length {
66        Err("Data does not contain a perfect square of pixels.".into())
67    } else {
68        Ok(side_length)
69    }
70}
71
72/// Builds an RGB image from the provided color data.
73///
74/// # Arguments
75///
76/// * `data` - A slice of bytes representing the color data.
77/// * `width` - The width of the resulting image.
78/// * `height` - The height of the resulting image.
79///
80/// # Returns
81///
82/// An `RgbImage` built from the color data.
83fn build_image(data: &[u8], width: usize, height: usize) -> RgbImage {
84    let mut image: RgbImage = ImageBuffer::new(width as u32, height as u32);
85    for (y, row) in data.chunks(width).enumerate() {
86        for (x, &block_id) in row.iter().enumerate() {
87            let color = map_block_color(block_id);
88            image.put_pixel(x as u32, y as u32, Rgb([color.0, color.1, color.2]));
89        }
90    }
91    image
92}
93
94/// Processes an image from memory, extracts color data, and returns the resulting image bytes.
95///
96/// # Arguments
97///
98/// * `input_data` - A slice of bytes representing the input image data.
99///
100/// # Returns
101///
102/// A `Result` containing a `Vec<u8>` of the resulting image bytes if successful.
103/// If an error occurs, returns a `JsValue` with an error message.
104#[wasm_bindgen]
105pub fn process_image_from_memory(input_data: &[u8]) -> Result<Vec<u8>, JsValue> {
106    let mut decoder = GzDecoder::new(input_data);
107    let mut buffer = Vec::new();
108    decoder.read_to_end(&mut buffer).map_err(|_| JsValue::from_str("Failed to read compressed data"))?;
109    let value = fastnbt::from_bytes(&buffer).map_err(|_| JsValue::from_str("Failed to parse NBT data"))?;
110    let color_data = extract_colors_data(&value).ok_or(JsValue::from_str("No color data found in NBT"))?;
111    create_image_bytes(&color_data)
112}
113
114/// Creates image bytes from the provided color data.
115///
116/// # Arguments
117///
118/// * `color_data` - A slice of bytes representing the color data.
119///
120/// # Returns
121///
122/// A `Result` containing a `Vec<u8>` of the resulting image bytes if successful.
123/// If an error occurs, returns a `JsValue` with an error message.
124fn create_image_bytes(color_data: &[u8]) -> Result<Vec<u8>, JsValue> {
125    let image_size = compute_image_size(color_data.len()).map_err(|e| JsValue::from_str(&e))?;
126    let image = build_image(color_data, image_size, image_size);
127    let mut image_bytes = Vec::new();
128    let mut cursor = Cursor::new(&mut image_bytes);
129    image.write_to(&mut cursor, image::ImageOutputFormat::Png).map_err(|_| JsValue::from_str("Failed to write image to buffer"))?;
130    Ok(image_bytes)
131}