Support images with rounded corners (#3257)
* Add `Rect::ZERO` * Add `Rounding::ZERO` * Add `RectShape::new` * Add `Image::rounding` to support images with rounded corners
This commit is contained in:
parent
481f44828c
commit
3c4223c6b1
|
|
@ -555,13 +555,12 @@ impl CollapsingHeader {
|
|||
let visuals = ui.style().interact_selectable(&header_response, selected);
|
||||
|
||||
if ui.visuals().collapsing_header_frame || show_background {
|
||||
ui.painter().add(epaint::RectShape {
|
||||
rect: header_response.rect.expand(visuals.expansion),
|
||||
rounding: visuals.rounding,
|
||||
fill: visuals.weak_bg_fill,
|
||||
stroke: visuals.bg_stroke,
|
||||
// stroke: Default::default(),
|
||||
});
|
||||
ui.painter().add(epaint::RectShape::new(
|
||||
header_response.rect.expand(visuals.expansion),
|
||||
visuals.rounding,
|
||||
visuals.weak_bg_fill,
|
||||
visuals.bg_stroke,
|
||||
));
|
||||
}
|
||||
|
||||
if selected || selectable && (header_response.hovered() || header_response.has_focus())
|
||||
|
|
|
|||
|
|
@ -383,12 +383,12 @@ fn button_frame(
|
|||
|
||||
ui.painter().set(
|
||||
where_to_put_background,
|
||||
epaint::RectShape {
|
||||
rect: outer_rect.expand(visuals.expansion),
|
||||
rounding: visuals.rounding,
|
||||
fill: visuals.weak_bg_fill,
|
||||
stroke: visuals.bg_stroke,
|
||||
},
|
||||
epaint::RectShape::new(
|
||||
outer_rect.expand(visuals.expansion),
|
||||
visuals.rounding,
|
||||
visuals.weak_bg_fill,
|
||||
visuals.bg_stroke,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -235,12 +235,7 @@ impl Frame {
|
|||
stroke,
|
||||
} = *self;
|
||||
|
||||
let frame_shape = Shape::Rect(epaint::RectShape {
|
||||
rect: outer_rect,
|
||||
rounding,
|
||||
fill,
|
||||
stroke,
|
||||
});
|
||||
let frame_shape = Shape::Rect(epaint::RectShape::new(outer_rect, rounding, fill, stroke));
|
||||
|
||||
if shadow == Default::default() {
|
||||
frame_shape
|
||||
|
|
|
|||
|
|
@ -311,12 +311,7 @@ impl Painter {
|
|||
fill_color: impl Into<Color32>,
|
||||
stroke: impl Into<Stroke>,
|
||||
) {
|
||||
self.add(RectShape {
|
||||
rect,
|
||||
rounding: rounding.into(),
|
||||
fill: fill_color.into(),
|
||||
stroke: stroke.into(),
|
||||
});
|
||||
self.add(RectShape::new(rect, rounding, fill_color, stroke));
|
||||
}
|
||||
|
||||
pub fn rect_filled(
|
||||
|
|
@ -325,12 +320,7 @@ impl Painter {
|
|||
rounding: impl Into<Rounding>,
|
||||
fill_color: impl Into<Color32>,
|
||||
) {
|
||||
self.add(RectShape {
|
||||
rect,
|
||||
rounding: rounding.into(),
|
||||
fill: fill_color.into(),
|
||||
stroke: Default::default(),
|
||||
});
|
||||
self.add(RectShape::filled(rect, rounding, fill_color));
|
||||
}
|
||||
|
||||
pub fn rect_stroke(
|
||||
|
|
@ -339,12 +329,7 @@ impl Painter {
|
|||
rounding: impl Into<Rounding>,
|
||||
stroke: impl Into<Stroke>,
|
||||
) {
|
||||
self.add(RectShape {
|
||||
rect,
|
||||
rounding: rounding.into(),
|
||||
fill: Default::default(),
|
||||
stroke: stroke.into(),
|
||||
});
|
||||
self.add(RectShape::stroke(rect, rounding, stroke));
|
||||
}
|
||||
|
||||
/// Show an arrow starting at `origin` and going in the direction of `vec`, with the length `vec.length()`.
|
||||
|
|
|
|||
|
|
@ -318,12 +318,12 @@ impl<'a> Widget for Checkbox<'a> {
|
|||
// let visuals = ui.style().interact_selectable(&response, *checked); // too colorful
|
||||
let visuals = ui.style().interact(&response);
|
||||
let (small_icon_rect, big_icon_rect) = ui.spacing().icon_rectangles(rect);
|
||||
ui.painter().add(epaint::RectShape {
|
||||
rect: big_icon_rect.expand(visuals.expansion),
|
||||
rounding: visuals.rounding,
|
||||
fill: visuals.bg_fill,
|
||||
stroke: visuals.bg_stroke,
|
||||
});
|
||||
ui.painter().add(epaint::RectShape::new(
|
||||
big_icon_rect.expand(visuals.expansion),
|
||||
visuals.rounding,
|
||||
visuals.bg_fill,
|
||||
visuals.bg_stroke,
|
||||
));
|
||||
|
||||
if *checked {
|
||||
// Check mark:
|
||||
|
|
@ -535,7 +535,7 @@ impl Widget for ImageButton {
|
|||
let selection = ui.visuals().selection;
|
||||
(
|
||||
Vec2::ZERO,
|
||||
Rounding::none(),
|
||||
Rounding::ZERO,
|
||||
selection.bg_fill,
|
||||
selection.stroke,
|
||||
)
|
||||
|
|
@ -552,6 +552,8 @@ impl Widget for ImageButton {
|
|||
Default::default()
|
||||
};
|
||||
|
||||
let image = image.rounding(rounding); // apply rounding to the image
|
||||
|
||||
// Draw frame background (for transparent images):
|
||||
ui.painter()
|
||||
.rect_filled(rect.expand2(expansion), rounding, fill);
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ pub struct Image {
|
|||
tint: Color32,
|
||||
sense: Sense,
|
||||
rotation: Option<(Rot2, Vec2)>,
|
||||
rounding: Rounding,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
|
|
@ -54,6 +55,7 @@ impl Image {
|
|||
tint: Color32::WHITE,
|
||||
sense: Sense::hover(),
|
||||
rotation: None,
|
||||
rounding: Rounding::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,8 +91,26 @@ impl Image {
|
|||
/// Origin is a vector in normalized UV space ((0,0) in top-left, (1,1) bottom right).
|
||||
///
|
||||
/// To rotate about the center you can pass `Vec2::splat(0.5)` as the origin.
|
||||
///
|
||||
/// Due to limitations in the current implementation,
|
||||
/// this will turn off rounding of the image.
|
||||
pub fn rotate(mut self, angle: f32, origin: Vec2) -> Self {
|
||||
self.rotation = Some((Rot2::from_angle(angle), origin));
|
||||
self.rounding = Rounding::ZERO; // incompatible with rotation
|
||||
self
|
||||
}
|
||||
|
||||
/// Round the corners of the image.
|
||||
///
|
||||
/// The default is no rounding ([`Rounding::ZERO`]).
|
||||
///
|
||||
/// Due to limitations in the current implementation,
|
||||
/// this will turn off any rotation of the image.
|
||||
pub fn rounding(mut self, rounding: impl Into<Rounding>) -> Self {
|
||||
self.rounding = rounding.into();
|
||||
if self.rounding != Rounding::ZERO {
|
||||
self.rotation = None; // incompatible with rounding
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
@ -111,6 +131,7 @@ impl Image {
|
|||
tint,
|
||||
sense: _,
|
||||
rotation,
|
||||
rounding,
|
||||
} = self;
|
||||
|
||||
if *bg_fill != Default::default() {
|
||||
|
|
@ -119,14 +140,27 @@ impl Image {
|
|||
ui.painter().add(Shape::mesh(mesh));
|
||||
}
|
||||
|
||||
{
|
||||
// TODO(emilk): builder pattern for Mesh
|
||||
if let Some((rot, origin)) = rotation {
|
||||
// TODO(emilk): implement this using `PathShape` (add texture support to it).
|
||||
// This will also give us anti-aliasing of rotated images.
|
||||
egui_assert!(
|
||||
*rounding == Rounding::ZERO,
|
||||
"Image had both rounding and rotation. Please pick only one"
|
||||
);
|
||||
|
||||
let mut mesh = Mesh::with_texture(*texture_id);
|
||||
mesh.add_rect_with_uv(rect, *uv, *tint);
|
||||
if let Some((rot, origin)) = rotation {
|
||||
mesh.rotate(*rot, rect.min + *origin * *size);
|
||||
}
|
||||
mesh.rotate(*rot, rect.min + *origin * *size);
|
||||
ui.painter().add(Shape::mesh(mesh));
|
||||
} else {
|
||||
ui.painter().add(RectShape {
|
||||
rect,
|
||||
rounding: *rounding,
|
||||
fill: *tint,
|
||||
stroke: Stroke::NONE,
|
||||
fill_texture_id: *texture_id,
|
||||
uv: *uv,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,12 +127,7 @@ impl Bar {
|
|||
};
|
||||
|
||||
let rect = transform.rect_from_values(&self.bounds_min(), &self.bounds_max());
|
||||
let rect = Shape::Rect(RectShape {
|
||||
rect,
|
||||
rounding: Rounding::none(),
|
||||
fill,
|
||||
stroke,
|
||||
});
|
||||
let rect = Shape::Rect(RectShape::new(rect, Rounding::ZERO, fill, stroke));
|
||||
|
||||
shapes.push(rect);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,12 +150,7 @@ impl BoxElem {
|
|||
&self.point_at(self.argument - self.box_width / 2.0, self.spread.quartile1),
|
||||
&self.point_at(self.argument + self.box_width / 2.0, self.spread.quartile3),
|
||||
);
|
||||
let rect = Shape::Rect(RectShape {
|
||||
rect,
|
||||
rounding: Rounding::none(),
|
||||
fill,
|
||||
stroke,
|
||||
});
|
||||
let rect = Shape::Rect(RectShape::new(rect, Rounding::ZERO, fill, stroke));
|
||||
shapes.push(rect);
|
||||
|
||||
let line_between = |v1, v2| {
|
||||
|
|
|
|||
|
|
@ -864,12 +864,14 @@ impl Plot {
|
|||
|
||||
// Background
|
||||
if show_background {
|
||||
ui.painter().with_clip_rect(rect).add(epaint::RectShape {
|
||||
rect,
|
||||
rounding: Rounding::same(2.0),
|
||||
fill: ui.visuals().extreme_bg_color,
|
||||
stroke: ui.visuals().widgets.noninteractive.bg_stroke,
|
||||
});
|
||||
ui.painter()
|
||||
.with_clip_rect(rect)
|
||||
.add(epaint::RectShape::new(
|
||||
rect,
|
||||
Rounding::same(2.0),
|
||||
ui.visuals().extreme_bg_color,
|
||||
ui.visuals().widgets.noninteractive.bg_stroke,
|
||||
));
|
||||
}
|
||||
|
||||
// --- Legend ---
|
||||
|
|
|
|||
|
|
@ -368,31 +368,27 @@ impl<'t> TextEdit<'t> {
|
|||
let frame_rect = frame_rect.expand(visuals.expansion);
|
||||
let shape = if is_mutable {
|
||||
if output.response.has_focus() {
|
||||
epaint::RectShape {
|
||||
rect: frame_rect,
|
||||
rounding: visuals.rounding,
|
||||
// fill: ui.visuals().selection.bg_fill,
|
||||
fill: ui.visuals().extreme_bg_color,
|
||||
stroke: ui.visuals().selection.stroke,
|
||||
}
|
||||
epaint::RectShape::new(
|
||||
frame_rect,
|
||||
visuals.rounding,
|
||||
ui.visuals().extreme_bg_color,
|
||||
ui.visuals().selection.stroke,
|
||||
)
|
||||
} else {
|
||||
epaint::RectShape {
|
||||
rect: frame_rect,
|
||||
rounding: visuals.rounding,
|
||||
fill: ui.visuals().extreme_bg_color,
|
||||
stroke: visuals.bg_stroke, // TODO(emilk): we want to show something here, or a text-edit field doesn't "pop".
|
||||
}
|
||||
epaint::RectShape::new(
|
||||
frame_rect,
|
||||
visuals.rounding,
|
||||
ui.visuals().extreme_bg_color,
|
||||
visuals.bg_stroke, // TODO(emilk): we want to show something here, or a text-edit field doesn't "pop".
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let visuals = &ui.style().visuals.widgets.inactive;
|
||||
epaint::RectShape {
|
||||
rect: frame_rect,
|
||||
rounding: visuals.rounding,
|
||||
// fill: ui.visuals().extreme_bg_color,
|
||||
// fill: visuals.bg_fill,
|
||||
fill: Color32::TRANSPARENT,
|
||||
stroke: visuals.bg_stroke, // TODO(emilk): we want to show something here, or a text-edit field doesn't "pop".
|
||||
}
|
||||
epaint::RectShape::stroke(
|
||||
frame_rect,
|
||||
visuals.rounding,
|
||||
visuals.bg_stroke, // TODO(emilk): we want to show something here, or a text-edit field doesn't "pop".
|
||||
)
|
||||
};
|
||||
|
||||
ui.painter().set(where_to_put_background, shape);
|
||||
|
|
|
|||
|
|
@ -70,12 +70,12 @@ impl FrameHistory {
|
|||
let to_screen = emath::RectTransform::from_to(graph_rect, rect);
|
||||
|
||||
let mut shapes = Vec::with_capacity(3 + 2 * history.len());
|
||||
shapes.push(Shape::Rect(epaint::RectShape {
|
||||
shapes.push(Shape::Rect(epaint::RectShape::new(
|
||||
rect,
|
||||
rounding: style.rounding,
|
||||
fill: ui.visuals().extreme_bg_color,
|
||||
stroke: ui.style().noninteractive().bg_stroke,
|
||||
}));
|
||||
style.rounding,
|
||||
ui.visuals().extreme_bg_color,
|
||||
ui.style().noninteractive().bg_stroke,
|
||||
)));
|
||||
|
||||
let rect = rect.shrink(4.0);
|
||||
let color = ui.visuals().text_color();
|
||||
|
|
|
|||
|
|
@ -64,12 +64,7 @@ pub fn drop_target<R>(
|
|||
|
||||
ui.painter().set(
|
||||
where_to_put_background,
|
||||
epaint::RectShape {
|
||||
rounding: style.rounding,
|
||||
fill,
|
||||
stroke,
|
||||
rect,
|
||||
},
|
||||
epaint::RectShape::new(rect, style.rounding, fill, stroke),
|
||||
);
|
||||
|
||||
InnerResponse::new(ret, response)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ fn main() {
|
|||
|
||||
let mut egui_glium = egui_glium::EguiGlium::new(&display, &event_loop);
|
||||
|
||||
let png_data = include_bytes!("../../../examples/retained_image/src/rust-logo-256x256.png");
|
||||
let png_data = include_bytes!("../../../examples/retained_image/src/crab.png");
|
||||
let image = load_glium_image(png_data);
|
||||
let image_size = egui::vec2(image.width as f32, image.height as f32);
|
||||
// Load to gpu memory
|
||||
|
|
|
|||
|
|
@ -58,6 +58,12 @@ impl Rect {
|
|||
max: pos2(f32::NAN, f32::NAN),
|
||||
};
|
||||
|
||||
/// A [`Rect`] filled with zeroes.
|
||||
pub const ZERO: Self = Self {
|
||||
min: Pos2::ZERO,
|
||||
max: Pos2::ZERO,
|
||||
};
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn from_min_max(min: Pos2, max: Pos2) -> Self {
|
||||
Rect { min, max }
|
||||
|
|
|
|||
|
|
@ -120,13 +120,13 @@ impl Mesh {
|
|||
pub fn append_ref(&mut self, other: &Mesh) {
|
||||
crate::epaint_assert!(other.is_valid());
|
||||
|
||||
if !self.is_empty() {
|
||||
if self.is_empty() {
|
||||
self.texture_id = other.texture_id;
|
||||
} else {
|
||||
assert_eq!(
|
||||
self.texture_id, other.texture_id,
|
||||
"Can't merge Mesh using different textures"
|
||||
);
|
||||
} else {
|
||||
self.texture_id = other.texture_id;
|
||||
}
|
||||
|
||||
let index_offset = self.vertices.len() as u32;
|
||||
|
|
|
|||
|
|
@ -282,6 +282,8 @@ impl Shape {
|
|||
pub fn texture_id(&self) -> super::TextureId {
|
||||
if let Shape::Mesh(mesh) = self {
|
||||
mesh.texture_id
|
||||
} else if let Shape::Rect(rect_shape) = self {
|
||||
rect_shape.fill_texture_id
|
||||
} else {
|
||||
super::TextureId::default()
|
||||
}
|
||||
|
|
@ -406,6 +408,8 @@ pub struct PathShape {
|
|||
|
||||
/// Color and thickness of the line.
|
||||
pub stroke: Stroke,
|
||||
// TODO(emilk): Add texture support either by supplying uv for each point,
|
||||
// or by some transform from points to uv (e.g. a callback or a linear transform matrix).
|
||||
}
|
||||
|
||||
impl PathShape {
|
||||
|
|
@ -476,7 +480,7 @@ impl From<PathShape> for Shape {
|
|||
pub struct RectShape {
|
||||
pub rect: Rect,
|
||||
|
||||
/// How rounded the corners are. Use `Rounding::none()` for no rounding.
|
||||
/// How rounded the corners are. Use `Rounding::ZERO` for no rounding.
|
||||
pub rounding: Rounding,
|
||||
|
||||
/// How to fill the rectangle.
|
||||
|
|
@ -484,9 +488,37 @@ pub struct RectShape {
|
|||
|
||||
/// The thickness and color of the outline.
|
||||
pub stroke: Stroke,
|
||||
|
||||
/// If the rect should be filled with a texture, which one?
|
||||
///
|
||||
/// The texture is multiplied with [`Self::fill`].
|
||||
pub fill_texture_id: TextureId,
|
||||
|
||||
/// What UV coordinates to use for the texture?
|
||||
///
|
||||
/// To display a texture, set [`Self::fill_texture_id`],
|
||||
/// and set this to `Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0))`.
|
||||
pub uv: Rect,
|
||||
}
|
||||
|
||||
impl RectShape {
|
||||
#[inline]
|
||||
pub fn new(
|
||||
rect: Rect,
|
||||
rounding: impl Into<Rounding>,
|
||||
fill_color: impl Into<Color32>,
|
||||
stroke: impl Into<Stroke>,
|
||||
) -> Self {
|
||||
Self {
|
||||
rect,
|
||||
rounding: rounding.into(),
|
||||
fill: fill_color.into(),
|
||||
stroke: stroke.into(),
|
||||
fill_texture_id: Default::default(),
|
||||
uv: Rect::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn filled(
|
||||
rect: Rect,
|
||||
|
|
@ -498,6 +530,8 @@ impl RectShape {
|
|||
rounding: rounding.into(),
|
||||
fill: fill_color.into(),
|
||||
stroke: Default::default(),
|
||||
fill_texture_id: Default::default(),
|
||||
uv: Rect::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -508,6 +542,8 @@ impl RectShape {
|
|||
rounding: rounding.into(),
|
||||
fill: Default::default(),
|
||||
stroke: stroke.into(),
|
||||
fill_texture_id: Default::default(),
|
||||
uv: Rect::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -549,7 +585,7 @@ pub struct Rounding {
|
|||
impl Default for Rounding {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::none()
|
||||
Self::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -566,6 +602,14 @@ impl From<f32> for Rounding {
|
|||
}
|
||||
|
||||
impl Rounding {
|
||||
/// No rounding on any corner.
|
||||
pub const ZERO: Self = Self {
|
||||
nw: 0.0,
|
||||
ne: 0.0,
|
||||
sw: 0.0,
|
||||
se: 0.0,
|
||||
};
|
||||
|
||||
#[inline]
|
||||
pub fn same(radius: f32) -> Self {
|
||||
Self {
|
||||
|
|
@ -577,6 +621,7 @@ impl Rounding {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[deprecated = "Use Rounding::ZERO"]
|
||||
pub fn none() -> Self {
|
||||
Self {
|
||||
nw: 0.0,
|
||||
|
|
|
|||
|
|
@ -492,6 +492,20 @@ impl Path {
|
|||
pub fn fill(&mut self, feathering: f32, color: Color32, out: &mut Mesh) {
|
||||
fill_closed_path(feathering, &mut self.0, color, out);
|
||||
}
|
||||
|
||||
/// Like [`Self::fill`] but with texturing.
|
||||
///
|
||||
/// The `uv_from_pos` is called for each vertex position.
|
||||
pub fn fill_with_uv(
|
||||
&mut self,
|
||||
feathering: f32,
|
||||
color: Color32,
|
||||
texture_id: TextureId,
|
||||
uv_from_pos: impl Fn(Pos2) -> Pos2,
|
||||
out: &mut Mesh,
|
||||
) {
|
||||
fill_closed_path_with_uv(feathering, &mut self.0, color, texture_id, uv_from_pos, out);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod path {
|
||||
|
|
@ -508,7 +522,7 @@ pub mod path {
|
|||
|
||||
let r = clamp_radius(rounding, rect);
|
||||
|
||||
if r == Rounding::none() {
|
||||
if r == Rounding::ZERO {
|
||||
let min = rect.min;
|
||||
let max = rect.max;
|
||||
path.reserve(4);
|
||||
|
|
@ -728,6 +742,89 @@ fn fill_closed_path(feathering: f32, path: &mut [PathPoint], color: Color32, out
|
|||
}
|
||||
}
|
||||
|
||||
/// Like [`fill_closed_path`] but with texturing.
|
||||
///
|
||||
/// The `uv_from_pos` is called for each vertex position.
|
||||
fn fill_closed_path_with_uv(
|
||||
feathering: f32,
|
||||
path: &mut [PathPoint],
|
||||
color: Color32,
|
||||
texture_id: TextureId,
|
||||
uv_from_pos: impl Fn(Pos2) -> Pos2,
|
||||
out: &mut Mesh,
|
||||
) {
|
||||
if color == Color32::TRANSPARENT {
|
||||
return;
|
||||
}
|
||||
|
||||
if out.is_empty() {
|
||||
out.texture_id = texture_id;
|
||||
} else {
|
||||
assert_eq!(
|
||||
out.texture_id, texture_id,
|
||||
"Mixing different `texture_id` in the same "
|
||||
);
|
||||
}
|
||||
|
||||
let n = path.len() as u32;
|
||||
if feathering > 0.0 {
|
||||
if cw_signed_area(path) < 0.0 {
|
||||
// Wrong winding order - fix:
|
||||
path.reverse();
|
||||
for point in path.iter_mut() {
|
||||
point.normal = -point.normal;
|
||||
}
|
||||
}
|
||||
|
||||
out.reserve_triangles(3 * n as usize);
|
||||
out.reserve_vertices(2 * n as usize);
|
||||
let color_outer = Color32::TRANSPARENT;
|
||||
let idx_inner = out.vertices.len() as u32;
|
||||
let idx_outer = idx_inner + 1;
|
||||
|
||||
// The fill:
|
||||
for i in 2..n {
|
||||
out.add_triangle(idx_inner + 2 * (i - 1), idx_inner, idx_inner + 2 * i);
|
||||
}
|
||||
|
||||
// The feathering:
|
||||
let mut i0 = n - 1;
|
||||
for i1 in 0..n {
|
||||
let p1 = &path[i1 as usize];
|
||||
let dm = 0.5 * feathering * p1.normal;
|
||||
|
||||
let pos = p1.pos - dm;
|
||||
out.vertices.push(Vertex {
|
||||
pos,
|
||||
uv: uv_from_pos(pos),
|
||||
color,
|
||||
});
|
||||
|
||||
let pos = p1.pos + dm;
|
||||
out.vertices.push(Vertex {
|
||||
pos,
|
||||
uv: uv_from_pos(pos),
|
||||
color: color_outer,
|
||||
});
|
||||
|
||||
out.add_triangle(idx_inner + i1 * 2, idx_inner + i0 * 2, idx_outer + 2 * i0);
|
||||
out.add_triangle(idx_outer + i0 * 2, idx_outer + i1 * 2, idx_inner + 2 * i1);
|
||||
i0 = i1;
|
||||
}
|
||||
} else {
|
||||
out.reserve_triangles(n as usize);
|
||||
let idx = out.vertices.len() as u32;
|
||||
out.vertices.extend(path.iter().map(|p| Vertex {
|
||||
pos: p.pos,
|
||||
uv: uv_from_pos(p.pos),
|
||||
color,
|
||||
}));
|
||||
for i in 2..n {
|
||||
out.add_triangle(idx, idx + i - 1, idx + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tessellate the given path as a stroke with thickness.
|
||||
fn stroke_path(
|
||||
feathering: f32,
|
||||
|
|
@ -1304,6 +1401,8 @@ impl Tessellator {
|
|||
rounding,
|
||||
fill,
|
||||
stroke,
|
||||
fill_texture_id,
|
||||
uv,
|
||||
} = *rect;
|
||||
|
||||
if self.options.coarse_tessellation_culling
|
||||
|
|
@ -1345,7 +1444,21 @@ impl Tessellator {
|
|||
path.clear();
|
||||
path::rounded_rectangle(&mut self.scratchpad_points, rect, rounding);
|
||||
path.add_line_loop(&self.scratchpad_points);
|
||||
path.fill(self.feathering, fill, out);
|
||||
|
||||
if uv.is_positive() {
|
||||
// Textured
|
||||
let uv_from_pos = |p: Pos2| {
|
||||
pos2(
|
||||
remap(p.x, rect.x_range(), uv.x_range()),
|
||||
remap(p.y, rect.y_range(), uv.y_range()),
|
||||
)
|
||||
};
|
||||
path.fill_with_uv(self.feathering, fill, fill_texture_id, uv_from_pos, out);
|
||||
} else {
|
||||
// Untextured
|
||||
path.fill(self.feathering, fill, out);
|
||||
}
|
||||
|
||||
path.stroke_closed(self.feathering, stroke, out);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
|
|
@ -6,7 +6,7 @@ use egui_extras::RetainedImage;
|
|||
fn main() -> Result<(), eframe::Error> {
|
||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||
let options = eframe::NativeOptions {
|
||||
initial_window_size: Some(egui::vec2(300.0, 900.0)),
|
||||
initial_window_size: Some(egui::vec2(400.0, 1000.0)),
|
||||
..Default::default()
|
||||
};
|
||||
eframe::run_native(
|
||||
|
|
@ -18,48 +18,66 @@ fn main() -> Result<(), eframe::Error> {
|
|||
|
||||
struct MyApp {
|
||||
image: RetainedImage,
|
||||
rounding: f32,
|
||||
tint: egui::Color32,
|
||||
}
|
||||
|
||||
impl Default for MyApp {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
image: RetainedImage::from_image_bytes(
|
||||
"rust-logo-256x256.png",
|
||||
include_bytes!("rust-logo-256x256.png"),
|
||||
)
|
||||
.unwrap(),
|
||||
tint: egui::Color32::from_rgb(255, 0, 255),
|
||||
// crab image is CC0, found on https://stocksnap.io/search/crab
|
||||
image: RetainedImage::from_image_bytes("crab.png", include_bytes!("crab.png")).unwrap(),
|
||||
rounding: 32.0,
|
||||
tint: egui::Color32::from_rgb(100, 200, 200),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for MyApp {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
let Self {
|
||||
image,
|
||||
rounding,
|
||||
tint,
|
||||
} = self;
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("This is an image:");
|
||||
self.image.show(ui);
|
||||
image.show(ui);
|
||||
|
||||
ui.heading("This is a rotated image with a tint:");
|
||||
ui.add_space(32.0);
|
||||
|
||||
ui.heading("This is a tinted image with rounded corners:");
|
||||
ui.add(
|
||||
egui::Image::new(self.image.texture_id(ctx), self.image.size_vec2())
|
||||
.rotate(45.0_f32.to_radians(), egui::Vec2::splat(0.5))
|
||||
.tint(self.tint),
|
||||
egui::Image::new(image.texture_id(ctx), image.size_vec2())
|
||||
.tint(*tint)
|
||||
.rounding(*rounding),
|
||||
);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Tint:");
|
||||
egui::color_picker::color_edit_button_srgba(
|
||||
ui,
|
||||
&mut self.tint,
|
||||
tint,
|
||||
egui::color_picker::Alpha::BlendOrAdditive,
|
||||
);
|
||||
|
||||
ui.add_space(16.0);
|
||||
|
||||
ui.label("Rounding:");
|
||||
ui.add(
|
||||
egui::DragValue::new(rounding)
|
||||
.speed(1.0)
|
||||
.clamp_range(0.0..=0.5 * image.size_vec2().min_elem()),
|
||||
);
|
||||
});
|
||||
|
||||
ui.add_space(32.0);
|
||||
|
||||
ui.heading("This is an image you can click:");
|
||||
ui.add(egui::ImageButton::new(
|
||||
self.image.texture_id(ctx),
|
||||
self.image.size_vec2(),
|
||||
image.texture_id(ctx),
|
||||
image.size_vec2(),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 33 KiB |
|
|
@ -1 +0,0 @@
|
|||
Rust logo by Mozilla, from https://github.com/rust-lang/rust-artwork
|
||||
Loading…
Reference in New Issue