soblendwa 0.1.0

SoblendWA: a blazing fast WhatsApp Web multi-device client library written in Rust. Crafted by Soblend Development Studio.
Documentation
use std::io::Cursor;

use image::codecs::jpeg::JpegEncoder;
use image::imageops::FilterType;
use image::{DynamicImage, GenericImageView};

use crate::error::{Error, Result};

pub(crate) const THUMBNAIL_WIDTH: u32 = 32;
const PROFILE_PICTURE_SIZE: u32 = 640;

pub(super) fn encode_jpeg(image: &DynamicImage, quality: u8) -> Result<Vec<u8>> {
    let mut out = Cursor::new(Vec::new());
    JpegEncoder::new_with_quality(&mut out, quality)
        .encode_image(&image.to_rgb8())
        .map_err(|err| Error::Media(err.to_string()))?;
    Ok(out.into_inner())
}

fn decode(data: &[u8]) -> Result<DynamicImage> {
    image::load_from_memory(data).map_err(|err| Error::Media(format!("unsupported image: {err}")))
}

pub fn image_dimensions(data: &[u8]) -> Option<(u32, u32)> {
    image::ImageReader::new(Cursor::new(data)).with_guessed_format().ok()?.into_dimensions().ok()
}

pub fn thumbnail(data: &[u8], width: u32) -> Result<Vec<u8>> {
    let image = decode(data)?;
    let (w, h) = image.dimensions();
    let target_w = width.min(w).max(1);
    let target_h = ((u64::from(h) * u64::from(target_w)) / u64::from(w.max(1))).max(1) as u32;
    encode_jpeg(&image.resize_exact(target_w, target_h, FilterType::Triangle), 50)
}

pub fn profile_picture(data: &[u8]) -> Result<Vec<u8>> {
    let image = decode(data)?;
    let (w, h) = image.dimensions();
    let side = w.min(h);
    let square = image.crop_imm((w - side) / 2, (h - side) / 2, side, side);
    let size = side.min(PROFILE_PICTURE_SIZE);
    encode_jpeg(&square.resize_exact(size, size, FilterType::Lanczos3), 80)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn makes_thumbnails_and_square_profile_pictures() {
        let source = DynamicImage::new_rgb8(300, 120);
        let mut png = Cursor::new(Vec::new());
        source.write_to(&mut png, image::ImageFormat::Png).unwrap();
        let png = png.into_inner();
        assert_eq!(image_dimensions(&png), Some((300, 120)));
        let thumb = thumbnail(&png, 32).unwrap();
        assert_eq!(image_dimensions(&thumb), Some((32, 12)));
        let avatar = profile_picture(&png).unwrap();
        assert_eq!(image_dimensions(&avatar), Some((120, 120)));
    }
}