about summary refs log tree commit diff
path: root/src/image.rs
blob: 177eff2bfb71f62a78d3cac50a49ab13cf9740fc (plain)
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
use gifed::{block::Palette, videogif::Frame, writer::ImageBuilder, Gif};

use crate::Vec2;

#[allow(dead_code)]
pub struct Image {
	width: u32,
	height: u32,
	data: Vec<u32>,
}

impl Image {
	pub fn new(width: u32, height: u32, fill: Option<Color>) -> Self {
		let size = width as usize * height as usize;
		let data = match fill {
			None => vec![0; size],
			Some(color) => vec![color.into(); size],
		};

		Self {
			width,
			height,
			data,
		}
	}

	pub fn data(&self) -> &[u32] {
		&self.data
	}

	pub fn data_mut(&mut self) -> &mut [u32] {
		&mut self.data
	}

	pub fn gif(&self) -> Gif {
		let mut palette = self.data.clone();
		palette.sort();
		palette.dedup();

		let indicies: Vec<u8> = self
			.data
			.clone()
			.into_iter()
			.map(|pix| palette.iter().position(|&x| x == pix).unwrap() as u8)
			.collect();

		let palette: Vec<gifed::Color> = palette
			.into_iter()
			.map(|c| {
				let b = c.to_be_bytes();
				gifed::Color::from([b[3], b[2], b[1]])
			})
			.collect();

		let img = ImageBuilder::new(self.width as u16, self.height as u16).build(indicies).unwrap();
		let mut gif = Gif::new(self.width as u16, self.height as u16);
		gif.set_palette(Some(Palette::try_from(palette).unwrap()));
		gif.push(img);

		gif
	}

	pub fn width(&self) -> u32 {
		self.width
	}

	pub fn height(&self) -> u32 {
		self.height
	}

	pub fn fill(&mut self, clr: Color) {
		self.data.fill(clr.into());
	}

	pub fn rect(&mut self, pos: Vec2<u32>, dim: Vec2<u32>, clr: Color) {
		let x_start = pos.x as usize;
		let x_end = (pos.x + dim.x) as usize;

		for idx in x_start..x_end {
			for idy in pos.y as usize..pos.y as usize + dim.y as usize {
				let data_idx = idx + idy * self.width as usize;
				self.data[data_idx] = clr.into();
			}
		}
	}

	/// Primitive, naive line drawing funciton. lerps the points together and
	/// draws a rect of the specified width at that location.
	pub fn line(&mut self, p1: Vec2<u32>, p2: Vec2<u32>, width: u32, clr: Color) {
		let start_x = p1.x.min(p2.x) as f32;
		let mut start_y = p1.y.min(p2.y) as f32;
		let end_x = p1.x.max(p2.x) as f32;
		let mut end_y = p1.y.max(p2.y) as f32;

		if start_y < end_y && start_x < end_x {
			// this just swaps the problem.
			//FIXME: genny- 2024-12
			let temp_y = start_y;
			start_y = end_y;
			end_y = temp_y;
		}

		tracing::trace!("LINE");
		tracing::trace!("\tstart_x = {start_x} / end_x = {end_x}");
		tracing::trace!("\tstart_y = {start_y} / end_y = {end_y}");

		let dx = end_x - start_x;
		let dy = end_y - start_y;
		let long = dx.max(dy);

		let dim = Vec2::new(width, width);
		for idx in 0..long as usize {
			let x = start_x + dx * (idx as f32 / long);
			let y = start_y + dy * (idx as f32 / long);

			self.rect(Vec2::new(x, y).as_u32(), dim, clr);
		}
	}
}

#[derive(Copy, Clone, Debug)]
pub struct Color {
	pub r: u8,
	pub g: u8,
	pub b: u8,
}

impl Color {
	pub fn new(r: u8, g: u8, b: u8) -> Self {
		Color { r, g, b }
	}
}

impl Into<u32> for Color {
	fn into(self) -> u32 {
		((self.r as u32) << 16) | ((self.g as u32) << 8) | (self.b as u32)
	}
}

impl Into<Color> for u32 {
	fn into(self) -> Color {
		let bytes = self.to_be_bytes();

		Color {
			r: bytes[1],
			g: bytes[2],
			b: bytes[3],
		}
	}
}