kobo-core 0.2.3

Kobo e-reader device SDK: device database, sysfs/ioctl, rendering, audio pipeline, trait surface
Documentation
pub mod blit;
pub mod fonts;

pub use blit::*;
pub use fonts::*;

const FONT_LATIN: &[u8] = include_bytes!("../../fonts/NotoSansLatin.ttf");

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Script {
    Latin,
    Bengali,
    Devanagari,
    Arabic,
    Hebrew,
    Cjk,
    Thai,
    Other,
}

impl Script {
    pub fn is_rtl(self) -> bool {
        matches!(self, Script::Arabic | Script::Hebrew)
    }

    pub fn uses_word_spacing(self) -> bool {
        !matches!(self, Script::Cjk | Script::Thai)
    }

    pub fn lang_tag(self) -> &'static str {
        match self {
            Script::Latin => "en-US",
            Script::Bengali => "bn-BD",
            Script::Devanagari => "hi-IN",
            Script::Arabic => "ar-SA",
            Script::Hebrew => "he-IL",
            Script::Cjk => "ja-JP",
            Script::Thai => "th-TH",
            Script::Other => "en-US",
        }
    }
}

pub fn detect_script(text: &str) -> Script {
    for c in text.chars() {
        if !c.is_alphabetic() {
            continue;
        }
        let code = c as u32;
        return match code {
            0x0980..=0x09FF => Script::Bengali,
            0x0900..=0x097F => Script::Devanagari,
            0x0600..=0x06FF | 0x0750..=0x077F | 0xFB50..=0xFDFF | 0xFE70..=0xFEFF => Script::Arabic,
            0x0590..=0x05FF => Script::Hebrew,
            0x3040..=0x30FF | 0x4E00..=0x9FFF | 0x3400..=0x4DBF | 0xAC00..=0xD7AF => Script::Cjk,
            0x0E00..=0x0E7F => Script::Thai,
            0x0000..=0x024F => Script::Latin,
            _ => Script::Other,
        };
    }
    Script::Latin
}

static WIDTH_CACHE: std::sync::OnceLock<
    std::sync::Mutex<std::collections::HashMap<(String, u32), f32>>,
> = std::sync::OnceLock::new();

pub(crate) fn width_cache(
) -> &'static std::sync::Mutex<std::collections::HashMap<(String, u32), f32>> {
    WIDTH_CACHE.get_or_init(std::sync::Mutex::default)
}

#[allow(dead_code)]
pub fn clear_width_cache() {
    if let Ok(mut cache) = width_cache().lock() {
        cache.clear();
    }
}

pub struct DecodedImage {
    pub rgb: Vec<u8>,
    pub width: usize,
    pub height: usize,
}

pub fn decode_image(raw: &[u8], max_w: usize, max_h: usize) -> Option<DecodedImage> {
    let img = image::load_from_memory(raw).ok()?;
    let (ow, oh) = (img.width() as usize, img.height() as usize);
    if ow == 0 || oh == 0 {
        return None;
    }
    let scale = max_w as f32 / ow as f32;
    let mut nw = max_w;
    let mut nh = (oh as f32 * scale).round() as usize;
    if nh == 0 {
        return None;
    }
    if nh > max_h {
        let hscale = max_h as f32 / nh as f32;
        nh = max_h;
        nw = (nw as f32 * hscale).round() as usize;
        if nw == 0 {
            return None;
        }
    }
    let resized = img.resize(nw as u32, nh as u32, image::imageops::FilterType::Triangle);
    let rgb = resized.to_rgb8();
    let (rw, rh) = (rgb.width() as usize, rgb.height() as usize);
    Some(DecodedImage {
        rgb: rgb.into_raw(),
        width: rw,
        height: rh,
    })
}

pub fn blit_rgb565_image(
    buf: &mut [u8],
    buf_stride: usize,
    rgb: &[u8],
    iw: usize,
    ih: usize,
    ox: usize,
    oy: usize,
    max_w: usize,
    max_h: usize,
) {
    for ry in 0..ih {
        let py = oy + ry;
        if py >= max_h {
            break;
        }
        for rx in 0..iw {
            let px = ox + rx;
            if px >= max_w {
                break;
            }
            let idx = (ry * iw + rx) * 3;
            let r = rgb[idx] as u16;
            let g = rgb[idx + 1] as u16;
            let b = rgb[idx + 2] as u16;
            let r5 = (r >> 3) & 0x1f;
            let g6 = (g >> 2) & 0x3f;
            let b5 = (b >> 3) & 0x1f;
            let v = (r5 << 11) | (g6 << 5) | b5;
            let off = (py * buf_stride + px) * 2;
            if off + 2 > buf.len() {
                continue;
            }
            buf[off] = (v & 0xff) as u8;
            buf[off + 1] = (v >> 8) as u8;
        }
    }
}

#[cfg(test)]
mod tests;