215 lines
7.8 KiB
Rust
215 lines
7.8 KiB
Rust
#![allow(deprecated)]
|
|
//! Visual test GUI for the decoupled `hcie-blend` library using `egui`.
|
|
//! Compiles only when running `cargo run --example blend_tester`.
|
|
|
|
use eframe::egui;
|
|
use hcie_blend::blend_buffers;
|
|
|
|
fn main() -> eframe::Result {
|
|
let options = eframe::NativeOptions {
|
|
viewport: egui::ViewportBuilder::default()
|
|
.with_inner_size([600.0, 400.0])
|
|
.with_title("HCIE Blend Engine Visual Tester"),
|
|
..Default::default()
|
|
};
|
|
eframe::run_native(
|
|
"HCIE Blend Visual Tester",
|
|
options,
|
|
Box::new(|cc| Ok(Box::new(BlendTesterApp::new(cc)))),
|
|
)
|
|
}
|
|
|
|
struct BlendTesterApp {
|
|
background: Vec<u8>,
|
|
foreground: Vec<u8>,
|
|
blended: Vec<u8>,
|
|
width: u32,
|
|
height: u32,
|
|
opacity: f32,
|
|
blend_mode: i32,
|
|
texture: Option<egui::TextureHandle>,
|
|
}
|
|
|
|
impl BlendTesterApp {
|
|
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
|
let width = 256;
|
|
let height = 256;
|
|
let len = (width * height * 4) as usize;
|
|
|
|
// Generate background: Checkerboard (White / Light Gray)
|
|
let mut background = vec![0u8; len];
|
|
for y in 0..height {
|
|
for x in 0..width {
|
|
let i = ((y * width + x) * 4) as usize;
|
|
let is_gray = ((x / 16) + (y / 16)) % 2 == 0;
|
|
let c = if is_gray { 200 } else { 255 };
|
|
background[i] = c;
|
|
background[i + 1] = c;
|
|
background[i + 2] = c;
|
|
background[i + 3] = 255;
|
|
}
|
|
}
|
|
|
|
// Generate foreground: A soft-edged red circle in the center
|
|
let mut foreground = vec![0u8; len];
|
|
let cx = width as f32 / 2.0;
|
|
let cy = height as f32 / 2.0;
|
|
let r = 80.0;
|
|
for y in 0..height {
|
|
for x in 0..width {
|
|
let i = ((y * width + x) * 4) as usize;
|
|
let dx = x as f32 - cx;
|
|
let dy = y as f32 - cy;
|
|
let dist = (dx * dx + dy * dy).sqrt();
|
|
|
|
// Soft edge alpha transition
|
|
let alpha = if dist < r - 10.0 {
|
|
255
|
|
} else if dist < r + 10.0 {
|
|
(((r + 10.0 - dist) / 20.0) * 255.0) as u8
|
|
} else {
|
|
0
|
|
};
|
|
|
|
foreground[i] = 255; // Red
|
|
foreground[i + 1] = 50; // Orange-ish tint
|
|
foreground[i + 2] = 50;
|
|
foreground[i + 3] = alpha;
|
|
}
|
|
}
|
|
|
|
let blended = background.clone();
|
|
|
|
let mut app = Self {
|
|
background,
|
|
foreground,
|
|
blended,
|
|
width,
|
|
height,
|
|
opacity: 1.0,
|
|
blend_mode: 0, // Normal
|
|
texture: None,
|
|
};
|
|
app.update_blended();
|
|
app
|
|
}
|
|
|
|
fn update_blended(&mut self) {
|
|
// Reset blended buffer to background first
|
|
self.blended.copy_from_slice(&self.background);
|
|
|
|
// Run the blending function
|
|
let mode = match self.blend_mode {
|
|
0 => hcie_blend::BlendMode::Normal,
|
|
1 => hcie_blend::BlendMode::Dissolve,
|
|
2 => hcie_blend::BlendMode::Darken,
|
|
3 => hcie_blend::BlendMode::Multiply,
|
|
4 => hcie_blend::BlendMode::ColorBurn,
|
|
5 => hcie_blend::BlendMode::LinearBurn,
|
|
6 => hcie_blend::BlendMode::DarkerColor,
|
|
7 => hcie_blend::BlendMode::Lighten,
|
|
8 => hcie_blend::BlendMode::Screen,
|
|
9 => hcie_blend::BlendMode::ColorDodge,
|
|
10 => hcie_blend::BlendMode::LinearDodge,
|
|
11 => hcie_blend::BlendMode::LighterColor,
|
|
12 => hcie_blend::BlendMode::Overlay,
|
|
13 => hcie_blend::BlendMode::SoftLight,
|
|
14 => hcie_blend::BlendMode::HardLight,
|
|
15 => hcie_blend::BlendMode::VividLight,
|
|
16 => hcie_blend::BlendMode::LinearLight,
|
|
17 => hcie_blend::BlendMode::PinLight,
|
|
18 => hcie_blend::BlendMode::HardMix,
|
|
19 => hcie_blend::BlendMode::Difference,
|
|
20 => hcie_blend::BlendMode::Exclusion,
|
|
21 => hcie_blend::BlendMode::Subtract,
|
|
22 => hcie_blend::BlendMode::Divide,
|
|
23 => hcie_blend::BlendMode::Hue,
|
|
24 => hcie_blend::BlendMode::Saturation,
|
|
25 => hcie_blend::BlendMode::Color,
|
|
26 => hcie_blend::BlendMode::Luminosity,
|
|
_ => hcie_blend::BlendMode::Normal,
|
|
};
|
|
blend_buffers(
|
|
&mut self.blended,
|
|
&self.foreground,
|
|
mode,
|
|
self.opacity,
|
|
);
|
|
self.texture = None; // Invalidate texture to force reload
|
|
}
|
|
}
|
|
|
|
impl eframe::App for BlendTesterApp {
|
|
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
|
|
let ctx = ui.ctx().clone();
|
|
let modes = [
|
|
(0, "Normal"), (1, "Dissolve"), (2, "Darken"), (3, "Multiply"),
|
|
(4, "Color Burn"), (5, "Linear Burn"), (6, "Darker Color"),
|
|
(7, "Lighten"), (8, "Screen"), (9, "Color Dodge"), (10, "Linear Dodge"),
|
|
(11, "Lighter Color"), (12, "Overlay"), (13, "Soft Light"), (14, "Hard Light"),
|
|
(15, "Vivid Light"), (16, "Linear Light"), (17, "Pin Light"), (18, "Hard Mix"),
|
|
(19, "Difference"), (20, "Exclusion"), (21, "Subtract"), (22, "Divide"),
|
|
(23, "Hue"), (24, "Saturation"), (25, "Color"), (26, "Luminosity"),
|
|
];
|
|
|
|
// Ensure texture is loaded
|
|
if self.texture.is_none() {
|
|
let color_image = egui::ColorImage::from_rgba_unmultiplied(
|
|
[self.width as usize, self.height as usize],
|
|
&self.blended,
|
|
);
|
|
self.texture = Some(ctx.load_texture("blended_preview", color_image, Default::default()));
|
|
}
|
|
let texture = self.texture.as_ref().unwrap().clone();
|
|
|
|
egui::CentralPanel::default().show(&ctx, |ui| {
|
|
ui.heading("HCIE Blend Engine Visual Tester");
|
|
ui.add_space(10.0);
|
|
|
|
ui.horizontal(|ui| {
|
|
// Left Column: Preview Canvas
|
|
ui.vertical(|ui| {
|
|
ui.image((texture.id(), texture.size_vec2()));
|
|
});
|
|
|
|
ui.add_space(20.0);
|
|
|
|
// Right Column: Controls
|
|
ui.vertical(|ui| {
|
|
let mut changed = false;
|
|
|
|
// Blend Mode Selection
|
|
ui.label("Blend Mode:");
|
|
let current_label = modes.iter().find(|m| m.0 == self.blend_mode).map(|m| m.1).unwrap_or("Normal");
|
|
egui::ComboBox::from_id_source("blend_mode_select")
|
|
.selected_text(current_label)
|
|
.show_ui(ui, |ui| {
|
|
for &(val, label) in &modes {
|
|
if ui.selectable_value(&mut self.blend_mode, val, label).clicked() {
|
|
changed = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
ui.add_space(15.0);
|
|
|
|
// Opacity Slider
|
|
ui.label(format!("Opacity: {:.2}", self.opacity));
|
|
if ui.add(egui::Slider::new(&mut self.opacity, 0.0..=1.0)).changed() {
|
|
changed = true;
|
|
}
|
|
|
|
if changed {
|
|
self.update_blended();
|
|
}
|
|
|
|
ui.add_space(30.0);
|
|
ui.colored_label(egui::Color32::LIGHT_GREEN, "✓ Decoupled from hcie-protocol");
|
|
ui.colored_label(egui::Color32::LIGHT_GREEN, "✓ Compiles as a dynamic C FFI library");
|
|
ui.colored_label(egui::Color32::LIGHT_GREEN, "✓ Operating with parallel Rayon logic");
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|