boilerplate.rs (8553B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use api::ImageFormat; 6 use euclid::{Transform3D, UnknownUnit}; 7 use glutin::{self, PossiblyCurrent}; 8 use gleam::gl; 9 use wr_glyph_rasterizer::{RasterizedGlyph, GlyphFormat}; 10 11 use std::{ffi::CStr, rc::Rc}; 12 13 #[allow(unused)] 14 pub struct Gl { 15 pub gl: Rc<dyn gl::Gl>, 16 program: gl::GLuint, 17 vb: gl::GLuint, 18 vao: gl::GLuint, 19 vs: gl::GLuint, 20 fs: gl::GLuint, 21 textures: Vec<gl::GLuint>, 22 u_transform: i32, 23 u_text_color: i32, 24 u_sampler_color: i32, 25 glyphs: Vec<RasterizedGlyph>, 26 } 27 28 pub fn load(gl_context: &glutin::Context<PossiblyCurrent>, glyphs: Vec<RasterizedGlyph>) -> Gl { 29 env_logger::init(); 30 31 #[cfg(target_os = "macos")] 32 { 33 use core_foundation::{self as cf, base::TCFType}; 34 let i = cf::bundle::CFBundle::main_bundle().info_dictionary(); 35 let mut i = unsafe { i.to_mutable() }; 36 i.set( 37 cf::string::CFString::new("NSSupportsAutomaticGraphicsSwitching"), 38 cf::boolean::CFBoolean::true_value().into_CFType(), 39 ); 40 } 41 42 let gl = match gl_context.get_api() { 43 glutin::Api::OpenGl => unsafe { 44 gl::GlFns::load_with(|symbol| gl_context.get_proc_address(symbol) as *const _) 45 }, 46 glutin::Api::OpenGlEs => unsafe { 47 gl::GlesFns::load_with(|symbol| gl_context.get_proc_address(symbol) as *const _) 48 }, 49 glutin::Api::WebGl => unimplemented!(), 50 }; 51 52 let version = unsafe { 53 let data = CStr::from_ptr(gl.get_string(gl::VERSION).as_ptr() as *const _) 54 .to_bytes() 55 .to_vec(); 56 String::from_utf8(data).unwrap() 57 }; 58 59 println!("OpenGL version {}", version); 60 61 let vs = gl.create_shader(gl::VERTEX_SHADER); 62 gl.shader_source(vs, &[VS_SRC]); 63 gl.compile_shader(vs); 64 65 let fs = gl.create_shader(gl::FRAGMENT_SHADER); 66 gl.shader_source(fs, &[FS_SRC]); 67 gl.compile_shader(fs); 68 69 let program = gl.create_program(); 70 gl.attach_shader(program, vs); 71 gl.attach_shader(program, fs); 72 gl.link_program(program); 73 gl.use_program(program); 74 75 let vb = gl.gen_buffers(1)[0]; 76 gl.bind_buffer(gl::ARRAY_BUFFER, vb); 77 gl.buffer_data_untyped( 78 gl::ARRAY_BUFFER, 79 (6 * 4 * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, 80 std::ptr::null(), 81 gl::DYNAMIC_DRAW, 82 ); 83 84 let vao = gl.gen_vertex_arrays(1)[0]; 85 gl.bind_vertex_array(vao); 86 87 let u_transform = gl.get_uniform_location(program, "uTransform"); 88 let u_text_color = gl.get_uniform_location(program, "uTextColor"); 89 let u_sampler_color = gl.get_uniform_location(program, "uSamplerColor"); 90 let i_position = gl.get_attrib_location(program, "iPosition"); 91 let i_tex_coords = gl.get_attrib_location(program, "iTexCoords"); 92 gl.vertex_attrib_pointer( 93 i_position as gl::types::GLuint, 94 2, 95 gl::FLOAT, 96 false, 97 4 * std::mem::size_of::<f32>() as gl::types::GLsizei, 98 0, 99 ); 100 gl.vertex_attrib_pointer( 101 i_tex_coords as gl::types::GLuint, 102 2, 103 gl::FLOAT, 104 false, 105 4 * std::mem::size_of::<f32>() as gl::types::GLsizei, 106 (2 * std::mem::size_of::<f32>()) as gl::types::GLuint, 107 ); 108 gl.enable_vertex_attrib_array(i_position as gl::types::GLuint); 109 gl.enable_vertex_attrib_array(i_tex_coords as gl::types::GLuint); 110 111 let textures = create_texture(&gl, &glyphs); 112 113 gl.bind_buffer(gl::ARRAY_BUFFER, 0); 114 gl.bind_vertex_array(0); 115 116 unsafe { log_shader(&gl, vs) }; 117 unsafe { log_shader(&gl, fs) }; 118 119 Gl { 120 gl, 121 program, 122 vb, 123 vao, 124 u_transform, 125 u_text_color, 126 u_sampler_color, 127 glyphs, 128 textures, 129 vs, 130 fs, 131 } 132 } 133 134 fn create_texture(gl: &Rc<dyn gl::Gl>, glyphs: &[RasterizedGlyph]) -> Vec<gl::GLuint> { 135 let textures = gl.gen_textures(glyphs.len() as gl::types::GLsizei); 136 for (i, glyph) in glyphs.iter().enumerate() { 137 let (internal_format, external_format) = get_texture_format(&glyph.format); 138 let texture = textures[i]; 139 gl.bind_texture(gl::TEXTURE_2D, texture); 140 gl.tex_parameter_i( 141 gl::TEXTURE_2D, 142 gl::TEXTURE_MAG_FILTER, 143 gl::LINEAR as gl::GLint, 144 ); 145 gl.tex_parameter_i( 146 gl::TEXTURE_2D, 147 gl::TEXTURE_MIN_FILTER, 148 gl::LINEAR as gl::GLint, 149 ); 150 gl.tex_parameter_i( 151 gl::TEXTURE_2D, 152 gl::TEXTURE_WRAP_S, 153 gl::CLAMP_TO_EDGE as gl::GLint, 154 ); 155 gl.tex_parameter_i( 156 gl::TEXTURE_2D, 157 gl::TEXTURE_WRAP_T, 158 gl::CLAMP_TO_EDGE as gl::GLint, 159 ); 160 // TODO: use tex_storage_2d 161 gl.tex_image_2d( 162 gl::TEXTURE_2D, 163 0, 164 internal_format as gl::GLint, 165 glyph.width, 166 glyph.height, 167 0, 168 external_format, 169 gl::UNSIGNED_BYTE, 170 Some(&glyph.bytes), 171 ); 172 173 gl.pixel_store_i(gl::UNPACK_ALIGNMENT, 1); 174 gl.enable(gl::BLEND); 175 gl.blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); 176 } 177 178 textures 179 } 180 181 fn get_texture_format(format: &GlyphFormat) -> (gl::GLuint, gl::GLuint) { 182 match format.image_format(false) { 183 ImageFormat::BGRA8 => (gl::RGBA, gl::BGRA), 184 _ => unimplemented!(), 185 } 186 } 187 188 unsafe fn log_shader(gl: &Rc<dyn gl::Gl>, shader: gl::GLuint) { 189 let log = gl.get_shader_info_log(shader); 190 if log.len() != 0 { 191 println!("[ERROR] {}", log); 192 } 193 } 194 195 impl Gl { 196 pub fn draw_frame( 197 &self, 198 width: f32, 199 height: f32, 200 text_color: [f32; 4], 201 background_color: [f32; 4], 202 scale_factor: f32, 203 ) { 204 let projection: Transform3D<f32, UnknownUnit, UnknownUnit> = 205 Transform3D::ortho(0., width, height, 0., -1., 1.); 206 self.gl 207 .uniform_matrix_4fv(self.u_transform, false, &projection.to_array()); 208 self.gl.uniform_4fv(self.u_text_color, &text_color); 209 210 self.gl.active_texture(gl::TEXTURE0); 211 212 self.gl.bind_vertex_array(self.vao); 213 214 self.gl.clear_color( 215 background_color[0], 216 background_color[1], 217 background_color[2], 218 background_color[3], 219 ); 220 self.gl.clear(gl::COLOR_BUFFER_BIT); 221 222 let mut ax = 0.; 223 for (i, glyph) in self.glyphs.iter().enumerate() { 224 let texture = self.textures[i]; 225 226 let x = ax + glyph.left; 227 let y = glyph.top; 228 229 let w = (glyph.width as f32) * scale_factor; 230 let h = (glyph.height as f32) * scale_factor; 231 232 #[rustfmt::skip] 233 let vertices = [ 234 x, y, 0.0, 0.0, 235 x, y + h, 0.0, 1.0, 236 x + w, y + h, 1.0, 1.0, 237 238 x, y, 0.0, 0.0, 239 x + w, y + h, 1.0, 1.0, 240 x + w, y, 1.0, 0.0 241 ]; 242 243 self.gl.uniform_1i(self.u_sampler_color, 0); 244 self.gl.bind_texture(gl::TEXTURE_2D, texture); 245 self.gl.bind_buffer(gl::ARRAY_BUFFER, self.vb); 246 self.gl.buffer_data_untyped( 247 gl::ARRAY_BUFFER, 248 (vertices.len() * std::mem::size_of::<f32>()) as gl::GLsizeiptr, 249 vertices.as_ptr() as *const _, 250 gl::DYNAMIC_DRAW, 251 ); 252 self.gl.bind_buffer(gl::ARRAY_BUFFER, 0); 253 254 self.gl.draw_arrays(gl::TRIANGLES, 0, 6); 255 256 ax += (glyph.left * scale_factor) + (glyph.width as f32 * scale_factor); 257 } 258 self.gl.bind_vertex_array(0); 259 self.gl.bind_texture(gl::TEXTURE_2D, 0); 260 261 unsafe { 262 log_shader(&self.gl, self.vs); 263 log_shader(&self.gl, self.fs); 264 }; 265 } 266 } 267 268 const VS_SRC: &[u8] = b" 269 #version 150 270 271 in vec2 iPosition; 272 in vec2 iTexCoords; 273 274 uniform mat4 uTransform; 275 276 out vec2 vColorTexCoord; 277 278 void main() { 279 gl_Position = uTransform * vec4(iPosition, 0.0, 1.0); 280 vColorTexCoord = iTexCoords; 281 } 282 \0"; 283 284 const FS_SRC: &[u8] = b" 285 #version 150 286 287 in vec2 vColorTexCoord; 288 289 uniform sampler2D uSamplerColor; 290 uniform vec4 uTextColor; 291 292 out vec4 oFragmentColor; 293 294 void main() { 295 vec4 alpha = vec4(1.0, 1.0, 1.0, texture(uSamplerColor, vColorTexCoord).r); 296 oFragmentColor = uTextColor * alpha; 297 } 298 \0";