Checkpoint

This commit is contained in:
Nathan Sobo 2023-10-03 13:03:29 -06:00
parent 3698e89b88
commit a8c1958c75
13 changed files with 248 additions and 186 deletions

View file

@ -28,6 +28,15 @@ impl<T: Clone + Debug> Point<T> {
} }
} }
impl Point<Pixels> {
pub fn scale(&self, factor: f32) -> Point<ScaledPixels> {
Point {
x: self.x.scale(factor),
y: self.y.scale(factor),
}
}
}
impl<T, Rhs> Mul<Rhs> for Point<T> impl<T, Rhs> Mul<Rhs> for Point<T>
where where
T: Mul<Rhs, Output = T> + Clone + Debug, T: Mul<Rhs, Output = T> + Clone + Debug,
@ -122,6 +131,15 @@ impl<T: Clone + Debug> Size<T> {
} }
} }
impl Size<Pixels> {
pub fn scale(&self, factor: f32) -> Size<ScaledPixels> {
Size {
width: self.width.scale(factor),
height: self.height.scale(factor),
}
}
}
impl<T: Clone + Debug + Ord> Size<T> { impl<T: Clone + Debug + Ord> Size<T> {
pub fn max(&self, other: &Self) -> Self { pub fn max(&self, other: &Self) -> Self {
Size { Size {
@ -207,7 +225,6 @@ pub struct Bounds<T: Clone + Debug> {
pub size: Size<T>, pub size: Size<T>,
} }
// Bounds<f32> * Pixels = Bounds<Pixels>
impl<T, Rhs> Mul<Rhs> for Bounds<T> impl<T, Rhs> Mul<Rhs> for Bounds<T>
where where
T: Mul<Rhs, Output = Rhs> + Clone + Debug, T: Mul<Rhs, Output = Rhs> + Clone + Debug,
@ -277,6 +294,15 @@ impl<T: Clone + Debug + PartialOrd + Add<T, Output = T>> Bounds<T> {
} }
} }
impl Bounds<Pixels> {
pub fn scale(&self, factor: f32) -> Bounds<ScaledPixels> {
Bounds {
origin: self.origin.scale(factor),
size: self.size.scale(factor),
}
}
}
impl<T: Clone + Debug + Copy> Copy for Bounds<T> {} impl<T: Clone + Debug + Copy> Copy for Bounds<T> {}
#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)] #[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)]
@ -462,8 +488,8 @@ impl Pixels {
Self(self.0.floor()) Self(self.0.floor())
} }
pub fn to_device_pixels(&self, scale: f32) -> DevicePixels { pub fn scale(&self, factor: f32) -> ScaledPixels {
DevicePixels((self.0 * scale).ceil() as u32) ScaledPixels(self.0 * factor)
} }
} }
@ -542,22 +568,22 @@ impl From<Pixels> for f64 {
SubAssign, SubAssign,
)] )]
#[repr(transparent)] #[repr(transparent)]
pub struct DevicePixels(pub(crate) u32); pub struct DevicePixels(pub(crate) i32);
impl DevicePixels { impl DevicePixels {
pub fn to_bytes(&self, bytes_per_pixel: u8) -> u32 { pub fn to_bytes(&self, bytes_per_pixel: u8) -> u32 {
self.0 * bytes_per_pixel as u32 self.0 as u32 * bytes_per_pixel as u32
} }
} }
impl From<DevicePixels> for u32 { impl From<DevicePixels> for i32 {
fn from(device_pixels: DevicePixels) -> Self { fn from(device_pixels: DevicePixels) -> Self {
device_pixels.0 device_pixels.0
} }
} }
impl From<u32> for DevicePixels { impl From<i32> for DevicePixels {
fn from(val: u32) -> Self { fn from(val: i32) -> Self {
DevicePixels(val) DevicePixels(val)
} }
} }
@ -570,7 +596,25 @@ impl From<DevicePixels> for u64 {
impl From<u64> for DevicePixels { impl From<u64> for DevicePixels {
fn from(val: u64) -> Self { fn from(val: u64) -> Self {
DevicePixels(val as u32) DevicePixels(val as i32)
}
}
#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct ScaledPixels(pub(crate) f32);
impl Eq for ScaledPixels {}
impl Debug for ScaledPixels {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} px (scaled)", self.0)
}
}
impl From<ScaledPixels> for DevicePixels {
fn from(scaled: ScaledPixels) -> Self {
DevicePixels(scaled.0.ceil() as i32)
} }
} }

View file

