Skip to content

Commit

Permalink
Merge branch 'subtitles-overhaul'
Browse files Browse the repository at this point in the history
  • Loading branch information
Cobrand committed Jul 22, 2016
2 parents 418c184 + a09eb96 commit 52a4ad8
Show file tree
Hide file tree
Showing 23 changed files with 1,178 additions and 662 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 59 additions & 0 deletions src/display/displayer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
extern crate sdl2_ttf;
use sdl2::render::{Renderer, BlendMode};
use sdl2::pixels::Color;
use ::display::font::*;

pub struct Displayer<'a> {
fonts: FontList,
renderer: Renderer<'a>,
#[allow(dead_code)]
ttf_context: sdl2_ttf::Sdl2TtfContext,
}

impl<'a> Displayer<'a> {
pub fn new(mut renderer: Renderer<'a>)
-> Result<Displayer<'a>, ()> {
renderer.set_blend_mode(BlendMode::Blend);
let ttf_context = sdl2_ttf::init().unwrap();
let font_list = FontList::new(&ttf_context).unwrap();
let displayer = Displayer {
fonts: font_list,
ttf_context: ttf_context,
renderer: renderer,
};
Ok(displayer)
}

pub fn fatal_error_message(&self,title:&str,info:&str) {
::sdl2::messagebox::show_simple_message_box(::sdl2::messagebox::MESSAGEBOX_ERROR,
title,
info,
self.sdl_renderer().window());
}

// width and height must be between 0 and 1
pub fn sub_screen_dims(&self,
width: Option<f32>,
height: Option<f32>)
-> (Option<u32>, Option<u32>) {
let dims: (u32, u32) = self.renderer.window().unwrap().size();
(width.and_then(|width| Some((width * (dims.0 as f32)) as u32)),
height.and_then(|height| Some((height * (dims.1 as f32)) as u32)))
}

pub fn render(&mut self) {
self.sdl_renderer_mut().window().unwrap().gl_swap_window();
}

pub fn sdl_renderer_mut(&mut self) -> &mut Renderer<'a> {
&mut self.renderer
}

pub fn sdl_renderer(&self) -> &Renderer<'a> {
&self.renderer
}

pub fn fonts(&self) -> &FontList {
&self.fonts
}
}
48 changes: 20 additions & 28 deletions src/font.rs → src/display/font.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
extern crate sdl2_ttf;

use std::ops::Index;
use std::cmp::Ordering;
use std::path::Path;
use sdl2::rwops::RWops;
pub const OUTLINE_WIDTH: u16 = 2;

