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