Compare commits

...

11 Commits

Author SHA1 Message Date
Matthias Clasen
c6c039e11b tests: Add a difficult node
This node has the Fira e glyph in various sizes,
to demonstrate clipping of text bounds.
2024-05-03 19:05:36 -04:00
Matthias Clasen
20e269655b Hack: Debug for text bounds
Set DEBUG_TEXT_BOUNDS=1 to see the bounds of your text nodes
as beautiful yellow boxes.
2024-05-03 19:04:00 -04:00
Matthias Clasen
0c7d22207b Hack: Download atlases
Make ngl save its current atlas every frame.
2024-05-03 19:03:44 -04:00
Matthias Clasen
49c5692958 Just go back to +1 2024-05-03 19:03:44 -04:00
Matthias Clasen
a0faa99b80 Add a back a helper to get extents
Make a helper function that returns unhinted extents.
2024-05-03 19:03:18 -04:00
Matthias Clasen
ba354b6478 nodeparser: Accept antialias: good 2024-05-03 19:03:18 -04:00
Matthias Clasen
edbd13f36f gsk: Drop an unused private function
gsk_font_get_hint_style is no longer used.
2024-05-03 19:03:18 -04:00
Matthias Clasen
949aacb417 gsk: Use gsk_font_get_rendering
Use gsk_font_get_rendering to find the font options to use.
It is a bit unfortunate that we have to call that function
multiple times. Maybe we should pass the font options down.
2024-05-03 19:03:18 -04:00
Matthias Clasen
791ce9ba24 Add gsk_font_get_rendering
This function takes the font and scale, and determines what font
options to use, based on the passed down font rendering setting,
and the device pixel size of the font.

Currently, we use hinting if the font size is less than 31 pixels,
which corresponds to the size of an 11pt font at a dpi of 200.
2024-05-03 19:03:18 -04:00
Matthias Clasen
4ef73f74e5 widget: Delay deciding font rendering
Don't translate the font rendering setting into font options
ahead of time. Instead just avoid rounding to application pixels
during layout, and pass the font rendering setting to gsk for
a later decision.
2024-05-03 19:03:18 -04:00
Matthias Clasen
4184e09429 gtk-demo: Add smaller font size to waterfall 2024-05-03 19:03:18 -04:00
14 changed files with 300 additions and 80 deletions

View File

@@ -746,7 +746,7 @@ update_display (void)
if (do_waterfall)
{
waterfall = g_string_new ("");
int sizes[] = { 7, 8, 9, 10, 12, 14, 16, 20, 24, 30, 40, 50, 60, 70, 90 };
int sizes[] = { 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 20, 24, 30, 40, 50, 60, 70, 90 };
start = 0;
for (int i = 0; i < G_N_ELEMENTS (sizes); i++)
{

View File

@@ -132,6 +132,47 @@ gsk_gl_device_make_current (GskGpuDevice *device)
gdk_gl_context_make_current (gdk_display_get_gl_context (gsk_gpu_device_get_display (device)));
}
static void
save_texture_to_png (GdkDisplay *display,
int texture_id,
int width,
int height,
const char *filename)
{
GdkGLTextureBuilder *builder;
GdkTexture *texture;
builder = gdk_gl_texture_builder_new ();
gdk_gl_texture_builder_set_context (builder, gdk_display_get_gl_context (display));
gdk_gl_texture_builder_set_id (builder, texture_id);
gdk_gl_texture_builder_set_width (builder, width);
gdk_gl_texture_builder_set_height (builder, height);
texture = gdk_gl_texture_builder_build (builder, NULL, NULL);
gdk_texture_save_to_png (texture, filename);
g_object_unref (texture);
g_object_unref (builder);
}
static void
gsk_gl_device_end_frame (GskGpuDevice *device)
{
GskGpuImage *image;
static int count = 0;
char *filename;
image = gsk_gpu_device_get_atlas_image (device);
filename = g_strdup_printf ("atlas%d.png", count++);
save_texture_to_png (gsk_gpu_device_get_display (device),
gsk_gl_image_peek_texture (GSK_GL_IMAGE (image)),
gsk_gpu_image_get_width (image),
gsk_gpu_image_get_height (image),
filename);
g_free (filename);
}
static void
gsk_gl_device_finalize (GObject *object)
{
@@ -159,6 +200,7 @@ gsk_gl_device_class_init (GskGLDeviceClass *klass)
gpu_device_class->create_upload_image = gsk_gl_device_create_upload_image;
gpu_device_class->create_download_image = gsk_gl_device_create_download_image;
gpu_device_class->make_current = gsk_gl_device_make_current;
gpu_device_class->end_frame = gsk_gl_device_end_frame;
object_class->finalize = gsk_gl_device_finalize;
}
@@ -698,4 +740,3 @@ gsk_gl_device_find_gl_format (GskGLDevice *self,
/* fallbacks will always fallback to a supported format */
g_assert_not_reached ();
}

View File

@@ -316,3 +316,9 @@ gsk_gl_image_steal_texture (GskGLImage *self)
return self->texture_id;
}
GLuint
gsk_gl_image_peek_texture (GskGLImage *self)
{
return self->texture_id;
}

