aboutsummaryrefslogtreecommitdiffstats
path: root/components/compositing/gl.rs
blob: 45d33f19a7d1b9e89cb5c9a08d8ad386f5c8647a (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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/* 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 https://mozilla.org/MPL/2.0/. */

use gleam::gl;
use image::RgbImage;
use servo_geometry::FramebufferUintLength;

#[derive(Default)]
pub struct RenderTargetInfo {
    framebuffer_ids: Vec<gl::GLuint>,
    renderbuffer_ids: Vec<gl::GLuint>,
    texture_ids: Vec<gl::GLuint>,
}

pub fn initialize_png(
    gl: &dyn gl::Gl,
    width: FramebufferUintLength,
    height: FramebufferUintLength,
) -> RenderTargetInfo {
    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 gl::GLint,
        width.get() as gl::GLsizei,
        height.get() as gl::GLsizei,
        0,
        gl::RGB,
        gl::UNSIGNED_BYTE,
        None,
    );
    gl.tex_parameter_i(
        gl::TEXTURE_2D,
        gl::TEXTURE_MAG_FILTER,
        gl::NEAREST as gl::GLint,
    );
    gl.tex_parameter_i(
        gl::TEXTURE_2D,
        gl::TEXTURE_MIN_FILTER,
        gl::NEAREST as gl::GLint,
    );

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

    gl.bind_texture(gl::TEXTURE_2D, 0);

    let renderbuffer_ids = gl.gen_renderbuffers(1);
    let depth_rb = renderbuffer_ids[0];
    gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
    gl.renderbuffer_storage(
        gl::RENDERBUFFER,
        gl::DEPTH_COMPONENT24,
        width.get() as gl::GLsizei,
        height.get() as gl::GLsizei,
    );
    gl.framebuffer_renderbuffer(
        gl::FRAMEBUFFER,
        gl::DEPTH_ATTACHMENT,
        gl::RENDERBUFFER,
        depth_rb,
    );

    RenderTargetInfo {
        framebuffer_ids,
        renderbuffer_ids,
        texture_ids,
    }
}

pub fn draw_img(
    gl: &dyn gl::Gl,
    render_target_info: RenderTargetInfo,
    x: i32,
    y: i32,
    width: FramebufferUintLength,
    height: FramebufferUintLength,
) -> RgbImage {
    let width = width.get() as usize;
    let height = height.get() as usize;
    // For some reason, OSMesa fails to render on the 3rd
    // attempt in headless mode, under some conditions.
    // I think this can only be some kind of synchronization
    // bug in OSMesa, but explicitly un-binding any vertex
    // array here seems to work around that bug.
    // See https://github.com/servo/servo/issues/18606.
    gl.bind_vertex_array(0);

    let mut pixels = gl.read_pixels(
        x,
        y,
        width as gl::GLsizei,
        height as gl::GLsizei,
        gl::RGB,
        gl::UNSIGNED_BYTE,
    );

    gl.bind_framebuffer(gl::FRAMEBUFFER, 0);

    gl.delete_textures(&render_target_info.texture_ids);
    gl.delete_renderbuffers(&render_target_info.renderbuffer_ids);
    gl.delete_framebuffers(&render_target_info.framebuffer_ids);

    // flip image vertically (texture is upside down)
    let orig_pixels = pixels.clone();
    let stride = width * 3;
    for y in 0..height {
        let dst_start = y * stride;
        let src_start = (height - y - 1) * stride;
        let src_slice = &orig_pixels[src_start..src_start + stride];
        (&mut pixels[dst_start..dst_start + stride]).clone_from_slice(&src_slice[..stride]);
    }

    RgbImage::from_raw(width as u32, height as u32, pixels).expect("Flipping image failed!")
}