wayk_core 0.2.2

Provides data structures and other utilities used by Wayk products not specific to protocol itself
Documentation
use gfwx::{Encoder, Filter, Header, Intent, Quantization};
use num_derive::FromPrimitive;
use wayk_proto_derive::{Decode, Encode};

#[derive(Debug, Clone)]
pub struct NowGfwxParams {
    pub version: u32,
    pub layers: u16,
    pub channels: u16,
    pub bit_depth: u8,
    pub is_signed: bool,
    pub intent: Intent,
    pub metadata_size: u32,
}

#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, FromPrimitive)]
#[repr(u8)]
pub enum NowGWFXFilter {
    Linear = 0,
    Cubic = 1,
}

#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, FromPrimitive)]
#[repr(u8)]
pub enum NowGWFXQuantization {
    Scalar = 0,
}

#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, FromPrimitive)]
#[repr(u8)]
pub enum NowGWFXEncoder {
    Turbo = 0,
    Fast = 1,
    Contextual = 2,
}

#[derive(Encode, Decode, Clone, Debug)]
pub struct NowGfwxHeader {
    pub magic: u32,
    pub version: u8,
    pub offset: u8,
    pub flags: u16,

    pub quality_level: u16,
    pub chroma_scale: u8,
    pub block_size: u8,
    pub filter: NowGWFXFilter,
    pub quantization: NowGWFXQuantization,
    pub encoder: NowGWFXEncoder,
    pub boost: u8,

    pub image_width: u32,
    pub image_height: u32,
    pub layer_count: u16,
    pub channel_count: u16,

    pub transform: u32,
    pub color_flags: u32,
}

impl From<NowGWFXFilter> for Filter {
    fn from(now_gfwx_filter: NowGWFXFilter) -> Filter {
        match now_gfwx_filter {
            NowGWFXFilter::Linear => Filter::Linear,
            NowGWFXFilter::Cubic => Filter::Cubic,
        }
    }
}

impl From<NowGWFXQuantization> for Quantization {
    fn from(now_gfwx_quantization: NowGWFXQuantization) -> Quantization {
        match now_gfwx_quantization {
            NowGWFXQuantization::Scalar => Quantization::Scalar,
        }
    }
}

impl From<NowGWFXEncoder> for Encoder {
    fn from(now_gfwx_encoder: NowGWFXEncoder) -> Encoder {
        match now_gfwx_encoder {
            NowGWFXEncoder::Turbo => Encoder::Turbo,
            NowGWFXEncoder::Fast => Encoder::Fast,
            NowGWFXEncoder::Contextual => Encoder::Contextual,
        }
    }
}

