1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 13:20:05 -04:00

Merge branch 'master' into cache_galley_lines

This commit is contained in:
Hubert Głuchowski
2024-12-19 23:21:09 +01:00
committed by GitHub
138 changed files with 2386 additions and 1394 deletions

View File

@@ -5,6 +5,13 @@ This file is updated upon each release.
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
## 0.30.0 - 2024-12-16
* Expand max font atlas size from 8k to 16k [#5257](https://github.com/emilk/egui/pull/5257) by [@rustbasic](https://github.com/rustbasic)
* Put font data into `Arc` to reduce memory consumption [#5276](https://github.com/emilk/egui/pull/5276) by [@StarStarJ](https://github.com/StarStarJ)
* Reduce aliasing when painting thin box outlines [#5484](https://github.com/emilk/egui/pull/5484) by [@emilk](https://github.com/emilk)
* Fix zero-width strokes still affecting the feathering color of boxes [#5485](https://github.com/emilk/egui/pull/5485) by [@emilk](https://github.com/emilk)
## 0.29.1 - 2024-10-01
Nothing new

View File

@@ -55,11 +55,6 @@ log = ["dep:log"]
## [`mint`](https://docs.rs/mint) enables interoperability with other math libraries such as [`glam`](https://docs.rs/glam) and [`nalgebra`](https://docs.rs/nalgebra).
mint = ["emath/mint"]
## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
##
## Only enabled on native, because of the low resolution (1ms) of clocks in browsers.
puffin = ["dep:puffin"]
## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon).
##
## This can help performance for graphics-intense applications.
@@ -79,6 +74,7 @@ ab_glyph = "0.2.11"
ahash.workspace = true
nohash-hasher.workspace = true
parking_lot.workspace = true # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios.
profiling = { workspace = true}
#! ### Optional dependencies
bytemuck = { workspace = true, optional = true, features = ["derive"] }
@@ -87,7 +83,6 @@ bytemuck = { workspace = true, optional = true, features = ["derive"] }
document-features = { workspace = true, optional = true }
log = { workspace = true, optional = true }
puffin = { workspace = true, optional = true }
rayon = { version = "1.7", optional = true }
## Allow serialization using [`serde`](https://docs.rs/serde) .

View File

@@ -207,17 +207,21 @@ impl CubicBezierShape {
/// B.x = (P3.x - 3 * P2.x + 3 * P1.x - P0.x) * t^3 + (3 * P2.x - 6 * P1.x + 3 * P0.x) * t^2 + (3 * P1.x - 3 * P0.x) * t + P0.x
/// B.y = (P3.y - 3 * P2.y + 3 * P1.y - P0.y) * t^3 + (3 * P2.y - 6 * P1.y + 3 * P0.y) * t^2 + (3 * P1.y - 3 * P0.y) * t + P0.y
/// Combine the above three equations and iliminate B.x and B.y, we get:
/// ```text
/// t^3 * ( (P3.x - 3*P2.x + 3*P1.x - P0.x) * (P3.y - P0.y) - (P3.y - 3*P2.y + 3*P1.y - P0.y) * (P3.x - P0.x))
/// + t^2 * ( (3 * P2.x - 6 * P1.x + 3 * P0.x) * (P3.y - P0.y) - (3 * P2.y - 6 * P1.y + 3 * P0.y) * (P3.x - P0.x))
/// + t^1 * ( (3 * P1.x - 3 * P0.x) * (P3.y - P0.y) - (3 * P1.y - 3 * P0.y) * (P3.x - P0.x))
/// + (P0.x * (P3.y - P0.y) - P0.y * (P3.x - P0.x)) + P0.x * (P0.y - P3.y) + P0.y * (P3.x - P0.x)
/// = 0
/// or a * t^3 + b * t^2 + c * t + d = 0
/// ```
/// or `a * t^3 + b * t^2 + c * t + d = 0`
///
/// let x = t - b / (3 * a), then we have:
/// ```text
/// x^3 + p * x + q = 0, where:
/// p = (3.0 * a * c - b^2) / (3.0 * a^2)
/// q = (2.0 * b^3 - 9.0 * a * b * c + 27.0 * a^2 * d) / (27.0 * a^3)
/// ```
///
/// when p > 0, there will be one real root, two complex roots
/// when p = 0, there will be two real roots, when p=q=0, there will be three real roots but all 0.

View File

@@ -154,8 +154,10 @@ impl ColorImage {
let max_x = (region.max.x * pixels_per_point) as usize;
let min_y = (region.min.y * pixels_per_point) as usize;
let max_y = (region.max.y * pixels_per_point) as usize;
assert!(min_x <= max_x);
assert!(min_y <= max_y);
assert!(
min_x <= max_x && min_y <= max_y,
"Screenshot region is invalid: {region:?}"
);
let width = max_x - min_x;
let height = max_y - min_y;
let mut output = Vec::with_capacity(width * height);

View File

@@ -143,33 +143,3 @@ pub enum Primitive {
/// Was epaint compiled with the `rayon` feature?
pub const HAS_RAYON: bool = cfg!(feature = "rayon");
// ---------------------------------------------------------------------------
mod profiling_scopes {
#![allow(unused_macros)]
#![allow(unused_imports)]
/// Profiling macro for feature "puffin"
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_function!($($arg)*);
};
}
pub(crate) use profile_function;
/// Profiling macro for feature "puffin"
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_scope!($($arg)*);
};
}
pub(crate) use profile_scope;
}
#[allow(unused_imports)]
pub(crate) use profiling_scopes::{profile_function, profile_scope};

View File

@@ -85,7 +85,7 @@ impl Mesh {
/// Are all indices within the bounds of the contained vertices?
pub fn is_valid(&self) -> bool {
crate::profile_function!();
profiling::function_scope!();
if let Ok(n) = u32::try_from(self.vertices.len()) {
self.indices.iter().all(|&i| i < n)
@@ -111,7 +111,7 @@ impl Mesh {
///
/// Panics when `other` mesh has a different texture.
pub fn append(&mut self, other: Self) {
crate::profile_function!();
profiling::function_scope!();
debug_assert!(other.is_valid());
if self.is_empty() {

View File

@@ -161,10 +161,15 @@ where
impl From<Stroke> for PathStroke {
fn from(value: Stroke) -> Self {
Self {
width: value.width,
color: ColorMode::Solid(value.color),
kind: StrokeKind::default(),
if value.is_empty() {
// Important, since we use the stroke color when doing feathering of the fill!
Self::NONE
} else {
Self {
width: value.width,
color: ColorMode::Solid(value.color),
kind: StrokeKind::default(),
}
}
}
}

View File

@@ -502,6 +502,8 @@ impl Path {
/// Calling this may reverse the vertices in the path if they are wrong winding order.
///
/// The preferred winding order is clockwise.
///
/// The stroke colors is used for color-correct feathering.
pub fn fill(&mut self, feathering: f32, color: Color32, stroke: &PathStroke, out: &mut Mesh) {
fill_closed_path(feathering, &mut self.0, color, stroke, out);
}
@@ -918,7 +920,7 @@ fn stroke_path(
) {
let n = path.len() as u32;
if stroke.width <= 0.0 || stroke.color == ColorMode::TRANSPARENT || n < 2 {
if stroke.is_empty() || n < 2 {
return;
}
@@ -1277,6 +1279,11 @@ impl Tessellator {
((point * self.pixels_per_point - 0.5).round() + 0.5) / self.pixels_per_point
}
#[inline(always)]
pub fn round_pos_to_pixel(&self, pos: Pos2) -> Pos2 {
pos2(self.round_to_pixel(pos.x), self.round_to_pixel(pos.y))
}
#[inline(always)]
pub fn round_pos_to_pixel_center(&self, pos: Pos2) -> Pos2 {
pos2(
@@ -1363,7 +1370,7 @@ impl Tessellator {
self.tessellate_ellipse(ellipse, out);
}
Shape::Mesh(mesh) => {
crate::profile_scope!("mesh");
profiling::scope!("mesh");
if self.options.validate_meshes && !mesh.is_valid() {
debug_assert!(false, "Invalid Mesh in Shape::Mesh");
@@ -1596,7 +1603,7 @@ impl Tessellator {
return;
}
crate::profile_function!();
profiling::function_scope!();
let PathShape {
points,
@@ -1702,6 +1709,20 @@ impl Tessellator {
self.tessellate_line(line, stroke, out); // …and forth
}
} else {
let rect = if !stroke.is_empty() && stroke.width < self.feathering {
// Very thin rectangle strokes create extreme aliasing when they move around.
// We can fix that by rounding the rectangle corners to pixel centers.
// TODO(#5164): maybe do this for all shapes and stroke sizes
// TODO(emilk): since we use StrokeKind::Outside, we should probably round the
// corners after offsetting them with half the stroke width (see `translate_stroke_point`).
Rect {
min: self.round_pos_to_pixel_center(rect.min),
max: self.round_pos_to_pixel_center(rect.max),
}
} else {
rect
};
let path = &mut self.scratchpad_path;
path.clear();
path::rounded_rectangle(&mut self.scratchpad_points, rect, rounding);
@@ -1979,7 +2000,7 @@ impl Tessellator {
/// A list of clip rectangles with matching [`Mesh`].
#[allow(unused_mut)]
pub fn tessellate_shapes(&mut self, mut shapes: Vec<ClippedShape>) -> Vec<ClippedPrimitive> {
crate::profile_function!();
profiling::function_scope!();
#[cfg(feature = "rayon")]
if self.options.parallel_tessellation {
@@ -1989,7 +2010,7 @@ impl Tessellator {
let mut clipped_primitives: Vec<ClippedPrimitive> = Vec::default();
{
crate::profile_scope!("tessellate");
profiling::scope!("tessellate");
for clipped_shape in shapes {
self.tessellate_clipped_shape(clipped_shape, &mut clipped_primitives);
}
@@ -2026,7 +2047,7 @@ impl Tessellator {
/// then replace the original shape with their tessellated meshes.
#[cfg(feature = "rayon")]
fn parallel_tessellation_of_large_shapes(&self, shapes: &mut [ClippedShape]) {
crate::profile_function!();
profiling::function_scope!();
use rayon::prelude::*;
@@ -2056,7 +2077,7 @@ impl Tessellator {
.enumerate()
.filter(|(_, clipped_shape)| should_parallelize(&clipped_shape.shape))
.map(|(index, clipped_shape)| {
crate::profile_scope!("tessellate_big_shape");
profiling::scope!("tessellate_big_shape");
// TODO(emilk): reuse tessellator in a thread local
let mut tessellator = (*self).clone();
let mut mesh = Mesh::default();
@@ -2065,7 +2086,7 @@ impl Tessellator {
})
.collect();
crate::profile_scope!("distribute results", tessellated.len().to_string());
profiling::scope!("distribute results", tessellated.len().to_string());
for (index, mesh) in tessellated {
shapes[index].shape = Shape::Mesh(mesh);
}

View File

@@ -66,6 +66,7 @@ impl TextureHandle {
}
/// Assign a new image to an existing texture.
#[allow(clippy::needless_pass_by_ref_mut)] // Intentionally hide interiority of mutability
pub fn set(&mut self, image: impl Into<ImageData>, options: TextureOptions) {
self.tex_mngr
.write()
@@ -73,6 +74,7 @@ impl TextureHandle {
}
/// Assign a new image to a subregion of the whole texture.
#[allow(clippy::needless_pass_by_ref_mut)] // Intentionally hide interiority of mutability
pub fn set_partial(
&mut self,
pos: [usize; 2],