Commit cd25d1a3 authored by Michael Hölzl's avatar Michael Hölzl
Browse files

The first version of a canvas and removed the old graphicspath.

parent cc8e75f4
extern crate orbclient;
use orbclient::{Color, Window, Renderer, EventOption, GraphicsPath, Mode};
use orbclient::{Color, Window, Renderer, EventOption, Canvas, Mode};
fn main() {
......@@ -37,34 +37,90 @@ fn main() {
window.wu_line(100, 220, 400, 250, Color::rgba(255,0,0,255));
window.line(100, 230, 400, 260, Color::rgba(255,0,0,255));
// path and bezier curve example draw a cloud
let mut cloud_path = GraphicsPath::new();
cloud_path.move_to(170, 80);
cloud_path.bezier_curve_to(130, 100, 130, 150, 230, 150);
cloud_path.bezier_curve_to(250, 180, 320, 180, 340, 150);
cloud_path.bezier_curve_to(420, 150, 420, 120, 390, 100);
cloud_path.bezier_curve_to(430, 40, 370, 30, 340, 50);
cloud_path.bezier_curve_to(320, 5, 250, 20, 250, 50);
cloud_path.bezier_curve_to(200, 5, 150, 20, 170, 80);
window.draw_path_stroke(cloud_path, Color::rgb(0, 0, 255));
// path and quadratic curve example draw a balloon
let mut balloon_path = GraphicsPath::new();
balloon_path.move_to(75,25);
balloon_path.quadratic_curve_to(25,25,25,62);
balloon_path.quadratic_curve_to(25,100,50,100);
balloon_path.quadratic_curve_to(50,120,30,125);
balloon_path.quadratic_curve_to(60,120,65,100);
balloon_path.quadratic_curve_to(125,100,125,62);
balloon_path.quadratic_curve_to(125,25,75,25);
window.draw_path_stroke(balloon_path, Color::rgb(0, 0, 255));
//Init a new canvas
let mut canvas = Canvas::new(500.0,500.0);
//Transform the canvas
canvas.transform(3.0,0.0,0.0, 3.0,0.0,200.0);
//canvas.transform(0.71,-0.71,0.71, 0.71,0.0,0.0);
//Set canvas fill and stroke style
canvas.set_fill_style(Color::rgba(255,255,255,200));
canvas.set_stroke_style(Color::rgba(200,200,200,255));
//Create the polygon
canvas.begin_path();
canvas.move_to(48.355,17.922);
canvas.bezier_curve_to(52.06,20.245,54.658,24.176,55.131,28.739);
canvas.bezier_curve_to(56.642,29.445,58.319,29.851,60.097,29.851);
canvas.bezier_curve_to(66.588,29.851,71.849,24.59,71.849,18.1);
canvas.bezier_curve_to(71.849,11.609,66.588,6.348,60.097,6.348);
canvas.bezier_curve_to(53.668,6.35,48.453,11.517,48.355,17.922);
canvas.close_path();
canvas.move_to(40.656,41.984);
canvas.bezier_curve_to(47.147,41.984,52.408,36.722,52.408,30.232);
canvas.bezier_curve_to(52.408,23.742,47.146,18.481,40.656,18.481);
canvas.bezier_curve_to(34.166,18.481,28.902,23.743,28.902,30.233);
canvas.bezier_curve_to(28.902,36.723,34.166,41.984,40.656,41.984);
canvas.close_path();
canvas.move_to(45.641,42.785);
canvas.line_to(35.669,42.785);
canvas.bezier_curve_to(27.372,42.785,20.622,49.536,20.622,57.833);
canvas.line_to(20.622,70.028);
canvas.line_to(20.653,70.219);
canvas.line_to(21.493,70.482);
canvas.bezier_curve_to(29.411,72.956,36.290,73.781,41.952,73.781);
canvas.bezier_curve_to(53.011,73.781,59.421,70.628,59.816,70.427);
canvas.line_to(60.601,70.03);
canvas.line_to(60.685,70.03);
canvas.line_to(60.685,57.833);
canvas.bezier_curve_to(60.688,49.536,53.938,42.785,45.641,42.785);
canvas.close_path();
canvas.move_to(65.084,30.653);
canvas.line_to(55.189,30.653);
canvas.bezier_curve_to(55.082,34.612,53.392,38.177,50.719,40.741);
canvas.bezier_curve_to(58.094,42.934,63.49,49.773,63.49,57.851);
canvas.line_to(63.49,61.609);
canvas.bezier_curve_to(73.26,61.251,78.89,58.482,79.261,58.296);
canvas.line_to(80.046,57.898);
canvas.line_to(80.13,57.898);
canvas.line_to(80.13,45.699);
canvas.bezier_curve_to(80.13,37.403,73.38,30.653,65.084,30.653);
canvas.close_path();
canvas.move_to(20.035,29.853);
canvas.bezier_curve_to(22.334,29.853,24.473,29.182,26.285,28.039);
canvas.bezier_curve_to(26.861,24.282,28.875,20.999,31.752,18.763);
canvas.bezier_curve_to(31.764,18.543,31.785,18.325,31.785,18.103);
canvas.bezier_curve_to(31.785,11.612,26.523,6.351,20.035,6.351);
canvas.bezier_curve_to(13.543,6.351,8.283,11.612,8.283,18.103);
canvas.bezier_curve_to(8.283,24.591,13.543,29.853,20.035,29.853);
canvas.close_path();
canvas.move_to(30.589,40.741);
canvas.bezier_curve_to(27.929,38.19,26.245,34.644,26.122,30.709);
canvas.bezier_curve_to(25.755,30.682,25.392,30.653,25.018,30.653);
canvas.line_to(15.047,30.653);
canvas.bezier_curve_to(6.75,30.653,0.0,37.403,0.0,45.699);
canvas.line_to(0.0,57.896);
canvas.line_to(0.031,58.084);
canvas.line_to(0.871,58.349);
canvas.bezier_curve_to(7.223,60.332,12.892,61.246,17.816,61.534);
canvas.line_to(17.816,57.851);
canvas.bezier_curve_to(17.818,49.773,23.212,42.936,30.589,40.74);
//Fill the polygon and draw a stroke
canvas.fill();
canvas.stroke();
window.image_fast(500,0,500,500, unsafe { &canvas.data });
window.char(200, 200, '═', Color::rgb(0, 0, 0));
window.char(208, 200, '═', Color::rgb(0, 0, 0));
// testing for non existent x,y position : does not panic but returns Color(0,0,0,0)
let _non_existent_pixel = window.getpixel(width as i32 +10,height as i32 +10);
// testing PartialEq for Color
if Color::rgb(11,2,3) == Color::rgba(1,2,3,100) {
println!("Testing colors: they are the same!")
......
use color::Color;
use point::Point;
use pathbuilder::PathBuilder;
use edge::Edge;
use canvaspaintstate::CanvasPaintState;
#[repr(packed)]
#[allow(unused)]
pub struct Canvas {
pub width: f32,
pub height: f32,
pub data: Vec<Color>,
path_builder: PathBuilder,
state: CanvasPaintState,
saved_states: Vec<CanvasPaintState>,
}
impl Canvas {
pub fn new(width: f32, height: f32) -> Self {
let size:u64 = (width * height) as u64;
Canvas {
width: width,
height: height,
data: vec![Color::rgba(0,0,0,0); size as usize],
path_builder: PathBuilder::new(),
state: CanvasPaintState::new(),
saved_states: vec![]
}
}
fn pixel(&mut self, x: i32, y: i32, color: Color) {
let replace = true;
let w = self.width as i32;
let h = self.height as i32;
let data = unsafe{ &mut self.data };
if x >= 0 && y >= 0 && x < w as i32 && y < h as i32 {
let new = color.data;
let alpha = (new >> 24) & 0xFF;
let old = unsafe{ &mut data[y as usize * w as usize + x as usize].data };
if alpha >= 255 || replace {
*old = new;
} else if alpha >0 {
let n_alpha = 255 - alpha;
let rb = ((n_alpha * ( *old & 0x00FF00FF)) + (alpha * (new & 0x00FF00FF))) >> 8;
let ag = (n_alpha * ((*old & 0xFF00FF00) >> 8)) + ( alpha * (0x01000000 | ((new & 0x0000FF00) >>8)));
*old = (rb & 0x00FF00FF) | (ag & 0xFF00FF00);
}
}
}
/// Draw a line
pub fn line(&mut self, argx1: i32, argy1: i32, argx2: i32, argy2: i32, color: Color) {
let mut x = argx1;
let mut y = argy1;
let dx = if argx1 > argx2 { argx1 - argx2 } else { argx2 - argx1 };
let dy = if argy1 > argy2 { argy1 - argy2 } else { argy2 - argy1 };
let sx = if argx1 < argx2 { 1 } else { -1 };
let sy = if argy1 < argy2 { 1 } else { -1 };
let mut err = if dx > dy { dx } else {-dy} / 2;
let mut err_tolerance;
loop {
self.pixel(x, y, color);
if x == argx2 && y == argy2 { break };
err_tolerance = 2 * err;
if err_tolerance > -dx { err -= dy; x += sx; }
if err_tolerance < dy { err += dx; y += sy; }
}
}
}
/// Common
#[allow(unused)]
impl Canvas {
pub fn save(&mut self) {
unsafe {
self.saved_states.push(self.state);
}
}
pub fn restore(&mut self) {
unsafe {
if self.saved_states.len() > 0 {
self.state = self.saved_states.pop().unwrap();
}
}
}
}
//Paths
#[allow(unused)]
impl Canvas {
pub fn scanline(&mut self, y: i32) -> Vec<i32> {
let mut cross_points:Vec<i32> = Vec::new();
let mut edges: Vec<Edge> = Vec::new();
unsafe {
for edge in &mut self.path_builder.edges {
edges.push(*edge);
}
}
for edge in edges {
let start_point = unsafe { self.state.transform.apply_to_point(edge.start) };
let end_point = unsafe { self.state.transform.apply_to_point(edge.end) };
let t: f32 = (((end_point.x - start_point.x) * (y as f32 - start_point.y)) / (end_point.y - start_point.y)) + start_point.x;
if (start_point.y > y as f32) != (end_point.y > y as f32) {
cross_points.push(t as i32);
}
}
cross_points.sort();
cross_points
}
pub fn fill(&mut self){
let color: Color;
unsafe {
color = self.state.fill_style;
}
for y in 0..self.height as i32 {
let mut lines = self.scanline(y);
let mut j: i32 = 0;
while j < lines.len() as i32 {
if j+1 < lines.len() as i32 {
self.line(lines[j as usize],y, lines[(j+1) as usize], y, color);
j = j + 2;
} else {
j = lines.len() as i32;
}
}
}
}
pub fn stroke(&mut self){
let mut x: i32 = 0;
let mut y: i32 = 0;
let color: Color;
let mut edges: Vec<Edge> = Vec::new();
unsafe {
color = self.state.stroke_style;
for edge in &mut self.path_builder.edges {
edges.push(*edge);
}
}
for edge in edges {
let start_point = unsafe { self.state.transform.apply_to_point(edge.start) };
let end_point = unsafe { self.state.transform.apply_to_point(edge.end) };
self.line(start_point.x as i32,start_point.y as i32 , end_point.x as i32, end_point.y as i32, color);
}
}
///
pub fn begin_path(&mut self){
self.path_builder = PathBuilder::new();
}
///
pub fn close_path(&mut self){
let path_builder = unsafe { &mut self.path_builder };
path_builder.close_path();
}
/// move to position
pub fn move_to(&mut self, x: f32, y: f32) {
let p = Point::new(x as f32, y as f32);
let path_builder = unsafe { &mut self.path_builder };
path_builder.move_to(x ,y );
}
/// create a line between the last and new point
pub fn line_to(&mut self, x: f32, y: f32) {
let p = Point::new(x as f32, y as f32);
let path_builder = unsafe { &mut self.path_builder };
path_builder.line_to(x ,y );
}
/// quadratic bezier curve
pub fn quadratic_curve_to(&mut self, cpx: f32, cpy: f32, x: f32, y: f32){
let path_builder = unsafe { &mut self.path_builder };
path_builder.quadratic_curve_to(cpx,cpy,x,y);
}
/// cubic bezier curve
pub fn bezier_curve_to(&mut self, cp1x: f32, cp1y: f32, cp2x: f32, cp2y: f32, x: f32, y: f32){
let path_builder = unsafe { &mut self.path_builder };
path_builder.bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x,y);
}
pub fn rect(&mut self, x: f32, y: f32, width: f32, height: f32){
let path_builder = unsafe { &mut self.path_builder };
path_builder.move_to(x, y);
path_builder.line_to((x + width), y);
path_builder.line_to((x + width),(y+height));
path_builder.line_to(x, (y+height));
path_builder.line_to(x, y);
}
}
/// Transformations
#[allow(unused)]
impl Canvas {
/// Scales the current drawing bigger or smaller
pub fn scale(&mut self) {}
/// Rotates the current drawing
pub fn rotate(&mut self) {}
/// Remaps the (0,0) position on the canvas
pub fn translate(&mut self) {}
/// Replaces the current transformation matrix for the drawing
pub fn transform(&mut self, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) {
unsafe {
self.state.transform.transform(a,b,c,d,e,f);
}
}
/// Resets the current transform to the identity matrix. Then runs transform()
pub fn set_transform(&mut self, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) {
unsafe {
self.state.transform.set_transform(a,b,c,d,e,f);
}
}
}
/// Transformations
#[allow(unused)]
impl Canvas {
pub fn set_fill_style(&mut self, color: Color) {
self.state.fill_style = color;
}
pub fn set_stroke_style(&mut self, color: Color) {
self.state.stroke_style = color;
}
}
use color::Color;
use matrix::Matrix;
#[derive(Copy, Clone)]
pub struct CanvasPaintState {
pub fill_style: Color,
pub stroke_style: Color,
pub line_width: f32,
pub transform: Matrix,
}
impl CanvasPaintState {
pub fn new() -> Self {
CanvasPaintState {
fill_style: Color::rgba(0,0,0,0),
stroke_style: Color::rgba(0,0,0,0),
line_width: 1.0,
transform: Matrix::new(),
}
}
}
\ No newline at end of file
use point::Point;
#[derive(Copy, Clone)]
pub enum EdgeType {
Visible,
Hidden,
}
#[derive(Copy, Clone)]
pub struct Edge {
pub start: Point,
pub end: Point,
}
impl Edge {
pub fn new(start: Point, end: Point) -> Self {
Edge {
start: start,
end: end,
}
}
}
use point::Point;
#[derive(Copy, Clone)]
pub struct Matrix {
pub a: f32,
pub b: f32,
pub c: f32,
pub d: f32,
pub e: f32,
pub f: f32,
}
impl Matrix {
pub fn new() -> Self {
Matrix {
a: 1.0,
b: 0.0,
c: 0.0,
d: 1.0,
e: 0.0,
f: 0.0,
}
}
pub fn set_transform(&mut self, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32){
self.a = a;
self.b = b;
self.c = c;
self.d = d;
self.e = e;
self.f = f;
}
pub fn transform(&mut self, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32){
let org_a= self.a;
let org_b= self.b;
let org_c= self.c;
let org_d= self.d;
let org_e= self.e;
let org_f= self.f;
self.a = org_a * a + org_c * b;
self.b = org_b * a + org_d * b;
self.c = org_a * c + org_c * d;
self.d = org_b * c + org_d * d;
self.e = org_a * e + org_c * f + org_e;
self.f = org_b * e + org_d * f + org_f;
}
pub fn apply_to_point(&mut self, point: Point) -> (Point){
Point::new(
point.x * self.a + point.y * self.c + self.e,
point.x * self.b + point.y * self.d + self.f
)
}
}
use alloc::Vec;
use edge::Edge;
use point::Point;
/// graphic path with similar functions like html canvas
#[derive(Clone)]
pub struct PathBuilder {
last_point: Point,
last_moved_point: Point,
pub edges: Vec<Edge>,
}
impl PathBuilder {
pub fn new() -> Self {
PathBuilder {
last_point : Point::new(0.0,0.0),
last_moved_point : Point::new(0.0,0.0),
edges: Vec::new(),
}
}
/// move to position
pub fn move_to(&mut self, x: f32, y: f32){
//self.points.push((x,y,PathPointType::Move));
self.last_point = Point::new(x,y);
self.last_moved_point = Point::new(x,y);
}
/// create a line between the last and new point
pub fn line_to(&mut self, x: f32, y: f32) {
//self.points.push((x,y,PathPointType::Connect));
self.edges.push(Edge::new(self.last_point, Point::new(x,y)));
self.last_point = Point::new(x,y);
}
pub fn close_path(&mut self) {
self.edges.push(Edge::new(self.last_point, self.last_moved_point));
//self.x = x;
//self.y = y;
}
/// quadratic bezier curve
pub fn quadratic_curve_to(&mut self, argx1: f32, argy1: f32, argx2: f32, argy2: f32){
let mut t:f32 = 0.0;
let mut u:f32;
let mut tt:f32;
let mut uu:f32;
let mut x:f32;
let mut y:f32;
let mut tmp_point = self.last_point;
while t < 1.0 {
u = 1.0 - t;
uu = u * u;
tt = t * t;
x = self.last_point.x * uu;
y = self.last_point.y * uu;
x += 2.0 * u * t * argx1;
y += 2.0 * u * t * argy1 ;
x += tt * argx2;
y += tt * argy2;
t += 0.01;
//self.points.push((x as f32, y as f32, PathPointType::Connect));
self.edges.push(Edge::new(tmp_point, Point::new(x,y)));
tmp_point = Point::new(x,y);
}
self.last_point = Point::new(argx2,argy2);
}
/// cubic bezier curve
pub fn bezier_curve_to(&mut self, argx1: f32, argy1: f32, argx2: f32, argy2: f32, argx3: f32, argy3: f32){
let mut t:f32 = 0.0;
let mut u:f32;
let mut tt:f32;
let mut uu:f32;
let mut uuu:f32;
let mut ttt:f32;
let mut x:f32;
let mut y:f32;
let mut tmp_point = self.last_point;
while t < 1.0 {
u = 1.0 - t;
tt = t * t;
uu = u * u;
uuu = uu * u;
ttt = tt * t;
x = self.last_point.x as f32 * uuu;
y = self.last_point.y as f32 * uuu;
x += 3.0 * uu * t * argx1 as f32;
y += 3.0 * uu * t * argy1 as f32;
x += 3.0 * u * tt * argx2 as f32;
y += 3.0 * u * tt * argy2 as f32;
x += ttt * argx3 as f32;
y += ttt * argy3 as f32;