#distance-field #signed #multi-channel #graphics #shape #msdf #generation

fdsm

多通道有符号距离场生成的纯Rust实现

7个版本 (4个破坏性更新)

0.5.0 2024年6月13日
0.4.0 2024年5月6日
0.3.0 2023年10月18日
0.2.0 2023年10月15日
0.1.1 2023年8月26日

#110图形API

Download history 2/week @ 2024-04-15 17/week @ 2024-04-22 131/week @ 2024-05-06 9/week @ 2024-05-20 8/week @ 2024-05-27 185/week @ 2024-06-10 5/week @ 2024-06-17 7/week @ 2024-07-15 47/week @ 2024-07-22

每月下载 54

MIT 和可能 CC-PDDC

660KB
3.5K SLoC

fdsm

多通道有符号距离场生成 的纯Rust重实现

此实现主要遵循 Victor Chlumský的硕士论文。尽管它不是C++ msdfgen 库的精确翻译,但它确实遵循了其中的一些代码部分。

fdsm 还使用了从 msdfgen-rs 中改编的代码进行测试。

包功能

  • ttf-parser: 使用 ttf-parser 导入字形
  • visualize: 可视化辅助工具

使用方法

use fdsm::bezier::scanline::FillRule;
use fdsm::generate::generate_msdf;
use fdsm::render::{correct_sign_msdf, render_msdf};
use fdsm::shape::Shape;
use fdsm::transform::Transform;
use image::{GrayImage, RgbImage};
use nalgebra::{Affine2, Similarity2, Vector2};
use ttf_parser::Face;

// First, acquire a [`Shape`]. This can be done by procedurally
// generating one or by loading one from a font:

let face = Face::parse(notosans::REGULAR_TTF, 0).unwrap();
let glyph_id = face.glyph_index('A').unwrap();
let mut shape = Shape::load_from_face(&face, glyph_id);

// Prepare your transformation matrix and calculate the dimensions of
// the resulting signed distance field. As an example, we set this up
// using ‘shrinkage’ (font units per texel) and ‘range’ (number of
// texels for the margin) values.
// Note that since font files interpret a positive y-offset as
// pointing up, the resulting distance field will be upside-down.
// This can be corrected either by flipping the resulting image
// vertically or by modifying the transformation matrix. We omit
// this fix for simplicity.

let bbox = face.glyph_bounding_box(glyph_id).unwrap();

const RANGE: f64 = 4.0;
const SHRINKAGE: f64 = 16.0;
let transformation = nalgebra::convert::<_, Affine2<f64>>(Similarity2::new(
    Vector2::new(
        RANGE - bbox.x_min as f64 / SHRINKAGE,
        RANGE - bbox.y_min as f64 / SHRINKAGE,
    ),
    0.0,
    1.0 / SHRINKAGE,
));
let width =
    ((bbox.x_max as f64 - bbox.x_min as f64) / SHRINKAGE + 2.0 * RANGE).ceil() as u32;
let height =
    ((bbox.y_max as f64 - bbox.y_min as f64) / SHRINKAGE + 2.0 * RANGE).ceil() as u32;

// Unlike msdfgen, the transformation is not passed into the
// `generate_msdf` function – the coordinates of the control points
// must be expressed in terms of pixels on the distance field. To get
// the correct units, we pre-transform the shape:

shape.transform(&transformation);

// We now color the edges of the shape. We also have to prepare
// it for calculations:

let colored_shape = Shape::edge_coloring_simple(shape, 0.03, 69441337420);
let prepared_colored_shape = colored_shape.prepare();

// Set up the resulting image and generate the distance field:

let mut msdf = RgbImage::new(width, height);
generate_msdf(&prepared_colored_shape, RANGE, &mut msdf);
correct_sign_msdf(&mut msdf, &prepared_colored_shape, FillRule::Nonzero);

// As a test, try previewing the distance field:

let mut preview = GrayImage::new(msdf.width() * 10, msdf.height() * 10);
render_msdf(&msdf, &mut preview, RANGE);

路线图

目前,fdsm 具有生成MSDFs的基本功能,并为Noto Sans中的从 AZ 的字形生成正确的距离场。然而,它不具有 msdfgen 中的所有功能。

  • 错误纠正
  • 错误估计
  • 符号校正
  • 形状简化(参考Chlumský,2015年,第3.1节)
  • 替代边缘着色算法
  • msdfgen 的基准测试

依赖项

~4MB
~85K SLoC