summaryrefslogtreecommitdiffstats
path: root/gfx/wr/wr_glyph_rasterizer/examples
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/wr/wr_glyph_rasterizer/examples
parentInitial commit. (diff)
downloadfirefox-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.rs177
-rw-r--r--gfx/wr/wr_glyph_rasterizer/examples/common/boilerplate.rs298
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";