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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::platform::opengl;
use opengl::context::bindings::types::*;
use opengl::texture::Texture;

use super::FontStyle;

// 0: char
// 1,2: u,v
// 3,4: uv width,height
// 5-8: relative bb
// 9: relative advance width
pub(super) type GlyphMetricsSource =
    (char, f32, f32, f32, f32, f32, f32, f32, f32, f32);
type KerningSource = (char, char, f32);

pub type FontSource<'a> = (
    u8, // texture channel
    &'a [GlyphMetricsSource],
    &'a [KerningSource],
);

/// A source for a font family.
///
/// This is normally created automatically using the crate `suzy_build_tools`.
pub type FontFamilySource = FontFamilySourceDynamic<'static>;

/// A font family which has been loaded.
pub type FontFamily = FontFamilyDynamic<'static>;

/// A source for a font family.
///
/// This is normally created automatically using the crate `suzy_build_tools`.
pub struct FontFamilySourceDynamic<'a> {
    /// The bytes of the SDF font atlas texture.
    pub atlas_image: &'static [u8],

    /// The number of channels in the atlas texture.
    pub image_channels: GLsizei,

    /// The width of the atlas texture.
    pub image_width: u16,

    /// The height of the atlas texture.
    pub image_height: u16,

    /// The alignment of the pixel rows in the atlas texture.
    pub image_row_alignment: u16,

    /// The font source for the normal font.
    pub normal: FontSource<'a>,

    /// The font source for the bold font.
    pub bold: Option<FontSource<'a>>,

    /// The font source for the italic font.
    pub italic: Option<FontSource<'a>>,

    /// The font source for the bold and italic font.
    pub bold_italic: Option<FontSource<'a>>,
}

pub(super) type ChannelMask = (u8, u8, u8, u8);

const ALPHA_MASKS: &[ChannelMask] = &[(0, 0, 0, 0xff)];
const RGB_MASKS: &[ChannelMask] =
    &[(0xff, 0, 0, 0), (0, 0xff, 0, 0), (0, 0, 0xff, 0)];
const RGBA_MASKS: &[ChannelMask] = &[
    (0xff, 0, 0, 0),
    (0, 0xff, 0, 0),
    (0, 0, 0xff, 0),
    (0, 0, 0, 0xff),
];

impl<'a> FontFamilySourceDynamic<'a> {
    /// Load a font family from this source.
    pub fn load(&self) -> FontFamilyDynamic<'a> {
        let (texture, channel_masks) = match self.image_channels {
            1 => {
                let texture = Texture::from_alpha(
                    self.image_width,
                    self.image_height,
                    self.image_row_alignment,
                    self.atlas_image,
                );
                (texture, ALPHA_MASKS)
            }
            3 => {
                let texture = Texture::from_rgb(
                    self.image_width,
                    self.image_height,
                    self.image_row_alignment,
                    self.atlas_image,
                );
                (texture, RGB_MASKS)
            }
            4 => {
                let texture = Texture::from_rgba(
                    self.image_width,
                    self.image_height,
                    self.image_row_alignment,
                    self.atlas_image,
                );
                (texture, RGBA_MASKS)
            }
            _ => panic!(
                concat!(
                    "Invalid number of channels specified ({}). Must be one",
                    " of (1, 3, or 4)"
                ),
                self.image_channels,
            ),
        };
        FontFamilyDynamic {
            texture,
            channel_masks,
            normal: self.normal,
            bold: self.bold,
            italic: self.italic,
            bold_italic: self.bold_italic,
        }
    }
}

/// A font family which has been loaded.
#[derive(Clone)]
pub struct FontFamilyDynamic<'a> {
    pub(super) texture: Texture,
    pub(super) channel_masks: &'static [ChannelMask],
    pub(super) normal: FontSource<'a>,
    pub(super) bold: Option<FontSource<'a>>,
    pub(super) italic: Option<FontSource<'a>>,
    pub(super) bold_italic: Option<FontSource<'a>>,
}

impl<'a> FontFamilyDynamic<'a> {
    pub(super) fn channel_mask(&self, style: FontStyle) -> ChannelMask {
        let index: usize = self.best_font_source(style).0.into();
        self.channel_masks
            .get(index)
            .copied()
            .unwrap_or((0, 0, 0, 0))
    }

    #[doc(hidden)]
    pub fn best_font_source(&self, style: FontStyle) -> &FontSource {
        match style {
            FontStyle::Normal => &self.normal,
            FontStyle::Bold => self.bold.as_ref().unwrap_or(&self.normal),
            FontStyle::Italic => self.italic.as_ref().unwrap_or(&self.normal),
            FontStyle::BoldItalic => self
                .bold_italic
                .as_ref()
                .or_else(|| self.bold.as_ref())
                .or_else(|| self.italic.as_ref())
                .unwrap_or(&self.normal),
        }
    }
}