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