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:
@@ -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
|
||||
|
||||
|
||||
@@ -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) .
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user