Skip to content

Instantly share code, notes, and snippets.

@drott
Last active January 21, 2021 13:59
Show Gist options
  • Save drott/83bca80294eec7db308231f95655d1c0 to your computer and use it in GitHub Desktop.
Save drott/83bca80294eec7db308231f95655d1c0 to your computer and use it in GitHub Desktop.
Example of COLRv1 implementation based on FreeType
void colrv1_start_glyph(FT_Face ft_face) {
// [..Configure your graphics library's graphics context and prepare to paint and clip..]
FT_OpaquePaint opaque_paint;
opaque_paint.p = nullptr;
uint_16_t glyph_id = 42;
// Get initial paint object for glyph id 42:
if (!FT_Get_Color_Glyph_Paint(ft_face, glyph_id, &opaque_paint))
return false;
colrv1_traverse_paint(face, opaque_paint)
}
bool colrv1_traverse_paint(FT_Face face,
FT_OpaquePaint opaque_paint) {
FT_COLR_Paint paint;
if (!FT_Get_Paint(face, opaque_paint, &paint)) return false;
switch (paint.format) {
case COLR_PAINTFORMAT_COLR_LAYERS: {
FT_LayerIterator& layer_iterator = paint.u.colr_layers.layer_iterator;
FT_OpaquePaint opaque_paint_fetch;
opaque_paint_fetch.p = nullptr;
while (FT_Get_Paint_Layers(face, &layer_iterator, &opaque_paint_fetch)) {
colrv1_traverse_paint(canvas, scale, palette, face, path_maker, opaque_paint_fetch);
}
break;
}
case COLR_PAINTFORMAT_GLYPH:
// [...create a layer in your graphics library...]
// Traverse / draw operation will clip layer.
colrv1_draw_paint(canvas, scale, palette, face, path_maker, paint);
traverse_result = colrv1_traverse_paint(canvas, scale, palette, face, path_maker,
paint.u.glyph.paint);
// [...merge the layer to background in your graphics library...]
break;
case COLR_PAINTFORMAT_COLR_GLYPH:
traverse_result = colrv1_start_glyph(canvas, scale, palette, face, path_maker,
paint.u.colr_glyph.glyphID);
break;
case COLR_PAINTFORMAT_TRANSFORMED:
// [...create a layer in your graphics library...]
// Traverse / draw operation will apply transform.
colrv1_draw_paint(canvas, scale, palette, face, path_maker, paint);
traverse_result = colrv1_traverse_paint(canvas, scale, palette, face, path_maker,
paint.u.transformed.paint);
// [...merge the layer to background in your graphics library...]
break;
case COLR_PAINTFORMAT_ROTATE:
case COLR_PAINTFORMAT_SKEW:
case COLR_PAINTFORMAT_TRANSLATE:
[...similarly apply transformations to your canvas...]
case COLR_PAINTFORMAT_COMPOSITE: {
traverse_result = colrv1_traverse_paint(canvas, scale, palette, face, path_maker,
paint.u.composite.backdrop_paint);
// [...create a layer in your graphics library...]
// [...and set blend mode to paint.u.composite.composite_mode...]
colrv1_traverse_paint(canvas, scale, palette, face, path_maker,
paint.u.composite.source_paint);
// [...merge the layer to background in your graphics library...]
break;
}
case COLR_PAINTFORMAT_RADIAL_GRADIENT:
case COLR_PAINTFORMAT_LINEAR_GRADIENT:
case COLR_PAINTFORMAT_SOLID: {
colrv1_draw_paint(canvas, scale, palette, face, path_maker, paint);
break;
}
default:
break;
}
return traverse_result;
}
void colrv1_draw_paint(FT_Face face,
FT_COLR_Paint colrv1_paint) {
float upem = /* units per em from font */;
switch (colrv1_paint.format) {
case COLR_PAINTFORMAT_GLYPH: {
// [..clip the path for glyph shape colrv1_paint.u.glyph.glyphID on your graphics context..]
break;
}
case COLR_PAINTFORMAT_SOLID: {
// [..configure a solid color according to
// palette[colrv1_paint.u.solid.color.palette_index] and alpha values
// and fill with that..]
case COLR_PAINTFORMAT_LINEAR_GRADIENT: {
/* Retrieve color stops */
const FT_UInt num_color_stops =
colrv1_paint.u.linear_gradient.colorline.color_stop_iterator.num_color_stops;
Point line_positions[2];
line_positions[0].fX = colrv1_paint.u.linear_gradient.p0.x / upem * scale.x();
line_positions[0].fY = colrv1_paint.u.linear_gradient.p0.y / upem * (-scale.y());
line_positions[1].fX = colrv1_paint.u.linear_gradient.p1.x / upem * scale.x();
line_positions[1].fY = colrv1_paint.u.linear_gradient.p1.y / upem * (-scale.y());
/* populate points */
float stops[num_color_stops];
Color colors[num_color_stops];
FT_ColorStop color_stop;
while (FT_Get_Colorline_Stops(
face, &color_stop,
&colrv1_paint.u.linear_gradient.colorline.color_stop_iterator)) {
FT_UInt index = colrv1_paint.u.linear_gradient.colorline.color_stop_iterator
.current_color_stop -
1;
stops[index] = color_stop.stop_offset / float(1 << 14);
FT_UInt16& palette_index = color_stop.color.palette_index;
colors[index] = ColorSetARGB(
palette[palette_index].alpha * AlphaToFloat(color_stop.color.alpha),
palette[palette_index].red, palette[palette_index].green,
palette[palette_index].blue);
}
printf("Gradient linear: pos[0] (%f,%f) pos[1] (%f,%f), col[0] %u "
"col[1] %u, "
"stops[0] "
"%f stops[1] %f, num: %d\n",
line_positions[0].x(), line_positions[0].y(), line_positions[1].x(),
line_positions[1].y(), colors[0], colors[1], stops[0], stops[1],
num_color_stops);
// [..Configure a linear gradient based on line_positions, colors and stops..
// .. and fill the drawing region with that..]
break;
}
case COLR_PAINTFORMAT_RADIAL_GRADIENT: {
// [..Do the same analogously as in LINEAR_GRADIENT..]
}
case COLR_PAINTFORMAT_TRANSFORMED:
case COLR_PAINTFORMAT_ROTATE:
case COLR_PAINTFORMAT_SKEW:
// [...Apply the required transformations to your graphics context
// based on colrv1_paint.u.transformed, colrv1_paint.u.rotate, colrv1_paint.u.skew etc....]
break;
}
default:
break;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment