summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md11
-rw-r--r--Cargo.toml16
-rw-r--r--source/benoit/benoit.rs9
-rw-r--r--source/benoit/benoit/app.rs14
-rw-r--r--source/benoit/benoit/app/allocate_buffers.rs1
-rw-r--r--source/benoit/benoit/app/animate.rs7
-rw-r--r--source/benoit/benoit/app/colour.rs43
-rw-r--r--source/benoit/benoit/app/colour_row.rs61
-rw-r--r--source/benoit/benoit/app/handle_keys.rs35
-rw-r--r--source/benoit/benoit/app/image_filename.rs1
-rw-r--r--source/benoit/benoit/app/initialise.rs9
-rw-r--r--source/benoit/benoit/app/interactive.rs10
-rw-r--r--source/benoit/benoit/app/poll_events.rs1
-rw-r--r--source/benoit/benoit/app/run.rs1
-rw-r--r--source/benoit/benoit/app/still.rs7
-rw-r--r--source/benoit/benoit/configuration.rs184
-rw-r--r--source/benoit/benoit/configuration/default.rs64
-rw-r--r--source/benoit/benoit/configuration/load.rs184
-rw-r--r--source/benoit/benoit/factorisation.rs50
-rw-r--r--source/benoit/benoit/palette.rs101
-rw-r--r--source/benoit/benoit/palette/paint.rs (renamed from source/benoit/benoit/render/paint.rs)0
-rw-r--r--source/benoit/benoit/palette/paint/ancient.rs (renamed from source/benoit/benoit/render/paint/ancient.rs)12
-rw-r--r--source/benoit/benoit/palette/paint/fire.rs (renamed from source/benoit/benoit/render/paint/fire.rs)30
-rw-r--r--source/benoit/benoit/palette/paint/greyscale.rs (renamed from source/benoit/benoit/render/paint/greyscale.rs)8
-rw-r--r--source/benoit/benoit/palette/paint/hsv.rs (renamed from source/benoit/benoit/render/paint/hsv.rs)8
-rw-r--r--source/benoit/benoit/palette/paint/lch.rs (renamed from source/benoit/benoit/render/paint/lch.rs)22
-rw-r--r--source/benoit/benoit/palette/paint/sapphire.rs (renamed from source/benoit/benoit/render/paint/sapphire.rs)8
-rw-r--r--source/benoit/benoit/render.rs14
-rw-r--r--source/benoit/benoit/render/colour.rs74
-rw-r--r--source/benoit/benoit/render/colour_data.rs50
-rw-r--r--source/benoit/benoit/render/factorise.rs28
-rw-r--r--source/benoit/benoit/render/factorise/smooth.rs30
-rw-r--r--source/benoit/benoit/render/factorise/stepped.rs31
-rw-r--r--source/benoit/benoit/render/render.rs (renamed from source/benoit/benoit/app/render.rs)17
-rw-r--r--source/benoit/benoit/render/render_data.rs14
-rw-r--r--source/benoit/benoit/render/render_row/julia.rs2
-rw-r--r--source/benoit/benoit/render/render_row/normal.rs2
37 files changed, 467 insertions, 692 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c09d5b4..56c5591 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+# 2.1.0
+
+* Refactor code structure
+* Pre-calculate palettes
+* Depend on ctor
+* Remove factoriser functions
+* Depend on enum-iterator
+* Bump minor version
+* Bump dependency versions
+* Fix control guide
+
# 2.0.0
* Bump major version
diff --git a/Cargo.toml b/Cargo.toml
index b99670b..1e0d590 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "benoit"
-version = "2.0.0"
+version = "2.1.0"
authors = ["Gabriel Bjørnager Jensen"]
edition = "2021"
description = "Mandelbrot renderer."
@@ -16,9 +16,11 @@ codegen-units = 1
lto = "fat"
[dependencies]
-png = "0.17.10"
-rayon = "1.8.0"
-rug = "1.22.0"
-sdl2 = "0.35.2"
-toml = "0.8.0"
-webp = "0.2.6"
+ctor = "0.2.5"
+enum-iterator = "1.4.1"
+png = "0.17.10"
+rayon = "1.8.0"
+rug = "1.22.0"
+sdl2 = "0.35.2"
+toml = "0.8.2"
+webp = "0.2.6"
diff --git a/source/benoit/benoit.rs b/source/benoit/benoit.rs
index bbbc158..1214d16 100644
--- a/source/benoit/benoit.rs
+++ b/source/benoit/benoit.rs
@@ -27,7 +27,6 @@ use rug::Float;
pub mod app;
pub mod configuration;
-pub mod factorisation;
pub mod fractal;
pub mod palette;
pub mod rendering;
@@ -45,18 +44,12 @@ pub struct Version<T> {
pub const VERSION: Version::<u32> = Version::<u32> {
major: 0x2,
- minor: 0x0,
+ minor: 0x1,
patch: 0x0,
};
pub const PRECISION: u32 = 0x80;
-// We would like to precalculate the palettes at
-// compile-time, but Rust does not support
-// floating-point arithmetic there.
-#[allow(dead_code)]
-pub const PALETTE_LENGTH: usize = 0x100;
-
#[derive(Clone, Copy)]
pub enum ImageFormat {
Png,
diff --git a/source/benoit/benoit/app.rs b/source/benoit/benoit/app.rs
index 1863a77..feac808 100644
--- a/source/benoit/benoit/app.rs
+++ b/source/benoit/benoit/app.rs
@@ -22,11 +22,10 @@
*/
use crate::benoit::ImageFormat;
-use crate::benoit::factorisation::Factorisation;
use crate::benoit::fractal::Fractal;
use crate::benoit::palette::Palette;
use crate::benoit::rendering::Rendering;
-use crate::benoit::render::{FactoriserFunction, IteratorFunction, PaletteFunction, RowRenderer};
+use crate::benoit::render::{IteratorFunction, RowRenderer};
use crate::benoit::video::Video;
extern crate rug;
@@ -35,8 +34,6 @@ use rug::Float;
pub mod allocate_buffers;
pub mod animate;
-pub mod colour;
-pub mod colour_row;
pub mod drop;
pub mod dump;
pub mod handle_keys;
@@ -44,7 +41,6 @@ pub mod image_filename;
pub mod initialise;
pub mod interactive;
pub mod poll_events;
-pub mod render;
pub mod run;
pub mod still;
@@ -68,9 +64,8 @@ pub struct App {
max_iter_count: u32,
- factorisation: Factorisation,
- palette: Palette,
- colour_range: f32,
+ palette: Palette,
+ colour_range: f32,
dump_path: String,
image_format: ImageFormat,
@@ -83,7 +78,4 @@ pub struct App {
row_renderer: RowRenderer,
iterator_function: IteratorFunction,
-
- factoriser: FactoriserFunction,
- palette_function: PaletteFunction,
}
diff --git a/source/benoit/benoit/app/allocate_buffers.rs b/source/benoit/benoit/app/allocate_buffers.rs
index b8616d5..44bbb28 100644
--- a/source/benoit/benoit/app/allocate_buffers.rs
+++ b/source/benoit/benoit/app/allocate_buffers.rs
@@ -24,6 +24,7 @@
use crate::benoit::app::App;
impl App {
+ #[must_use]
pub fn allocate_buffers(canvas_width: u32, canvas_height: u32) -> (Vec::<u32>, Vec::<f32>, Vec::<u8>) {
let canvas_size = canvas_height as usize * canvas_width as usize;
diff --git a/source/benoit/benoit/app/animate.rs b/source/benoit/benoit/app/animate.rs
index 7cc3549..3d35bdb 100644
--- a/source/benoit/benoit/app/animate.rs
+++ b/source/benoit/benoit/app/animate.rs
@@ -23,6 +23,7 @@
use crate::benoit::PRECISION;
use crate::benoit::app::App;
+use crate::benoit::render::{colour, render};
extern crate rug;
extern crate sdl2;
@@ -71,19 +72,19 @@ impl App {
factor
};
- eprintln!("animating {} frames at {}{:+}i to {:.3} (fac. {:.3})", self.frame_count, self.centre_real.to_f64(), self.centre_imag.to_f64(), zoom_stop.to_f64(), zoom_factor.to_f64());
+ eprintln!("animating {} frames at {}{:+}i to {:.3} (fac. {:.3})", self.frame_count, self.centre_real.to_f32(), self.centre_imag.to_f32(), zoom_stop.to_f32(), zoom_factor.to_f32());
for frame in 0x0..self.frame_count {
eprint!("{frame:010} (2^{:.9}x)...", zoom.to_f64().log2());
let time_start = Instant::now();
- self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], &self.centre_real, &self.centre_imag, &zoom, self.max_iter_count);
+ render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], self.canvas_width, self.canvas_height, &self.centre_real, &self.centre_imag, &zoom, self.max_iter_count, self.row_renderer, self.iterator_function);
let render_time = time_start.elapsed();
eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
- self.colour(&mut image[..], self.multibrot_exponent, self.max_iter_count, &mut iter_count_buffer[..], &mut square_dist_buffer[..]);
+ colour(&mut image[..], self.canvas_width, self.canvas_height, self.multibrot_exponent, self.max_iter_count, self.colour_range, self.palette, &iter_count_buffer[..], &square_dist_buffer[..]);
let colour_time = time_start.elapsed() - render_time;
eprint!(" {:.3}ms...", colour_time.as_micros() as f32 / 1000.0);
diff --git a/source/benoit/benoit/app/colour.rs b/source/benoit/benoit/app/colour.rs
deleted file mode 100644
index 03093ac..0000000
--- a/source/benoit/benoit/app/colour.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- Copyright 2021, 2023 Gabriel Bjørnager Jensen.
-
- This file is part of Benoit.
-
- Benoit is free software: you can redistribute it
- and/or modify it under the terms of the GNU
- Affero General Public License as published by
- the Free Software Foundation, either version 3
- of the License, or (at your option) any later
- version.
-
- Benoit is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without
- even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU
- Affero General Public License along with Benoit.
- If not, see <https://www.gnu.org/licenses/>.
-*/
-
-use crate::benoit::app::App;
-use crate::benoit::render::colour_data::ColourData;
-
-extern crate rayon;
-
-use rayon::prelude::*;
-use std::sync::Arc;
-
-impl App {
- pub fn colour(&self, buffer: &mut [u8], multibrot_exponent: f32, max_iter_count: u32, iter_count_buffer: &[u32], square_dist_buffer: &[f32]) {
- let data = Arc::new(ColourData::new(buffer, self.canvas_width, multibrot_exponent, max_iter_count, self.colour_range, iter_count_buffer, square_dist_buffer));
-
- let factoriser = self.factoriser;
- let palette_function = self.palette_function;
-
- (0x0..self.canvas_height).into_par_iter().for_each(|row| {
- App::colour_row(data.clone(), row as u32, factoriser, palette_function);
- });
- }
-}
diff --git a/source/benoit/benoit/app/colour_row.rs b/source/benoit/benoit/app/colour_row.rs
deleted file mode 100644
index a4baca7..0000000
--- a/source/benoit/benoit/app/colour_row.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- Copyright 2021, 2023 Gabriel Bjørnager Jensen.
-
- This file is part of Benoit.
-
- Benoit is free software: you can redistribute it
- and/or modify it under the terms of the GNU
- Affero General Public License as published by
- the Free Software Foundation, either version 3
- of the License, or (at your option) any later
- version.
-
- Benoit is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without
- even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU
- Affero General Public License along with Benoit.
- If not, see <https://www.gnu.org/licenses/>.
-*/
-
-use crate::benoit::app::App;
-use crate::benoit::render::{FactoriserFunction, PaletteFunction};
-use crate::benoit::render::colour_data::ColourData;
-
-use std::sync::Arc;
-
-impl App {
- pub fn colour_row(data: Arc<ColourData>, y: u32, factoriser: FactoriserFunction, palette_function: PaletteFunction) {
- let (iter_count_buffer, square_dist_buffer, image) = unsafe { data.slice(y) };
-
- for x in 0x0..data.canvas_width {
- let x = x as usize;
-
- let iter_count = unsafe { *iter_count_buffer.get_unchecked( x) };
- let distance = unsafe { *square_dist_buffer.get_unchecked(x) }.sqrt();
-
- let factor = if iter_count < data.max_iter_count {
- factoriser(iter_count, distance, data.colour_range, data.exponent)
- } else {
- f32::NAN
- };
-
- let (red, green, blue) = palette_function(factor);
-
- let red = (red * 255.0).round() as u8;
- let green = (green * 255.0).round() as u8;
- let blue = (blue * 255.0).round() as u8;
-
- unsafe {
- let x = x * 0x3;
-
- *image.get_unchecked_mut(x) = red;
- *image.get_unchecked_mut(x + 0x1) = green;
- *image.get_unchecked_mut(x + 0x2) = blue;
- }
- }
- }
-}
diff --git a/source/benoit/benoit/app/handle_keys.rs b/source/benoit/benoit/app/handle_keys.rs
index da88bfc..0dc5b5a 100644
--- a/source/benoit/benoit/app/handle_keys.rs
+++ b/source/benoit/benoit/app/handle_keys.rs
@@ -21,12 +21,11 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::PRECISION;
+use crate::benoit::{PRECISION};
use crate::benoit::app::App;
-use crate::benoit::factorisation::Factorisation;
use crate::benoit::fractal::Fractal;
use crate::benoit::palette::Palette;
-use crate::benoit::render::{FactoriserFunction, IteratorFunction, PaletteFunction, RowRenderer};
+use crate::benoit::render::{IteratorFunction, RowRenderer};
use crate::benoit::rendering::Rendering;
extern crate rug;
@@ -35,17 +34,19 @@ extern crate sdl2;
use rug::Float;
use sdl2::keyboard::Scancode;
+pub const MIN_COLOUR_RANGE: f32 = 2.0;
+
impl App {
+ #[must_use]
pub fn handle_keys(&mut self, scan_code: Scancode) -> bool {
match scan_code {
Scancode::C => self.do_render = true,
Scancode::Escape => return true,
Scancode::F1 => self.do_textual_feedback = !self.do_textual_feedback,
Scancode::LAlt => (self.fractal, self.multibrot_exponent, self.iterator_function) = cycle_fractal(self.fractal, -0x1),
- Scancode::LCtrl => (self.factorisation, self.factoriser) = toggle_smooth(self.factorisation),
- Scancode::Left => (self.palette, self.palette_function) = cycle_palette(self.palette, -0x1),
+ Scancode::Left => self.palette = cycle_palette(self.palette, -0x1),
Scancode::RAlt => (self.fractal, self.multibrot_exponent, self.iterator_function) = cycle_fractal(self.fractal, 0x1),
- Scancode::Right => (self.palette, self.palette_function) = cycle_palette(self.palette, 0x1),
+ Scancode::Right => self.palette = cycle_palette(self.palette, 0x1),
Scancode::Tab => (self.rendering, self.row_renderer) = toggle_julia(self.rendering),
Scancode::Z => eprintln!("c = {}{:+}i, {}x @ {} iter. (range.: {:.3})", &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count, self.colour_range),
_ => {},
@@ -63,7 +64,7 @@ impl App {
self.colour_range = match scan_code {
Scancode::Up => self.colour_range * COLOUR_RANGE_FACTOR,
- Scancode::Down => (self.colour_range / COLOUR_RANGE_FACTOR).max(2.0),
+ Scancode::Down => (self.colour_range / COLOUR_RANGE_FACTOR).max(MIN_COLOUR_RANGE),
_ => self.colour_range,
};
@@ -125,24 +126,10 @@ fn toggle_julia(rendering: Rendering) -> (Rendering, RowRenderer) {
return (rendering, row_renderer);
}
-fn toggle_smooth(factorisation: Factorisation) -> (Factorisation, FactoriserFunction) {
- let factorisation = factorisation.cycle();
-
- let factoriser = factorisation.get_factoriser();
-
- match factorisation {
- Factorisation::Smooth => eprintln!("disabled smoothing"),
- Factorisation::Stepped => eprintln!("enabled smoothing"),
- };
-
- return (factorisation, factoriser);
-}
-
-fn cycle_palette(palette: Palette, direction: i8) -> (Palette, PaletteFunction) {
- let palette = palette + direction;
- let palette_function = palette.get_function();
+fn cycle_palette(palette: Palette, direction: i8) -> Palette {
+ let palette = palette.cycle(direction);
eprintln!("using palette \"{}\"", palette.get_name());
- return (palette, palette_function);
+ return palette;
}
diff --git a/source/benoit/benoit/app/image_filename.rs b/source/benoit/benoit/app/image_filename.rs
index 9ab111a..7abc71a 100644
--- a/source/benoit/benoit/app/image_filename.rs
+++ b/source/benoit/benoit/app/image_filename.rs
@@ -25,6 +25,7 @@ use crate::benoit::ImageFormat;
use crate::benoit::app::App;
impl App {
+ #[must_use]
pub fn image_filename(name: &str, image_format: ImageFormat) -> String {
let file_extension = match image_format {
ImageFormat::Png => ".png",
diff --git a/source/benoit/benoit/app/initialise.rs b/source/benoit/benoit/app/initialise.rs
index 6a4d385..75535e8 100644
--- a/source/benoit/benoit/app/initialise.rs
+++ b/source/benoit/benoit/app/initialise.rs
@@ -32,6 +32,7 @@ use std::env::args;
use std::thread::available_parallelism;
impl App {
+ #[must_use]
pub fn initialise() -> App {
let mut arguments = args();
@@ -77,9 +78,8 @@ impl App {
max_iter_count: configuration.max_iter_count,
- factorisation: configuration.factorisation,
- palette: configuration.palette,
- colour_range: configuration.colour_range,
+ palette: configuration.palette,
+ colour_range: configuration.colour_range,
dump_path: configuration.dump_path,
image_format: configuration.image_format,
@@ -92,9 +92,6 @@ impl App {
row_renderer: configuration.rendering.get_row_renderer(),
iterator_function: configuration.fractal.get_iterator(),
-
- factoriser: configuration.factorisation.get_factoriser(),
- palette_function: configuration.palette.get_function(),
};
}
}
diff --git a/source/benoit/benoit/app/interactive.rs b/source/benoit/benoit/app/interactive.rs
index f03576d..e4ecd7c 100644
--- a/source/benoit/benoit/app/interactive.rs
+++ b/source/benoit/benoit/app/interactive.rs
@@ -23,6 +23,7 @@
use crate::benoit::FeedbackInfo;
use crate::benoit::app::App;
+use crate::benoit::render::{colour, render};
use crate::benoit::rendering::Rendering;
use crate::benoit::video::Video;
@@ -32,6 +33,7 @@ use rug::{Assign, Float};
use std::time::Instant;
impl App {
+ #[must_use]
pub fn interactive(&mut self) -> i32 {
assert_eq!(self.interactive, true);
let mut video = self.video.take().unwrap();
@@ -61,7 +63,7 @@ impl App {
let time_start = Instant::now();
- self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count);
+ render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], self.canvas_width, self.canvas_height, &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count, self.row_renderer, self.iterator_function);
let render_time = time_start.elapsed();
eprintln!(" {:.3}ms", render_time.as_micros() as f32 / 1000.0);
@@ -76,7 +78,7 @@ impl App {
self.do_render = false;
}
- self.colour(&mut image[..], prev_multibrot_exponent, prev_max_iter_count.min(self.max_iter_count), &mut iter_count_buffer[..], &mut square_dist_buffer[..]);
+ colour(&mut image[..], self.canvas_width, self.canvas_height, prev_multibrot_exponent, prev_max_iter_count.min(self.max_iter_count), self.colour_range, self.palette, &iter_count_buffer[..], &square_dist_buffer[..]);
video.draw(&image[..], self.canvas_width, self.canvas_height, self.scale);
self.draw_feedback(&mut video, &prev_centre_real, &prev_centre_imag, &prev_zoom);
@@ -144,9 +146,9 @@ impl App {
println!("- \u{1B}[1mRight\u{1B}[0m Cycle to next palette");
println!();
println!("- \u{1B}[1mF1\u{1B}[0m Toggle textual feedback");
- println!("- \u{1B}[1mC\u{1B}[0m Print centre value (c)");
+ println!("- \u{1B}[1mZ\u{1B}[0m Print centre value (c)");
println!();
- println!("- \u{1B}[1mSpace\u{1B}[0m Render frame");
+ println!("- \u{1B}[1mC\u{1B}[0m Render frame");
println!();
}
} \ No newline at end of file
diff --git a/source/benoit/benoit/app/poll_events.rs b/source/benoit/benoit/app/poll_events.rs
index f5e38b6..2ba81fa 100644
--- a/source/benoit/benoit/app/poll_events.rs
+++ b/source/benoit/benoit/app/poll_events.rs
@@ -29,6 +29,7 @@ use sdl2::EventPump;
use sdl2::event::Event;
impl App {
+ #[must_use]
pub fn poll_events(&mut self, event_pump: &mut EventPump) -> bool {
for event in event_pump.poll_iter() {
let quit = match event {
diff --git a/source/benoit/benoit/app/run.rs b/source/benoit/benoit/app/run.rs
index 9211716..b1ef578 100644
--- a/source/benoit/benoit/app/run.rs
+++ b/source/benoit/benoit/app/run.rs
@@ -27,6 +27,7 @@ use crate::benoit::app::App;
extern crate sdl2;
impl App {
+ #[must_use]
pub fn run(&mut self) -> i32 {
println!();
println!("\u{1B}[1mBENO\u{CE}T\u{1B}[0m {:X}.{:X}.{:X}", VERSION.major, VERSION.minor, VERSION.patch);
diff --git a/source/benoit/benoit/app/still.rs b/source/benoit/benoit/app/still.rs
index bd5ea9e..ff483b6 100644
--- a/source/benoit/benoit/app/still.rs
+++ b/source/benoit/benoit/app/still.rs
@@ -22,8 +22,7 @@
*/
use crate::benoit::app::App;
-
-extern crate rug;
+use crate::benoit::render::{colour, render};
use std::time::Instant;
@@ -36,12 +35,12 @@ impl App {
eprint!("rendering at {}{:+}i ({}x)...", self.centre_real.to_f32(), self.centre_imag.to_f32(), self.zoom.to_f32());
let time_start = Instant::now();
- self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count);
+ render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], self.canvas_width, self.canvas_height, &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count, self.row_renderer, self.iterator_function);
let render_time = time_start.elapsed();
eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
- self.colour(&mut image[..], self.multibrot_exponent, self.max_iter_count, &iter_count_buffer[..], &square_dist_buffer[..]);
+ colour(&mut image[..], self.canvas_width, self.canvas_height, self.multibrot_exponent, self.max_iter_count, self.colour_range, self.palette, &iter_count_buffer[..], &square_dist_buffer[..]);
let colour_time = time_start.elapsed() - render_time;
eprint!(" {:.3}ms...", colour_time.as_micros() as f32 / 1000.0);
diff --git a/source/benoit/benoit/configuration.rs b/source/benoit/benoit/configuration.rs
index 828ccae..ea6a841 100644
--- a/source/benoit/benoit/configuration.rs
+++ b/source/benoit/benoit/configuration.rs
@@ -21,18 +21,18 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::ImageFormat;
-use crate::benoit::factorisation::Factorisation;
+use crate::benoit::{ImageFormat, PRECISION};
use crate::benoit::fractal::Fractal;
use crate::benoit::palette::Palette;
use crate::benoit::rendering::Rendering;
extern crate rug;
+extern crate toml;
use rug::Float;
-
-pub mod default;
-pub mod load;
+use std::fs::read;
+use std::str::FromStr;
+use toml::{Table, Value};
pub struct Configuration {
pub thread_count: u32,
@@ -51,12 +51,180 @@ pub struct Configuration {
pub max_iter_count: u32,
- pub factorisation: Factorisation,
- pub palette: Palette,
- pub colour_range: f32,
+ pub palette: Palette,
+ pub colour_range: f32,
pub dump_path: String,
pub image_format: ImageFormat,
pub interactive: bool,
}
+
+impl Configuration {
+ #[must_use]
+ pub fn default() -> Configuration {
+ return Configuration {
+ thread_count: 0x0,
+
+ fractal: Fractal::Mandelbrot,
+ rendering: Rendering::Normal,
+
+ canvas_width: 0x100,
+ canvas_height: 0xC0,
+ scale: 0x2,
+ frame_count: 0x10,
+
+ centre_real: Float::with_val(PRECISION, 0.0),
+ centre_imag: Float::with_val(PRECISION, 0.0),
+ zoom: Float::with_val(PRECISION, 1.0),
+
+ max_iter_count: 0x100,
+
+ palette: Palette::Fire,
+ colour_range: 64.0,
+
+ dump_path: "./render".to_string(),
+ image_format: ImageFormat::Png,
+
+ interactive: true,
+ };
+ }
+
+ #[must_use]
+ pub fn load(path: &str) -> Configuration {
+ eprintln!("loading configuration at \"{path}\"");
+
+ let mut configuration = Configuration::default();
+
+ configuration.interactive = false;
+
+ let configuration_text = match read(path) {
+ Ok(content) => String::from_utf8_lossy(&content).to_string(),
+ Err(..) => panic!("unable to read configuration file"),
+ };
+
+ let configuration_table = Table::from_str(configuration_text.as_str()).expect("unable to parse configuration");
+
+ get_integer(&mut configuration.thread_count, &configuration_table, "thread_count");
+
+ if let Some(name) = get_string(&configuration_table, "fractal") {
+ configuration.fractal = match name.as_str() {
+ "burningship" => Fractal::BurningShip,
+ "mandelbrot" => Fractal::Mandelbrot,
+ "multibrot3" => Fractal::Multibrot3,
+ "tricorn" => Fractal::Tricorn,
+ name => panic!("invalid fractal kind \"{name}\""),
+ }
+ }
+
+ if let Some(name) = get_string(&configuration_table, "rendering") {
+ configuration.rendering = match name.as_str() {
+ "julia" => Rendering::Julia,
+ "normal" => Rendering::Normal,
+ name => panic!("invalid rendering method \"{name}\""),
+ };
+ }
+
+ get_integer(&mut configuration.canvas_width, &configuration_table, "canvas_width");
+ get_integer(&mut configuration.canvas_height, &configuration_table, "canvas_height");
+ get_integer(&mut configuration.frame_count, &configuration_table, "frame_count");
+
+ get_bigfloat(&mut configuration.centre_real, &configuration_table, "real");
+ get_bigfloat(&mut configuration.centre_imag, &configuration_table, "imaginary");
+ get_bigfloat(&mut configuration.zoom, &configuration_table, "zoom");
+
+ get_integer(&mut configuration.max_iter_count, &configuration_table, "maximum_iteration_count");
+
+ if let Some(name) = get_string(&configuration_table, "palette") {
+ configuration.palette = match name.as_str() {
+ "ancient" => Palette::Ancient,
+ "fire" => Palette::Fire,
+ "greyscale" => Palette::Greyscale,
+ "hsv" => Palette::Hsv,
+ "lch" => Palette::Lch,
+ "sapphire" => Palette::Sapphire,
+ name => panic!("invalid palette \"{name}\""),
+ };
+ }
+
+ get_float(&mut configuration.colour_range, &configuration_table, "colour_range");
+
+ if let Some(path) = get_string(&configuration_table, "dump_path") {
+ configuration.dump_path = path.clone();
+ }
+
+ if let Some(name) = get_string(&configuration_table, "image_format") {
+ configuration.image_format = match name.as_str() {
+ "png" => ImageFormat::Png,
+ "webp" => ImageFormat::Webp,
+ name => panic!("invalid image format \"{name}\""),
+ };
+ }
+
+ match check_configuration(&configuration) {
+ Err(message) => panic!("invalid configuration: {message}"),
+ _ => {},
+ }
+
+ return configuration;
+ }
+}
+
+fn check_configuration(configuration: &Configuration) -> Result<(), &str> {
+ // We allow thread counts of zero as those signal
+ // automatic thread count detection.
+ if configuration.canvas_width == 0x0 {
+ return Err("only non-zero values for canvas_width are allowed");
+ } else if configuration.scale == 0x0 {
+ return Err("only non-zero values for scale are allowed");
+ } else if configuration.frame_count == 0x0 {
+ return Err("only non-zero values for frame_count are allowed");
+ } else if configuration.max_iter_count == 0x0 {
+ return Err("only non-zero values for maximum_iteration_count are allowed");
+ }
+
+ return Ok(());
+}
+
+fn get_value<'a>(table: &'a Table, name: &str) -> Option<&'a Value> {
+ if !table.contains_key(name) { return None };
+
+ return Some(&table[name]);
+}
+
+fn get_integer(buffer: &mut u32, table: &Table, name: &str) {
+ match get_value(table, name) {
+ Some(Value::Integer(value)) => *buffer = (*value) as u32,
+ Some(_) => panic!("\"{name}\" should be an integer"),
+ _ => {},
+ };
+}
+
+fn get_float(buffer: &mut f32, table: &Table, name: &str) {
+ match get_value(table, name) {
+ Some(Value::Float(value)) => *buffer = (*value) as f32,
+ Some(_) => panic!("\"{name}\" should be a float"),
+ _ => {},
+ };
+}
+
+fn get_bigfloat(buffer: &mut Float, table: &Table, name: &str) {
+ return match get_value(table, name) {
+ Some(Value::String(string)) => {
+ *buffer = match Float::parse(string) {
+ Ok(value) => Float::with_val(PRECISION, value),
+ _ => panic!("invalid format of \"{name}\""),
+ }
+ },
+ Some(_) => panic!("\"{name}“ should be a quoted float"),
+ _ => {},
+ };
+}
+
+fn get_string(table: &Table, name: &str) -> Option<String> {
+ return match get_value(table, name) {
+ Some(Value::String(value)) => Some(value.clone()),
+ Some(_) => panic!("\"{name}\" should be a string"),
+ _ => None,
+ };
+}
diff --git a/source/benoit/benoit/configuration/default.rs b/source/benoit/benoit/configuration/default.rs
deleted file mode 100644
index f7f30d7..0000000
--- a/source/benoit/benoit/configuration/default.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- Copyright 2021, 2023 Gabriel Bjørnager Jensen.
-
- This file is part of Benoit.
-
- Benoit is free software: you can redistribute it
- and/or modify it under the terms of the GNU
- Affero General Public License as published by
- the Free Software Foundation, either version 3
- of the License, or (at your option) any later
- version.
-
- Benoit is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without
- even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU
- Affero General Public License along with Benoit.
- If not, see <https://www.gnu.org/licenses/>.
-*/
-
-use crate::benoit::{ImageFormat, PRECISION};
-use crate::benoit::factorisation::Factorisation;
-use crate::benoit::fractal::Fractal;
-use crate::benoit::palette::Palette;
-use crate::benoit::rendering::Rendering;
-use crate::benoit::configuration::Configuration;
-
-extern crate rug;
-
-use rug::Float;
-
-impl Configuration {
- pub fn default() -> Configuration {
- return Configuration {
- thread_count: 0x0,
-
- fractal: Fractal::Mandelbrot,
- rendering: Rendering::Normal,
-
- canvas_width: 0x100,
- canvas_height: 0xC0,
- scale: 0x2,
- frame_count: 0x10,
-
- centre_real: Float::with_val(PRECISION, 0.0),
- centre_imag: Float::with_val(PRECISION, 0.0),
- zoom: Float::with_val(PRECISION, 1.0),
-
- max_iter_count: 0x100,
-
- factorisation: Factorisation::Smooth,
- palette: Palette::Fire,
- colour_range: 64.0,
-
- dump_path: "./render".to_string(),
- image_format: ImageFormat::Png,
-
- interactive: true,
- };
- }
-}
diff --git a/source/benoit/benoit/configuration/load.rs b/source/benoit/benoit/configuration/load.rs
deleted file mode 100644
index 0b4b20f..0000000
--- a/source/benoit/benoit/configuration/load.rs
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- Copyright 2021, 2023 Gabriel Bjørnager Jensen.
-
- This file is part of Benoit.
-
- Benoit is free software: you can redistribute it
- and/or modify it under the terms of the GNU
- Affero General Public License as published by
- the Free Software Foundation, either version 3
- of the License, or (at your option) any later
- version.
-
- Benoit is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without
- even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU
- Affero General Public License along with Benoit.
- If not, see <https://www.gnu.org/licenses/>.
-*/
-
-use crate::benoit::{ImageFormat, PRECISION};
-use crate::benoit::factorisation::Factorisation;
-use crate::benoit::fractal::Fractal;
-use crate::benoit::palette::Palette;
-use crate::benoit::rendering::Rendering;
-use crate::benoit::configuration::Configuration;
-
-extern crate rug;
-extern crate toml;
-
-use rug::Float;
-use std::fs::read;
-use std::str::FromStr;
-use toml::{Table, Value};
-
-impl Configuration {
- pub fn load(path: &str) -> Configuration {
- eprintln!("loading configuration at \"{path}\"");
-
- let mut configuration = Configuration::default();
-
- configuration.interactive = false;
-
- let configuration_text = match read(path) {
- Ok(content) => String::from_utf8_lossy(&content).to_string(),
- Err(..) => panic!("unable to read configuration file"),
- };
-
- let configuration_table = Table::from_str(configuration_text.as_str()).expect("unable to parse configuration");
-
- get_integer(&mut configuration.thread_count, &configuration_table, "thread_count");
-
- if let Some(name) = get_string(&configuration_table, "fractal") {
- configuration.fractal = match name.as_str() {
- "burningship" => Fractal::BurningShip,
- "mandelbrot" => Fractal::Mandelbrot,
- "multibrot3" => Fractal::Multibrot3,
- "tricorn" => Fractal::Tricorn,
- name => panic!("invalid fractal kind \"{name}\""),
- }
- }
-
- if let Some(name) = get_string(&configuration_table, "rendering") {
- configuration.rendering = match name.as_str() {
- "julia" => Rendering::Julia,
- "normal" => Rendering::Normal,
- name => panic!("invalid rendering method \"{name}\""),
- };
- }
-
- get_integer(&mut configuration.canvas_width, &configuration_table, "canvas_width");
- get_integer(&mut configuration.canvas_height, &configuration_table, "canvas_height");
- get_integer(&mut configuration.frame_count, &configuration_table, "frame_count");
-
- get_bigfloat(&mut configuration.centre_real, &configuration_table, "real");
- get_bigfloat(&mut configuration.centre_imag, &configuration_table, "imaginary");
- get_bigfloat(&mut configuration.zoom, &configuration_table, "zoom");
-
- get_integer(&mut configuration.max_iter_count, &configuration_table, "maximum_iteration_count");
-
- if let Some(name) = get_string(&configuration_table, "factorisation") {
- configuration.factorisation = match name.as_str() {
- "smooth" => Factorisation::Smooth,
- "stepped" => Factorisation::Stepped,
- name => panic!("invalid factorisation method \"{name}\""),
- };
- }
-
- if let Some(name) = get_string(&configuration_table, "palette") {
- configuration.palette = match name.as_str() {
- "ancient" => Palette::Ancient,
- "fire" => Palette::Fire,
- "greyscale" => Palette::Greyscale,
- "hsv" => Palette::Hsv,
- "lch" => Palette::Lch,
- "sapphire" => Palette::Sapphire,
- name => panic!("invalid palette \"{name}\""),
- };
- }
-
- get_float(&mut configuration.colour_range, &configuration_table, "colour_range");
-
- if let Some(path) = get_string(&configuration_table, "dump_path") {
- configuration.dump_path = path.clone();
- }
-
- if let Some(name) = get_string(&configuration_table, "image_format") {
- configuration.image_format = match name.as_str() {
- "png" => ImageFormat::Png,
- "webp" => ImageFormat::Webp,
- name => panic!("invalid image format \"{name}\""),
- };
- }
-
- match check_configuration(&configuration) {
- Err(message) => panic!("invalid configuration: {message}"),
- _ => {},
- }
-
- return configuration;
- }
-}
-
-fn check_configuration(configuration: &Configuration) -> Result<(), &str> {
- // We allow thread counts of zero as those signal
- // automatic thread count detection.
- if configuration.canvas_width == 0x0 {
- return Err("only non-zero values for canvas_width are allowed");
- } else if configuration.scale == 0x0 {
- return Err("only non-zero values for scale are allowed");
- } else if configuration.frame_count == 0x0 {
- return Err("only non-zero values for frame_count are allowed");
- } else if configuration.max_iter_count == 0x0 {
- return Err("only non-zero values for maximum_iteration_count are allowed");
- }
-
- return Ok(());
-}
-
-fn get_value<'a>(table: &'a Table, name: &str) -> Option<&'a Value> {
- if !table.contains_key(name) { return None };
-
- return Some(&table[name]);
-}
-
-fn get_integer(buffer: &mut u32, table: &Table, name: &str) {
- match get_value(table, name) {
- Some(Value::Integer(value)) => *buffer = (*value) as u32,
- Some(_) => panic!("\"{name}\" should be an integer"),
- _ => {},
- };
-}
-
-fn get_float(buffer: &mut f32, table: &Table, name: &str) {
- match get_value(table, name) {
- Some(Value::Float(value)) => *buffer = (*value) as f32,
- Some(_) => panic!("\"{name}\" should be a float"),
- _ => {},
- };
-}
-
-fn get_bigfloat(buffer: &mut Float, table: &Table, name: &str) {
- return match get_value(table, name) {
- Some(Value::String(string)) => {
- *buffer = match Float::parse(string) {
- Ok(value) => Float::with_val(PRECISION, value),
- _ => panic!("invalid format of \"{name}\""),
- }
- },
- Some(_) => panic!("\"{name}“ should be a quoted float"),
- _ => {},
- };
-}
-
-fn get_string(table: &Table, name: &str) -> Option<String> {
- return match get_value(table, name) {
- Some(Value::String(value)) => Some(value.clone()),
- Some(_) => panic!("\"{name}\" should be a string"),
- _ => None,
- };
-}
diff --git a/source/benoit/benoit/factorisation.rs b/source/benoit/benoit/factorisation.rs
deleted file mode 100644
index fc0f7ba..0000000
--- a/source/benoit/benoit/factorisation.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- Copyright 2021, 2023 Gabriel Bjørnager Jensen.
-
- This file is part of Benoit.
-
- Benoit is free software: you can redistribute it
- and/or modify it under the terms of the GNU
- Affero General Public License as published by
- the Free Software Foundation, either version 3
- of the License, or (at your option) any later
- version.
-
- Benoit is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without
- even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU
- Affero General Public License along with Benoit.
- If not, see <https://www.gnu.org/licenses/>.
-*/
-
-use crate::benoit::render::FactoriserFunction;
-use crate::benoit::render::factorise;
-
-use std::mem::transmute;
-
-#[derive(Clone, Copy)]
-pub enum Factorisation {
- Smooth,
- Stepped,
-}
-
-impl Factorisation {
- pub fn get_factoriser(self) -> FactoriserFunction {
- return match self {
- Factorisation::Smooth => factorise::smooth,
- Factorisation::Stepped => factorise::stepped,
- };
- }
-
- pub fn cycle(self) -> Self {
- let raw = !(self as u8 != 0x0);
-
- let new: Self = unsafe { transmute(raw) };
-
- return new;
- }
-}
diff --git a/source/benoit/benoit/palette.rs b/source/benoit/benoit/palette.rs
index 628c5f4..5477242 100644
--- a/source/benoit/benoit/palette.rs
+++ b/source/benoit/benoit/palette.rs
@@ -21,13 +21,16 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::render::PaletteFunction;
-use crate::benoit::render::paint;
+extern crate ctor;
+extern crate enum_iterator;
+use ctor::ctor;
+use enum_iterator::{all, Sequence};
use std::mem::transmute;
-use std::ops::Add;
-#[derive(Clone, Copy)]
+mod paint;
+
+#[derive(Clone, Copy, Sequence)]
pub enum Palette {
Ancient,
Fire,
@@ -37,9 +40,45 @@ pub enum Palette {
Sapphire,
}
+// We would like to precalculate the palettes at
+// compile-time, but Rust does not support
+// floating-point arithmetic there.
+
+pub const PALETTE_DATA_LENGTH: usize = 0x1000;
+pub type PaletteData = [(f32, f32, f32); PALETTE_DATA_LENGTH];
+
+const fn default_palette_data() -> PaletteData {
+ return [(0.0, 0.0, 0.0); PALETTE_DATA_LENGTH];
+}
+
+static mut DATA_ANCIENT: PaletteData = default_palette_data();
+static mut DATA_FIRE: PaletteData = default_palette_data();
+static mut DATA_GREYSCALE: PaletteData = default_palette_data();
+static mut DATA_HSV: PaletteData = default_palette_data();
+static mut DATA_LCH: PaletteData = default_palette_data();
+static mut DATA_SAPPHIRE: PaletteData = default_palette_data();
+
+#[ctor]
+fn calculate_palettes() {
+ for palette in all::<Palette>() {
+ let data = palette.get_data_mut();
+ let function = palette.get_function();
+
+ for index in 0x0..PALETTE_DATA_LENGTH {
+ let factor = index as f32 / PALETTE_DATA_LENGTH as f32;
+
+ let (red, green, blue) = function(factor);
+
+ data[index as usize] = (red, green, blue);
+ }
+ }
+}
+
impl Palette {
- const MAX: u8 = Palette::Sapphire as u8;
+ const MIN: Self = Palette::Ancient;
+ const MAX: Self = Palette::Sapphire;
+ #[must_use]
pub fn get_name(self) -> &'static str {
return match self {
Palette::Ancient => "ancient",
@@ -51,7 +90,28 @@ impl Palette {
};
}
- pub fn get_function(self) -> PaletteFunction {
+ #[must_use]
+ pub fn get_data(self) -> &'static PaletteData {
+ return &*self.get_data_mut();
+ }
+
+ #[must_use]
+ pub fn cycle(&self, direction: i8) -> Self {
+ let raw = *self as i8 + direction;
+
+ let new: Palette = if raw < 0x0 {
+ Palette::MAX
+ } else if raw > Palette::MAX as i8 {
+ Palette::MIN
+ } else {
+ unsafe { transmute(raw) }
+ };
+
+ return new;
+ }
+
+ #[must_use]
+ fn get_function(self) -> fn(f32) -> (f32, f32, f32) {
return match self {
Palette::Ancient => paint::ancient,
Palette::Fire => paint::fire,
@@ -61,25 +121,16 @@ impl Palette {
Palette::Sapphire => paint::sapphire,
};
}
-}
-
-impl Add<i8> for Palette {
- type Output = Palette;
- fn add(self, direction: i8) -> Self {
- assert!(direction != 0x0);
-
- let raw = self as i8 + direction;
- let raw: u8 = if raw < 0x0 {
- Palette::MAX
- } else if raw > Palette::MAX as i8 {
- 0x0
- } else {
- raw as u8
- };
-
- let new: Self = unsafe { transmute(raw) };
-
- return new;
+ #[must_use]
+ fn get_data_mut(self) -> &'static mut PaletteData {
+ return unsafe { match self {
+ Palette::Ancient => &mut DATA_ANCIENT,
+ Palette::Fire => &mut DATA_FIRE,
+ Palette::Greyscale => &mut DATA_GREYSCALE,
+ Palette::Hsv => &mut DATA_HSV,
+ Palette::Lch => &mut DATA_LCH,
+ Palette::Sapphire => &mut DATA_SAPPHIRE,
+ } };
}
}
diff --git a/source/benoit/benoit/render/paint.rs b/source/benoit/benoit/palette/paint.rs
index ff3bd47..ff3bd47 100644
--- a/source/benoit/benoit/render/paint.rs
+++ b/source/benoit/benoit/palette/paint.rs
diff --git a/source/benoit/benoit/render/paint/ancient.rs b/source/benoit/benoit/palette/paint/ancient.rs
index a2c01eb..cd11e1a 100644
--- a/source/benoit/benoit/render/paint/ancient.rs
+++ b/source/benoit/benoit/palette/paint/ancient.rs
@@ -27,15 +27,9 @@
pub fn ancient(factor: f32) -> (f32, f32, f32) {
let factor = factor % 1.0;
- let (red, green, blue) = if !factor.is_nan() {
- let red = 9.0 * (1.0 - factor) * factor * factor * factor;
- let green = 15.0 * (1.0 - factor) * (1.0 - factor) * factor * factor;
- let blue = 8.5 * (1.0 - factor) * (1.0 - factor) * (1.0 - factor) * factor;
-
- (red, green, blue)
- } else {
- (0.0, 0.0, 0.0)
- };
+ let red = 9.0 * (1.0 - factor) * factor * factor * factor;
+ let green = 15.0 * (1.0 - factor) * (1.0 - factor) * factor * factor;
+ let blue = 8.5 * (1.0 - factor) * (1.0 - factor) * (1.0 - factor) * factor;
return (red, green, blue);
}
diff --git a/source/benoit/benoit/render/paint/fire.rs b/source/benoit/benoit/palette/paint/fire.rs
index 23ab2b0..9895210 100644
--- a/source/benoit/benoit/render/paint/fire.rs
+++ b/source/benoit/benoit/palette/paint/fire.rs
@@ -27,24 +27,20 @@
pub fn fire(factor: f32) -> (f32, f32, f32) {
let factor = factor % 1.0;
- let (red, green, blue) = if !factor.is_nan() {
- if factor <= 1.0 / 4.0 {
- (factor * 4.0, 0.0, 0.0)
- } else if factor <= 1.0 / 2.0 {
- let factor = factor - (1.0 / 4.0);
-
- (1.0, factor * 4.0, 0.0)
- } else if factor <= 3.0 / 4.0 {
- let factor = factor - (1.0 / 2.0);
-
- (1.0, 1.0, factor * 4.0)
- } else {
- let factor = 1.0 - factor;
-
- (factor * 4.0, factor * 4.0, factor * 4.0)
- }
+ let (red, green, blue) = if factor <= 1.0 / 4.0 {
+ (factor * 4.0, 0.0, 0.0)
+ } else if factor <= 1.0 / 2.0 {
+ let factor = factor - (1.0 / 4.0);
+
+ (1.0, factor * 4.0, 0.0)
+ } else if factor <= 3.0 / 4.0 {
+ let factor = factor - (1.0 / 2.0);
+
+ (1.0, 1.0, factor * 4.0)
} else {
- (0.0, 0.0, 0.0)
+ let factor = 1.0 - factor;
+
+ (factor * 4.0, factor * 4.0, factor * 4.0)
};
return (red, green, blue);
diff --git a/source/benoit/benoit/render/paint/greyscale.rs b/source/benoit/benoit/palette/paint/greyscale.rs
index 977edf1..046f6b8 100644
--- a/source/benoit/benoit/render/paint/greyscale.rs
+++ b/source/benoit/benoit/palette/paint/greyscale.rs
@@ -33,11 +33,5 @@ pub fn greyscale(factor: f32) -> (f32, f32, f32) {
factor
}) * 2.0;
- let (red, green, blue) = if !factor.is_nan() {
- (factor, factor, factor)
- } else {
- (0.0, 0.0, 0.0)
- };
-
- return (red, green, blue);
+ return (factor, factor, factor);
}
diff --git a/source/benoit/benoit/render/paint/hsv.rs b/source/benoit/benoit/palette/paint/hsv.rs
index e6b414b..7f04261 100644
--- a/source/benoit/benoit/render/paint/hsv.rs
+++ b/source/benoit/benoit/palette/paint/hsv.rs
@@ -24,13 +24,7 @@
use std::hint::unreachable_unchecked;
pub fn hsv(factor: f32) -> (f32, f32, f32) {
- let (red, green, blue) = if !factor.is_nan() {
- hsv_to_rgb(factor, 7.0 / 8.0, 7.0 / 8.0)
- } else {
- (0.0, 0.0, 0.0)
- };
-
- return (red, green, blue);
+ return hsv_to_rgb(factor, 7.0 / 8.0, 7.0 / 8.0);
}
fn hsv_to_rgb(hue: f32, saturation: f32, value: f32) -> (f32, f32, f32) {
diff --git a/source/benoit/benoit/render/paint/lch.rs b/source/benoit/benoit/palette/paint/lch.rs
index 38dfa0f..51d2ac0 100644
--- a/source/benoit/benoit/render/paint/lch.rs
+++ b/source/benoit/benoit/palette/paint/lch.rs
@@ -24,23 +24,17 @@
use std::f32::consts::PI;
pub fn lch(factor: f32) -> (f32, f32, f32) {
- let (red, green, blue) = if !factor.is_nan() {
- // Convert turns to radians:
- // 1 turn = 2pi radians
+ // Convert turns to radians:
+ // 1 turn = 2pi radians
- let angle = factor * PI;
+ let angle = factor * PI * 2.0;
- let (l, a, b) = lch_to_lab( 200.0 / 3.0, 1053.0 / 20.0, angle);
- let (x, y, z) = lab_to_xyz( l, a, b);
- let (r, g, b) = xyz_to_rgb( x, y, z);
- let (r, g, b) = rgb_to_srgb(r, g, b);
+ let (l, a, b) = lch_to_lab( 200.0 / 3.0, 1053.0 / 20.0, angle);
+ let (x, y, z) = lab_to_xyz( l, a, b);
+ let (r, g, b) = xyz_to_rgb( x, y, z);
+ let (r, g, b) = rgb_to_srgb(r, g, b);
- (r, g, b)
- } else {
- (0.0, 0.0, 0.0)
- };
-
- return (red, green, blue);
+ return (r, g, b);
}
fn rgb_to_srgb(r: f32, g: f32, b: f32) -> (f32, f32, f32) {
diff --git a/source/benoit/benoit/render/paint/sapphire.rs b/source/benoit/benoit/palette/paint/sapphire.rs
index 4f3e29a..9ae4b0c 100644
--- a/source/benoit/benoit/render/paint/sapphire.rs
+++ b/source/benoit/benoit/palette/paint/sapphire.rs
@@ -30,11 +30,5 @@ pub fn sapphire(factor: f32) -> (f32, f32, f32) {
factor
}) * 2.0;
- let (red, green, blue) = if !factor.is_nan() {
- (factor * factor * factor, factor * factor, factor)
- } else {
- (0.0, 0.0, 0.0)
- };
-
- return (red, green, blue);
+ return (factor * factor * factor, factor * factor, factor);
}
diff --git a/source/benoit/benoit/render.rs b/source/benoit/benoit/render.rs
index ddea2f6..b6c0ac8 100644
--- a/source/benoit/benoit/render.rs
+++ b/source/benoit/benoit/render.rs
@@ -28,20 +28,16 @@ extern crate rug;
use rug::Float;
use std::sync::Arc;
+pub mod colour;
pub mod colour_data;
-pub mod factorise;
pub mod iterate;
-pub mod paint;
+pub mod render;
pub mod render_data;
pub mod render_row;
+pub use colour::*;
+pub use render::*;
+
pub type IteratorFunction = fn(&mut Float, &mut Float, &Float, &Float);
pub type RowRenderer = fn(Arc<RenderData>, u32, IteratorFunction);
-
-// We pass the multibrot exponent to the factoriser
-// as it is important with regard to smoothing, as
-// the distance grows according to this exponent.
-pub type FactoriserFunction = fn(u32, f32, f32, f32) -> f32;
-
-pub type PaletteFunction = fn(f32) -> (f32, f32, f32);
diff --git a/source/benoit/benoit/render/colour.rs b/source/benoit/benoit/render/colour.rs
new file mode 100644
index 0000000..9debac4
--- /dev/null
+++ b/source/benoit/benoit/render/colour.rs
@@ -0,0 +1,74 @@
+/*
+ Copyright 2021, 2023 Gabriel Bjørnager Jensen.
+
+ This file is part of Benoit.
+
+ Benoit is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Benoit is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without
+ even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Benoit.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::benoit::palette::{Palette, PALETTE_DATA_LENGTH};
+use crate::benoit::render::colour_data::ColourData;
+
+extern crate rayon;
+
+use rayon::prelude::*;
+use std::sync::Arc;
+
+pub fn colour(buffer: &mut [u8], canvas_width: u32, canvas_height: u32, multibrot_exponent: f32, max_iter_count: u32, colour_range: f32, palette: Palette, iter_count_buffer: &[u32], square_dist_buffer: &[f32]) {
+ let data = Arc::new(ColourData::new(buffer, canvas_width, canvas_height, multibrot_exponent, max_iter_count, colour_range, palette, iter_count_buffer, square_dist_buffer));
+
+ (0x0..canvas_height).into_par_iter().for_each(|row| {
+ colour_row(data.clone(), row as u32);
+ });
+}
+
+fn colour_row(data: Arc<ColourData>, y: u32) {
+ let (iter_count_buffer, square_dist_buffer) = data.input_buffers(y);
+
+ let image = data.output_buffers(y);
+
+ let (canvas_width, exponent, max_iter_count, colour_range, palette_data) = data.consts();
+
+ for x in 0x0..canvas_width {
+ let x = x as usize;
+
+ let iter_count = unsafe { *iter_count_buffer.get_unchecked( x) };
+ let distance = unsafe { *square_dist_buffer.get_unchecked(x) }.sqrt();
+
+ let (red, green, blue) = if iter_count < max_iter_count {
+ let factor = (iter_count as f32 + 1.0 - distance.log(exponent).log(exponent)) / colour_range;
+
+ let index = (factor * PALETTE_DATA_LENGTH as f32).round() as usize % PALETTE_DATA_LENGTH;
+ unsafe { *palette_data.get_unchecked(index) }
+ } else {
+ (0.0, 0.0, 0.0)
+ };
+
+ let red = (red * 255.0).round() as u8;
+ let green = (green * 255.0).round() as u8;
+ let blue = (blue * 255.0).round() as u8;
+
+ unsafe {
+ let x = x * 0x3;
+
+ *image.get_unchecked_mut(x) = red;
+ *image.get_unchecked_mut(x + 0x1) = green;
+ *image.get_unchecked_mut(x + 0x2) = blue;
+ }
+ }
+}
diff --git a/source/benoit/benoit/render/colour_data.rs b/source/benoit/benoit/render/colour_data.rs
index 1d8e278..33123db 100644
--- a/source/benoit/benoit/render/colour_data.rs
+++ b/source/benoit/benoit/render/colour_data.rs
@@ -21,14 +21,19 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::palette::{Palette, PaletteData};
+
use std::slice::{from_raw_parts, from_raw_parts_mut};
pub struct ColourData {
- pub canvas_width: u32,
+ canvas_width: u32,
+ canvas_height: u32,
+
+ exponent: f32,
+ max_iter_count: u32,
+ colour_range: f32,
- pub exponent: f32,
- pub max_iter_count: u32,
- pub colour_range: f32,
+ palette_data: &'static PaletteData,
iter_count_buffer: *const u32,
square_dist_buffer: *const f32,
@@ -37,14 +42,18 @@ pub struct ColourData {
}
impl ColourData {
- pub fn new(image: &mut [u8], canvas_width: u32, exponent: f32, max_iter_count: u32, colour_range: f32, iter_count_buffer: &[u32], square_dist_buffer: &[f32]) -> ColourData {
+ #[must_use]
+ pub fn new(image: &mut [u8], canvas_width: u32, canvas_height: u32, exponent: f32, max_iter_count: u32, colour_range: f32, palette: Palette, iter_count_buffer: &[u32], square_dist_buffer: &[f32]) -> ColourData {
return ColourData {
- canvas_width: canvas_width,
+ canvas_width: canvas_width,
+ canvas_height: canvas_height,
exponent: exponent,
max_iter_count: max_iter_count,
colour_range: colour_range,
+ palette_data: palette.get_data(),
+
iter_count_buffer: iter_count_buffer.as_ptr(),
square_dist_buffer: square_dist_buffer.as_ptr(),
@@ -52,17 +61,32 @@ impl ColourData {
};
}
- pub unsafe fn slice(&self, row: u32) -> (&[u32], &[f32], &mut [u8]) {
- let offset = row as isize * self.canvas_width as isize;
+ #[must_use]
+ pub fn input_buffers(&self, row: u32) -> (&[u32], &[f32]) {
+ assert!(row < self.canvas_height);
- let iter_count = from_raw_parts(self.iter_count_buffer.offset(offset), self.canvas_width as usize);
- let dist = from_raw_parts(self.square_dist_buffer.offset(offset), self.canvas_width as usize);
+ let offset = row as usize * self.canvas_width as usize;
- let offset = offset * 0x3;
+ let iter_count = unsafe { from_raw_parts(self.iter_count_buffer.add(offset), self.canvas_width as usize) };
+ let dist = unsafe { from_raw_parts(self.square_dist_buffer.add(offset), self.canvas_width as usize) };
+
+ return (iter_count, dist);
+ }
- let image = from_raw_parts_mut(self.image.offset(offset), self.canvas_width as usize * 0x3);
+ #[must_use]
+ pub fn output_buffers(&self, row: u32) -> &mut [u8] {
+ assert!(row < self.canvas_height);
+
+ let offset = row as usize * self.canvas_width as usize * 0x3;
+
+ let image = unsafe { from_raw_parts_mut(self.image.add(offset), self.canvas_width as usize * 0x3) };
+
+ return image;
+ }
- return (iter_count, dist, image);
+ #[must_use]
+ pub fn consts(&self) -> (u32, f32, u32, f32, &'static PaletteData) {
+ return (self.canvas_width, self.exponent, self.max_iter_count, self.colour_range, self.palette_data);
}
}
diff --git a/source/benoit/benoit/render/factorise.rs b/source/benoit/benoit/render/factorise.rs
deleted file mode 100644
index ab2f75e..0000000
--- a/source/benoit/benoit/render/factorise.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- Copyright 2021, 2023 Gabriel Bjørnager Jensen.
-
- This file is part of Benoit.
-
- Benoit is free software: you can redistribute it
- and/or modify it under the terms of the GNU
- Affero General Public License as published by
- the Free Software Foundation, either version 3
- of the License, or (at your option) any later
- version.
-
- Benoit is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without
- even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU
- Affero General Public License along with Benoit.
- If not, see <https://www.gnu.org/licenses/>.
-*/
-
-pub mod smooth;
-pub mod stepped;
-
-pub use smooth::*;
-pub use stepped::*;
diff --git a/source/benoit/benoit/render/factorise/smooth.rs b/source/benoit/benoit/render/factorise/smooth.rs
deleted file mode 100644
index b324af5..0000000
--- a/source/benoit/benoit/render/factorise/smooth.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- Copyright 2021, 2023 Gabriel Bjørnager Jensen.
-
- This file is part of Benoit.
-
- Benoit is free software: you can redistribute it
- and/or modify it under the terms of the GNU
- Affero General Public License as published by
- the Free Software Foundation, either version 3
- of the License, or (at your option) any later
- version.
-
- Benoit is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without
- even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU
- Affero General Public License along with Benoit.
- If not, see <https://www.gnu.org/licenses/>.
-*/
-
-// Classic colour palette, fixed from version ↋.
-
-pub fn smooth(iter_count: u32, distance: f32, colour_range: f32, exponent: f32) -> f32 {
- let factor = (iter_count as f32 + 1.0 - distance.log(exponent).log(exponent)) / colour_range;
-
- return factor;
-}
diff --git a/source/benoit/benoit/render/factorise/stepped.rs b/source/benoit/benoit/render/factorise/stepped.rs
deleted file mode 100644
index df9ac2d..0000000
--- a/source/benoit/benoit/render/factorise/stepped.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- Copyright 2021, 2023 Gabriel Bjørnager Jensen.
-
- This file is part of Benoit.
-
- Benoit is free software: you can redistribute it
- and/or modify it under the terms of the GNU
- Affero General Public License as published by
- the Free Software Foundation, either version 3
- of the License, or (at your option) any later
- version.
-
- Benoit is distributed in the hope that it will
- be useful, but WITHOUT ANY WARRANTY; without
- even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
-
- You should have received a copy of the GNU
- Affero General Public License along with Benoit.
- If not, see <https://www.gnu.org/licenses/>.
-*/
-
-// Classic colour palette, fixed (and smoothed)
-// from version ↋.
-
-pub fn stepped(iter_count: u32, _distance: f32, colour_range: f32, _exponent: f32) -> f32 {
- let factor = iter_count as f32 / colour_range;
-
- return factor;
-}
diff --git a/source/benoit/benoit/app/render.rs b/source/benoit/benoit/render/render.rs
index 6e4a382..947bca7 100644
--- a/source/benoit/benoit/app/render.rs
+++ b/source/benoit/benoit/render/render.rs
@@ -21,7 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::app::App;
+use crate::benoit::render::{IteratorFunction, RowRenderer};
use crate::benoit::render::render_data::RenderData;
extern crate rayon;
@@ -31,15 +31,10 @@ use rayon::prelude::*;
use rug::Float;
use std::sync::Arc;
-impl App {
- pub fn render(&self, iter_count_buffer: &mut [u32], square_dist_buffer: &mut [f32], centre_real: &Float, centre_imag: &Float, zoom: &Float, max_iter_count: u32) {
- let row_renderer = self.row_renderer;
- let iterator = self.iterator_function;
+pub fn render(iter_count_buffer: &mut [u32], square_dist_buffer: &mut [f32], canvas_width: u32, canvas_height: u32, centre_real: &Float, centre_imag: &Float, zoom: &Float, max_iter_count: u32, row_renderer: RowRenderer, iterator: IteratorFunction) {
+ let data = Arc::new(RenderData::new(iter_count_buffer, square_dist_buffer, canvas_width, canvas_height, centre_real.clone(), centre_imag.clone(), zoom.clone(), max_iter_count));
- let data = Arc::new(RenderData::new(iter_count_buffer, square_dist_buffer, self.canvas_width, self.canvas_height, centre_real.clone(), centre_imag.clone(), zoom.clone(), max_iter_count));
-
- (0x0..self.canvas_height).into_par_iter().for_each(|row| {
- row_renderer(data.clone(), row as u32, iterator);
- });
- }
+ (0x0..canvas_height).into_par_iter().for_each(|row| {
+ row_renderer(data.clone(), row as u32, iterator);
+ });
}
diff --git a/source/benoit/benoit/render/render_data.rs b/source/benoit/benoit/render/render_data.rs
index b0f0851..765c0c0 100644
--- a/source/benoit/benoit/render/render_data.rs
+++ b/source/benoit/benoit/render/render_data.rs
@@ -49,6 +49,7 @@ pub struct RenderData {
}
impl RenderData {
+ #[must_use]
pub fn new(iter_count_buffer: &mut [u32], square_dist_buffer: &mut [f32], canvas_width: u32, canvas_height: u32, centre_real: Float, centre_imag: Float, zoom: Float, max_iter_count: u32) -> RenderData {
let (width_ratio, height_ratio) = width_height_ratio(canvas_width, canvas_height);
@@ -73,15 +74,13 @@ impl RenderData {
};
}
+ #[must_use]
pub fn input(&self) -> (u32, &Float, &Float, &Float, u32) {
return (self.canvas_width, &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count);
}
- pub fn consts(&self) -> (f32, f32, f32, f32) {
- return (self.x_offset, self.y_offset, self.x_factor, self.y_factor);
- }
-
- pub fn output(&self, row: u32) -> (&mut [u32], &mut [f32]) {
+ #[must_use]
+ pub fn output_buffers(&self, row: u32) -> (&mut [u32], &mut [f32]) {
assert!(row < self.canvas_height);
let offset = row as isize * self.canvas_width as isize;
@@ -91,6 +90,11 @@ impl RenderData {
return (iter_count, square_dist);
}
+
+ #[must_use]
+ pub fn consts(&self) -> (f32, f32, f32, f32) {
+ return (self.x_offset, self.y_offset, self.x_factor, self.y_factor);
+ }
}
unsafe impl Send for RenderData {}
diff --git a/source/benoit/benoit/render/render_row/julia.rs b/source/benoit/benoit/render/render_row/julia.rs
index 7b7c2ef..9202a19 100644
--- a/source/benoit/benoit/render/render_row/julia.rs
+++ b/source/benoit/benoit/render/render_row/julia.rs
@@ -32,7 +32,7 @@ use rug::float::Special;
use std::sync::Arc;
pub fn julia(data: Arc<RenderData>, y: u32, iterator: IteratorFunction) {
- let (iter_count_buffer, square_dist_buffer) = data.output(y);
+ let (iter_count_buffer, square_dist_buffer) = data.output_buffers(y);
let (canvas_width, centre_real, centre_imag, _zoom, max_iter_count) = data.input();
diff --git a/source/benoit/benoit/render/render_row/normal.rs b/source/benoit/benoit/render/render_row/normal.rs
index b39af7c..6e310d7 100644
--- a/source/benoit/benoit/render/render_row/normal.rs
+++ b/source/benoit/benoit/render/render_row/normal.rs
@@ -32,7 +32,7 @@ use rug::float::Special;
use std::sync::Arc;
pub fn normal(data: Arc<RenderData>, y: u32, iterator: IteratorFunction) {
- let (iter_count_buffer, square_dist_buffer) = data.output(y);
+ let (iter_count_buffer, square_dist_buffer) = data.output_buffers(y);
let (canvas_width, centre_real, centre_imag, zoom, max_iter_count) = data.input();