pub struct FontSet {
// rwops needs to live with the font itself
// it is not used for anything, but if it were to be placed somewhere else
// it would probably call Drop while the font is being used (which is bad)
#[allow(dead_code)]
rwops_regular:RWops<'static>,
#[allow(dead_code)]
rwops_bold:RWops<'static>,
/// size of the loaded font
font_size: u16,
Expand Down Expand Up @@ -59,23 +62,20 @@ pub struct FontList {
fonts: Vec<FontSet>,
}

const mono_font_bytes : &'static [u8] = include_bytes!("../res/DejaVuSansMono-Bold.ttf");
const DEJAVUSANS_MONO_BYTES : &'static [u8] = include_bytes!("../../res/DejaVuSansMono-Bold.ttf");

impl FontList {
pub fn new(ttf_context: &sdl2_ttf::Sdl2TtfContext) -> Result<FontList, ()> {
pub fn new(ttf_context: &sdl2_ttf::Sdl2TtfContext) -> Result<FontList, String> {
use sdl2::rwops::RWops;
let mut result = FontList { fonts: Vec::<FontSet>::new() };
let mut font_size = 3;
let font_size_max = 128;
let font_size_increment = 1;
let mut error: bool = false;
'fontlist: while (font_size < font_size_max) {
let rwops_regular = RWops::from_bytes(mono_font_bytes).expect("Failed to load font bytes into rwops");
let rwops_bold = RWops::from_bytes(mono_font_bytes).expect("Failed to load font bytes into rwops");
let font_regular = ttf_context.load_font_from_rwops(&rwops_regular, font_size)
.expect("Unable to load font from rwops");
let mut font_bold = ttf_context.load_font_from_rwops(&rwops_bold, font_size)
.expect("Unable to load font from rwops");
let rwops_regular = try!(RWops::from_bytes(DEJAVUSANS_MONO_BYTES));
let rwops_bold = try!(RWops::from_bytes(DEJAVUSANS_MONO_BYTES));
let font_regular = try!(ttf_context.load_font_from_rwops(&rwops_regular, font_size));
let mut font_bold = try!(ttf_context.load_font_from_rwops(&rwops_bold, font_size));
font_bold.set_outline_width(OUTLINE_WIDTH);
result.fonts.push(FontSet {
rwops_regular:rwops_regular,
Expand All @@ -86,11 +86,7 @@ impl FontList {
});
font_size += font_size_increment;
}
if error {
Err(())
} else {
Ok(result)
}
Ok(result)
}

pub fn get_font_set(&self, index: usize) -> Option<&FontSet> {
Expand All @@ -102,24 +98,20 @@ impl FontList {
string: &str,
max_dims: (Option<u32>, Option<u32>),
outline: bool)
-> Result<&FontSet, ()> {
-> Result<&FontSet, String> {
if max_dims == (None, None) {
Err(()) // cant get the fittiest if both are None !
Err(String::from("can't get fittiest font if both dims are None")) // cant get the fittiest if both are None !
} else {
match self.fonts.len() {
0 => Err(()),
0 => Err(String::from("can't get the fittest font if there is none available")),
1 => Ok(self.fonts.first().unwrap()),
_ => {
use std::error::Error;
let search_result = self.fonts.binary_search_by(|fontset| {
let ref font_bold = fontset.font_bold;
let ref font_regular = fontset.font_regular;
let string_dims = if outline {
font_bold
} else {
font_regular
}
.size_of(string)
.expect("Failed to get size of some string");
let search_font =
if outline { &fontset.font_bold } else { &fontset.font_regular };
let string_dims =
search_font.size_of(string).expect("Failed to get dimensions");
match max_dims {
(Some(width), Some(height)) => {
match (string_dims.0.cmp(&width), string_dims.1.cmp(&height)) {
Expand Down
169 changes: 169 additions & 0 deletions src/display/frame.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
use display::*;
use utils::*;
use ::subtitles::{Sentence,Subtitles,Syllable,Position as SentencePos};

#[derive(Debug)]
pub enum TextureType {
Logo,
}

#[derive(Debug)]
pub struct Texture {
pub texture_type:TextureType,
pub size: Size,
pub pos: (PosX, PosY),
pub anchor: (f32, f32),
}

impl Display for Texture {
fn draw(self, displayer: &mut Displayer) {

}
}

#[derive(Debug)]
pub struct Frame {
pub textures:Vec<Texture>,
pub vec_text2d:Vec<Text2D>
}

impl Display for Frame {
fn draw(self, displayer: &mut Displayer) {
for text_2d in self.vec_text2d.into_iter() {
text_2d.draw(displayer);
}
for texture in self.textures.into_iter() {
texture.draw(displayer);
}
}
}

fn compute_sentence_alpha(sentence:&Sentence,frame_number:u32) -> f32 {
match (sentence.syllables.first(), sentence.syllables.last()) {
(Some( &Syllable {begin:frame_begin, ..} ),
Some( &Syllable {end :frame_end , ..} )) => {
let end_fade_frame : u32 = (sentence.sentence_options.transition_time
- sentence.sentence_options.fade_time) as u32 ;
let begin_first_fade_frame =
frame_begin.saturating_sub(sentence.sentence_options.transition_time as u32);
let end_first_fade_frame =
frame_begin.saturating_sub(end_fade_frame);
let begin_second_fade_frame =
frame_end.saturating_add(end_fade_frame);
let end_second_fade_frame =
frame_end.saturating_add(sentence.sentence_options.transition_time as u32);
debug_assert_eq!(end_second_fade_frame - begin_second_fade_frame,
sentence.sentence_options.fade_time as u32);
if (end_first_fade_frame < frame_number &&
begin_second_fade_frame > frame_number) {
1.0
} else if begin_first_fade_frame <= frame_number &&
end_first_fade_frame >= frame_number {
(frame_number - begin_first_fade_frame) as f32 /
(end_first_fade_frame - begin_first_fade_frame) as f32
} else if begin_second_fade_frame <= frame_number &&
end_second_fade_frame >= frame_number {
1.0 -
((frame_number - begin_second_fade_frame) as f32 /
(end_second_fade_frame - begin_second_fade_frame) as f32)
} else {
0.0
}
}
_ => 0.0,
}
}

fn add_syllable(mut text_elts : &mut Vec<::display::TextElement>,
syllable:&Syllable,
current_frame:u32,
alpha:f32) {
if (current_frame < syllable.begin) {
let text_2d = ::display::TextElement {
text: syllable.text.clone(),
color: fade_color(syllable.syllable_options.alive_color, alpha),
outline: syllable.syllable_options.outline.map(|outline| outline.color ),
shadow: None,
};
// TODO do the same optimization like dead_color
text_elts.push(text_2d);
} else if (syllable.begin <= current_frame) && (current_frame <= syllable.end) {
let percent = (current_frame - syllable.begin) as f32 /
(syllable.end - syllable.begin) as f32;
// lets ease the percent a lil bits
let percent = 1.0 - (1.0 - percent*percent).sqrt();
let transition_color = mix_colors(syllable.syllable_options.transition_color,
syllable.syllable_options.dead_color,
percent);
let text_2d = ::display::TextElement {
text: syllable.text.clone(),
color: transition_color,
outline: syllable.syllable_options.outline.map(|outline| outline.color ),
shadow: None,
};
text_elts.push(text_2d);
} else {
if (text_elts.is_empty()) {
let text_2d = ::display::TextElement {
text: syllable.text.clone(),
color: fade_color(syllable.syllable_options.dead_color, alpha),
outline: syllable.syllable_options.outline.map(|outline| outline.color),
shadow: None,
};
text_elts.push(text_2d);
} else {
let mut text_2d = text_elts.last_mut().unwrap();
text_2d.text.push_str(&*syllable.text);
}
}
}

impl Frame {
pub fn from_subtitles(subtitles:&Subtitles,frame_number:u32) -> Frame {
let mut frame : Frame = Frame {
textures: Vec::with_capacity(4),
vec_text2d:Vec::with_capacity(4),
};
let mut sentence_iter = subtitles.sentences.iter().enumerate().filter(|&(_,ref sentence)| {
match (sentence.syllables.first(),sentence.syllables.last()) {
(None,_) | (_,None) => false,
(Some(ref first_syllable),Some(ref last_syllable)) => {
let first_frame = first_syllable.begin
.saturating_sub(sentence.sentence_options.transition_time as u32);
let last_frame = last_syllable.end
.saturating_add(sentence.sentence_options.transition_time as u32);
if ( frame_number >= first_frame && frame_number <= last_frame ) {
true
} else {
false
}
}
}
}); // get all the sentences displayed on screen
for (sentence_number,ref sentence) in sentence_iter {
let sentence_alpha = compute_sentence_alpha(sentence,frame_number);
let mut text_elts = vec![] ;
for syllable in sentence.syllables.iter() {
add_syllable(&mut text_elts,syllable,frame_number,sentence_alpha);
}
let text_pos = match sentence.position {
SentencePos::Row(l) => {
(::display::PosX::Centered,
::display::PosY::FromTopPercent(l as f32 / 7.0 + 0.025 ))
}
SentencePos::ForcePos(x,y) => {
(::display::PosX::FromLeftPercent(x),
::display::PosY::FromTopPercent(y))
},
};
let text_2d = ::display::Text2D {
text: text_elts,
size: ::display::Size::FitPercent(Some(0.95), Some(0.1)),
pos: text_pos,
anchor: (0.5, 0.0),
};
frame.vec_text2d.push(text_2d);
};
frame
}
}
27 changes: 27 additions & 0 deletions src/display/misc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#[allow(dead_code)]
#[derive(Copy,Debug,Clone)]
pub enum PosX {
Centered,
FromLeft(u32),
FromRight(u32),
FromLeftPercent(f32),
FromRightPercent(f32),
}

#[allow(dead_code)]
#[derive(Copy,Debug,Clone)]
pub enum PosY {
Centered,
FromTop(u32),
FromBottom(u32),
FromTopPercent(f32),
FromBottomPercent(f32),
}

#[allow(dead_code)]
#[derive(Copy,Debug,Clone)]
pub enum Size {
// Percentage(f64),
FitPercent(Option<f32>, Option<f32>),
Fit(Option<u32>, Option<u32>),
}
15 changes: 15 additions & 0 deletions src/display/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
mod font;
mod displayer;
mod misc;
mod text_element;
mod text2d;
pub mod frame;
pub use self::displayer::*;
pub use self::misc::*;
pub use self::text_element::*;
pub use self::text2d::*;


pub trait Display {
fn draw(self, &mut Displayer);
}
Loading

0 comments on commit 52a4ad8

Please sign in to comment.