Skip to main content

inset_embedder/
cue.rs

1//! Subtitles and captions as a player hands them to the app: Media3's `Cue`, field for field, since
2//! Media3 turns every subtitle format it reads into one, and each system's player gives the same
3//! as data (Apple's legible output, Windows' timed text cues). A cue carries the media's own
4//! styling alone, as Media3's does and as Apple's legible output gives it at its
5//! `SourceAndRulesOnly` styling: a field the media says nothing of is `None`, and the app fills it
6//! in from the user's caption style ([`CaptionStyle`](crate::CaptionStyle)) as it draws the cue.
7//!
8//! Sizes and places are fractions of the picture the cue is drawn over, so a cue reads the same
9//! at any size the picture is shown at.
10
11use crate::Color;
12
13/// One subtitle or caption to show: Media3's `Cue`.
14#[derive(Clone, Debug, PartialEq)]
15pub struct Cue {
16    pub content: CueContent,
17    /// Where its box sits from the top: a fraction of the picture's height, or a line number;
18    /// `None` for the default, the bottom.
19    pub line: Option<CueLine>,
20    /// Which side of its box `line` places.
21    pub line_anchor: CueAnchor,
22    /// Where its box sits across, as a fraction of the picture's width; `None` for the default,
23    /// the centre.
24    pub position: Option<f32>,
25    /// Which side of its box `position` places.
26    pub position_anchor: CueAnchor,
27    /// Its box's width as a fraction of the picture's; `None` to fit what it holds.
28    pub size: Option<f32>,
29    pub text_alignment: CueTextAlignment,
30    /// Whether its text runs in columns, and which way they follow each other; `None` for rows.
31    pub vertical: Option<CueVertical>,
32    /// The colour of the box behind it; `None` where the media says none, Media3's
33    /// `windowColorSet` false.
34    pub window_color: Option<Color>,
35}
36
37impl Cue {
38    /// A cue showing `content` where Media3's `Cue.Builder` puts one by default: centred at the
39    /// bottom, its text centred, its box's colour unsaid.
40    pub fn new(content: CueContent) -> Cue {
41        Cue {
42            content,
43            line: None,
44            line_anchor: CueAnchor::Start,
45            position: None,
46            position_anchor: CueAnchor::Start,
47            size: None,
48            text_alignment: CueTextAlignment::Center,
49            vertical: None,
50            window_color: None,
51        }
52    }
53}
54
55/// What a cue shows.
56#[derive(Clone, Debug, PartialEq)]
57pub enum CueContent {
58    /// Its text, in runs of one style.
59    Text(Vec<CueTextRun>),
60    /// A picture subtitle, as Blu-ray, DVD and DVB carry them, decoded.
61    Image(CueImage),
62}
63
64/// Text of one style. Each field the media may leave unsaid is `None` where it does.
65#[derive(Clone, Debug, PartialEq)]
66pub struct CueTextRun {
67    pub text: String,
68    pub bold: bool,
69    pub italic: bool,
70    pub underline: bool,
71    pub color: Option<Color>,
72    /// The colour behind these characters.
73    pub background: Option<Color>,
74    /// The text's height as a fraction of the picture's.
75    pub font_size: Option<f32>,
76    pub font_family: Option<String>,
77    pub edge: Option<CueTextEdge>,
78    pub edge_color: Option<Color>,
79}
80
81impl CueTextRun {
82    /// The height Media3's `SubtitleView` gives text by default, `DEFAULT_TEXT_SIZE_FRACTION`: a
83    /// fraction of the picture's.
84    pub const DEFAULT_FONT_SIZE: f32 = 0.0533;
85
86    /// `text` in no style of its own: neither bold, italic nor underlined, and every other field
87    /// unsaid.
88    pub fn new(text: impl Into<String>) -> CueTextRun {
89        CueTextRun {
90            text: text.into(),
91            bold: false,
92            italic: false,
93            underline: false,
94            color: None,
95            background: None,
96            font_size: None,
97            font_family: None,
98            edge: None,
99            edge_color: None,
100        }
101    }
102}
103
104/// A picture subtitle's pixels.
105#[derive(Clone, Debug, PartialEq)]
106pub struct CueImage {
107    pub width: u32,
108    pub height: u32,
109    /// RGBA, 8 bits a channel, unpremultiplied, rows from the top.
110    pub pixels: Vec<u8>,
111    /// Its height on screen as a fraction of the picture's; its width is the cue's `size`.
112    pub display_height: f32,
113}
114
115/// Where a cue's box sits from the top.
116#[derive(Clone, Copy, Debug, PartialEq)]
117pub enum CueLine {
118    /// A fraction of the picture's height.
119    Fraction(f32),
120    /// A line number: from the top counting from 0, from the bottom counting from -1, in lines
121    /// of the cue's first line's height.
122    Number(i32),
123}
124
125/// Which side of a cue's box its `line` or `position` places.
126#[derive(Clone, Copy, Debug, PartialEq, Eq)]
127pub enum CueAnchor {
128    Start,
129    Middle,
130    End,
131}
132
133/// How a cue's lines align in its box.
134#[derive(Clone, Copy, Debug, PartialEq, Eq)]
135pub enum CueTextAlignment {
136    Start,
137    Center,
138    End,
139}
140
141/// Which way a vertical cue's columns follow each other.
142#[derive(Clone, Copy, Debug, PartialEq, Eq)]
143pub enum CueVertical {
144    RightToLeft,
145    LeftToRight,
146}
147
148/// What is drawn round a run's characters to set them off from the picture.
149#[derive(Clone, Copy, Debug, PartialEq, Eq)]
150pub enum CueTextEdge {
151    None,
152    Outline,
153    DropShadow,
154    Raised,
155    Depressed,
156}