graphics_tree/
lib.rs

1//! A 2D graphics backend for Piston-Graphics that stores and optimizes commands.
2
3#![deny(missing_docs)]
4
5extern crate graphics;
6extern crate image;
7extern crate range;
8extern crate texture;
9
10use std::sync::{Arc, RwLock};
11use std::collections::HashMap;
12
13use graphics::{DrawState, Graphics, ImageSize};
14use graphics::types::Color;
15use image::RgbaImage;
16use range::Range;
17use texture::CreateTexture;
18
19/// A graphics backend that stores and optimizes commands
20pub struct GraphicsTree {
21    commands: Vec<Command>,
22    vertices: Vec<[f32; 2]>,
23    uvs: Vec<[f32; 2]>,
24    colors: Vec<[f32; 4]>,
25    current_color: Color,
26    current_draw_state: DrawState,
27}
28
29enum Command {
30    ClearColor(Color),
31    ClearStencil(u8),
32    ChangeColor(Color),
33    ChangeDrawState(DrawState),
34    Colored(Range),
35    Colors(Range, Range),
36    Textured(Texture, Range, Range),
37    TexturedColor(Texture, Range, Range, Range),
38}
39
40/// Simplifies some common operations on textures.
41#[derive(Clone)]
42pub struct Texture(pub Arc<RwLock<TextureInner>>);
43
44/// Stores the inner data to keep track of a texture.
45pub struct TextureInner {
46    /// Id used by `TextureBuffer` to look up texture.
47    pub id: Option<u64>,
48    /// Whether the texture needs to be updated.
49    pub needs_update: bool,
50    /// The image data associated with a texture.
51    pub image: RgbaImage,
52}
53
54/// Stores textures.
55pub struct TextureBuffer<F, T> {
56    /// The factory that creates textures.
57    pub factory: F,
58    textures: HashMap<u64, T>,
59    next_id: u64,
60}
61
62impl GraphicsTree {
63    /// Creates a new graphics tree.
64    pub fn new() -> GraphicsTree {
65        GraphicsTree {
66            commands: vec![],
67            vertices: vec![],
68            uvs: vec![],
69            colors: vec![],
70            current_color: [0.0; 4],
71            current_draw_state: Default::default(),
72        }
73    }
74
75    /// Returns `true` if graphics tree is empty.
76    pub fn is_empty(&self) -> bool {
77        self.commands.len() == 0 &&
78        self.vertices.len() == 0 &&
79        self.uvs.len() == 0 &&
80        self.colors.len() == 0
81    }
82
83    /// Clears all graphics.
84    pub fn clear(&mut self) {
85        self.commands.clear();
86        self.vertices.clear();
87        self.uvs.clear();
88        self.colors.clear();
89    }
90
91    /// Draws graphics to backend.
92    pub fn draw<F, T, G>(
93        &self,
94        texture_buffer: &mut TextureBuffer<F, T>,
95        g: &mut G
96    )
97        where
98            T: ImageSize + CreateTexture<F>,
99            G: Graphics<Texture=T>
100    {
101        use Command::*;
102        use graphics::BACK_END_MAX_VERTEX_COUNT;
103
104        let bufsize = 2 * BACK_END_MAX_VERTEX_COUNT;
105        let mut color: Color = [0.0; 4];
106        let mut draw_state: DrawState = Default::default();
107        for command in &self.commands {
108            match *command {
109                ClearColor(color) => g.clear_color(color),
110                ClearStencil(value) => g.clear_stencil(value),
111                ChangeColor(new_color) => color = new_color,
112                ChangeDrawState(new_draw_state) => draw_state = new_draw_state,
113                Colored(range) => {
114                    // Split range in chunks to respect `Graphics` interface.
115                    let offset = range.offset;
116                    let length = range.length;
117                    let chunks = length / bufsize;
118                    g.tri_list(&draw_state, &color, |f| {
119                        for i in 0..chunks {
120                            let start = offset + chunks * i;
121                            let end = start + bufsize;
122                            f(&self.vertices[start..end]);
123                        }
124                        if chunks * bufsize < length {
125                            let start = chunks * bufsize;
126                            let len = length - start;
127                            f(&self.vertices[offset + start..offset + len]);
128                        }
129                    });
130                }
131                Colors(vertex_range, color_range) => {
132                    let offset_v = vertex_range.offset;
133                    let length_v = vertex_range.length;
134                    let chunks_v = length_v / bufsize;
135                    let offset_c = color_range.offset;
136                    let length_c = color_range.length;
137                    let chunks_c = length_c / bufsize;
138                    g.tri_list_c(&draw_state, |f| {
139                        for i in 0..chunks_v {
140                            let start_v = offset_v + chunks_v * i;
141                            let end_v = start_v + bufsize;
142                            let start_c = offset_c + chunks_c * i;
143                            let end_c = start_c + bufsize;
144                            f(&self.vertices[start_v..end_v], &self.colors[start_c..end_c]);
145                        }
146                        if chunks_v * bufsize < length_v {
147                            let start_v = chunks_v * bufsize;
148                            let len_v = length_v - start_v;
149                            let start_c = chunks_c * bufsize;
150                            let len_c = length_c - start_c;
151                            f(&self.vertices[offset_v + start_v..offset_v + len_v],
152                              &self.colors[offset_c + start_c..offset_c + len_c]);
153                        }
154                    });
155                }
156                Textured(ref tex, vertex_range, uv_range) => {
157                    // Split range in chunks to respect `Graphics` interface.
158                    let offset_v = vertex_range.offset;
159                    let length_v = vertex_range.length;
160                    let chunks_v = length_v / bufsize;
161                    let offset_uv = uv_range.offset;
162                    let length_uv = uv_range.length;
163                    let chunks_uv = length_uv / bufsize;
164
165                    let texture = if let Ok(mut inner) = tex.0.write() {
166                        if inner.id.is_none() {
167                            use texture::{Format, TextureSettings};
168
169                            let (width, height) = inner.image.dimensions();
170                            let new_texture: T = CreateTexture::create(
171                                &mut texture_buffer.factory,
172                                Format::Rgba8,
173                                &inner.image,
174                                [width, height],
175                                &TextureSettings::new()
176                            ).unwrap_or_else(|_| panic!("Could not create texture"));
177                            texture_buffer.textures.insert(texture_buffer.next_id, new_texture);
178                            inner.id = Some(texture_buffer.next_id);
179                            texture_buffer.next_id += 1;
180                        } else if inner.needs_update {
181                            // Create a new texture, because updating is not
182                            // supported directly yet.
183                            use texture::{Format, TextureSettings};
184
185                            let id = inner.id.unwrap();
186                            let (width, height) = inner.image.dimensions();
187                            let new_texture: T = CreateTexture::create(
188                                &mut texture_buffer.factory,
189                                Format::Rgba8,
190                                &inner.image,
191                                [width, height],
192                                &TextureSettings::new()
193                            ).unwrap_or_else(|_| panic!("Could not create texture"));
194                            texture_buffer.textures.insert(id, new_texture);
195                            inner.needs_update = false;
196                        }
197                        if let Some(texture) = texture_buffer.textures.get(&inner.id.unwrap()) {
198                            texture
199                        } else {
200                            panic!("Texture does not exist");
201                        }
202                    } else {
203                        panic!("Image is used elsewhere");
204                    };
205
206                    g.tri_list_uv(&draw_state, &color, texture, |f| {
207                        for i in 0..chunks_v {
208                            let start_v = offset_v + chunks_v * i;
209                            let end_v = start_v + bufsize;
210                            let start_uv = offset_uv + chunks_uv * i;
211                            let end_uv = start_uv + bufsize;
212                            f(&self.vertices[start_v..end_v],
213                              &self.uvs[start_uv..end_uv]);
214                        }
215                        if chunks_v * bufsize < length_v {
216                            let start_v = chunks_v * bufsize;
217                            let len_v = length_v - start_v;
218                            let start_uv = chunks_uv * bufsize;
219                            let len_uv = length_uv - start_uv;
220                            f(&self.vertices[offset_v + start_v..offset_v + len_v],
221                              &self.uvs[offset_uv + start_uv..offset_uv + len_uv]);
222                        }
223                    });
224                }
225                TexturedColor(ref tex, vertex_range, uv_range, color_range) => {
226                    // Split range in chunks to respect `Graphics` interface.
227                    let offset_v = vertex_range.offset;
228                    let length_v = vertex_range.length;
229                    let chunks_v = length_v / bufsize;
230                    let offset_uv = uv_range.offset;
231                    let length_uv = uv_range.length;
232                    let chunks_uv = length_uv / bufsize;
233                    let offset_c = color_range.offset;
234                    let length_c = color_range.length;
235                    let chunks_c = length_c / bufsize;
236
237                    let texture = if let Ok(mut inner) = tex.0.write() {
238                        if inner.id.is_none() {
239                            use texture::{Format, TextureSettings};
240
241                            let (width, height) = inner.image.dimensions();
242                            let new_texture: T = CreateTexture::create(
243                                &mut texture_buffer.factory,
244                                Format::Rgba8,
245                                &inner.image,
246                                [width, height],
247                                &TextureSettings::new()
248                            ).unwrap_or_else(|_| panic!("Could not create texture"));
249                            texture_buffer.textures.insert(texture_buffer.next_id, new_texture);
250                            inner.id = Some(texture_buffer.next_id);
251                            texture_buffer.next_id += 1;
252                        } else if inner.needs_update {
253                            // Create a new texture, because updating is not
254                            // supported directly yet.
255                            use texture::{Format, TextureSettings};
256
257                            let id = inner.id.unwrap();
258                            let (width, height) = inner.image.dimensions();
259                            let new_texture: T = CreateTexture::create(
260                                &mut texture_buffer.factory,
261                                Format::Rgba8,
262                                &inner.image,
263                                [width, height],
264                                &TextureSettings::new()
265                            ).unwrap_or_else(|_| panic!("Could not create texture"));
266                            texture_buffer.textures.insert(id, new_texture);
267                            inner.needs_update = false;
268                        }
269                        if let Some(texture) = texture_buffer.textures.get(&inner.id.unwrap()) {
270                            texture
271                        } else {
272                            panic!("Texture does not exist");
273                        }
274                    } else {
275                        panic!("Image is used elsewhere");
276                    };
277
278                    g.tri_list_uv_c(&draw_state, texture, |f| {
279                        for i in 0..chunks_v {
280                            let start_v = offset_v + chunks_v * i;
281                            let end_v = start_v + bufsize;
282                            let start_uv = offset_uv + chunks_uv * i;
283                            let end_uv = start_uv + bufsize;
284                            let start_c = offset_c + chunks_c * i;
285                            let end_c = start_c + bufsize;
286                            f(&self.vertices[start_v..end_v],
287                              &self.uvs[start_uv..end_uv],
288                              &self.colors[start_c..end_c]);
289                        }
290                        if chunks_v * bufsize < length_v {
291                            let start_v = chunks_v * bufsize;
292                            let len_v = length_v - start_v;
293                            let start_uv = chunks_uv * bufsize;
294                            let len_uv = length_uv - start_uv;
295                            let start_c = chunks_c * bufsize;
296                            let len_c = length_c - start_c;
297                            f(&self.vertices[offset_v + start_v..offset_v + len_v],
298                              &self.uvs[offset_uv + start_uv..offset_uv + len_uv],
299                              &self.colors[offset_c + start_c..offset_c + len_c]);
300                        }
301                    });
302                }
303            }
304        }
305    }
306}
307
308impl ImageSize for Texture {
309    fn get_size(&self) -> (u32, u32) {
310        use std::ops::Deref;
311
312        self.0.read().unwrap().deref().image.dimensions()
313    }
314}
315
316impl Graphics for GraphicsTree {
317    type Texture = Texture;
318
319    fn clear_color(&mut self, color: Color) {
320        self.commands.push(Command::ClearColor(color));
321    }
322
323    fn clear_stencil(&mut self, value: u8) {
324        self.commands.push(Command::ClearStencil(value));
325    }
326
327    fn tri_list<F>(
328        &mut self,
329        draw_state: &DrawState,
330        color: &Color,
331        mut f: F
332    ) where F: FnMut(&mut dyn FnMut(&[[f32; 2]])) {
333        if color != &self.current_color {
334            self.commands.push(Command::ChangeColor(*color));
335        }
336        if draw_state != &self.current_draw_state {
337            self.commands.push(Command::ChangeDrawState(*draw_state));
338        }
339        let start = self.vertices.len();
340        f(&mut |chunk| self.vertices.extend_from_slice(chunk));
341        self.commands.push(Command::Colored(Range::new(start, self.vertices.len() - start)));
342    }
343
344    fn tri_list_c<F>(
345        &mut self,
346        draw_state: &DrawState,
347        mut f: F
348    ) where F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 4]])) {
349        if draw_state != &self.current_draw_state {
350            self.commands.push(Command::ChangeDrawState(*draw_state));
351        }
352        let start_v = self.vertices.len();
353        let start_c = self.colors.len();
354        f(&mut |chunk, chunk_color| {
355            self.vertices.extend_from_slice(chunk);
356            self.colors.extend_from_slice(chunk_color);
357        });
358        self.commands.push(Command::Colors(
359            Range::new(start_v, self.vertices.len() - start_v),
360            Range::new(start_c, self.colors.len() - start_c)
361        ));
362    }
363
364    fn tri_list_uv<F>(
365        &mut self,
366        draw_state: &DrawState,
367        color: &[f32; 4],
368        texture: &Self::Texture,
369        mut f: F
370    ) where F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]])) {
371        if color != &self.current_color {
372            self.commands.push(Command::ChangeColor(*color));
373        }
374        if draw_state != &self.current_draw_state {
375            self.commands.push(Command::ChangeDrawState(*draw_state));
376        }
377        let start_vertices = self.vertices.len();
378        let start_uvs = self.uvs.len();
379        f(&mut |chunk, chunk_uvs| {
380            self.vertices.extend_from_slice(chunk);
381            self.uvs.extend_from_slice(chunk_uvs);
382        });
383        self.commands.push(Command::Textured(
384            texture.clone(),
385            Range::new(start_vertices, self.vertices.len() - start_vertices),
386            Range::new(start_uvs, self.uvs.len() - start_uvs)
387        ));
388    }
389
390    fn tri_list_uv_c<F>(
391        &mut self,
392        draw_state: &DrawState,
393        texture: &Self::Texture,
394        mut f: F
395    ) where F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]], &[[f32; 4]])) {
396        if draw_state != &self.current_draw_state {
397            self.commands.push(Command::ChangeDrawState(*draw_state));
398        }
399        let start_vertices = self.vertices.len();
400        let start_uvs = self.uvs.len();
401        let start_c = self.colors.len();
402        f(&mut |chunk, chunk_uvs, chunk_c| {
403            self.vertices.extend_from_slice(chunk);
404            self.uvs.extend_from_slice(chunk_uvs);
405            self.colors.extend_from_slice(chunk_c);
406        });
407        self.commands.push(Command::TexturedColor(
408            texture.clone(),
409            Range::new(start_vertices, self.vertices.len() - start_vertices),
410            Range::new(start_uvs, self.uvs.len() - start_uvs),
411            Range::new(start_c, self.colors.len() - start_c)
412        ));
413    }
414}
415
416impl From<RgbaImage> for Texture {
417    fn from(image: RgbaImage) -> Texture {
418        Texture(Arc::new(RwLock::new(TextureInner {
419            id: None,
420            needs_update: false,
421            image: image
422        })))
423    }
424}
425
426
427impl<F, T> TextureBuffer<F, T> {
428    /// Creates a new `TextureBuffer`.
429    pub fn new(factory: F) -> TextureBuffer<F, T> {
430        TextureBuffer {
431            factory: factory,
432            textures: HashMap::new(),
433            next_id: 0,
434        }
435    }
436}
437
438impl Texture {
439    /// Edit image.
440    pub fn with_image_mut<F>(&self, f: F)
441        where F: FnOnce(&mut RgbaImage) {
442        let mut inner = self.0.write().unwrap();
443        f(&mut inner.image);
444        inner.needs_update = true;
445    }
446}