Load the c64 font as a raw bitmap without using the image crate.
[mandelwow.git] / text.rs
1 use cgmath::conv::array4x4;
2 use cgmath::{Matrix4, Vector3};
3 use glium;
4 use glium::{Surface, texture};
5 use std;
6
7 fn gamma<T>(x: T) -> f32
8 where
9     f32: From<T>,
10     T: Copy,
11 {
12     ((f32::from(x)) / 255.).powf(2.2)
13 }
14
15 fn srgb<T>(c: [T; 3]) -> [f32; 4]
16 where
17     f32: From<T>,
18     T: Copy,
19 {
20     [gamma(c[0]), gamma(c[1]), gamma(c[2]), 0.0]
21 }
22
23 #[cfg(feature = "image")]
24 fn c64_font() -> (u32, u32, Vec<u8>) {
25     use image;
26     let image =
27         image::load_from_memory_with_format(&include_bytes!("c64-font.png")[..], image::PNG)
28             .unwrap()
29             .to_luma();
30     let (w, h) = image.dimensions();
31     (w, h, image.into_raw())
32 }
33
34 #[cfg(not(feature = "image"))]
35 fn c64_font() -> (u32, u32, Vec<u8>) {
36     let pixels = &include_bytes!("c64-font.gray")[..];
37     (128, 128, Vec::from(pixels))
38 }
39
40 #[derive(Copy, Clone)]
41 struct Vertex {
42     position: [f32; 2],
43     tex_coords: [f32; 2],
44 }
45 implement_vertex!(Vertex, position, tex_coords);
46
47 pub struct Text<'a> {
48     tex: texture::Texture2d,
49     vertex_buffer: glium::VertexBuffer<Vertex>,
50     index_buffer: glium::IndexBuffer<u16>,
51     program: glium::Program,
52     params: glium::DrawParameters<'a>,
53     model: Matrix4<f32>,
54 }
55
56 impl<'a> Text<'a> {
57     pub fn new(display: &glium::Display) -> Text {
58         let (w, h, pixels) = c64_font();
59         let image = glium::texture::RawImage2d {
60             data: std::borrow::Cow::from(pixels),
61             width: w,
62             height: h,
63             format: glium::texture::ClientFormat::U8,
64         };
65         let tex = glium::texture::Texture2d::with_format(
66             display,
67             image,
68             glium::texture::UncompressedFloatFormat::U8,
69             glium::texture::MipmapsOption::NoMipmap,
70         ).unwrap();
71
72         // building the vertex buffer, which contains all the vertices that we will draw
73         let vertex_buffer = {
74             glium::VertexBuffer::new(
75                 display,
76                 &[
77                     Vertex {
78                         position: [-1.0, -1.0],
79                         tex_coords: [0.0, 1.0],
80                     },
81                     Vertex {
82                         position: [-1.0, 1.0],
83                         tex_coords: [0.0, 0.0],
84                     },
85                     Vertex {
86                         position: [1.0, 1.0],
87                         tex_coords: [1.0, 0.0],
88                     },
89                     Vertex {
90                         position: [1.0, -1.0],
91                         tex_coords: [1.0, 1.0],
92                     },
93                 ],
94             ).unwrap()
95         };
96
97         let index_buffer = glium::IndexBuffer::new(
98             display,
99             glium::index::PrimitiveType::TriangleStrip,
100             &[1 as u16, 2, 0, 3],
101         ).unwrap();
102
103         // compiling shaders and linking them together
104         let program = program!(display,
105         140 => {
106             vertex: "
107                 #version 140
108
109                 uniform mat4 model;
110                 uniform mat4 perspview;
111                 uniform int index;
112
113                 in vec2 position;
114                 in vec2 tex_coords;
115
116                 out vec2 v_tex_coords;
117                 out ivec4 v_fgcolor;
118
119                 void main() {
120                     gl_Position = perspview * model * vec4(position, 0.0, 1.0);
121
122                     // Characters are arranged in a 16x16 square.
123                     int xpos = index % 16;
124                     int ypos = index / 16;
125                     v_tex_coords = (tex_coords + vec2(xpos, ypos)) / 16.;
126                 }
127             ",
128
129             fragment: "
130                 #version 140
131                 uniform sampler2D tex;
132                 uniform vec4 bgcolor;
133                 uniform vec4 fgcolor;
134
135                 in vec2 v_tex_coords;
136                 out vec4 f_color;
137
138                 void main() {
139                     f_color = texture(tex, v_tex_coords).x == 0U ? bgcolor : fgcolor;
140                 }
141             "
142         }).unwrap();
143
144         let params = glium::DrawParameters {
145             depth: glium::Depth {
146                 test: glium::draw_parameters::DepthTest::IfLess,
147                 write: true,
148                 ..Default::default()
149             },
150             multisampling: true,
151             ..Default::default()
152         };
153
154         Text {
155             model: Matrix4::from_translation(Vector3::unit_z() * (-1.0)),
156             tex: tex,
157             vertex_buffer: vertex_buffer,
158             index_buffer: index_buffer,
159             program: program,
160             params: params,
161         }
162     }
163
164     pub fn draw(&self, frame: &mut glium::Frame, perspview: &[[f32; 4]; 4]) {
165         let uniforms =
166             uniform! {
167             model: array4x4(self.model),
168             perspview: *perspview,
169             tex: self.tex.sampled()
170                 .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest),
171             index: 'C' as i32,
172             // RGB values from http://unusedino.de/ec64/technical/misc/vic656x/colors/
173             bgcolor: srgb([ 64,  50, 133u8]),  //  6 - blue
174             fgcolor: srgb([120, 106, 189u8]),  // 14 - light blue
175         };
176         frame
177             .draw(
178                 &self.vertex_buffer,
179                 &self.index_buffer,
180                 &self.program,
181                 &uniforms,
182                 &self.params,
183             )
184             .unwrap();
185     }
186 }