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
use image::{Rgba, RgbaImage};
use hsl::HSL;
use {hsl_to_rgba, fill_rect};

pub struct Options {
	pub size: u32,
	pub scale: u32,
	pub seed: Vec<u8>,
	pub color: Option<Rgba<u8>>,
	pub background_color: Option<Rgba<u8>>,
	pub spot_color: Option<Rgba<u8>>,
}

pub struct Ethereum {
	randseed: [i32; 4],
}

#[derive(Debug, Clone)]
enum FillType {
	None,
	Color,
	SpotColor,
}

impl Ethereum {
	fn seedrand(&mut self, seed: &[u8]) {
		self.randseed = [0i32; 4];

		for i in 0..seed.len() {
			let (tmp, _) = (self.randseed[i % 4] << 5).overflowing_sub(self.randseed[i % 4]);
			self.randseed[i % 4] = tmp + seed[i] as i32;
		}
	}

	fn rand(&mut self) -> f64 {
		let t = self.randseed[0] ^ (self.randseed[0] << 11);
		self.randseed[0] = self.randseed[1];
		self.randseed[1] = self.randseed[2];
		self.randseed[2] = self.randseed[3];
		self.randseed[3] = self.randseed[3] ^ (self.randseed[3] >> 19) ^ (t ^ (t >> 8));

		((self.randseed[3].abs() as f64) / ((1i32 << 31) as f64)).abs()
	}

	fn create_color(&mut self) -> Rgba<u8> {
		let hsl = HSL {
			h: (self.rand() * 360.0).floor(),
			s: (self.rand() * 60.0 + 40.0) / 100.0,
			l: (self.rand() + self.rand() + self.rand() + self.rand()) * 25.0 / 100.0,
		};
		hsl_to_rgba(hsl)
	}

	fn create_image_data(&mut self, size: u32) -> Vec<FillType> {
		let odd = size % 2 == 1;
		let data_width = size / 2;

		(0..size)
			.into_iter()
			.map(|_| {
				let row = (0..data_width)
					.into_iter()
					.map(|_| {
						match (self.rand() * 2.3).floor() {
							0.0 => FillType::None,
							1.0 => FillType::Color,
							_ => FillType::SpotColor,
						}
					})
					.collect::<Vec<_>>();
				let mut cloned_row = row.clone();
				if odd {
					let last = cloned_row.last().cloned().unwrap_or(FillType::None);
					cloned_row.push(last);
				}

				cloned_row.into_iter().chain(row.into_iter().rev()).collect::<Vec<_>>()
			})
			.flat_map(|x| x)
			.collect()
	}

	pub fn create_icon(options: Options) -> RgbaImage {
		let mut builder = Ethereum {
			randseed: [0i32; 4],
		};

		builder.seedrand(&options.seed);

		let scale = options.scale;
		let color = options.color.unwrap_or_else(|| builder.create_color());
		let background_color = options.background_color.unwrap_or_else(|| builder.create_color());
		let spot_color = options.spot_color.unwrap_or_else(|| builder.create_color());
		let image_data = builder.create_image_data(options.size);
		let real_size = options.size * scale;
		let mut image = RgbaImage::new(real_size, real_size);
		fill_rect(&mut image, 0, 0, real_size, background_color);

		for (index, fill) in image_data.into_iter().enumerate() {
			let index = index as u32;
			let row = index / options.size;
			let col = index % options.size;

			match fill {
				FillType::None => (),
				FillType::Color => fill_rect(&mut image, col * scale, row * scale, scale, color),
				FillType::SpotColor => fill_rect(&mut image, col * scale, row * scale, scale, spot_color),
			}
		}

		image
	}
}