1use std::io::{
4 Read, Seek, SeekFrom
5};
6use image::{
7 ImageBuffer, Rgba
8};
9use sdl2::{
10 mixer::{
11 Channel, Chunk, Music
12 }, pixels::{
13 Color, PixelFormatEnum
14 }, rect::Rect,
15 render::{
16 Canvas, Texture, TextureCreator
17 }, rwops::RWops,
18 surface::Surface,
19 ttf::Sdl2TtfContext,
20 video::{
21 Window, WindowContext
22 }
23};
24
25pub struct Image<'a> {
28 tex: Texture<'a>
29}
30
31impl<'a> Image<'a> {
32 pub fn new(
33 img: &mut ImageBuffer<Rgba<u8>, Vec<u8>>,
34 creator: &'a TextureCreator<WindowContext>) -> Result<Self, String> {
35 let mut img_data = img.pixels().flat_map(|px| px.0).collect::<Vec<u8>>();
36 let pitch = img.width() * 4;
37 let sfc = Surface::from_data(
38 &mut img_data, img.width(), img.height(), pitch, PixelFormatEnum::RGBA32
39 )?;
40 let tex = Texture::from_surface(&sfc, creator).map_err(|e| e.to_string())?;
41 Ok(Self {
42 tex
43 })
44 }
45
46 pub fn render(
47 &self, cnv: &mut Canvas<Window>, src: &Rect, dest: &Rect,
48 angle: f64, flip: (bool, bool)) -> Result<(), String> {
49 cnv.copy_ex(&self.tex, Some(*src), Some(*dest), angle, None, flip.0, flip.1)
50 .map_err(|e| e.to_string())?;
51 Ok(())
52 }
53}
54
55pub struct Font<'a, 'b> {
56 font: sdl2::ttf::Font<'a, 'b>
57}
58
59impl<'a, 'b> Font<'a, 'b> {
60 pub fn new(font_data: &'b [u8], size: u16, ttf_ctx: &'a Sdl2TtfContext) -> Result<Self, String> {
61 Ok(Self {
62 font: Self::load_font_from_bytes(ttf_ctx, font_data, size)?
63 })
64 }
65
66 fn load_font_from_bytes(
67 ttf_context: &'a Sdl2TtfContext,
68 font_data: &'b [u8],
69 size: u16) -> Result<sdl2::ttf::Font<'a, 'b>, String> {
70 let font_reader = RWops::from_bytes(font_data)?;
71 let font = ttf_context.load_font_from_rwops(font_reader, size);
72 font.map_err(|e| e.to_string())
73 }
74
75 pub fn render(
76 &self, cnv: &mut Canvas<Window>, creator: &TextureCreator<WindowContext>,
77 msg: &str, color: &Color, pos: (i32, i32), angle: f64,
78 flip: (bool, bool)) -> Result<(), String> {
79 let sfc = self.font.render(msg)
80 .solid(*color)
81 .map_err(|e| e.to_string())?;
82 let tex = Texture::from_surface(&sfc, creator).map_err(|e| e.to_string())?;
83 let width = sfc.width();
84 let height = sfc.height();
85 let dest = Rect::new(pos.0, pos.1, width, height);
86 cnv.copy_ex(&tex, None, Some(dest), angle, None, flip.0, flip.1)
87 }
88}
89
90pub enum Sound<'a> {
91 Music(Music<'a>),
92 Chunk(Chunk)
93}
94
95impl<'a> Sound<'a> {
96 pub fn load_music(src: &'static [u8]) -> Result<Self, String> {
97 Ok(Self::Music(Music::from_static_bytes(src)?))
98 }
99
100 pub fn load_chunk(src: &[u8]) -> Result<Self, String> {
101 Ok(Self::Chunk(Self::load_chunk_from_bytes(src)?))
102 }
103
104 fn load_chunk_from_bytes(src: &[u8]) -> Result<sdl2::mixer::Chunk, String> {
105 let rw_ops = RWops::from_bytes(src)?;
106 let buff = Self::rwops_to_boxed_slice(rw_ops)?;
107 Chunk::from_raw_buffer(buff)
108 }
109
110 fn rwops_to_boxed_slice(mut rwops: RWops) -> Result<Box<[u8]>, String> {
111 let mut buffer = Vec::<u8>::new();
112 rwops.seek(SeekFrom::Start(0))
113 .map_err(|e| e.to_string())?;
114 rwops.read_to_end(&mut buffer).map_err(|e| e.to_string())?;
115 let boxed_slice = buffer.into_boxed_slice();
116 Ok(boxed_slice)
117 }
118
119 pub fn is_music_playing() -> bool {
120 Music::is_playing()
121 }
122
123 pub fn pause_music() {
124 Music::pause()
125 }
126
127 pub fn resume_music() {
128 Music::resume()
129 }
130
131 pub fn halt_music() {
132 Music::halt()
133 }
134
135 pub fn play(&self) -> Result<(), String> {
136 match self {
137 Sound::Music(music) => {
138 music.play(0).map_err(|e| e.to_string())?;
139 }, Sound::Chunk(chunk) => {
140 Channel::all().play(chunk, 0).map_err(|e| e.to_string())?;
141 }
142 }
143 Ok(())
144 }
145}
146
147