1use std::{mem, rc::Rc};
2
3#[cfg(not(target_arch = "wasm32"))]
4use std::ffi::c_void;
5
6#[cfg(all(feature = "glutin", not(target_arch = "wasm32")))]
7use glutin::display::GlDisplay;
8
9use fnv::FnvHashMap;
10use imgref::ImgVec;
11use rgb::RGBA8;
12
13use crate::{
14 renderer::{GlyphTexture, ImageId, Vertex},
15 BlendFactor, Color, CompositeOperationState, ErrorKind, FillRule, ImageFilter, ImageInfo, ImageSource, ImageStore,
16 Scissor,
17};
18
19use glow::HasContext;
20
21use super::{Command, CommandType, Params, RenderTarget, Renderer, ShaderType, SurfacelessRenderer};
22
23mod program;
24use program::MainProgram;
25
26mod gl_texture;
27use gl_texture::GlTexture;
28
29mod framebuffer;
30use framebuffer::Framebuffer;
31
32mod uniform_array;
33use uniform_array::UniformArray;
34
35/// Represents an OpenGL renderer.
36pub struct OpenGl {
37 debug: bool,
38 antialias: bool,
39 is_opengles_2_0: bool,
40 view: [f32; 2],
41 screen_view: [f32; 2],
42 // All types of the vertex/fragment shader, indexed by shader_type when has_glyph_texture is true
43 main_programs_with_glyph_texture: [Option<MainProgram>; 7],
44 // Same shader programs but with has_glyph_texture being false
45 main_programs_without_glyph_texture: [Option<MainProgram>; 7],
46 current_program: u8,
47 current_program_needs_glyph_texture: bool,
48 vert_arr: Option<<glow::Context as glow::HasContext>::VertexArray>,
49 vert_buff: Option<<glow::Context as glow::HasContext>::Buffer>,
50 framebuffers: FnvHashMap<ImageId, Result<Framebuffer, ErrorKind>>,
51 context: Rc<glow::Context>,
52 screen_target: Option<Framebuffer>,
53 current_render_target: RenderTarget,
54}
55
56impl OpenGl {
57 /// Creates a new OpenGL renderer from a function loader.
58 ///
59 /// # Safety
60 /// This function is unsafe because it requires a function loader that can load OpenGL functions
61 /// and create a valid OpenGL context.
62 #[cfg(not(target_arch = "wasm32"))]
63 pub unsafe fn new_from_function<F>(load_fn: F) -> Result<Self, ErrorKind>
64 where
65 F: FnMut(&str) -> *const c_void,
66 {
67 let context = glow::Context::from_loader_function(load_fn);
68 let version = context.get_parameter_string(glow::VERSION);
69 let is_opengles_2_0 = version.starts_with("OpenGL ES 2.");
70 Self::new_from_context(context, is_opengles_2_0)
71 }
72
73 /// Creates a new OpenGL renderer from a function loader that takes C-style strings.
74 ///
75 /// # Safety
76 /// This function is unsafe because it requires a function loader that can load OpenGL functions
77 /// and create a valid OpenGL context.
78 #[cfg(not(target_arch = "wasm32"))]
79 pub unsafe fn new_from_function_cstr<F>(load_fn: F) -> Result<Self, ErrorKind>
80 where
81 F: FnMut(&std::ffi::CStr) -> *const c_void,
82 {
83 let context = glow::Context::from_loader_function_cstr(load_fn);
84 let version = context.get_parameter_string(glow::VERSION);
85 let is_opengles_2_0 = version.starts_with("OpenGL ES 2.");
86 Self::new_from_context(context, is_opengles_2_0)
87 }
88
89 /// Creates a new OpenGL renderer from a Glutin display.
90 #[cfg(all(feature = "glutin", not(target_arch = "wasm32")))]
91 pub fn new_from_glutin_display(display: &impl GlDisplay) -> Result<Self, ErrorKind> {
92 unsafe { OpenGl::new_from_function_cstr(|s| display.get_proc_address(s).cast()) }
93 }
94
95 /// Creates a new OpenGL renderer from an HTML canvas element in a WASM32 target.
96 #[cfg(target_arch = "wasm32")]
97 pub fn new_from_html_canvas(canvas: &web_sys::HtmlCanvasElement) -> Result<Self, ErrorKind> {
98 let attrs = web_sys::WebGlContextAttributes::new();
99 attrs.set_stencil(true);
100 attrs.set_antialias(false);
101
102 use wasm_bindgen::JsCast;
103 let webgl2_context = match canvas.get_context_with_context_options("webgl2", &attrs) {
104 Ok(Some(context)) => context.dyn_into::<web_sys::WebGl2RenderingContext>().unwrap(),
105 _ => {
106 return Err(ErrorKind::GeneralError(
107 "Canvas::getContext failed to retrieve WebGL 2 context".to_owned(),
108 ))
109 }
110 };
111
112 let context = glow::Context::from_webgl2_context(webgl2_context);
113 Self::new_from_context(context, true)
114 }
115
116 fn new_from_context(context: glow::Context, is_opengles_2_0: bool) -> Result<Self, ErrorKind> {
117 let debug = cfg!(debug_assertions);
118 let antialias = true;
119
120 let context = Rc::new(context);
121
122 let generate_shader_program_variants = |with_glyph_texture| -> Result<_, ErrorKind> {
123 Ok([
124 Some(MainProgram::new(
125 &context,
126 antialias,
127 ShaderType::FillGradient,
128 with_glyph_texture,
129 )?),
130 Some(MainProgram::new(
131 &context,
132 antialias,
133 ShaderType::FillImage,
134 with_glyph_texture,
135 )?),
136 if with_glyph_texture {
137 // No stencil fill with glyph texture
138 None
139 } else {
140 Some(MainProgram::new(&context, antialias, ShaderType::Stencil, false)?)
141 },
142 Some(MainProgram::new(
143 &context,
144 antialias,
145 ShaderType::FillImageGradient,
146 with_glyph_texture,
147 )?),
148 if with_glyph_texture {
149 // Image filter is unrelated to glyph rendering
150 None
151 } else {
152 Some(MainProgram::new(&context, antialias, ShaderType::FilterImage, false)?)
153 },
154 Some(MainProgram::new(
155 &context,
156 antialias,
157 ShaderType::FillColor,
158 with_glyph_texture,
159 )?),
160 if with_glyph_texture {
161 // Texture blitting is unrelated to glyph rendering
162 None
163 } else {
164 Some(MainProgram::new(
165 &context,
166 antialias,
167 ShaderType::TextureCopyUnclipped,
168 false,
169 )?)
170 },
171 ])
172 };
173
174 let main_programs_with_glyph_texture = generate_shader_program_variants(true)?;
175 let main_programs_without_glyph_texture = generate_shader_program_variants(false)?;
176
177 let mut opengl = Self {
178 debug,
179 antialias,
180 is_opengles_2_0: false,
181 view: [0.0, 0.0],
182 screen_view: [0.0, 0.0],
183 main_programs_with_glyph_texture,
184 main_programs_without_glyph_texture,
185 current_program: 0,
186 current_program_needs_glyph_texture: true,
187 vert_arr: None,
188 vert_buff: None,
189 framebuffers: Default::default(),
190 context,
191 screen_target: None,
192 current_render_target: RenderTarget::Screen,
193 };
194
195 unsafe {
196 opengl.is_opengles_2_0 = is_opengles_2_0;
197
198 opengl.vert_arr = opengl.context.create_vertex_array().ok();
199 opengl.vert_buff = opengl.context.create_buffer().ok();
200 }
201
202 Ok(opengl)
203 }
204
205 /// Checks if the renderer is using OpenGL ES.
206 pub fn is_opengles(&self) -> bool {
207 self.is_opengles_2_0
208 }
209
210 fn check_error(&self, label: &str) {
211 if !self.debug {
212 return;
213 }
214
215 let err = unsafe { self.context.get_error() };
216
217 if err == glow::NO_ERROR {
218 return;
219 }
220
221 let message = match err {
222 glow::INVALID_ENUM => "Invalid enum",
223 glow::INVALID_VALUE => "Invalid value",
224 glow::INVALID_OPERATION => "Invalid operation",
225 glow::OUT_OF_MEMORY => "Out of memory",
226 glow::INVALID_FRAMEBUFFER_OPERATION => "Invalid framebuffer operation",
227 _ => "Unknown error",
228 };
229
230 log::error!("({err}) Error on {label} - {message}");
231 }
232
233 fn gl_factor(factor: BlendFactor) -> u32 {
234 match factor {
235 BlendFactor::Zero => glow::ZERO,
236 BlendFactor::One => glow::ONE,
237 BlendFactor::SrcColor => glow::SRC_COLOR,
238 BlendFactor::OneMinusSrcColor => glow::ONE_MINUS_SRC_COLOR,
239 BlendFactor::DstColor => glow::DST_COLOR,
240 BlendFactor::OneMinusDstColor => glow::ONE_MINUS_DST_COLOR,
241 BlendFactor::SrcAlpha => glow::SRC_ALPHA,
242 BlendFactor::OneMinusSrcAlpha => glow::ONE_MINUS_SRC_ALPHA,
243 BlendFactor::DstAlpha => glow::DST_ALPHA,
244 BlendFactor::OneMinusDstAlpha => glow::ONE_MINUS_DST_ALPHA,
245 BlendFactor::SrcAlphaSaturate => glow::SRC_ALPHA_SATURATE,
246 }
247 }
248
249 fn set_composite_operation(&self, blend_state: CompositeOperationState) {
250 unsafe {
251 self.context.blend_func_separate(
252 Self::gl_factor(blend_state.src_rgb),
253 Self::gl_factor(blend_state.dst_rgb),
254 Self::gl_factor(blend_state.src_alpha),
255 Self::gl_factor(blend_state.dst_alpha),
256 );
257 }
258 }
259
260 fn convex_fill(&mut self, images: &ImageStore<GlTexture>, cmd: &Command, gpu_paint: &Params) {
261 self.set_uniforms(images, gpu_paint, cmd.image, cmd.glyph_texture);
262
263 for drawable in &cmd.drawables {
264 if let Some((start, count)) = drawable.fill_verts {
265 unsafe {
266 self.context.draw_arrays(glow::TRIANGLES, start as i32, count as i32);
267 }
268 }
269
270 if let Some((start, count)) = drawable.stroke_verts {
271 unsafe {
272 self.context
273 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
274 }
275 }
276 }
277
278 self.check_error("convex_fill");
279 }
280
281 fn concave_fill(
282 &mut self,
283 images: &ImageStore<GlTexture>,
284 cmd: &Command,
285 stencil_paint: &Params,
286 fill_paint: &Params,
287 ) {
288 unsafe {
289 self.context.enable(glow::STENCIL_TEST);
290 self.context.stencil_mask(0xff);
291 self.context.stencil_func(glow::ALWAYS, 0, 0xff);
292 self.context.color_mask(false, false, false, false);
293 //glow::DepthMask(glow::FALSE);
294 }
295
296 self.set_uniforms(images, stencil_paint, None, GlyphTexture::None);
297
298 unsafe {
299 self.context
300 .stencil_op_separate(glow::FRONT, glow::KEEP, glow::KEEP, glow::INCR_WRAP);
301 self.context
302 .stencil_op_separate(glow::BACK, glow::KEEP, glow::KEEP, glow::DECR_WRAP);
303 self.context.disable(glow::CULL_FACE);
304 }
305
306 for drawable in &cmd.drawables {
307 if let Some((start, count)) = drawable.fill_verts {
308 unsafe {
309 self.context.draw_arrays(glow::TRIANGLES, start as i32, count as i32);
310 }
311 }
312 }
313
314 unsafe {
315 self.context.enable(glow::CULL_FACE);
316 // Draw anti-aliased pixels
317 self.context.color_mask(true, true, true, true);
318 //glow::DepthMask(glow::TRUE);
319 }
320
321 self.set_uniforms(images, fill_paint, cmd.image, cmd.glyph_texture);
322
323 if self.antialias {
324 unsafe {
325 match cmd.fill_rule {
326 FillRule::NonZero => self.context.stencil_func(glow::EQUAL, 0x0, 0xff),
327 FillRule::EvenOdd => self.context.stencil_func(glow::EQUAL, 0x0, 0x1),
328 }
329
330 self.context.stencil_op(glow::KEEP, glow::KEEP, glow::KEEP);
331 }
332
333 // draw fringes
334 for drawable in &cmd.drawables {
335 if let Some((start, count)) = drawable.stroke_verts {
336 unsafe {
337 self.context
338 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
339 }
340 }
341 }
342 }
343
344 unsafe {
345 match cmd.fill_rule {
346 FillRule::NonZero => self.context.stencil_func(glow::NOTEQUAL, 0x0, 0xff),
347 FillRule::EvenOdd => self.context.stencil_func(glow::NOTEQUAL, 0x0, 0x1),
348 }
349
350 self.context.stencil_op(glow::ZERO, glow::ZERO, glow::ZERO);
351
352 if let Some((start, count)) = cmd.triangles_verts {
353 self.context
354 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
355 }
356
357 self.context.disable(glow::STENCIL_TEST);
358 }
359
360 self.check_error("concave_fill");
361 }
362
363 fn stroke(&mut self, images: &ImageStore<GlTexture>, cmd: &Command, paint: &Params) {
364 self.set_uniforms(images, paint, cmd.image, cmd.glyph_texture);
365
366 for drawable in &cmd.drawables {
367 if let Some((start, count)) = drawable.stroke_verts {
368 unsafe {
369 self.context
370 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
371 }
372 }
373 }
374
375 self.check_error("stroke");
376 }
377
378 fn stencil_stroke(&mut self, images: &ImageStore<GlTexture>, cmd: &Command, paint1: &Params, paint2: &Params) {
379 unsafe {
380 self.context.enable(glow::STENCIL_TEST);
381 self.context.stencil_mask(0xff);
382
383 // Fill the stroke base without overlap
384 self.context.stencil_func(glow::EQUAL, 0x0, 0xff);
385 self.context.stencil_op(glow::KEEP, glow::KEEP, glow::INCR);
386 }
387
388 self.set_uniforms(images, paint2, cmd.image, cmd.glyph_texture);
389
390 for drawable in &cmd.drawables {
391 if let Some((start, count)) = drawable.stroke_verts {
392 unsafe {
393 self.context
394 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
395 }
396 }
397 }
398
399 // Draw anti-aliased pixels.
400 self.set_uniforms(images, paint1, cmd.image, cmd.glyph_texture);
401
402 unsafe {
403 self.context.stencil_func(glow::EQUAL, 0x0, 0xff);
404 self.context.stencil_op(glow::KEEP, glow::KEEP, glow::KEEP);
405 }
406
407 for drawable in &cmd.drawables {
408 if let Some((start, count)) = drawable.stroke_verts {
409 unsafe {
410 self.context
411 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
412 }
413 }
414 }
415
416 unsafe {
417 // Clear stencil buffer.
418 self.context.color_mask(false, false, false, false);
419 self.context.stencil_func(glow::ALWAYS, 0x0, 0xff);
420 self.context.stencil_op(glow::ZERO, glow::ZERO, glow::ZERO);
421 }
422
423 for drawable in &cmd.drawables {
424 if let Some((start, count)) = drawable.stroke_verts {
425 unsafe {
426 self.context
427 .draw_arrays(glow::TRIANGLE_STRIP, start as i32, count as i32);
428 }
429 }
430 }
431
432 unsafe {
433 self.context.color_mask(true, true, true, true);
434 self.context.disable(glow::STENCIL_TEST);
435 }
436
437 self.check_error("stencil_stroke");
438 }
439
440 fn triangles(&mut self, images: &ImageStore<GlTexture>, cmd: &Command, paint: &Params) {
441 self.set_uniforms(images, paint, cmd.image, cmd.glyph_texture);
442
443 if let Some((start, count)) = cmd.triangles_verts {
444 unsafe {
445 self.context.draw_arrays(glow::TRIANGLES, start as i32, count as i32);
446 }
447 }
448
449 self.check_error("triangles");
450 }
451
452 fn set_uniforms(
453 &mut self,
454 images: &ImageStore<GlTexture>,
455 paint: &Params,
456 image_tex: Option<ImageId>,
457 glyph_tex: GlyphTexture,
458 ) {
459 self.select_main_program(paint);
460 let arr = UniformArray::from(paint);
461 self.main_program().set_config(arr.as_slice());
462 self.check_error("set_uniforms uniforms");
463
464 let tex = image_tex.and_then(|id| images.get(id)).map(GlTexture::id);
465
466 unsafe {
467 self.context.active_texture(glow::TEXTURE0);
468 self.context.bind_texture(glow::TEXTURE_2D, tex);
469 }
470
471 let glyphtex = glyph_tex.image_id().and_then(|id| images.get(id).map(GlTexture::id));
472
473 unsafe {
474 self.context.active_texture(glow::TEXTURE0 + 1);
475 self.context.bind_texture(glow::TEXTURE_2D, glyphtex);
476 }
477
478 self.check_error("set_uniforms texture");
479 }
480
481 fn clear_rect(&self, x: u32, y: u32, width: u32, height: u32, color: Color) {
482 unsafe {
483 self.context.enable(glow::SCISSOR_TEST);
484 self.context.scissor(
485 x as i32,
486 self.view[1] as i32 - (height as i32 + y as i32),
487 width as i32,
488 height as i32,
489 );
490 self.context.clear_color(color.r, color.g, color.b, color.a);
491 self.context.clear(glow::COLOR_BUFFER_BIT | glow::STENCIL_BUFFER_BIT);
492 self.context.disable(glow::SCISSOR_TEST);
493 }
494 }
495
496 fn set_target(&mut self, images: &ImageStore<GlTexture>, target: RenderTarget) {
497 self.current_render_target = target;
498 match (target, &self.screen_target) {
499 (RenderTarget::Screen, None) => unsafe {
500 Framebuffer::unbind(&self.context);
501 self.view = self.screen_view;
502 self.context.viewport(0, 0, self.view[0] as i32, self.view[1] as i32);
503 },
504 (RenderTarget::Screen, Some(framebuffer)) => {
505 framebuffer.bind();
506 self.view = self.screen_view;
507 unsafe {
508 self.context.viewport(0, 0, self.view[0] as i32, self.view[1] as i32);
509 }
510 }
511 (RenderTarget::Image(id), _) => {
512 let context = self.context.clone();
513 if let Some(texture) = images.get(id) {
514 if let Ok(fb) = self
515 .framebuffers
516 .entry(id)
517 .or_insert_with(|| Framebuffer::new(&context, texture))
518 {
519 fb.bind();
520
521 self.view[0] = texture.info().width() as f32;
522 self.view[1] = texture.info().height() as f32;
523
524 unsafe {
525 self.context
526 .viewport(0, 0, texture.info().width() as i32, texture.info().height() as i32);
527 }
528 }
529 }
530 }
531 }
532 }
533
534 /// Make the "Screen" `RenderTarget` actually render to a framebuffer object. This is useful when
535 /// embedding femtovg into another program where final composition is handled by an external task.
536 /// The given `framebuffer_object` must refer to a Framebuffer Object created on the current OpenGL
537 /// Context, and must have a depth & stencil attachment.
538 ///
539 /// Pass `None` to clear any previous Framebuffer Object ID that was passed and target rendering to
540 /// the default target (normally the window).
541 pub fn set_screen_target(&mut self, framebuffer_object: Option<<glow::Context as glow::HasContext>::Framebuffer>) {
542 match framebuffer_object {
543 Some(fbo_id) => self.screen_target = Some(Framebuffer::from_external(&self.context, fbo_id)),
544 None => self.screen_target = None,
545 }
546 }
547
548 fn render_filtered_image(
549 &mut self,
550 images: &mut ImageStore<GlTexture>,
551 cmd: Command,
552 target_image: ImageId,
553 filter: ImageFilter,
554 ) {
555 match filter {
556 ImageFilter::GaussianBlur { sigma } => self.render_gaussian_blur(images, cmd, target_image, sigma),
557 }
558 }
559
560 fn render_gaussian_blur(
561 &mut self,
562 images: &mut ImageStore<GlTexture>,
563 mut cmd: Command,
564 target_image: ImageId,
565 sigma: f32,
566 ) {
567 let original_render_target = self.current_render_target;
568
569 // The filtering happens in two passes, first a horizontal blur and then the vertial blur. The
570 // first pass therefore renders into an intermediate, temporarily allocated texture.
571
572 let source_image_info = images.get(cmd.image.unwrap()).unwrap().info();
573
574 let image_paint = crate::Paint::image(
575 cmd.image.unwrap(),
576 0.,
577 0.,
578 source_image_info.width() as _,
579 source_image_info.height() as _,
580 0.,
581 1.,
582 );
583 let mut blur_params = Params::new(
584 images,
585 &Default::default(),
586 &image_paint.flavor,
587 &Default::default(),
588 &Scissor::default(),
589 0.,
590 0.,
591 0.,
592 );
593 blur_params.shader_type = ShaderType::FilterImage;
594
595 let gauss_coeff_x = 1. / ((2. * std::f32::consts::PI).sqrt() * sigma);
596 let gauss_coeff_y = f32::exp(-0.5 / (sigma * sigma));
597 let gauss_coeff_z = gauss_coeff_y * gauss_coeff_y;
598
599 blur_params.image_blur_filter_coeff[0] = gauss_coeff_x;
600 blur_params.image_blur_filter_coeff[1] = gauss_coeff_y;
601 blur_params.image_blur_filter_coeff[2] = gauss_coeff_z;
602
603 blur_params.image_blur_filter_direction = [1.0, 0.0];
604
605 // GLES 2.0 does not allow non-constant loop indices, so limit the standard devitation to allow for a upper fixed limit
606 // on the number of iterations in the fragment shader.
607 blur_params.image_blur_filter_sigma = sigma.min(8.);
608
609 let horizontal_blur_buffer = images.alloc(self, source_image_info).unwrap();
610 self.set_target(images, RenderTarget::Image(horizontal_blur_buffer));
611 self.main_program().set_view(self.view);
612
613 self.clear_rect(
614 0,
615 0,
616 source_image_info.width() as _,
617 source_image_info.height() as _,
618 Color::rgbaf(0., 0., 0., 0.),
619 );
620
621 self.triangles(images, &cmd, &blur_params);
622
623 self.set_target(images, RenderTarget::Image(target_image));
624 self.main_program().set_view(self.view);
625
626 self.clear_rect(
627 0,
628 0,
629 source_image_info.width() as _,
630 source_image_info.height() as _,
631 Color::rgbaf(0., 0., 0., 0.),
632 );
633
634 blur_params.image_blur_filter_direction = [0.0, 1.0];
635
636 cmd.image = Some(horizontal_blur_buffer);
637
638 self.triangles(images, &cmd, &blur_params);
639
640 images.remove(self, horizontal_blur_buffer);
641
642 // restore previous render target and view
643 self.set_target(images, original_render_target);
644 self.main_program().set_view(self.view);
645 }
646
647 fn main_program(&self) -> &MainProgram {
648 let programs = if self.current_program_needs_glyph_texture {
649 &self.main_programs_with_glyph_texture
650 } else {
651 &self.main_programs_without_glyph_texture
652 };
653 programs[self.current_program as usize]
654 .as_ref()
655 .expect("internal error: invalid shader program selected for given paint")
656 }
657
658 fn select_main_program(&mut self, params: &Params) {
659 let program_index = params.shader_type.to_u8();
660 if program_index != self.current_program
661 || params.uses_glyph_texture() != self.current_program_needs_glyph_texture
662 {
663 unsafe {
664 self.context.active_texture(glow::TEXTURE0);
665 self.context.bind_texture(glow::TEXTURE_2D, None);
666 self.context.active_texture(glow::TEXTURE0 + 1);
667 self.context.bind_texture(glow::TEXTURE_2D, None);
668 }
669
670 self.main_program().unbind();
671 self.current_program = program_index;
672 self.current_program_needs_glyph_texture = params.uses_glyph_texture();
673
674 let program = self.main_program();
675 program.bind();
676 // Bind the two uniform samplers to texture units
677 program.set_tex(0);
678 program.set_glyphtex(1);
679 program.set_view(self.view);
680 }
681 }
682}
683
684impl Renderer for OpenGl {
685 type Image = GlTexture;
686 type NativeTexture = <glow::Context as glow::HasContext>::Texture;
687 type Surface = ();
688 type CommandBuffer = ();
689
690 fn set_size(&mut self, width: u32, height: u32, _dpi: f32) {
691 self.view[0] = width as f32;
692 self.view[1] = height as f32;
693
694 self.screen_view = self.view;
695
696 unsafe {
697 self.context.viewport(0, 0, width as i32, height as i32);
698 }
699 }
700
701 fn get_native_texture(&self, image: &Self::Image) -> Result<Self::NativeTexture, ErrorKind> {
702 Ok(image.id())
703 }
704
705 fn render(
706 &mut self,
707 _surface: &Self::Surface,
708 images: &mut ImageStore<Self::Image>,
709 verts: &[Vertex],
710 commands: Vec<Command>,
711 ) {
712 self.current_program = 0;
713 self.main_program().bind();
714
715 unsafe {
716 self.context.enable(glow::CULL_FACE);
717
718 self.context.cull_face(glow::BACK);
719 self.context.front_face(glow::CCW);
720 self.context.enable(glow::BLEND);
721 self.context.disable(glow::DEPTH_TEST);
722 self.context.disable(glow::SCISSOR_TEST);
723 self.context.color_mask(true, true, true, true);
724 self.context.stencil_mask(0xffff_ffff);
725 self.context.stencil_op(glow::KEEP, glow::KEEP, glow::KEEP);
726 self.context.stencil_func(glow::ALWAYS, 0, 0xffff_ffff);
727 self.context.active_texture(glow::TEXTURE0);
728 self.context.bind_texture(glow::TEXTURE_2D, None);
729 self.context.active_texture(glow::TEXTURE0 + 1);
730 self.context.bind_texture(glow::TEXTURE_2D, None);
731
732 self.context.bind_vertex_array(self.vert_arr);
733
734 let vertex_size = mem::size_of::<Vertex>();
735
736 self.context.bind_buffer(glow::ARRAY_BUFFER, self.vert_buff);
737 self.context
738 .buffer_data_u8_slice(glow::ARRAY_BUFFER, verts.align_to().1, glow::STREAM_DRAW);
739
740 self.context.enable_vertex_attrib_array(0);
741 self.context.enable_vertex_attrib_array(1);
742
743 self.context
744 .vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, vertex_size as i32, 0);
745 self.context.vertex_attrib_pointer_f32(
746 1,
747 2,
748 glow::FLOAT,
749 false,
750 vertex_size as i32,
751 2 * mem::size_of::<f32>() as i32,
752 );
753 }
754
755 self.check_error("render prepare");
756
757 for cmd in commands {
758 self.set_composite_operation(cmd.composite_operation);
759
760 match cmd.cmd_type {
761 CommandType::ConvexFill { ref params } => self.convex_fill(images, &cmd, params),
762 CommandType::ConcaveFill {
763 ref stencil_params,
764 ref fill_params,
765 } => self.concave_fill(images, &cmd, stencil_params, fill_params),
766 CommandType::Stroke { ref params } => self.stroke(images, &cmd, params),
767 CommandType::StencilStroke {
768 ref params1,
769 ref params2,
770 } => self.stencil_stroke(images, &cmd, params1, params2),
771 CommandType::Triangles { ref params } => self.triangles(images, &cmd, params),
772 CommandType::ClearRect { color } => {
773 if let Some((start, _)) = cmd.triangles_verts {
774 let x = verts[start].x as _;
775 let y = verts[start].y as _;
776 let width = verts[start + 1].x as u32 - x;
777 let height = verts[start + 1].y as u32 - y;
778 self.clear_rect(x, y, width, height, color);
779 }
780 }
781 CommandType::SetRenderTarget(target) => {
782 self.set_target(images, target);
783 self.main_program().set_view(self.view);
784 }
785 CommandType::RenderFilteredImage { target_image, filter } => {
786 self.render_filtered_image(images, cmd, target_image, filter)
787 }
788 }
789 }
790
791 unsafe {
792 self.context.disable_vertex_attrib_array(0);
793 self.context.disable_vertex_attrib_array(1);
794 self.context.bind_vertex_array(None);
795
796 self.context.disable(glow::CULL_FACE);
797 self.context.bind_buffer(glow::ARRAY_BUFFER, None);
798 self.context.bind_texture(glow::TEXTURE_2D, None);
799 }
800
801 self.main_program().unbind();
802
803 self.check_error("render done");
804 }
805
806 fn alloc_image(&mut self, info: ImageInfo) -> Result<Self::Image, ErrorKind> {
807 Self::Image::new(&self.context, info, self.is_opengles_2_0)
808 }
809
810 fn create_image_from_native_texture(
811 &mut self,
812 native_texture: Self::NativeTexture,
813 info: ImageInfo,
814 ) -> Result<Self::Image, ErrorKind> {
815 Ok(Self::Image::new_from_native_texture(native_texture, info))
816 }
817
818 fn update_image(
819 &mut self,
820 image: &mut Self::Image,
821 data: ImageSource,
822 x: usize,
823 y: usize,
824 ) -> Result<(), ErrorKind> {
825 image.update(&self.context, data, x, y, self.is_opengles_2_0)
826 }
827
828 fn delete_image(&mut self, image: Self::Image, image_id: ImageId) {
829 self.framebuffers.remove(&image_id);
830 image.delete(&self.context);
831 }
832
833 fn screenshot(&mut self) -> Result<ImgVec<RGBA8>, ErrorKind> {
834 //let mut image = image::RgbaImage::new(self.view[0] as u32, self.view[1] as u32);
835 let w = self.view[0] as usize;
836 let h = self.view[1] as usize;
837
838 let mut image = ImgVec::new(
839 vec![
840 RGBA8 {
841 r: 255,
842 g: 255,
843 b: 255,
844 a: 255
845 };
846 w * h
847 ],
848 w,
849 h,
850 );
851
852 unsafe {
853 self.context.read_pixels(
854 0,
855 0,
856 self.view[0] as i32,
857 self.view[1] as i32,
858 glow::RGBA,
859 glow::UNSIGNED_BYTE,
860 glow::PixelPackData::Slice(Some(image.buf_mut().align_to_mut().1)),
861 );
862 }
863
864 let mut flipped = Vec::with_capacity(w * h);
865
866 for row in image.rows().rev() {
867 flipped.extend_from_slice(row);
868 }
869
870 Ok(ImgVec::new(flipped, w, h))
871 }
872}
873
874impl SurfacelessRenderer for OpenGl {
875 fn render_surfaceless(&mut self, images: &mut ImageStore<Self::Image>, verts: &[Vertex], commands: Vec<Command>) {
876 self.render(&(), images, verts, commands)
877 }
878}
879
880impl Drop for OpenGl {
881 fn drop(&mut self) {
882 if let Some(vert_arr: NativeVertexArray) = self.vert_arr {
883 unsafe {
884 self.context.delete_vertex_array(vert_arr);
885 }
886 }
887
888 if let Some(vert_buff: NativeBuffer) = self.vert_buff {
889 unsafe {
890 self.context.delete_buffer(vert_buff);
891 }
892 }
893 }
894}
895