1use std::error::Error;
2
3use crate::font::{Font, FontSource, FontTextureWriter, FontDrawParams};
4use crate::image::ImageDrawParams;
5use crate::render::{
6 view_matrix, DrawList, DrawMode, FontHandle, Renderer, TexCoord, TextureData, TextureHandle,
7};
8use crate::theme_definition::CharacterRange;
9use crate::{Color, Frame, Point, Rect};
10
11mod program;
12use program::Program;
13
14mod texture;
15use texture::GLTexture;
16
17mod vertex_buffer;
18use vertex_buffer::VAO;
19
20pub struct GLRenderer {
39 base_program: Program,
40 font_program: Program,
41
42 textures: Vec<GLTexture>,
44 fonts: Vec<GLTexture>,
45
46 draw_list: GLDrawList,
48 groups: Vec<DrawGroup>,
49 matrix: [[f32; 4]; 4],
50}
51
52impl Default for GLRenderer {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl GLRenderer {
59 pub fn new() -> GLRenderer {
61 let base_program = Program::new(VERT_SHADER_SRC, GEOM_SHADER_SRC, FRAGMENT_SHADER_SRC);
62
63 let font_program = Program::new(VERT_SHADER_SRC, GEOM_SHADER_SRC, FONT_FRAGMENT_SHADER_SRC);
64
65 GLRenderer {
66 base_program,
67 font_program,
68 fonts: Vec::new(),
69 textures: Vec::new(),
70 draw_list: GLDrawList::new(),
71 groups: Vec::new(),
72 matrix: view_matrix(Point::default(), Point { x: 100.0, y: 100.0 }),
73 }
74 }
75
76 fn font(&self, font: FontHandle) -> &GLTexture {
77 &self.fonts[font.id()]
78 }
79
80 fn texture(&self, texture: TextureHandle) -> &GLTexture {
81 &self.textures[texture.id()]
82 }
83
84 pub fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
86 unsafe {
87 gl::ClearColor(r, g, b, a);
88 gl::Clear(gl::COLOR_BUFFER_BIT);
89 }
90 }
91
92 pub fn draw_frame(&mut self, frame: Frame) {
94 let mouse_cursor = frame.mouse_cursor();
95 let (context, widgets, render_groups) = frame.finish_frame();
96 let context = context.internal().borrow();
97
98 let time_millis = context.time_millis();
99 let display_pos = Point::default();
100 let display_size = context.display_size();
101 let scale = context.scale_factor();
102 self.matrix = view_matrix(display_pos, display_size);
103
104 self.draw_list.clear();
105 self.groups.clear();
106
107 unsafe {
108 gl::Enable(gl::BLEND);
109 gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
110 gl::Enable(gl::CLIP_DISTANCE0);
111 gl::Enable(gl::CLIP_DISTANCE1);
112 gl::Enable(gl::CLIP_DISTANCE2);
113 gl::Enable(gl::CLIP_DISTANCE3);
114 }
115
116 for render_group in render_groups.into_iter().rev() {
117 let mut draw_mode = None;
118
119 for widget in render_group.iter(&widgets) {
121 if !widget.visible() {
122 continue;
123 }
124 let image_handle = match widget.background() {
125 None => continue,
126 Some(handle) => handle,
127 };
128 let time_millis = time_millis - context.base_time_millis_for(widget.id());
129 let image = context.themes().image(image_handle);
130
131 self.write_group_if_changed(&mut draw_mode, DrawMode::Image(image.texture()));
132
133 image.draw(
134 &mut self.draw_list,
135 ImageDrawParams {
136 pos: widget.pos().into(),
137 size: widget.size().into(),
138 anim_state: widget.anim_state(),
139 clip: widget.clip(),
140 time_millis,
141 scale,
142 color: widget.image_color(),
143 },
144 );
145 }
146
147 for widget in render_group.iter(&widgets) {
149 if !widget.visible() {
150 continue;
151 }
152
153 let border = widget.border();
154 let fg_pos = widget.pos() + border.tl();
155 let fg_size = widget.inner_size();
156
157 if let Some(image_handle) = widget.foreground() {
158 let time_millis = time_millis - context.base_time_millis_for(widget.id());
159 let image = context.themes().image(image_handle);
160 self.write_group_if_changed(&mut draw_mode, DrawMode::Image(image.texture()));
161
162 image.draw(
163 &mut self.draw_list,
164 ImageDrawParams {
165 pos: fg_pos.into(),
166 size: fg_size.into(),
167 anim_state: widget.anim_state(),
168 clip: widget.clip(),
169 time_millis,
170 scale,
171 color: widget.image_color(),
172 },
173 );
174 }
175
176 if let Some(text) = widget.text() {
177 if let Some(font_sum) = widget.font() {
178 self.write_group_if_changed(
179 &mut draw_mode,
180 DrawMode::Font(font_sum.handle),
181 );
182 let font = context.themes().font(font_sum.handle);
183
184 let params = FontDrawParams {
185 area_size: fg_size * scale,
186 pos: fg_pos * scale,
187 indent: widget.text_indent(),
188 align: widget.text_align(),
189 };
190
191 font.draw(
192 &mut self.draw_list,
193 params,
194 text,
195 widget.text_color(),
196 widget.clip() * scale,
197 )
198 }
199 }
200 }
201
202 if let Some(mode) = draw_mode {
204 self.write_group(mode);
205 }
206 }
207
208 if let Some((mouse_cursor, align, anim_state)) = mouse_cursor {
209 let image = context.themes().image(mouse_cursor);
210 let mouse_pos = context.mouse_pos();
211 let size = image.base_size();
212 let pos = mouse_pos - align.adjust_for(size);
213 let clip = Rect::new(pos, size);
214
215 let params = ImageDrawParams {
216 pos: pos.into(),
217 size: size.into(),
218 anim_state,
219 clip,
220 time_millis,
221 scale,
222 color: Color::white(),
223 };
224
225 image.draw(&mut self.draw_list, params);
226 self.write_group(DrawMode::Image(image.texture()));
227 }
228
229 unsafe {
230 gl::Enable(gl::FRAMEBUFFER_SRGB);
231 }
232 let vao = VAO::new(&self.draw_list.vertices);
234 vao.bind();
235
236 let font_uniform_tex = self.font_program.get_uniform_location("tex");
237 let font_uniform_matrix = self.font_program.get_uniform_location("matrix");
238
239 let base_uniform_tex = self.base_program.get_uniform_location("tex");
240 let base_uniform_matrix = self.base_program.get_uniform_location("matrix");
241
242 for group in &self.groups {
243 match group.mode {
244 DrawMode::Font(font_handle) => {
245 let font = self.font(font_handle);
246
247 font.bind(0);
248 self.font_program.use_program();
249
250 self.font_program
251 .uniform_matrix4fv(font_uniform_matrix, false, &self.matrix);
252 self.font_program.uniform1i(font_uniform_tex, 0);
253
254 unsafe {
255 gl::DrawArrays(gl::POINTS, group.start as _, (group.end - group.start) as _)
256 };
257 }
258 DrawMode::Image(tex_handle) => {
259 let texture = self.texture(tex_handle);
260
261 texture.bind(0);
262 self.base_program.use_program();
263
264 self.base_program.uniform1i(base_uniform_tex, 0);
265 self.base_program
266 .uniform_matrix4fv(base_uniform_matrix, false, &self.matrix);
267
268 unsafe {
269 gl::Disable(gl::FRAMEBUFFER_SRGB);
270 }
271 unsafe {
272 gl::DrawArrays(gl::POINTS, group.start as _, (group.end - group.start) as _)
273 };
274 }
275 };
276 }
277 }
278
279 fn write_group_if_changed(&mut self, mode: &mut Option<DrawMode>, desired_mode: DrawMode) {
280 match mode {
281 None => *mode = Some(desired_mode),
282 Some(cur_mode) => {
283 if *cur_mode != desired_mode {
284 self.write_group(*cur_mode);
285 *mode = Some(desired_mode);
286 }
287 }
288 }
289 }
290
291 fn write_group(&mut self, mode: DrawMode) {
292 let end = self.draw_list.vertices.len();
293 let start = match self.groups.last() {
295 None => 0,
296 Some(group) => group.end,
297 };
298 self.groups.push(DrawGroup { start, end, mode });
299 }
300}
301
302impl Renderer for GLRenderer {
303 fn register_texture(
304 &mut self,
305 handle: TextureHandle,
306 image_data: &[u8],
307 dimensions: (u32, u32),
308 ) -> Result<TextureData, crate::Error> {
309 let gl_texture = GLTexture::new(
310 image_data,
311 dimensions,
312 gl::LINEAR,
313 gl::CLAMP_TO_EDGE,
314 gl::RGBA,
315 gl::RGBA8,
316 );
317
318 assert!(handle.id() <= self.textures.len());
319 if handle.id() == self.textures.len() {
320 self.textures.push(gl_texture);
321 } else {
322 self.textures[handle.id()] = gl_texture;
323 }
324
325 Ok(TextureData::new(handle, dimensions.0, dimensions.1))
326 }
327
328 fn register_font(
329 &mut self,
330 handle: FontHandle,
331 source: &FontSource,
332 ranges: &[CharacterRange],
333 size: f32,
334 scale: f32,
335 ) -> Result<Font, crate::Error> {
336 let font = &source.font;
337
338 let writer = FontTextureWriter::new(font, ranges, size, scale);
339
340 let writer_out = writer.write(handle, ranges)?;
341
342 let font_texture = GLTexture::new(
343 &writer_out.data,
344 (writer_out.tex_width, writer_out.tex_height),
345 gl::NEAREST,
346 gl::CLAMP_TO_BORDER,
347 gl::RED,
348 gl::R8,
349 );
350
351 assert!(handle.id() <= self.fonts.len());
352 if handle.id() == self.fonts.len() {
353 self.fonts.push(font_texture);
354 } else {
355 self.fonts[handle.id()] = font_texture;
356 }
357
358 Ok(writer_out.font)
359 }
360}
361
362struct DrawGroup {
363 start: usize,
364 end: usize,
365 mode: DrawMode,
366}
367
368const VERT_SHADER_SRC: &str = r#"
370 #version 330
371
372 layout(location = 0) in vec2 position;
373 layout(location = 1) in vec2 size;
374 layout(location = 2) in vec2 tex0;
375 layout(location = 3) in vec2 tex1;
376 layout(location = 4) in vec4 color;
377 layout(location = 5) in vec2 clip_pos;
378 layout(location = 6) in vec2 clip_size;
379
380 out vec2 g_size;
381 out vec2 g_tex0;
382 out vec2 g_tex1;
383 out vec4 g_color;
384 out vec2 g_clip_pos;
385 out vec2 g_clip_size;
386
387 void main() {
388 gl_Position = vec4(position, 0.0, 1.0);
389
390 g_size = size;
391 g_tex0 = tex0;
392 g_tex1 = tex1;
393 g_color = color;
394 g_clip_pos = clip_pos;
395 g_clip_size = clip_size;
396 }
397"#;
398
399const GEOM_SHADER_SRC: &str = r#"
400 #version 150
401
402 layout (points) in;
403 layout (triangle_strip, max_vertices = 4) out;
404
405 in vec2 g_size[];
406 in vec2 g_tex0[];
407 in vec2 g_tex1[];
408 in vec4 g_color[];
409 in vec2 g_clip_pos[];
410 in vec2 g_clip_size[];
411
412 out vec2 v_tex_coords;
413 out vec4 v_color;
414
415 uniform mat4 matrix;
416
417 void main() {
418 vec4 base = gl_in[0].gl_Position;
419
420 vec2 clip_pos = g_clip_pos[0];
421 vec2 clip_size = g_clip_size[0];
422
423 // draw the rectangle using 2 triangles in triangle_strip
424
425 // [0, 0] vertex
426 vec4 position = base;
427 gl_ClipDistance[0] = position.x - clip_pos.x;
428 gl_ClipDistance[1] = clip_pos.x + clip_size.x - position.x;
429 gl_ClipDistance[2] = position.y - clip_pos.y;
430 gl_ClipDistance[3] = clip_pos.y + clip_size.y - position.y;
431 gl_Position = matrix * position;
432 v_tex_coords = g_tex0[0];
433 v_color = g_color[0];
434 EmitVertex();
435
436 // [0, 1] vertex
437 position = base + vec4(0.0, g_size[0].y, 0.0, 0.0);
438 gl_ClipDistance[0] = position.x - clip_pos.x;
439 gl_ClipDistance[1] = clip_pos.x + clip_size.x - position.x;
440 gl_ClipDistance[2] = position.y - clip_pos.y;
441 gl_ClipDistance[3] = clip_pos.y + clip_size.y - position.y;
442 gl_Position = matrix * position;
443 v_tex_coords = vec2(g_tex0[0].x, g_tex1[0].y);
444 v_color = g_color[0];
445 EmitVertex();
446
447 // [1, 0] vertex
448 position = base + vec4(g_size[0].x, 0.0, 0.0, 0.0);
449 gl_ClipDistance[0] = position.x - clip_pos.x;
450 gl_ClipDistance[1] = clip_pos.x + clip_size.x - position.x;
451 gl_ClipDistance[2] = position.y - clip_pos.y;
452 gl_ClipDistance[3] = clip_pos.y + clip_size.y - position.y;
453 gl_Position = matrix * position;
454 v_tex_coords = vec2(g_tex1[0].x, g_tex0[0].y);
455 v_color = g_color[0];
456 EmitVertex();
457
458 // [1, 1] vertex
459 position = base + vec4(g_size[0].x, g_size[0].y, 0.0, 0.0);
460 gl_ClipDistance[0] = position.x - clip_pos.x;
461 gl_ClipDistance[1] = clip_pos.x + clip_size.x - position.x;
462 gl_ClipDistance[2] = position.y - clip_pos.y;
463 gl_ClipDistance[3] = clip_pos.y + clip_size.y - position.y;
464 gl_Position = matrix * position;
465 v_tex_coords = g_tex1[0];
466 v_color = g_color[0];
467 EmitVertex();
468
469 EndPrimitive();
470 }
471"#;
472
473const FRAGMENT_SHADER_SRC: &str = r#"
474 #version 150
475
476 in vec2 v_tex_coords;
477 in vec4 v_color;
478
479 out vec4 color;
480
481 uniform sampler2D tex;
482
483 void main() {
484 color = v_color * texture(tex, v_tex_coords);
485 }
486"#;
487
488const FONT_FRAGMENT_SHADER_SRC: &str = r#"
489 #version 150
490
491 in vec2 v_tex_coords;
492 in vec4 v_color;
493
494 out vec4 color;
495
496 uniform sampler2D tex;
497
498 void main() {
499 color = vec4(v_color.rgb, texture(tex, v_tex_coords).r);
500 }
501"#;
502
503struct GLDrawList {
504 vertices: Vec<GLVertex>,
505}
506
507impl GLDrawList {
508 fn new() -> Self {
509 GLDrawList {
510 vertices: Vec::new(),
511 }
512 }
513
514 fn clear(&mut self) {
515 self.vertices.clear();
516 }
517}
518
519impl DrawList for GLDrawList {
520 fn len(&self) -> usize {
521 self.vertices.len()
522 }
523
524 fn back_adjust_positions(&mut self, since_index: usize, amount: Point) {
525 for vert in self.vertices.iter_mut().skip(since_index) {
526 vert.position[0] += amount.x;
527 vert.position[1] += amount.y;
528 }
529 }
530
531 fn push_rect(
532 &mut self,
533 pos: [f32; 2],
534 size: [f32; 2],
535 tex: [TexCoord; 2],
536 color: Color,
537 clip: Rect,
538 ) {
539 let vert = GLVertex {
540 position: pos,
541 size,
542 tex0: [tex[0].x(), tex[0].y()],
543 tex1: [tex[1].x(), tex[1].y()],
544 color: color.into(),
545 clip_pos: clip.pos.into(),
546 clip_size: clip.size.into(),
547 };
548
549 self.vertices.push(vert);
550 }
551}
552
553#[derive(Copy, Clone)]
554#[repr(C)]
555pub(crate) struct GLVertex {
556 pub position: [f32; 2],
557 pub size: [f32; 2],
558 pub tex0: [f32; 2],
559 pub tex1: [f32; 2],
560 pub color: [f32; 4],
561 pub clip_pos: [f32; 2],
562 pub clip_size: [f32; 2],
563}
564
565#[derive(Debug)]
567pub enum GlError {
568 GlutinCreation(glutin::CreationError),
570
571 GlutinContext(glutin::ContextError),
573}
574
575impl std::fmt::Display for GlError {
576 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
577 use self::GlError::*;
578 match self {
579 GlutinCreation(e) => write!(f, "Error creating Glutin context: {}", e),
580 GlutinContext(e) => write!(f, "Error in OpenGL context: {}", e),
581 }
582 }
583}
584
585impl Error for GlError {
586 fn source(&self) -> Option<&(dyn Error + 'static)> {
587 use self::GlError::*;
588 match self {
589 GlutinCreation(e) => Some(e),
590 GlutinContext(e) => Some(e),
591 }
592 }
593}
594
595impl From<glutin::CreationError> for GlError {
596 fn from(e: glutin::CreationError) -> GlError {
597 GlError::GlutinCreation(e)
598 }
599}
600
601impl From<glutin::ContextError> for GlError {
602 fn from(e: glutin::ContextError) -> GlError {
603 GlError::GlutinContext(e)
604 }
605}