Skip to content

Commit

Permalink
More refactorings
Browse files Browse the repository at this point in the history
* Remove `freetype-rs` dependency - use (unpublished) one from sdf_glyph_renderer
* get rid of all panics (unwraps)
* add proper thiserror-based errors
* cleanup exports to be on the same top level
  • Loading branch information
nyurik committed Jul 3, 2023
1 parent 675fc36 commit 03a8858
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 239 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ edition = "2021"
exclude = [".github/"]

[features]
freetype = ["freetype-rs", "sdf_glyph_renderer/freetype"]
freetype = ["dep:sdf_glyph_renderer"]

[dependencies]
freetype-rs = { version = "0.32.0", optional = true }
futures = "0.3.28"
protobuf = "3.2.0"
sdf_glyph_renderer = { version = "0.5.1", optional = true }
sdf_glyph_renderer = { version = "0.6.0", features = ["freetype"], optional = true, path = "../sdf_glyph_renderer" }
thiserror = "1"
tokio = { version = "1.28.2", features = ["rt"] }

[dev-dependencies]
Expand Down
18 changes: 18 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::freetype;

#[derive(thiserror::Error, Debug)]
pub enum PbfFontError {
#[error("Sub-process error: {0}")]
JoinError(#[from] tokio::task::JoinError),
#[error("Protobuf decoding error: {0}")]
ProtobufError(#[from] protobuf::Error),
#[cfg(feature = "freetype")]
#[error("SDF glyph error: {0}")]
SdfGlyphError(#[from] sdf_glyph_renderer::SdfGlyphError),
#[cfg(feature = "freetype")]
#[error("Font family name is not set")]
MissingFontFamilyName,
#[cfg(feature = "freetype")]
#[error("Freetype error: {0}")]
FreetypeError(#[from] freetype::Error),
}
113 changes: 113 additions & 0 deletions src/ft_generate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use crate::error::PbfFontError;
use crate::freetype;
use crate::{Fontstack, Glyph, Glyphs};
use sdf_glyph_renderer::{clamp_to_u8, render_sdf_from_face};

fn render_sdf_glyph(
face: &freetype::Face,
char_code: u32,
buffer: usize,
radius: usize,
cutoff: f64,
) -> Result<Glyph, PbfFontError> {
let glyph = render_sdf_from_face(face, char_code, buffer, radius)?;

let mut result = Glyph::new();
result.set_id(char_code);
result.set_bitmap(clamp_to_u8(&glyph.sdf, cutoff)?);
result.set_width(glyph.metrics.width as u32);
result.set_height(glyph.metrics.height as u32);
result.set_left(glyph.metrics.left_bearing);
result.set_top(glyph.metrics.top_bearing - glyph.metrics.ascender);
result.set_advance(glyph.metrics.h_advance);

Ok(result)
}

/// Renders a glyph range for the given font face into a Mapbox-compatible fontstack.
///
/// The `radius` and `cutoff` parameters are exposed in case you are working with an
/// alternate renderer with tunable options, but you are probably best off sticking
/// with 8 and 0.25 respectively.
///
/// The `radius` controls how many pixels out from the font outline to record distances
/// from the font outline (the rest will be clamped to zero). `cutoff` controls what
/// percentage of values will be used to record the negative values (since the SDF is
/// encoded as a vector of bytes, which have no sign). The value selected must be
/// between 0 and 1.
pub fn glyph_range_for_face(
face: &freetype::Face,
start: u32,
end: u32,
size: usize,
radius: usize,
cutoff: f64,
) -> Result<Fontstack, PbfFontError> {
let Some(family_name) = face.family_name() else {
return Err(PbfFontError::MissingFontFamilyName)?;
};

let mut stack = Fontstack::new();

let stack_name = if let Some(style_name) = face.style_name() {
format!("{family_name} {style_name}")
} else {
family_name
};

stack.set_name(stack_name);
stack.set_range(format!("{start}-{end}"));

// FreeType conventions: char width or height of zero means "use the same value"
// and setting both resolution values to zero results in the default value
// of 72 dpi.
//
// See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_set_char_size
// and https://www.freetype.org/freetype2/docs/tutorial/step1.html for details.
face.set_char_size(0, (size << 6) as isize, 0, 0)?;

for char_code in start..=end {
let result = render_sdf_glyph(face, char_code, 3, radius, cutoff);
match result {
Ok(glyph) => {
stack.glyphs.push(glyph);
}
Err(PbfFontError::SdfGlyphError(sdf_glyph_renderer::SdfGlyphError::FreeTypeError(
freetype::Error::InvalidGlyphIndex,
))) => {
// Do nothing; not all glyphs will be present in a font.
}
Err(e) => {
return Err(e);
}
}
}

Ok(stack)
}

pub fn glyph_range_for_font(
font_path: &std::path::Path,
start: u32,
end: u32,
size: usize,
radius: usize,
cutoff: f64,
) -> Result<Glyphs, PbfFontError> {
let lib = freetype::Library::init()?;
let mut face = lib.new_face(font_path, 0)?;
let num_faces = face.num_faces();

let mut result = Glyphs::new();

for face_index in 0..num_faces {
if face_index > 0 {
face = lib.new_face(font_path, face_index as isize)?;
}

let stack = glyph_range_for_face(&face, start, end, size, radius, cutoff)?;
result.stacks.push(stack);
}

Ok(result)
}
115 changes: 0 additions & 115 deletions src/generate.rs

This file was deleted.

Loading

0 comments on commit 03a8858

Please sign in to comment.