-
-
Save drott/83bca80294eec7db308231f95655d1c0 to your computer and use it in GitHub Desktop.
Example of COLRv1 implementation based on FreeType
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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