mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-25 16:38:42 -06:00
add 9 patch rendering
This commit is contained in:
parent
c0af88fee8
commit
19ca54b1f3
|
@ -14,5 +14,6 @@ glium = "0.34"
|
||||||
winit = "0.29"
|
winit = "0.29"
|
||||||
glam = "0.27"
|
glam = "0.27"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
image = { version = "0.25", features = ["jpeg", "png"] }
|
||||||
|
|
||||||
#created as a workaround for rust-analyzer dependency cycle (which should be allowed)
|
#created as a workaround for rust-analyzer dependency cycle (which should be allowed)
|
||||||
|
|
BIN
hui-examples/assets/ninepatch_button.png
Normal file
BIN
hui-examples/assets/ninepatch_button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 638 B |
|
@ -1,10 +1,9 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use glam::vec2;
|
||||||
use hui::{
|
use hui::{
|
||||||
color, element::{
|
color, element::{
|
||||||
container::Container,
|
container::Container, fill_rect::FillRect, text::Text, UiElementExt
|
||||||
fill_rect::FillRect,
|
}, frame::nine_patch::{NinePatchAsset, NinePatchFrame}, frame_rect, layout::{Alignment, Direction}, rect::Rect, size
|
||||||
UiElementExt
|
|
||||||
}, frame_rect, layout::{Alignment, Direction}, size
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[path = "../boilerplate.rs"]
|
#[path = "../boilerplate.rs"]
|
||||||
|
@ -13,21 +12,50 @@ mod boilerplate;
|
||||||
|
|
||||||
ui_main!(
|
ui_main!(
|
||||||
"hUI: 9-Patch demo",
|
"hUI: 9-Patch demo",
|
||||||
init: |_| {
|
init: |ui| {
|
||||||
|
NinePatchAsset {
|
||||||
|
image: ui.add_image_file_path("./hui-examples/assets/ninepatch_button.png").unwrap(),
|
||||||
|
size: (190, 49),
|
||||||
|
scalable_region: Rect {
|
||||||
|
position: vec2(8. / 190., 8. / 49.),
|
||||||
|
size: vec2(1. - 16. / 190., 1. - 18. / 49.),
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
run: |ui, size, _| {
|
run: |ui, size, asset| {
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(100%))
|
.with_size(size!(100%))
|
||||||
.with_align(Alignment::Center)
|
.with_align(Alignment::Center)
|
||||||
|
.with_gap(5.)
|
||||||
.with_background(color::WHITE)
|
.with_background(color::WHITE)
|
||||||
.with_children(|ui| {
|
.with_children(|ui| {
|
||||||
FillRect::default()
|
Container::default()
|
||||||
.with_size(size!(300, 100))
|
.with_size(size!(300, 100))
|
||||||
.with_frame(frame_rect! {
|
.with_background(NinePatchFrame::from_asset(*asset).with_color(color::RED))
|
||||||
color: color::RED
|
.with_padding(10.)
|
||||||
|
.with_children(|ui| {
|
||||||
|
Text::new("Hello, world!\nThis is a 9-patch frame used as a background \nfor Container with a Text element.\nIt's scalable and looks great!\nBelow, there are two FillRects with the same \n9-patch frame used as the background.")
|
||||||
|
.with_text_size(16)
|
||||||
|
.add_child(ui);
|
||||||
})
|
})
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
|
FillRect::default()
|
||||||
|
.with_size(size!(600, 75))
|
||||||
|
.with_frame(NinePatchFrame::from_asset(*asset).with_color(color::GREEN))
|
||||||
|
.add_child(ui);
|
||||||
|
Text::new("This one's fancy:")
|
||||||
|
.with_color(color::BLACK)
|
||||||
|
.with_text_size(32)
|
||||||
|
.add_child(ui);
|
||||||
|
FillRect::default()
|
||||||
|
.with_size(size!(800, 50))
|
||||||
|
.with_frame(NinePatchFrame::from_asset(*asset).with_color((
|
||||||
|
(1., 0., 1.),
|
||||||
|
(0., 1., 1.),
|
||||||
|
(1., 1., 0.),
|
||||||
|
(0., 0., 1.),
|
||||||
|
)))
|
||||||
|
.add_child(ui);
|
||||||
})
|
})
|
||||||
.add_root(ui, size);
|
.add_root(ui, size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ pub enum UiDrawCommand {
|
||||||
color: Corners<Vec4>,
|
color: Corners<Vec4>,
|
||||||
///Texture
|
///Texture
|
||||||
texture: Option<ImageHandle>,
|
texture: Option<ImageHandle>,
|
||||||
|
///Sub-UV coordinates for the texture
|
||||||
|
texture_uv: Option<Corners<Vec2>>,
|
||||||
///Rounded corners
|
///Rounded corners
|
||||||
rounded_corners: Option<RoundedCorners>,
|
rounded_corners: Option<RoundedCorners>,
|
||||||
},
|
},
|
||||||
|
@ -161,10 +163,32 @@ impl UiDrawCall {
|
||||||
v.position += center;
|
v.position += center;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UiDrawCommand::Rectangle { position, size, color, texture, rounded_corners } => {
|
UiDrawCommand::Rectangle { position, size, color, texture, texture_uv, rounded_corners } => {
|
||||||
let uvs = texture
|
let uvs = texture
|
||||||
.map(|x| atlas.get_uv(x))
|
.map(|x| atlas.get_uv(x))
|
||||||
.flatten()
|
.flatten()
|
||||||
|
.map(|guv| {
|
||||||
|
if let Some(texture_uv) = texture_uv {
|
||||||
|
//XXX: this assumes that it's not rotated :p
|
||||||
|
//hell will break loose if it is
|
||||||
|
//seriously, i fvcking despise this code, and i hope to never touch this file ever again
|
||||||
|
//FIXME: this is only valid if top_left is acutally the min (e.g. only for rectangular crops)
|
||||||
|
//We currently only need rectangular crops so i don't give a fvck
|
||||||
|
let uv_size = guv.bottom_right - guv.top_left;
|
||||||
|
let mut uv_mapped = *texture_uv;
|
||||||
|
uv_mapped.top_left *= uv_size;
|
||||||
|
uv_mapped.top_right *= uv_size;
|
||||||
|
uv_mapped.bottom_left *= uv_size;
|
||||||
|
uv_mapped.bottom_right *= uv_size;
|
||||||
|
uv_mapped.top_left += guv.top_left;
|
||||||
|
uv_mapped.top_right += guv.top_left;
|
||||||
|
uv_mapped.bottom_left += guv.top_left;
|
||||||
|
uv_mapped.bottom_right += guv.top_left;
|
||||||
|
uv_mapped
|
||||||
|
} else {
|
||||||
|
guv
|
||||||
|
}
|
||||||
|
})
|
||||||
.unwrap_or(Corners::all(Vec2::ZERO));
|
.unwrap_or(Corners::all(Vec2::ZERO));
|
||||||
|
|
||||||
let vidx = draw_call.vertices.len() as u32;
|
let vidx = draw_call.vertices.len() as u32;
|
||||||
|
|
|
@ -79,6 +79,7 @@ impl UiElement for Image {
|
||||||
size: ctx.measure.size,
|
size: ctx.measure.size,
|
||||||
color: self.color.corners(),
|
color: self.color.corners(),
|
||||||
texture: Some(self.image),
|
texture: Some(self.image),
|
||||||
|
texture_uv: None,
|
||||||
rounded_corners: (self.corner_radius.max_f32() > 0.).then_some({
|
rounded_corners: (self.corner_radius.max_f32() > 0.).then_some({
|
||||||
RoundedCorners::from_radius(self.corner_radius)
|
RoundedCorners::from_radius(self.corner_radius)
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -87,6 +87,7 @@ impl UiElement for ProgressBar {
|
||||||
size: ctx.measure.size,
|
size: ctx.measure.size,
|
||||||
color: self.background.corners(),
|
color: self.background.corners(),
|
||||||
texture: None,
|
texture: None,
|
||||||
|
texture_uv: None,
|
||||||
rounded_corners
|
rounded_corners
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -96,6 +97,7 @@ impl UiElement for ProgressBar {
|
||||||
size: ctx.measure.size * vec2(value, 1.0),
|
size: ctx.measure.size * vec2(value, 1.0),
|
||||||
color: self.foreground.corners(),
|
color: self.foreground.corners(),
|
||||||
texture: None,
|
texture: None,
|
||||||
|
texture_uv: None,
|
||||||
rounded_corners,
|
rounded_corners,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::draw::UiDrawCommandList;
|
||||||
pub mod point;
|
pub mod point;
|
||||||
mod rect;
|
mod rect;
|
||||||
pub mod stack;
|
pub mod stack;
|
||||||
|
pub mod nine_patch;
|
||||||
mod impls;
|
mod impls;
|
||||||
|
|
||||||
pub use rect::FrameRect;
|
pub use rect::FrameRect;
|
||||||
|
|
|
@ -13,6 +13,7 @@ impl Frame for ImageHandle {
|
||||||
size: parent_size,
|
size: parent_size,
|
||||||
color: color::WHITE.into(),
|
color: color::WHITE.into(),
|
||||||
texture: Some(*self),
|
texture: Some(*self),
|
||||||
|
texture_uv: None,
|
||||||
rounded_corners: None,
|
rounded_corners: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -29,6 +30,7 @@ impl Frame for FillColor {
|
||||||
size: parent_size,
|
size: parent_size,
|
||||||
color: self.corners(),
|
color: self.corners(),
|
||||||
texture: None,
|
texture: None,
|
||||||
|
texture_uv: None,
|
||||||
rounded_corners: None,
|
rounded_corners: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
224
hui/src/frame/nine_patch.rs
Normal file
224
hui/src/frame/nine_patch.rs
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
use glam::{vec2, UVec2, Vec2, Vec4};
|
||||||
|
use crate::{color, draw::{ImageHandle, UiDrawCommand}, rect::{Corners, FillColor, Rect}};
|
||||||
|
use super::Frame;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct NinePatchAsset {
|
||||||
|
pub image: ImageHandle,
|
||||||
|
//TODO: remove this
|
||||||
|
pub size: (u32, u32),
|
||||||
|
pub scalable_region: Rect,
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO allow scaling/moving corners
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct NinePatchFrame {
|
||||||
|
pub asset: NinePatchAsset,
|
||||||
|
pub color: FillColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NinePatchFrame {
|
||||||
|
pub fn from_asset(asset: NinePatchAsset) -> Self {
|
||||||
|
Self { asset, ..Default::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_color(mut self, color: impl Into<FillColor>) -> Self {
|
||||||
|
self.color = color.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NinePatchFrame {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
//This is not supposed to be left out as the default, so just set it to whatever :p
|
||||||
|
asset: NinePatchAsset { image: ImageHandle::default(), size: (0, 0), scalable_region: Rect::default() },
|
||||||
|
color: color::WHITE.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Frame for NinePatchFrame {
|
||||||
|
fn draw(&self, draw: &mut crate::draw::UiDrawCommandList, position: glam::Vec2, parent_size: glam::Vec2) {
|
||||||
|
// without this, shїt gets messed up when the position is not a whole number
|
||||||
|
//XXX: should we round the size as well?
|
||||||
|
let position = position.round();
|
||||||
|
|
||||||
|
let img_sz = UVec2::from(self.asset.size).as_vec2();
|
||||||
|
|
||||||
|
//Color stuff
|
||||||
|
let interpolate_color_rect = |uvs: Corners<Vec2>| {
|
||||||
|
Corners {
|
||||||
|
top_left: self.color.interpolate(uvs.top_left),
|
||||||
|
top_right: self.color.interpolate(uvs.top_right),
|
||||||
|
bottom_left: self.color.interpolate(uvs.bottom_left),
|
||||||
|
bottom_right: self.color.interpolate(uvs.bottom_right),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Inset coords, in UV space
|
||||||
|
let region_uv = self.asset.scalable_region.corners();
|
||||||
|
|
||||||
|
// Inset coords, in image (px) space
|
||||||
|
let corners_image_px = Corners {
|
||||||
|
top_left: img_sz * region_uv.top_left,
|
||||||
|
top_right: img_sz * region_uv.top_right,
|
||||||
|
bottom_left: img_sz * region_uv.bottom_left,
|
||||||
|
bottom_right: img_sz * region_uv.bottom_right,
|
||||||
|
};
|
||||||
|
|
||||||
|
let size_h = (
|
||||||
|
corners_image_px.top_left.x,
|
||||||
|
parent_size.x - corners_image_px.top_left.x - (img_sz.x - corners_image_px.top_right.x),
|
||||||
|
img_sz.x - corners_image_px.top_right.x,
|
||||||
|
);
|
||||||
|
|
||||||
|
let size_v = (
|
||||||
|
corners_image_px.top_left.y,
|
||||||
|
parent_size.y - corners_image_px.top_left.y - (img_sz.y - corners_image_px.bottom_left.y),
|
||||||
|
img_sz.y - corners_image_px.bottom_left.y,
|
||||||
|
);
|
||||||
|
|
||||||
|
//Top-left patch
|
||||||
|
let top_left_patch_uv = Corners {
|
||||||
|
top_left: vec2(0., 0.),
|
||||||
|
top_right: vec2(region_uv.top_left.x, 0.),
|
||||||
|
bottom_left: vec2(0., region_uv.top_left.y),
|
||||||
|
bottom_right: region_uv.top_left,
|
||||||
|
};
|
||||||
|
draw.add(UiDrawCommand::Rectangle {
|
||||||
|
position,
|
||||||
|
size: vec2(size_h.0, size_v.0),
|
||||||
|
color: interpolate_color_rect(top_left_patch_uv),
|
||||||
|
texture: Some(self.asset.image),
|
||||||
|
texture_uv: Some(top_left_patch_uv),
|
||||||
|
rounded_corners: None
|
||||||
|
});
|
||||||
|
|
||||||
|
//Top patch
|
||||||
|
let top_patch_uv = Corners {
|
||||||
|
top_left: vec2(region_uv.top_left.x, 0.),
|
||||||
|
top_right: vec2(region_uv.top_right.x, 0.),
|
||||||
|
bottom_left: region_uv.top_left,
|
||||||
|
bottom_right: region_uv.top_right,
|
||||||
|
};
|
||||||
|
draw.add(UiDrawCommand::Rectangle {
|
||||||
|
position: position + vec2(size_h.0, 0.),
|
||||||
|
size: vec2(size_h.1, size_v.0),
|
||||||
|
color: interpolate_color_rect(top_patch_uv),
|
||||||
|
texture: Some(self.asset.image),
|
||||||
|
texture_uv: Some(top_patch_uv),
|
||||||
|
rounded_corners: None
|
||||||
|
});
|
||||||
|
|
||||||
|
//Top-right patch
|
||||||
|
let top_right_patch_uv = Corners {
|
||||||
|
top_left: vec2(region_uv.top_right.x, 0.),
|
||||||
|
top_right: vec2(1., 0.),
|
||||||
|
bottom_left: region_uv.top_right,
|
||||||
|
bottom_right: vec2(1., region_uv.top_right.y),
|
||||||
|
};
|
||||||
|
draw.add(UiDrawCommand::Rectangle {
|
||||||
|
position: position + vec2(size_h.0 + size_h.1, 0.),
|
||||||
|
size: vec2(size_h.2, size_v.0),
|
||||||
|
color: interpolate_color_rect(top_right_patch_uv),
|
||||||
|
texture: Some(self.asset.image),
|
||||||
|
texture_uv: Some(top_right_patch_uv),
|
||||||
|
rounded_corners: None
|
||||||
|
});
|
||||||
|
|
||||||
|
//Left patch
|
||||||
|
let left_patch_uv = Corners {
|
||||||
|
top_left: vec2(0., region_uv.top_left.y),
|
||||||
|
top_right: region_uv.top_left,
|
||||||
|
bottom_left: vec2(0., region_uv.bottom_left.y),
|
||||||
|
bottom_right: region_uv.bottom_left,
|
||||||
|
};
|
||||||
|
draw.add(UiDrawCommand::Rectangle {
|
||||||
|
position: position + vec2(0., size_v.0),
|
||||||
|
size: vec2(size_h.0, size_v.1),
|
||||||
|
color: interpolate_color_rect(left_patch_uv),
|
||||||
|
texture: Some(self.asset.image),
|
||||||
|
texture_uv: Some(left_patch_uv),
|
||||||
|
rounded_corners: None
|
||||||
|
});
|
||||||
|
|
||||||
|
// Center patch
|
||||||
|
draw.add(UiDrawCommand::Rectangle {
|
||||||
|
position: position + vec2(size_h.0, size_v.0),
|
||||||
|
size: vec2(size_h.1, size_v.1),
|
||||||
|
color: interpolate_color_rect(region_uv),
|
||||||
|
texture: Some(self.asset.image),
|
||||||
|
texture_uv: Some(region_uv),
|
||||||
|
rounded_corners: None
|
||||||
|
});
|
||||||
|
|
||||||
|
//Right patch
|
||||||
|
let right_patch_uv = Corners {
|
||||||
|
top_left: region_uv.top_right,
|
||||||
|
top_right: vec2(1., region_uv.top_right.y),
|
||||||
|
bottom_left: region_uv.bottom_right,
|
||||||
|
bottom_right: vec2(1., region_uv.bottom_right.y),
|
||||||
|
};
|
||||||
|
draw.add(UiDrawCommand::Rectangle {
|
||||||
|
position: position + vec2(size_h.0 + size_h.1, size_v.0),
|
||||||
|
size: vec2(size_h.2, size_v.1),
|
||||||
|
color: interpolate_color_rect(right_patch_uv),
|
||||||
|
texture: Some(self.asset.image),
|
||||||
|
texture_uv: Some(right_patch_uv),
|
||||||
|
rounded_corners: None
|
||||||
|
});
|
||||||
|
|
||||||
|
//Bottom-left patch
|
||||||
|
let bottom_left_patch_uv = Corners {
|
||||||
|
top_left: vec2(0., region_uv.bottom_left.y),
|
||||||
|
top_right: region_uv.bottom_left,
|
||||||
|
bottom_left: vec2(0., 1.),
|
||||||
|
bottom_right: vec2(region_uv.bottom_left.x, 1.),
|
||||||
|
};
|
||||||
|
draw.add(UiDrawCommand::Rectangle {
|
||||||
|
position: position + vec2(0., size_v.0 + size_v.1),
|
||||||
|
size: vec2(size_h.0, size_v.2),
|
||||||
|
color: interpolate_color_rect(bottom_left_patch_uv),
|
||||||
|
texture: Some(self.asset.image),
|
||||||
|
texture_uv: Some(bottom_left_patch_uv),
|
||||||
|
rounded_corners: None
|
||||||
|
});
|
||||||
|
|
||||||
|
//Bottom patch
|
||||||
|
let bottom_patch_uv = Corners {
|
||||||
|
top_left: region_uv.bottom_left,
|
||||||
|
top_right: region_uv.bottom_right,
|
||||||
|
bottom_left: vec2(region_uv.bottom_left.x, 1.),
|
||||||
|
bottom_right: vec2(region_uv.bottom_right.x, 1.),
|
||||||
|
};
|
||||||
|
draw.add(UiDrawCommand::Rectangle {
|
||||||
|
position: position + vec2(size_h.0, size_v.0 + size_v.1),
|
||||||
|
size: vec2(size_h.1, size_v.2),
|
||||||
|
color: interpolate_color_rect(bottom_patch_uv),
|
||||||
|
texture: Some(self.asset.image),
|
||||||
|
texture_uv: Some(bottom_patch_uv),
|
||||||
|
rounded_corners: None
|
||||||
|
});
|
||||||
|
|
||||||
|
//Bottom-right patch
|
||||||
|
let bottom_right_patch_uv = Corners {
|
||||||
|
top_left: region_uv.bottom_right,
|
||||||
|
top_right: vec2(1., region_uv.bottom_right.y),
|
||||||
|
bottom_left: vec2(region_uv.bottom_right.x, 1.),
|
||||||
|
bottom_right: vec2(1., 1.),
|
||||||
|
};
|
||||||
|
draw.add(UiDrawCommand::Rectangle {
|
||||||
|
position: position + vec2(size_h.0 + size_h.1, size_v.0 + size_v.1),
|
||||||
|
size: vec2(size_h.2, size_v.2),
|
||||||
|
color: interpolate_color_rect(bottom_right_patch_uv),
|
||||||
|
texture: Some(self.asset.image),
|
||||||
|
texture_uv: Some(bottom_right_patch_uv),
|
||||||
|
rounded_corners: None
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,7 +95,7 @@ impl UiInstance {
|
||||||
/// (this will change to a soft error in the future)
|
/// (this will change to a soft error in the future)
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
pub fn add_image_file_path(&mut self, path: impl AsRef<std::path::Path>) -> Result<ImageHandle, std::io::Error> {
|
pub fn add_image_file_path(&mut self, path: impl AsRef<std::path::Path>) -> Result<ImageHandle, std::io::Error> {
|
||||||
use std::io::Read;
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
// Open the file (and wrap it in a bufreader)
|
// Open the file (and wrap it in a bufreader)
|
||||||
let mut file = std::io::BufReader::new(std::fs::File::open(path)?);
|
let mut file = std::io::BufReader::new(std::fs::File::open(path)?);
|
||||||
|
@ -106,6 +106,7 @@ impl UiInstance {
|
||||||
let mut magic = [0; 64];
|
let mut magic = [0; 64];
|
||||||
file.read_exact(&mut magic)?;
|
file.read_exact(&mut magic)?;
|
||||||
let format = image::guess_format(&magic).expect("Invalid image data (FORMAT)");
|
let format = image::guess_format(&magic).expect("Invalid image data (FORMAT)");
|
||||||
|
file.seek(std::io::SeekFrom::Start(0))?;
|
||||||
|
|
||||||
//Parse the image and read the raw uncompressed rgba data
|
//Parse the image and read the raw uncompressed rgba data
|
||||||
let image = image::load(file, format).expect("Invalid image data");
|
let image = image::load(file, format).expect("Invalid image data");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::Corners;
|
use super::Corners;
|
||||||
use glam::{Vec3, Vec4, vec4};
|
use glam::{Vec2, Vec3, Vec4, vec4};
|
||||||
|
|
||||||
/// Represents the fill color of a rectangle
|
/// Represents the fill color of a rectangle
|
||||||
///
|
///
|
||||||
|
@ -69,6 +69,14 @@ impl FillColor {
|
||||||
pub const fn corners(&self) -> Corners<Vec4> {
|
pub const fn corners(&self) -> Corners<Vec4> {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interpolate color on position, assuming a linear gradient
|
||||||
|
pub fn interpolate(&self, uv: Vec2) -> Vec4 {
|
||||||
|
let c = self.corners();
|
||||||
|
let top = c.top_left.lerp(c.top_right, uv.x);
|
||||||
|
let bottom = c.bottom_left.lerp(c.bottom_right, uv.x);
|
||||||
|
top.lerp(bottom, uv.y)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FillColor {
|
impl Default for FillColor {
|
||||||
|
|
Loading…
Reference in a new issue