[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Add support for FT_Face cloning

From: Tayyab Akram
Subject: Re: Add support for FT_Face cloning
Date: Fri, 4 Feb 2022 14:51:37 +0000 (UTC)


With the rise of variable fonts, it's possible to derive many variation instances of a typeface with different combination of coordinate values or colors. FreeType has already provided support for these fonts. Currently, two ways come into mind for adding support for these variations from a client's perspective.

* Have a single `FT_Face` object per font and setup specified variation coordinates on it whenever there's a request to perform a variation specific operation.
* Open a new `FT_Face` object per variation and reuse it when needed.

The first approach has following cons.
1. There's a potential cost of context switching with each variation-specific operation.
2. It's difficult to cache the face with variation-specific settings.

The second approach solves above drawbacks but it has following cons.
1. Additional cost of face initialization and setup.
2. Increased memory usage.


This proposal aims to take the best of both approaches by introducing a new function, `FT_Clone_Face`. This function would make a copy of the specified face by ensuring that the copied face shares as much memory as possible with the parent face. The output will be an exact copy of the source face leaving only glyph slot and size objects which will always be new. The client would be free to treat the cloned instance as any new face.

This would help in solving the drawbacks of 2nd approach effectively. As an addition, this would also help a client achieve immutability based on any combination of settings at `FT_Face` level.

Some Concepts

Before jumping into implementation details, there's a need to revise some concepts that would help understand the proposed solution.

A variable in a data structure can have the following traits.

* Static: The memory of this variable is statically allocated along with the parent struct and it has no life cycle of its own.
* Dynamic: The memory of this variable is dynamically allocated / freed at runtime and it has a proper life cycle.

* Immutable: This variable belongs to a data structure whose all members are set during initialization and not changed afterwards.
* Mutable: This variable belongs to a data structure whose members are expected to be changed after initialization.

* ReadWrite: The value of this variable can be set / changed anytime.
* Readonly: The value of this variable is set during initialaztion and not changed afterwards.
* Lazy: This variable is only initialized when accessing it for the first time.

Implementation Guidelines

The cloning can be done with the following set of rules.

Static & (Immutable | Mutable) & (ReadWrite | Readonly | Lazy)
Such variable can be copied in cloned instance with just simple assignment since it does not involve any kind of memory management.

Dynamic & Immutable & Readonly
Such variable can be shared in cloned instance provided that its lifecycle is only managed by the parent.

Dynamic & Immutable & Lazy
There are two ways to resolve such variable:
* Reset it along with related variables to initial values so that they can be initialized again on demand. In this case the lifecycle should be managed by the cloned instance.
* Copy its value with simple assignment in cloned instance. If it wasn't initialized during cloning, then on demand initializer function would be invoked when accessed for the first time. In this case, the cloned instance should check for the variable's existence in parent first. If it's already available in the parent, the values should be copied from there. If it's not available in the parent, then on demand initializer should be invoked on parent instance. After that, the values should be copied from the parent. Since the variable is only initialized in parent, its lifecycle should be managed by parent only.

Any Other Case
A new variable of same type must be created in cloned instance and the memory should be copied into it from the source variable. Needless to say, the cloned instance should also manage the lifecycle of new variable.

Implementation Details

As of now, the implementation of `FT_Clone_Face` is complete but currently it only covers the `truetype` format. It is available as part of merge request !134 (

The commit history will be revised where the first-ever commit will declare a new function, `FT_Face_CopyFunc` in `ftdrv.h`. It would take two parameters, a source face and a target face. The implementation would be responsible for copying format specific face properties into the target face. A new field `copy_face` would be declared in `FT_Driver_ClassRec`. Its data type would be `FT_Face_CopyFunc` and each format would set this to `NULL` initially. This `copy_face` would be used eventually to implement `FT_Clone_Face`.

Implementation Process

The `FT_Clone_Face` function will be implemented in multiple merge requests. The first MR will cover the base functionality and the `truetype` format. There will be subsequent MRs built on top of the first one. Each MR will cover a specific font format.


The cloned instance will not be fully thread-safe. So a lock would need to be associated with the root face or more precisely, the input stream. This is due the fact that a single stream object can be used to open multiple faces. This lock would need to be acquired whenever a client wants to mutate any face associated with the input stream, including the distinct faces, the parent face and the derived faces. An example of face mutation include the call to @FT_Load_Glyph and similar API. And of course this lock would be only needed when accessing any of the related face from multiple threads.


Please feel free to ask any questions or give suggestions.

Thanks to Werner Lemberg & Alexei Podtelezhnikov who helped in refining the problem statement, the scope and the process.

On Sunday, January 23, 2022, 03:59:59 AM GMT+5, Tayyab Akram <> wrote:

I have raised a PR for this feature.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]