graphics_buffer/
lib.rs

1#![deny(missing_docs)]
2
3/*!
4This library provides a buffer which can be used as a render target for
5[Piston's graphics library](https://github.com/PistonDevelopers/graphics).
6This buffer can be loaded from and/or saved to a file on disk. This allows
7for things like screenshots in games.
8
9There is also an optional feature for `RenderBuffer` that allows it to be
10converted into a `G2dTexture` so that it can be rendered with
11[`piston_window`](https://github.com/PistonDevelopers/piston_window). To
12enable this, add `features = ["piston_window_texture"]` to the `graphics_buffer`
13dependency in your `cargo.toml`. More about this feature can be found in
14the [`RenderBuffer` documentation](struct.RenderBuffer.html).
15*/
16
17mod glyphs;
18pub use crate::glyphs::*;
19
20use std::{error, fmt, fs::File, ops, path::Path};
21
22use bit_vec::BitVec;
23use graphics::{draw_state::DrawState, math::Matrix2d, types::Color, Graphics, ImageSize};
24use image::{DynamicImage, GenericImageView, ImageResult, Rgba, RgbaImage};
25#[cfg(feature = "piston_window_texture")]
26use piston_window::{G2dTexture, G2dTextureContext};
27use png::{Decoder as PngDecoder, Limits};
28use rayon::prelude::*;
29use texture::{CreateTexture, Format, TextureOp, TextureSettings, UpdateTexture};
30
31/// The identity matrix: `[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]`.
32pub const IDENTITY: Matrix2d = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
33
34/// An Error type for `RenderBuffer`.
35#[derive(Debug, Clone)]
36pub enum Error {
37    /// Pixels/bytes mismatch when creating texture
38    SizeMismatch(usize, usize),
39}
40
41impl fmt::Display for Error {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        match self {
44            Error::SizeMismatch(len, area) => write!(
45                f,
46                "Container is too small for the given dimensions. \
47                 \nContainer has {} bytes, which encode {} pixels, \
48                 \nbut the given demensions contain {} pixels",
49                len,
50                len / 4,
51                area
52            ),
53        }
54    }
55}
56
57impl error::Error for Error {}
58
59/**
60A buffer that can be rendered to with Piston's graphics library.
61*/
62#[derive(Debug, Clone, PartialEq, Eq, Hash)]
63pub struct RenderBuffer {
64    inner: RgbaImage,
65    used: Vec<BitVec>,
66}
67
68impl RenderBuffer {
69    /// Create a new `RenderBuffer` with the given witdth or height.
70    pub fn new(width: u32, height: u32) -> RenderBuffer {
71        RenderBuffer {
72            inner: RgbaImage::new(width, height),
73            used: vec![BitVec::from_elem(height as usize, false); width as usize],
74        }
75    }
76    /// Creates a new `RenderBuffer` by opening it from a file.
77    pub fn open<P: AsRef<Path>>(path: P) -> Result<RenderBuffer, Box<dyn error::Error>> {
78        if path
79            .as_ref()
80            .extension()
81            .map(|ext| ext == "png")
82            .unwrap_or(false)
83        {
84            let (info, mut reader) = PngDecoder::new_with_limits(
85                File::open(&path)?,
86                Limits {
87                    bytes: std::usize::MAX,
88                },
89            )
90            .read_info()?;
91            let mut buf = vec![0; info.buffer_size()];
92            reader.next_frame(&mut buf)?;
93            Ok(
94                if let Some(image) = image::RgbaImage::from_raw(info.width, info.height, buf) {
95                    image.into()
96                } else {
97                    image::open(path)?.into()
98                },
99            )
100        } else {
101            Ok(image::open(path)?.into())
102        }
103    }
104    /// Creates a new `RenderBuffer` by decoding image data.
105    pub fn decode_from_bytes(bytes: &[u8]) -> ImageResult<RenderBuffer> {
106        image::load_from_memory(bytes).map(RenderBuffer::from)
107    }
108    /// Clear the buffer with a color.
109    pub fn clear(&mut self, color: [f32; 4]) {
110        self.clear_color(color);
111    }
112    /// Returns the color of the pixel at the given coordinates.
113    pub fn pixel(&self, x: u32, y: u32) -> [f32; 4] {
114        color_rgba_f32(*self.inner.get_pixel(x, y))
115    }
116    /// Sets the color of the pixel at the given coordinates.
117    pub fn set_pixel(&mut self, x: u32, y: u32, color: [f32; 4]) {
118        self.inner.put_pixel(x, y, color_f32_rgba(&color));
119    }
120    fn reset_used(&mut self) {
121        let (width, height) = self.inner.dimensions();
122        self.used = vec![BitVec::from_elem(height as usize, false); width as usize];
123    }
124    /// Creates a `G2dTexture` from the `RenderBuffer` for drawing to a `PistonWindow`.
125    #[cfg(feature = "piston_window_texture")]
126    pub fn to_g2d_texture(
127        &self,
128        context: &mut G2dTextureContext,
129        settings: &TextureSettings,
130    ) -> Result<G2dTexture, Box<dyn error::Error>> {
131        Ok(G2dTexture::from_image(context, &self.inner, settings)?)
132    }
133}
134
135impl TextureOp<()> for RenderBuffer {
136    type Error = Error;
137}
138
139impl CreateTexture<()> for RenderBuffer {
140    fn create<S: Into<[u32; 2]>>(
141        _factory: &mut (),
142        _format: Format,
143        memory: &[u8],
144        size: S,
145        _settings: &TextureSettings,
146    ) -> Result<Self, Error> {
147        let size = size.into();
148        Ok(RenderBuffer::from(
149            RgbaImage::from_raw(size[0], size[1], memory.to_vec())
150                .ok_or_else(|| Error::SizeMismatch(memory.len(), (size[0] * size[1]) as usize))?,
151        ))
152    }
153}
154
155impl UpdateTexture<()> for RenderBuffer {
156    fn update<O, S>(
157        &mut self,
158        _factory: &mut (),
159        _format: Format,
160        memory: &[u8],
161        offset: O,
162        size: S,
163    ) -> Result<(), Self::Error>
164    where
165        O: Into<[u32; 2]>,
166        S: Into<[u32; 2]>,
167    {
168        let offset = offset.into();
169        let size = size.into();
170        let new_image = RenderBuffer::from(
171            RgbaImage::from_raw(size[0], size[1], memory.to_vec())
172                .ok_or_else(|| Error::SizeMismatch(memory.len(), (size[0] * size[1]) as usize))?,
173        );
174        for i in 0..size[0] {
175            for j in 0..size[1] {
176                let color = new_image.pixel(i, j);
177                self.set_pixel(i + offset[0], j + offset[1], color);
178            }
179        }
180        Ok(())
181    }
182}
183
184impl From<RgbaImage> for RenderBuffer {
185    fn from(image: RgbaImage) -> Self {
186        let (width, height) = image.dimensions();
187        RenderBuffer {
188            inner: image,
189            used: vec![BitVec::from_elem(height as usize, false); width as usize],
190        }
191    }
192}
193
194impl From<DynamicImage> for RenderBuffer {
195    fn from(image: DynamicImage) -> Self {
196        let (width, height) = image.dimensions();
197        RenderBuffer {
198            inner: image.to_rgba8(),
199            used: vec![BitVec::from_elem(height as usize, false); width as usize],
200        }
201    }
202}
203
204impl ops::Deref for RenderBuffer {
205    type Target = RgbaImage;
206    fn deref(&self) -> &Self::Target {
207        &self.inner
208    }
209}
210
211impl ImageSize for RenderBuffer {
212    fn get_size(&self) -> (u32, u32) {
213        self.inner.dimensions()
214    }
215}
216
217impl Graphics for RenderBuffer {
218    type Texture = RenderBuffer;
219    fn clear_color(&mut self, color: Color) {
220        for (_, _, pixel) in self.inner.enumerate_pixels_mut() {
221            *pixel = color_f32_rgba(&color);
222        }
223    }
224    fn clear_stencil(&mut self, _value: u8) {}
225    fn tri_list<F>(&mut self, _draw_state: &DrawState, color: &[f32; 4], mut f: F)
226    where
227        F: FnMut(&mut dyn FnMut(&[[f32; 2]])),
228    {
229        self.reset_used();
230        // Render Triangles
231        f(&mut |vertices| {
232            for tri in vertices.chunks(3) {
233                // Get tri bounds for efficiency
234                let mut tl = [0f32, 0f32];
235                let mut br = [0f32, 0f32];
236                for v in tri {
237                    tl[0] = tl[0].min(v[0]);
238                    tl[1] = tl[1].min(v[1]);
239                    br[0] = br[0].max(v[0]);
240                    br[1] = br[1].max(v[1]);
241                }
242                let tl = [tl[0].floor().max(0.0) as i32, tl[1].floor().max(0.0) as i32];
243                let br = [
244                    br[0].ceil().min(self.width() as f32) as i32,
245                    br[1].ceil().min(self.height() as f32) as i32,
246                ];
247                // Render
248                let inner = &self.inner;
249                let used = &self.used;
250                (tl[0]..br[0]).into_par_iter().for_each(|x| {
251                    let mut entered = false;
252                    for y in tl[1]..br[1] {
253                        if triangle_contains(tri, [x as f32, y as f32]) {
254                            entered = true;
255                            if !used[x as usize].get(y as usize).unwrap_or(true) {
256                                let under_color =
257                                    color_rgba_f32(*inner.get_pixel(x as u32, y as u32));
258                                let layered_color = layer_color(&color, &under_color);
259                                unsafe {
260                                    (inner as *const RgbaImage as *mut RgbaImage)
261                                        .as_mut()
262                                        .unwrap()
263                                        .put_pixel(
264                                            x as u32,
265                                            y as u32,
266                                            color_f32_rgba(&layered_color),
267                                        );
268                                    (used as *const Vec<BitVec> as *mut Vec<BitVec>)
269                                        .as_mut()
270                                        .unwrap()[x as usize]
271                                        .set(y as usize, true);
272                                }
273                            }
274                        } else if entered {
275                            break;
276                        }
277                    }
278                });
279            }
280        });
281    }
282    fn tri_list_uv<F>(
283        &mut self,
284        _draw_state: &DrawState,
285        color: &[f32; 4],
286        texture: &Self::Texture,
287        mut f: F,
288    ) where
289        F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]])),
290    {
291        self.reset_used();
292        // Render Triangles
293        f(&mut |vertices, tex_vertices| {
294            for (tri, tex_tri) in vertices.chunks(3).zip(tex_vertices.chunks(3)) {
295                // Get tri bounds for efficiency
296                let mut tl = [0f32, 0f32];
297                let mut br = [0f32, 0f32];
298                for v in tri {
299                    tl[0] = tl[0].min(v[0]);
300                    tl[1] = tl[1].min(v[1]);
301                    br[0] = br[0].max(v[0]);
302                    br[1] = br[1].max(v[1]);
303                }
304                let tl = [tl[0].floor().max(0.0) as i32, tl[1].floor().max(0.0) as i32];
305                let br = [
306                    br[0].ceil().min((self.width() - 1) as f32) as i32,
307                    br[1].ceil().min((self.height() - 1) as f32) as i32,
308                ];
309                let avg_y = ((tri[0][1] + tri[1][1] + tri[2][1]) / 3.0) as i32;
310                let vert_center = (br[1] - tl[1]) / 2;
311                let vertical_balance_top = avg_y < vert_center;
312                // Render
313                let scaled_tex_tri = tri_image_scale(tex_tri, texture.get_size());
314                let inner = &self.inner;
315                let used = &self.used;
316                (tl[0]..br[0]).into_par_iter().for_each(|x| {
317                    let mut entered = false;
318                    let range: Box<dyn Iterator<Item = i32>> = if vertical_balance_top {
319                        Box::new(tl[1]..br[1])
320                    } else {
321                        Box::new((tl[1]..br[1]).rev())
322                    };
323                    for y in range {
324                        if triangle_contains(tri, [x as f32, y as f32]) {
325                            entered = true;
326                            let mapped_point =
327                                map_to_triangle([x as f32, y as f32], tri, &scaled_tex_tri);
328                            let texel = color_rgba_f32(*texture.get_pixel(
329                                (mapped_point[0].round() as u32).min(texture.width() - 1),
330                                (mapped_point[1].round() as u32).min(texture.height() - 1),
331                            ));
332                            let over_color = color_mul(color, &texel);
333                            let under_color = color_rgba_f32(*inner.get_pixel(x as u32, y as u32));
334                            let layered_color = layer_color(&over_color, &under_color);
335                            unsafe {
336                                (inner as *const RgbaImage as *mut RgbaImage)
337                                    .as_mut()
338                                    .unwrap()
339                                    .put_pixel(x as u32, y as u32, color_f32_rgba(&layered_color));
340                                (used as *const Vec<BitVec> as *mut Vec<BitVec>)
341                                    .as_mut()
342                                    .unwrap()[x as usize]
343                                    .set(y as usize, true);
344                            }
345                        } else if entered {
346                            break;
347                        }
348                    }
349                });
350            }
351        });
352    }
353
354    fn tri_list_c<F>(&mut self, _: &DrawState, _: F)
355    where
356        F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 4]])),
357    {
358        unimplemented!("<RenderBuffer as Graphics>::tri_list_c is currently unimplemented")
359    }
360
361    fn tri_list_uv_c<F>(&mut self, _: &DrawState, _: &Self::Texture, _: F)
362    where
363        F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]], &[[f32; 4]])),
364    {
365        unimplemented!("<RenderBuffer as Graphics>::tri_list_uv_c is currently unimplemented")
366    }
367}
368
369fn color_f32_rgba(color: &[f32; 4]) -> Rgba<u8> {
370    Rgba([
371        (color[0] * 255.0) as u8,
372        (color[1] * 255.0) as u8,
373        (color[2] * 255.0) as u8,
374        (color[3] * 255.0) as u8,
375    ])
376}
377
378fn color_rgba_f32(color: Rgba<u8>) -> [f32; 4] {
379    [
380        f32::from(color[0]) / 255.0,
381        f32::from(color[1]) / 255.0,
382        f32::from(color[2]) / 255.0,
383        f32::from(color[3]) / 255.0,
384    ]
385}
386
387fn color_mul(a: &[f32; 4], b: &[f32; 4]) -> [f32; 4] {
388    [a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]]
389}
390
391fn layer_color(over: &[f32; 4], under: &[f32; 4]) -> [f32; 4] {
392    let over_weight = 1.0 - (1.0 - over[3]).powf(2.0);
393    let under_weight = 1.0 - over_weight;
394    [
395        over_weight * over[0] + under_weight * under[0],
396        over_weight * over[1] + under_weight * under[1],
397        over_weight * over[2] + under_weight * under[2],
398        (over[3].powf(2.0) + under[3].powf(2.0)).sqrt().min(1.0),
399    ]
400}
401
402fn sign(p1: [f32; 2], p2: [f32; 2], p3: [f32; 2]) -> f32 {
403    (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
404}
405
406fn triangle_contains(tri: &[[f32; 2]], point: [f32; 2]) -> bool {
407    let b1 = sign(point, tri[0], tri[1]) < 0.0;
408    let b2 = sign(point, tri[1], tri[2]) < 0.0;
409    let b3 = sign(point, tri[2], tri[0]) < 0.0;
410    b1 == b2 && b2 == b3
411}
412
413#[allow(clippy::many_single_char_names)]
414fn map_to_triangle(point: [f32; 2], from_tri: &[[f32; 2]], to_tri: &[[f32; 2]]) -> [f32; 2] {
415    let t = from_tri;
416    let p = point;
417    // Computer some values that are used multiple times
418    let a = t[1][1] - t[2][1];
419    let b = p[0] - t[2][0];
420    let c = t[2][0] - t[1][0];
421    let d = p[1] - t[2][1];
422    let e = t[0][0] - t[2][0];
423    let f = t[0][1] - t[2][1];
424    let g = t[2][1] - t[0][1];
425    let ae_cf = a * e + c * f;
426    let bary_a = (a * b + c * d) / ae_cf;
427    let bary_b = (g * b + e * d) / ae_cf;
428    let bary_c = 1.0 - bary_a - bary_b;
429    [
430        bary_a * to_tri[0][0] + bary_b * to_tri[1][0] + bary_c * to_tri[2][0],
431        bary_a * to_tri[0][1] + bary_b * to_tri[1][1] + bary_c * to_tri[2][1],
432    ]
433}
434
435fn point_image_scale(point: [f32; 2], size: (u32, u32)) -> [f32; 2] {
436    [point[0] * size.0 as f32, point[1] * size.1 as f32]
437}
438
439fn tri_image_scale(tri: &[[f32; 2]], size: (u32, u32)) -> [[f32; 2]; 3] {
440    [
441        point_image_scale(tri[0], size),
442        point_image_scale(tri[1], size),
443        point_image_scale(tri[2], size),
444    ]
445}