View File

@@ -39,4 +39,7 @@ GLenum gsk_gl_image_get_gl_type (GskGLIm
GLuint gsk_gl_image_steal_texture (GskGLImage *self);
GLuint gsk_gl_image_peek_texture (GskGLImage *self);
G_END_DECLS

View File

@@ -907,7 +907,9 @@ gsk_gpu_device_lookup_glyph_image (GskGpuDevice *self,
gsize atlas_x, atlas_y, padding;
float subpixel_x, subpixel_y;
PangoFont *scaled_font;
cairo_antialias_t antialias;
cairo_hint_metrics_t hint_metrics;
cairo_hint_style_t hint_style;
cache = g_hash_table_lookup (priv->glyph_cache, &lookup);
if (cache)
@@ -919,15 +921,8 @@ gsk_gpu_device_lookup_glyph_image (GskGpuDevice *self,
return cache->image;
}
/* The combination of hint-style != none and hint-metrics == off
* leads to broken rendering with some fonts.
*/
if (gsk_font_get_hint_style (font) != CAIRO_HINT_STYLE_NONE)
hint_metrics = CAIRO_HINT_METRICS_ON;
else
hint_metrics = CAIRO_HINT_METRICS_DEFAULT;
scaled_font = gsk_reload_font (font, scale, hint_metrics, CAIRO_HINT_STYLE_DEFAULT, CAIRO_ANTIALIAS_DEFAULT);
gsk_font_get_rendering (font, scale, &hint_metrics, &hint_style, &antialias);
scaled_font = gsk_reload_font (font, scale, hint_metrics, hint_style, antialias);
subpixel_x = (flags & 3) / 4.f;
subpixel_y = ((flags >> 2) & 3) / 4.f;
@@ -991,5 +986,12 @@ gsk_gpu_device_lookup_glyph_image (GskGpuDevice *self,
return cache->image;
}
void
gsk_gpu_device_end_frame (GskGpuDevice *self)
{
if (GSK_GPU_DEVICE_GET_CLASS (self))
GSK_GPU_DEVICE_GET_CLASS (self)->end_frame (self);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

View File

@@ -42,7 +42,7 @@ struct _GskGpuDeviceClass
gsize width,
gsize height);
void (* make_current) (GskGpuDevice *self);
void (* end_frame) (GskGpuDevice *self);
};
GType gsk_gpu_device_get_type (void) G_GNUC_CONST;
@@ -99,6 +99,8 @@ GskGpuImage * gsk_gpu_device_lookup_glyph_image (GskGpuD
graphene_point_t *out_origin);
void gsk_gpu_device_end_frame (GskGpuDevice *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskGpuDevice, g_object_unref)
G_END_DECLS

View File

@@ -3008,6 +3008,9 @@ gsk_gpu_node_processor_add_glyph_node (GskGpuNodeProcessor *self,
GskGpuImage *last_image;
guint32 descriptor;
const float inv_pango_scale = 1.f / PANGO_SCALE;
cairo_hint_metrics_t hint_metrics;
cairo_hint_style_t hint_style;
cairo_antialias_t antialias;
if (self->opacity < 1.0 &&
gsk_text_node_has_color_glyphs (node))
@@ -3016,6 +3019,17 @@ gsk_gpu_node_processor_add_glyph_node (GskGpuNodeProcessor *self,
return;
}
if (g_getenv ("DEBUG_TEXT_BOUNDS"))
{
GdkRGBA color;
GskRenderNode *c;
gdk_rgba_parse (&color, "yellow");
c = gsk_color_node_new (&color, &node->bounds);
gsk_gpu_node_processor_add_color_node (self, c);
gsk_render_node_unref (c);
}
device = gsk_gpu_frame_get_device (self->frame);
color = *gsk_text_node_get_color (node);
@@ -3030,7 +3044,8 @@ gsk_gpu_node_processor_add_glyph_node (GskGpuNodeProcessor *self,
scale = MAX (graphene_vec2_get_x (&self->scale), graphene_vec2_get_y (&self->scale));
inv_scale = 1.f / scale;
if (gsk_font_get_hint_style (font) != CAIRO_HINT_STYLE_NONE)
gsk_font_get_rendering (font, scale, &hint_metrics, &hint_style, &antialias);
if (hint_style != CAIRO_HINT_STYLE_NONE)
{
align_scale_x = scale * 4;
align_scale_y = scale;
@@ -3128,6 +3143,9 @@ gsk_gpu_node_processor_create_glyph_pattern (GskGpuPatternWriter *self,
float inv_align_scale_x, inv_align_scale_y;
unsigned int flags_mask;
const float inv_pango_scale = 1.f / PANGO_SCALE;
cairo_hint_metrics_t hint_metrics;
cairo_hint_style_t hint_style;
cairo_antialias_t antialias;
if (gsk_text_node_has_color_glyphs (node))
return FALSE;
@@ -3147,7 +3165,8 @@ gsk_gpu_node_processor_create_glyph_pattern (GskGpuPatternWriter *self,
gsk_gpu_pattern_writer_append_rgba (self, gsk_text_node_get_color (node));
gsk_gpu_pattern_writer_append_uint (self, num_glyphs);
if (gsk_font_get_hint_style (font) != CAIRO_HINT_STYLE_NONE)
gsk_font_get_rendering (font, scale, &hint_metrics, &hint_style, &antialias);
if (hint_style != CAIRO_HINT_STYLE_NONE)
{
align_scale_x = scale * 4;
align_scale_y = scale;

View File

@@ -423,6 +423,7 @@ gsk_gpu_renderer_render (GskRenderer *renderer,
gsk_gpu_device_queue_gc (priv->device);
gdk_draw_context_end_frame (priv->context);
gsk_gpu_device_end_frame (priv->device);
g_clear_pointer (&render_region, cairo_region_destroy);
}

View File

@@ -25,11 +25,27 @@ gsk_ensure_resources (void)
g_once (&register_resources_once, register_resources, NULL);
}
static cairo_font_options_t *font_options = NULL;
static inline cairo_font_options_t *
pango_font_get_cairo_font_options (PangoFont *font)
{
cairo_scaled_font_t *sf;
if (G_UNLIKELY (font_options == NULL))
font_options = cairo_font_options_create ();
sf = pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (font));
cairo_scaled_font_get_font_options (sf, font_options);
return font_options;
}
/*< private >
* gsk_reload_font:
* @font: a `PangoFont`
* @scale: the scale to apply
* @hint_metris: hint metrics to use or `CAIRO_HINT_METRICS_DEFAILT` to keep the
* @hint_metris: hint metrics to use or `CAIRO_HINT_METRICS_DEFAULT` to keep the
* hint metrics of the font unchanged
* @hint_style: hint style to use or `CAIRO_HINT_STYLE_DEFAULT` to keep the
* hint style of @font unchanged
@@ -48,9 +64,8 @@ gsk_reload_font (PangoFont *font,
cairo_hint_style_t hint_style,
cairo_antialias_t antialias)
{
static cairo_font_options_t *options = NULL;
static PangoContext *context = NULL;
cairo_scaled_font_t *sf;
cairo_font_options_t *options;
/* These requests often come in sequentially so keep the result
* around and re-use it if everything matches.
@@ -78,11 +93,7 @@ gsk_reload_font (PangoFont *font,
g_set_object (&last_font, font);
g_clear_object (&last_result);
if (G_UNLIKELY (options == NULL))
options = cairo_font_options_create ();
sf = pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (font));
cairo_scaled_font_get_font_options (sf, options);
options = pango_font_get_cairo_font_options (font);
if (hint_metrics == CAIRO_HINT_METRICS_DEFAULT)
hint_metrics = cairo_font_options_get_hint_metrics (options);
@@ -118,27 +129,103 @@ gsk_reload_font (PangoFont *font,
return g_object_ref (last_result);
}
/*< private >
* gsk_font_get_hint_style:
* @font: a `PangoFont`
*
* Get the hint style from the cairo font options.
*
* Returns: the hint style
*/
cairo_hint_style_t
gsk_font_get_hint_style (PangoFont *font)
static inline double
gsk_font_get_size (PangoFont *font)
{
static cairo_font_options_t *options = NULL;
cairo_scaled_font_t *sf;
cairo_hint_style_t style;
PangoFontDescription *desc;
double size;
if (G_UNLIKELY (options == NULL))
options = cairo_font_options_create ();
desc = pango_font_describe_with_absolute_size (font);
sf = pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (font));
cairo_scaled_font_get_font_options (sf, options);
style = cairo_font_options_get_hint_style (options);
size = pango_font_description_get_size (desc);
return style;
pango_font_description_free (desc);
return size / PANGO_SCALE;
}
/*< private >
* gsk_font_get_rendering:
* @font: a `PangoFont`
* @scale: the scale to use
* @hint_metrics: (out): return location for metrics hinting
* @hint_style: (out): return location for hint style
* @antialias: (out): return location for antialiasing
*
* Determines the font options to use for rendering with
* the font at the given scale.
*/
void
gsk_font_get_rendering (PangoFont *font,
float scale,
cairo_hint_metrics_t *hint_metrics,
cairo_hint_style_t *hint_style,
cairo_antialias_t *antialias)
{
const cairo_font_options_t *options;
options = pango_font_get_cairo_font_options (font);
/* Keep this in sync with gtkwidget.c:update_pango_context */
if (cairo_font_options_get_antialias (options) == CAIRO_ANTIALIAS_GOOD)
{
double font_size;
font_size = gsk_font_get_size (font) * scale;
*antialias = CAIRO_ANTIALIAS_GRAY;
*hint_metrics = CAIRO_HINT_METRICS_OFF;
/* 31 pixels is equivalent to an 11 pt font at 200 dpi */
if (font_size > 31)
*hint_style = CAIRO_HINT_STYLE_NONE;
else
*hint_style = CAIRO_HINT_STYLE_SLIGHT;
}
else
{
*antialias = cairo_font_options_get_antialias (options);
*hint_metrics = cairo_font_options_get_hint_metrics (options);
*hint_style = cairo_font_options_get_hint_style (options);
}
/* The combination of hint-style != none and hint-metrics == off
* leads to broken rendering with some fonts.
*/
if (*hint_style != CAIRO_HINT_STYLE_NONE)
*hint_metrics = CAIRO_HINT_METRICS_ON;
}
/*< private >
* gsk_font_get_extents:
* @font: a `PangoFont`
* @glyphs: a `PangoGlyphString`
* @ink_rect: (out): rectangle used to store the extents of the glyph
* string as drawn
*
* Compute the ink extents of a glyph string.
*
* This is like [method@Pango.GlyphString.extents], but it provides
* unhinted extents.
*/
void
gsk_font_get_extents (PangoFont *font,
PangoGlyphString *glyphs,
PangoRectangle *ink_rect)
{
PangoFont *unhinted;
unhinted = gsk_reload_font (font, 1.0, CAIRO_HINT_METRICS_OFF,
CAIRO_HINT_STYLE_NONE,
CAIRO_ANTIALIAS_GRAY);
pango_glyph_string_extents (glyphs, unhinted, ink_rect, NULL);
/* Hack: Without this, cff fonts like Fira get clipped */
ink_rect->x -= 1024;
ink_rect->y -= 1024;
ink_rect->width += 2048;
ink_rect->height += 2048;
g_object_unref (unhinted);
}

View File

@@ -6,15 +6,22 @@
G_BEGIN_DECLS
void gsk_ensure_resources (void);
void gsk_ensure_resources (void);
PangoFont *gsk_reload_font (PangoFont *font,
float scale,
cairo_hint_metrics_t hint_metrics,
cairo_hint_style_t hint_style,
cairo_antialias_t antialias);
PangoFont *gsk_reload_font (PangoFont *font,
float scale,
cairo_hint_metrics_t hint_metrics,
cairo_hint_style_t hint_style,
cairo_antialias_t antialias);
cairo_hint_style_t gsk_font_get_hint_style (PangoFont *font);
void gsk_font_get_rendering (PangoFont *font,
float scale,
cairo_hint_metrics_t *hint_metrics,
cairo_hint_style_t *hint_style,
cairo_antialias_t *antialias);
void gsk_font_get_extents (PangoFont *font,
PangoGlyphString *glyphs,
PangoRectangle *ink_rect);
G_END_DECLS

View File

@@ -5825,7 +5825,7 @@ gsk_text_node_new (PangoFont *font,
PangoGlyphInfo *glyph_infos;
int n;
pango_glyph_string_extents (glyphs, font, &ink_rect, NULL);
gsk_font_get_extents (font, glyphs, &ink_rect);
/* Don't create nodes with empty bounds */
if (ink_rect.width == 0 || ink_rect.height == 0)

View File

@@ -2302,7 +2302,8 @@ parse_antialias (GtkCssParser *parser,
return FALSE;
if (*(cairo_antialias_t *) out != CAIRO_ANTIALIAS_NONE &&
*(cairo_antialias_t *) out != CAIRO_ANTIALIAS_GRAY)
*(cairo_antialias_t *) out != CAIRO_ANTIALIAS_GRAY &&
*(cairo_antialias_t *) out != CAIRO_ANTIALIAS_GOOD)
{
gtk_css_parser_error_value (parser, "Unsupported value for enum \"%s\"",
g_type_name (CAIRO_GOBJECT_TYPE_ANTIALIAS));
@@ -3662,9 +3663,11 @@ gsk_text_node_serialize_font_options (GskRenderNode *node,
append_enum_param (p, "hint-style", CAIRO_GOBJECT_TYPE_HINT_STYLE, hint_style);
/* CAIRO_ANTIALIAS_NONE is the only value we ever emit here, since gray is the default,
* and we don't accept any other values.
* and we don't accept any other values. We do accept GOOD as GTK uses that to pass
* the gtk-font-rendering value down.
*/
if (antialias == CAIRO_ANTIALIAS_NONE)
if (antialias == CAIRO_ANTIALIAS_NONE ||
antialias == CAIRO_ANTIALIAS_GOOD)
append_enum_param (p, "antialias", CAIRO_GOBJECT_TYPE_ANTIALIAS, antialias);
/* CAIRO_HINT_METRICS_ON is the only value we ever emit here, since off is the default,

View File

@@ -6513,39 +6513,22 @@ gtk_widget_update_pango_context (GtkWidget *widget,
else
{
cairo_font_options_t *options;
double dpi = 96.;
GdkSurface *surface;
surface = gtk_widget_get_surface (widget);
if (surface)
{
GdkDisplay *display;
GdkMonitor *monitor;
display = gdk_surface_get_display (surface);
monitor = gdk_display_get_monitor_at_surface (display, surface);
if (monitor)
dpi = gdk_monitor_get_dpi (monitor);
}
options = cairo_font_options_create ();
cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
/* HACK: Use CAIRO_ANTIALIAS_GOOD to communicate 'automatic' to gsk */
cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GOOD);
if (dpi < 200.)
{
/* Not high-dpi. Prefer sharpness by enabling hinting */
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_SLIGHT);
}
else
{
/* High-dpi. Prefer precise positioning */
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
}
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
/* Don't do any rounding during layout. In particular with fractional
* scaling, we want to avoid rounding to application pixels, since the
* may not align with the device pixel grid.
*/
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
pango_context_set_round_glyph_positions (context, FALSE);
pango_cairo_context_set_font_options (context, options);
cairo_font_options_destroy (options);

66
tests/demo.node Normal file
View File

@@ -0,0 +1,66 @@
transform {
transform: scale(1);
child: container {
color {
color: white;
bounds: -5 -20 140 30;
}
text {
font: "Fira Sans Medium 21px";
glyphs: "e";
offset: -2 0;
}
text {
font: "Fira Sans Medium 20.5px";
glyphs: "e";
offset: 11 0;
}
text {
font: "Fira Sans Medium 20px";
glyphs: "e";
offset: 24 0;
}
text {
font: "Fira Sans Medium 19.5px";
glyphs: "e";
offset: 36 0;
}
text {
font: "Fira Sans Medium 19px";
glyphs: "e";
offset: 48 0;
}
text {
font: "Fira Sans Medium 18.5px";
glyphs: "e";
offset: 60 0;
}
text {
font: "Fira Sans Medium 18px";
glyphs: "e";
offset: 72 0;
}
text {
font: "Fira Sans Medium 17.5px";
glyphs: "e";
offset: 84 0;
}
text {
font: "Fira Sans Medium 17px";
glyphs: "e";
offset: 96 0;
}
text {
font: "Fira Sans Medium 16.5px";
glyphs: "e";
offset: 108 0;
}
text {
font: "Fira Sans Medium 16px";
glyphs: "e";
offset: 120 0;
}
}
}