Detailed changes
@@ -618,6 +618,12 @@ impl From<ScaledPixels> for DevicePixels {
}
}
+impl From<DevicePixels> for ScaledPixels {
+ fn from(device: DevicePixels) -> Self {
+ ScaledPixels(device.0 as f32)
+ }
+}
+
#[derive(Clone, Copy, Default, Add, Sub, Mul, Div)]
pub struct Rems(f32);
@@ -802,3 +808,76 @@ impl From<()> for Length {
Self::Definite(DefiniteLength::default())
}
}
+
+pub trait IsZero {
+ fn is_zero(&self) -> bool;
+}
+
+impl IsZero for DevicePixels {
+ fn is_zero(&self) -> bool {
+ self.0 == 0
+ }
+}
+
+impl IsZero for ScaledPixels {
+ fn is_zero(&self) -> bool {
+ self.0 == 0.
+ }
+}
+
+impl IsZero for Pixels {
+ fn is_zero(&self) -> bool {
+ self.0 == 0.
+ }
+}
+
+impl IsZero for Rems {
+ fn is_zero(&self) -> bool {
+ self.0 == 0.
+ }
+}
+
+impl IsZero for AbsoluteLength {
+ fn is_zero(&self) -> bool {
+ match self {
+ AbsoluteLength::Pixels(pixels) => pixels.is_zero(),
+ AbsoluteLength::Rems(rems) => rems.is_zero(),
+ }
+ }
+}
+
+impl IsZero for DefiniteLength {
+ fn is_zero(&self) -> bool {
+ match self {
+ DefiniteLength::Absolute(length) => length.is_zero(),
+ DefiniteLength::Fraction(fraction) => *fraction == 0.,
+ }
+ }
+}
+
+impl IsZero for Length {
+ fn is_zero(&self) -> bool {
+ match self {
+ Length::Definite(length) => length.is_zero(),
+ Length::Auto => false,
+ }
+ }
+}
+
+impl<T: IsZero + Debug + Clone> IsZero for Point<T> {
+ fn is_zero(&self) -> bool {
+ self.x.is_zero() && self.y.is_zero()
+ }
+}
+
+impl<T: IsZero + Debug + Clone> IsZero for Size<T> {
+ fn is_zero(&self) -> bool {
+ self.width.is_zero() && self.height.is_zero()
+ }
+}
+
+impl<T: IsZero + Debug + Clone> IsZero for Bounds<T> {
+ fn is_zero(&self) -> bool {
+ self.origin.is_zero() && self.size.is_zero()
+ }
+}
@@ -6,8 +6,8 @@ mod mac;
mod test;
use crate::{
- AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point,
- RasterizeGlyphParams, Result, Scene, ShapedLine, SharedString, Size,
+ AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId,
+ GlyphRasterizationParams, Pixels, Point, Result, Scene, ShapedLine, SharedString, Size,
};
use anyhow::anyhow;
use async_task::Runnable;
@@ -147,7 +147,7 @@ pub trait PlatformWindow {
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
fn draw(&self, scene: Scene);
- fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizeGlyphParams>>;
+ fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<GlyphRasterizationParams>>;
}
pub trait PlatformDispatcher: Send + Sync {
@@ -163,9 +163,13 @@ pub trait PlatformTextSystem: Send + Sync {
fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>>;
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>>;
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId>;
+ fn glyph_raster_bounds(
+ &self,
+ params: &GlyphRasterizationParams,
+ ) -> Result<Bounds<DevicePixels>>;
fn rasterize_glyph(
&self,
- glyph_id: &RasterizeGlyphParams,
+ params: &GlyphRasterizationParams,
) -> Result<(Size<DevicePixels>, Vec<u8>)>;
fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine;
fn wrap_line(
@@ -1,6 +1,6 @@
use crate::{
- point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad,
- RasterizeGlyphParams, Scene, Size,
+ point, size, AtlasTextureId, DevicePixels, GlyphRasterizationParams, MetalAtlas,
+ MonochromeSprite, Quad, Scene, Size,
};
use cocoa::{
base::{NO, YES},
@@ -22,7 +22,7 @@ pub struct MetalRenderer {
sprites_pipeline_state: metal::RenderPipelineState,
unit_vertices: metal::Buffer,
instances: metal::Buffer,
- glyph_atlas: Arc<MetalAtlas<RasterizeGlyphParams>>,
+ glyph_atlas: Arc<MetalAtlas<GlyphRasterizationParams>>,
}
impl MetalRenderer {
@@ -126,7 +126,7 @@ impl MetalRenderer {
&*self.layer
}
- pub fn glyph_atlas(&self) -> &Arc<MetalAtlas<RasterizeGlyphParams>> {
+ pub fn glyph_atlas(&self) -> &Arc<MetalAtlas<GlyphRasterizationParams>> {
&self.glyph_atlas
}
@@ -1,6 +1,6 @@
use crate::{
point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle,
- FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RasterizeGlyphParams, Result,
+ FontWeight, GlyphId, GlyphRasterizationParams, Pixels, PlatformTextSystem, Point, Result,
ShapedGlyph, ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS,
};
use anyhow::anyhow;
@@ -134,9 +134,16 @@ impl PlatformTextSystem for MacTextSystem {
self.0.read().glyph_for_char(font_id, ch)
}
+ fn glyph_raster_bounds(
+ &self,
+ params: &GlyphRasterizationParams,
+ ) -> Result<Bounds<DevicePixels>> {
+ self.0.read().raster_bounds(params)
+ }
+
fn rasterize_glyph(
&self,
- glyph_id: &RasterizeGlyphParams,
+ glyph_id: &GlyphRasterizationParams,
) -> Result<(Size<DevicePixels>, Vec<u8>)> {
self.0.read().rasterize_glyph(glyph_id)
}
@@ -230,37 +237,54 @@ impl MacTextSystemState {
})
}
+ fn raster_bounds(&self, params: &GlyphRasterizationParams) -> Result<Bounds<DevicePixels>> {
+ let font = &self.fonts[params.font_id.0];
+ let scale = Transform2F::from_scale(params.scale_factor);
+ Ok(font
+ .raster_bounds(
+ params.glyph_id.into(),
+ params.font_size.into(),
+ scale,
+ HintingOptions::None,
+ font_kit::canvas::RasterizationOptions::GrayscaleAa,
+ )?
+ .into())
+ }
+
fn rasterize_glyph(
&self,
- glyph_id: &RasterizeGlyphParams,
+ params: &GlyphRasterizationParams,
) -> Result<(Size<DevicePixels>, Vec<u8>)> {
- let font = &self.fonts[glyph_id.font_id.0];
- let scale = Transform2F::from_scale(glyph_id.scale_factor);
- let glyph_bounds = font.raster_bounds(
- glyph_id.glyph_id.into(),
- glyph_id.font_size.into(),
- scale,
- HintingOptions::None,
- font_kit::canvas::RasterizationOptions::GrayscaleAa,
- )?;
-
- if glyph_bounds.width() == 0 || glyph_bounds.height() == 0 {
+ let glyph_bounds = self.raster_bounds(params)?;
+
+ // let scale = Transform2F::from_scale(params.scale_factor);
+ // let glyph_bounds = font.raster_bounds(
+ // params.glyph_id.into(),
+ // params.font_size.into(),
+ // scale,
+ // HintingOptions::None,
+ // font_kit::canvas::RasterizationOptions::GrayscaleAa,
+ // )?;
+
+ if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 {
Err(anyhow!("glyph bounds are empty"))
} else {
- // Make room for subpixel variants.
- let subpixel_padding = Vector2I::new(
- glyph_id.subpixel_variant.x.min(1) as i32,
- glyph_id.subpixel_variant.y.min(1) as i32,
- );
- let bitmap_size = glyph_bounds.size() + subpixel_padding;
+ // Add an extra pixel when the subpixel variant isn't zero to make room for anti-aliasing.
+ let mut bitmap_size = glyph_bounds.size;
+ if params.subpixel_variant.x > 0 {
+ bitmap_size.width += DevicePixels(1);
+ }
+ if params.subpixel_variant.y > 0 {
+ bitmap_size.height += DevicePixels(1);
+ }
- let mut bytes = vec![0; bitmap_size.x() as usize * bitmap_size.y() as usize];
+ let mut bytes = vec![0; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize];
let cx = CGContext::create_bitmap_context(
Some(bytes.as_mut_ptr() as *mut _),
- bitmap_size.x() as usize,
- bitmap_size.y() as usize,
+ bitmap_size.width.0 as usize,
+ bitmap_size.height.0 as usize,
8,
- bitmap_size.x() as usize,
+ bitmap_size.width.0 as usize,
&CGColorSpace::create_device_gray(),
kCGImageAlphaOnly,
);
@@ -268,26 +292,27 @@ impl MacTextSystemState {
// Move the origin to bottom left and account for scaling, this
// makes drawing text consistent with the font-kit's raster_bounds.
cx.translate(
- -glyph_bounds.origin_x() as CGFloat,
- (glyph_bounds.origin_y() + glyph_bounds.height()) as CGFloat,
+ -glyph_bounds.origin.x.0 as CGFloat,
+ (glyph_bounds.origin.y.0 + glyph_bounds.size.height.0) as CGFloat,
);
cx.scale(
- glyph_id.scale_factor as CGFloat,
- glyph_id.scale_factor as CGFloat,
+ params.scale_factor as CGFloat,
+ params.scale_factor as CGFloat,
);
- let subpixel_shift = glyph_id
+ let subpixel_shift = params
.subpixel_variant
- .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32 / glyph_id.scale_factor);
+ .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32 / params.scale_factor);
cx.set_allows_font_subpixel_positioning(true);
cx.set_should_subpixel_position_fonts(true);
cx.set_allows_font_subpixel_quantization(false);
cx.set_should_subpixel_quantize_fonts(false);
- font.native_font()
- .clone_with_font_size(f32::from(glyph_id.font_size) as CGFloat)
+ self.fonts[params.font_id.0]
+ .native_font()
+ .clone_with_font_size(f32::from(params.font_size) as CGFloat)
.draw_glyphs(
- &[u32::from(glyph_id.glyph_id) as CGGlyph],
+ &[u32::from(params.glyph_id) as CGGlyph],
&[CGPoint::new(
subpixel_shift.x as CGFloat,
subpixel_shift.y as CGFloat,
@@ -1,10 +1,10 @@
use super::{ns_string, MetalRenderer, NSRange};
use crate::{
- point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers,
- ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt,
- Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen,
- PlatformWindow, Point, RasterizeGlyphParams, Scene, Size, Timer, WindowAppearance,
- WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
+ point, px, size, AnyWindowHandle, Bounds, Event, GlyphRasterizationParams, KeyDownEvent,
+ Keystroke, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
+ MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher,
+ PlatformInputHandler, PlatformScreen, PlatformWindow, Point, Scene, Size, Timer,
+ WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel,
};
use block::ConcreteBlock;
use cocoa::{
@@ -886,7 +886,7 @@ impl PlatformWindow for MacWindow {
}
}
- fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<RasterizeGlyphParams>> {
+ fn glyph_atlas(&self) -> Arc<dyn PlatformAtlas<GlyphRasterizationParams>> {
self.0.lock().renderer.glyph_atlas().clone()
}
}
@@ -1,6 +1,6 @@
use std::{iter::Peekable, mem};
-use super::{Bounds, Hsla, Pixels, Point};
+use super::{Bounds, Hsla, Point};
use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledPixels};
use collections::BTreeMap;
use smallvec::SmallVec;
@@ -35,7 +35,7 @@ impl Scene {
let primitive = primitive.into();
match primitive {
- Primitive::Quad(mut quad) => {
+ Primitive::Quad(quad) => {
layer.quads.push(quad);
}
Primitive::Sprite(sprite) => {
@@ -233,9 +233,9 @@ impl From<Quad> for Primitive {
#[repr(C)]
pub struct MonochromeSprite {
pub order: u32,
- pub bounds: Bounds<Pixels>,
- pub clip_bounds: Bounds<Pixels>,
- pub clip_corner_radii: Corners<Pixels>,
+ pub bounds: Bounds<ScaledPixels>,
+ pub clip_bounds: Bounds<ScaledPixels>,
+ pub clip_corner_radii: Corners<ScaledPixels>,
pub color: Hsla,
pub tile: AtlasTile,
}
@@ -215,9 +215,13 @@ impl TextSystem {
})
}
+ pub fn raster_bounds(&self, params: &GlyphRasterizationParams) -> Result<Bounds<DevicePixels>> {
+ self.platform_text_system.glyph_raster_bounds(params)
+ }
+
pub fn rasterize_glyph(
&self,
- glyph_id: &RasterizeGlyphParams,
+ glyph_id: &GlyphRasterizationParams,
) -> Result<(Size<DevicePixels>, Vec<u8>)> {
self.platform_text_system.rasterize_glyph(glyph_id)
}
@@ -380,7 +384,7 @@ pub struct ShapedGlyph {
}
#[derive(Clone, Debug, PartialEq)]
-pub struct RasterizeGlyphParams {
+pub struct GlyphRasterizationParams {
pub(crate) font_id: FontId,
pub(crate) glyph_id: GlyphId,
pub(crate) font_size: Pixels,
@@ -388,9 +392,9 @@ pub struct RasterizeGlyphParams {
pub(crate) scale_factor: f32,
}
-impl Eq for RasterizeGlyphParams {}
+impl Eq for GlyphRasterizationParams {}
-impl Hash for RasterizeGlyphParams {
+impl Hash for GlyphRasterizationParams {
fn hash<H: Hasher>(&self, state: &mut H) {
self.font_id.0.hash(state);
self.glyph_id.0.hash(state);
@@ -1,11 +1,10 @@
use crate::{
- black, point, px, Bounds, Corners, FontId, Hsla, Layout, MonochromeSprite, Pixels, Point,
- RunStyle, ShapedBoundary, ShapedLine, ShapedRun, UnderlineStyle, WindowContext,
+ black, point, px, Bounds, FontId, Hsla, Layout, Pixels, Point, RunStyle, ShapedBoundary,
+ ShapedLine, ShapedRun, UnderlineStyle, WindowContext,
};
use anyhow::Result;
use smallvec::SmallVec;
use std::sync::Arc;
-use util::ResultExt;
#[derive(Default, Debug, Clone)]
pub struct Line {
@@ -162,36 +161,14 @@ impl Line {
if glyph.is_emoji {
todo!()
} else {
- if let Some(tile) = cx
- .rasterize_glyph(
- run.font_id,
- glyph.id,
- self.layout.font_size,
- glyph_origin,
- cx.scale_factor(),
- )
- .log_err()
- {
- let layer_id = cx.current_layer_id();
-
- let bounds = Bounds {
- origin: glyph_origin + todo!(),
- size: todo!(),
- };
- // cx.text_system().raster_bounds()
-
- cx.scene().insert(
- layer_id,
- MonochromeSprite {
- order: layout.order,
- bounds,
- clip_bounds: bounds,
- clip_corner_radii: Corners::default(),
- color,
- tile,
- },
- );
- }
+ cx.paint_glyph(
+ glyph_origin,
+ layout.order,
+ run.font_id,
+ glyph.id,
+ self.layout.font_size,
+ color,
+ )?;
}
}
@@ -1,8 +1,9 @@
use crate::{
px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, Effect, Element, EntityId,
- FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformAtlas,
- PlatformWindow, Point, RasterizeGlyphParams, Reference, Scene, Size, StackContext,
- StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
+ FontId, GlyphId, GlyphRasterizationParams, Handle, Hsla, IsZero, LayoutId, MainThread,
+ MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference,
+ Scene, Size, StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions,
+ SUBPIXEL_VARIANTS,
};
use anyhow::Result;
use futures::Future;
@@ -15,7 +16,7 @@ pub struct AnyWindow {}
pub struct Window {
handle: AnyWindowHandle,
platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
- glyph_atlas: Arc<dyn PlatformAtlas<RasterizeGlyphParams>>,
+ glyph_atlas: Arc<dyn PlatformAtlas<GlyphRasterizationParams>>,
rem_size: Pixels,
content_size: Size<Pixels>,
layout_engine: TaffyLayoutEngine,
@@ -165,6 +166,57 @@ impl<'a, 'w> WindowContext<'a, 'w> {
})
}
+ pub fn paint_glyph(
+ &mut self,
+ origin: Point<Pixels>,
+ order: u32,
+ font_id: FontId,
+ glyph_id: GlyphId,
+ font_size: Pixels,
+ color: Hsla,
+ ) -> Result<()> {
+ let scale_factor = self.scale_factor();
+ let origin = origin.scale(scale_factor);
+ let subpixel_variant = Point {
+ x: (origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
+ y: (origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8,
+ };
+ let params = GlyphRasterizationParams {
+ font_id,
+ glyph_id,
+ font_size,
+ subpixel_variant,
+ scale_factor,
+ };
+
+ let raster_bounds = self.text_system().raster_bounds(¶ms)?;
+
+ if !raster_bounds.is_zero() {
+ let layer_id = self.current_layer_id();
+ let bounds = Bounds {
+ origin: origin + raster_bounds.origin.map(Into::into),
+ size: raster_bounds.size.map(Into::into),
+ };
+ let tile = self
+ .window
+ .glyph_atlas
+ .get_or_insert_with(¶ms, &mut || self.text_system().rasterize_glyph(¶ms))?;
+
+ self.window.scene.insert(
+ layer_id,
+ MonochromeSprite {
+ order,
+ bounds,
+ clip_bounds: bounds,
+ clip_corner_radii: Default::default(),
+ color,
+ tile,
+ },
+ );
+ }
+ Ok(())
+ }
+
pub fn rasterize_glyph(
&self,
font_id: FontId,
@@ -178,7 +230,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
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,
};
- let rasterized_glyph_id = RasterizeGlyphParams {
+ let rasterized_glyph_id = GlyphRasterizationParams {
font_id,
glyph_id,
font_size,