yds12 / tarsila

Pixel art and spritesheet editor
Other
141 stars 7 forks source link

Exporting to JPEG causing artifacts #5

Closed quiet-bear closed 1 year ago

quiet-bear commented 1 year ago

OS: Windows 11

Exporting to JPEG artifacts the image (doing what JPEG does best). However, in the case of pixel art, I don't believe the compression should be this bad. Since there's no option for setting compression level, JPEG doesn't seem viable for smaller images at the moment. It might be good to set compression to the lowest it can go and revisit whenever there's a dedicated menu for tweaking export settings.

Imports don't seem to be affected.

Example Example-JPEG

Steps to reproduce:

yds12 commented 1 year ago

I can confirm the same behaviour on Linux. I think the image crate that we use to save images has some default compression level. The quick solution would be to set compression to the lowest, if this setting is available in image. A more long-term solution would be to have a window where you can set the JPEG compression level when exporting.

quiet-bear commented 1 year ago

The image crate does have a compression quality that is defaulted to 75 on a scale from worst quality (1) to best (100). I took a look at this issue and it seems that the best way to have fine control over the encoder is to use it directly:

pub fn save_image<IMG: Bitmap>(bitmap: IMG, path: &str) {
    let bytes = bitmap.bytes();
    let width = bitmap.width() as u32;
    let height = bitmap.height() as u32;
    let color = image::ColorType::Rgba8;

    let file = std::fs::File::create(path).expect("Failed to create file from path");
    let buffer = std::io::BufWriter::new(file);
    let result = match ImageFormat::from_path(path)
        .unwrap_or(ImageFormat::Png)
        .into()
    {
        ImageOutputFormat::Png => {
            codecs::png::PngEncoder::new(buffer).write_image(bytes, width, height, color)
        }
        ImageOutputFormat::Jpeg(_) => codecs::jpeg::JpegEncoder::new_with_quality(buffer, 100)
            .write_image(bytes, width, height, color),
        _ => return, // Format not supported
    };

    result.expect("Failed to save image");
}

This does resolve JPEG compression by calling new_with_quality and bypassing the default quality. It might also be preferable in the future to have direct access to the encoders anyways.

yds12 commented 1 year ago

Looks good to me, it's a pretty minimal change.

quiet-bear commented 1 year ago

I'll get another PR created in a bit here.