diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/wr/wr_glyph_rasterizer/examples | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/wr/wr_glyph_rasterizer/examples')
-rw-r--r-- | gfx/wr/wr_glyph_rasterizer/examples/basic.rs | 177 | ||||
-rw-r--r-- | gfx/wr/wr_glyph_rasterizer/examples/common/boilerplate.rs | 298 |
2 files changed, 475 insertions, 0 deletions
diff --git a/gfx/wr/wr_glyph_rasterizer/examples/basic.rs b/gfx/wr/wr_glyph_rasterizer/examples/basic.rs new file mode 100644 index 0000000000..4c46004cdf --- /dev/null +++ b/gfx/wr/wr_glyph_rasterizer/examples/basic.rs @@ -0,0 +1,177 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::sync::Arc; +use std::mem; + +use api::{ + IdNamespace, FontTemplate, FontKey, FontInstanceKey, FontInstanceOptions, + FontInstancePlatformOptions, ColorF, FontInstanceFlags, units::DevicePoint, +}; +use glutin::ContextBuilder; +use glutin::dpi::PhysicalSize; +use glutin::event::{Event, WindowEvent}; +use glutin::event_loop::{ControlFlow, EventLoop}; +use glutin::window::WindowBuilder; +use rayon::ThreadPoolBuilder; +use wr_glyph_rasterizer::RasterizedGlyph; +use wr_glyph_rasterizer::{ + SharedFontResources, BaseFontInstance, GlyphRasterizer, FontInstance, GlyphKey, + SubpixelDirection, profiler::GlyphRasterizeProfiler, +}; + +#[path = "common/boilerplate.rs"] +mod boilerplate; + +struct Profiler; + +impl GlyphRasterizeProfiler for Profiler { + fn start_time(&mut self) {} + fn end_time(&mut self) -> f64 { + 0. + } + fn set(&mut self, _value: f64) {} +} + +fn load_glyphs() -> Vec<RasterizedGlyph> { + let namespace = IdNamespace(0); + let mut fonts = SharedFontResources::new(namespace); + + let font_key = FontKey::new(namespace, 0); + let raw_font_data = include_bytes!("../../wrench/reftests/text/FreeSans.ttf"); + let font_template = FontTemplate::Raw(Arc::new(raw_font_data.to_vec()), 0); + let shared_font_key = fonts + .font_keys + .add_key(&font_key, &font_template) + .expect("Failed to add font key"); + + let font_instance_key = FontInstanceKey::new(namespace, 1); + fonts.templates.add_font(shared_font_key, font_template); + assert!(fonts.templates.has_font(&shared_font_key)); + + // AddFontInstance will only be processed here, not in the resource cache, so it + // is safe to take the options rather than clone them. + let base = BaseFontInstance::new( + font_instance_key, + shared_font_key, + 32., + mem::take(&mut Some(FontInstanceOptions::default())), + mem::take(&mut Some(FontInstancePlatformOptions::default())), + mem::take(&mut Vec::new()), + ); + let shared_instance = fonts + .instance_keys + .add_key(base) + .expect("Failed to add font instance key"); + fonts.instances.add_font_instance(shared_instance); + + let workers = { + let worker = ThreadPoolBuilder::new() + .thread_name(|idx| format!("WRWorker#{}", idx)) + .build(); + Arc::new(worker.unwrap()) + }; + let mut glyph_rasterizer = GlyphRasterizer::new(workers, false); + + glyph_rasterizer.add_font( + shared_font_key, + fonts + .templates + .get_font(&shared_font_key) + .expect("Could not find FontTemplate"), + ); + + let mut font = FontInstance::new( + fonts + .instances + .get_font_instance(font_instance_key) + .expect("Could not found BaseFontInstance"), + ColorF::BLACK.into(), + api::FontRenderMode::Alpha, + FontInstanceFlags::SUBPIXEL_POSITION | FontInstanceFlags::FONT_SMOOTHING, + ); + + glyph_rasterizer.prepare_font(&mut font); + + let glyph_keys = [ + glyph_rasterizer + .get_glyph_index(shared_font_key, 'A') + .expect("Failed to get glyph index"), + glyph_rasterizer + .get_glyph_index(shared_font_key, 'B') + .expect("Failed to get glyph index"), + glyph_rasterizer + .get_glyph_index(shared_font_key, 'C') + .expect("Failed to get glyph index"), + ]; + + let glyph_keys = glyph_keys.map(|g| { + GlyphKey::new( + g, + DevicePoint::new(100., 100.), + SubpixelDirection::Horizontal, + ) + }); + + glyph_rasterizer.request_glyphs(font, &glyph_keys, |_| true); + + let mut glyphs = vec![]; + glyph_rasterizer.resolve_glyphs( + |job, _| { + if let Ok(glyph) = job.result { + glyphs.push(glyph); + } + }, + &mut Profiler, + ); + + glyphs +} + +fn main() { + let glyphs = load_glyphs(); + + let el = EventLoop::new(); + let wb = WindowBuilder::new() + .with_title("A fantastic window!") + .with_inner_size(PhysicalSize::new(1900. as f64, 1300. as f64)); + + let windowed_context = ContextBuilder::new() + .with_gl(glutin::GlRequest::GlThenGles { + opengl_version: (3, 2), + opengles_version: (3, 0), + }) + .build_windowed(wb, &el) + .unwrap(); + + let windowed_context = unsafe { windowed_context.make_current().unwrap() }; + + let gl = boilerplate::load(windowed_context.context(), glyphs); + + el.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + + match event { + Event::LoopDestroyed => (), + Event::WindowEvent { event, .. } => match event { + WindowEvent::Resized(physical_size) => windowed_context.resize(physical_size), + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + _ => (), + }, + Event::RedrawRequested(_) => { + let size = windowed_context.window().inner_size(); + let scale_factor = windowed_context.window().scale_factor(); + gl.draw_frame( + size.width as f32, + size.height as f32, + [0., 0., 0., 1.0], + [1., 1., 1., 1.0], + scale_factor as f32, + ); + windowed_context.swap_buffers().unwrap(); + } + _ => (), + } + }); +} diff --git a/gfx/wr/wr_glyph_rasterizer/examples/common/boilerplate.rs b/gfx/wr/wr_glyph_rasterizer/examples/common/boilerplate.rs new file mode 100644 index 0000000000..9590f92b4c --- /dev/null +++ b/gfx/wr/wr_glyph_rasterizer/examples/common/boilerplate.rs @@ -0,0 +1,298 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use api::ImageFormat; +use euclid::{Transform3D, UnknownUnit}; +use glutin::{self, PossiblyCurrent}; +use gleam::gl; +use wr_glyph_rasterizer::{RasterizedGlyph, GlyphFormat}; + +use std::{ffi::CStr, rc::Rc}; + +#[allow(unused)] +pub struct Gl { + pub gl: Rc<dyn gl::Gl>, + program: gl::GLuint, + vb: gl::GLuint, + vao: gl::GLuint, + vs: gl::GLuint, + fs: gl::GLuint, + textures: Vec<gl::GLuint>, + u_transform: i32, + u_text_color: i32, + u_sampler_color: i32, + glyphs: Vec<RasterizedGlyph>, +} + +pub fn load(gl_context: &glutin::Context<PossiblyCurrent>, glyphs: Vec<RasterizedGlyph>) -> Gl { + env_logger::init(); + + #[cfg(target_os = "macos")] + { + use core_foundation::{self as cf, base::TCFType}; + let i = cf::bundle::CFBundle::main_bundle().info_dictionary(); + let mut i = unsafe { i.to_mutable() }; + i.set( + cf::string::CFString::new("NSSupportsAutomaticGraphicsSwitching"), + cf::boolean::CFBoolean::true_value().into_CFType(), + ); + } + + let gl = match gl_context.get_api() { + glutin::Api::OpenGl => unsafe { + gl::GlFns::load_with(|symbol| gl_context.get_proc_address(symbol) as *const _) + }, + glutin::Api::OpenGlEs => unsafe { + gl::GlesFns::load_with(|symbol| gl_context.get_proc_address(symbol) as *const _) + }, + glutin::Api::WebGl => unimplemented!(), + }; + + let version = unsafe { + let data = CStr::from_ptr(gl.get_string(gl::VERSION).as_ptr() as *const _) + .to_bytes() + .to_vec(); + String::from_utf8(data).unwrap() + }; + + println!("OpenGL version {}", version); + + let vs = gl.create_shader(gl::VERTEX_SHADER); + gl.shader_source(vs, &[VS_SRC]); + gl.compile_shader(vs); + + let fs = gl.create_shader(gl::FRAGMENT_SHADER); + gl.shader_source(fs, &[FS_SRC]); + gl.compile_shader(fs); + + let program = gl.create_program(); + gl.attach_shader(program, vs); + gl.attach_shader(program, fs); + gl.link_program(program); + gl.use_program(program); + + let vb = gl.gen_buffers(1)[0]; + gl.bind_buffer(gl::ARRAY_BUFFER, vb); + gl.buffer_data_untyped( + gl::ARRAY_BUFFER, + (6 * 4 * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, + std::ptr::null(), + gl::DYNAMIC_DRAW, + ); + + let vao = gl.gen_vertex_arrays(1)[0]; + gl.bind_vertex_array(vao); + + let u_transform = gl.get_uniform_location(program, "uTransform"); + let u_text_color = gl.get_uniform_location(program, "uTextColor"); + let u_sampler_color = gl.get_uniform_location(program, "uSamplerColor"); + let i_position = gl.get_attrib_location(program, "iPosition"); + let i_tex_coords = gl.get_attrib_location(program, "iTexCoords"); + gl.vertex_attrib_pointer( + i_position as gl::types::GLuint, + 2, + gl::FLOAT, + false, + 4 * std::mem::size_of::<f32>() as gl::types::GLsizei, + 0, + ); + gl.vertex_attrib_pointer( + i_tex_coords as gl::types::GLuint, + 2, + gl::FLOAT, + false, + 4 * std::mem::size_of::<f32>() as gl::types::GLsizei, + (2 * std::mem::size_of::<f32>()) as gl::types::GLuint, + ); + gl.enable_vertex_attrib_array(i_position as gl::types::GLuint); + gl.enable_vertex_attrib_array(i_tex_coords as gl::types::GLuint); + + let textures = create_texture(&gl, &glyphs); + + gl.bind_buffer(gl::ARRAY_BUFFER, 0); + gl.bind_vertex_array(0); + + unsafe { log_shader(&gl, vs) }; + unsafe { log_shader(&gl, fs) }; + + Gl { + gl, + program, + vb, + vao, + u_transform, + u_text_color, + u_sampler_color, + glyphs, + textures, + vs, + fs, + } +} + +fn create_texture(gl: &Rc<dyn gl::Gl>, glyphs: &[RasterizedGlyph]) -> Vec<gl::GLuint> { + let textures = gl.gen_textures(glyphs.len() as gl::types::GLsizei); + for (i, glyph) in glyphs.iter().enumerate() { + let (internal_format, external_format) = get_texture_format(&glyph.format); + let texture = textures[i]; + gl.bind_texture(gl::TEXTURE_2D, texture); + gl.tex_parameter_i( + gl::TEXTURE_2D, + gl::TEXTURE_MAG_FILTER, + gl::LINEAR as gl::GLint, + ); + gl.tex_parameter_i( + gl::TEXTURE_2D, + gl::TEXTURE_MIN_FILTER, + gl::LINEAR as gl::GLint, + ); + gl.tex_parameter_i( + gl::TEXTURE_2D, + gl::TEXTURE_WRAP_S, + gl::CLAMP_TO_EDGE as gl::GLint, + ); + gl.tex_parameter_i( + gl::TEXTURE_2D, + gl::TEXTURE_WRAP_T, + gl::CLAMP_TO_EDGE as gl::GLint, + ); + // TODO: use tex_storage_2d + gl.tex_image_2d( + gl::TEXTURE_2D, + 0, + internal_format as gl::GLint, + glyph.width, + glyph.height, + 0, + external_format, + gl::UNSIGNED_BYTE, + Some(&glyph.bytes), + ); + + gl.pixel_store_i(gl::UNPACK_ALIGNMENT, 1); + gl.enable(gl::BLEND); + gl.blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); + } + + textures +} + +fn get_texture_format(format: &GlyphFormat) -> (gl::GLuint, gl::GLuint) { + match format.image_format(false) { + ImageFormat::BGRA8 => (gl::RGBA, gl::BGRA), + _ => unimplemented!(), + } +} + +unsafe fn log_shader(gl: &Rc<dyn gl::Gl>, shader: gl::GLuint) { + let log = gl.get_shader_info_log(shader); + if log.len() != 0 { + println!("[ERROR] {}", log); + } +} + +impl Gl { + pub fn draw_frame( + &self, + width: f32, + height: f32, + text_color: [f32; 4], + background_color: [f32; 4], + scale_factor: f32, + ) { + let projection: Transform3D<f32, UnknownUnit, UnknownUnit> = + Transform3D::ortho(0., width, height, 0., -1., 1.); + self.gl + .uniform_matrix_4fv(self.u_transform, false, &projection.to_array()); + self.gl.uniform_4fv(self.u_text_color, &text_color); + + self.gl.active_texture(gl::TEXTURE0); + + self.gl.bind_vertex_array(self.vao); + + self.gl.clear_color( + background_color[0], + background_color[1], + background_color[2], + background_color[3], + ); + self.gl.clear(gl::COLOR_BUFFER_BIT); + + let mut ax = 0.; + for (i, glyph) in self.glyphs.iter().enumerate() { + let texture = self.textures[i]; + + let x = ax + glyph.left; + let y = glyph.top; + + let w = (glyph.width as f32) * scale_factor; + let h = (glyph.height as f32) * scale_factor; + + #[rustfmt::skip] + let vertices = [ + x, y, 0.0, 0.0, + x, y + h, 0.0, 1.0, + x + w, y + h, 1.0, 1.0, + + x, y, 0.0, 0.0, + x + w, y + h, 1.0, 1.0, + x + w, y, 1.0, 0.0 + ]; + + self.gl.uniform_1i(self.u_sampler_color, 0); + self.gl.bind_texture(gl::TEXTURE_2D, texture); + self.gl.bind_buffer(gl::ARRAY_BUFFER, self.vb); + self.gl.buffer_data_untyped( + gl::ARRAY_BUFFER, + (vertices.len() * std::mem::size_of::<f32>()) as gl::GLsizeiptr, + vertices.as_ptr() as *const _, + gl::DYNAMIC_DRAW, + ); + self.gl.bind_buffer(gl::ARRAY_BUFFER, 0); + + self.gl.draw_arrays(gl::TRIANGLES, 0, 6); + + ax += (glyph.left * scale_factor) + (glyph.width as f32 * scale_factor); + } + self.gl.bind_vertex_array(0); + self.gl.bind_texture(gl::TEXTURE_2D, 0); + + unsafe { + log_shader(&self.gl, self.vs); + log_shader(&self.gl, self.fs); + }; + } +} + +const VS_SRC: &[u8] = b" +#version 150 + +in vec2 iPosition; +in vec2 iTexCoords; + +uniform mat4 uTransform; + +out vec2 vColorTexCoord; + +void main() { + gl_Position = uTransform * vec4(iPosition, 0.0, 1.0); + vColorTexCoord = iTexCoords; +} +\0"; + +const FS_SRC: &[u8] = b" +#version 150 + +in vec2 vColorTexCoord; + +uniform sampler2D uSamplerColor; +uniform vec4 uTextColor; + +out vec4 oFragmentColor; + +void main() { + vec4 alpha = vec4(1.0, 1.0, 1.0, texture(uSamplerColor, vColorTexCoord).r); + oFragmentColor = uTextColor * alpha; +} +\0"; |