Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDF-based mechanism for making letters bold #3385

Merged
merged 23 commits into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ff13403
WIP
farmaazon Apr 5, 2022
af0e995
Bold letters in SDF
farmaazon Apr 6, 2022
2290605
Working TextArea with SDF bold text
farmaazon Apr 6, 2022
0a944a6
Refactored some constants
farmaazon Apr 6, 2022
595f918
Fix
farmaazon Apr 7, 2022
76418de
Added set_font method
farmaazon Apr 7, 2022
c8c8626
Linter fix
farmaazon Apr 7, 2022
8a1ffe1
Make bold factor actually depending on the font size
farmaazon Apr 7, 2022
612d944
Apply lint comment
farmaazon Apr 7, 2022
f401664
Self-review + fix GLSL for mac
farmaazon Apr 7, 2022
eacadd7
Add CHANGELOG note
farmaazon Apr 7, 2022
a649c4b
Merge branch 'develop' into wip/farmaazon/text-sdf-bold-181641027
farmaazon Apr 8, 2022
9aa470a
Apply @wdanilo review
farmaazon Apr 8, 2022
5cd746d
Highlight !== Bold
farmaazon Apr 11, 2022
f75b6be
Always call defaulted atom arguments (#3358)
kustosz Apr 8, 2022
b7ca2a6
Merge branch 'develop' into wip/farmaazon/text-sdf-bold-181641027
farmaazon Apr 11, 2022
5bb9fe7
Merge branch 'develop' into wip/farmaazon/text-sdf-bold-181641027
mergify[bot] Apr 11, 2022
79258c9
Rename "highlight" to "dilate"
farmaazon Apr 12, 2022
250bd34
QA: extend debug scene for visual inspection
akavel Apr 12, 2022
8d9ae69
Revert "Rename "highlight" to "dilate""
farmaazon Apr 12, 2022
c951849
Rename again
farmaazon Apr 12, 2022
2ee2a65
Merge remote-tracking branch 'origin/develop' into wip/farmaazon/text…
farmaazon Apr 12, 2022
7b75fda
Merge remote-tracking branch 'origin/develop' into wip/farmaazon/text…
farmaazon Apr 12, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
- [Fixed developer console error about failing to decode a notification
"executionContext/visualisationEvaluationFailed"][3193]

#### EnsoGL (rendering engine)

- [You can change font and set letters bold in the <code>text::Area</code>
component][3385]. Use the <code>set_font</code> and
<code>set_bold_bytes</code> respectively.

#### Enso Standard Library

- [Implemented `Vector.distinct` allowing to remove duplicate elements from a
Expand Down Expand Up @@ -152,6 +158,7 @@
[3379]: https://github.com/enso-org/enso/pull/3379
[3381]: https://github.com/enso-org/enso/pull/3381
[3383]: https://github.com/enso-org/enso/pull/3383
[3385]: https://github.com/enso-org/enso/pull/3385
[3392]: https://github.com/enso-org/enso/pull/3392

#### Enso Compiler
Expand Down
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions lib/rust/ensogl/component/text/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ensogl-core = { path = "../../core" }
ensogl-text-embedded-fonts = { path = "embedded-fonts" }
ensogl-text-msdf-sys = { path = "msdf-sys" }
ensogl-hardcoded-theme = { path = "../../app/theme/hardcoded" }
const_format = "0.2.22"
xi-rope = { version = "0.3.0" }

[dev-dependencies]
Expand Down
8 changes: 8 additions & 0 deletions lib/rust/ensogl/component/text/src/buffer/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ def_style_property!(Size(f32));
def_style_property!(Bold(bool));
def_style_property!(Italic(bool));
def_style_property!(Underline(bool));
def_style_property!(SdfBold(f32));

impl Default for Size {
fn default() -> Self {
Expand All @@ -247,12 +248,19 @@ impl Default for Underline {
}
}

impl Default for SdfBold {
fn default() -> Self {
Self::new(0.0)
}
}

define_styles! {
size : Size,
color : color::Rgba,
bold : Bold,
italics : Italic,
underline : Underline,
sdf_bold : SdfBold,
}


Expand Down
4 changes: 3 additions & 1 deletion lib/rust/ensogl/component/text/src/buffer/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,8 @@ ensogl_core::define_endpoints! {
redo (),
set_default_color (color::Rgba),
set_default_text_size (style::Size),
set_color_bytes (buffer::Range<Bytes>,color::Rgba),
set_color_bytes (buffer::Range<Bytes>, color::Rgba),
set_sdf_bold (buffer::Range<Bytes>, style::SdfBold),
}

Output {
Expand Down Expand Up @@ -449,6 +450,7 @@ impl View {
eval input.set_default_color ((t) m.set_default(*t));
eval input.set_default_text_size ((t) m.set_default(*t));
eval input.set_color_bytes (((range,color)) m.replace(range,*color));
eval input.set_sdf_bold (((range,value)) m.replace(range,*value));
eval input.set_default_color ((color) m.set_default(*color));

output.source.selection_edit_mode <+ sel_on_modification;
Expand Down
62 changes: 50 additions & 12 deletions lib/rust/ensogl/component/text/src/component/area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,16 @@ ensogl_core::define_endpoints! {
insert (String),
set_color_bytes (buffer::Range<Bytes>,color::Rgba),
set_color_all (color::Rgba),
set_sdf_bold (buffer::Range<Bytes>,style::SdfBold),
set_default_color (color::Rgba),
set_selection_color (color::Rgb),
set_default_text_size (style::Size),
/// Set font in the text area. The name will be looked up in [`typeface::font::Registry`].
///
/// Note, that this is a relatively heavy operation - it requires not only redrawing all
/// lines, but also re-load internal structures for rendering (like WebGL buffers,
/// MSDF texture, etc.).
set_font (String),
set_content (String),
}
Output {
Expand Down Expand Up @@ -474,6 +481,11 @@ impl Area {
input.remove_all_cursors();
});

// === Font ===

eval input.set_font ((t) m.set_font(t));


// === Colors ===

eval input.set_default_color ((t) m.buffer.frp.set_default_color(*t));
Expand All @@ -495,6 +507,13 @@ impl Area {
});
self.frp.source.selection_color <+ self.frp.set_selection_color;


// === Style ===

m.buffer.frp.set_sdf_bold <+ input.set_sdf_bold;
eval_ input.set_sdf_bold (m.redraw(false));


// === Changes ===

// The `content` event should be fired first, as any listener for `changed` may want to
Expand Down Expand Up @@ -545,7 +564,7 @@ impl Area {

#[cfg(target_arch = "wasm32")]
fn symbols(&self) -> SmallVec<[display::Symbol; 1]> {
let text_symbol = self.data.glyph_system.sprite_system().symbol.clone_ref();
let text_symbol = self.data.glyph_system.borrow().sprite_system().symbol.clone_ref();
let shapes = &self.data.app.display.default_scene.shapes;
let selection_system = shapes.shape_system(PhantomData::<selection::shape::Shape>);
let _selection_symbol = selection_system.shape_system.symbol.clone_ref();
Expand Down Expand Up @@ -573,7 +592,7 @@ pub struct AreaModel {
buffer: buffer::View,
display_object: display::object::Instance,
#[cfg(target_arch = "wasm32")]
glyph_system: glyph::System,
glyph_system: Rc<RefCell<glyph::System>>,
lines: Lines,
single_line: Rc<Cell<bool>>,
selection_map: Rc<RefCell<SelectionMap>>,
Expand All @@ -586,21 +605,20 @@ impl AreaModel {
let scene = &app.display.default_scene;
let logger = Logger::new("text_area");
let selection_map = default();
let display_object = display::object::Instance::new(&logger);
#[cfg(target_arch = "wasm32")]
let glyph_system = {
let fonts = scene.extension::<typeface::font::Registry>();
let font = fonts.load("DejaVuSansMono");
typeface::glyph::System::new(&scene, font)
let glyph_system = typeface::glyph::System::new(&scene, font);
display_object.add_child(&glyph_system);
Rc::new(RefCell::new(glyph_system))
};
let display_object = display::object::Instance::new(&logger);
let buffer = default();
let lines = default();
let single_line = default();
let camera = Rc::new(CloneRefCell::new(scene.camera().clone_ref()));

#[cfg(target_arch = "wasm32")]
display_object.add_child(&glyph_system);

// FIXME[WD]: These settings should be managed wiser. They should be set up during
// initialization of the shape system, not for every area creation. To be improved during
// refactoring of the architecture some day.
Expand Down Expand Up @@ -792,12 +810,13 @@ impl AreaModel {
let line_object = line.display_object().clone_ref();
let line_range = self.buffer.byte_range_of_view_line_index_snapped(view_line_index.into());
let mut line_style = self.buffer.sub_style(line_range.start..line_range.end).iter();
let mut pen = pen::Pen::new(&self.glyph_system.font);
let glyph_system = self.glyph_system.borrow();
let mut pen = pen::Pen::new(&glyph_system.font);
let mut divs = vec![];
let mut column = 0.column();
let mut last_cursor = None;
let mut last_cursor_target = default();
line.resize_with(content.chars().count(), || self.glyph_system.new_glyph());
line.resize_with(content.chars().count(), || glyph_system.new_glyph());
let mut iter = line.glyphs.iter_mut().zip(content.chars());
loop {
let next = iter.next();
Expand All @@ -822,15 +841,16 @@ impl AreaModel {
Some((glyph, chr)) => {
let chr_bytes: Bytes = chr.len_utf8().into();
line_style.drop(chr_bytes - 1.bytes());
let glyph_info = self.glyph_system.font.glyph_info(chr);
let size = glyph_info.scale.scale(chr_size);
let glyph_info = glyph_system.font.glyph_info(chr);
let glyph_offset = glyph_info.offset.scale(chr_size);
let glyph_x = info.offset + glyph_offset.x;
let glyph_y = glyph_offset.y;
glyph.set_position_xy(Vector2(glyph_x, glyph_y));
glyph.set_char(chr);
glyph.set_color(style.color);
glyph.size.set(size);
glyph.set_bold(style.bold.raw);
glyph.set_sdf_bold(style.sdf_bold.raw);
glyph.set_font_size(chr_size);
match &last_cursor {
None => line_object.add_child(glyph),
Some(cursor) => {
Expand Down Expand Up @@ -920,6 +940,24 @@ impl AreaModel {
let end = self.buffer.snap_location(selection.end);
selection.with_start(start).with_end(end)
}

#[cfg(target_arch = "wasm32")]
fn set_font(&self, font_name: &str) {
let app = &self.app;
let scene = &app.display.default_scene;
let fonts = scene.extension::<typeface::font::Registry>();
let font = fonts.load(font_name);
let glyph_system = typeface::glyph::System::new(&scene, font);
self.display_object.add_child(&glyph_system);
let old_glyph_system = self.glyph_system.replace(glyph_system);
self.display_object.remove_child(&old_glyph_system);
// Remove old Glyph structures, as they still refer to the old Glyph System.
self.lines.rc.take();
self.redraw(true);
}

#[cfg(not(target_arch = "wasm32"))]
fn set_font(&self, _font_name: &str) {}
}

impl display::Object for AreaModel {
Expand Down
32 changes: 23 additions & 9 deletions lib/rust/ensogl/component/text/src/typeface/glsl/glyph.glsl
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
// A factor describing much the bold letters will be fattened, expressed as the fraction of font size.
const float BOLD_FATTING = 0.04;

highp float median(highp vec3 v) {
return max(min(v.x, v.y), min(max(v.x, v.y), v.z));
}

highp vec2 get_scaled_uv() {
/// Compute the uv coordinates of the MSDF texture fragment where it should be sampled.
///
/// Essentially, it's an input_uv which is a bit transformed to "cut off" the half of the MSDF cell from each side. This
/// way we have better pixel alignment on low resolutions.
highp vec2 msdf_fragment_uv() {
highp vec2 msdf_cell_size = 1.0/input_msdf_size;
highp vec2 offset = msdf_cell_size/2.0;
highp vec2 scale = 1.0 - msdf_cell_size;
return offset + input_uv * scale;
}

highp vec2 get_texture_coord() {
highp vec2 msdf_fragment_size = input_msdf_size / vec2(textureSize(input_atlas,0));
highp vec2 msdf_fragment_size = input_msdf_size / vec2(textureSize(input_atlas, 0));
highp vec2 offset = vec2(0.0, input_atlas_index) * msdf_fragment_size;
return offset + get_scaled_uv() * msdf_fragment_size;
return offset + msdf_fragment_uv() * msdf_fragment_size;
}

highp float get_fatting() {
bool glyph_is_bold = (input_style & STYLE_BOLD_FLAG) != 0;
highp vec2 local_to_px_ratio = 1.0 / fwidth(input_local.xy);
highp float font_size_px = input_font_size * (local_to_px_ratio.x + local_to_px_ratio.y) / 2.0;
highp float fatting = (glyph_is_bold ? BOLD_FATTING : 0.0) + input_sdf_bold;
return font_size_px * fatting;
}

highp float msdf_alpha() {
Expand All @@ -23,12 +38,11 @@ highp float msdf_alpha() {

// We use this parameter to fatten somewhat font on low resolutions. The thershold and exact
// value of this fattening was picked by trial an error, searching for best rendering effect.
highp float dpi_dilate = avg_msdf_unit_px < input_msdf_range*0.49 ? 1.0 : 0.0;

highp vec3 msdf_sample = texture(input_atlas,tex_coord).rgb;
highp float sig_dist = median(msdf_sample) - 0.5;
highp float sig_dist_px = sig_dist * avg_msdf_unit_px;
highp float opacity = 0.5 + sig_dist_px + dpi_dilate * 0.08;
highp float dpi_dilate = avg_msdf_unit_px < input_msdf_range*0.49 ? 1.0 : 0.0;
highp vec3 msdf_sample = texture(input_atlas,tex_coord).rgb;
highp float sig_dist = median(msdf_sample) - 0.5;
highp float sig_dist_px = sig_dist * avg_msdf_unit_px + get_fatting();
highp float opacity = 0.5 + sig_dist_px + dpi_dilate * 0.08;
opacity = clamp(opacity, 0.0, 1.0);
return opacity;
}
Expand Down
29 changes: 22 additions & 7 deletions lib/rust/ensogl/component/text/src/typeface/glsl/glyph_mac.glsl
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
// A factor describing much the bold letters will be fattened, expressed as the fraction of font size.
const float BOLD_FATTING = 0.04;

Comment on lines +1 to +3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the file for non-mac has more changes, is it intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, not all realignments/refactorings were moved. However, logic changes should be the same.

highp float median(highp vec3 v) {
return max(min(v.x, v.y), min(max(v.x, v.y), v.z));
}

highp vec2 get_scaled_uv() {
/// Compute the uv coordinates of the MSDF texture fragment where it should be sampled.
///
/// Essentially, it's an input_uv which is a bit transformed to "cut off" the half of the MSDF cell from each side. This
/// way we have better pixel alignment on low resolutions.
highp vec2 msdf_fragment_uv() {
highp vec2 msdf_cell_size = 1.0/input_msdf_size;
highp vec2 offset = msdf_cell_size/2.0;
highp vec2 scale = 1.0 - msdf_cell_size;
return offset + input_uv * scale;
}


highp vec2 get_texture_coord() {
highp vec2 msdf_fragment_size = input_msdf_size / vec2(textureSize(input_atlas,0));
highp vec2 offset = vec2(0.0, input_atlas_index) * msdf_fragment_size;
return offset + get_scaled_uv() * msdf_fragment_size;
}

highp float get_fatting() {
bool glyph_is_bold = (input_style & STYLE_BOLD_FLAG) != 0;
highp vec2 local_to_px_ratio = 1.0 / fwidth(input_local.xy);
highp float font_size_px = input_font_size * (local_to_px_ratio.x + local_to_px_ratio.y) / 2.0;
highp float fatting = (glyph_is_bold ? BOLD_FATTING : 0.0) + input_sdf_bold;
return font_size_px * fatting;
}

// FIXME
// The following function uses non-standard font adjustiments (lines marked with FIXME). They make
// the font bolder and more crisp. It was designed to look nice on nodes in the GUI but leaves the
Expand All @@ -28,12 +44,11 @@ highp float msdf_alpha() {

// We use this parameter to fatten somewhat font on low resolutions. The thershold and exact
// value of this fattening was picked by trial an error, searching for best rendering effect.
highp float dpi_dilate = avg_msdf_unit_px < input_msdf_range*0.49 ? 1.0 : 0.0;

highp vec3 msdf_sample = texture(input_atlas,tex_coord).rgb;
highp float sig_dist = median(msdf_sample) - 0.5;
highp float sig_dist_px = sig_dist * avg_msdf_unit_px;
highp float opacity = 0.5 + sig_dist_px + dpi_dilate * 0.08;
highp float dpi_dilate = avg_msdf_unit_px < input_msdf_range*0.49 ? 1.0 : 0.0;
highp vec3 msdf_sample = texture(input_atlas,tex_coord).rgb;
highp float sig_dist = median(msdf_sample) - 0.5;
highp float sig_dist_px = sig_dist * avg_msdf_unit_px + get_fatting();
highp float opacity = 0.5 + sig_dist_px + dpi_dilate * 0.08;
opacity += 0.6; // FIXME: Widen + sharpen
opacity = clamp(opacity, 0.0, 1.0);
opacity = pow(opacity,3.0); // FIXME: sharpen
Expand Down
Loading