aboutsummaryrefslogtreecommitdiffstats
path: root/components/canvas/webgl_paint_task.rs
blob: 1afcf20089c87ef33b2d60fe47ee75704f53cea0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use canvas_msg::{CanvasWebGLMsg, CanvasCommonMsg, CanvasMsg};
use geom::size::Size2D;

use gleam::gl;
use gleam::gl::types::{GLint, GLsizei};

use util::task::spawn_named;

use std::borrow::ToOwned;
use std::sync::mpsc::{channel, Sender};
use util::vec::byte_swap;

use glutin::{HeadlessRendererBuilder};

pub struct WebGLPaintTask {
    size: Size2D<i32>,
}

impl WebGLPaintTask {
    fn new(size: Size2D<i32>) -> WebGLPaintTask {
        WebGLPaintTask::create(size);
        WebGLPaintTask {
            size: size,
        }
    }

    pub fn start(size: Size2D<i32>) -> Sender<CanvasMsg> {
        let (chan, port) = channel::<CanvasMsg>();
        spawn_named("WebGLTask".to_owned(), move || {
            let mut painter = WebGLPaintTask::new(size);
            painter.init();
            loop {
                match port.recv().unwrap() {
                    CanvasMsg::WebGL(message) => {
                        match message {
                            CanvasWebGLMsg::Clear(mask) => painter.clear(mask),
                            CanvasWebGLMsg::ClearColor(r, g, b, a) => painter.clear_color(r, g, b, a),
                        }
                    },
                    CanvasMsg::Common(message) => {
                        match message {
                            CanvasCommonMsg::Close => break,
                            CanvasCommonMsg::SendPixelContents(chan) => painter.send_pixel_contents(chan),
                            CanvasCommonMsg::Recreate(size) => painter.recreate(size),
                        }
                    },
                    CanvasMsg::Canvas2d(_) => panic!("Wrong message sent to WebGLTask"),
                }
            }
        });
        chan
    }

    fn create(size: Size2D<i32>) {
        // It creates OpenGL context
        let context = HeadlessRendererBuilder::new(size.width as u32, size.height as u32).build().unwrap();
        unsafe {
            context.make_current();
        }
    }

    fn init(&self) {
        let framebuffer_ids = gl::gen_framebuffers(1);
        gl::bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]);

        let texture_ids = gl::gen_textures(1);
        gl::bind_texture(gl::TEXTURE_2D, texture_ids[0]);

        gl::tex_image_2d(gl::TEXTURE_2D, 0, gl::RGB as GLint, self.size.width as GLsizei,
                         self.size.height as GLsizei, 0, gl::RGB, gl::UNSIGNED_BYTE, None);
        gl::tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
        gl::tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);

        gl::framebuffer_texture_2d(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D,
                                   texture_ids[0], 0);
        gl::bind_texture(gl::TEXTURE_2D, 0);

        gl::viewport(0 as GLint, 0 as GLint,
                     self.size.width as GLsizei, self.size.height as GLsizei);
    }

    fn clear(&self, mask: u32) {
        gl::clear(mask);
    }

    fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
        gl::clear_color(r, g, b, a);
    }

    fn send_pixel_contents(&mut self, chan: Sender<Vec<u8>>) {
        // FIXME(#5652, dmarcos) Instead of a readback strategy we have
        // to layerize the canvas
        let mut pixels = gl::read_pixels(0, 0,
                                    self.size.width as gl::GLsizei,
                                    self.size.height as gl::GLsizei,
                                    gl::RGBA, gl::UNSIGNED_BYTE);

        // rgba -> bgra
        byte_swap(pixels.as_mut_slice());
        chan.send(pixels).unwrap();
    }

    fn recreate(&mut self, size: Size2D<i32>) {
        self.size = size;
        self.init();
    }

}