mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 09:32:17 +03:00
Render different variants according to subpixel positioning
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
9178e91cc0
commit
bc977fc873
@ -14,7 +14,6 @@ parking_lot = "0.11.1"
|
|||||||
pathfinder_color = "0.5"
|
pathfinder_color = "0.5"
|
||||||
pathfinder_geometry = "0.5"
|
pathfinder_geometry = "0.5"
|
||||||
pin-project = "1.0.5"
|
pin-project = "1.0.5"
|
||||||
png = "0.16"
|
|
||||||
rand = "0.8.3"
|
rand = "0.8.3"
|
||||||
replace_with = "0.1.7"
|
replace_with = "0.1.7"
|
||||||
smallvec = "1.6.1"
|
smallvec = "1.6.1"
|
||||||
@ -25,6 +24,9 @@ tree-sitter = "0.17"
|
|||||||
bindgen = "0.57"
|
bindgen = "0.57"
|
||||||
cc = "1.0.67"
|
cc = "1.0.67"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
png = "0.16"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
cocoa = "0.24"
|
cocoa = "0.24"
|
||||||
|
@ -158,29 +158,3 @@ impl FontCache {
|
|||||||
metric * font_size / self.metric(font_id, |m| m.units_per_em as f32)
|
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::{
|
geometry::{
|
||||||
rect::{RectF, RectI},
|
rect::{RectF, RectI},
|
||||||
transform2d::Transform2F,
|
transform2d::Transform2F,
|
||||||
vector::vec2f,
|
vector::{vec2f, vec2i},
|
||||||
},
|
},
|
||||||
platform,
|
platform,
|
||||||
text_layout::{Glyph, Line, Run},
|
text_layout::{Glyph, Line, Run},
|
||||||
@ -18,9 +18,7 @@ use core_foundation::{
|
|||||||
use core_graphics::{
|
use core_graphics::{
|
||||||
base::CGGlyph, color_space::CGColorSpace, context::CGContext, geometry::CGAffineTransform,
|
base::CGGlyph, color_space::CGColorSpace, context::CGContext, geometry::CGAffineTransform,
|
||||||
};
|
};
|
||||||
use core_text::{
|
use core_text::{line::CTLine, string_attributes::kCTFontAttributeName};
|
||||||
font_descriptor::kCTFontSizeAttribute, line::CTLine, string_attributes::kCTFontAttributeName,
|
|
||||||
};
|
|
||||||
use font_kit::{
|
use font_kit::{
|
||||||
canvas::RasterizationOptions, hinting::HintingOptions, metrics::Metrics,
|
canvas::RasterizationOptions, hinting::HintingOptions, metrics::Metrics,
|
||||||
properties::Properties, source::SystemSource,
|
properties::Properties, source::SystemSource,
|
||||||
@ -73,11 +71,12 @@ impl platform::FontSystem for FontSystem {
|
|||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
glyph_id: GlyphId,
|
glyph_id: GlyphId,
|
||||||
|
horizontal_shift: f32,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
) -> Option<(RectI, Vec<u8>)> {
|
) -> Option<(RectI, Vec<u8>)> {
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.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(
|
fn layout_str(
|
||||||
@ -127,6 +126,7 @@ impl FontSystemState {
|
|||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
glyph_id: GlyphId,
|
glyph_id: GlyphId,
|
||||||
|
horizontal_shift: f32,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
) -> Option<(RectI, Vec<u8>)> {
|
) -> Option<(RectI, Vec<u8>)> {
|
||||||
let font = &self.fonts[font_id.0];
|
let font = &self.fonts[font_id.0];
|
||||||
@ -144,6 +144,8 @@ impl FontSystemState {
|
|||||||
if bounds.width() == 0 || bounds.height() == 0 {
|
if bounds.width() == 0 || bounds.height() == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} 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 mut pixels = vec![0; bounds.width() as usize * bounds.height() as usize];
|
||||||
let ctx = CGContext::create_bitmap_context(
|
let ctx = CGContext::create_bitmap_context(
|
||||||
Some(pixels.as_mut_ptr() as *mut _),
|
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(&font.native_font().copy_to_CGFont());
|
||||||
ctx.set_font_size(font_size as CGFloat);
|
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))
|
Some((bounds, pixels))
|
||||||
}
|
}
|
||||||
@ -335,4 +343,32 @@ mod tests {
|
|||||||
);
|
);
|
||||||
Ok(())
|
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_id,
|
||||||
glyph.font_size,
|
glyph.font_size,
|
||||||
glyph.id,
|
glyph.id,
|
||||||
|
glyph.origin.x(),
|
||||||
scene.scale_factor(),
|
scene.scale_factor(),
|
||||||
) {
|
) {
|
||||||
sprites_by_atlas
|
sprites_by_atlas
|
||||||
.entry(sprite.atlas_id)
|
.entry(sprite.atlas_id)
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(shaders::GPUISprite {
|
.push(shaders::GPUISprite {
|
||||||
origin: (glyph.origin * scene.scale_factor() + sprite.offset.to_f32())
|
origin: (glyph.origin * scene.scale_factor() + sprite.offset).to_float2(),
|
||||||
.to_float2(),
|
|
||||||
size: sprite.size.to_float2(),
|
size: sprite.size.to_float2(),
|
||||||
atlas_origin: sprite.atlas_origin.to_float2(),
|
atlas_origin: sprite.atlas_origin.to_float2(),
|
||||||
color: glyph.color.to_uchar4(),
|
color: glyph.color.to_uchar4(),
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
fonts::{FontId, GlyphId},
|
fonts::{FontId, GlyphId},
|
||||||
geometry::{
|
geometry::{
|
||||||
rect::RectI,
|
rect::RectI,
|
||||||
vector::{vec2i, Vector2I},
|
vector::{vec2i, Vector2F, Vector2I},
|
||||||
},
|
},
|
||||||
platform,
|
platform,
|
||||||
};
|
};
|
||||||
@ -16,13 +16,14 @@ struct GlyphDescriptor {
|
|||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
font_size: OrderedFloat<f32>,
|
font_size: OrderedFloat<f32>,
|
||||||
glyph_id: GlyphId,
|
glyph_id: GlyphId,
|
||||||
|
subpixel_variant: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GlyphSprite {
|
pub struct GlyphSprite {
|
||||||
pub atlas_id: usize,
|
pub atlas_id: usize,
|
||||||
pub atlas_origin: Vector2I,
|
pub atlas_origin: Vector2I,
|
||||||
pub offset: Vector2I,
|
pub offset: Vector2F,
|
||||||
pub size: Vector2I,
|
pub size: Vector2I,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,21 +60,34 @@ impl SpriteCache {
|
|||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
glyph_id: GlyphId,
|
glyph_id: GlyphId,
|
||||||
|
target_x: f32,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
) -> Option<GlyphSprite> {
|
) -> Option<GlyphSprite> {
|
||||||
|
const SUBPIXEL_VARIANTS: u8 = 4;
|
||||||
|
|
||||||
|
let target_x = target_x * scale_factor;
|
||||||
let fonts = &self.fonts;
|
let fonts = &self.fonts;
|
||||||
let atlasses = &mut self.atlasses;
|
let atlasses = &mut self.atlasses;
|
||||||
let atlas_size = self.atlas_size;
|
let atlas_size = self.atlas_size;
|
||||||
let device = &self.device;
|
let device = &self.device;
|
||||||
|
let subpixel_variant =
|
||||||
|
(target_x.fract() * SUBPIXEL_VARIANTS as f32).round() as u8 % SUBPIXEL_VARIANTS;
|
||||||
self.glyphs
|
self.glyphs
|
||||||
.entry(GlyphDescriptor {
|
.entry(GlyphDescriptor {
|
||||||
font_id,
|
font_id,
|
||||||
font_size: OrderedFloat(font_size),
|
font_size: OrderedFloat(font_size),
|
||||||
glyph_id,
|
glyph_id,
|
||||||
|
subpixel_variant,
|
||||||
})
|
})
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
let (glyph_bounds, mask) =
|
let horizontal_shift = subpixel_variant as f32 / SUBPIXEL_VARIANTS as f32;
|
||||||
fonts.rasterize_glyph(font_id, font_size, glyph_id, scale_factor)?;
|
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.width() < atlas_size.x());
|
||||||
assert!(glyph_bounds.height() < atlas_size.y());
|
assert!(glyph_bounds.height() < atlas_size.y());
|
||||||
|
|
||||||
@ -88,10 +102,12 @@ impl SpriteCache {
|
|||||||
bounds
|
bounds
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut offset = glyph_bounds.origin().to_f32();
|
||||||
|
offset.set_x(offset.x() - target_x.fract());
|
||||||
Some(GlyphSprite {
|
Some(GlyphSprite {
|
||||||
atlas_id: atlasses.len() - 1,
|
atlas_id: atlasses.len() - 1,
|
||||||
atlas_origin: atlas_bounds.origin(),
|
atlas_origin: atlas_bounds.origin(),
|
||||||
offset: glyph_bounds.origin(),
|
offset,
|
||||||
size: glyph_bounds.size(),
|
size: glyph_bounds.size(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -78,6 +78,7 @@ pub trait FontSystem: Send + Sync {
|
|||||||
font_id: FontId,
|
font_id: FontId,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
glyph_id: GlyphId,
|
glyph_id: GlyphId,
|
||||||
|
horizontal_shift: f32,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
) -> Option<(RectI, Vec<u8>)>;
|
) -> Option<(RectI, Vec<u8>)>;
|
||||||
fn layout_str(&self, text: &str, font_size: f32, runs: &[(Range<usize>, FontId)]) -> Line;
|
fn layout_str(&self, text: &str, font_size: f32, runs: &[(Range<usize>, FontId)]) -> Line;
|
||||||
|
Loading…
Reference in New Issue
Block a user