poccariswet / apng

animated png encoder 🦀
MIT License
31 stars 11 forks source link

Rust APNG Creation Slower than JavaScript ? #46

Open WXperia opened 2 weeks ago

WXperia commented 2 weeks ago

Questions:

  1. What could be the reasons for the Rust implementation being slower than the JavaScript one? Are there any specific optimizations I can apply to the Rust code to improve its performance? Could the libraries used in Rust be a factor in the performance difference? Any insights or suggestions would be greatly appreciated! Thank you!

    // Rust Code
    fn create_apng2(input_dir: &str, output_dir: &str, fps: u16) -> Result<(), String> {
    // ... existing code ...
    let mut paths = fs::read_dir(&input_dir)
        .expect("Directory not found")
        .filter_map(Result::ok)
        .map(|entry| entry.path())
        .collect::<Vec<_>>();
    
    paths.sort_by_key(|path| {
        path.file_name()
            .and_then(|name| name.to_str())
            .and_then(|name_str| {
                name_str
                    .chars()
                    .filter(|c| c.is_digit(10))
                    .collect::<String>()
                    .parse::<u32>()
                    .ok()
            })
            .unwrap_or(0)
    });
    
    let mut png_images: Vec<PNGImage> = Vec::new();
    
    for f in paths.iter() {
        match apng::load_png(f.to_str().expect("Invalid path")) {
            Ok(image) => png_images.push(image),
            Err(err) => eprintln!("Failed to load image {}: {}", f.display(), err),
        }
    }
    
    let path = Path::new(&output_dir).join("out.png");
    let file = File::create(&path).expect("Failed to create output file");
    let mut out = BufWriter::new(file);
    
    let config = apng::create_config(&png_images, None).expect("Failed to create config");
    let mut encoder = Encoder::new(&mut out, config).expect("Failed to create encoder");
    
    let frame = Frame {
        delay_num: Some(1),
        delay_den: Some(fps),
        ..Default::default()
    };
    
    match encoder.encode_all(png_images, Some(&frame)) {
        Ok(_n) => println!("success"),
        Err(err) => eprintln!("{}", err),
    };
    
    Ok(())
    }

And here is the JavaScript code using UAPNG.js: https://github.com/photopea/UPNG.js


async function createAPNG(images, outputPath, config) {
    console.log('Creating APNG...')
    const { width, height } = images[0]
    const canvas = createCanvas(width, height);
    const ctx = canvas.getContext('2d');
    const imageBuffers = await Promise.all(images.map(async (img) => {
        ctx.clearRect(0, 0, width, height);
        ctx.drawImage(img, 0, 0);
        return ctx.getImageData(0, 0, width, height).data.buffer;
    }));

    const data = UPNG.encode(imageBuffers, width, height, 0, images.map(() => 1000 / config.createOption.fps));
    const apngpath = path.join(outputPath, 'anime');
    fse.mkdirSync(apngpath);
    const apngFilePath = path.join(apngpath, '/anime.png');
    await fse.promises.writeFile(apngFilePath, new Uint8Array(data));
}
WXperia commented 2 weeks ago

js: image rust: image