mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-21 22:58:42 -06:00
Compare commits
No commits in common. "108deeab3443774951d0bcdb6b89bf3662afe71d" and "8202e99c8f6e4ea41902390ee6b8e5370db02bf5" have entirely different histories.
108deeab34
...
8202e99c8f
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,4 +4,3 @@ Cargo.lock
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
*.pdb
|
*.pdb
|
||||||
rustc-ice-*.txt
|
rustc-ice-*.txt
|
||||||
.direnv
|
|
||||||
|
|
101
flake.lock
101
flake.lock
|
@ -1,101 +0,0 @@
|
||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"fenix": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"rust-analyzer-src": "rust-analyzer-src"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1725172314,
|
|
||||||
"narHash": "sha256-BtLY9lWu/pe6/ImFwuRRRqMwLacY5AZOKA2hUHUQ64k=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"rev": "28b42d01f549c38bd165296fbcb4fe66d98fc24f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"ref": "monthly",
|
|
||||||
"repo": "fenix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1710146030,
|
|
||||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1725534445,
|
|
||||||
"narHash": "sha256-Yd0FK9SkWy+ZPuNqUgmVPXokxDgMJoGuNpMEtkfcf84=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "9bb1e7571aadf31ddb4af77fc64b2d59580f9a39",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"fenix": "fenix",
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": "nixpkgs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rust-analyzer-src": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1725630423,
|
|
||||||
"narHash": "sha256-gNCLk3Zg7JlAwmWbVHTH6f3+iqdeQ4fheOotCZy8x5M=",
|
|
||||||
"owner": "rust-lang",
|
|
||||||
"repo": "rust-analyzer",
|
|
||||||
"rev": "08c7bbc2dbe4dcc8968484f1a0e1e6fe7a1d4f6d",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "rust-lang",
|
|
||||||
"ref": "nightly",
|
|
||||||
"repo": "rust-analyzer",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
51
flake.nix
51
flake.nix
|
@ -1,51 +0,0 @@
|
||||||
{
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
|
||||||
fenix = {
|
|
||||||
url = "github:nix-community/fenix/monthly";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
outputs = { self, nixpkgs, flake-utils, fenix }:
|
|
||||||
flake-utils.lib.eachDefaultSystem (system: let
|
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
|
||||||
in {
|
|
||||||
devShell = pkgs.mkShell.override {
|
|
||||||
stdenv = if pkgs.stdenv.isLinux then
|
|
||||||
pkgs.stdenvAdapters.useMoldLinker pkgs.clangStdenv
|
|
||||||
else
|
|
||||||
pkgs.clangStdenv;
|
|
||||||
} rec {
|
|
||||||
packages = with pkgs; [
|
|
||||||
(fenix.packages.${system}.complete.withComponents [
|
|
||||||
"cargo"
|
|
||||||
"rustc"
|
|
||||||
"rust-src"
|
|
||||||
"rustfmt"
|
|
||||||
"rust-analyzer"
|
|
||||||
"clippy"
|
|
||||||
])
|
|
||||||
pkg-config
|
|
||||||
cmake
|
|
||||||
];
|
|
||||||
buildInputs = with pkgs; [
|
|
||||||
wayland
|
|
||||||
xorg.libxcb
|
|
||||||
libxkbcommon
|
|
||||||
vulkan-tools
|
|
||||||
vulkan-headers
|
|
||||||
vulkan-loader
|
|
||||||
libGL
|
|
||||||
xorg.libX11
|
|
||||||
xorg.libXcursor
|
|
||||||
xorg.libXi
|
|
||||||
xorg.libXrandr
|
|
||||||
glslang
|
|
||||||
];
|
|
||||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
|
|
||||||
# RUSTFLAGS = "-Zthreads=8"; (causes rustc to ICE)
|
|
||||||
MANGOHUD_DLSYM = "1"; # mangohud glium fix
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -7,10 +7,9 @@ publish = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hui = { path = "../hui" }
|
hui = { path = "../hui" }
|
||||||
hui-painter = { path = "../hui-painter" }
|
|
||||||
hui-glium = { path = "../hui-glium" }
|
hui-glium = { path = "../hui-glium" }
|
||||||
hui-winit = { path = "../hui-winit" }
|
hui-winit = { path = "../hui-winit" }
|
||||||
kubi-logging = { git = "https://github.com/griffi-gh/kubi", rev = "be1e24ba0c9e6d24128e7d0e74bebd8b90c23be7" }
|
kubi-logging = { git = "https://github.com/griffi-gh/kubi", rev = "c162893fd" }
|
||||||
glium = "0.35"
|
glium = "0.35"
|
||||||
winit = "0.30"
|
winit = "0.30"
|
||||||
glam = "0.28"
|
glam = "0.28"
|
||||||
|
|
|
@ -53,9 +53,6 @@ pub fn ui<T>(
|
||||||
WindowEvent::CloseRequested => {
|
WindowEvent::CloseRequested => {
|
||||||
window_target.exit();
|
window_target.exit();
|
||||||
},
|
},
|
||||||
WindowEvent::Resized(size) => {
|
|
||||||
display.resize((size.width, size.height));
|
|
||||||
},
|
|
||||||
WindowEvent::RedrawRequested => {
|
WindowEvent::RedrawRequested => {
|
||||||
let mut frame = display.draw();
|
let mut frame = display.draw();
|
||||||
frame.clear_color_srgb(0.5, 0.5, 0.5, 1.);
|
frame.clear_color_srgb(0.5, 0.5, 0.5, 1.);
|
||||||
|
|
|
@ -21,4 +21,3 @@ log = "0.4"
|
||||||
rect_packer = "0.2" # TODO: use sth else like `crunch` instead?
|
rect_packer = "0.2" # TODO: use sth else like `crunch` instead?
|
||||||
hashbrown = "0.14"
|
hashbrown = "0.14"
|
||||||
nohash-hasher = "0.2"
|
nohash-hasher = "0.2"
|
||||||
fontdue = "0.9"
|
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
pub mod paint;
|
pub mod paint;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
pub mod text;
|
|
||||||
|
|
||||||
use text::FontManager;
|
|
||||||
use texture::TextureAtlas;
|
use texture::TextureAtlas;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Painter {
|
pub struct Painter {
|
||||||
pub atlas: TextureAtlas,
|
atlas: TextureAtlas,
|
||||||
pub fonts: FontManager,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Painter {
|
impl Painter {
|
||||||
|
@ -16,19 +13,7 @@ impl Painter {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn atlas(&self) -> &TextureAtlas {
|
pub fn atlas(&self) -> &TextureAtlas {
|
||||||
// &self.atlas
|
&self.atlas
|
||||||
// }
|
}
|
||||||
|
|
||||||
// pub fn atlas_mut(&mut self) -> &mut TextureAtlas {
|
|
||||||
// &mut self.atlas
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn fonts(&self) -> &FontManager {
|
|
||||||
// &self.fonts
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn fonts_mut(&mut self) -> &mut FontManager {
|
|
||||||
// &mut self.fonts
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
use glam::Vec2;
|
use crate::paint::buffer::PaintBuffer;
|
||||||
use crate::{paint::buffer::PaintBuffer, Painter};
|
|
||||||
|
|
||||||
// mod root;
|
|
||||||
// pub use root::RootCommand;
|
|
||||||
|
|
||||||
mod transform;
|
mod transform;
|
||||||
pub use transform::PaintTransform;
|
pub use transform::PaintTransform;
|
||||||
|
@ -14,20 +10,5 @@ mod text;
|
||||||
pub use text::PaintText;
|
pub use text::PaintText;
|
||||||
|
|
||||||
pub trait PaintCommand {
|
pub trait PaintCommand {
|
||||||
/// Called before actual paint command is executed\
|
fn paint(&self, into: &mut PaintBuffer);
|
||||||
/// Opportunity to pre-cache bitmaps, etc.
|
|
||||||
///
|
|
||||||
/// Make sure to propagate this call to children!
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn pre_paint(&self, ctx: &mut Painter) {}
|
|
||||||
|
|
||||||
/// Paint the command into the buffer
|
|
||||||
///
|
|
||||||
/// Do not allocate new textures or cache glyphs here, use `pre_paint` instead!\
|
|
||||||
/// (Doing this WILL lead to atlas corruption flicker for a single frame if it's forced to resize!)
|
|
||||||
fn paint(&self, ctx: &mut Painter, into: &mut PaintBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Measurable: PaintCommand {
|
|
||||||
fn size(&self, ctx: &Painter) -> Vec2;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,16 @@
|
||||||
use std::num::NonZeroU16;
|
use glam::{Vec2, vec2};
|
||||||
use glam::{vec2, Vec2, Vec4};
|
|
||||||
use hui_shared::{color, rect::{Corners, FillColor}};
|
use hui_shared::{color, rect::{Corners, FillColor}};
|
||||||
use crate::{
|
use crate::paint::{
|
||||||
paint::{
|
buffer::PaintBuffer,
|
||||||
buffer::{PaintBuffer, Vertex},
|
command::PaintCommand,
|
||||||
command::PaintCommand,
|
|
||||||
},
|
|
||||||
texture::TextureHandle,
|
|
||||||
Painter
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Calculate the number of points based on the maximum corner radius
|
|
||||||
fn point_count(corners: Corners<f32>) -> NonZeroU16 {
|
|
||||||
//Increase for higher quality
|
|
||||||
const VTX_PER_CORER_RADIUS_PIXEL: f32 = 0.5;
|
|
||||||
NonZeroU16::new(
|
|
||||||
(corners.max_f32() * VTX_PER_CORER_RADIUS_PIXEL).round() as u16 + 2
|
|
||||||
).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PaintRectangle {
|
pub struct PaintRectangle {
|
||||||
/// Color of the rectangle.
|
/// Color of the rectangle.
|
||||||
pub color: FillColor,
|
pub color: FillColor,
|
||||||
|
|
||||||
/// Size of the rectangle.
|
|
||||||
///
|
|
||||||
/// (Only different from using transform if the rectangle has border radius.)
|
|
||||||
pub size: Vec2,
|
|
||||||
|
|
||||||
/// Texture to use for the rectangle.
|
/// Texture to use for the rectangle.
|
||||||
///
|
pub texture: Option<u32>,
|
||||||
/// Invalid handles will be ignored.
|
|
||||||
pub texture: Option<TextureHandle>,
|
|
||||||
|
|
||||||
/// UV coords inside the texture
|
/// UV coords inside the texture
|
||||||
pub texture_uv: Corners<Vec2>,
|
pub texture_uv: Corners<Vec2>,
|
||||||
|
@ -39,20 +18,14 @@ pub struct PaintRectangle {
|
||||||
/// Border width.
|
/// Border width.
|
||||||
pub border_radius: Corners<f32>,
|
pub border_radius: Corners<f32>,
|
||||||
|
|
||||||
// TODO per-corner border radius point count override
|
/// Border color.
|
||||||
|
pub border_radius_points_override: Option<f32>,
|
||||||
/// Border radius point count.
|
|
||||||
///
|
|
||||||
/// - If not set, it will be calculated based on the maximum radius.
|
|
||||||
/// - If set, it will be used for all corners.
|
|
||||||
pub border_radius_points_override: Option<NonZeroU16>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PaintRectangle {
|
impl Default for PaintRectangle {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
color: color::WHITE.into(),
|
color: color::WHITE.into(),
|
||||||
size: Vec2::ONE,
|
|
||||||
texture: None,
|
texture: None,
|
||||||
texture_uv: Corners {
|
texture_uv: Corners {
|
||||||
top_left: vec2(0., 0.),
|
top_left: vec2(0., 0.),
|
||||||
|
@ -66,213 +39,8 @@ impl Default for PaintRectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintRectangle {
|
|
||||||
pub fn from_color(color: impl Into<FillColor>) -> Self {
|
|
||||||
Self {
|
|
||||||
color: color.into(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_texture(texture: TextureHandle) -> Self {
|
|
||||||
Self {
|
|
||||||
texture: Some(texture),
|
|
||||||
color: color::WHITE.into(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_texture_color(texture: TextureHandle, color: impl Into<FillColor>) -> Self {
|
|
||||||
Self {
|
|
||||||
texture: Some(texture),
|
|
||||||
color: color.into(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PaintCommand for PaintRectangle {
|
impl PaintCommand for PaintRectangle {
|
||||||
fn paint(&self, ctx: &mut Painter, into: &mut PaintBuffer) {
|
fn paint(&self, into: &mut PaintBuffer) {
|
||||||
// If texture is set:
|
todo!()
|
||||||
// - Get texture UV
|
|
||||||
// - Map local UVs to texture UV coords
|
|
||||||
// Otherwise, if texture handle is not set or invalid, use the bottom left
|
|
||||||
// corner of the texture which contains a white pixel.
|
|
||||||
let uvs = self.texture
|
|
||||||
.map(|handle| ctx.atlas.get_uv(handle))
|
|
||||||
.flatten()
|
|
||||||
.map(|global_uv| {
|
|
||||||
let texture_uv = self.texture_uv;
|
|
||||||
let texture_uv_is_default =
|
|
||||||
texture_uv.top_left == vec2(0., 0.) &&
|
|
||||||
texture_uv.top_right == vec2(1., 0.) &&
|
|
||||||
texture_uv.bottom_left == vec2(0., 1.) &&
|
|
||||||
texture_uv.bottom_right == vec2(1., 1.);
|
|
||||||
|
|
||||||
if texture_uv_is_default {
|
|
||||||
global_uv
|
|
||||||
} else {
|
|
||||||
let top = global_uv.top_left
|
|
||||||
.lerp(global_uv.top_right, texture_uv.top_left.x);
|
|
||||||
let bottom = global_uv.bottom_left
|
|
||||||
.lerp(global_uv.bottom_right, texture_uv.top_left.x);
|
|
||||||
let top_left = top
|
|
||||||
.lerp(bottom, texture_uv.top_left.y);
|
|
||||||
|
|
||||||
let top = global_uv.top_left
|
|
||||||
.lerp(global_uv.top_right, texture_uv.top_right.x);
|
|
||||||
let bottom = global_uv.bottom_left
|
|
||||||
.lerp(global_uv.bottom_right, texture_uv.top_right.x);
|
|
||||||
let top_right = top
|
|
||||||
.lerp(bottom, texture_uv.top_right.y);
|
|
||||||
|
|
||||||
let top = global_uv.top_left
|
|
||||||
.lerp(global_uv.top_right, texture_uv.bottom_left.x);
|
|
||||||
let bottom = global_uv.bottom_left
|
|
||||||
.lerp(global_uv.bottom_right, texture_uv.bottom_left.x);
|
|
||||||
let bottom_left = top
|
|
||||||
.lerp(bottom, texture_uv.bottom_left.y);
|
|
||||||
|
|
||||||
let top = global_uv.top_left
|
|
||||||
.lerp(global_uv.top_right, texture_uv.bottom_right.x);
|
|
||||||
let bottom = global_uv.bottom_left
|
|
||||||
.lerp(global_uv.bottom_right, texture_uv.bottom_right.x);
|
|
||||||
let bottom_right = top
|
|
||||||
.lerp(bottom, texture_uv.bottom_right.y);
|
|
||||||
|
|
||||||
Corners { top_left, top_right, bottom_left, bottom_right }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or(Corners::all(Vec2::ZERO)); // For non-textured rectangles
|
|
||||||
|
|
||||||
// Get corner colors
|
|
||||||
let colors = self.color.corners();
|
|
||||||
|
|
||||||
// Get the base index for the vertices
|
|
||||||
let idx_base = into.vertices.len() as u32;
|
|
||||||
|
|
||||||
if self.border_radius.max_f32() == 0. {
|
|
||||||
// No border radius:
|
|
||||||
// Draw a simple quad (2 tris)
|
|
||||||
let indices = Corners {
|
|
||||||
top_left: idx_base + 0,
|
|
||||||
top_right: idx_base + 1,
|
|
||||||
bottom_left: idx_base + 2,
|
|
||||||
bottom_right: idx_base + 3,
|
|
||||||
};
|
|
||||||
into.indices.extend([
|
|
||||||
indices.top_left, indices.bottom_left, indices.top_right,
|
|
||||||
indices.top_right, indices.bottom_left, indices.bottom_right,
|
|
||||||
]);
|
|
||||||
into.vertices.extend([
|
|
||||||
Vertex {
|
|
||||||
position: vec2(0., 0.) * self.size,
|
|
||||||
uv: uvs.top_left,
|
|
||||||
color: colors.top_left,
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: vec2(1., 0.) * self.size,
|
|
||||||
uv: uvs.top_right,
|
|
||||||
color: colors.top_right,
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: vec2(0., 1.) * self.size,
|
|
||||||
uv: uvs.bottom_left,
|
|
||||||
color: colors.bottom_left,
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: vec2(1., 1.) * self.size,
|
|
||||||
uv: uvs.bottom_right,
|
|
||||||
color: colors.bottom_right,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
// Yes border radius :3
|
|
||||||
// Draw a rounded rectangle with the given border radius and point count
|
|
||||||
|
|
||||||
let point_count = self.border_radius_points_override
|
|
||||||
.unwrap_or(point_count(self.border_radius))
|
|
||||||
.get();
|
|
||||||
|
|
||||||
// Get vertex for a point in scaled pixel space
|
|
||||||
let point_impl = |point: Vec2| {
|
|
||||||
let point_uv = point / self.size;
|
|
||||||
let color_at_point =
|
|
||||||
colors.bottom_right * point_uv.x * point_uv.y +
|
|
||||||
colors.top_right * point_uv.x * (1. - point_uv.y) +
|
|
||||||
colors.bottom_left * (1. - point_uv.x) * point_uv.y +
|
|
||||||
colors.top_left * (1. - point_uv.x) * (1. - point_uv.y);
|
|
||||||
let uv_at_point =
|
|
||||||
uvs.bottom_right * point_uv.x * point_uv.y +
|
|
||||||
uvs.top_right * point_uv.x * (1. - point_uv.y) +
|
|
||||||
uvs.bottom_left * (1. - point_uv.x) * point_uv.y +
|
|
||||||
uvs.top_left * (1. - point_uv.x) * (1. - point_uv.y);
|
|
||||||
Vertex {
|
|
||||||
position: point,
|
|
||||||
color: color_at_point,
|
|
||||||
uv: uv_at_point,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
into.vertices.reserve(point_count as usize * 4);
|
|
||||||
into.indices.reserve((point_count as usize - 1) * 12 * 4);
|
|
||||||
|
|
||||||
for i in 0..point_count as u32 {
|
|
||||||
let frac = i as f32 / (point_count - 1) as f32;
|
|
||||||
let angle = frac * std::f32::consts::PI * 0.5;
|
|
||||||
let x = angle.sin();
|
|
||||||
let y = angle.cos();
|
|
||||||
into.vertices.extend([
|
|
||||||
point_impl(vec2(x, 1. - y) * self.border_radius.top_right + vec2(self.size.x - self.border_radius.top_right, 0.)),
|
|
||||||
point_impl(vec2(x - 1., y) * self.border_radius.bottom_right + vec2(self.size.x, self.size.y - self.border_radius.bottom_right)),
|
|
||||||
point_impl(vec2(1. - x, y) * self.border_radius.bottom_left + vec2(0., self.size.y - self.border_radius.bottom_left)),
|
|
||||||
point_impl(vec2(1. - x, 1. - y) * self.border_radius.top_left),
|
|
||||||
]);
|
|
||||||
if i > 0 {
|
|
||||||
// mental illness:
|
|
||||||
into.indices.extend([
|
|
||||||
//Top-right corner
|
|
||||||
idx_base,
|
|
||||||
idx_base + 1 + (i - 1) * 4,
|
|
||||||
idx_base + 1 + i * 4,
|
|
||||||
//Bottom-right corner
|
|
||||||
idx_base,
|
|
||||||
idx_base + 1 + (i - 1) * 4 + 1,
|
|
||||||
idx_base + 1 + i * 4 + 1,
|
|
||||||
//Bottom-left corner
|
|
||||||
idx_base,
|
|
||||||
idx_base + 1 + (i - 1) * 4 + 2,
|
|
||||||
idx_base + 1 + i * 4 + 2,
|
|
||||||
//Top-left corner
|
|
||||||
idx_base,
|
|
||||||
idx_base + 1 + (i - 1) * 4 + 3,
|
|
||||||
idx_base + 1 + i * 4 + 3,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Fill in the rest
|
|
||||||
//mental illness 2:
|
|
||||||
into.indices.extend([
|
|
||||||
//Top
|
|
||||||
idx_base,
|
|
||||||
idx_base + 4,
|
|
||||||
idx_base + 1,
|
|
||||||
//Right?, i think
|
|
||||||
idx_base,
|
|
||||||
idx_base + 1 + (point_count as u32 - 1) * 4,
|
|
||||||
idx_base + 1 + (point_count as u32 - 1) * 4 + 1,
|
|
||||||
//Left???
|
|
||||||
idx_base,
|
|
||||||
idx_base + 1 + (point_count as u32 - 1) * 4 + 2,
|
|
||||||
idx_base + 1 + (point_count as u32 - 1) * 4 + 3,
|
|
||||||
//Bottom???
|
|
||||||
idx_base,
|
|
||||||
idx_base + 3,
|
|
||||||
idx_base + 2,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
unimplemented!("Border radius is not supported yet");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,97 +1,14 @@
|
||||||
use std::{borrow::Cow, sync::Arc};
|
use crate::paint::{
|
||||||
use fontdue::layout::{self, CoordinateSystem, GlyphRasterConfig, Layout};
|
buffer::PaintBuffer,
|
||||||
use glam::{vec2, Vec2};
|
command::PaintCommand,
|
||||||
use crate::{
|
|
||||||
paint::{
|
|
||||||
buffer::PaintBuffer,
|
|
||||||
command::PaintCommand,
|
|
||||||
}, text::FontHandle, Painter
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Measurable;
|
|
||||||
|
|
||||||
// TODO align, multichunk etc
|
|
||||||
|
|
||||||
pub struct TextChunk {
|
|
||||||
pub text: Cow<'static, str>,
|
|
||||||
pub font: FontHandle,
|
|
||||||
pub size: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PaintText {
|
pub struct PaintText {
|
||||||
// TODO multiple text chunks
|
//TODO: PaintText command
|
||||||
pub text: TextChunk,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PaintText {
|
|
||||||
pub fn new(text: impl Into<Cow<'static, str>>, font: FontHandle, size: f32) -> Self {
|
|
||||||
Self {
|
|
||||||
text: TextChunk {
|
|
||||||
text: text.into(),
|
|
||||||
font,
|
|
||||||
size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_font_array<'a>(&self, ctx: &'a Painter) -> Vec<&'a fontdue::Font> {
|
|
||||||
let font = ctx.fonts.get_fontdue_font(self.text.font)
|
|
||||||
.expect("FontHandle is invalid");
|
|
||||||
vec![&font]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_layout(&self, font_array: &[&fontdue::Font]) -> Layout {
|
|
||||||
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
|
||||||
layout.append(
|
|
||||||
&font_array,
|
|
||||||
&fontdue::layout::TextStyle::new(
|
|
||||||
&self.text.text,
|
|
||||||
self.text.size,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
);
|
|
||||||
layout
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintCommand for PaintText {
|
impl PaintCommand for PaintText {
|
||||||
fn pre_paint(&self, ctx: &mut Painter) {
|
fn paint(&self, into: &mut PaintBuffer) {
|
||||||
let font_array = self.build_font_array(ctx);
|
|
||||||
let layout = self.build_layout(&font_array);
|
|
||||||
|
|
||||||
for glyph in layout.glyphs() {
|
|
||||||
ctx.fonts.render_glyph(&mut ctx.atlas, self.text.font, glyph.key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(&self, ctx: &mut Painter, into: &mut PaintBuffer) {
|
|
||||||
// let font_array = self.build_font_array(ctx);
|
|
||||||
// let layout = self.build_layout(&font_array);
|
|
||||||
|
|
||||||
// for glyph in layout.glyphs() {
|
|
||||||
// let config = GlyphRasterConfig {
|
|
||||||
// glyph_index: glyph.font_index
|
|
||||||
// };
|
|
||||||
// let glyph_raster = ctx.fonts().render_glyph(atlas, font, config);
|
|
||||||
// }
|
|
||||||
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Measurable for PaintText {
|
|
||||||
fn size(&self, ctx: &Painter) -> Vec2 {
|
|
||||||
let font_array = self.build_font_array(ctx);
|
|
||||||
let layout = self.build_layout(&font_array);
|
|
||||||
|
|
||||||
let width = layout.lines().map(|lines| {
|
|
||||||
lines.iter().fold(0.0_f32, |acc, x| {
|
|
||||||
let glyph = layout.glyphs().get(x.glyph_end).unwrap();
|
|
||||||
acc.max(glyph.x + glyph.width as f32)
|
|
||||||
})
|
|
||||||
}).unwrap_or(0.);
|
|
||||||
let height = layout.height();
|
|
||||||
|
|
||||||
vec2(width, height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,47 +1,29 @@
|
||||||
use crate::{
|
use crate::paint::{
|
||||||
Painter,
|
buffer::PaintBuffer,
|
||||||
paint::{
|
command::PaintCommand,
|
||||||
buffer::PaintBuffer,
|
|
||||||
command::PaintCommand,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//TODO: use generics instead
|
||||||
|
|
||||||
pub struct PaintTransform {
|
pub struct PaintTransform {
|
||||||
pub transform: glam::Affine2,
|
pub transform: glam::Affine2,
|
||||||
pub children: Vec<Box<dyn PaintCommand>>,
|
pub children: Vec<Box<dyn PaintCommand>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintCommand for PaintTransform {
|
impl PaintCommand for PaintTransform {
|
||||||
fn pre_paint(&self, ctx: &mut Painter) {
|
fn paint(&self, into: &mut PaintBuffer) {
|
||||||
for child in &self.children {
|
|
||||||
child.pre_paint(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(&self, ctx: &mut Painter, into: &mut PaintBuffer) {
|
|
||||||
// remember the starting index
|
// remember the starting index
|
||||||
let starting_index = into.vertices.len();
|
let starting_index = into.vertices.len();
|
||||||
|
|
||||||
// paint children nodes
|
// paint children nodes
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
child.paint(ctx, into);
|
child.paint(into);
|
||||||
}
|
|
||||||
|
|
||||||
let mut min_point = glam::Vec2::splat(f32::MAX);
|
|
||||||
let mut max_point = glam::Vec2::splat(f32::MIN);
|
|
||||||
for vtx in &into.vertices[starting_index..] {
|
|
||||||
min_point = min_point.min(vtx.position);
|
|
||||||
max_point = max_point.max(vtx.position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// trans the children in-place
|
// trans the children in-place
|
||||||
for vtx in &mut into.vertices[starting_index..] {
|
for vtx in &mut into.vertices[starting_index..] {
|
||||||
//HACK: to match the old behavior:
|
//TODO fix for rotation around the center of the object
|
||||||
//(shift the origin to the center before transforming)
|
|
||||||
let offset = (max_point + min_point) / 2.0;
|
|
||||||
vtx.position -= offset;
|
|
||||||
vtx.position = self.transform.transform_point2(vtx.position);
|
vtx.position = self.transform.transform_point2(vtx.position);
|
||||||
vtx.position += offset;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
use fontdue::layout::GlyphRasterConfig;
|
|
||||||
use crate::texture::{TextureAtlas, TextureHandle};
|
|
||||||
|
|
||||||
pub(crate) mod ftm;
|
|
||||||
pub(crate) mod font;
|
|
||||||
|
|
||||||
pub use font::FontHandle;
|
|
||||||
|
|
||||||
pub struct FontManager {
|
|
||||||
fonts: font::FontHandleManager,
|
|
||||||
ftm: ftm::FontTextureManager,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FontManager {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
fonts: font::FontHandleManager::new(),
|
|
||||||
ftm: ftm::FontTextureManager::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a font to the manager.
|
|
||||||
///
|
|
||||||
/// Panics:
|
|
||||||
/// - If the font data is invalid.
|
|
||||||
pub fn add_font(&mut self, data: &[u8]) -> FontHandle {
|
|
||||||
let font = self.fonts.add_font(data);
|
|
||||||
self.ftm.init_font(font);
|
|
||||||
font
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove and deallocate a font from the manager.
|
|
||||||
///
|
|
||||||
/// Panics:
|
|
||||||
/// - If the font handle is invalid.
|
|
||||||
pub fn remove_font(&mut self, font: FontHandle, atlas: &mut TextureAtlas) {
|
|
||||||
self.ftm.drop_font(font, atlas);
|
|
||||||
self.fonts.remove_font(font);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render a glyph and cache it in the texture atlas.
|
|
||||||
///
|
|
||||||
/// Panics:
|
|
||||||
/// - If the font handle is invalid or not initialized.
|
|
||||||
/// - Fuck around and find out, this api is unstable
|
|
||||||
pub(crate) fn render_glyph(
|
|
||||||
&mut self,
|
|
||||||
atlas: &mut TextureAtlas,
|
|
||||||
font: FontHandle,
|
|
||||||
config: GlyphRasterConfig
|
|
||||||
) -> TextureHandle {
|
|
||||||
self.ftm.render_glyph(font, &self.fonts, config, atlas)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal API
|
|
||||||
pub(crate) fn get_fontdue_font(
|
|
||||||
&self,
|
|
||||||
handle: FontHandle
|
|
||||||
) -> Option<&fontdue::Font> {
|
|
||||||
self.fonts.get_font_repr(handle)
|
|
||||||
.map(|x| &x.font)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FontManager {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
use hashbrown::HashMap;
|
|
||||||
use nohash_hasher::BuildNoHashHasher;
|
|
||||||
|
|
||||||
pub(crate) type FontId = u16;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct FontHandle(pub(crate) FontId);
|
|
||||||
|
|
||||||
pub(crate) struct FontRepr {
|
|
||||||
pub(crate) font: fontdue::Font,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FontHandleManager {
|
|
||||||
idc: FontId,
|
|
||||||
fonts: HashMap<FontId, FontRepr,BuildNoHashHasher<FontId>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FontHandleManager {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
idc: 0,
|
|
||||||
fonts: HashMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a font to the manager.
|
|
||||||
///
|
|
||||||
/// Panics:
|
|
||||||
/// - If the font data is invalid.
|
|
||||||
pub fn add_font(&mut self, data: &[u8]) -> FontHandle {
|
|
||||||
let font = fontdue::Font::from_bytes(data, fontdue::FontSettings::default()).unwrap();
|
|
||||||
self.fonts.insert_unique_unchecked(self.idc, FontRepr { font });
|
|
||||||
self.idc += 1;
|
|
||||||
FontHandle(self.idc - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal function
|
|
||||||
///
|
|
||||||
/// Remove and deallocate a font from the manager if the font handle is valid.
|
|
||||||
///
|
|
||||||
/// Panics:
|
|
||||||
/// - If the font handle is invalid.
|
|
||||||
pub(crate) fn remove_font(&mut self, handle: FontHandle) {
|
|
||||||
self.fonts.remove(&handle.0).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the font handle for the specified font.
|
|
||||||
pub(crate) fn get_font_repr(&self, handle: FontHandle) -> Option<&FontRepr> {
|
|
||||||
self.fonts.get(&handle.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
use fontdue::layout::GlyphRasterConfig;
|
|
||||||
use hashbrown::HashMap;
|
|
||||||
use nohash_hasher::BuildNoHashHasher;
|
|
||||||
use crate::texture::{SourceTextureFormat, TextureAtlas, TextureHandle};
|
|
||||||
use super::font::{FontHandle, FontHandleManager, FontId};
|
|
||||||
|
|
||||||
/// Maps to the actual texture handle.
|
|
||||||
struct RasterizedGlyphInternal {
|
|
||||||
handle: TextureHandle,
|
|
||||||
metrics: fontdue::Metrics,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Map from raster config to glyph cache item.
|
|
||||||
///
|
|
||||||
/// Partitioned by font id in FtM :3
|
|
||||||
type PartitionKey = HashMap<GlyphRasterConfig, RasterizedGlyphInternal>;
|
|
||||||
|
|
||||||
/// Manages glyph cache items in a texture atlas.
|
|
||||||
pub struct FontTextureManager {
|
|
||||||
partition: HashMap<FontId, PartitionKey, BuildNoHashHasher<FontId>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FontTextureManager {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
partition: HashMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Drop cached data for the specified font.
|
|
||||||
///
|
|
||||||
/// Panics:
|
|
||||||
/// - If the font handle is invalid or not initialized.
|
|
||||||
/// - If any of the cached items are not found in the texture atlas or became invalid.\
|
|
||||||
/// This may happen if, for example, a different atlas is passed than the one used to allocate the items.
|
|
||||||
pub(crate) fn drop_font(&mut self, font: FontHandle, atlas: &mut TextureAtlas) {
|
|
||||||
let dump = self.partition.remove(&font.0).expect("Font handle is invalid");
|
|
||||||
for (_, item) in dump {
|
|
||||||
atlas.deallocate(item.handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize the partition for the specified font.
|
|
||||||
///
|
|
||||||
/// Panics:
|
|
||||||
/// - If the partition for the font already exists.
|
|
||||||
pub(crate) fn init_font(&mut self, font: FontHandle) {
|
|
||||||
assert!(!self.partition.contains_key(&font.0), "Font handle already initialized");
|
|
||||||
self.partition.insert_unique_unchecked(font.0, HashMap::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render a glyph and cache it in the texture atlas.
|
|
||||||
///
|
|
||||||
/// Panics:
|
|
||||||
/// - If the font handle is invalid or not initialized.
|
|
||||||
/// - Fuck around and find out, this api is unstable
|
|
||||||
pub(crate) fn render_glyph(
|
|
||||||
&mut self,
|
|
||||||
font_handle: FontHandle,
|
|
||||||
fhm_internal: &FontHandleManager,
|
|
||||||
config: GlyphRasterConfig,
|
|
||||||
atlas: &mut TextureAtlas
|
|
||||||
) -> TextureHandle {
|
|
||||||
// Get partiton
|
|
||||||
let partition = self.partition.get_mut(&font_handle.0)
|
|
||||||
.expect("Font handle is not registered in FtM");
|
|
||||||
|
|
||||||
// Check if glyph is alr cached
|
|
||||||
if let Some(item) = partition.get(&config) {
|
|
||||||
return item.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get fontdue font from the manager
|
|
||||||
let font = &fhm_internal.get_font_repr(font_handle)
|
|
||||||
.expect("Font handle is invalid")
|
|
||||||
.font;
|
|
||||||
|
|
||||||
// Rasterize the font and copy the texture data
|
|
||||||
let (metrics, data) = font.rasterize_config(config);
|
|
||||||
let handle = atlas.allocate_with_data(SourceTextureFormat::A8, &data, metrics.width);
|
|
||||||
|
|
||||||
// Create a texture item struct and insert it into the partition
|
|
||||||
let itm = RasterizedGlyphInternal { handle, metrics };
|
|
||||||
partition.insert_unique_unchecked(config, itm);
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FontTextureManager {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,4 @@
|
||||||
use glam::{ivec2, uvec2, vec2, UVec2, Vec2};
|
use glam::{UVec2, uvec2, ivec2};
|
||||||
use hui_shared::rect::Corners;
|
|
||||||
use rect_packer::DensePacker;
|
use rect_packer::DensePacker;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use nohash_hasher::BuildNoHashHasher;
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
|
@ -343,22 +342,6 @@ impl TextureAtlas {
|
||||||
|
|
||||||
handle
|
handle
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get uv coordinates for the texture handle.
|
|
||||||
pub(crate) fn get_uv(&self, handle: TextureHandle) -> Option<Corners<Vec2>> {
|
|
||||||
let TextureAllocation { offset, size, .. } = self.allocations
|
|
||||||
.get(&handle.id)?;
|
|
||||||
let p0x = offset.x as f32 / self.size.x as f32;
|
|
||||||
let p1x = (offset.x as f32 + size.x as f32) / self.size.x as f32;
|
|
||||||
let p0y = offset.y as f32 / self.size.y as f32;
|
|
||||||
let p1y = (offset.y as f32 + size.y as f32) / self.size.y as f32;
|
|
||||||
Some(Corners {
|
|
||||||
top_left: vec2(p0x, p0y),
|
|
||||||
top_right: vec2(p1x, p0y),
|
|
||||||
bottom_left: vec2(p0x, p1y),
|
|
||||||
bottom_right: vec2(p1x, p1y),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextureAtlas {
|
impl Default for TextureAtlas {
|
||||||
|
|
|
@ -211,7 +211,7 @@ impl UiDrawCall {
|
||||||
//TODO: fix some corners tris being invisible (but it's already close enough lol)
|
//TODO: fix some corners tris being invisible (but it's already close enough lol)
|
||||||
let rounded_corner_verts = corner.point_count.get() as u32;
|
let rounded_corner_verts = corner.point_count.get() as u32;
|
||||||
for i in 0..rounded_corner_verts {
|
for i in 0..rounded_corner_verts {
|
||||||
let cratio = i as f32 / (rounded_corner_verts - 1) as f32;
|
let cratio = i as f32 / rounded_corner_verts as f32;
|
||||||
let angle = cratio * std::f32::consts::PI * 0.5;
|
let angle = cratio * std::f32::consts::PI * 0.5;
|
||||||
let x = angle.sin();
|
let x = angle.sin();
|
||||||
let y = angle.cos();
|
let y = angle.cos();
|
||||||
|
|
Loading…
Reference in a new issue