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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
/* mod.rs
*
* Developed by Tim Walls <tim.walls@snowgoons.com>
* Copyright (c) All Rights Reserved, Tim Walls
*/
//! Graphics primitives for our display driver layer. The general philosophy
//! we are taking is that we have far more CPU cycles spare than memory on
//! a chip like an AVR. So rather than keeping a bitmap buffer and writing to
//! it, we take the approach of not holding any buffer and instead
//! implement primitives that can calculate the appropriate value for any
//! given pixel on-demand.
// Imports ===================================================================
use avrox_display::GfxResult;
pub mod primitives;
pub mod pixels;
mod patterns;
pub mod sevenseg;
pub mod dynamic;
pub mod img;
pub mod fills;
#[cfg(test)]
mod test;
use avr_oxide::OxideResult::{Ok};
// Declarations ==============================================================
/// Hints that tell a component the order in which a device will typically
/// ask for pixel data. This may be used by the graphics command to
/// optimise how it retrieves, calculates or caches data.
#[cfg_attr(not(target_arch="avr"), derive(Debug))]
#[cfg_attr(target_arch="avr", derive(ufmt::derive::uDebug))]
pub enum RenderOrderingHint {
/// The device typically renders row-by-row
Rows,
/// The device typically renders column-by-column
Columns,
/// No device follows no rhyme or reason
Random
}
/// X-coordinates
pub type XCoord = u16;
/// Y-coordinates
pub type YCoord = u16;
/// An (X,Y) coordinate pair.
#[cfg_attr(not(target_arch="avr"), derive(Debug))]
#[cfg_attr(target_arch="avr", derive(ufmt::derive::uDebug))]
#[derive(Copy,Clone,Eq,PartialEq)]
pub struct Point(pub XCoord, pub YCoord);
#[cfg_attr(not(target_arch="avr"), derive(Debug,PartialEq))]
#[cfg_attr(target_arch="avr", derive(ufmt::derive::uDebug))]
pub struct Area {
pub(crate) tl: Point,
pub(crate) w: XCoord,
pub(crate) h: YCoord
}
pub trait RenderPlane {
const WIDTH : XCoord;
const HEIGHT : YCoord;
const ORDERHINT: RenderOrderingHint;
}
/// Trait implemented by any graphics command that is capable of being
/// rendered onto a 2d plane
pub trait Renderable {
type PIXEL;
/// Return the value of the pixel at the given X-Y coordinate
fn get_pixel_at<P: RenderPlane>(&self, coord: Point) -> GfxResult<Self::PIXEL>;
/// Return the dimensions of this primitive. Other container types may
/// use this to aid in optimisation or layout.
fn get_dimensions<P: RenderPlane>(&self) -> GfxResult<(XCoord,YCoord)> {
Ok(( P::WIDTH, P::HEIGHT ))
}
/// Indicate if any of the content of this renderable has (or may have)
/// changed.
fn has_changes<P: RenderPlane>(&self) -> bool {
true
}
/// Return the area of this renderable that has (or may have) changed.
fn get_change_area<P: RenderPlane>(&self) -> GfxResult<Option<Area>> {
if self.has_changes::<P>() {
// Default implementation is just to return our entire area; this will
// always be 'correct' (it 'may' have changed) even if inefficient
let dims = self.get_dimensions::<P>()?;
Ok(Some(Area {
tl: Point(0, 0),
w: dims.0,
h: dims.1
}))
} else {
Ok(None)
}
}
}
// Code ======================================================================
/// Merge two areas into a new one which encompasses both the original areas.
fn merge_areas(area1: Option<Area>, area2: Option<Area>) -> Option<Area> {
#[cfg(test)]
println!(" Merging {:?} with {:?}", &area1, &area2);
let merged = match (area1, area2) {
(None, None) => None,
(Some(a), None) => Some(a),
(None, Some(b)) => Some(b),
(Some(a), Some(b)) => {
let ( tl_a_x, tl_a_y ) = ( a.tl.0, a.tl.1 );
let ( tl_b_x, tl_b_y ) = ( b.tl.0, b.tl.1 );
let ( br_a_x, br_a_y ) = ( tl_a_x + a.w, tl_a_y + a.h );
let ( br_b_x, br_b_y ) = ( tl_b_x + b.w, tl_b_y + b.h );
let tl_x = core::cmp::min(tl_a_x, tl_b_x);
let tl_y = core::cmp::min(tl_a_y, tl_b_y);
let br_x = core::cmp::max(br_a_x, br_b_x);
let br_y = core::cmp::max(br_a_y, br_b_y);
let w = br_x - tl_x;
let h = br_y - tl_y;
Some(Area {
tl: Point(tl_x, tl_y),w,h
})
}
};
#[cfg(test)]
println!(" ==> {:?}", &merged);
merged
}
// Tests =====================================================================
#[cfg(test)]
mod tests {
use avrox_display::gfx::{Area, XCoord, Point, YCoord, merge_areas};
fn area(tlx: XCoord, tly: YCoord, w: XCoord, h: YCoord) -> Area {
Area {
tl: Point(tlx, tly),
w,
h
}
}
#[test]
fn test_area_merges() {
let area1 = Some(area(0,0,24,7));
let area2 = Some(area(24, 0, 72, 14));
assert_eq!(merge_areas(area1,area2), Some(area(0,0,24+72,14)));
}
}