.
This commit is contained in:
132
t/src/canvas/_draw.rs
Normal file
132
t/src/canvas/_draw.rs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
use std::cmp::min;
|
||||||
|
use eframe::egui;
|
||||||
|
use eframe::egui::Color32;
|
||||||
|
use image::{GenericImage, Pixel, Rgb, Rgba};
|
||||||
|
use rusttype::{point, Font, Scale};
|
||||||
|
use crate::canvas::Canvas;
|
||||||
|
|
||||||
|
impl Canvas {
|
||||||
|
|
||||||
|
pub fn text_center(
|
||||||
|
&mut self,
|
||||||
|
font: &Font,
|
||||||
|
color: egui::Color32,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
text: &str,
|
||||||
|
scale: Scale,
|
||||||
|
) {
|
||||||
|
self.refresh();
|
||||||
|
|
||||||
|
let v_metrics = font.v_metrics(scale);
|
||||||
|
|
||||||
|
let glyphs: Vec<_> = font
|
||||||
|
.layout(text, scale, point(0.0, 0.0 + v_metrics.ascent))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let (width, height) = {
|
||||||
|
let mut min_x = i32::MAX;
|
||||||
|
let mut min_y = i32::MAX;
|
||||||
|
let mut max_x = i32::MIN;
|
||||||
|
let mut max_y = i32::MIN;
|
||||||
|
|
||||||
|
for g in glyphs.iter()
|
||||||
|
.map(|p|p.pixel_bounding_box())
|
||||||
|
.flatten() {
|
||||||
|
min_x = min_x.min(g.min.x);
|
||||||
|
min_y = min_y.min(g.min.y);
|
||||||
|
max_x = max_x.max(g.max.x);
|
||||||
|
max_y = max_y.max(g.max.y);
|
||||||
|
// self.debug_rect(
|
||||||
|
// Color32::GREEN,
|
||||||
|
// g.min.x as _,
|
||||||
|
// g.min.y as _,
|
||||||
|
// g.max.x as _,
|
||||||
|
// g.max.y as _,
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
|
// self.debug_rect(
|
||||||
|
// Color32::PURPLE,
|
||||||
|
// min_x as _, min_y as _,
|
||||||
|
// max_x as _, max_y as _);
|
||||||
|
((max_x/* - min_x*/) as f32,
|
||||||
|
(max_y/* - min_y*/) as f32)
|
||||||
|
};
|
||||||
|
let (x, y) = (
|
||||||
|
x - (width / 2.0),
|
||||||
|
y - (height / 2.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
// self.debug_rect(Color32::RED, x, y, x + width, y + height);
|
||||||
|
|
||||||
|
let glyphs: Vec<_> = font
|
||||||
|
.layout(text, scale, point(x, y + v_metrics.ascent))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for glyph in glyphs {
|
||||||
|
if let Some(bounding_box) = glyph.pixel_bounding_box() {
|
||||||
|
|
||||||
|
// self.debug_rect(
|
||||||
|
// Color32::RED,
|
||||||
|
// bounding_box.min.x as _,
|
||||||
|
// bounding_box.min.y as _,
|
||||||
|
// bounding_box.max.x as _,
|
||||||
|
// bounding_box.max.y as _);
|
||||||
|
|
||||||
|
glyph.draw(|x, y, v| {
|
||||||
|
let p = self.image.get_pixel_mut(
|
||||||
|
x + bounding_box.min.x as u32,
|
||||||
|
y + bounding_box.min.y as u32,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut op = p.to_rgba();
|
||||||
|
let np = Rgba([
|
||||||
|
color.r(),
|
||||||
|
color.g(),
|
||||||
|
color.b(),
|
||||||
|
(color.a() as f32 * v) as u8
|
||||||
|
]);
|
||||||
|
op.blend(&np);
|
||||||
|
*p = op.to_rgb()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug_rect(
|
||||||
|
&mut self,
|
||||||
|
color: Color32,
|
||||||
|
mnx: f32,
|
||||||
|
mny: f32,
|
||||||
|
mxx: f32,
|
||||||
|
mxy: f32,
|
||||||
|
) {
|
||||||
|
self.refresh();
|
||||||
|
|
||||||
|
// sort the values just in case
|
||||||
|
let (mnx, mxx) = (mnx.min(mxx), mxx.max(mxx));
|
||||||
|
let (mny, mxy) = (mny.min(mxy), mxy.max(mxy));
|
||||||
|
|
||||||
|
let rgb = Rgb([
|
||||||
|
color.r(),
|
||||||
|
color.g(),
|
||||||
|
color.b(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let w = (mxx - mnx) as u32;
|
||||||
|
let h = (mxy - mny) as u32;
|
||||||
|
for w in 0..w {
|
||||||
|
self.image.put_pixel(mnx as u32 + w, mny as u32, rgb);
|
||||||
|
self.image.put_pixel(mnx as u32 + w, mxy as u32, rgb);
|
||||||
|
self.image.put_pixel(mnx as u32 + w, mny as u32 + h, rgb);
|
||||||
|
|
||||||
|
}
|
||||||
|
for h in 0..h {
|
||||||
|
self.image.put_pixel(mnx as u32, mny as u32 + h, rgb);
|
||||||
|
self.image.put_pixel(mxx as u32, mny as u32 + h, rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
52
t/src/canvas/mod.rs
Normal file
52
t/src/canvas/mod.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use eframe::egui::{Color32, ColorImage, Context, ImageData, TextureHandle, TextureOptions};
|
||||||
|
use eframe::egui::load::SizedTexture;
|
||||||
|
use image::{RgbImage};
|
||||||
|
|
||||||
|
mod _draw;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Canvas {
|
||||||
|
image: RgbImage,
|
||||||
|
handle: Option<TextureHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Canvas {
|
||||||
|
|
||||||
|
pub fn new(path: impl AsRef<Path>) -> Self {
|
||||||
|
Self {
|
||||||
|
image: image::open(path)
|
||||||
|
.unwrap()
|
||||||
|
.to_rgb8(),
|
||||||
|
handle: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rgba(&self) -> &RgbImage { &self.image }
|
||||||
|
pub fn rgba_mut(&mut self) -> &RgbImage { &mut self.image }
|
||||||
|
|
||||||
|
pub fn image(&mut self, ctx: &Context) -> SizedTexture {
|
||||||
|
let handle = self.handle.get_or_insert_with(|| {
|
||||||
|
let img = ColorImage::new(
|
||||||
|
[self.image.width() as _, self.image.height() as _],
|
||||||
|
self.image
|
||||||
|
.pixels()
|
||||||
|
.map(|a| Color32::from_rgb(a.0[0], a.0[1], a.0[2]))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
ctx.load_texture(
|
||||||
|
"_temp",
|
||||||
|
ImageData::Color(Arc::new(img)),
|
||||||
|
TextureOptions::LINEAR
|
||||||
|
)
|
||||||
|
});
|
||||||
|
SizedTexture::new(handle.id(), handle.size_vec2())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh(&mut self) {
|
||||||
|
self.handle = None
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
38
t/src/cells.rs
Normal file
38
t/src/cells.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
const CELLS_START_X: i32 = 30;
|
||||||
|
const CELLS_START_Y: i32 = 25;
|
||||||
|
const CELLS: (i32, i32) = (20, 4);
|
||||||
|
|
||||||
|
// 255 * 72
|
||||||
|
//
|
||||||
|
// cells are 18x18
|
||||||
|
// 20 x 4 cells
|
||||||
|
|
||||||
|
pub struct Cells {
|
||||||
|
cells: Vec<Cell>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cells {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
|
||||||
|
let mut cells = Vec::new();
|
||||||
|
|
||||||
|
for x in 0..CELLS.0 {
|
||||||
|
for y in 0..CELLS.1 {
|
||||||
|
cells.push(Cell {
|
||||||
|
desired_value: 0.0,
|
||||||
|
x: CELLS_START_X + (x * 18),
|
||||||
|
y: CELLS_START_Y + (y * 18),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cells { cells, }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cell {
|
||||||
|
desired_value: f32,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
67
t/src/main.rs
Normal file
67
t/src/main.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
use eframe::egui::{Color32, Vec2, Widget};
|
||||||
|
use eframe::{NativeOptions, egui};
|
||||||
|
use rusttype::Scale;
|
||||||
|
use crate::canvas::Canvas;
|
||||||
|
|
||||||
|
mod canvas;
|
||||||
|
mod cells;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut blank = Canvas::new("targets/blank.png");
|
||||||
|
let mut target = Canvas::new("targets/target.png");
|
||||||
|
let mut test = Canvas::new("targets/test.png");
|
||||||
|
|
||||||
|
// dbg start
|
||||||
|
use rusttype::{Font};
|
||||||
|
|
||||||
|
let mut test2 = test.clone();
|
||||||
|
let font = Font::try_from_vec(std::fs::read("targets/arial.ttf").unwrap()).unwrap();
|
||||||
|
|
||||||
|
let mut scale = 59.5;
|
||||||
|
let mut ox = 3.0;
|
||||||
|
let mut oy = -2.0;
|
||||||
|
|
||||||
|
let mut once = true;
|
||||||
|
// dbg end
|
||||||
|
|
||||||
|
let cells = cells::Cells::new();
|
||||||
|
|
||||||
|
eframe::run_simple_native(
|
||||||
|
"jessie was here",
|
||||||
|
NativeOptions {
|
||||||
|
window_builder: Some(Box::new(|w| {
|
||||||
|
w.with_inner_size(Vec2::new(600.0,800.0))
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
move |ctx, _frame| {
|
||||||
|
|
||||||
|
egui::CentralPanel::default().show(&ctx, |ui| {
|
||||||
|
|
||||||
|
ui.image(blank.image(&ctx));
|
||||||
|
ui.image(target.image(&ctx));
|
||||||
|
ui.image(test.image(&ctx));
|
||||||
|
ui.image(test2.image(&ctx));
|
||||||
|
|
||||||
|
let c = once
|
||||||
|
| egui::Slider::new(&mut scale, 30.0..=80.0).ui(ui).changed()
|
||||||
|
| egui::Slider::new(&mut ox, -30.0..=30.0).ui(ui).changed()
|
||||||
|
| egui::Slider::new(&mut oy, -30.0..=30.0).ui(ui).changed();
|
||||||
|
|
||||||
|
if c {
|
||||||
|
once = false;
|
||||||
|
test2 = blank.clone();
|
||||||
|
test2.text_center(
|
||||||
|
&font,
|
||||||
|
Color32::BLACK,
|
||||||
|
205.0 + ox, 58.0 + oy,
|
||||||
|
"CCBXFQ7X",
|
||||||
|
Scale::uniform(scale)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
BIN
t/targets/arial.ttf
Normal file
BIN
t/targets/arial.ttf
Normal file
Binary file not shown.
BIN
t/targets/arialmt.ttf
Normal file
BIN
t/targets/arialmt.ttf
Normal file
Binary file not shown.
BIN
t/targets/blank.png
Normal file
BIN
t/targets/blank.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
BIN
t/targets/target.png
Normal file
BIN
t/targets/target.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
BIN
t/targets/test.png
Normal file
BIN
t/targets/test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
Reference in New Issue
Block a user