Skip to content

Commit

Permalink
gpui: Draw Path with 2x to anti-aliasing and add move_to, translate m…
Browse files Browse the repository at this point in the history
…ethod.
  • Loading branch information
huacnlee committed Nov 26, 2024
1 parent 933c11a commit b71dde3
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 48 deletions.
83 changes: 49 additions & 34 deletions crates/gpui/examples/painting.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use gpui::{
canvas, div, point, prelude::*, px, size, App, AppContext, Bounds, MouseDownEvent, Path,
canvas, div, point, prelude::*, px, size, App, AppContext, Bounds, Hsla, MouseDownEvent, Path,
Pixels, Point, Render, ViewContext, WindowOptions,
};
struct PaintingViewer {
default_lines: Vec<Path<Pixels>>,
default_lines: Vec<(Path<Pixels>, Hsla)>,
lines: Vec<Vec<Point<Pixels>>>,
start: Point<Pixels>,
_painting: bool,
Expand All @@ -14,33 +14,48 @@ impl PaintingViewer {
let mut lines = vec![];

// draw a line
let mut path = Path::new(point(px(50.), px(180.)));
path.line_to(point(px(100.), px(120.)));
let mut path = Path::new(point(px(20.), px(100.)));
path.line_to(point(px(50.), px(160.)));
path.line_to(point(px(60.), px(100.)));
path.line_to(point(px(80.), px(140.)));
// go back to close the path
path.line_to(point(px(100.), px(121.)));
path.line_to(point(px(50.), px(181.)));
lines.push(path);
path.move_to(point(px(81.), px(141.)));
path.line_to(point(px(60.), px(101.)));
path.line_to(point(px(50.), px(161.)));
path.line_to(point(px(20.), px(101.)));
lines.push((path, gpui::black()));

// draw a triangle
let mut path = Path::new(point(px(25.), px(0.)));
path.line_to(point(px(50.), px(50.)));
path.line_to(point(px(0.), px(50.)));
path.translate(point(px(100.), px(100.)));
lines.push((path, gpui::red()));

// draw a lightening bolt ⚡
let mut path = Path::new(point(px(150.), px(200.)));
path.line_to(point(px(200.), px(125.)));
path.line_to(point(px(200.), px(175.)));
path.line_to(point(px(250.), px(100.)));
lines.push(path);
let mut path = Path::new(point(px(-50.), px(50.)));
path.line_to(point(px(0.), px(-25.)));
path.line_to(point(px(0.), px(0.)));
path.move_to(point(px(0.), px(0.)));
path.line_to(point(px(50.), px(-50.)));
path.line_to(point(px(0.), px(25.)));
path.line_to(point(px(0.), px(5.)));
path.translate(point(px(220.), px(150.)));
lines.push((path, gpui::blue()));

// draw a ⭐
let mut path = Path::new(point(px(350.), px(100.)));
path.line_to(point(px(370.), px(160.)));
path.line_to(point(px(430.), px(160.)));
path.line_to(point(px(380.), px(200.)));
path.line_to(point(px(400.), px(260.)));
path.line_to(point(px(350.), px(220.)));
path.line_to(point(px(300.), px(260.)));
path.line_to(point(px(320.), px(200.)));
path.line_to(point(px(270.), px(160.)));
path.line_to(point(px(330.), px(160.)));
path.line_to(point(px(350.), px(100.)));
lines.push(path);
let mut path = Path::new(point(px(76.8), px(116.864)));
path.line_to(point(px(31.6608), px(142.1312)));
path.line_to(point(px(41.7408), px(91.392)));
path.line_to(point(px(3.7568), px(56.2688)));
path.line_to(point(px(55.1296), px(50.176)));
path.line_to(point(px(76.8), px(3.2)));
path.line_to(point(px(98.4704), px(50.176)));
path.line_to(point(px(149.8432), px(56.2688)));
path.line_to(point(px(111.8592), px(91.392)));
path.line_to(point(px(121.9392), px(142.1312)));
path.translate(point(px(270.), px(80.)));
lines.push((path, gpui::yellow()));

let square_bounds = Bounds {
origin: point(px(450.), px(100.)),
Expand All @@ -59,8 +74,7 @@ impl PaintingViewer {
square_bounds.lower_right(),
square_bounds.upper_right() + point(px(0.0), vertical_offset),
);
path.line_to(square_bounds.lower_left());
lines.push(path);
lines.push((path, gpui::green()));

Self {
default_lines: lines.clone(),
Expand Down Expand Up @@ -115,9 +129,9 @@ impl Render for PaintingViewer {
canvas(
move |_, _| {},
move |_, _, cx| {
const STROKE_WIDTH: Pixels = px(2.0);
for path in default_lines {
cx.paint_path(path, gpui::black());
const STROKE_WIDTH: Pixels = px(1.0);
for (path, color) in default_lines {
cx.paint_path(path, color);
}
for points in lines {
let mut path = Path::new(points[0]);
Expand All @@ -127,11 +141,12 @@ impl Render for PaintingViewer {

let mut last = points.last().unwrap();
for p in points.iter().rev() {
let mut offset_x = px(0.);
if last.x == p.x {
offset_x = STROKE_WIDTH;
}
path.line_to(point(p.x + offset_x, p.y + STROKE_WIDTH));
let dx = p.x - last.x;
let dy = p.y - last.y;
let distance = (dx * dx + dy * dy).0.sqrt();
let offset_x = (STROKE_WIDTH * dy / distance).clamp(px(0.0), STROKE_WIDTH);
let offset_y = (STROKE_WIDTH * dx / distance).clamp(px(0.0), STROKE_WIDTH);
path.line_to(point(p.x + offset_x, p.y - offset_y));
last = p;
}

Expand Down
1 change: 1 addition & 0 deletions crates/gpui/src/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2456,6 +2456,7 @@ impl From<usize> for Pixels {
Copy,
Default,
Div,
Mul,
Eq,
Hash,
Ord,
Expand Down
19 changes: 13 additions & 6 deletions crates/gpui/src/platform/blade/blade_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::{BladeAtlas, PATH_TEXTURE_FORMAT};
use crate::{
AtlasTextureKind, AtlasTile, Bounds, ContentMask, DevicePixels, GPUSpecs, Hsla,
MonochromeSprite, Path, PathId, PathVertex, PolychromeSprite, PrimitiveBatch, Quad,
ScaledPixels, Scene, Shadow, Size, Underline,
ScaledPixels, Scene, Shadow, Size, Underline, PATH_SUBPIXEL_VARIANTS,
};
use bytemuck::{Pod, Zeroable};
use collections::HashMap;
Expand Down Expand Up @@ -622,12 +622,19 @@ impl BladeRenderer {
for path in paths {
let tile = &self.path_tiles[&path.id];
let tex_info = self.atlas.get_texture_info(tile.texture_id);
let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
let origin = path
.bounds
.intersect(&path.content_mask.bounds)
.origin
.map(|p| (p / PATH_SUBPIXEL_VARIANTS).floor());
let size = tile
.bounds
.size
.map(|s| s / PATH_SUBPIXEL_VARIANTS as i32)
.map(Into::into);

let sprites = [PathSprite {
bounds: Bounds {
origin: origin.map(|p| p.floor()),
size: tile.bounds.size.map(Into::into),
},
bounds: Bounds { origin, size },
color: path.color,
tile: (*tile).clone(),
}];
Expand Down
17 changes: 12 additions & 5 deletions crates/gpui/src/platform/mac/metal_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{
point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, ContentMask, DevicePixels,
Hsla, MonochromeSprite, PaintSurface, Path, PathId, PathVertex, PolychromeSprite,
PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, Surface, Underline,
PATH_SUBPIXEL_VARIANTS,
};
use anyhow::{anyhow, Result};
use block::ConcreteBlock;
Expand Down Expand Up @@ -733,12 +734,18 @@ impl MetalRenderer {
if let Some((path, tile)) = paths_and_tiles.peek() {
if prev_texture_id.map_or(true, |texture_id| texture_id == tile.texture_id) {
prev_texture_id = Some(tile.texture_id);
let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
let origin = path
.bounds
.intersect(&path.content_mask.bounds)
.origin
.map(|p| (p / PATH_SUBPIXEL_VARIANTS).floor());
let size = tile
.bounds
.size
.map(|s| s / PATH_SUBPIXEL_VARIANTS as i32)
.map(Into::into);
sprites.push(PathSprite {
bounds: Bounds {
origin: origin.map(|p| p.floor()),
size: tile.bounds.size.map(Into::into),
},
bounds: Bounds { origin, size },
color: path.color,
tile: (*tile).clone(),
});
Expand Down
23 changes: 22 additions & 1 deletion crates/gpui/src/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ use crate::{
};
use std::{fmt::Debug, iter::Peekable, ops::Range, slice};

/// Subpixel variants for antialiasing for Path render.
pub(crate) const PATH_SUBPIXEL_VARIANTS: f32 = 2.0;

#[allow(non_camel_case_types, unused)]
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;

Expand Down Expand Up @@ -785,13 +788,20 @@ impl Path<Pixels> {
.iter()
.map(|vertex| vertex.scale(factor))
.collect(),
start: self.start.map(|start| start.scale(factor)),
start: self.start.scale(factor),
current: self.current.scale(factor),
contour_count: self.contour_count,
color: self.color,
}
}

/// Move the current point of the path to the given point.
pub fn move_to(&mut self, to: Point<Pixels>) {
self.start = to;
self.current = to;
self.contour_count = 0;
}

/// Draw a straight line from the current point to the given point.
pub fn line_to(&mut self, to: Point<Pixels>) {
self.contour_count += 1;
Expand Down Expand Up @@ -821,6 +831,17 @@ impl Path<Pixels> {
self.current = to;
}

/// Translate path by the given offset.
pub fn translate(&mut self, offset: Point<Pixels>) {
self.start = self.start + offset;
self.current = self.current + offset;
self.bounds.origin = self.bounds.origin + offset;
self.content_mask.bounds.origin = self.content_mask.bounds.origin + offset;
for vertex in &mut self.vertices {
vertex.xy_position = vertex.xy_position + offset;
}
}

fn push_triangle(
&mut self,
xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
Expand Down
4 changes: 2 additions & 2 deletions crates/gpui/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement, TransformationMatrix, Underline,
UnderlineStyle, View, VisualContext, WeakView, WindowAppearance, WindowBackgroundAppearance,
WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem,
SUBPIXEL_VARIANTS,
PATH_SUBPIXEL_VARIANTS, SUBPIXEL_VARIANTS,
};
use anyhow::{anyhow, Context as _, Result};
use collections::{FxHashMap, FxHashSet};
Expand Down Expand Up @@ -2330,7 +2330,7 @@ impl<'a> WindowContext<'a> {
self.window
.next_frame
.scene
.insert_primitive(path.scale(scale_factor));
.insert_primitive(path.scale(scale_factor * PATH_SUBPIXEL_VARIANTS));
}

/// Paint an underline into the scene for the next frame at the current z-index.
Expand Down

0 comments on commit b71dde3

Please sign in to comment.