summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/benoit/benoit.rs8
-rw-r--r--source/benoit/benoit/app.rs6
-rw-r--r--source/benoit/benoit/app/animate.rs16
-rw-r--r--source/benoit/benoit/app/colour_row.rs6
-rw-r--r--source/benoit/benoit/app/dump.rs35
-rw-r--r--source/benoit/benoit/app/handle_keys.rs44
-rw-r--r--source/benoit/benoit/app/image_filename.rs36
-rw-r--r--source/benoit/benoit/app/initialise.rs3
-rw-r--r--source/benoit/benoit/app/loop.rs21
-rw-r--r--source/benoit/benoit/app/render_row_julia.rs7
-rw-r--r--source/benoit/benoit/app/render_row_normal.rs7
-rw-r--r--source/benoit/benoit/configuration.rs4
-rw-r--r--source/benoit/benoit/configuration/default.rs7
-rw-r--r--source/benoit/benoit/configuration/load.rs54
-rw-r--r--source/benoit/benoit/video/initialise.rs9
15 files changed, 191 insertions, 72 deletions
diff --git a/source/benoit/benoit.rs b/source/benoit/benoit.rs
index c0ca227..da40689 100644
--- a/source/benoit/benoit.rs
+++ b/source/benoit/benoit.rs
@@ -40,12 +40,18 @@ pub struct Version<T> {
pub const VERSION: Version::<u32> = Version::<u32> {
major: 0x1,
- minor: 0x1,
+ minor: 0x2,
patch: 0x0,
};
pub const PRECISION: u32 = 0x80;
+#[derive(Clone, Copy)]
+pub enum ImageFormat {
+ Png,
+ Webp,
+}
+
pub struct FeedbackInfo<'a> {
prev_centre_real: &'a Float,
prev_centre_imag: &'a Float,
diff --git a/source/benoit/benoit/app.rs b/source/benoit/benoit/app.rs
index f7ebb47..255461e 100644
--- a/source/benoit/benoit/app.rs
+++ b/source/benoit/benoit/app.rs
@@ -21,6 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::ImageFormat;
use crate::benoit::fractal::Fractal;
use crate::benoit::iteration::IteratorFunction;
use crate::benoit::task::render_data::RenderData;
@@ -39,6 +40,7 @@ pub mod dump;
pub mod get_iterator_function;
pub mod get_row_renderer;
pub mod handle_keys;
+pub mod image_filename;
pub mod initialise;
pub mod r#loop;
pub mod poll_events;
@@ -50,6 +52,7 @@ pub mod run;
pub type RowRenderer = fn(Arc<RenderData>, u32, IteratorFunction);
pub struct App {
+ #[allow(dead_code)]
thread_count: u32,
fractal: Fractal,
@@ -66,7 +69,8 @@ pub struct App {
colour_range: f32,
- dump_path: String,
+ dump_path: String,
+ image_format: ImageFormat,
video: Option<Video>,
diff --git a/source/benoit/benoit/app/animate.rs b/source/benoit/benoit/app/animate.rs
index f732e61..3de4a76 100644
--- a/source/benoit/benoit/app/animate.rs
+++ b/source/benoit/benoit/app/animate.rs
@@ -51,12 +51,16 @@ impl App {
self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count);
let render_time = time_start.elapsed();
+ eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
+
self.colour(&mut image[..], self.max_iter_count, &mut iter_count_buffer[..], &mut square_dist_buffer[..]);
let colour_time = time_start.elapsed() - render_time;
+ eprint!(" {:.3}ms...", colour_time.as_micros() as f32 / 1000.0);
+
self.dump(format!("{}/render.webp", self.dump_path), &image, self.canvas_width);
- eprintln!(" rend. {:.3}ms, col. {:.3}ms", render_time.as_micros() as f32 / 1000.0, colour_time.as_micros() as f32 / 1000.0);
+ eprintln!(" done");
return 0x0;
}
@@ -98,16 +102,24 @@ impl App {
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());
for frame in 0x0..self.frame_count {
- eprint!("{frame:010} ({:.3}x)...", zoom.to_f32());
+ 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);
let render_time = time_start.elapsed();
+ eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
+
self.colour(&mut image[..], self.max_iter_count, &mut iter_count_buffer[..], &mut square_dist_buffer[..]);
let colour_time = time_start.elapsed() - render_time;
+ eprint!(" {:.3}ms...", colour_time.as_micros() as f32 / 1000.0);
+
+ self.dump(format!("{}/render.webp", self.dump_path), &image, self.canvas_width);
+
+ eprintln!(" done");
+
self.dump(format!("{}/frame{frame:010}.webp", self.dump_path), &image, self.canvas_width);
eprintln!(" rend. {:.3}ms, col. {:.3}ms", render_time.as_micros() as f32 / 1000.0, colour_time.as_micros() as f32 / 1000.0);
diff --git a/source/benoit/benoit/app/colour_row.rs b/source/benoit/benoit/app/colour_row.rs
index 81896cc..0348499 100644
--- a/source/benoit/benoit/app/colour_row.rs
+++ b/source/benoit/benoit/app/colour_row.rs
@@ -34,12 +34,12 @@ impl App {
for x in 0x0..data.canvas_width {
let x = x as usize;
- let iter_count = iter_count_buffer[ x];
- let distance = square_dist_buffer[x].sqrt();
+ let iter_count = unsafe { *iter_count_buffer.get_unchecked( x) };
+ let distance = unsafe { *square_dist_buffer.get_unchecked(x) }.sqrt();
let factor = (iter_count as f32 + 1.0 - distance.log2().log2()) / data.colour_range % 1.0;
- let (red, green, blue) = if iter_count != data.max_iter_count {
+ let (red, green, blue) = if iter_count < data.max_iter_count {
hsv_to_rgb(factor, 7.0 / 8.0, 7.0 / 8.0)
} else {
(0.0, 0.0, 0.0)
diff --git a/source/benoit/benoit/app/dump.rs b/source/benoit/benoit/app/dump.rs
index 6001e31..094650a 100644
--- a/source/benoit/benoit/app/dump.rs
+++ b/source/benoit/benoit/app/dump.rs
@@ -21,19 +21,42 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::ImageFormat;
use crate::benoit::app::App;
+extern crate png;
extern crate webp;
-use std::fs::write;
-use webp::Encoder;
+use std::fs::{File, write};
+use std::io::BufWriter;
impl App {
pub fn dump(&self, path: String, image: &[u8], canvas_width: u32) {
- let encoder = Encoder::from_rgb(&image[..], canvas_width, canvas_width);
+ match self.image_format {
+ ImageFormat::Png => dump_png( &path, image, canvas_width),
+ ImageFormat::Webp => dump_webp(&path, image, canvas_width),
+ }
+ }
+}
- let data = encoder.encode_lossless();
+fn dump_png(path: &String, image: &[u8], canvas_width: u32) {
+ let file = File::create(path).expect("unable to create file");
+ let file_buffer = BufWriter::new(file);
- write(path, &*data).expect("unable to write image");
- }
+ let mut encoder = png::Encoder::new(file_buffer, canvas_width, canvas_width);
+ encoder.set_color(png::ColorType::Rgb);
+ encoder.set_depth(png::BitDepth::Eight);
+ encoder.set_compression(png::Compression::Fast);
+ encoder.set_srgb(png::SrgbRenderingIntent::Perceptual);
+
+ let mut writer = encoder.write_header().expect("unable to write image");
+ writer.write_image_data(image).expect("unable to write image");
+}
+
+fn dump_webp(path: &String, image: &[u8], canvas_width: u32) {
+ let encoder = webp::Encoder::from_rgb(&image[..], canvas_width, canvas_width);
+
+ let data = encoder.encode_lossless();
+
+ write(path, &*data).expect("unable to write image");
}
diff --git a/source/benoit/benoit/app/handle_keys.rs b/source/benoit/benoit/app/handle_keys.rs
index 2b2cff5..50956f8 100644
--- a/source/benoit/benoit/app/handle_keys.rs
+++ b/source/benoit/benoit/app/handle_keys.rs
@@ -40,13 +40,35 @@ impl App {
Scancode::C => self.do_render = true,
Scancode::Tab => (self.julia, self.row_renderer) = toggle_julia(self.julia),
Scancode::X => self.do_dump = true,
- Scancode::Z => eprintln!("c = {}{:+}i -- {}x @ {} iter.", &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count),
+ Scancode::Z => eprintln!("c = {}{:+}i -- {}x @ {} iter. (range: {:.3})", &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count, self.colour_range),
_ => {},
}
+ self.handle_translation(scan_code);
+
+ self.max_iter_count = match scan_code {
+ Scancode::F => self.max_iter_count * 0x2,
+ Scancode::R => self.max_iter_count / 0x2,
+ _ => self.max_iter_count,
+ };
+
+ const COLOUR_RANGE_FACTOR: f32 = 1.0 + 1.0 / 16.0;
+
+ self.colour_range = match scan_code {
+ Scancode::Up => self.colour_range * COLOUR_RANGE_FACTOR,
+ Scancode::Down => self.colour_range / COLOUR_RANGE_FACTOR,
+ _ => self.colour_range,
+ };
+
+ return false;
+ }
+
+ fn handle_translation(&mut self, scan_code: Scancode) {
+ const ZOOM_FACTOR: f32 = 1.0 + 1.0 / 4.0;
+
match scan_code {
- Scancode::E => self.zoom *= 2.0,
- Scancode::Q => self.zoom /= 2.0,
+ Scancode::E => self.zoom *= ZOOM_FACTOR,
+ Scancode::Q => self.zoom /= ZOOM_FACTOR,
_ => {},
};
@@ -69,22 +91,6 @@ impl App {
Scancode::W => self.centre_imag += &translate_ammount,
_ => {},
};
-
- self.max_iter_count = match scan_code {
- Scancode::F => self.max_iter_count * 0x2,
- Scancode::R => self.max_iter_count / 0x2,
- _ => self.max_iter_count,
- };
-
- const COLOUR_RANGE_FACTOR: f32 = 1.0 + 1.0 / 16.0;
-
- self.colour_range = match scan_code {
- Scancode::Up => self.colour_range * COLOUR_RANGE_FACTOR,
- Scancode::Down => self.colour_range / COLOUR_RANGE_FACTOR,
- _ => self.colour_range,
- };
-
- return false;
}
}
diff --git a/source/benoit/benoit/app/image_filename.rs b/source/benoit/benoit/app/image_filename.rs
new file mode 100644
index 0000000..9ab111a
--- /dev/null
+++ b/source/benoit/benoit/app/image_filename.rs
@@ -0,0 +1,36 @@
+/*
+ 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;
+use crate::benoit::app::App;
+
+impl App {
+ pub fn image_filename(name: &str, image_format: ImageFormat) -> String {
+ let file_extension = match image_format {
+ ImageFormat::Png => ".png",
+ ImageFormat::Webp => ".webp",
+ };
+
+ return name.to_owned() + file_extension;
+ }
+}
diff --git a/source/benoit/benoit/app/initialise.rs b/source/benoit/benoit/app/initialise.rs
index 15d98ac..a357940 100644
--- a/source/benoit/benoit/app/initialise.rs
+++ b/source/benoit/benoit/app/initialise.rs
@@ -77,7 +77,8 @@ impl App {
colour_range: configuration.colour_range,
- dump_path: configuration.dump_path,
+ dump_path: configuration.dump_path,
+ image_format: configuration.image_format,
video: video,
diff --git a/source/benoit/benoit/app/loop.rs b/source/benoit/benoit/app/loop.rs
index 9d55431..69eb3d4 100644
--- a/source/benoit/benoit/app/loop.rs
+++ b/source/benoit/benoit/app/loop.rs
@@ -84,7 +84,7 @@ impl App {
self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count);
let render_time = time_start.elapsed();
- eprintln!(" rend. {:.3}ms", render_time.as_micros() as f32 / 1000.0);
+ eprintln!(" {:.3}ms", render_time.as_micros() as f32 / 1000.0);
prev_centre_real.assign(&self.centre_real);
prev_centre_imag.assign(&self.centre_imag);
@@ -94,7 +94,7 @@ impl App {
self.do_render = false;
}
- self.colour(&mut image[..], prev_max_iter_count, &mut iter_count_buffer[..], &mut square_dist_buffer[..]);
+ self.colour(&mut image[..], prev_max_iter_count.min(self.max_iter_count), &mut iter_count_buffer[..], &mut square_dist_buffer[..]);
{
let feedback_info = FeedbackInfo {
@@ -106,16 +106,25 @@ impl App {
next_zoom: &self.zoom,
};
- let feedback_info = match self.julia {
- false => Some(&feedback_info),
- true => None,
+ let feedback_info = if {
+ // Don't draw feedback if rendering a Julia set or
+ // if we haven't done any viewport translations.
+
+ !self.julia
+ && (self.centre_real != prev_centre_real
+ || self.centre_imag != prev_centre_imag
+ || self.zoom != prev_zoom)
+ } {
+ Some(&feedback_info)
+ } else {
+ None
};
unsafe { self.video.as_mut().unwrap_unchecked().draw(&image[..], self.canvas_width, self.scale, feedback_info) };
}
if self.do_dump {
- let path = format!("{}/image.webp", self.dump_path);
+ let path = App::image_filename(format!("{}/image", self.dump_path).as_str(), self.image_format);
eprintln!("dumping image at \"{path}\"");
self.dump(path, &image, self.canvas_width);
diff --git a/source/benoit/benoit/app/render_row_julia.rs b/source/benoit/benoit/app/render_row_julia.rs
index 5c3cae8..2710423 100644
--- a/source/benoit/benoit/app/render_row_julia.rs
+++ b/source/benoit/benoit/app/render_row_julia.rs
@@ -40,9 +40,6 @@ impl App {
for x in 0x0..data.canvas_width {
let canvas_width = Float::with_val(PRECISION, data.canvas_width);
- let x_float = Float::with_val(PRECISION, x);
- let y_float = Float::with_val(PRECISION, y);
-
// For more information, see render_row_normal.
let ca = &data.centre_real;
@@ -56,7 +53,7 @@ impl App {
let mut za = {
let mut za = Float::with_val(PRECISION, &canvas_width / 2.0);
za.neg_assign();
- za += &x_float;
+ za += x;
za *= 4.0;
za /= &canvas_width;
@@ -66,7 +63,7 @@ impl App {
let mut zb = {
let mut zb = Float::with_val(PRECISION, &canvas_width / 2.0);
zb.neg_assign();
- zb += &y_float;
+ zb += y;
zb *= 4.0;
zb /= &canvas_width;
diff --git a/source/benoit/benoit/app/render_row_normal.rs b/source/benoit/benoit/app/render_row_normal.rs
index 381b8bc..832d178 100644
--- a/source/benoit/benoit/app/render_row_normal.rs
+++ b/source/benoit/benoit/app/render_row_normal.rs
@@ -40,13 +40,10 @@ impl App {
for x in 0x0..data.canvas_width {
let canvas_width = Float::with_val(PRECISION, data.canvas_width);
- let x_float = Float::with_val(PRECISION, x);
- let y_float = Float::with_val(PRECISION, y);
-
let ca = {
let mut ca = Float::with_val(PRECISION, &canvas_width / 2.0);
ca.neg_assign();
- ca += &x_float;
+ ca += x;
ca *= 4.0;
ca /= &canvas_width;
ca /= &data.zoom;
@@ -58,7 +55,7 @@ impl App {
let cb = {
let mut cb = Float::with_val(PRECISION, &canvas_width / 2.0);
cb.neg_assign();
- cb += &y_float;
+ cb += y;
cb *= 4.0;
cb /= &canvas_width;
cb /= &data.zoom;
diff --git a/source/benoit/benoit/configuration.rs b/source/benoit/benoit/configuration.rs
index f472f62..6e22f01 100644
--- a/source/benoit/benoit/configuration.rs
+++ b/source/benoit/benoit/configuration.rs
@@ -21,6 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::ImageFormat;
use crate::benoit::fractal::Fractal;
extern crate rug;
@@ -47,7 +48,8 @@ pub struct Configuration {
pub colour_range: f32,
- pub dump_path: String,
+ pub dump_path: String,
+ pub image_format: ImageFormat,
pub interactive: bool,
}
diff --git a/source/benoit/benoit/configuration/default.rs b/source/benoit/benoit/configuration/default.rs
index 46373cd..2a0ae9f 100644
--- a/source/benoit/benoit/configuration/default.rs
+++ b/source/benoit/benoit/configuration/default.rs
@@ -21,7 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::PRECISION;
+use crate::benoit::{ImageFormat, PRECISION};
use crate::benoit::configuration::Configuration;
use crate::benoit::fractal::Fractal;
@@ -46,9 +46,10 @@ impl Configuration {
zoom: Float::with_val(PRECISION, 1.0),
max_iter_count: 0x100,
- colour_range: 16.0,
+ colour_range: 64.0,
- dump_path: "./render/".to_string(),
+ 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
index 8373c22..cd4b812 100644
--- a/source/benoit/benoit/configuration/load.rs
+++ b/source/benoit/benoit/configuration/load.rs
@@ -21,7 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::PRECISION;
+use crate::benoit::{ImageFormat, PRECISION};
use crate::benoit::configuration::Configuration;
use crate::benoit::fractal::Fractal;
@@ -100,21 +100,18 @@ impl Configuration {
get_integer(&mut configuration.thread_count, &configuration_table, "thread_count");
- configuration.fractal = if let Some(name) = get_string(&configuration_table, "fractal") {
- match name.as_str() {
+ if let Some(name) = get_string(&configuration_table, "fractal") {
+ configuration.fractal = match name.as_str() {
"burningship" => Fractal::BurningShip,
"mandelbrot" => Fractal::Mandelbrot,
"tricorn" => Fractal::Tricorn,
- name => panic!("invalid fractal name {name}"),
+ name => panic!("invalid fractal name \"{name}\""),
}
- } else {
- configuration.fractal
- };
+ }
get_boolean(&mut configuration.julia, &configuration_table, "julia");
get_integer(&mut configuration.canvas_width, &configuration_table, "canvas_width");
- get_integer(&mut configuration.scale, &configuration_table, "scale");
get_integer(&mut configuration.frame_count, &configuration_table, "frame_count");
get_float( &mut configuration.centre_real, &configuration_table, "real");
@@ -124,18 +121,39 @@ impl Configuration {
get_float32(&mut configuration.colour_range, &configuration_table, "colour_range");
- // We allow thread counts of zero as those signal
- // automatic thread count detection.
- if configuration.canvas_width == 0x0 {
- panic!("only non-zero values for canvas_width are allowed");
- } else if configuration.scale == 0x0 {
- panic!("only non-zero values for scale are allowed");
- } else if configuration.frame_count == 0x0 {
- panic!("only non-zero values for frame_count are allowed");
- } else if configuration.max_iter_count == 0x0 {
- panic!("only non-zero values for maximum_iteration_count are allowed");
+ 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(());
+}
diff --git a/source/benoit/benoit/video/initialise.rs b/source/benoit/benoit/video/initialise.rs
index f97ae8b..ae633b3 100644
--- a/source/benoit/benoit/video/initialise.rs
+++ b/source/benoit/benoit/video/initialise.rs
@@ -33,12 +33,19 @@ impl Video {
let sdl = sdl2::init().expect("unable to initialise sdl2");
let sdl_video = sdl.video().expect("unable to initialise video");
- let window = sdl_video.window(format!("Benoit {:X}.{:X}.{:X}", VERSION.major, VERSION.minor, VERSION.patch).as_str(), canvas_width * scale, canvas_width * scale).position_centered().build().expect("unable to open window");
+ let mut window_builder = sdl_video.window(format!("Beno\u{00EE}t {:X}.{:X}.{:X}", VERSION.major, VERSION.minor, VERSION.patch).as_str(), canvas_width * scale, canvas_width * scale);
+ window_builder.borderless();
+ window_builder.position_centered();
+
+ let window = window_builder.build().expect("unable to open window");
let mut canvas = window.into_canvas().build().expect("unable to create canvas");
canvas.set_blend_mode(BlendMode::Blend);
+ // We only want to scale the render, not the
+ // feedback, so we can't use SDL's scaling feature.
+
return Video {
sdl: sdl,
sdl_video: sdl_video,