impl NowGfwxHeader {
    pub fn to_gfwx_header_with_params(&self, params: NowGfwxParams) -> Header {
        Header {
            version: params.version,
            width: self.image_width,
            height: self.image_height,
            layers: params.layers,
            channels: params.channels,
            bit_depth: params.bit_depth,
            is_signed: params.is_signed,
            quality: self.quality_level,
            chroma_scale: self.chroma_scale,
            block_size: self.block_size,
            filter: Filter::from(self.filter),
            quantization: Quantization::from(self.quantization),
            encoder: Encoder::from(self.encoder),
            intent: params.intent,
            metadata_size: params.metadata_size,
            channel_size: self.image_width as usize * self.image_height as usize,
            image_size: self.image_width as usize
                * self.image_height as usize
                * params.layers as usize
                * params.channels as usize,
        }
    }
}

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

    const ENCODED_NOW_GFWX_HEADER: [u8; 36] = [
        0x47, 0x46, 0x57, 0x58, 0x02, 0x24, 0x00, 0x00, 0x40, 0x00, 0x04, 0x07, 0x00, 0x00, 0x01, 0x08, 0x24, 0x01,
        0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x41, 0x37, 0x31, 0x31, 0x00, 0x00, 0x00, 0x00,
    ];

    const EXPECTED_NOW_GFWX_HEADER: NowGfwxHeader = NowGfwxHeader {
        magic: 1482114631,
        version: 2,
        offset: 36,
        flags: 0,
        quality_level: 64,
        chroma_scale: 4,
        block_size: 7,
        filter: NowGWFXFilter::Linear,
        quantization: NowGWFXQuantization::Scalar,
        encoder: NowGWFXEncoder::Fast,
        boost: 8,
        image_width: 292,
        image_height: 40,
        layer_count: 1,
        channel_count: 3,
        transform: 825308993,
        color_flags: 0,
    };

    const NOW_GFWX_PARAMS: NowGfwxParams = NowGfwxParams {
        version: 1,
        layers: 1,
        channels: 3,
        bit_depth: 8,
        is_signed: false,
        intent: Intent::RGB,
        metadata_size: 0,
    };

    const HEADER: Header = Header {
        version: NOW_GFWX_PARAMS.version,
        width: EXPECTED_NOW_GFWX_HEADER.image_width,
        height: EXPECTED_NOW_GFWX_HEADER.image_height,
        layers: NOW_GFWX_PARAMS.layers,
        channels: NOW_GFWX_PARAMS.channels,
        bit_depth: NOW_GFWX_PARAMS.bit_depth,
        is_signed: NOW_GFWX_PARAMS.is_signed,
        quality: EXPECTED_NOW_GFWX_HEADER.quality_level,
        chroma_scale: EXPECTED_NOW_GFWX_HEADER.chroma_scale,
        block_size: EXPECTED_NOW_GFWX_HEADER.block_size,
        filter: Filter::Linear,
        quantization: Quantization::Scalar,
        encoder: Encoder::Fast,
        intent: NOW_GFWX_PARAMS.intent,
        metadata_size: NOW_GFWX_PARAMS.metadata_size,
        channel_size: EXPECTED_NOW_GFWX_HEADER.image_width as usize * EXPECTED_NOW_GFWX_HEADER.image_height as usize,
        image_size: EXPECTED_NOW_GFWX_HEADER.image_width as usize
            * EXPECTED_NOW_GFWX_HEADER.image_height as usize
            * NOW_GFWX_PARAMS.layers as usize
            * NOW_GFWX_PARAMS.channels as usize,
    };

    #[test]
    fn now_gfwx_header_decode() {
        let now_gfwx_header = NowGfwxHeader::decode(&ENCODED_NOW_GFWX_HEADER).unwrap();

        assert_eq!(EXPECTED_NOW_GFWX_HEADER.magic, now_gfwx_header.magic);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.version, now_gfwx_header.version);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.offset, now_gfwx_header.offset);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.flags, now_gfwx_header.flags);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.quality_level, now_gfwx_header.quality_level);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.chroma_scale, now_gfwx_header.chroma_scale);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.block_size, now_gfwx_header.block_size);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.filter, now_gfwx_header.filter);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.quantization, now_gfwx_header.quantization);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.encoder, now_gfwx_header.encoder);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.boost, now_gfwx_header.boost);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.image_width, now_gfwx_header.image_width);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.image_height, now_gfwx_header.image_height);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.layer_count, now_gfwx_header.layer_count);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.channel_count, now_gfwx_header.channel_count);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.transform, now_gfwx_header.transform);
        assert_eq!(EXPECTED_NOW_GFWX_HEADER.color_flags, now_gfwx_header.color_flags);
    }

    #[test]
    fn header_decode() {
        let header = EXPECTED_NOW_GFWX_HEADER.to_gfwx_header_with_params(NOW_GFWX_PARAMS);

        assert_eq!(header.version, HEADER.version);
        assert_eq!(header.width, HEADER.width);
        assert_eq!(header.height, HEADER.height);
        assert_eq!(header.layers, HEADER.layers);
        assert_eq!(header.channels, HEADER.channels);
        assert_eq!(header.bit_depth, HEADER.bit_depth);
        assert_eq!(header.is_signed, HEADER.is_signed);
        assert_eq!(header.quality, HEADER.quality);
        assert_eq!(header.chroma_scale, HEADER.chroma_scale);
        assert_eq!(header.block_size, HEADER.block_size);
        assert_eq!(header.filter, HEADER.filter);
        assert_eq!(header.quantization, HEADER.quantization);
        assert_eq!(header.encoder, HEADER.encoder);
        assert_eq!(header.intent, HEADER.intent);
        assert_eq!(header.metadata_size, HEADER.metadata_size);
        assert_eq!(header.channel_size, HEADER.channel_size);
        assert_eq!(header.image_size, HEADER.image_size);
    }
}