vg/renderer/
opengl.rs

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
52/// Represents opengl
53pub 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    /// Creates an OpenGl struct
70    #[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    /// check renderer is OpenGL ES 2.0
133    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            //glow::DepthMask(glow::FALSE);
215        }
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            // Draw anti-aliased pixels
238            self.context.color_mask(true, true, true, true);
239            //glow::DepthMask(glow::TRUE);
240        }
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            // draw fringes
255            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            // Fill the stroke base without overlap
305            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        // Draw anti-aliased pixels.
321        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            // Clear stencil buffer.
339            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    /// Make the "Screen" RenderTarget actually render to a framebuffer object. This is useful when
460    /// embedding VG into another program where final composition is handled by an external task.
461    /// The given `framebuffer_object` must refer to a Framebuffer Object created on the current OpenGL
462    /// Context, and must have a depth & stencil attachment.
463    ///
464    /// Pass `None` to clear any previous Framebuffer Object ID that was passed and target rendering to
465    /// the default target (normally the window).
466    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        // The filtering happens in two passes, first a horizontal blur and then the vertial blur. The
495        // first pass therefore renders into an intermediate, temporarily allocated texture.
496
497        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        // GLES 2.0 does not allow non-constant loop indices, so limit the standard devitation to allow for a upper fixed limit
522        // on the number of iterations in the fragment shader.
523        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        // restore previous render target and view
559        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        // Bind the two uniform samplers to texture units
622        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 mut image = image::RgbaImage::new(self.view[0] as u32, self.view[1] as u32);
697        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}