Add `epaint::Brush` for controlling `RectShape` texturing (#5565)
Also wraps `Shape::Mesh` in an `Arc`. No new features, but decreases size of `Shape` from 72 bytes to 64.
This commit is contained in:
parent
72ac2113dd
commit
aeea70d9e7
|
|
@ -9,7 +9,7 @@ use epaint::{
|
||||||
use crate::{
|
use crate::{
|
||||||
load::{Bytes, SizeHint, SizedTexture, TextureLoadResult, TexturePoll},
|
load::{Bytes, SizeHint, SizedTexture, TextureLoadResult, TexturePoll},
|
||||||
pos2, Color32, Context, Id, Mesh, Painter, Rect, Response, Rounding, Sense, Shape, Spinner,
|
pos2, Color32, Context, Id, Mesh, Painter, Rect, Response, Rounding, Sense, Shape, Spinner,
|
||||||
Stroke, TextStyle, TextureOptions, Ui, Vec2, Widget, WidgetInfo, WidgetType,
|
TextStyle, TextureOptions, Ui, Vec2, Widget, WidgetInfo, WidgetType,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A widget which displays an image.
|
/// A widget which displays an image.
|
||||||
|
|
@ -822,15 +822,10 @@ pub fn paint_texture_at(
|
||||||
painter.add(Shape::mesh(mesh));
|
painter.add(Shape::mesh(mesh));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
painter.add(RectShape {
|
painter.add(
|
||||||
rect,
|
RectShape::filled(rect, options.rounding, options.tint)
|
||||||
rounding: options.rounding,
|
.with_texture(texture.id, options.uv),
|
||||||
fill: options.tint,
|
);
|
||||||
stroke: Stroke::NONE,
|
|
||||||
blur_width: 0.0,
|
|
||||||
fill_texture_id: texture.id,
|
|
||||||
uv: options.uv,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::{Rect, TextureId};
|
||||||
|
|
||||||
|
/// Controls texturing of a [`crate::RectShape`].
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct Brush {
|
||||||
|
/// If the rect should be filled with a texture, which one?
|
||||||
|
///
|
||||||
|
/// The texture is multiplied with [`crate::RectShape::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))`.
|
||||||
|
///
|
||||||
|
/// Use [`Rect::ZERO`] to turn off texturing.
|
||||||
|
pub uv: Rect,
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
#![allow(clippy::manual_range_contains)]
|
#![allow(clippy::manual_range_contains)]
|
||||||
|
|
||||||
|
mod brush;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
mod margin;
|
mod margin;
|
||||||
|
|
@ -44,6 +45,7 @@ pub mod util;
|
||||||
mod viewport;
|
mod viewport;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
|
brush::Brush,
|
||||||
color::ColorMode,
|
color::ColorMode,
|
||||||
image::{ColorImage, FontImage, ImageData, ImageDelta},
|
image::{ColorImage, FontImage, ImageData, ImageDelta},
|
||||||
margin::Margin,
|
margin::Margin,
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,7 @@ pub fn adjust_colors(
|
||||||
fill,
|
fill,
|
||||||
stroke,
|
stroke,
|
||||||
blur_width: _,
|
blur_width: _,
|
||||||
fill_texture_id: _,
|
brush: _,
|
||||||
uv: _,
|
|
||||||
}) => {
|
}) => {
|
||||||
adjust_color(fill);
|
adjust_color(fill);
|
||||||
adjust_color(&mut stroke.color);
|
adjust_color(&mut stroke.color);
|
||||||
|
|
@ -87,7 +86,7 @@ pub fn adjust_colors(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !galley.is_empty() {
|
if !galley.is_empty() {
|
||||||
let galley = std::sync::Arc::make_mut(galley);
|
let galley = Arc::make_mut(galley);
|
||||||
for row in &mut galley.rows {
|
for row in &mut galley.rows {
|
||||||
for vertex in &mut row.visuals.mesh.vertices {
|
for vertex in &mut row.visuals.mesh.vertices {
|
||||||
adjust_color(&mut vertex.color);
|
adjust_color(&mut vertex.color);
|
||||||
|
|
@ -96,11 +95,13 @@ pub fn adjust_colors(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape::Mesh(Mesh {
|
Shape::Mesh(mesh) => {
|
||||||
indices: _,
|
let Mesh {
|
||||||
vertices,
|
indices: _,
|
||||||
texture_id: _,
|
vertices,
|
||||||
}) => {
|
texture_id: _,
|
||||||
|
} = Arc::make_mut(mesh);
|
||||||
|
|
||||||
for v in vertices {
|
for v in vertices {
|
||||||
adjust_color(&mut v.color);
|
adjust_color(&mut v.color);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// How to paint a rectangle.
|
/// How to paint a rectangle.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct RectShape {
|
pub struct RectShape {
|
||||||
pub rect: Rect,
|
pub rect: Rect,
|
||||||
|
|
@ -28,18 +30,23 @@ pub struct RectShape {
|
||||||
/// The blur is currently implemented using a simple linear blur in sRGBA gamma space.
|
/// The blur is currently implemented using a simple linear blur in sRGBA gamma space.
|
||||||
pub blur_width: f32,
|
pub blur_width: f32,
|
||||||
|
|
||||||
/// If the rect should be filled with a texture, which one?
|
/// Controls texturing, if any.
|
||||||
///
|
///
|
||||||
/// The texture is multiplied with [`Self::fill`].
|
/// Since most rectangles do not have a texture, this is optional and in an `Arc`,
|
||||||
pub fill_texture_id: TextureId,
|
/// so that [`RectShape`] is kept small..
|
||||||
|
pub brush: Option<Arc<Brush>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// What UV coordinates to use for the texture?
|
#[test]
|
||||||
///
|
fn rect_shape_size() {
|
||||||
/// To display a texture, set [`Self::fill_texture_id`],
|
assert_eq!(
|
||||||
/// and set this to `Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0))`.
|
std::mem::size_of::<RectShape>(), 48,
|
||||||
///
|
"RectShape changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it."
|
||||||
/// Use [`Rect::ZERO`] to turn off texturing.
|
);
|
||||||
pub uv: Rect,
|
assert!(
|
||||||
|
std::mem::size_of::<RectShape>() <= 64,
|
||||||
|
"RectShape is getting way too big!"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RectShape {
|
impl RectShape {
|
||||||
|
|
@ -57,8 +64,7 @@ impl RectShape {
|
||||||
fill: fill_color.into(),
|
fill: fill_color.into(),
|
||||||
stroke: stroke.into(),
|
stroke: stroke.into(),
|
||||||
blur_width: 0.0,
|
blur_width: 0.0,
|
||||||
fill_texture_id: Default::default(),
|
brush: Default::default(),
|
||||||
uv: Rect::ZERO,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,29 +74,14 @@ impl RectShape {
|
||||||
rounding: impl Into<Rounding>,
|
rounding: impl Into<Rounding>,
|
||||||
fill_color: impl Into<Color32>,
|
fill_color: impl Into<Color32>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self::new(rect, rounding, fill_color, Stroke::NONE)
|
||||||
rect,
|
|
||||||
rounding: rounding.into(),
|
|
||||||
fill: fill_color.into(),
|
|
||||||
stroke: Default::default(),
|
|
||||||
blur_width: 0.0,
|
|
||||||
fill_texture_id: Default::default(),
|
|
||||||
uv: Rect::ZERO,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The stroke extends _outside_ the [`Rect`].
|
/// The stroke extends _outside_ the [`Rect`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stroke(rect: Rect, rounding: impl Into<Rounding>, stroke: impl Into<Stroke>) -> Self {
|
pub fn stroke(rect: Rect, rounding: impl Into<Rounding>, stroke: impl Into<Stroke>) -> Self {
|
||||||
Self {
|
let fill = Color32::TRANSPARENT;
|
||||||
rect,
|
Self::new(rect, rounding, fill, stroke)
|
||||||
rounding: rounding.into(),
|
|
||||||
fill: Default::default(),
|
|
||||||
stroke: stroke.into(),
|
|
||||||
blur_width: 0.0,
|
|
||||||
fill_texture_id: Default::default(),
|
|
||||||
uv: Rect::ZERO,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If larger than zero, the edges of the rectangle
|
/// If larger than zero, the edges of the rectangle
|
||||||
|
|
@ -105,6 +96,16 @@ impl RectShape {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the texture to use when painting this rectangle, if any.
|
||||||
|
#[inline]
|
||||||
|
pub fn with_texture(mut self, fill_texture_id: TextureId, uv: Rect) -> Self {
|
||||||
|
self.brush = Some(Arc::new(Brush {
|
||||||
|
fill_texture_id,
|
||||||
|
uv,
|
||||||
|
}));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// The visual bounding rectangle (includes stroke width)
|
/// The visual bounding rectangle (includes stroke width)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn visual_bounding_rect(&self) -> Rect {
|
pub fn visual_bounding_rect(&self) -> Rect {
|
||||||
|
|
@ -115,6 +116,15 @@ impl RectShape {
|
||||||
self.rect.expand(width + self.blur_width / 2.0)
|
self.rect.expand(width + self.blur_width / 2.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The texture to use when painting this rectangle, if any.
|
||||||
|
///
|
||||||
|
/// If no texture is set, this will return [`TextureId::default`].
|
||||||
|
pub fn fill_texture_id(&self) -> TextureId {
|
||||||
|
self.brush
|
||||||
|
.as_ref()
|
||||||
|
.map_or_else(TextureId::default, |brush| brush.fill_texture_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RectShape> for Shape {
|
impl From<RectShape> for Shape {
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,9 @@ pub enum Shape {
|
||||||
/// A general triangle mesh.
|
/// A general triangle mesh.
|
||||||
///
|
///
|
||||||
/// Can be used to display images.
|
/// Can be used to display images.
|
||||||
Mesh(Mesh),
|
///
|
||||||
|
/// Wrapped in an [`Arc`] to minimize the size of [`Shape`].
|
||||||
|
Mesh(Arc<Mesh>),
|
||||||
|
|
||||||
/// A quadratic [Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
|
/// A quadratic [Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
|
||||||
QuadraticBezier(QuadraticBezierShape),
|
QuadraticBezier(QuadraticBezierShape),
|
||||||
|
|
@ -68,6 +70,18 @@ pub enum Shape {
|
||||||
Callback(PaintCallback),
|
Callback(PaintCallback),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shape_size() {
|
||||||
|
assert_eq!(
|
||||||
|
std::mem::size_of::<Shape>(), 64,
|
||||||
|
"Shape changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it."
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
std::mem::size_of::<Shape>() <= 64,
|
||||||
|
"Shape is getting way too big!"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shape_impl_send_sync() {
|
fn shape_impl_send_sync() {
|
||||||
fn assert_send_sync<T: Send + Sync>() {}
|
fn assert_send_sync<T: Send + Sync>() {}
|
||||||
|
|
@ -84,6 +98,13 @@ impl From<Vec<Self>> for Shape {
|
||||||
impl From<Mesh> for Shape {
|
impl From<Mesh> for Shape {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(mesh: Mesh) -> Self {
|
fn from(mesh: Mesh) -> Self {
|
||||||
|
Self::Mesh(mesh.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Arc<Mesh>> for Shape {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(mesh: Arc<Mesh>) -> Self {
|
||||||
Self::Mesh(mesh)
|
Self::Mesh(mesh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -314,7 +335,8 @@ impl Shape {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn mesh(mesh: Mesh) -> Self {
|
pub fn mesh(mesh: impl Into<Arc<Mesh>>) -> Self {
|
||||||
|
let mesh = mesh.into();
|
||||||
debug_assert!(mesh.is_valid());
|
debug_assert!(mesh.is_valid());
|
||||||
Self::Mesh(mesh)
|
Self::Mesh(mesh)
|
||||||
}
|
}
|
||||||
|
|
@ -369,7 +391,7 @@ impl Shape {
|
||||||
if let Self::Mesh(mesh) = self {
|
if let Self::Mesh(mesh) = self {
|
||||||
mesh.texture_id
|
mesh.texture_id
|
||||||
} else if let Self::Rect(rect_shape) = self {
|
} else if let Self::Rect(rect_shape) = self {
|
||||||
rect_shape.fill_texture_id
|
rect_shape.fill_texture_id()
|
||||||
} else {
|
} else {
|
||||||
crate::TextureId::default()
|
crate::TextureId::default()
|
||||||
}
|
}
|
||||||
|
|
@ -446,7 +468,7 @@ impl Shape {
|
||||||
galley.rect = transform.scaling * galley.rect;
|
galley.rect = transform.scaling * galley.rect;
|
||||||
}
|
}
|
||||||
Self::Mesh(mesh) => {
|
Self::Mesh(mesh) => {
|
||||||
mesh.transform(transform);
|
Arc::make_mut(mesh).transform(transform);
|
||||||
}
|
}
|
||||||
Self::QuadraticBezier(bezier_shape) => {
|
Self::QuadraticBezier(bezier_shape) => {
|
||||||
bezier_shape.points[0] = transform * bezier_shape.points[0];
|
bezier_shape.points[0] = transform * bezier_shape.points[0];
|
||||||
|
|
|
||||||
|
|
@ -1406,7 +1406,7 @@ impl Tessellator {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.append(mesh);
|
out.append_ref(&mesh);
|
||||||
}
|
}
|
||||||
Shape::LineSegment { points, stroke } => {
|
Shape::LineSegment { points, stroke } => {
|
||||||
self.tessellate_line_segment(points, stroke, out);
|
self.tessellate_line_segment(points, stroke, out);
|
||||||
|
|
@ -1693,14 +1693,14 @@ impl Tessellator {
|
||||||
/// * `rect`: the rectangle to tessellate.
|
/// * `rect`: the rectangle to tessellate.
|
||||||
/// * `out`: triangles are appended to this.
|
/// * `out`: triangles are appended to this.
|
||||||
pub fn tessellate_rect(&mut self, rect: &RectShape, out: &mut Mesh) {
|
pub fn tessellate_rect(&mut self, rect: &RectShape, out: &mut Mesh) {
|
||||||
|
let brush = rect.brush.as_ref();
|
||||||
let RectShape {
|
let RectShape {
|
||||||
mut rect,
|
mut rect,
|
||||||
mut rounding,
|
mut rounding,
|
||||||
fill,
|
fill,
|
||||||
stroke,
|
stroke,
|
||||||
mut blur_width,
|
mut blur_width,
|
||||||
fill_texture_id,
|
..
|
||||||
uv,
|
|
||||||
} = *rect;
|
} = *rect;
|
||||||
|
|
||||||
if self.options.coarse_tessellation_culling
|
if self.options.coarse_tessellation_culling
|
||||||
|
|
@ -1775,7 +1775,11 @@ impl Tessellator {
|
||||||
path.add_line_loop(&self.scratchpad_points);
|
path.add_line_loop(&self.scratchpad_points);
|
||||||
let path_stroke = PathStroke::from(stroke).outside();
|
let path_stroke = PathStroke::from(stroke).outside();
|
||||||
|
|
||||||
if uv.is_positive() {
|
if let Some(brush) = brush {
|
||||||
|
let crate::Brush {
|
||||||
|
fill_texture_id,
|
||||||
|
uv,
|
||||||
|
} = **brush;
|
||||||
// Textured
|
// Textured
|
||||||
let uv_from_pos = |p: Pos2| {
|
let uv_from_pos = |p: Pos2| {
|
||||||
pos2(
|
pos2(
|
||||||
|
|
@ -2173,7 +2177,7 @@ impl Tessellator {
|
||||||
|
|
||||||
profiling::scope!("distribute results", tessellated.len().to_string());
|
profiling::scope!("distribute results", tessellated.len().to_string());
|
||||||
for (index, mesh) in tessellated {
|
for (index, mesh) in tessellated {
|
||||||
shapes[index].shape = Shape::Mesh(mesh);
|
shapes[index].shape = Shape::Mesh(mesh.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue