Gradients for COLR/CPAL Fonts
December 2019
Authors:
Table of Contents
- Introduction
- Graphical Primitives / Paints
- OFF Changes
- Implementation
- References
- Acknowledgements
- Annex A: Proposed changes to ISO/IEC 14496-22
Introduction
The Introduction section is expected be converted into specific OFF section edits later in the specification process
We propose an extension of the COLR table to allow gradient fills in addition to the existing solid color fills. The current version number of COLR table is 0. We propose this as COLR table format version 1.
It is our understanding that this brings the capabilities of COLR/CPAL to match those of SVG Native for vector graphics. SVG Native allows embedding PNG and JPEG images while this proposal does not. We like to explore in the future, how COLR/CPAL can be mixed with sbix to address that limitation as well.
High-level Design
The COLR table is extended to expose a new vector of layers per glyph. If a glyph is not found in the new vector, the client will try finding it in the COLR v0 glyph vector and fall back to no-color if the glyph is not found there either.
A glyph using the new extension is mapped to a list of layers. Each layer in this vector of layers is formed by a directed acyclic graph of paints. The glyph rendering is defined by executing and combining the paint operations as described by this graph.
Note: Each paint reflects typical operations found in 2D graphics libraries.
Several different types of paint are defined:
- Glyph fills the shape of a non-COLR glyph with subsequent paints
- Solid color and gradient paints
- Solid paints a solid color
- Linear gradient paints a linear gradient
- Radial gradient paints a radial gradient
- Transformation reuses another paint, applying an affine transformation
- Composition reuses two other paints, applying a
compositing rule to combine them
- We draw on https://www.w3.org/TR/compositing-1/ for our composite modes
- COLR Glyph reuses a COLR v1 glyph at a new position in the graph
- COLR Layers paint one or more layers
We have added an "alpha" (transparency) scalar to each invocation of a palette color. This allows for the expression of translucent versions of palette entries, as well as foreground, which we find useful. Without this, various translucent shades of the same color would need to be encoded separately in the color palette, which is undesirable since color palette entries are designed to be exposed to end-users.
All values expressed are variable by way of OFF Font Variations.
Backwards Compatibility
The proposed design allows full backwards compatibility. This means, that a font designed for COLR format v1 specification, can contain sufficient information to be readable by a layout and rasterization engine that understands the v0 format. This is possible because the format version of the COLR table is a short format, as such considered a "minor", not a “major” version number.
If table format v1 is chosen, additional data will be read which specifies the additional information for gradients.
Graphical Primitives / Paints
Glyph
A glyph paint PaintGlyph
fills the region within the glyph shape
identified by gid
with downstream paint operations specified by paint
.
Note: An implementation may chose to implement this as clipping the drawing
region to the shape specified by gid
, then opening a temporary layer and
recurse to the subsequent paint opperations specified by paint
, after which
the temporary layer is merged.
Solid Color and Gradient Paints
The main graphical primitives that are added in this proposal are linear and radial gradients, transformations and compositions. Solid fills, which are also defined, are similar to COLR v0. Gradients are defined by the help of Color Lines and color stops, explained in the sections further below.
Solid
A solid paint fills the drawing region with a solid color specified by
ColorIndex
. ColorIndex
references color paletteIndex
from the CPAL
palette, and applies alpha value alpha
when drawing.
COLR Glyph
PaintColrGlyph
is a special paint which allows reuse of a COLR glyph of this
proposed exension as a paint. Painting a PaintColrGlyph
means executing the
paint operations that are described by the BaseGlyphV1Record
matching the
glyph id gid
specified in PaintColrGlyph
. See section Reusable Parts.
COLR Layers
PaintColrLayers
points to a sequence of Paint pointers and specifies how many
to consume. The mechanism can be used to share layers or sequences of layers
between multiple COLR glyphs.
Example: Suppose glyph A has 10 layers, 3 of which are a common backdrop that glyph B also uses. Glyph B can define a PaintColrLayers record that points to the same layers as A for the common parts.
See section Reusable Parts.
OFF Changes
NOTE: Content within the doc is in process of being reorganized. For clarity a new section for the proposed OFF changes is created at the end of the doc: Annex A: Proposed changes to ISO/IEC 14496-22
OFF 5.7.11 COLR – Color Table
The current header should be noted as COLR version 0 header.
A new section for COLR version 1 header should be added, along with a set of related records and tables.
COLR v1 data structures
This section describes new and modified tables and records for COLR v1.
Offsets are always relative to the start of the containing struct.
Header, glyphs, layers
V1 Header
Type | Field name | Description |
---|---|---|
uint16 | version | Table version number—set to 1. |
uint16 | numBaseGlyphRecords | May be 0 in a version 1 table. |
Offset32 | baseGlyphRecordsOffset | Offset to baseGlyphRecords array (may be NULL). |
Offset32 | layerRecordsOffset | Offset to layerRecords array (may be NULL). |
uint16 | numLayerRecords | May be 0 in a version 1 table. |
Offset32 | baseGlyphV1ListOffset | Offset to BaseGlyphV1List table. |
Offset32 | layersV1Offset | Offset to LayerV1List table. |
Offset32 | itemVariationStoreOffset | Offset to ItemVariationStore (may be NULL). |
BaseGlyphV1List table
Type | Name | Description |
---|---|---|
uint32 | numBaseGlyphV1Records | |
BaseGlyphV1Record | baseGlyphV1Records[numBaseGlyphV1Records] |
Entries shall be sorted in ascending order of the glyphID
field of the BaseGlyphV1Record
s.
Note: The sorted order allows implementations to perform binary search to
find a matching BaseGlyphV1Record
for a specific glyphID
.
BaseGlyphV1Record
Type | Name | Description |
---|---|---|
uint16 | glyphID | Glyph ID of the base glyph. |
Offset32 | paintOffset | Offset to Paint, typically a PaintColrLayers |
Note: The glyph ID is not limited to the numGlyphs value in the 'maxp' table.
LayerV1List table
Type | Field name | Description |
---|---|---|
uint32 | numLayers | |
Offset32 | paintOffsets[numLayers] | Offsets to Paint tables. |
Only layers referenced by PaintColrLayers
(format 1) records need to be
encoded here.
Variation structures
The following records are defined to facilitate COLR v1 font variation support.
To indicate no variation, set varOuterIndex and varInnerIndex to 0xFFFF.
VarFWord record
Type | Name | Description |
---|---|---|
FWORD | coordinate | |
uint16 | varOuterIndex | |
uint16 | varInnerIndex |
VarUFWord record
Type | Name | Description |
---|---|---|
UFWORD | distance | |
uint16 | varOuterIndex | |
uint16 | varInnerIndex |
VarFixed record
Type | Name | Description |
---|---|---|
Fixed | value | |
uint16 | varOuterIndex | |
uint16 | varInnerIndex |
Note: In order to combine deltas with Fixed values, the ItemVariationStore format is extended to allow for int32 deltas. When combining a Fixed value with 32-bit deltas, the Fixed value is treated as though it were int32.
VarF2Dot14 record
Type | Name | Description |
---|---|---|
F2Dot14 | value | |
uint16 | varOuterIndex | |
uint16 | varInnerIndex |
Values are inherently limited to [-2., 2). In some contexts, limited to the [-1., 1.] or [0., 1.].
Note: When combining an F2Dot14 with 16-bit deltas, the F2Dot14 is treated as though it were int16.
Color structures
Extend enumeration
Value | Name | Description |
---|---|---|
0 | EXTEND_PAD | Use nearest color stop. |
1 | EXTEND_REPEAT | Repeat from farthest color stop. |
2 | EXTEND_REFLECT | Mirror color line from nearest end. |
If a ColorLine.extend value is not recognized, use EXTEND_PAD.
ColorIndex record
Type | Name | Description |
---|---|---|
uint16 | paletteIndex | Index for a CPAL palette entry. |
VarF2Dot14 | alpha | Variable alpha value. |
Values for alpha outside [0.,1.] are reserved.
The ColorIndex alpha is multiplied into the alpha of the CPAL entry (converted to float -- divide by 255) to produce a final alpha.
ColorStop record
Type | Name | Description |
---|---|---|
VarF2Dot14 | stopOffset | Proportional distance on a color line; variable. |
ColorIndex | color |
ColorLine table
Type | Name | Description |
---|---|---|
uint8 | extend | An Extend enum value. |
uint16 | numStops | Number of ColorStop records. |
ColorStop | colorStops[numStops] |
Paint structures
PaintColrLayers table (format 1)
Type | Field name | Description |
---|---|---|
uint8 | format | Set to 1. |
uint8 | numLayers | Number of offsets to Paint to read from layers. |
uint32 | firstLayerIndex | Index into the LayerV1List. |
Each layer is composited on top of previous with mode COMPOSITE_SRC_OVER.
Note: uint8 size saves bytes in most cases. Large layer counts can be achieved by way of PaintComposite or a tree of PaintColrLayers.
PaintSolid table (format 2)
Type | Field name | Description |
---|---|---|
uint8 | format | Set to 2. |
ColorIndex | color | Solid color fill. |
PaintLinearGradient table (format 3)
Type | Field name | Description |
---|---|---|
uint8 | format | Set to 3. |
Offset24 | colorLineOffset | Offset to ColorLine, from start of PaintLinearGradient table. |
VarFWord | x0 | Start point x coordinate. |
VarFWord | y0 | Start point y coordinate. |
VarFWord | x1 | End point x coordinate. |
VarFWord | y1 | End point y coordinate. |
VarFWord | x2 | Rotation vector end point x coordinate. |
VarFWord | y2 | Rotation vector end point y coordinate. |
For linear gradient without skew, set x2,y2 to x1,y1.
PaintRadialGradient table (format 4)
Type | Field name | Description |
---|---|---|
uint8 | format | set to 4. |
Offset24 | colorLineOffset | offset from start of PaintRadialGradient table |
VarFWord | x0 | start circle center x coordinate |
VarFWord | y0 | start circle center y coordinate |
VarUFWord | radius0 | start circle radius |
VarFWord | x1 | end circle center x coordinate |
VarFWord | y1 | end circle center y coordinate |
VarUFWord | radius1 | end circle radius |
PaintGlyph table (format 5)
Type | Field name | Description |
---|---|---|
uint8 | format | Set to 5. |
Offset24 | paintOffset | Offset to a Paint table, from start of PaintGlyph table. |
uint16 | glyphID | Glyph ID for the source outline. |
Glyph outline is used as clip mask for the content in the Paint subtable. Glyph ID shall be less than the numGlyphs value in the 'maxp' table.
PaintColrGlyph table (format 6)
Type | Field name | Description |
---|---|---|
uint8 | format | Set to 6. |
uint16 | glyphID | Virtual glyph ID for a BaseGlyphV1List base glyph. |
Glyph ID shall be in the BaseGlyphV1List; may be greater than maxp.numGlyphs.
Note: The PaintColrGlyph and PaintColrLayers tables are similar in that they provide a way to reference a graph of paint tables as a sub-component within a color glyph description. (The PaintColrGlyph does this indirectly via a base glyph ID.) They may be handled differently in implementations, however. In particular, an implementation can process and cache the result of the color glyph description for a given base glyph ID. In that case, subsequent references to that base glyph ID using a PaintColrGlyph table would not require the corresponding graph of paint tables to be re-processed. As a result, using a PaintColrGlyph for re-used graphic components could provide performance benefits.
PaintTransformed table (format 7)
Type | Field name | Description |
---|---|---|
uint8 | format | Set to 7. |
Offset24 | paintOffset | Offset to a Paint subtable, from start of PaintTransformed table. |
Affine2x3 | transform | An Affine2x3 record (inline). |
PaintRotate table (format 8)
Type | Field name | Description |
---|---|---|
uint8 | format | Set to 8. |
Offset24 | paintOffset | Offset to a Paint subtable, from start of PaintRotate table. |
VarFixed | angle | Rotation angle, in counter-clockwise degrees. |
VarFixed | centerX | x coordinate for the center of rotation. |
VarFixed | centerY | y coordinate for the center of rotation. |
Note: Rotation can also be represented using the PaintTransformed table. The important difference is in allowing an angle to be specified directly in degrees, which is more amenable to smooth variation.
PaintSkew table (format 9)
Type | Field name | Description |
---|---|---|
uint8 | format | Set to 9. |
Offset24 | paintOffset | Offset to a Paint subtable, from start of PaintSkew table. |
VarFixed | xSkewAngle | Angle of skew in the direction of the x-axis, in counter-clockwise degrees. |
VarFixed | ySkewAngle | Angle of skew in the direction of the y-axis, in counter-clockwise degrees. |
VarFixed | centerX | x coordinate for the center of skew. |
VarFixed | centerY | y coordinate for the center of skew. |
Note: Skews can also be represented using the PaintTransformed table. The important difference is in being able to specify skew as an angle rather than as changes to basis vectors. Also, when varying angles, a representation directly in degrees is more amenable to smooth variation.
PaintComposite table (format 10)
Type | Field name | Description |
---|---|---|
uint8 | format | Set to 10. |
Offset24 | sourcePaintOffset | Offset to a source Paint table, from start of PaintComposite table. |
uint8 | compositeMode | A CompositeMode enumeration value. |
Offset24 | backdropPaintOffset | Offset to a backdrop Paint table, from start of PaintComposite table. |
If compositeMode value is not recognized, COMPOSITE_CLEAR is used.
Composite modes
Supported composition modes are taken from the W3C Compositing and Blending Level 1 specification.
CompositeMode enumeration
Value | Name | Description |
---|---|---|
Porter-Duff modes | ||
0 | COMPOSITE_CLEAR | See Clear |
1 | COMPOSITE_SRC | See Copy |
2 | COMPOSITE_DEST | See Destination |
3 | COMPOSITE_SRC_OVER | See Source Over |
4 | COMPOSITE_DEST_OVER | See Destination Over |
5 | COMPOSITE_SRC_IN | See Source In |
6 | COMPOSITE_DEST_IN | See Destination In |
7 | COMPOSITE_SRC_OUT | See Source Out |
8 | COMPOSITE_DEST_OUT | See Destination Out |
9 | COMPOSITE_SRC_ATOP | See Source Atop |
10 | COMPOSITE_DEST_ATOP | See Destination Atop |
11 | COMPOSITE_XOR | See XOR |
Separable color blend modes: | ||
12 | COMPOSITE_SCREEN | See screen blend mode |
13 | COMPOSITE_OVERLAY | See overlay blend mode |
14 | COMPOSITE_DARKEN | See darken blend mode |
15 | COMPOSITE_LIGHTEN | See lighten blend mode |
16 | COMPOSITE_COLOR_DODGE | See color-dodge blend mode |
17 | COMPOSITE_COLOR_BURN | See color-burn blend mode |
18 | COMPOSITE_HARD_LIGHT | See hard-light blend mode |
19 | COMPOSITE_SOFT_LIGHT | See soft-light blend mode |
20 | COMPOSITE_DIFFERENCE | See difference blend mode |
21 | COMPOSITE_EXCLUSION | See exclusion blend mode |
22 | COMPOSITE_MULTIPLY | See multiply blend mode |
Non-separable color blend modes: | ||
23 | COMPOSITE_HSL_HUE | See hue blend mode |
24 | COMPOSITE_HSL_SATURATION | See saturation blend mode |
25 | COMPOSITE_HSL_COLOR | See color blend mode |
26 | COMPOSITE_HSL_LUMINOSITY | See luminosity blend mode |
Transform
Affine2x3 record
Type | Name | Description |
---|---|---|
VarFixed | xx | x-component of transformed x-basis vector |
VarFixed | yx | y-component of transformed x-basis vector |
VarFixed | xy | x-component of transformed y-basis vector |
VarFixed | yy | y-component of transformed y-basis vector |
VarFixed | dx | Translation in x direction. |
VarFixed | dy | Translation in y direction. |
The Affine2x3
record is a 2x3 matrix for 2D affine transformations, so
that for a transformation matrix M and an extended starting vector v = (x, y, 1)
the mapped vector v′ is calculated as v′ = M * v. That is:
v′x = xx * x + xy * y + dx
v′y = yx * x + yy * y + dy
Note: It is helpful to understand linear transformations by their effect on x- and y-basis vectors î = (1, 0) and ĵ = (0, 1). The transform described by the Affine2x3 record maps the basis vectors to î′ = (xx, yx) and ĵ′ = (xy, yy), and translates the origin to (dx, dy).
Constraints
Constraints on the data structures making up a COLR version 1 should be noted.
Acyclic Graphs Only
PaintColrGlyph
and PaintColrLayers
allow recursive composition of
COLR glyphs. This is desirable for reusable parts but introduces the
possibility of a cyclic graph. Implementations should fail if a cycle
is detected.
Note: Cycle detection can be achieved by keeping a set of addresses of visited paints. Before processing a paint check if it's address is in the set, if it is we have a cycle and should fail. Once done processing a given paint, including children, take it's address out of the set. This allows the same paint to be reached repeatedly as long as no cycle is formed. Pseudocode:
# called initially with the base glyph paint and an empty set
function paintIsAcyclic(paint, active_paints)
if paint in active_paints
fail: we have a cycle
add paint to active_paints
process paint, calling paintIsAcyclic() recursively for referenced paints
remove paint from active_paints
Bounded Layers Only
The BaseGlyphV1Record
paint must define a bounded region. That is,
is must paint within an area for which a finite bounding box could be
defined. Implementations must confirm this invariant.
A BaseGlyphV1Record
with an unbounded paint must not render.
The following paints are always bounded:
PaintGlyph
PaintColrGlyph
The following paints are always unbounded:
PaintSolid
PaintLinearGradient
PaintRadialGradient
The following paints may be bounded:
PaintTransformed
is bounded IFF the source is boundedPaintComposite
boundedness varies by mode:- Always bounded
COMPOSITE_CLEAR
- Bounded IFF src is bounded
COMPOSITE_SRC
COMPOSITE_SRC_OUT
- Bounded IFF backdrop is bounded
COMPOSITE_DEST
COMPOSITE_DEST_OUT
- Bounded IFF src OR backdrop is bounded
COMPOSITE_SRC_IN
COMPOSITE_DEST_IN
- Bounded IFF src AND backdrop are bounded
- all other modes
- Always bounded
PaintColrLayers
is bounded IFF all referenced layers are bounded
Bounding Box
The bounding box of the base (non-COLR) glyph referenced from the
BaseGlyphV1Record
(by BaseGlyphV1Record::gid
) should be taken
to describe the bounding box for the COLR v1 glyph.
Note: A glyf
entry with two points at the diagonal extrema would suffice.
Note: This can be used to allocate a drawing surface without traversing the COLR v1 glyph structure.
Understanding COLR v1
Addition of explanatory content explaining how COLR version 1 functions should be added.
Alpha
The alpha channel for a layer can be populated using PaintComposite
:
PaintSolid
can be used to set a blanket alphaPaindLinearGradient
andPaintRadialGradient
can be used to set gradient alpha- Mode Source In can be used to mask
Implementation
This section is NOT meant for ISO submissions
C++ Structures
The following provides a C++ implementation of the structures defined above.
// Base template types
template <typename T, typename Length=uint16>
struct ArrayOf
{
Length count;
T array[/*count*/];
};
typedef uint32 VarIdx;
template <typename T>
struct Variable
{
T value;
VarIdx varIdx; // Use 0xFFFFFFFF to indicate no variation.
};
// Variation structures
typedef Variable<FWORD> VarFWORD;
typedef Variable<UFWORD> VarUFWORD;
typedef Variable<Fixed> VarFixed;
typedef Variable<F2DOT14> VarF2DOT14;
// Color structures
// The ColorIndex alpha is multiplied into the alpha of the CPAL entry
// (converted to float -- divide by 255) looked up using paletteIndex to
// produce a final alpha.
struct ColorIndex
{
uint16 paletteIndex;
VarF2DOT14 alpha; // Default 1.0. Values outside [0.,1.] reserved.
};
struct ColorStop
{
VarF2DOT14 stopOffset;
ColorIndex color;
};
enum Extend : uint8
{
EXTEND_PAD = 0,
EXTEND_REPEAT = 1,
EXTEND_REFLECT = 2,
};
struct ColorLine
{
Extend extend;
ArrayOf<ColorStop> stops;
};
// Composition modes
// Compositing modes are taken from https://www.w3.org/TR/compositing-1/
// NOTE: a brief audit of major implementations suggests most support most
// or all of the specified modes.
enum CompositeMode : uint8
{
// Porter-Duff modes
// https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators
COMPOSITE_CLEAR = 0, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_clear
COMPOSITE_SRC = 1, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_src
COMPOSITE_DEST = 2, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dst
COMPOSITE_SRC_OVER = 3, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcover
COMPOSITE_DEST_OVER = 4, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstover
COMPOSITE_SRC_IN = 5, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcin
COMPOSITE_DEST_IN = 6, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstin
COMPOSITE_SRC_OUT = 7, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcout
COMPOSITE_DEST_OUT = 8, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstout
COMPOSITE_SRC_ATOP = 9, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcatop
COMPOSITE_DEST_ATOP = 10, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstatop
COMPOSITE_XOR = 11, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_xor
// Blend modes
// https://www.w3.org/TR/compositing-1/#blending
COMPOSITE_SCREEN = 12, // https://www.w3.org/TR/compositing-1/#blendingscreen
COMPOSITE_OVERLAY = 13, // https://www.w3.org/TR/compositing-1/#blendingoverlay
COMPOSITE_DARKEN = 14, // https://www.w3.org/TR/compositing-1/#blendingdarken
COMPOSITE_LIGHTEN = 15, // https://www.w3.org/TR/compositing-1/#blendinglighten
COMPOSITE_COLOR_DODGE = 16, // https://www.w3.org/TR/compositing-1/#blendingcolordodge
COMPOSITE_COLOR_BURN = 17, // https://www.w3.org/TR/compositing-1/#blendingcolorburn
COMPOSITE_HARD_LIGHT = 18, // https://www.w3.org/TR/compositing-1/#blendinghardlight
COMPOSITE_SOFT_LIGHT = 19, // https://www.w3.org/TR/compositing-1/#blendingsoftlight
COMPOSITE_DIFFERENCE = 20, // https://www.w3.org/TR/compositing-1/#blendingdifference
COMPOSITE_EXCLUSION = 21, // https://www.w3.org/TR/compositing-1/#blendingexclusion
COMPOSITE_MULTIPLY = 22, // https://www.w3.org/TR/compositing-1/#blendingmultiply
// Modes that, uniquely, do not operate on components
// https://www.w3.org/TR/compositing-1/#blendingnonseparable
COMPOSITE_HSL_HUE = 23, // https://www.w3.org/TR/compositing-1/#blendinghue
COMPOSITE_HSL_SATURATION = 24, // https://www.w3.org/TR/compositing-1/#blendingsaturation
COMPOSITE_HSL_COLOR = 25, // https://www.w3.org/TR/compositing-1/#blendingcolor
COMPOSITE_HSL_LUMINOSITY = 26, // https://www.w3.org/TR/compositing-1/#blendingluminosity
};
// Affine 2D transformations
// This is a standard 2x3 matrix for 2D affine transformation.
struct Affine2x3
{
VarFixed xx;
VarFixed yx;
VarFixed xy;
VarFixed yy;
VarFixed dx;
VarFixed dy;
};
// Paint tables
// Each layer is composited on top of previous with mode COMPOSITE_SRC_OVER.
// NOTE: uint8 size saves bytes in most cases and does not
// preclude use of large layer counts via PaintComposite or a tree
// of PaintColrLayers.
struct PaintColrLayers
{
uint8 format; // = 1
uint8 numLayers;
uint32 firstLayerIndex; // index into COLRv1::layersV1
}
struct PaintSolid
{
uint8 format; // = 2
ColorIndex color;
};
struct PaintLinearGradient
{
uint8 format; // = 3
Offset24<ColorLine> colorLine;
VarFWORD x0;
VarFWORD y0;
VarFWORD x1;
VarFWORD y1;
VarFWORD x2; // Normal; Equal to (x1,y1) in simple cases.
VarFWORD y2;
};
struct PaintRadialGradient
{
uint8 format; // = 4
Offset24<ColorLine> colorLine;
VarFWORD x0;
VarFWORD y0;
VarUFWORD radius0;
VarFWORD x1;
VarFWORD y1;
VarUFWORD radius1;
};
// Paint a non-COLR glyph, filled as indicated by paint.
struct PaintGlyph
{
uint8 format; // = 5
Offset24<Paint> paint;
uint16 gid; // not a COLR-only gid
// shall be less than maxp.numGlyphs
}
struct PaintColrGlyph
{
uint8 format; // = 6
uint16 gid; // shall be a COLR gid
}
struct PaintTransformed
{
uint8 format; // = 7
Offset24<Paint> src;
Affine2x3 transform;
};
struct PaintRotate
{
uint8 format; // = 8
Offset24<Paint> src;
VarFixed angle
VarFixed centerX
VarFixed centerY
};
struct PaintSkew
{
uint8 format; // = 9
Offset24<Paint> src;
VarFixed xSkewAngle
VarFixed ySkewAngle
VarFixed centerX
VarFixed centerY
};
struct PaintComposite
{
uint8 format; // = 10
Offset24<Paint> src;
CompositeMode mode; // If mode is unrecognized use COMPOSITE_CLEAR
Offset24<Paint> backdrop;
};
struct BaseGlyphV1Record
{
uint16 gid;
Offset32<Paint> paint; // Typically PaintColrLayers
};
// Entries shall be sorted in ascending order of the `glyphID` field of the `BaseGlyphV1Record`s.
typedef ArrayOf<BaseGlyphV1Record, uint32> BaseGlyphV1List;
// Only layers accessed via PaintColrLayers (format 1) need be encoded here.
typedef ArrayOf<Offset32<Paint>, uint32> LayerV1List;
struct COLRv1
{
// Version-0 fields
uint16 version;
uint16 numBaseGlyphsV0;
Offset32<SortedUnsizedArrayOf<BaseGlyphRecordV0>> baseGlyphsV0;
Offset32<UnsizedArrayOf<LayerRecordV0>> layersV0;
uint16 numLayersV0;
// Version-1 additions
Offset32<BaseGlyphV1List> baseGlyphsV1;
Offset32<LayerV1List> layersV1;
Offset32<ItemVariationStore> varStore;
};
Font Tooling
Cosimo (@anthrotype) and Rod (@rsheeter) have implemented nanoemoji to compile a set of SVGs into color font formats, including COLR v1.
color-fonts has a collection of sample color fonts.
Rendering
Pseudocode
Allocate a bitmap for the glyph according to glyf table entry extents for gid
0) Start at base glyph paint.
a) Paint a paint, switch:
1) PaintColrLayers
Paint each referenced layer by performing a)
2) PaintSolid
SkCanvas::drawColor with color configured
3) PaintLinearGradient
SkCanvas::drawPaint with liner gradient configured
(expected to be bounded by parent composite mode or clipped by current clip, check bounds?)
4) PaintRadialGradient
SkCanvas::drawPaint with radial gradient configured
(expected to be bounded by parent composite mode or clipped by current clip, check bounds?)
5) PaintGlyph
gid must not COLRv1
saveLayer
setClipPath to gid path
recurse to a)
restore
6) PaintColrGlyph
gid must be from
if gid on recursion blacklist, do nothing
recurse to 0) with different gid
7) PaintTransformed
saveLayer()
apply transform
call a) for paint
restore
8) PaintRotate
saveLayer()
apply transform
call a) for paint
restore
9) PaintSkew
saveLayer()
apply transform
call a) for paint
restore
10) PaintComposite
paint Paint for backdrop, call a)
saveLayer() with setting composite mode, on SkPaint
paint Paint for src, call a)
restore with save composite mode
FreeType
FreeType API extensions needed for a) rasterisation b ) as well as API exposing all the details of the gradients so clients can render them as they wish, by encoding as gradients in PDF output for example.
Chromium
Prototype an implementation inside Skia's FreeType based COLR/CPAL
implementation extending solid color fills with gradient fills, after extracting
gradient fill implementation from FreeType. See
SkFonstHost_FreeType_common.cpp
HarfBuzz
HarfBuzz implementation will follow later. No major client relies on HarfBuzz for color fonts currently, but we certainly want to implement later as there are clients who like to remove FreeType dependency completely.
References
Acknowledgements
This section is NOT meant for inclusion in ISO submissions
Thanks to Benjamin Wagner (@bungeman), Dave Crossland (@davelab6), and Roderick Sheeter (@rsheeter) for review and detailed feedback on earlier proposal.
Annex A: Proposed changes to ISO/IEC 14496-22
Changes to the following sections of ISO/IEC 14496-22:2019 Open Font Format (OFF) are proposed:
- 4.3 Data types
- 5.7.11 COLR – Color Table
- 7.2.3 Item variation stores
- Bibliography
A.1 Changes to OFF 4.3 Data types
Replace the table defining data types with the following (added row for Offset24):
Data Types | Description |
---|---|
uint8 | 8-bit unsigned integer. |
int8 | 8-bit signed integer. |
uint16 | 16-bit unsigned integer. |
int16 | 16-bit signed integer. |
uint24 | 24-bit unsigned integer. |
uint32 | 32-bit unsigned integer. |
int32 | 32-bit signed integer. |
Fixed | 32-bit signed fixed-point number (16.16) |
FWORD | int16 that describes a quantity in font design units. |
UFWORD | uint16 that describes a quantity in font design units. |
F2DOT14 | 16-bit signed fixed number with the low 14 bits of fraction (2.14). |
LONGDATETIME | Date and time represented in number of seconds since 12:00 midnight, January 1, 1904. The value is represented as a signed 64-bit integer. |
Tag | Array of four uint8s (length = 32 bits) used to identify a table, design-variation axis, script, language system, feature, or baseline |
Offset16 | Short offset to a table, same as uint16, NULL offset = 0x0000 |
Offset24 | 24-bit offset to a table, same as uint24, NULL offset = 0x000000 |
Offset32 | Long offset to a table, same as uint32, NULL offset = 0x00000000 |
A.2 Changes to OFF 5.7.11 - Color Table
Replace the content of clause 5.7.11 with the following:
The COLR table adds support for multi-colored glyphs in a manner that integrates with the rasterizers of existing text engines and that is designed to be easy to support with current OpenType font files.
The COLR table defines color presentations for glyphs. The color presentation of a glyph is specified as a graphic composition using other glyphs, such as a layered arrangement of glyphs, each with a different color. The term “color glyph” is used informally to refer to such a graphic composition defined in the COLR table; and the term “base glyph” is used to refer to a glyph for which a color glyph is provided. Processing of the COLR table is done on glyph sequences after text layout processing is completed and prior to final presentation of glyphs. Typically, a base glyph is a glyph that may occur in a sequence that results from the text layout process. In some cases, a base glyph may be a virtual glyph defined within this table as a re-usable color composition.
For example, the Unicode character U+1F600 is the grinning face emoji. Suppose in an emoji font the 'cmap' table maps U+1F600 to glyph ID 718. Assuming no glyph substitutions, glyph ID 718 would be considered the base glyph. Suppose the COLR table has data describing a color presentation for this using a layered arrangement of other glyphs with different colors assigned: that description and its presentation result would be considered the corresponding color glyph.
Two versions of the COLR table are defined.
Version 0 allows for a simple composition of colored elements: a linear sequence of glyphs that are stacked vertically as layers in bottom-up z-order. Each layer combines a glyph outline from the 'glyf', CFF or CFF2 table (referenced by glyph ID) with a solid color fill. These capabilities are sufficient to define color glyphs such as those illustrated in figure 5.6.
Figure 5.6 Examples of the graphic capabilities of COLR version 0
Version 1 supports additional graphic capabilities. In addition to solid colors, gradient fills can be used, as well as more complex fills using other graphic operations, including affine transformations and various blending modes. Version 1 capabilities allow for color glyphs such as those illustrated in figure 5.7:
Figure 5.7 Examples of the graphic capabilities of COLR version 1
Version 1 also extends capabilities in variable fonts. A COLR version 0 table can be used in variable fonts with glyph outlines being variable, but no other aspect of the color composition being variable. In version 1, all of the new constructs for which it could be relevant have been designed to be variable; for example, the placement of color stops in a gradient, or the alpha values applied to colors. The graphic capabilities supported in version 0 and in version 1 are described in more detail below.
The COLR table is used in combination with the CPAL table (5.7.12): all color values are specified as entries in color palettes defined in the CPAL table. If the COLR table is present in a font but no CPAL table exists, then the COLR table is ignored.
5.7.11.1 Graphic Compositions
The graphic compositions in a color glyph definition use a set of 2D graphic concepts and constructs:
- Shapes (or geometries)
- Fills (or shadings)
- Layering—a z-order—of elements
- Composition and blending modes—different ways that the content of a layer is combined with the content of layers above or below it
- Affine transformations
For both version 0 and version 1, shapes are obtained from glyph outlines in the 'glyf', 'CFF ' or CFF2 table, referenced by glyph ID. Colors used in fills are obtained from the CPAL table.
The simplest color glyphs use just a few of the concepts above: shapes, solid color fills, and layering. This is the set of capabilities provided by version 0 of the COLR table. In version 0, a base glyph record specifies the color glyph for a given base glyph as a sequence of layers. Each layer is specified in a layer record and has a shape (a glyph ID) and a solid color fill (a CPAL palette entry). The filled shapes in the layer stack are composed using only alpha blending.
Figure 5.8 illustrates the version 0 capabilities: three shapes are in a layered stack: a blue square in the bottom layer, an opaque green circle in the next layer, and a red triangle with some transparency in the top layer.
Figure 5.8 Basic graphic capabilities of COLR version 0
The basic concepts also apply to color glyphs defined using the version 1 formats: shapes are arranged in layers and have fills. But the additional formats of version 1 support much richer capabilities. In a version 1 color glyph, graphic constructs and capabilities are represented primarily in Paint tables, which are linked together in a directed, acyclic graph. Several different Paint formats are defined, each describing a particular type of graphic operation:
-
A PaintColrLayers table provides a layering structure used for creating a color glyph from layered elements. A PaintColrLayers table can be used at the root of the graph, providing a base layering structure for the entire color glyph definition. A PaintColrLayers table can also be nested within the graph, providing a set of layers to define some graphic sub-component within the color glyph.
-
The PaintSolid, PaintLinearGradient, and PaintRadialGradient tables provide basic fills, using color entries from the CPAL table.
-
The PaintGlyph table provides glyph outlines as the basic shapes.
-
The PaintTransformed table is used to apply an affine transformation matrix to a sub-graph of paint tables, and the graphic operations they represent. The PaintRotate and PaintSkew tables support specific transformations specified as angles.
-
The PaintComposite table supports alternate compositing and blending modes for two sub-graphs.
-
The PaintColrGlyph table allows a color glyph definition, referenced by a base glyph ID, to be re-used as a sub-graph within multiple color glyphs.
In a simple color glyph description, a PaintGlyph table might be linked to a PaintSolid table, for example, representing a glyph outline filled using a basic solid color fill. But the PaintGlyph table could instead be linked to a much more complex sub-graph of Paint tables, representing a shape that gets filled using the more-complex set of operations described by the sub-graph of Paint tables.
The graphic capabilities are described in more detail in 5.7.11.1.1 – 5.7.11.1.7. The formats used for each are specified 5.7.11.2.
5.7.11.1.1 Colors and solid color fills
All colors are specified as a base zero index into CPAL (5.7.12) palette entries. A font can define alternate palettes in its CPAL table; it is up to the application to determine which palette is used. A palette entry index value of 0xFFFF is a special case indicating that the text foreground color (defined by the application) should be used, and shall not be treated as an actual index into the CPAL ColorRecord array.
The CPAL color data includes alpha information, as well as RGB values. In the COLR version 0 formats, a color reference is made in LayerRecord as a palette entry index alone. In the formats added for COLR version 1, a color reference is made in a ColorIndex record, which includes a palette entry index and a separate alpha value. Separation of alpha from palette entries in version 1 allows use of transparency in a color glyph definition independent of the choice of palette. The alpha value in the ColorIndex record is multiplied into the alpha value given in the CPAL color entry.
In version 1, a solid color fill is specified using a PaintSolid table. See 5.7.11.1.3 for details on how a PaintSolid fill is applied to a shape.
5.7.11.1.2 Gradients
COLR version 1 supports two types of gradients: linear gradients, and radial gradients. Both types of gradient are defined using a color line.
5.7.11.1.2.1 Color Lines
A color line is a function that maps real numbers to a color value to define a 1-dimensional gradient, to be used and referenced from Linear Gradients and Radial Gradients. Colors of the gradient are defined by color stops.
Color stops are defined at color stop positions. Color stop position 0 maps to the start point of a linear gradient or the center of the first circle of a radial gradient. Color stop position 1 maps to the end point of a linear gradient or the center of the second circle of a radial gradient. In the interval [0, 1] the color line must contain at least one color stop, but may contain multiple color stops that define the gradient.
Outside the defined interval, the gradient pattern in between the outer defined positions is repeated according to the color line extend mode.
If there are multiple color stops defined for the same coordinate, the first one is used for computing the color value for values below the coordinate, the last one is used for computing the color value for values above. All other color stops for this coordinate are ignored.
Limiting the specified interval to a sub-range of [0, 1] allows for looping through colors repeatedly along the mapped distance, without having to encode them multiple times. In that sense, our color line is similar to CSS repeating-linear-gradient() and repeating-radial-gradient() functions.
In order to achieve:
-
one gradient along the gradient positions (linear, or radial) and padded colors outside this range, color stops at 0 and 1 must be defined, and color line extend mode pad must be used. This achieves similarly behavior as defined in CSS linear-gradient() and radial-gradient() functions.
-
a repeated gradient along the gradient positions (linear or radial): divide 1 by the number of desired repetitions and use the result as your maximum color stop, then use color line extend mode repeat to have it continue outside the defined interval.
-
a mirrored / color-circle gradient: divide 1 by two times the number of desired full color stripes, and define the color stops between the 0 and the result of this division, then use color line extend mode reflect to have it continue mirrored.
Figure 1: Repeating linear and radial gradients (source)
Extend Mode
We propose three extend modes to control the behavior of the gradient outside its specified endpoints:
Extend Pad
For numbers outside the defined interval the color line continues to map to the outer color values, i.e. for values less than the leftmost defined color stop, it maps to the leftmost color stop value; for values greater than the rightmost defined color stop value, it maps to the rightmost defined color value.
Extend Repeat
For numbers outside the interval, the color line continues to map as if the defined interval was repeated.
Extend Reflect
For numbers outside the defined interval, the color continues to map as if the interval would continue mirrored from the previous interval. This allows defining stripes in rotating colors.
5.7.11.1.2.2 Linear gradients
We propose definitions of linear gradients with two color line points P0 and P1 between which a gradient is interpolated. A point P₂ is defined to rotate the gradient angle / orientation separately from the color line endpoints.
If the dot-product (P₁ - P₀) . (P₂ - P₀) is zero (or near-zero for an implementation-defined definition) then gradient is ill-formed and nothing must be rendered.
Figure 2: Examples of linear gradients and their defining points with extend modes pad, repeat and reflect (top to bottom) with color stops for blue at 0, yellow at 0.5 and red at 1. (Illustration generated from images/radial_gradients.svg, requires glMatrix.js to work)
5.7.11.1.2.3 Radial gradients
Radial gradients in this proposal are defined based on circles. If subject to
a transform (via PaintTransformed
) those circles may become ellipses.
A radial gradient in this proposal is a gradient between two—optionally transformed—circles, namely with center c₀ and radius r₀, and center c₁ and radius r₁ and a specified color line. The circle c₀, r₀ will be drawn with the color at color line position 0. The circle c₁, r₁ will be drawn with the color at color line colorLine position 1.
The drawing algorithm radial gradients follows the HTML WHATWG Canvas spec for createRadialGradient(). Quoting and adapting from there. With circle center points c₀ and c₁ defined as c₀ = (x₀, y₀) and c₁ = (x₁, y₁):
Radial gradients must be rendered by following these steps:
- If c₀ = c₁ and r₀ = r₁ then the radial gradient must paint nothing. Return.
- Let x(ω) = (x₁-x₀)ω + x₀
Let y(ω) = (y₁-y₀)ω + y₀
Let r(ω) = (r₁-r₀)ω + r₀
Let the color at ω be the color at that position on the gradient color line (with the colors coming from the interpolation and extrapolation described above). - For all values of ω where r(ω) > 0, starting with the value of ω nearest to positive infinity and ending with the value of ω nearest to negative infinity, draw the circumference of the ellipse resulting from translating circle with radius r(ω) by affine transform at position (x(ω), y(ω)), with the color at ω, but only painting on the parts of the bitmap that have not yet been painted on by earlier circles in this step for this rendering of the gradient.
Figure 3: Example of a radial gradient rendering with extend modes pad, repeat and reflect (top to bottom) with color stops for blue at 0, yellow at 0.5 and red at 1. (Illustration generated from images/radial_gradients.svg.)
Because the rendering algorithm progresses ω in a particular direction, from positive infinity to negative infinity, and because pixels are not re-painted as ω progresses, the appearance will be affected by which circle is considered circle 0 and which is circle 1.
This is illustrated in the figure 4, in which three radial gradients are shown. The first is the same as the first gradient shown in figure 3, using the pad extend mode. In this gradient, circle 0 is the small circle, on the left. In the second gradient of figure 4, the start and end circles are reversed: circle 0 is the large circle, on the right. The color line is kept the same, and so the red end starts at circle 0, now on the right. In the third gradient, the order of stops in the color line is also reversed to put red on the left.
The key difference to notice between the gradients in figure 4 is the way colors are painted in the interior: when the two circles are not overlapping, the arc of color always bends towards circle 0. This difference does not exist if one circle is wholly contained within the other, however: in that case, the individual colors are complete, concentric circles.
Figure 4: Radial gradients with start and end circles swapped. (Illustration generated from images/radial_gradient_direction.svg
Note: It follows from the algorithm that: a) When both radii are 0, r₀ = r₁ = 0, then r(ω) is always 0 and nothing is painted. b) If the centers of the circles are distinct, one has a zero radius and lies outside the radius of the other, the other has a non-zero radius, the resulting shape resembles a cone that is open to one side, see Figure 3 below. c) If the circles centers are identical, one has a zero radius, one has a non-zero radius, then the gradient is well-defined and is painted according to the algorithm.
If either circle is contained within the other, the gradient is well-defined and will be drawn by the algorithm. This applies regardless of whether the centers are identical or different, or whether the inner circle has a zero or non-zero radius, Colors inside the inner circle and outside the outer circle will be determined by the extend mode. The extension of the gradient beyond the outer circle will fill the entire surface. This is illustrated in the following figure for the three extend modes.
Figure 5: Radial gradients with one circle contained within the other. (Illustration generated from images/radial_gradients_circle_within_circle.svg.)
Note: When a radial gradient is nested below a transformation which flattens the circles so that they resemble lines, a radial gradient may appear as a strip or a cone filled with a linear gradient.
5.7.11.1.3 Filling shapes
5.7.11.1.4 Layers
5.7.11.1.5 Transformations
A transformation as defined by a PaintTransformed
applies a matrix
transformation transform
to the current drawing region. The transformation is
to be applied for subsequent nested paints, as defined by the paint referenced
in src
. The transformation affects all nested drawing operations. It affects
how nested solid paints and gradients are drawn, as well as how nested clip
operations or nested COLR glyph reuse operations are performed.
5.7.11.1.6 Compositing and blending
A composition is a graphical primitive that allows combining two paints given a
blending rule for each pixel. A composition as defined by a PaintComposite
references two nested paints, backdrop
and src
. First, the paint operations
for backdrop
are executed, then the drawing operations for src
are executed
and combined with backdrop
given the blending rule specified in
mode
. Compositing modes are taken from Compositing modes are taken from the
W3C Compositing specification.
5.7.11.1.7 Re-usable components
Use PaintTransformed
to reuse parts in different positions or sizes.
Use PaintColrGlyph
to reuse entire COLR glyphs.
Use PaintColrLayers
to reuse parts of COLR glyphs. For example, a common
backdrop made up of several layers.
For example, consider the Noto clock emoji (hand colored for emphasis):
The entire backdrop (outline, gradient-circle, 4 dots, the minute hand) is reusable for all versions of the clock:
The hour hand is reusable as a transformed glyph.
Another example might be emoji faces: many have the same backdrop with different eyes, noses, tears, etc drawn on top.
5.7.11.2 COLR table formats
[under construction—more to come]
A.3 Changes to OFF 7.2.3 Item variation stores
Delete the fourth paragraph, "Variation data is comprised...".
Add a new sub-clause 7.2.3.1 after the third paragraph ("The item variation store formats..."), with text as follows:
7.2.3.1 Associating target items to variation data
Variation data is comprised of delta adjustment values that apply to particular target items. Some mechanism is needed to associate delta values with target items. In the item variation store, a block of delta values has an implicit delta-set index, and separate data outside the item variation store is provided that indicates the delta-set index associated with a given target item. Depending on the parent table in which an item variation store is used, different means are used to provide these associations:
- In the MVAR table, an array of records identifies target data items in various other tables, along with the delta-set index for each respective item.
- In the HVAR and VVAR tables, the target data items are glyph metric arrays in the 'hmtx' and 'vmtx' tables. Subtables in the HVAR and VVAR tables provide the mapping between the target data items and delta-set indices.
- For the BASE, GDEF, GPOS, and JSTF tables, a target data item is associated with a delta-set index using a related VariationIndex table (see 6.2.8) within the same subtable that contains the target item.
- In the COLR table, target data items are specified in structures that combine a basic data type, such FWORD, with a delta-set index.
The structures used in the COLR table currently are used only in that table but may be used in other tables in future versions, and so are defined here as common formats. Structures are defined to wrap the FWORD, UFWORD, F2DOT14 and Fixed basic types.
Note: as described below, each delta-set index is represented as two index components, an outer index and an inner index, corresponding to a two-level organizational hierarchy. This is described in detail below.
VarFWord
The FWORD type is used to represent coordinates in the glyph design grid. The VarFWord record is used to represent a coordinate that can be variable.
Type | Name | Description |
---|---|---|
FWORD | coordinate | |
uint16 | varOuterIndex | |
uint16 | varInnerIndex |
VarUFWord
The UFWord type is used to represent distances in the glyph design grid. The VarUFWord record is used to represent a distance that can be variable.
Type | Name | Description |
---|---|---|
UFWORD | distance | |
uint16 | varOuterIndex | |
uint16 | varInnerIndex |
VarF2Dot14
The F2DOT14 type is typically used to represent values that are inherently limited to a range of [-1, 1], or a range of [0, 1]. The VarF2Dot14 record is used to represent such a value that can be variable.
Type | Name | Description |
---|---|---|
F2Dot14 | value | |
uint16 | varOuterIndex | |
uint16 | varInnerIndex |
In general, variation deltas are (logically) signed 16-bit integers, and in most cases, they are applied to signed 16-bit values (FWORDs) or unsigned 16-bit values (UFWORDs). When scaled deltas are applied to F2DOT14 values, the F2DOT14 value is treated like a 16-bit integer. (In this sense, the delta and the F2DOT14 value can be viewed as integer values in units of 1/16384ths.)
If the context in which the VarF2Dot14 is used contrains the valid range for the default value, then any variations by applying deltas are clipped to that range.
VarFixed
The Fixed type is intended for floating values, such as variation-space coordinates. The VarFixed record is used to represent such a value that can be variable.
Type | Name | Description |
---|---|---|
Fixed | value | |
uint16 | varOuterIndex | |
uint16 | varInnerIndex |
While in most cases deltas are applied to 16-bit types, Fixed is a 32-bit (16.16) type and requires 32-bit deltas. The DeltaSet record used in the ItemVariationData subtable format can accommodate deltas that are, logically, either 16-bit or 32-bit. See the description of the ItemVariationData subtable, below, for details.
When scaled deltas are applied to Fixed values, the Fixed value is treated like a 32-bit integer. (In this sense, the delta and the Fixed value can be viewed as integer values in units of 1/65536ths.)
Insert a sub-clause heading, "7.2.3.2 Variation data", after the newly-inserted text above, and before the paragraph beginning, "The ItemVariationStore table includes a variation region list..." Re-number subsequent sub-clauses accordingly.
In the fifth paragraph that follows the figure in (now) 7.2.3.2, delete the first sentence, "A complete delta set index... within that subtable." Before that pragraph, insert the following paragraph:
A complete delta-set index involves an outer-level index into the ItemVariationData subtable array, plus an inner-level index to a delta-set row within that subtable. A special meaning is assigned to a delta-set index 0xFFFF/0xFFFF (that is, outer-level and inner-level portions are both 0xFFFF): this is used to indicate that there is no variation data for a given item. Functionally, this would be equivalent to referencing delta-set data consisting of only deltas of 0 for all regions.
In 7.2.3.3 (previously, 7.2.3.1), "Variation regions", in the table for the VariationRegionList structure, add the following sentence to the end of the description for the regionCount field.
Shall be less than 32,736.
After the table for the VariationRegionList structure, add the following paragraph:
The high-order bit of the regionCount field is reserved for future use, and shall be cleared.
In 7.2.3.4 (previously 7.2.3.2), "Item variation store and item variation data tables", in the paragraph that follows the table for the ItemVariationStore structure, delete the first sentence, "The item variation store includes an array of offsets to item variation data subtables. Before that paragraph, insert the following paragraph and note:
The item variation store includes an offset to a variation region list and an array of offsets to item variation data subtables.
NOTE: Indices into the itemVariationDataOffsets array are stored in parent tables as delta-set “outer” indices with each such index having a corresponding “inner” index. If the outer index points to a NULL offset, then any inner index will be invalid. The itemVariationDataOffsets array should not include any NULL offsets.
In 7.2.3.4, in the table for the ItemVariationData subtable structure, replace the field name "shortDeltaCount" with "wordDeltaCount", and replace the description of that field with the following:"
A packed field: the high bit is a flag—see details below.
Following the table for the ItemVariationData subtable structure, replace the remainder of 7.2.3.4 (including the table for the DeltaSet record structure) with the following:
The wordDeltaCount field contains a packed value that includes a flag and a “word” delta count. The format of this value is as follows:
Mask | Name | Description |
---|---|---|
0x8000 | LONG_WORDS | Flag indicating that “word” deltas are long (int32) |
0x7FFF | WORD_DELTA_COUNT_MASK | Count of “word” deltas |
The representation of delta values uses a mix of long types (“words”) and short types. If the LONG_WORDS flag is set, deltas are represented using a mix of int32 and int16 values. This representation is only used for deltas that are to be applied to data items of Fixed or 32-bit integer types. If the flag is not set, deltas are presented using a mix of int16 and int8 values. See the description of the DeltaSet record below for additional details.
The count value indicated by WORD_DELTA_COUNT_MASK is a count of the number of deltas that use the long (“word”) representation, and shall be less than or equal to regionIndexCount.
The deltaSets array represents a logical two-dimensional table of delta values with itemCount rows and regionIndexCount columns. Rows in the table provide sets of deltas for particular target items, and columns correspond to regions of the variation space. Each DeltaSet record in the array represents one row of the delta-value table — one delta set.
DeltaSet record:
Type | Name | Description |
---|---|---|
int16 and int8 or int32 and int16 |
deltaData[regionIndexCount] | Variation delta values. |
Logically, each DeltaSet record has regionIndexCount number of elements. The elements are represented using long and short types, as described above. These are either int16 and int8, or int32 and int16, according to whether the LONG_WORDS flag was set. The delta array has a sequence of deltas using the long type followed by sequence of deltas using the short type. The count of deltas using the long type is derived using WORD_DELTA_COUNT_MASK. The remaining elements use the short type. The length of the data for each row, in bytes, is regionIndexCount + (wordDeltaCount && WORD_DELTA_COUNT_MASK) if the LONG_WORDS flag is not set, or 2 x that amount if the flag is set.
NOTE: Delta values are each represented directly. They are not packed as in the tuple variation store.
A.4 Changes to OFF Bibliography
Add two new entries as follows:
-
HTML Living Standard, 4.12.5, The canvas element. https://html.spec.whatwg.org/multipage/canvas.html#the-canvas-element
-
Compositing and Blending Level 1. W3C Candidate Recommendation, 13 January 2015. https://www.w3.org/TR/compositing-1/