1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::mem::replace;

use bytemuck::cast_slice;
use image::{EncodableLayout, GenericImageView, ImageFormat};

use crate::{Error, Result};

/// Used to build the window to host the ASCII rendering.

pub struct Builder {
    /// The size of the inside of the window (in pixels).
    pub(crate) inner_size: (usize, usize),
    /// The title of the window.
    pub(crate) title: String,
    /// The font used to render the text.
    pub(crate) font: Font,
}

/// Represents the font type used in the window.
pub(crate) enum Font {
    /// Use the built-in font.
    Default,
    /// Use a custom font.
    Custom(FontData),
}

/// Contains the font pixel data for custom fonts.
pub struct FontData {
    pub data: Vec<u32>,
    pub width: u32,
    pub height: u32,
}

//
// Builder implementation
//

impl Builder {
    /// Create a new builder with default settings.
    ///
    /// The default settings will produce a 800x600 window aligned to character
    /// cell size, the title "mterm" and the default built-in font.
    pub fn new() -> Self {
        Builder {
            inner_size: (800, 600),
            title: "mterm".to_string(),
            font: Font::Default,
        }
    }

    /// Set the size of the window when it is created.
    ///
    /// The size given is the number of pixels inside the window's frame.  On
    /// creation the frame size will be reduced so that there are no margins
    /// around the characters.
    pub fn with_inner_size(&mut self, width: usize, height: usize) -> &mut Self {
        self.inner_size = (width, height);
        self
    }

    /// Set the title of the window.
    pub fn with_title(&mut self, title: &str) -> &mut Self {
        self.title = String::from(title);
        self
    }

    /// Choose a font for rendering.
    ///
    /// A `FontData` structure can be created using the `load_font_image`.
    ///
    /// The font image passed in must contain 256 characters on a 16x16 equally
    /// sized grid.  The size of each character is determined by taking the
    /// width and size of the data in `FontData` and dividing it by 16.
    pub fn with_font(&mut self, font: FontData) -> &mut Self {
        self.font = Font::Custom(font);
        self
    }

    /// Finalise the builder and return an instance.
    pub fn build(&mut self) -> Self {
        Builder {
            inner_size: self.inner_size,
            font: replace(&mut self.font, Font::Default),
            title: self.title.clone(),
        }
    }
}

/// Load a font from a given image in a byte array and generate a FontData
/// structure.
///
/// # Arguments
///
/// * __data__ - byte array that contains the image data.  You can use the
///   `include_bytes!` macro to generate this from a file at compile time.
/// * __format__ - The image::ImageFormat enum that declares the file format the
///   image data is in.
///
/// # Notes
///
/// This function will assume that the image contains 256 characters in a 16x16
/// grid of equally sized cells.

pub fn load_font_image(data: &[u8], format: ImageFormat) -> Result<FontData> {
    let font_image =
        image::load_from_memory_with_format(data, format).map_err(|_| Error::BadFont)?;
    let dimensions = font_image.dimensions();
    let font_rgba = font_image.to_rgba8();
    let font_data = font_rgba.as_bytes();
    let data_u32: &[u32] = cast_slice(font_data);
    let char_width = dimensions.0 / 16;
    let char_height = dimensions.1 / 16;
    if char_width == 0 || char_height == 0 {
        return Err(Error::BadFont);
    }

    Ok(FontData {
        width: char_width,
        height: char_height,
        data: Vec::from(data_u32),
    })
}