Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
51 changes: 23 additions & 28 deletions src/font/face/coretext.zig
Original file line number Diff line number Diff line change
Expand Up @@ -319,17 +319,6 @@ pub const Face = struct {
rect.origin.y -= line_width / 2;
};

// We make an assumption that font smoothing ("thicken")
// adds no more than 1 extra pixel to any edge. We don't
// add extra size if it's a sbix color font though, since
// bitmaps aren't affected by smoothing.
if (opts.thicken and !sbix) {
rect.size.width += 2.0;
rect.size.height += 2.0;
rect.origin.x -= 1.0;
rect.origin.y -= 1.0;
}

// If our rect is smaller than a quarter pixel in either axis
// then it has no outlines or they're too small to render.
//
Expand Down Expand Up @@ -378,16 +367,6 @@ pub const Face = struct {
var width = glyph_size.width;
var height = glyph_size.height;

// If this is a bitmap glyph, it will always render as full pixels,
// not fractional pixels, so we need to quantize its position and
// size accordingly to align to full pixels so we get good results.
if (sbix) {
width = cell_width - @round(cell_width - width - x) - @round(x);
height = cell_height - @round(cell_height - height - y) - @round(y);
x = @round(x);
y = @round(y);
}

// We center all glyphs within the pixel-rounded and adjusted
// cell width if it's larger than the face width, so that they
// aren't weirdly off to the left.
Expand All @@ -400,10 +379,26 @@ pub const Face = struct {
x += (cell_width - metrics.face_width) / 2;
}

// If this is a bitmap glyph, it will always render as full pixels,
// not fractional pixels, so we need to quantize its position and
// size accordingly to align to full pixels so we get good results.
if (sbix) {
width = cell_width - @round(cell_width - width - x) - @round(x);
height = cell_height - @round(cell_height - height - y) - @round(y);
x = @round(x);
y = @round(y);
}

// We make an assumption that font smoothing ("thicken")
// adds no more than 1 extra pixel to any edge. We don't
// add extra size if it's a sbix color font though, since
// bitmaps aren't affected by smoothing.
const canvas_padding: u32 = if (opts.thicken and !sbix) 1 else 0;

// Our whole-pixel bearings for the final glyph.
// The fractional portion will be included in the rasterized position.
const px_x: i32 = @intFromFloat(@floor(x));
const px_y: i32 = @intFromFloat(@floor(y));
const px_x = @as(i32, @intFromFloat(@floor(x))) - @as(i32, @intCast(canvas_padding));
const px_y = @as(i32, @intFromFloat(@floor(y))) - @as(i32, @intCast(canvas_padding));

// We keep track of the fractional part of the pixel bearings, which
// we will add as an offset when rasterizing to make sure we get the
Expand All @@ -413,9 +408,9 @@ pub const Face = struct {

// Add the fractional pixel to the width and height and take
// the ceiling to get a canvas size that will definitely fit
// our drawn glyph, including the fractional offset.
const px_width: u32 = @intFromFloat(@ceil(width + frac_x));
const px_height: u32 = @intFromFloat(@ceil(height + frac_y));
// our drawn glyph, including the fractional offset and font smoothing.
const px_width = @as(u32, @intFromFloat(@ceil(width + frac_x))) + (2 * canvas_padding);
const px_height = @as(u32, @intFromFloat(@ceil(height + frac_y))) + (2 * canvas_padding);

// Settings that are specific to if we are rendering text or emoji.
const color: struct {
Expand Down Expand Up @@ -526,8 +521,8 @@ pub const Face = struct {
// `drawGlyphs`, we pass the negated bearings.
context.translateCTM(
ctx,
frac_x,
frac_y,
frac_x + @as(f64, @floatFromInt(canvas_padding)),
frac_y + @as(f64, @floatFromInt(canvas_padding)),
);

// Scale the drawing context so that when we draw
Expand Down
44 changes: 22 additions & 22 deletions src/font/face/freetype.zig
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,19 @@ pub const Face = struct {
try self.face.loadGlyph(glyph_index, self.glyphLoadFlags(opts.constraint.doesAnything()));
const glyph = self.face.handle.*.glyph;

// For synthetic bold, we embolden the glyph.
if (self.synthetic.bold) {
// We need to scale the embolden amount based on the font size.
// This is a heuristic I found worked well across a variety of
// founts: 1 pixel per 64 units of height.
const font_height: f64 = @floatFromInt(self.face.handle.*.size.*.metrics.height);
const ratio: f64 = 64.0 / 2048.0;
const amount = @ceil(font_height * ratio);
_ = freetype.c.FT_Outline_Embolden(&glyph.*.outline, @intFromFloat(amount));
}

// We get a rect that represents the position
// and size of the glyph before any changes.
// and size of the glyph before constraints.
const rect = getGlyphSize(glyph);

// If our glyph is smaller than a quarter pixel in either axis
Expand All @@ -447,17 +458,6 @@ pub const Face = struct {
.atlas_y = 0,
};

// For synthetic bold, we embolden the glyph.
if (self.synthetic.bold) {
// We need to scale the embolden amount based on the font size.
// This is a heuristic I found worked well across a variety of
// founts: 1 pixel per 64 units of height.
const font_height: f64 = @floatFromInt(self.face.handle.*.size.*.metrics.height);
const ratio: f64 = 64.0 / 2048.0;
const amount = @ceil(font_height * ratio);
_ = freetype.c.FT_Outline_Embolden(&glyph.*.outline, @intFromFloat(amount));
}

const metrics = opts.grid_metrics;
const cell_width: f64 = @floatFromInt(metrics.cell_width);
const cell_height: f64 = @floatFromInt(metrics.cell_height);
Expand Down Expand Up @@ -492,16 +492,6 @@ pub const Face = struct {
var x = glyph_size.x;
var y = glyph_size.y;

// If this is a bitmap glyph, it will always render as full pixels,
// not fractional pixels, so we need to quantize its position and
// size accordingly to align to full pixels so we get good results.
if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_BITMAP) {
width = cell_width - @round(cell_width - width - x) - @round(x);
height = cell_height - @round(cell_height - height - y) - @round(y);
x = @round(x);
y = @round(y);
}

// We center all glyphs within the pixel-rounded and adjusted
// cell width if it's larger than the face width, so that they
// aren't weirdly off to the left.
Expand All @@ -520,6 +510,16 @@ pub const Face = struct {
x += @round((cell_width - metrics.face_width) / 2);
}

// If this is a bitmap glyph, it will always render as full pixels,
// not fractional pixels, so we need to quantize its position and
// size accordingly to align to full pixels so we get good results.
if (glyph.*.format == freetype.c.FT_GLYPH_FORMAT_BITMAP) {
width = cell_width - @round(cell_width - width - x) - @round(x);
height = cell_height - @round(cell_height - height - y) - @round(y);
x = @round(x);
y = @round(y);
}

// Now we can render the glyph.
var bitmap: freetype.c.FT_Bitmap = undefined;
_ = freetype.c.FT_Bitmap_Init(&bitmap);
Expand Down