Detailed changes
@@ -14,7 +14,6 @@ parking_lot = "0.11.1"
pathfinder_color = "0.5"
pathfinder_geometry = "0.5"
pin-project = "1.0.5"
-png = "0.16"
rand = "0.8.3"
replace_with = "0.1.7"
smallvec = "1.6.1"
@@ -25,6 +24,9 @@ tree-sitter = "0.17"
bindgen = "0.57"
cc = "1.0.67"
+[dev-dependencies]
+png = "0.16"
+
[target.'cfg(target_os = "macos")'.dependencies]
anyhow = "1"
cocoa = "0.24"
@@ -158,29 +158,3 @@ impl FontCache {
metric * font_size / self.metric(font_id, |m| m.units_per_em as f32)
}
}
-
-// #[cfg(test)]
-// mod tests {
-// use std::{fs::File, io::BufWriter, path::Path};
-
-// use super::*;
-
-// #[test]
-// fn test_render_glyph() {
-// let cache = FontCache::new();
-// let family_id = cache.load_family(&["Fira Code"]).unwrap();
-// let font_id = cache.select_font(family_id, &Default::default()).unwrap();
-// let glyph_id = cache.font(font_id).glyph_for_char('G').unwrap();
-// let (bounds, bytes) = cache.render_glyph(font_id, 16.0, glyph_id, 1.).unwrap();
-
-// let path = Path::new(r"/Users/as-cii/Desktop/image.png");
-// let file = File::create(path).unwrap();
-// let ref mut w = BufWriter::new(file);
-
-// let mut encoder = png::Encoder::new(w, bounds.width() as u32, bounds.height() as u32);
-// encoder.set_color(png::ColorType::Grayscale);
-// encoder.set_depth(png::BitDepth::Eight);
-// let mut writer = encoder.write_header().unwrap();
-// writer.write_image_data(&bytes).unwrap();
-// }
-// }
@@ -3,7 +3,7 @@ use crate::{
geometry::{
rect::{RectF, RectI},
transform2d::Transform2F,
- vector::vec2f,
+ vector::{vec2f, vec2i},
},
platform,
text_layout::{Glyph, Line, Run},
@@ -18,9 +18,7 @@ use core_foundation::{
use core_graphics::{
base::CGGlyph, color_space::CGColorSpace, context::CGContext, geometry::CGAffineTransform,
};
-use core_text::{
- font_descriptor::kCTFontSizeAttribute, line::CTLine, string_attributes::kCTFontAttributeName,
-};
+use core_text::{line::CTLine, string_attributes::kCTFontAttributeName};
use font_kit::{
canvas::RasterizationOptions, hinting::HintingOptions, metrics::Metrics,
properties::Properties, source::SystemSource,
@@ -73,11 +71,12 @@ impl platform::FontSystem for FontSystem {
font_id: FontId,
font_size: f32,
glyph_id: GlyphId,
+ horizontal_shift: f32,
scale_factor: f32,
) -> Option<(RectI, Vec<u8>)> {
self.0
.read()
- .rasterize_glyph(font_id, font_size, glyph_id, scale_factor)
+ .rasterize_glyph(font_id, font_size, glyph_id, horizontal_shift, scale_factor)
}
fn layout_str(
@@ -127,6 +126,7 @@ impl FontSystemState {
font_id: FontId,
font_size: f32,
glyph_id: GlyphId,
+ horizontal_shift: f32,
scale_factor: f32,
) -> Option<(RectI, Vec<u8>)> {
let font = &self.fonts[font_id.0];
@@ -144,6 +144,8 @@ impl FontSystemState {
if bounds.width() == 0 || bounds.height() == 0 {
None
} else {
+ // Make room for subpixel variants.
+ let bounds = RectI::new(bounds.origin(), bounds.size() + vec2i(1, 0));
let mut pixels = vec![0; bounds.width() as usize * bounds.height() as usize];
let ctx = CGContext::create_bitmap_context(
Some(pixels.as_mut_ptr() as *mut _),
@@ -170,7 +172,13 @@ impl FontSystemState {
ctx.set_font(&font.native_font().copy_to_CGFont());
ctx.set_font_size(font_size as CGFloat);
- ctx.show_glyphs_at_positions(&[glyph_id as CGGlyph], &[CGPoint::new(0.0, 0.0)]);
+ ctx.show_glyphs_at_positions(
+ &[glyph_id as CGGlyph],
+ &[CGPoint::new(
+ (horizontal_shift / scale_factor) as CGFloat,
+ 0.0,
+ )],
+ );
Some((bounds, pixels))
}
@@ -335,4 +343,32 @@ mod tests {
);
Ok(())
}
+
+ // #[test]
+ // fn test_render_glyph() {
+ // use std::{fs::File, io::BufWriter, path::Path};
+
+ // let fonts = FontSystem::new();
+ // let font_ids = fonts.load_family("Fira Code").unwrap();
+ // let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap();
+ // let glyph_id = fonts.glyph_for_char(font_id, 'G').unwrap();
+
+ // for i in 0..2 {
+ // let variant = 0.5 * i as f32;
+ // let (bounds, bytes) = fonts
+ // .rasterize_glyph(font_id, 16.0, glyph_id, variant, 2.)
+ // .unwrap();
+
+ // let name = format!("/Users/as-cii/Desktop/twog-{}.png", i);
+ // let path = Path::new(&name);
+ // let file = File::create(path).unwrap();
+ // let ref mut w = BufWriter::new(file);
+
+ // let mut encoder = png::Encoder::new(w, bounds.width() as u32, bounds.height() as u32);
+ // encoder.set_color(png::ColorType::Grayscale);
+ // encoder.set_depth(png::BitDepth::Eight);
+ // let mut writer = encoder.write_header().unwrap();
+ // writer.write_image_data(&bytes).unwrap();
+ // }
+ // }
}
@@ -272,14 +272,14 @@ impl Renderer {
glyph.font_id,
glyph.font_size,
glyph.id,
+ glyph.origin.x(),
scene.scale_factor(),
) {
sprites_by_atlas
.entry(sprite.atlas_id)
.or_insert_with(Vec::new)
.push(shaders::GPUISprite {
- origin: (glyph.origin * scene.scale_factor() + sprite.offset.to_f32())
- .to_float2(),
+ origin: (glyph.origin * scene.scale_factor() + sprite.offset).to_float2(),
size: sprite.size.to_float2(),
atlas_origin: sprite.atlas_origin.to_float2(),
color: glyph.color.to_uchar4(),
@@ -2,7 +2,7 @@ use crate::{
fonts::{FontId, GlyphId},
geometry::{
rect::RectI,
- vector::{vec2i, Vector2I},
+ vector::{vec2i, Vector2F, Vector2I},
},
platform,
};
@@ -16,13 +16,14 @@ struct GlyphDescriptor {
font_id: FontId,
font_size: OrderedFloat<f32>,
glyph_id: GlyphId,
+ subpixel_variant: u8,
}
#[derive(Clone)]
pub struct GlyphSprite {
pub atlas_id: usize,
pub atlas_origin: Vector2I,
- pub offset: Vector2I,
+ pub offset: Vector2F,
pub size: Vector2I,
}
@@ -59,21 +60,34 @@ impl SpriteCache {
font_id: FontId,
font_size: f32,
glyph_id: GlyphId,
+ target_x: f32,
scale_factor: f32,
) -> Option<GlyphSprite> {
+ const SUBPIXEL_VARIANTS: u8 = 4;
+
+ let target_x = target_x * scale_factor;
let fonts = &self.fonts;
let atlasses = &mut self.atlasses;
let atlas_size = self.atlas_size;
let device = &self.device;
+ let subpixel_variant =
+ (target_x.fract() * SUBPIXEL_VARIANTS as f32).round() as u8 % SUBPIXEL_VARIANTS;
self.glyphs
.entry(GlyphDescriptor {
font_id,
font_size: OrderedFloat(font_size),
glyph_id,
+ subpixel_variant,
})
.or_insert_with(|| {
- let (glyph_bounds, mask) =
- fonts.rasterize_glyph(font_id, font_size, glyph_id, scale_factor)?;
+ let horizontal_shift = subpixel_variant as f32 / SUBPIXEL_VARIANTS as f32;
+ let (glyph_bounds, mask) = fonts.rasterize_glyph(
+ font_id,
+ font_size,
+ glyph_id,
+ horizontal_shift,
+ scale_factor,
+ )?;
assert!(glyph_bounds.width() < atlas_size.x());
assert!(glyph_bounds.height() < atlas_size.y());
@@ -88,10 +102,12 @@ impl SpriteCache {
bounds
});
+ let mut offset = glyph_bounds.origin().to_f32();
+ offset.set_x(offset.x() - target_x.fract());
Some(GlyphSprite {
atlas_id: atlasses.len() - 1,
atlas_origin: atlas_bounds.origin(),
- offset: glyph_bounds.origin(),
+ offset,
size: glyph_bounds.size(),
})
})
@@ -78,6 +78,7 @@ pub trait FontSystem: Send + Sync {
font_id: FontId,
font_size: f32,
glyph_id: GlyphId,
+ horizontal_shift: f32,
scale_factor: f32,
) -> Option<(RectI, Vec<u8>)>;
fn layout_str(&self, text: &str, font_size: f32, runs: &[(Range<usize>, FontId)]) -> Line;