icu_lib 0.6.0

Image Converter Ultra Library
Documentation
use crate::endecoder::lvgl::color_converter::{extract_indexed, rgba8888_from, rgba8888_to};
use crate::endecoder::lvgl::{
    has_flag, with_flag, Compress, Flags, HeaderFlag, ImageCompressedHeader, ImageDescriptor,
    ImageHeader, LVGLVersion, LVGL,
};
use crate::endecoder::{EnDecoder, ImageInfo};
use crate::midata::{IndexedImageData, MiData};
use crate::EncoderParams;
use image::imageops;
use image::RgbaImage;
use serde_json::{json, Value};
use std::io::{Cursor, Write};

impl EnDecoder for LVGL {
    fn can_decode(&self, data: &[u8]) -> bool {
        let header_size = size_of::<ImageHeader>();
        if data.len() < header_size {
            return false;
        }

        let header_data = &data[..header_size];

        let header = ImageHeader::decode(Vec::from(header_data));
        header.version() != LVGLVersion::Unknown
    }

    fn encode(&self, data: &MiData, encoder_params: EncoderParams) -> Vec<u8> {
        let color_format: crate::endecoder::lvgl::ColorFormat = encoder_params.color_format.into();
        if color_format == crate::endecoder::lvgl::ColorFormat::UNKNOWN {
            return Vec::new();
        }

        match data {
            MiData::RGBA(img) => {
                let stride = color_format.get_stride_size(img.width(), encoder_params.stride_align);
                let mut img_data = img.clone();

                if let Some(dither) = encoder_params.dither {
                    let cmap = color_quant::NeuQuant::new(dither as i32, 256, img_data.as_mut());
                    imageops::dither(&mut img_data, &cmap);
                }

                let mut img_data = rgba8888_to(
                    img_data.as_mut(),
                    color_format,
                    img.width(),
                    img.height(),
                    stride,
                    encoder_params.dither,
                );

                let mut flags = Flags::from(0u16);

                match encoder_params.compress {
                    Compress::NONE => {}
                    Compress::Rle => {
                        use super::super::utils::rle::RleCoder;
                        let blk_size = ((color_format.get_bpp() + 7) >> 3) as usize;
                        let rle_coder = RleCoder::new().with_block_size(blk_size).unwrap();
                        let mut compressed_data = match rle_coder.encode(&img_data) {
                            Ok(data) => data,
                            Err(err) => {
                                log::error!("RLE encoding failed: {err:?}");
                                return vec![];
                            }
                        };

                        let image_compressed_header = ImageCompressedHeader::new()
                            .with_method(encoder_params.compress)
                            .with_compressed_size(compressed_data.len() as u32)
                            .with_decompressed_size(img_data.len() as u32);
                        let mut ich_vec = image_compressed_header.into_bytes().to_vec();
                        ich_vec.append(&mut compressed_data);

                        img_data = ich_vec;
                        flags = with_flag(flags, HeaderFlag::COMPRESSED);
                    }
                    _ => {}
                }

                let mut buf = Cursor::new(Vec::new());
                buf.write_all(
                    &ImageDescriptor::new(
                        ImageHeader::new(
                            encoder_params.lvgl_version,
                            color_format,
                            flags,
                            img.width() as u16,
                            img.height() as u16,
                            stride as u16,
                        ),
                        img_data,
                    )
                    .encode(),
                )
                .unwrap();

                buf.into_inner()
            }
            _ => Vec::new(),
        }
    }

    fn decode(&self, data: Vec<u8>) -> MiData {
        log::trace!("Decoding image with data size: {}", data.len());
        let img_desc = ImageDescriptor::decode(data);

        if img_desc.data_size == 0 {
            return MiData::RGBA(RgbaImage::new(0, 0));
        }

        let header = &img_desc.header;

        log::trace!("Decoding image with color format: {:?}", header.cf());
        log::trace!("Decoded image header: {:#?}", img_desc.header);
        log::trace!("Converting image data to RGBA");

        let cf = header.cf();
        let w = header.w() as u32;
        let h = header.h() as u32;
        let stride = header.stride() as u32;

        if let Some((palette, indexes, bpp)) = extract_indexed(&img_desc.data, cf, w, h, stride) {
            let rgba_buf = rgba8888_from(&img_desc.data, cf, w, h, stride);
            let rgba = RgbaImage::from_vec(w, h, rgba_buf).unwrap_or_else(|| RgbaImage::new(0, 0));
            return MiData::INDEXED(IndexedImageData {
                rgba,
                palette,
                indexes,
                bpp,
                width: w,
                height: h,
            });
        }

        let img_buffer =
            RgbaImage::from_vec(w, h, rgba8888_from(&img_desc.data, cf, w, h, stride)).unwrap();

        log::trace!("Converted image data to RGBA");
        log::trace!(
            "Decoded image with size: {}x{}",
            img_buffer.width(),
            img_buffer.height()
        );
        log::trace!("Creating MiData object with RGBA image data and returning it");

        MiData::RGBA(img_buffer)
    }

    fn info(&self, data: &[u8]) -> ImageInfo {
        let header_size = size_of::<ImageHeader>();

        let header_data = &data[..header_size];

        let header = ImageHeader::decode(Vec::from(header_data));

        let mut other_info = serde_json::Map::new();

        other_info.insert(
            "LVGL Version".to_string(),
            Value::from(format!("{:#?}", header.version())),
        );
        other_info.insert(
            "Color Format".to_string(),
            Value::from(format!("{:#?}", header.cf())),
        );
        other_info.insert(
            "Flags".to_string(),
            Value::from(format!("{:#?}", header.flags())),
        );
        if header.version() == LVGLVersion::V9 {
            other_info.insert("Stride".to_string(), Value::from(header.stride()));
        }

        // Deal Flag has Compressed
        if has_flag(header.flags(), HeaderFlag::COMPRESSED) {
            let data = &data[header.header_size()..];
            let compressed_header = ImageCompressedHeader::from_bytes([
                data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
                data[9], data[10], data[11],
            ]);

            other_info.insert(
                "Compressed Info".to_owned(),
                json!({
                    "Method": format!("{:#?}", compressed_header.method()),
                    "Size": compressed_header.compressed_size(),
                    "Decompressed Size": compressed_header.decompressed_size()
                }),
            );
        }

        ImageInfo {
            width: header.w() as u32,
            height: header.h() as u32,
            data_size: data.len() as u32,
            format: format!("LVGL.{:?}({:?})", header.version(), header.cf()),
            other_info: Value::from(other_info),
        }
    }
}