Merge pull request #238 from exoticusd/master

Shrink Binary Size
macos-click-through
Keith Simmons 5 years ago committed by GitHub
commit bea55d321f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,7 +5,19 @@
"version": "0.2.0",
"configurations": [
{
"name": "(Windows) Launch",
"name": "(Mac/Linux) Debug",
"type": "lldb",
"request": "launch",
"program": "${workspaceRoot}/target/debug/neovide",
"args": [],
"cwd": "${workspaceRoot}",
"env": {
"RUST_LOG": "info",
"RUST_BACKTRACE": "1"
}
},
{
"name": "(Windows) Debug",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceRoot}/target/debug/neovide.exe",

18
.vscode/tasks.json vendored

@ -0,0 +1,18 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "cargo",
"command": "run",
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

1
Cargo.lock generated

@ -1151,6 +1151,7 @@ version = "0.5.0"
dependencies = [
"anyhow",
"async-trait",
"cfg-if",
"derive-new",
"euclid",
"flexi_logger",

@ -31,6 +31,7 @@ log = "0.4.8"
flexi_logger = { version = "0.14.6", default-features = false }
anyhow = "1.0.26"
parking_lot="0.10.0"
cfg-if = "0.1.10"
[target.'cfg(windows)'.dependencies]
winapi = "0.3.8"
@ -40,6 +41,8 @@ winres = "0.1.11"
[profile.release]
debug = true
lto = true
incremental = true
[package.metadata.bundle]
name = "Neovide"

Binary file not shown.

Binary file not shown.

@ -1,94 +0,0 @@
Copyright (c) 2019 - Present, Microsoft Corporation,
with Reserved Font Name Cascadia Code.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

@ -119,6 +119,7 @@ impl Editor {
pub fn build_draw_commands(&mut self) -> (Vec<DrawCommand>, bool) {
let mut draw_commands = Vec::new();
for (row_index, row) in self.grid.rows().enumerate() {
let mut command = None;

@ -1,45 +1,38 @@
use std::collections::HashMap;
use cfg_if::cfg_if as define;
use font_kit::{
family_name::FamilyName,
font::Font,
metrics::Metrics,
properties::{Properties, Stretch, Style, Weight},
source::SystemSource,
handle::Handle
};
use log::{trace, warn};
use lru::LruCache;
use skribo::{FontCollection, FontFamily, FontRef as SkriboFont, LayoutSession, TextStyle};
use skulpin::skia_safe::{Data, Font as SkiaFont, TextBlob, TextBlobBuilder, Typeface};
use log::{info, trace, warn};
use std::collections::HashMap;
const STANDARD_CHARACTER_STRING: &str =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
const MONOSPACE_FONT: &str = "Fira Code Regular Nerd Font Complete.otf";
const MONOSPACE_BOLD_FONT: &str = "Fira Code Bold Nerd Font Complete.otf";
const SYMBOL_FONT: &str = "DejaVuSansMono.ttf";
const EMOJI_FONT: &str = "NotoColorEmoji.ttf";
const WIDE_FONT: &str = "NotoSansMonoCJKjp-Regular.otf";
const WIDE_BOLD_FONT: &str = "NotoSansMonoCJKjp-Bold.otf";
#[cfg(target_os = "windows")]
const SYSTEM_SYMBOL_FONT: &str = "Segoe UI Symbol";
#[cfg(target_os = "linux")]
const SYSTEM_SYMBOL_FONT: &str = "Unifont";
#[cfg(target_os = "macos")]
const SYSTEM_SYMBOL_FONT: &str = "Apple Symbols";
#[cfg(target_os = "windows")]
const SYSTEM_EMOJI_FONT: &str = "Segoe UI Emoji";
#[cfg(target_os = "macos")]
const SYSTEM_EMOJI_FONT: &str = "Apple Color Emoji";
define! {
if #[cfg(target_os = "windows")] {
const SYSTEM_DEFAULT_FONT: &str = "Consolas";
const SYSTEM_SYMBOL_FONT: &str = "Segoe UI Symbol";
const SYSTEM_EMOJI_FONT: &str = "Segoe UI Emoji";
} else if #[cfg(target_os = "linux")] {
const SYSTEM_DEFAULT_FONT: &str = "Droid Sans Mono";
const SYSTEM_SYMBOL_FONT: &str = "Unifont";
const SYSTEM_EMOJI_FONT: &str = "Noto Color Emoji";
} else if #[cfg(target_os = "macos")] {
const SYSTEM_DEFAULT_FONT: &str = "Menlo";
const SYSTEM_SYMBOL_FONT: &str = "Apple Symbols";
const SYSTEM_EMOJI_FONT: &str = "Apple Color Emoji";
}
}
#[cfg(target_os = "linux")]
const SYSTEM_EMOJI_FONT: &str = "Noto Color Emoji";
const EXTRA_SYMBOL_FONT: &str = "Extra Symbols.otf";
const MISSING_GLYPH_FONT: &str = "Missing Glyphs.otf";
#[cfg(feature = "embed-fonts")]
#[derive(RustEmbed)]
@ -48,6 +41,114 @@ struct Asset;
const DEFAULT_FONT_SIZE: f32 = 14.0;
#[derive(Clone)]
pub struct ExtendedFontFamily {
pub fonts: Vec<SkriboFont>,
}
impl ExtendedFontFamily {
pub fn new() -> ExtendedFontFamily {
ExtendedFontFamily { fonts: Vec::new() }
}
pub fn add_font(&mut self, font: SkriboFont) {
self.fonts.push(font);
}
pub fn get(&self, props: Properties) -> Option<&Font> {
for handle in &self.fonts {
let font = &handle.font;
let properties = font.properties();
if properties.weight == props.weight && properties.style == props.style {
return Some(&font);
}
}
if let Some(handle) = &self.fonts.first() {
return Some(&handle.font);
}
None
}
pub fn from_normal_font_family(fonts: &[Handle]) -> ExtendedFontFamily {
let mut family = ExtendedFontFamily::new();
for font in fonts.into_iter() {
if let Ok(font) = font.load() {
family.add_font(SkriboFont::new(font));
}
}
family
}
pub fn to_normal_font_family(self) -> FontFamily {
let mut new_family = FontFamily::new();
for font in self.fonts {
new_family.add_font(font);
}
new_family
}
}
pub struct FontLoader {
cache: LruCache<String, ExtendedFontFamily>,
source: SystemSource,
}
impl FontLoader {
pub fn new() -> FontLoader {
FontLoader {
cache: LruCache::new(10),
source: SystemSource::new(),
}
}
fn get(&mut self, font_name: &str) -> Option<ExtendedFontFamily> {
return self.cache.get(&String::from(font_name)).cloned();
}
#[cfg(feature = "embed-fonts")]
fn load_from_asset(&mut self, font_name: &str) -> Option<ExtendedFontFamily> {
let mut family = ExtendedFontFamily::new();
Asset::get(font_name)
.and_then(|font_data| Font::from_bytes(font_data.to_vec().into(), 0).ok())
.map(|font| family.add_font(SkriboFont::new(font)));
self.cache.put(String::from(font_name), family);
self.get(font_name)
}
fn load(&mut self, font_name: &str) -> Option<ExtendedFontFamily> {
let handle = match self.source.select_family_by_name(font_name) {
Ok(it) => it,
_ => return None,
};
let family = ExtendedFontFamily::from_normal_font_family(handle.fonts());
self.cache.put(String::from(font_name), family);
self.get(font_name)
}
pub fn get_or_load(&mut self, font_name: &str, asset: bool) -> Option<ExtendedFontFamily> {
if let Some(family) = self.get(font_name) {
return Some(family);
}
if asset {
self.load_from_asset(font_name)
} else {
self.load(font_name)
}
}
}
#[derive(new, Clone, Hash, PartialEq, Eq, Debug)]
struct ShapeKey {
pub text: String,
@ -55,94 +156,45 @@ struct ShapeKey {
pub italic: bool,
}
pub fn add_font_to_collection_by_name(
name: &str,
source: &SystemSource,
collection: &mut FontCollection,
) -> Option<()> {
source
.select_family_by_name(name)
.ok()
.and_then(|matching_fonts| matching_fonts.fonts()[0].load().ok())
.map(|font| collection.add_family(FontFamily::new_from_font(font)))
}
#[cfg(feature = "embed-fonts")]
pub fn add_asset_font_to_collection(name: &str, collection: &mut FontCollection) -> Option<()> {
Asset::get(name)
.and_then(|font_data| Font::from_bytes(font_data.to_vec().into(), 0).ok())
.map(|font| collection.add_family(FontFamily::new_from_font(font)))
}
pub fn build_collection_by_font_name(
loader: &mut FontLoader,
font_name: Option<&str>,
bold: bool,
italic: bool,
) -> FontCollection {
let source = SystemSource::new();
let mut collection = FontCollection::new();
if let Some(font_name) = font_name {
let weight = if bold { Weight::BOLD } else { Weight::NORMAL };
let style = if italic { Style::Italic } else { Style::Normal };
let properties = Properties {
weight,
style,
stretch: Stretch::NORMAL,
};
if let Ok(custom) =
source.select_best_match(&[FamilyName::Title(font_name.to_string())], &properties)
{
custom
.load()
.ok()
.map(|matching_font| {
collection.add_family(FontFamily::new_from_font(matching_font))
})
.unwrap_or_else(|| warn!("Could not load gui font"));
}
}
#[cfg(feature = "embed-fonts")]
{
let monospace_style = if bold {
MONOSPACE_BOLD_FONT
} else {
MONOSPACE_FONT
};
add_asset_font_to_collection(monospace_style, &mut collection)
.unwrap_or_else(|| warn!("Could not load embedded monospace font"));
}
let gui_fonts = &[font_name, SYSTEM_DEFAULT_FONT];
if add_font_to_collection_by_name(SYSTEM_EMOJI_FONT, &source, &mut collection).is_none() {
#[cfg(feature = "embed-fonts")]
{
if cfg!(not(target_os = "macos"))
&& add_asset_font_to_collection(EMOJI_FONT, &mut collection).is_some()
{
info!("Fell back to embedded emoji font");
} else {
warn!("Could not load emoji font");
for font_name in gui_fonts {
if let Some(family) = loader.get_or_load(font_name, false) {
if let Some(font) = family.get(properties) {
collection.add_family(FontFamily::new_from_font(font.clone()));
break;
}
}
}
}
add_font_to_collection_by_name(SYSTEM_SYMBOL_FONT, &source, &mut collection)
.unwrap_or_else(|| warn!("Could not load system symbol font"));
#[cfg(feature = "embed-fonts")]
{
let wide_style = if bold { WIDE_BOLD_FONT } else { WIDE_FONT };
add_asset_font_to_collection(wide_style, &mut collection)
.unwrap_or_else(|| warn!("Could not load embedded wide font"));
for font in &[SYSTEM_SYMBOL_FONT, SYSTEM_EMOJI_FONT] {
if let Some(family) = loader.get_or_load(font, false) {
collection.add_family(family.to_normal_font_family());
}
}
add_asset_font_to_collection(SYMBOL_FONT, &mut collection)
.unwrap_or_else(|| warn!("Could not load embedded symbol font"));
for font in &[EXTRA_SYMBOL_FONT, MISSING_GLYPH_FONT] {
if let Some(family) = loader.get_or_load(font, true) {
collection.add_family(family.to_normal_font_family());
}
}
collection
@ -155,11 +207,11 @@ struct FontSet {
}
impl FontSet {
fn new(font_name: Option<&str>) -> FontSet {
fn new(font_name: Option<&str>, mut loader: &mut FontLoader) -> FontSet {
FontSet {
normal: build_collection_by_font_name(font_name, false, false),
bold: build_collection_by_font_name(font_name, true, false),
italic: build_collection_by_font_name(font_name, false, true),
normal: build_collection_by_font_name(&mut loader, font_name, false, false),
bold: build_collection_by_font_name(&mut loader, font_name, true, false),
italic: build_collection_by_font_name(&mut loader, font_name, false, true),
}
}
@ -176,6 +228,7 @@ pub struct CachingShaper {
pub font_name: Option<String>,
pub base_size: f32,
font_set: FontSet,
font_loader: FontLoader,
font_cache: LruCache<String, SkiaFont>,
blob_cache: LruCache<ShapeKey, Vec<TextBlob>>,
}
@ -190,10 +243,13 @@ fn build_skia_font_from_skribo_font(skribo_font: &SkriboFont, base_size: f32) ->
impl CachingShaper {
pub fn new() -> CachingShaper {
let mut loader = FontLoader::new();
CachingShaper {
font_name: None,
font_name: Some(String::from(SYSTEM_DEFAULT_FONT)),
base_size: DEFAULT_FONT_SIZE,
font_set: FontSet::new(None),
font_set: FontSet::new(Some(SYSTEM_DEFAULT_FONT), &mut loader),
font_loader: loader,
font_cache: LruCache::new(10),
blob_cache: LruCache::new(10000),
}
@ -201,6 +257,7 @@ impl CachingShaper {
fn get_skia_font(&mut self, skribo_font: &SkriboFont) -> Option<&SkiaFont> {
let font_name = skribo_font.font.postscript_name()?;
if !self.font_cache.contains(&font_name) {
let font = build_skia_font_from_skribo_font(skribo_font, self.base_size)?;
self.font_cache.put(font_name.clone(), font);
@ -214,7 +271,7 @@ impl CachingShaper {
.normal
.itemize("a")
.next()
.unwrap()
.expect("Cannot get font metrics")
.1
.font
.metrics()
@ -226,17 +283,15 @@ impl CachingShaper {
};
let session = LayoutSession::create(text, &style, &self.font_set.get(bold, italic));
let metrics = self.metrics();
let ascent = metrics.ascent * self.base_size / metrics.units_per_em as f32;
let mut blobs = Vec::new();
for layout_run in session.iter_all() {
let skribo_font = layout_run.font();
if let Some(skia_font) = self.get_skia_font(&skribo_font) {
let mut blob_builder = TextBlobBuilder::new();
let count = layout_run.glyphs().count();
let (glyphs, positions) =
blob_builder.alloc_run_pos_h(&skia_font, count, ascent, None);
@ -245,9 +300,10 @@ impl CachingShaper {
glyphs[i] = glyph.glyph_id as u16;
positions[i] = glyph.offset.x;
}
blobs.push(blob_builder.make().unwrap());
} else {
warn!("Could not load scribo font");
warn!("Could not load skribo font");
}
}
@ -256,6 +312,7 @@ impl CachingShaper {
pub fn shape_cached(&mut self, text: &str, bold: bool, italic: bool) -> &Vec<TextBlob> {
let key = ShapeKey::new(text.to_string(), bold, italic);
if !self.blob_cache.contains(&key) {
let blobs = self.shape(text, bold, italic);
self.blob_cache.put(key.clone(), blobs);
@ -268,7 +325,7 @@ impl CachingShaper {
trace!("Font changed {:?} {:?}", &font_name, &base_size);
self.font_name = font_name.map(|name| name.to_string());
self.base_size = base_size.unwrap_or(DEFAULT_FONT_SIZE);
self.font_set = FontSet::new(font_name);
self.font_set = FontSet::new(font_name, &mut self.font_loader);
self.font_cache.clear();
self.blob_cache.clear();
}
@ -277,13 +334,11 @@ impl CachingShaper {
let metrics = self.metrics();
let font_height =
(metrics.ascent - metrics.descent) * self.base_size / metrics.units_per_em as f32;
let style = TextStyle {
size: self.base_size,
};
let session =
LayoutSession::create(STANDARD_CHARACTER_STRING, &style, &self.font_set.normal);
let layout_run = session.iter_all().next().unwrap();
let glyph_offsets: Vec<f32> = layout_run.glyphs().map(|glyph| glyph.offset.x).collect();
let glyph_advances: Vec<f32> = glyph_offsets
@ -292,12 +347,14 @@ impl CachingShaper {
.collect();
let mut amounts = HashMap::new();
for advance in glyph_advances.iter() {
amounts
.entry(advance.to_string())
.and_modify(|e| *e += 1)
.or_insert(1);
}
let (font_width, _) = amounts.into_iter().max_by_key(|(_, count)| *count).unwrap();
let font_width = font_width.parse::<f32>().unwrap();
@ -309,25 +366,3 @@ impl CachingShaper {
-metrics.underline_position * self.base_size / metrics.units_per_em as f32
}
}
#[cfg(test)]
mod tests {
use super::*;
use font_kit::source::SystemSource;
use skribo::FontCollection;
#[test]
fn unmatched_font_returns_nothing() {
assert!(add_font_to_collection_by_name(
"Foobar",
&SystemSource::new(),
&mut FontCollection::new()
)
.is_none());
}
#[test]
fn build_font_collection_works() {
build_collection_by_font_name(None, true, true);
}
}

@ -105,14 +105,17 @@ impl Corner {
) -> bool {
// Update destination if needed
let mut immediate_movement = false;
if destination != self.previous_destination {
let travel_distance = destination - self.previous_destination;
let chars_travel_x = travel_distance.x / font_dimensions.x;
if travel_distance.y == 0.0 && (chars_travel_x - 1.0).abs() < 0.1 {
// We're moving one character to the right. Make movement immediate to avoid lag
// while typing
immediate_movement = true;
}
self.t = 0.0;
self.start_position = self.current_position;
self.previous_destination = destination;
@ -127,8 +130,7 @@ impl Corner {
let relative_scaled_position: Point = (
self.relative_position.x * font_dimensions.x,
self.relative_position.y * font_dimensions.y,
)
.into();
).into();
let corner_destination = destination + relative_scaled_position;
@ -240,7 +242,6 @@ impl CursorRenderer {
dt: f32,
) {
let render = self.blink_status.update_status(&cursor);
let settings = SETTINGS.get::<CursorSettings>();
if settings.vfx_mode != self.previous_vfx_mode {
@ -270,7 +271,6 @@ impl CursorRenderer {
};
let (grid_x, grid_y) = self.previous_position;
let (character, font_dimensions): (String, Point) = {
let editor = EDITOR.lock();
let character = match editor.grid.get_cell(grid_x, grid_y) {
@ -289,9 +289,9 @@ impl CursorRenderer {
};
(character, (font_width, font_height).into())
};
let destination: Point = (grid_x as f32 * font_width, grid_y as f32 * font_height).into();
let center_destination = destination + font_dimensions * 0.5;
let new_cursor = Some(cursor.shape.clone());
if self.previous_cursor_shape != new_cursor {
@ -307,6 +307,7 @@ impl CursorRenderer {
}
let mut animating = false;
if !center_destination.is_zero() {
for corner in self.corners.iter_mut() {
let corner_animating =

@ -6,8 +6,8 @@ use skulpin::skia_safe::{colors, dash_path_effect, Budgeted, Canvas, Paint, Rect
use skulpin::CoordinateSystemHelper;
mod caching_shaper;
pub mod cursor_renderer;
pub mod cursor_renderer;
pub use caching_shaper::CachingShaper;
use crate::editor::{Style, EDITOR};
@ -151,6 +151,7 @@ impl Renderer {
dt: f32,
) -> bool {
trace!("Rendering");
let ((draw_commands, should_clear), default_style, cursor, font_name, font_size) = {
let mut editor = EDITOR.lock();
(
@ -162,10 +163,14 @@ impl Renderer {
)
};
let font_changed = font_name != self.shaper.font_name
let current_font = Some(self.shaper.font_name.clone().unwrap_or(String::from("")));
let editor_font = if font_name.clone().unwrap_or_default().is_empty() { &current_font } else { &font_name };
let font_changed = current_font != *editor_font
|| font_size
.map(|new_size| (new_size - self.shaper.base_size).abs() > std::f32::EPSILON)
.unwrap_or(false);
if font_changed {
self.set_font(font_name.as_deref(), font_size);
}

Loading…
Cancel
Save