@ -7,7 +7,7 @@ mod test;
use crate::{ use crate::{
AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point, AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point,
RasterizedGlyphId, Result, Scene, ShapedLine, SharedString, Size, RasterizeGlyphParams, Result, Scene, ShapedLine, SharedString, Size,
}; };
use anyhow::anyhow; use anyhow::anyhow;
use async_task::Runnable; use async_task::Runnable;
@ -147,7 +147,7 @@ pub trait PlatformWindow {
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool; fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
fn draw(&self, scene: Scene); fn draw(&self, scene: Scene);
fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizedGlyphId>>; fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizeGlyphParams>>;
} }
pub trait PlatformDispatcher: Send + Sync { pub trait PlatformDispatcher: Send + Sync {
@ -165,8 +165,8 @@ pub trait PlatformTextSystem: Send + Sync {
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>; fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
fn rasterize_glyph( fn rasterize_glyph(
&self, &self,
glyph_id: &RasterizedGlyphId, glyph_id: &RasterizeGlyphParams,
) -> Result<(Bounds<DevicePixels>, Vec<u8>)>; ) -> Result<(Size<DevicePixels>, Vec<u8>)>;
fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine; fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine;
fn wrap_line( fn wrap_line(
&self, &self,
@ -197,7 +197,7 @@ pub struct AtlasTile {
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)] #[repr(C)]
pub(crate) struct AtlasTextureId(pub(crate) u32); pub(crate) struct AtlasTextureId(pub(crate) u32); // We use u32 instead of usize for Metal Shader Language compatibility
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(C)] #[repr(C)]

View file

@ -28,6 +28,10 @@ impl<Key> MetalAtlas<Key> {
tiles_by_key: Default::default(), tiles_by_key: Default::default(),
})) }))
} }
pub(crate) fn texture(&self, id: AtlasTextureId) -> metal::Texture {
self.0.lock().textures[id.0 as usize].metal_texture.clone()
}
} }
struct MetalAtlasState<Key> { struct MetalAtlasState<Key> {
@ -123,10 +127,10 @@ impl MetalAtlasTexture {
bounds: allocation.rectangle.into(), bounds: allocation.rectangle.into(),
}; };
let region = metal::MTLRegion::new_2d( let region = metal::MTLRegion::new_2d(
u32::from(tile.bounds.origin.x) as u64, tile.bounds.origin.x.into(),
u32::from(tile.bounds.origin.y) as u64, tile.bounds.origin.y.into(),
u32::from(tile.bounds.size.width) as u64, tile.bounds.size.width.into(),
u32::from(tile.bounds.size.height) as u64, tile.bounds.size.height.into(),
); );
self.metal_texture.replace_region( self.metal_texture.replace_region(
region, region,
@ -149,15 +153,15 @@ impl MetalAtlasTexture {
impl From<Size<DevicePixels>> for etagere::Size { impl From<Size<DevicePixels>> for etagere::Size {
fn from(size: Size<DevicePixels>) -> Self { fn from(size: Size<DevicePixels>) -> Self {
etagere::Size::new(u32::from(size.width) as i32, u32::from(size.width) as i32) etagere::Size::new(size.width.into(), size.width.into())
} }
} }
impl From<etagere::Point> for Point<DevicePixels> { impl From<etagere::Point> for Point<DevicePixels> {
fn from(value: etagere::Point) -> Self { fn from(value: etagere::Point) -> Self {
Point { Point {
x: DevicePixels::from(value.x as u32), x: DevicePixels::from(value.x),
y: DevicePixels::from(value.y as u32), y: DevicePixels::from(value.y),
} }
} }
} }
@ -165,8 +169,8 @@ impl From<etagere::Point> for Point<DevicePixels> {
impl From<etagere::Size> for Size<DevicePixels> { impl From<etagere::Size> for Size<DevicePixels> {
fn from(size: etagere::Size) -> Self { fn from(size: etagere::Size) -> Self {
Size { Size {
width: DevicePixels::from(size.width as u32), width: DevicePixels::from(size.width),
height: DevicePixels::from(size.height as u32), height: DevicePixels::from(size.height),
} }
} }
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad, point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad,
RasterizedGlyphId, Scene, Size, RasterizeGlyphParams, Scene, Size,
}; };
use cocoa::{ use cocoa::{
base::{NO, YES}, base::{NO, YES},
@ -18,10 +18,11 @@ pub struct MetalRenderer {
device: metal::Device, device: metal::Device,
layer: metal::MetalLayer, layer: metal::MetalLayer,
command_queue: CommandQueue, command_queue: CommandQueue,
quad_pipeline_state: metal::RenderPipelineState, quads_pipeline_state: metal::RenderPipelineState,
sprites_pipeline_state: metal::RenderPipelineState,
unit_vertices: metal::Buffer, unit_vertices: metal::Buffer,
instances: metal::Buffer, instances: metal::Buffer,
glyph_atlas: Arc<MetalAtlas<RasterizedGlyphId>>, glyph_atlas: Arc<MetalAtlas<RasterizeGlyphParams>>,
} }
impl MetalRenderer { impl MetalRenderer {
@ -81,15 +82,24 @@ impl MetalRenderer {
MTLResourceOptions::StorageModeManaged, MTLResourceOptions::StorageModeManaged,
); );
let quad_pipeline_state = build_pipeline_state( let quads_pipeline_state = build_pipeline_state(
&device, &device,
&library, &library,
"quad", "quads",
"quad_vertex", "quad_vertex",
"quad_fragment", "quad_fragment",
PIXEL_FORMAT, PIXEL_FORMAT,
); );
let sprites_pipeline_state = build_pipeline_state(
&device,
&library,
"sprites",
"monochrome_sprite_vertex",
"monochrome_sprite_fragment",
PIXEL_FORMAT,
);
let command_queue = device.new_command_queue(); let command_queue = device.new_command_queue();
let glyph_atlas = Arc::new(MetalAtlas::new( let glyph_atlas = Arc::new(MetalAtlas::new(
Size { Size {
@ -104,7 +114,8 @@ impl MetalRenderer {
device, device,
layer, layer,
command_queue, command_queue,
quad_pipeline_state, quads_pipeline_state,
sprites_pipeline_state,
unit_vertices, unit_vertices,
instances, instances,
glyph_atlas, glyph_atlas,
@ -115,7 +126,7 @@ impl MetalRenderer {
&*self.layer &*self.layer
} }
pub fn glyph_atlas(&self) -> &Arc<MetalAtlas<RasterizedGlyphId>> { pub fn glyph_atlas(&self) -> &Arc<MetalAtlas<RasterizeGlyphParams>> {
&self.glyph_atlas &self.glyph_atlas
} }
@ -123,8 +134,8 @@ impl MetalRenderer {
let layer = self.layer.clone(); let layer = self.layer.clone();
let viewport_size = layer.drawable_size(); let viewport_size = layer.drawable_size();
let viewport_size: Size<DevicePixels> = size( let viewport_size: Size<DevicePixels> = size(
(viewport_size.width.ceil() as u32).into(), (viewport_size.width.ceil() as i32).into(),
(viewport_size.height.ceil() as u32).into(), (viewport_size.height.ceil() as i32).into(),
); );
let drawable = if let Some(drawable) = layer.next_drawable() { let drawable = if let Some(drawable) = layer.next_drawable() {
drawable drawable
@ -144,8 +155,8 @@ impl MetalRenderer {
depth_texture_desc.set_pixel_format(metal::MTLPixelFormat::Depth32Float); depth_texture_desc.set_pixel_format(metal::MTLPixelFormat::Depth32Float);
depth_texture_desc.set_storage_mode(metal::MTLStorageMode::Private); depth_texture_desc.set_storage_mode(metal::MTLStorageMode::Private);
depth_texture_desc.set_usage(metal::MTLTextureUsage::RenderTarget); depth_texture_desc.set_usage(metal::MTLTextureUsage::RenderTarget);
depth_texture_desc.set_width(u32::from(viewport_size.width) as u64); depth_texture_desc.set_width(i32::from(viewport_size.width) as u64);
depth_texture_desc.set_height(u32::from(viewport_size.height) as u64); depth_texture_desc.set_height(i32::from(viewport_size.height) as u64);
let depth_texture = self.device.new_texture(&depth_texture_desc); let depth_texture = self.device.new_texture(&depth_texture_desc);
let depth_attachment = render_pass_descriptor.depth_attachment().unwrap(); let depth_attachment = render_pass_descriptor.depth_attachment().unwrap();
@ -168,8 +179,8 @@ impl MetalRenderer {
command_encoder.set_viewport(metal::MTLViewport { command_encoder.set_viewport(metal::MTLViewport {
originX: 0.0, originX: 0.0,
originY: 0.0, originY: 0.0,
width: u32::from(viewport_size.width) as f64, width: i32::from(viewport_size.width) as f64,
height: u32::from(viewport_size.height) as f64, height: i32::from(viewport_size.height) as f64,
znear: 0.0, znear: 0.0,
zfar: 1.0, zfar: 1.0,
}); });
@ -226,7 +237,7 @@ impl MetalRenderer {
} }
align_offset(offset); align_offset(offset);
command_encoder.set_render_pipeline_state(&self.quad_pipeline_state); command_encoder.set_render_pipeline_state(&self.quads_pipeline_state);
command_encoder.set_vertex_buffer( command_encoder.set_vertex_buffer(
QuadInputIndex::Vertices as u64, QuadInputIndex::Vertices as u64,
Some(&self.unit_vertices), Some(&self.unit_vertices),
@ -273,12 +284,77 @@ impl MetalRenderer {
fn draw_monochrome_sprites( fn draw_monochrome_sprites(
&mut self, &mut self,
texture_id: AtlasTextureId, texture_id: AtlasTextureId,
monochrome: &[MonochromeSprite], sprites: &[MonochromeSprite],
offset: &mut usize, offset: &mut usize,
viewport_size: Size<DevicePixels>, viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef, command_encoder: &metal::RenderCommandEncoderRef,
) { ) {
// todo!() // dbg!(sprites);
if sprites.is_empty() {
return;
}
align_offset(offset);
let texture = self.glyph_atlas.texture(texture_id);
let texture_size = size(
DevicePixels(texture.width() as i32),
DevicePixels(texture.height() as i32),
);
command_encoder.set_render_pipeline_state(&self.sprites_pipeline_state);
command_encoder.set_vertex_buffer(
MonochromeSpriteInputIndex::Vertices as u64,
Some(&self.unit_vertices),
0,
);
command_encoder.set_vertex_buffer(
MonochromeSpriteInputIndex::Sprites as u64,
Some(&self.instances),
*offset as u64,
);
command_encoder.set_vertex_bytes(
MonochromeSpriteInputIndex::ViewportSize as u64,
mem::size_of_val(&viewport_size) as u64,
&viewport_size as *const Size<DevicePixels> as *const _,
);
command_encoder.set_vertex_bytes(
MonochromeSpriteInputIndex::AtlasTextureSize as u64,
mem::size_of_val(&texture_size) as u64,
&texture_size as *const Size<DevicePixels> as *const _,
);
command_encoder.set_fragment_buffer(
MonochromeSpriteInputIndex::Sprites as u64,
Some(&self.instances),
*offset as u64,
);
command_encoder.set_fragment_texture(
MonochromeSpriteInputIndex::AtlasTexture as u64,
Some(&texture),
);
let sprite_bytes_len = mem::size_of::<MonochromeSprite>() * sprites.len();
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
unsafe {
ptr::copy_nonoverlapping(
sprites.as_ptr() as *const u8,
buffer_contents,
sprite_bytes_len,
);
}
let next_offset = *offset + sprite_bytes_len;
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
);
command_encoder.draw_primitives_instanced(
metal::MTLPrimitiveType::Triangle,
0,
6,
sprites.len() as u64,
);
*offset = next_offset;
} }
} }
@ -334,6 +410,6 @@ enum MonochromeSpriteInputIndex {
Vertices = 0, Vertices = 0,
Sprites = 1, Sprites = 1,
ViewportSize = 2, ViewportSize = 2,
AtlasSize = 3, AtlasTextureSize = 3,
AtlasTexture = 4, AtlasTexture = 4,
} }

View file

@ -126,7 +126,7 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
constant Size_DevicePixels *viewport_size constant Size_DevicePixels *viewport_size
[[buffer(MonochromeSpriteInputIndex_ViewportSize)]], [[buffer(MonochromeSpriteInputIndex_ViewportSize)]],
constant Size_DevicePixels *atlas_size constant Size_DevicePixels *atlas_size
[[buffer(MonochromeSpriteInputIndex_AtlasSize)]]) { [[buffer(MonochromeSpriteInputIndex_AtlasTextureSize)]]) {
float2 unit_vertex = unit_vertices[unit_vertex_id]; float2 unit_vertex = unit_vertices[unit_vertex_id];
MonochromeSprite sprite = sprites[sprite_id]; MonochromeSprite sprite = sprites[sprite_id];
@ -149,11 +149,13 @@ fragment float4 monochrome_sprite_fragment(
MonochromeSpriteVertexOutput input [[stage_in]], MonochromeSpriteVertexOutput input [[stage_in]],
constant MonochromeSprite *sprites constant MonochromeSprite *sprites
[[buffer(MonochromeSpriteInputIndex_Sprites)]], [[buffer(MonochromeSpriteInputIndex_Sprites)]],
texture2d<float> atlas texture2d<float> atlas_texture
[[texture(MonochromeSpriteInputIndex_AtlasTexture)]]) { [[texture(MonochromeSpriteInputIndex_AtlasTexture)]]) {
MonochromeSprite sprite = sprites[input.sprite_id]; MonochromeSprite sprite = sprites[input.sprite_id];
constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear); constexpr sampler atlas_texture_sampler(mag_filter::linear,
float4 sample = atlas.sample(atlas_sampler, input.tile_position); min_filter::linear);
float4 sample =
atlas_texture.sample(atlas_texture_sampler, input.tile_position);
float clip_distance = float clip_distance =
quad_sdf(input.position.xy, sprite.clip_bounds, sprite.clip_corner_radii); quad_sdf(input.position.xy, sprite.clip_bounds, sprite.clip_corner_radii);
float4 color = input.color; float4 color = input.color;
@ -256,52 +258,3 @@ float quad_sdf(float2 point, Bounds_Pixels bounds,
return distance; return distance;
} }
// struct SpriteFragmentInput {
// float4 position [[position]];
// float2 atlas_position;
// float4 color [[flat]];
// uchar compute_winding [[flat]];
// };
// vertex SpriteFragmentInput sprite_vertex(
// uint unit_vertex_id [[vertex_id]],
// uint sprite_id [[instance_id]],
// constant float2 *unit_vertices
// [[buffer(GPUISpriteVertexInputIndexVertices)]], constant GPUISprite
// *sprites [[buffer(GPUISpriteVertexInputIndexSprites)]], constant float2
// *viewport_size [[buffer(GPUISpriteVertexInputIndexViewportSize)]],
// constant float2 *atlas_size
// [[buffer(GPUISpriteVertexInputIndexAtlasSize)]]
// ) {
// float2 unit_vertex = unit_vertices[unit_vertex_id];
// GPUISprite sprite = sprites[sprite_id];
// float2 position = unit_vertex * sprite.target_size + sprite.origin;
// float4 device_position = to_device_position(position, *viewport_size);
// float2 atlas_position = (unit_vertex * sprite.source_size +
// sprite.atlas_origin) / *atlas_size;
// return SpriteFragmentInput {
// device_position,
// atlas_position,
// coloru_to_colorf(sprite.color),
// sprite.compute_winding
// };
// }
// fragment float4 sprite_fragment(
// SpriteFragmentInput input [[stage_in]],
// texture2d<float> atlas [[ texture(GPUISpriteFragmentInputIndexAtlas) ]]
// ) {
// constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear);
// float4 color = input.color;
// float4 sample = atlas.sample(atlas_sampler, input.atlas_position);
// float mask;
// if (input.compute_winding) {
// mask = 1. - abs(1. - fmod(sample.r, 2.));
// } else {
// mask = sample.a;
// }
// color.a *= mask;
// return color;
// }

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle, point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle,
FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RasterizedGlyphId, Result, ShapedGlyph, FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RasterizeGlyphParams, Result,
ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS, ShapedGlyph, ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS,
}; };
use anyhow::anyhow; use anyhow::anyhow;
use cocoa::appkit::{CGFloat, CGPoint}; use cocoa::appkit::{CGFloat, CGPoint};
@ -136,8 +136,8 @@ impl PlatformTextSystem for MacTextSystem {
fn rasterize_glyph( fn rasterize_glyph(
&self, &self,
glyph_id: &RasterizedGlyphId, glyph_id: &RasterizeGlyphParams,
) -> Result<(Bounds<DevicePixels>, Vec<u8>)> { ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
self.0.read().rasterize_glyph(glyph_id) self.0.read().rasterize_glyph(glyph_id)
} }
@ -232,8 +232,8 @@ impl MacTextSystemState {
fn rasterize_glyph( fn rasterize_glyph(
&self, &self,
glyph_id: &RasterizedGlyphId, glyph_id: &RasterizeGlyphParams,
) -> Result<(Bounds<DevicePixels>, Vec<u8>)> { ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
let font = &self.fonts[glyph_id.font_id.0]; let font = &self.fonts[glyph_id.font_id.0];
let scale = Transform2F::from_scale(glyph_id.scale_factor); let scale = Transform2F::from_scale(glyph_id.scale_factor);
let glyph_bounds = font.raster_bounds( let glyph_bounds = font.raster_bounds(
@ -252,18 +252,15 @@ impl MacTextSystemState {
glyph_id.subpixel_variant.x.min(1) as i32, glyph_id.subpixel_variant.x.min(1) as i32,
glyph_id.subpixel_variant.y.min(1) as i32, glyph_id.subpixel_variant.y.min(1) as i32,
); );
let cx_bounds = RectI::new( let bitmap_size = glyph_bounds.size() + subpixel_padding;
glyph_bounds.origin(),
glyph_bounds.size() + subpixel_padding,
);
let mut bytes = vec![0; cx_bounds.width() as usize * cx_bounds.height() as usize]; let mut bytes = vec![0; bitmap_size.x() as usize * bitmap_size.y() as usize];
let cx = CGContext::create_bitmap_context( let cx = CGContext::create_bitmap_context(
Some(bytes.as_mut_ptr() as *mut _), Some(bytes.as_mut_ptr() as *mut _),
cx_bounds.width() as usize, bitmap_size.x() as usize,
cx_bounds.height() as usize, bitmap_size.y() as usize,
8, 8,
cx_bounds.width() as usize, bitmap_size.x() as usize,
&CGColorSpace::create_device_gray(), &CGColorSpace::create_device_gray(),
kCGImageAlphaOnly, kCGImageAlphaOnly,
); );
@ -298,7 +295,7 @@ impl MacTextSystemState {
cx, cx,
); );
Ok((cx_bounds.into(), bytes)) Ok((bitmap_size.into(), bytes))
} }
} }
@ -511,14 +508,23 @@ impl From<RectF> for Bounds<f32> {
impl From<RectI> for Bounds<DevicePixels> { impl From<RectI> for Bounds<DevicePixels> {
fn from(rect: RectI) -> Self { fn from(rect: RectI) -> Self {
Bounds { Bounds {
origin: point( origin: point(DevicePixels(rect.origin_x()), DevicePixels(rect.origin_y())),
DevicePixels(rect.origin_x() as u32), size: size(DevicePixels(rect.width()), DevicePixels(rect.height())),
DevicePixels(rect.origin_y() as u32), }
), }
size: size( }
DevicePixels(rect.width() as u32),
DevicePixels(rect.height() as u32), impl From<Vector2I> for Size<DevicePixels> {
), fn from(value: Vector2I) -> Self {
size(value.x().into(), value.y().into())
}
}
impl From<RectI> for Bounds<i32> {
fn from(rect: RectI) -> Self {
Bounds {
origin: point(rect.origin_x(), rect.origin_y()),
size: size(rect.width(), rect.height()),
} }
} }
} }

View file

@ -3,8 +3,8 @@ use crate::{
point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers,
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt,
Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen, Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen,
PlatformWindow, Point, RasterizedGlyphId, Scene, Size, Timer, WindowAppearance, WindowBounds, PlatformWindow, Point, RasterizeGlyphParams, Scene, Size, Timer, WindowAppearance,
WindowKind, WindowOptions, WindowPromptLevel, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
}; };
use block::ConcreteBlock; use block::ConcreteBlock;
use cocoa::{ use cocoa::{
@ -886,7 +886,7 @@ impl PlatformWindow for MacWindow {
} }
} }
fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizedGlyphId>> { fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizeGlyphParams>> {
self.0.lock().renderer.glyph_atlas().clone() self.0.lock().renderer.glyph_atlas().clone()
} }
} }

View file

@ -1,7 +1,7 @@
use std::{iter::Peekable, mem}; use std::{iter::Peekable, mem};
use super::{Bounds, Hsla, Pixels, Point}; use super::{Bounds, Hsla, Pixels, Point};
use crate::{AtlasTextureId, AtlasTile, Corners, Edges}; use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledPixels};
use collections::BTreeMap; use collections::BTreeMap;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -36,7 +36,6 @@ impl Scene {
let primitive = primitive.into(); let primitive = primitive.into();
match primitive { match primitive {
Primitive::Quad(mut quad) => { Primitive::Quad(mut quad) => {
quad.scale(self.scale_factor);
layer.quads.push(quad); layer.quads.push(quad);
} }
Primitive::Sprite(sprite) => { Primitive::Sprite(sprite) => {
@ -187,17 +186,17 @@ pub(crate) enum PrimitiveBatch<'a> {
#[repr(C)] #[repr(C)]
pub struct Quad { pub struct Quad {
pub order: u32, pub order: u32,
pub bounds: Bounds<Pixels>, pub bounds: Bounds<ScaledPixels>,
pub clip_bounds: Bounds<Pixels>, pub clip_bounds: Bounds<ScaledPixels>,
pub clip_corner_radii: Corners<Pixels>, pub clip_corner_radii: Corners<ScaledPixels>,
pub background: Hsla, pub background: Hsla,
pub border_color: Hsla, pub border_color: Hsla,
pub corner_radii: Corners<Pixels>, pub corner_radii: Corners<ScaledPixels>,
pub border_widths: Edges<Pixels>, pub border_widths: Edges<ScaledPixels>,
} }
impl Quad { impl Quad {
pub fn vertices(&self) -> impl Iterator<Item = Point<Pixels>> { pub fn vertices(&self) -> impl Iterator<Item = Point<ScaledPixels>> {
let x1 = self.bounds.origin.x; let x1 = self.bounds.origin.x;
let y1 = self.bounds.origin.y; let y1 = self.bounds.origin.y;
let x2 = x1 + self.bounds.size.width; let x2 = x1 + self.bounds.size.width;
@ -210,14 +209,6 @@ impl Quad {
] ]
.into_iter() .into_iter()
} }
pub fn scale(&mut self, factor: f32) {
self.bounds *= factor;
self.clip_bounds *= factor;
self.clip_corner_radii *= factor;
self.corner_radii *= factor;
self.border_widths *= factor;
}
} }
impl Ord for Quad { impl Ord for Quad {

View file

@ -182,6 +182,7 @@ impl Style {
/// Paints the background of an element styled with this style. /// Paints the background of an element styled with this style.
pub fn paint<V: 'static>(&self, order: u32, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) { pub fn paint<V: 'static>(&self, order: u32, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
let rem_size = cx.rem_size(); let rem_size = cx.rem_size();
let scale = cx.scale_factor();
let background_color = self.fill.as_ref().and_then(Fill::color); let background_color = self.fill.as_ref().and_then(Fill::color);
if background_color.is_some() || self.is_border_visible() { if background_color.is_some() || self.is_border_visible() {
@ -190,13 +191,19 @@ impl Style {
layer_id, layer_id,
Quad { Quad {
order, order,
bounds, bounds: bounds.scale(scale),
clip_bounds: bounds, // todo! clip_bounds: bounds.scale(scale), // todo!
clip_corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), clip_corner_radii: self
.corner_radii
.map(|length| length.to_pixels(rem_size).scale(scale)),
background: background_color.unwrap_or_default(), background: background_color.unwrap_or_default(),
border_color: self.border_color.unwrap_or_default(), border_color: self.border_color.unwrap_or_default(),
corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), corner_radii: self
border_widths: self.border_widths.map(|length| length.to_pixels(rem_size)), .corner_radii
.map(|length| length.to_pixels(rem_size).scale(scale)),
border_widths: self
.border_widths
.map(|length| length.to_pixels(rem_size).scale(scale)),
}, },
); );
} }

View file

@ -217,8 +217,8 @@ impl TextSystem {
pub fn rasterize_glyph( pub fn rasterize_glyph(
&self, &self,
glyph_id: &RasterizedGlyphId, glyph_id: &RasterizeGlyphParams,
) -> Result<(Bounds<DevicePixels>, Vec<u8>)> { ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
self.platform_text_system.rasterize_glyph(glyph_id) self.platform_text_system.rasterize_glyph(glyph_id)
} }
} }
@ -380,7 +380,7 @@ pub struct ShapedGlyph {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct RasterizedGlyphId { pub struct RasterizeGlyphParams {
pub(crate) font_id: FontId, pub(crate) font_id: FontId,
pub(crate) glyph_id: GlyphId, pub(crate) glyph_id: GlyphId,
pub(crate) font_size: Pixels, pub(crate) font_size: Pixels,
@ -388,9 +388,9 @@ pub struct RasterizedGlyphId {
pub(crate) scale_factor: f32, pub(crate) scale_factor: f32,
} }
impl Eq for RasterizedGlyphId {} impl Eq for RasterizeGlyphParams {}
impl Hash for RasterizedGlyphId { impl Hash for RasterizeGlyphParams {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.font_id.0.hash(state); self.font_id.0.hash(state);
self.glyph_id.0.hash(state); self.glyph_id.0.hash(state);

View file

@ -157,35 +157,29 @@ impl Line {
if let Some((_underline_origin, _underline_style)) = finished_underline { if let Some((_underline_origin, _underline_style)) = finished_underline {
todo!() todo!()
// cx.scene().insert(Underline {
// origin: underline_origin,
// width: glyph_origin.x - underline_origin.x,
// thickness: underline_style.thickness.into(),
// color: underline_style.color.unwrap(),
// squiggly: underline_style.squiggly,
// });
} }
if glyph.is_emoji { if glyph.is_emoji {
todo!() todo!()
// cx.scene().push_image_glyph(scene::ImageGlyph {
// font_id: run.font_id,
// font_size: self.layout.font_size,
// id: glyph.id,
// origin: glyph_origin,
// });
} else { } else {
if let Some((tile, bounds)) = cx if let Some(tile) = cx
.rasterize_glyph( .rasterize_glyph(
run.font_id, run.font_id,
glyph.id, glyph.id,
self.layout.font_size, self.layout.font_size,
cx.scale_factor(),
glyph_origin, glyph_origin,
cx.scale_factor(),
) )
.log_err() .log_err()
{ {
let layer_id = cx.current_layer_id(); let layer_id = cx.current_layer_id();
let bounds = Bounds {
origin: glyph_origin + todo!(),
size: todo!(),
};
// cx.text_system().raster_bounds()
cx.scene().insert( cx.scene().insert(
layer_id, layer_id,
MonochromeSprite { MonochromeSprite {

View file

@ -1,9 +1,8 @@
use crate::{ use crate::{
px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, DevicePixels, Effect, px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, Effect, Element, EntityId,
Element, EntityId, FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformAtlas,
MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, RasterizedGlyphId, Reference, PlatformWindow, Point, RasterizeGlyphParams, Reference, Scene, Size, StackContext,
Scene, Size, StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
SUBPIXEL_VARIANTS,
}; };
use anyhow::Result; use anyhow::Result;
use futures::Future; use futures::Future;
@ -16,7 +15,7 @@ pub struct AnyWindow {}
pub struct Window { pub struct Window {
handle: AnyWindowHandle, handle: AnyWindowHandle,
platform_window: MainThreadOnly<Box<dyn PlatformWindow>>, platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
glyph_atlas: Arc<dyn PlatformAtlas<RasterizedGlyphId>>, glyph_atlas: Arc<dyn PlatformAtlas<RasterizeGlyphParams>>,
rem_size: Pixels, rem_size: Pixels,
content_size: Size<Pixels>, content_size: Size<Pixels>,
layout_engine: TaffyLayoutEngine, layout_engine: TaffyLayoutEngine,
@ -171,39 +170,26 @@ impl<'a, 'w> WindowContext<'a, 'w> {
font_id: FontId, font_id: FontId,
glyph_id: GlyphId, glyph_id: GlyphId,
font_size: Pixels, font_size: Pixels,
scale_factor: f32,
target_position: Point<Pixels>, target_position: Point<Pixels>,
) -> Result<(AtlasTile, Bounds<Pixels>)> { scale_factor: f32,
) -> Result<AtlasTile> {
let target_position = target_position * scale_factor; let target_position = target_position * scale_factor;
let subpixel_variant = Point { let subpixel_variant = Point {
x: (target_position.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, x: (target_position.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
y: (target_position.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, y: (target_position.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
}; };
let rasterized_glyph_id = RasterizedGlyphId { let rasterized_glyph_id = RasterizeGlyphParams {
font_id, font_id,
glyph_id, glyph_id,
font_size, font_size,
subpixel_variant, subpixel_variant,
scale_factor, scale_factor,
}; };
let mut offset = Default::default(); self.window
let tile = self
.window
.glyph_atlas .glyph_atlas
.get_or_insert_with(&rasterized_glyph_id, &mut || { .get_or_insert_with(&rasterized_glyph_id, &mut || {
let (bounds, pixels) = self.text_system().rasterize_glyph(&rasterized_glyph_id)?; self.text_system().rasterize_glyph(&rasterized_glyph_id)
offset = bounds.origin; })
Ok((bounds.size, pixels))
})?;
// Align bounding box surrounding glyph to pixel grid
let mut origin = (target_position * scale_factor).map(|p| p.floor());
// Position glyph within bounding box
origin += offset.map(|o| px(u32::from(o) as f32));
let size = tile.bounds.size.map(|b| px(b.0 as f32));
let bounds = Bounds { origin, size };
Ok((tile, bounds))
} }
pub(crate) fn draw(&mut self) -> Result<()> { pub(crate) fn draw(&mut self) -> Result<()> {

View file

@ -4,7 +4,7 @@ use crate::{
themes::rose_pine_dawn, themes::rose_pine_dawn,
}; };
use gpui3::{ use gpui3::{
div, img, svg, view, Context, Element, ParentElement, RootView, StyleHelpers, View, black, div, img, svg, view, Context, Element, ParentElement, RootView, StyleHelpers, View,
ViewContext, WindowContext, ViewContext, WindowContext,
}; };
@ -29,6 +29,7 @@ impl Workspace {
let theme = rose_pine_dawn(); let theme = rose_pine_dawn();
div() div()
.font("Helvetica") .font("Helvetica")
.text_color(black())
.text_base() .text_base()
.size_full() .size_full()
.fill(theme.middle.positive.default.background) .fill(theme.middle.positive.default.background)