[cairo] Font analysis

Owen Taylor otaylor at redhat.com
Fri Mar 18 11:48:36 PST 2005


Carl Worth wrote:

>>1. Don't expose a ScaledFont object, but use a FontName + scale
>>    everywhere in the public API. This is more or less how the
>>    older cairo API was set up, though the right set of FontNames
>>    wasn't available. With fixed FontNames it works better, but
>>    falls over on two counts:
> 
> 
> I like this proposal the best I think.
> 
> 
>>     - Performance. For every glyph we want to measure, we have
>>       to do a full lookup. Even with the right hash tables, this
>>       is prohibitive.
> 
> I don't understand this point. Given a FontName+scale, cairo can
> lookup and cache the ScaledFont in cairo_t. So, the lookup overhead is
> "for every time the FontName or scale changes", not "for every
> glyph". In your "million glyphs per second" requirement, how many
> changes to the ScaledFont should we be able to handle?

Somewhere between 20,000 (50 character lines of text) and 100,000
(10 character cells in a list view or spreadsheet)

Two problems (discussed more below):

  - I don't want to require a cairo_t for measuring
  - There's significant work to change the FontName in a cairo_t

> I know you said something once about definitely not wanting to go
> through cairo_t to get metrics, but I don't recall what the specific
> concern was there.

The specific concern is that a cairo_t exists only for the duration
of a single drawing operation. However, I cannot scope PangoLayout
the same way ... GtkLabel, for example, keeps a PangoLayout around
all the time.

This means that PangoLayout cannot keep a reference to a cairo_t.
And their are public APIs in Pango like:

  void pango_font_get_glyph_extents (PangoFont        *font,
                                     PangoGlyph        glyph,
                                     PangoRectangle   *ink_rect,
                                     PangoRectangle   *logical_rect);

This function would have to *create* a cairo_t for the individual
glyph. (Or more likely, use a per-thread singleton.)

To avoid you claiming that I'm forcing constraints on Cairo for the
convienience of supporting "bad API choices" in Pango, let me argue
that text measurement is generally something that you need to do
outside the context of drawing.

Say, I want to write a PNG file appropriately sized to contain a
single string of text. Once we make cairo_create() take a
cairo_surface_t, that will force someone to create a 1x1 image
surface to do the measurement when using the toy API.

> It should be a fairly simple to document which cairo function calls
> change the un-exposed ScaledFont that cairo is using, (anything that
> changes the FontName, the FontScale, or the CTM). So, an application
> should be able to know when it can rely on the underlying platform
> font object it got from cairo, and when it needs to get a new one,
> right?

In the framework where we make cairo_t the public handle to a
ScaledFont, yes, you could write docs that told people what was
safe and what was not.

>>     - Inability to reference the underlying platform font objects.
>>
>>       Currently we have cairo_win32_font_select_font()/done_font()
>>       for Win32 and cairo_ft_font_lock_face()/unlock_face()
>>       for FreeType. These return scaled objects for the
>>       underlying backend, and so need scaled objects in the
>>       FreeType API.
> 
> 
> Couldn't we just add similar font-backend-specific functions to the
> cairo_t? I don't see why it would be unable to return the platform
> font object it's using. (We would have a new naming problem, as
> currently all font-backend-specific functions are on cairo_font_t, not
> cairo_t, but that doesn't seem like a showstopper).

Avoiding exposing a ScaledFont object by using a cairo_t as a wrapper
to hide it and extending the cairo_t API to have everything we would
want on the ScaledFont object seems rather silly to me. Even leaving
aside my distaste for having to have a cairo_t around.

[...]

>>3. Expose a ScaledFont object, but only allow setting a FontName
>>    on a cairo_t. So, Pango would when creating a PangoFont
>>    would store the current scale, create a FontName, then
>>    create a ScaledFont for the FontName and scale. The
>>    PangoFont holds references to both ScaledFont and FontName.
> 
> This also sounds quite workable to me.
> 
> 
>>    This obviously has some performance impact, though
>>    per-string overhead when drawing is *much* better
>>    than per-glyph overhead when measuring.
> 
> 
> Why is there any performance impact here? Can't pango just not re-set
> the FontName if it hasn't changed since the last drawing operation and
> avoid the lookup overhead

In many cases, you have to draw a bunch of strings without necessarily
having any persistant PangoRenderer object around to know what FontName
it set last (or ability for Pango to know that someone else hasn't
changed the FontName on the cairo_t) in the mean time. So, any 
optimization here has to be at the Cairo level. OK, so Cairo can 
probably use pointer equality for FontName and double equality for
font matrix ... fine ... that's not going to be noticeable compared
to drawing.

The performance overhead I'm more worried about is when we *are* 
switching fonts. In something like a syntax highlighted program,
you can be switching fonts every 5-10 characters. Pango is
sitting on a ScaledFont object, but Cairo is going to have to
recreate it from the FontName and scale. We can likely reduce that
a malloc or two, and/or hash table lookup on 40 bytes of data.
I wouldn't expect it to show up at the top of profiles ... drawing
is just far less performance critical than measuring.  But it's
not completely negliable.

>>4. Allow setting either a ScaledFont or a FontName on a
>>    cairo_t. If both are set, the ScaledFont overrides the
>>    FontName. scale_font() transform_font() don't affect
>>    the ScaledFont.

> This is basically what we have in CVS now, and I don't like it.

It's slightly different ... I dont' think Ned would have been
quite as confused if he had gotten a 1 pixel version of his
selected font rather than the default font.

>>5. Allow setting either a ScaledFont or a FontName on a
>>    cairo_t. If a ScaledFont is set and the scale or
>>    font_matrix don't match the CTM, linearly scale the
>>    outlines or bitmaps to match the CTM.
> 
> This is what I proposed to fix what's in CVS, and you don't like
> it.

Indeed :-). Note that scaling the ScaledFont this way doesnt'
address what Alex wanted.

> I'm perfectly happy with either (1) or (3) from the point-of-view of
> API cleanliness. The downsides of those proposals are mostly(1) or
> exclusively(3) related to performance. And, unless I'm still not
> grasping part of the problem, it seems like we should be able to get
> the overhead down to per ScaledFont change, which I would expect to be
> quite manageable.

I consider (1) pretty unattractive:

  - It requires a cairo_t to measure text
  - It clutters cairo_t with backend specific functions, something we've
    avoided so far. (This maps horribly to object-oriented languages....
    you need a  different subclass of cairo_t depending on the font you
    set on it!)
  - We need to recreate or relookup the ScaledFont per-font-change *when
    measuring*. (In (3) we only need to do that when drawing, which is
    much more acceptable.)

I'll take a look at the concrete details of implementing (3).

Regards,
						Owen



More information about the cairo mailing list