1use std::mem;
2use std::rc::Rc;
3
4#[cfg(not(target_arch = "wasm32"))]
5use std::ffi::c_void;
6
7use fnv::FnvHashMap;
8use imgref::ImgVec;
9use rgb::RGBA8;
10
11use crate::{
12 renderer::{
13 GlyphTexture,
14 ImageId,
15 Vertex,
16 },
17 BlendFactor,
18 Color,
19 CompositeOperationState,
20 ErrorKind,
21 FillRule,
22 ImageFilter,
23 ImageInfo,
24 ImageSource,
25 ImageStore,
26 Scissor,
27};
28
29use glow::HasContext;
30
31use super::{
32 Command,
33 CommandType,
34 Params,
35 RenderTarget,
36 Renderer,
37 ShaderType,
38};
39
40mod program;
41use program::MainProgram;
42
43mod gl_texture;
44use gl_texture::GlTexture;
45
46mod framebuffer;
47use framebuffer::Framebuffer;
48
49mod uniform_array;
50use uniform_array::UniformArray;
51
52pub struct OpenGl {
54 debug: bool,
55 antialias: bool,
56 is_opengles_2_0: bool,
57 view: [f32; 2],
58 screen_view: [f32; 2],
59 main_program: MainProgram,
60 vert_arr: Option<<glow::Context as glow::HasContext>::VertexArray>,
61 vert_buff: Option<<glow::Context as glow::HasContext>::Buffer>,
62 framebuffers: FnvHashMap<ImageId, Result<Framebuffer, ErrorKind>>,
63 context: Rc<glow::Context>,
64 screen_target: Option<Framebuffer>,
65 current_render_target: RenderTarget,
66}
67
68impl OpenGl {
69 #[cfg(not(target_arch = "wasm32"))]
71 pub fn new<F>(load_fn: F) -> Result<Self, ErrorKind>
72 where
73 F: FnMut(&str) -> *const c_void,
74 {
75 let context = unsafe { glow::Context::from_loader_function(load_fn) };
76 let version = unsafe { context.get_parameter_string(glow::VERSION) };
77 let is_opengles_2_0 = version.starts_with("OpenGL ES 2.");
78 Self::new_from_context(context, is_opengles_2_0)
79 }
80
81 #[cfg(target_arch = "wasm32")]
82 pub fn new_from_html_canvas(canvas: &web_sys::HtmlCanvasElement) -> Result<Self, ErrorKind> {
83 let mut attrs = web_sys::WebGlContextAttributes::new();
84 attrs.stencil(true);
85 attrs.antialias(false);
86
87 use wasm_bindgen::JsCast;
88 let webgl1_context = canvas
89 .get_context_with_context_options("webgl", attrs.as_ref())
90 .map_err(|_| ErrorKind::GeneralError("Canvas::getContext failed to retrieve WebGL 1 context".to_owned()))?
91 .unwrap()
92 .dyn_into::<web_sys::WebGlRenderingContext>()
93 .unwrap();
94
95 let context = glow::Context::from_webgl1_context(webgl1_context);
96 Self::new_from_context(context, true)
97 }
98
99 fn new_from_context(context: glow::Context, is_opengles_2_0: bool) -> Result<Self, ErrorKind> {
100 let debug = cfg!(debug_assertions);
101 let antialias = true;
102
103 let context = Rc::new(context);
104
105 let main_program = MainProgram::new(&context, antialias)?;
106
107 let mut opengl = OpenGl {
108 debug,
109 antialias,
110 is_opengles_2_0: false,
111 view: [0.0, 0.0],
112 screen_view: [0.0, 0.0],
113 main_program,
114 vert_arr: Default::default(),
115 vert_buff: Default::default(),
116 framebuffers: Default::default(),
117 context,
118 screen_target: None,
119 current_render_target: RenderTarget::Screen,
120 };
121
122 unsafe {
123 opengl.is_opengles_2_0 = is_opengles_2_0;
124
125 opengl.vert_arr = opengl.context.create_vertex_array().ok();
126 opengl.vert_buff = opengl.context.create_buffer().ok();
127 }
128
129 Ok(opengl)
130 }
131
132 pub fn is_opengles(&self) -> bool {
134 self.is_opengles_2_0
135 }
136
137 fn check_error(&self, label: &str) {
138 if !self.debug {
139 return;
140 }
141
142 let err = unsafe { self.context.get_error() };
143
144 if err == glow::NO_ERROR {
145 return;
146 }
147
148 let message = match err {
149 glow::INVALID_ENUM => "Invalid enum",
150 glow::INVALID_VALUE => "Invalid value",
151 glow::INVALID_OPERATION => "Invalid operation",
152 glow::OUT_OF_MEMORY => "Out of memory",
153 glow::INVALID_FRAMEBUFFER_OPERATION => "Invalid framebuffer operation",
154 _ => "Unknown error",
155 };
156
157 eprintln!("({}) Error on {} - {}", err, label, message);
158 }
159
160 fn gl_factor(factor: BlendFactor) -> u32 {
161 match factor {
162 BlendFactor::Zero => glow::ZERO,
163 BlendFactor::One => glow::ONE,
164 BlendFactor::SrcColor => glow::SRC_COLOR,
165 BlendFactor::OneMinusSrcColor => glow::ONE_MINUS_SRC_COLOR,
166 BlendFactor::DstColor => glow::DST_COLOR,
167 BlendFactor::OneMinusDstColor => glow::ONE_MINUS_DST_COLOR,
168 BlendFactor::SrcAlpha => glow::SRC_ALPHA,
169 BlendFactor::OneMinusSrcAlpha => glow::ONE_MINUS_SRC_ALPHA,
170 BlendFactor::DstAlpha => glow::DST_ALPHA,
171 BlendFactor::OneMinusDstAlpha => glow::ONE_MINUS_DST_ALPHA,
172 BlendFactor::SrcAlphaSaturate => glow::SRC_ALPHA_SATURATE,
173 }
174 }
175
176 fn set_composite_operation(&self, blend_state: CompositeOperationState) {
177 unsafe {
178 self.context.blend_func_separate(
179 Self::gl_factor(blend_state.src_rgb),
180 Self::gl_factor(blend_state.dst_rgb),
181 Self::gl_factor(blend_state.src_alpha),
182 Self::gl_factor(blend_state.dst_alpha),
183 );
184 }
185 }
186
187 fn convex_fill(&self, images: &ImageStore<GlTexture>, cmd: &Command, gpu_paint: &Params) {
188 self.set_uniforms(images, gpu_paint, cmd.image, cmd.glyph_texture);
189
190 for drawable in &cmd.drawables {
191 if let Some((start, count)) = drawable.fill_verts {
192 unsafe {
193 self.context.draw_arrays(glow::TRIANGLE_FAN, start as i32, count as i32);
194 }
195 }
196
197 if let Some((start, count)) = drawable.stroke_verts {
198 unsafe {
199 self.context
200 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
201 }
202 }
203 }
204
205 self.check_error("convex_fill");
206 }
207
208 fn concave_fill(&self, images: &ImageStore<GlTexture>, cmd: &Command, stencil_paint: &Params, fill_paint: &Params) {
209 unsafe {
210 self.context.enable(glow::STENCIL_TEST);
211 self.context.stencil_mask(0xff);
212 self.context.stencil_func(glow::ALWAYS, 0, 0xff);
213 self.context.color_mask(false, false, false, false);
214 }
216
217 self.set_uniforms(images, stencil_paint, None, GlyphTexture::None);
218
219 unsafe {
220 self.context
221 .stencil_op_separate(glow::FRONT, glow::KEEP, glow::KEEP, glow::INCR_WRAP);
222 self.context
223 .stencil_op_separate(glow::BACK, glow::KEEP, glow::KEEP, glow::DECR_WRAP);
224 self.context.disable(glow::CULL_FACE);
225 }
226
227 for drawable in &cmd.drawables {
228 if let Some((start, count)) = drawable.fill_verts {
229 unsafe {
230 self.context.draw_arrays(glow::TRIANGLE_FAN, start as i32, count as i32);
231 }
232 }
233 }
234
235 unsafe {
236 self.context.enable(glow::CULL_FACE);
237 self.context.color_mask(true, true, true, true);
239 }
241
242 self.set_uniforms(images, fill_paint, cmd.image, cmd.glyph_texture);
243
244 if self.antialias {
245 unsafe {
246 match cmd.fill_rule {
247 FillRule::NonZero => self.context.stencil_func(glow::EQUAL, 0x0, 0xff),
248 FillRule::EvenOdd => self.context.stencil_func(glow::EQUAL, 0x0, 0x1),
249 }
250
251 self.context.stencil_op(glow::KEEP, glow::KEEP, glow::KEEP);
252 }
253
254 for drawable in &cmd.drawables {
256 if let Some((start, count)) = drawable.stroke_verts {
257 unsafe {
258 self.context
259 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
260 }
261 }
262 }
263 }
264
265 unsafe {
266 match cmd.fill_rule {
267 FillRule::NonZero => self.context.stencil_func(glow::NOTEQUAL, 0x0, 0xff),
268 FillRule::EvenOdd => self.context.stencil_func(glow::NOTEQUAL, 0x0, 0x1),
269 }
270
271 self.context.stencil_op(glow::ZERO, glow::ZERO, glow::ZERO);
272
273 if let Some((start, count)) = cmd.triangles_verts {
274 self.context
275 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
276 }
277
278 self.context.disable(glow::STENCIL_TEST);
279 }
280
281 self.check_error("concave_fill");
282 }
283
284 fn stroke(&self, images: &ImageStore<GlTexture>, cmd: &Command, paint: &Params) {
285 self.set_uniforms(images, paint, cmd.image, cmd.glyph_texture);
286
287 for drawable in &cmd.drawables {
288 if let Some((start, count)) = drawable.stroke_verts {
289 unsafe {
290 self.context
291 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
292 }
293 }
294 }
295
296 self.check_error("stroke");
297 }
298
299 fn stencil_stroke(&self, images: &ImageStore<GlTexture>, cmd: &Command, paint1: &Params, paint2: &Params) {
300 unsafe {
301 self.context.enable(glow::STENCIL_TEST);
302 self.context.stencil_mask(0xff);
303
304 self.context.stencil_func(glow::EQUAL, 0x0, 0xff);
306 self.context.stencil_op(glow::KEEP, glow::KEEP, glow::INCR);
307 }
308
309 self.set_uniforms(images, paint2, cmd.image, cmd.glyph_texture);
310
311 for drawable in &cmd.drawables {
312 if let Some((start, count)) = drawable.stroke_verts {
313 unsafe {
314 self.context
315 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
316 }
317 }
318 }
319
320 self.set_uniforms(images, paint1, cmd.image, cmd.glyph_texture);
322
323 unsafe {
324 self.context.stencil_func(glow::EQUAL, 0x0, 0xff);
325 self.context.stencil_op(glow::KEEP, glow::KEEP, glow::KEEP);
326 }
327
328 for drawable in &cmd.drawables {
329 if let Some((start, count)) = drawable.stroke_verts {
330 unsafe {
331 self.context
332 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
333 }
334 }
335 }
336
337 unsafe {
338 self.context.color_mask(false, false, false, false);
340 self.context.stencil_func(glow::ALWAYS, 0x0, 0xff);
341 self.context.stencil_op(glow::ZERO, glow::ZERO, glow::ZERO);
342 }
343
344 for drawable in &cmd.drawables {
345 if let Some((start, count)) = drawable.stroke_verts {
346 unsafe {
347 self.context
348 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
349 }
350 }
351 }
352
353 unsafe {
354 self.context.color_mask(true, true, true, true);
355 self.context.disable(glow::STENCIL_TEST);
356 }
357
358 self.check_error("stencil_stroke");
359 }
360
361 fn triangles(&self, images: &ImageStore<GlTexture>, cmd: &Command, paint: &Params) {
362 self.set_uniforms(images, paint, cmd.image, cmd.glyph_texture);
363
364 if let Some((start, count)) = cmd.triangles_verts {
365 unsafe {
366 self.context.draw_arrays(glow::TRIANGLES, start as i32, count as i32);
367 }
368 }
369
370 self.check_error("triangles");
371 }
372
373 fn set_uniforms(
374 &self,
375 images: &ImageStore<GlTexture>,
376 paint: &Params,
377 image_tex: Option<ImageId>,
378 glyph_tex: GlyphTexture,
379 ) {
380 let arr = UniformArray::from(paint);
381 self.main_program.set_config(arr.as_slice());
382 self.check_error("set_uniforms uniforms");
383
384 let tex = image_tex
385 .and_then(|id| images.get(id))
386 .map(|tex| tex.id());
387
388 unsafe {
389 self.context.active_texture(glow::TEXTURE0);
390 self.context.bind_texture(glow::TEXTURE_2D, tex);
391 }
392
393 let glyphtex = match glyph_tex {
394 GlyphTexture::None => None,
395 GlyphTexture::AlphaMask(id) | GlyphTexture::ColorTexture(id) => images.get(id).map(|tex| tex.id()),
396 };
397
398 unsafe {
399 self.context.active_texture(glow::TEXTURE0 + 1);
400 self.context.bind_texture(glow::TEXTURE_2D, glyphtex);
401 }
402
403 self.check_error("set_uniforms texture");
404 }
405
406 fn clear_rect(&self, x: u32, y: u32, width: u32, height: u32, color: Color) {
407 unsafe {
408 self.context.enable(glow::SCISSOR_TEST);
409 self.context.scissor(
410 x as i32,
411 self.view[1] as i32 - (height as i32 + y as i32),
412 width as i32,
413 height as i32,
414 );
415 self.context.clear_color(color.r, color.g, color.b, color.a);
416 self.context.clear(glow::COLOR_BUFFER_BIT | glow::STENCIL_BUFFER_BIT);
417 self.context.disable(glow::SCISSOR_TEST);
418 }
419 }
420
421 fn set_target(&mut self, images: &ImageStore<GlTexture>, target: RenderTarget) {
422 self.current_render_target = target;
423 match (target, &self.screen_target) {
424 (RenderTarget::Screen, None) => unsafe {
425 Framebuffer::unbind(&self.context);
426 self.view = self.screen_view;
427 self.context.viewport(0, 0, self.view[0] as i32, self.view[1] as i32);
428 },
429 (RenderTarget::Screen, Some(framebuffer)) => {
430 framebuffer.bind();
431 self.view = self.screen_view;
432 unsafe {
433 self.context.viewport(0, 0, self.view[0] as i32, self.view[1] as i32);
434 }
435 }
436 (RenderTarget::Image(id), _) => {
437 let context = self.context.clone();
438 if let Some(texture) = images.get(id) {
439 if let Ok(fb) = self
440 .framebuffers
441 .entry(id)
442 .or_insert_with(|| Framebuffer::new(&context, texture))
443 {
444 fb.bind();
445
446 self.view[0] = texture.info().width() as f32;
447 self.view[1] = texture.info().height() as f32;
448
449 unsafe {
450 self.context
451 .viewport(0, 0, texture.info().width() as i32, texture.info().height() as i32);
452 }
453 }
454 }
455 }
456 }
457 }
458
459 pub fn set_screen_target(&mut self, framebuffer_object: Option<<glow::Context as glow::HasContext>::Framebuffer>) {
467 match framebuffer_object {
468 Some(fbo_id) => self.screen_target = Some(Framebuffer::from_external(&self.context, fbo_id)),
469 None => self.screen_target = None,
470 }
471 }
472
473 fn render_filtered_image(
474 &mut self,
475 images: &mut ImageStore<GlTexture>,
476 cmd: Command,
477 target_image: ImageId,
478 filter: ImageFilter,
479 ) {
480 match filter {
481 ImageFilter::GaussianBlur { sigma } => self.render_gaussian_blur(images, cmd, target_image, sigma),
482 }
483 }
484
485 fn render_gaussian_blur(
486 &mut self,
487 images: &mut ImageStore<GlTexture>,
488 mut cmd: Command,
489 target_image: ImageId,
490 sigma: f32,
491 ) {
492 let original_render_target = self.current_render_target;
493
494 let source_image_info = images.get(cmd.image.unwrap()).unwrap().info();
498
499 let image_paint = crate::Paint::image(
500 cmd.image.unwrap(),
501 0.,
502 0.,
503 source_image_info.width() as _,
504 source_image_info.height() as _,
505 0.,
506 1.,
507 );
508 let mut blur_params = Params::new(images, &image_paint, &Scissor::default(), 0., 0., 0.);
509 blur_params.shader_type = ShaderType::FilterImage.to_f32();
510
511 let gauss_coeff_x = 1. / ((2. * std::f32::consts::PI).sqrt() * sigma);
512 let gauss_coeff_y = f32::exp(-0.5 / (sigma * sigma));
513 let gauss_coeff_z = gauss_coeff_y * gauss_coeff_y;
514
515 blur_params.image_blur_filter_coeff[0] = gauss_coeff_x;
516 blur_params.image_blur_filter_coeff[1] = gauss_coeff_y;
517 blur_params.image_blur_filter_coeff[2] = gauss_coeff_z;
518
519 blur_params.image_blur_filter_direction = [1.0, 0.0];
520
521 blur_params.image_blur_filter_sigma = sigma.min(8.);
524
525 let horizontal_blur_buffer = images.alloc(self, source_image_info).unwrap();
526 self.set_target(images, RenderTarget::Image(horizontal_blur_buffer));
527 self.main_program.set_view(self.view);
528
529 self.clear_rect(
530 0,
531 0,
532 source_image_info.width() as _,
533 source_image_info.height() as _,
534 Color::rgbaf(0., 0., 0., 0.),
535 );
536
537 self.triangles(images, &cmd, &blur_params);
538
539 self.set_target(images, RenderTarget::Image(target_image));
540 self.main_program.set_view(self.view);
541
542 self.clear_rect(
543 0,
544 0,
545 source_image_info.width() as _,
546 source_image_info.height() as _,
547 Color::rgbaf(0., 0., 0., 0.),
548 );
549
550 blur_params.image_blur_filter_direction = [0.0, 1.0];
551
552 cmd.image = Some(horizontal_blur_buffer);
553
554 self.triangles(images, &cmd, &blur_params);
555
556 images.remove(self, horizontal_blur_buffer);
557
558 self.set_target(images, original_render_target);
560 self.main_program.set_view(self.view);
561 }
562}
563
564impl Renderer for OpenGl {
565 type Image = GlTexture;
566
567 fn set_size(&mut self, width: u32, height: u32, _dpi: f32) {
568 self.view[0] = width as f32;
569 self.view[1] = height as f32;
570
571 self.screen_view = self.view;
572
573 unsafe {
574 self.context.viewport(0, 0, width as i32, height as i32);
575 }
576 }
577
578 fn render(&mut self, images: &mut ImageStore<Self::Image>, verts: &[Vertex], commands: Vec<Command>) {
579 self.main_program.bind();
580
581 unsafe {
582 self.context.enable(glow::CULL_FACE);
583
584 self.context.cull_face(glow::BACK);
585 self.context.front_face(glow::CCW);
586 self.context.enable(glow::BLEND);
587 self.context.disable(glow::DEPTH_TEST);
588 self.context.disable(glow::SCISSOR_TEST);
589 self.context.color_mask(true, true, true, true);
590 self.context.stencil_mask(0xffff_ffff);
591 self.context.stencil_op(glow::KEEP, glow::KEEP, glow::KEEP);
592 self.context.stencil_func(glow::ALWAYS, 0, 0xffff_ffff);
593 self.context.active_texture(glow::TEXTURE0);
594 self.context.bind_texture(glow::TEXTURE_2D, None);
595 self.context.active_texture(glow::TEXTURE0 + 1);
596 self.context.bind_texture(glow::TEXTURE_2D, None);
597
598 self.context.bind_vertex_array(self.vert_arr);
599
600 let vertex_size = mem::size_of::<Vertex>();
601
602 self.context.bind_buffer(glow::ARRAY_BUFFER, self.vert_buff);
603 self.context
604 .buffer_data_u8_slice(glow::ARRAY_BUFFER, verts.align_to().1, glow::STREAM_DRAW);
605
606 self.context.enable_vertex_attrib_array(0);
607 self.context.enable_vertex_attrib_array(1);
608
609 self.context
610 .vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, vertex_size as i32, 0);
611 self.context.vertex_attrib_pointer_f32(
612 1,
613 2,
614 glow::FLOAT,
615 false,
616 vertex_size as i32,
617 2 * mem::size_of::<f32>() as i32,
618 );
619 }
620
621 self.main_program.set_tex(0);
623 self.main_program.set_glyphtex(1);
624
625 self.check_error("render prepare");
626
627 for cmd in commands.into_iter() {
628 self.set_composite_operation(cmd.composite_operation);
629
630 match cmd.cmd_type {
631 CommandType::ConvexFill { ref params } => self.convex_fill(images, &cmd, params),
632 CommandType::ConcaveFill {
633 ref stencil_params,
634 ref fill_params,
635 } => self.concave_fill(images, &cmd, stencil_params, fill_params),
636 CommandType::Stroke { ref params } => self.stroke(images, &cmd, params),
637 CommandType::StencilStroke {
638 ref params1,
639 ref params2,
640 } => self.stencil_stroke(images, &cmd, params1, params2),
641 CommandType::Triangles { ref params } => self.triangles(images, &cmd, params),
642 CommandType::ClearRect {
643 x,
644 y,
645 width,
646 height,
647 color,
648 } => {
649 self.clear_rect(x, y, width, height, color);
650 }
651 CommandType::SetRenderTarget(target) => {
652 self.set_target(images, target);
653 self.main_program.set_view(self.view);
654 }
655 CommandType::RenderFilteredImage { target_image, filter } => {
656 self.render_filtered_image(images, cmd, target_image, filter)
657 }
658 }
659 }
660
661 unsafe {
662 self.context.disable_vertex_attrib_array(0);
663 self.context.disable_vertex_attrib_array(1);
664 self.context.bind_vertex_array(None);
665
666 self.context.disable(glow::CULL_FACE);
667 self.context.bind_buffer(glow::ARRAY_BUFFER, None);
668 self.context.bind_texture(glow::TEXTURE_2D, None);
669 }
670
671 self.main_program.unbind();
672
673 self.check_error("render done");
674 }
675
676 fn alloc_image(&mut self, info: ImageInfo) -> Result<Self::Image, ErrorKind> {
677 Self::Image::new(&self.context, info, self.is_opengles_2_0)
678 }
679
680 fn update_image(
681 &mut self,
682 image: &mut Self::Image,
683 data: ImageSource,
684 x: usize,
685 y: usize,
686 ) -> Result<(), ErrorKind> {
687 image.update(data, x, y, self.is_opengles_2_0)
688 }
689
690 fn delete_image(&mut self, image: Self::Image, image_id: ImageId) {
691 self.framebuffers.remove(&image_id);
692 image.delete();
693 }
694
695 fn screenshot(&mut self) -> Result<ImgVec<RGBA8>, ErrorKind> {
696 let w = self.view[0] as usize;
698 let h = self.view[1] as usize;
699
700 let mut image = ImgVec::new(
701 vec![
702 RGBA8 {
703 r: 255,
704 g: 255,
705 b: 255,
706 a: 255
707 };
708 w * h
709 ],
710 w,
711 h,
712 );
713
714 unsafe {
715 self.context.read_pixels(
716 0,
717 0,
718 self.view[0] as i32,
719 self.view[1] as i32,
720 glow::RGBA,
721 glow::UNSIGNED_BYTE,
722 glow::PixelPackData::Slice(image.buf_mut().align_to_mut().1),
723 );
724 }
725
726 let mut flipped = Vec::with_capacity(w * h);
727
728 for row in image.rows().rev() {
729 flipped.extend_from_slice(row);
730 }
731
732 Ok(ImgVec::new(flipped, w, h))
733 }
734}
735
736impl Drop for OpenGl {
737 fn drop(&mut self) {
738 if let Some(vert_arr) = self.vert_arr {
739 unsafe {
740 self.context.delete_vertex_array(vert_arr);
741 }
742 }
743
744 if let Some(vert_buff) = self.vert_buff {
745 unsafe {
746 self.context.delete_buffer(vert_buff);
747 }
748 }
749 }
750}