Use correct and consistent pre-constraint glyph rect
In Freetype, measure rect after emboldening, so constraints apply to the true glyph size like in CoreText. In CoreText, don't let font smoothing affect the rect (only the canvas).pull/8580/head
parent
96fbff681b
commit
5c129205a5
|
|
@ -319,17 +319,6 @@ pub const Face = struct {
|
||||||
rect.origin.y -= line_width / 2;
|
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
|
// If our rect is smaller than a quarter pixel in either axis
|
||||||
// then it has no outlines or they're too small to render.
|
// then it has no outlines or they're too small to render.
|
||||||
//
|
//
|
||||||
|
|
@ -400,10 +389,16 @@ pub const Face = struct {
|
||||||
y = @round(y);
|
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.
|
// Our whole-pixel bearings for the final glyph.
|
||||||
// The fractional portion will be included in the rasterized position.
|
// The fractional portion will be included in the rasterized position.
|
||||||
const px_x: i32 = @intFromFloat(@floor(x));
|
const px_x = @as(i32, @intFromFloat(@floor(x))) - @as(i32, @intCast(canvas_padding));
|
||||||
const px_y: i32 = @intFromFloat(@floor(y));
|
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 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
|
// we will add as an offset when rasterizing to make sure we get the
|
||||||
|
|
@ -413,9 +408,9 @@ pub const Face = struct {
|
||||||
|
|
||||||
// Add the fractional pixel to the width and height and take
|
// Add the fractional pixel to the width and height and take
|
||||||
// the ceiling to get a canvas size that will definitely fit
|
// the ceiling to get a canvas size that will definitely fit
|
||||||
// our drawn glyph, including the fractional offset.
|
// our drawn glyph, including the fractional offset and font smoothing.
|
||||||
const px_width: u32 = @intFromFloat(@ceil(width + frac_x));
|
const px_width = @as(u32, @intFromFloat(@ceil(width + frac_x))) + (2 * canvas_padding);
|
||||||
const px_height: u32 = @intFromFloat(@ceil(height + frac_y));
|
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.
|
// Settings that are specific to if we are rendering text or emoji.
|
||||||
const color: struct {
|
const color: struct {
|
||||||
|
|
@ -526,8 +521,8 @@ pub const Face = struct {
|
||||||
// `drawGlyphs`, we pass the negated bearings.
|
// `drawGlyphs`, we pass the negated bearings.
|
||||||
context.translateCTM(
|
context.translateCTM(
|
||||||
ctx,
|
ctx,
|
||||||
frac_x,
|
frac_x + @as(f64, @floatFromInt(canvas_padding)),
|
||||||
frac_y,
|
frac_y + @as(f64, @floatFromInt(canvas_padding)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Scale the drawing context so that when we draw
|
// Scale the drawing context so that when we draw
|
||||||
|
|
|
||||||
|
|
@ -429,6 +429,17 @@ pub const Face = struct {
|
||||||
try self.face.loadGlyph(glyph_index, self.glyphLoadFlags(opts.constraint.doesAnything()));
|
try self.face.loadGlyph(glyph_index, self.glyphLoadFlags(opts.constraint.doesAnything()));
|
||||||
const glyph = self.face.handle.*.glyph;
|
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
|
// We get a rect that represents the position
|
||||||
// and size of the glyph before any changes.
|
// and size of the glyph before any changes.
|
||||||
const rect = getGlyphSize(glyph);
|
const rect = getGlyphSize(glyph);
|
||||||
|
|
@ -447,17 +458,6 @@ pub const Face = struct {
|
||||||
.atlas_y = 0,
|
.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 metrics = opts.grid_metrics;
|
||||||
const cell_width: f64 = @floatFromInt(metrics.cell_width);
|
const cell_width: f64 = @floatFromInt(metrics.cell_width);
|
||||||
const cell_height: f64 = @floatFromInt(metrics.cell_height);
|
const cell_height: f64 = @floatFromInt(metrics.cell_height);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue