1
extern crate boxrs;
2
#[macro_use]
3
extern crate glium;
4

            
5
use std::default::Default;
6
use std::env;
7
use std::fs::File;
8
use std::io::Read;
9
use std::path::Path;
10

            
11
use boxrs::css::Color;
12
use boxrs::dom::Node;
13
use boxrs::layout::Rect;
14
use boxrs::painting::DisplayCommand;
15
use glium::glutin;
16
use glium::index::{NoIndices, PrimitiveType};
17
use glium::{Display, Frame, Program, Surface, VertexBuffer};
18

            
19
#[derive(Copy, Clone)]
20
struct Vertex {
21
    position: [f32; 2],
22
}
23

            
24
implement_vertex!(Vertex, position);
25

            
26
fn draw_color_rectangle(
27
    target: &mut Frame,
28
    square_buffer: &VertexBuffer<Vertex>,
29
    program: &Program,
30
    color: &Color,
31
    rect: &Rect,
32
    layer: f32,
33
) {
34
    let indices = NoIndices(PrimitiveType::TriangleStrip);
35

            
36
    let uniforms = uniform! {
37
        x: rect.x,
38
        y: rect.y,
39
        width: rect.width,
40
        height: rect.height,
41
        layer: layer,
42
        in_color: [color.r as f32, color.g as f32, color.b as f32, color.a as f32]
43
    };
44

            
45
    target
46
        .draw(
47
            square_buffer,
48
            indices,
49
            program,
50
            &uniforms,
51
            &Default::default(),
52
        )
53
        .unwrap();
54
}
55

            
56
fn main() {
57
    let mut args = env::args().skip(1);
58
    let html_filename = args.next().expect("HTML file provided as first argument");
59

            
60
    let html = read_source(&html_filename);
61

            
62
    // Since we don't have an actual window, hard-code the "viewport" size.
63
    let width = 800;
64
    let height = 600;
65

            
66
    let mut viewport: boxrs::layout::Dimensions = Default::default();
67
    viewport.content.width = width as f32;
68
    viewport.content.height = height as f32;
69

            
70
    // Parsing and rendering:
71
    let root_node = boxrs::parse_html(&html);
72

            
73
    // Extract title:
74
    let title = match root_node.get_elements_by_tag_name("title").first() {
75
        Some(node) => node.get_text_content(),
76
        None => "html2gl".to_owned(),
77
    };
78

            
79
    // TODO: replace with:
80
    // let css_filename = match root_node.select("html > head > link[rel=stylesheet][href]") {
81
    //   Some(node) => Some(node.get_attribute("href")),
82
    //   None => None,
83
    // }
84

            
85
    // TODO: of course, really replace this with something that keeps track of all sheets
86

            
87
    let base = Path::new(&html_filename).parent().unwrap();
88

            
89
    let mut css_filename = None;
90

            
91
    if let Some(Node::Element { attrs, .. }) = root_node.get_elements_by_tag_name("link").first() {
92
        if attrs.contains(&("rel".to_owned(), "stylesheet".to_owned())) {
93
            for attr in attrs {
94
                if attr.0 == "href" {
95
                    css_filename = Some(base.join(attr.1.clone()));
96
                }
97
            }
98
        }
99
    }
100

            
101
    println!("Opening CSS file {}", css_filename.as_ref().unwrap().display());
102

            
103
    let css = read_source(&css_filename.unwrap());
104

            
105
    // Combine HTML with CSS to create list of draw commands
106
    let stylesheet = boxrs::parse_css(&css);
107
    let style_root = boxrs::build_style_tree(&root_node, &stylesheet);
108
    let layout_root = boxrs::build_layout_tree(&style_root, viewport);
109
    let display_list = boxrs::build_display_list(&layout_root);
110

            
111
    // Render with OpenGL:
112
    let event_loop = glutin::event_loop::EventLoop::new();
113
    let wb = glutin::window::WindowBuilder::new().with_title(format!("{title} - html2gl"));
114
    let cb = glutin::ContextBuilder::new().with_depth_buffer(24);
115
    let display = Display::new(wb, cb, &event_loop).unwrap();
116

            
117
    let square_shape = vec![
118
        Vertex {
119
            position: [0.0, 0.0],
120
        },
121
        Vertex {
122
            position: [1.0, 0.0],
123
        },
124
        Vertex {
125
            position: [0.0, 1.0],
126
        },
127
        Vertex {
128
            position: [1.0, 1.0],
129
        },
130
    ];
131
    let square_buffer = VertexBuffer::new(&display, &square_shape).unwrap();
132

            
133
    let vertex_shader_src = r#"
134
        #version 140
135

            
136
        in vec2 position;
137

            
138
        uniform float x;
139
        uniform float y;
140
        uniform float width;
141
        uniform float height;
142
        uniform float layer;
143

            
144
        void main() {
145
            gl_Position = vec4(
146
                (x + position.x * width) / 800.0 * 2.0 - 1.0,
147
                (y + position.y * height) / 600.0 * -2.0 + 1.0,
148
                layer,
149
                1.0
150
            );
151
        }
152
    "#;
153

            
154
    let fragment_shader_src = r#"
155
        #version 140
156

            
157
        out vec4 color;
158

            
159
        uniform vec4 in_color;
160

            
161
        vec4 normalize(vec4 c) {
162
            return c / 255;
163
        }
164

            
165
        vec3 to_gamma_curve(vec3 c)
166
        {
167
            return pow(c, vec3(2.2));
168
        }
169

            
170
        void main() {
171
            vec4 normalized = normalize(in_color);
172
            color = vec4(to_gamma_curve(normalized.rgb), normalized.a);
173
        }
174
    "#;
175

            
176
    let program =
177
        Program::from_source(&display, vertex_shader_src, fragment_shader_src, None).unwrap();
178

            
179
    event_loop.run(move |ev, _, control_flow| {
180
        let mut target = display.draw();
181
        target.clear_color_and_depth((1.0, 1.0, 1.0, 1.0), 1.0);
182

            
183
        let mut layer = 0.0;
184

            
185
        for item in &display_list {
186
            match item {
187
                DisplayCommand::SolidColor(color, rect) => {
188
                    draw_color_rectangle(&mut target, &square_buffer, &program, color, rect, layer);
189
                }
190
            }
191

            
192
            layer += 0.001;
193
        }
194

            
195
        target.finish().unwrap();
196

            
197
        let next_frame_time =
198
            std::time::Instant::now() + std::time::Duration::from_nanos(16_666_667);
199
        *control_flow = glutin::event_loop::ControlFlow::WaitUntil(next_frame_time);
200

            
201
        if let glutin::event::Event::WindowEvent { event, .. } = ev {
202
            if event == glutin::event::WindowEvent::CloseRequested {
203
                *control_flow = glutin::event_loop::ControlFlow::Exit;
204
            }
205
        }
206
    });
207
}
208

            
209
fn read_source<P: AsRef<Path>>(filename: P) -> String {
210
    let mut s = String::new();
211
    File::open(filename)
212
        .unwrap()
213
        .read_to_string(&mut s)
214
        .unwrap();
215
    s
216
}