diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml index 2d2827a66..b685bfd3a 100644 --- a/docs/harfbuzz-docs.xml +++ b/docs/harfbuzz-docs.xml @@ -57,6 +57,7 @@ + diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index a35d3aa39..e4f89d264 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -191,8 +191,11 @@ HB_DEPRECATED_FOR
hb-draw hb_draw_funcs_create +hb_draw_funcs_get_empty hb_draw_funcs_reference hb_draw_funcs_destroy +hb_draw_funcs_set_user_data +hb_draw_funcs_get_user_data hb_draw_funcs_make_immutable hb_draw_funcs_is_immutable hb_draw_move_to_func_t @@ -215,6 +218,55 @@ hb_draw_funcs_t hb_draw_state_t
+
+hb-paint +hb_paint_funcs_t +hb_paint_funcs_create +hb_paint_funcs_get_empty +hb_paint_funcs_reference +hb_paint_funcs_destroy +hb_paint_funcs_set_user_data +hb_paint_funcs_get_user_data +hb_paint_funcs_make_immutable +hb_paint_funcs_is_immutable + +hb_paint_push_transform_func_t +hb_paint_funcs_set_push_transform_func +hb_paint_pop_transform_func_t +hb_paint_funcs_set_pop_transform_func +hb_paint_push_clip_glyph_func_t +hb_paint_funcs_set_push_clip_glyph_func +hb_paint_push_clip_rectangle_func_t +hb_paint_funcs_set_push_clip_rectangle_func +hb_paint_pop_clip_func_t +hb_paint_funcs_set_pop_clip_func +hb_paint_color_func_t +hb_paint_funcs_set_color_func +HB_PAINT_IMAGE_FORMAT_PNG +HB_PAINT_IMAGE_FORMAT_SVG +HB_PAINT_IMAGE_FORMAT_BGRA +hb_paint_image_func_t +hb_paint_funcs_set_image_func +hb_color_line_t +hb_color_stop_t +hb_color_line_get_color_stops_func_t +hb_color_line_get_color_stops +hb_paint_extend_t +hb_color_line_get_extend_func_t +hb_color_line_get_extend +hb_paint_linear_gradient_func_t +hb_paint_funcs_set_linear_gradient_func +hb_paint_radial_gradient_func_t +hb_paint_funcs_set_radial_gradient_func +hb_paint_sweep_gradient_func_t +hb_paint_funcs_set_sweep_gradient_func +hb_paint_composite_mode_t +hb_paint_push_group_func_t +hb_paint_funcs_set_push_group_func +hb_paint_pop_group_func_t +hb_paint_funcs_set_pop_group_func +
+
hb-deprecated HB_BUFFER_FLAGS_DEFAULT @@ -324,6 +376,8 @@ hb_font_get_glyph_v_origin hb_font_get_glyph_origin_for_direction hb_font_get_glyph_name hb_font_get_glyph_shape +hb_font_draw_glyph +hb_font_paint_glyph hb_font_get_nominal_glyph hb_font_get_nominal_glyphs hb_font_get_variation_glyph @@ -386,6 +440,10 @@ hb_font_get_glyph_name_func_t hb_font_funcs_set_glyph_name_func hb_font_get_glyph_shape_func_t hb_font_funcs_set_glyph_shape_func +hb_font_draw_glyph_func_t +hb_font_funcs_set_draw_glyph_func +hb_font_paint_glyph_func_t +hb_font_funcs_set_paint_glyph_func hb_font_get_nominal_glyph_func_t hb_font_funcs_set_nominal_glyph_func hb_font_get_nominal_glyphs_func_t @@ -485,6 +543,7 @@ hb_ot_color_glyph_get_layers hb_ot_color_glyph_reference_png hb_ot_color_glyph_reference_svg hb_ot_color_has_layers +hb_ot_color_has_paint hb_ot_color_has_palettes hb_ot_color_has_png hb_ot_color_has_svg diff --git a/perf/benchmark-font.cc b/perf/benchmark-font.cc index 98b6310d2..4688d6923 100644 --- a/perf/benchmark-font.cc +++ b/perf/benchmark-font.cc @@ -163,7 +163,7 @@ static void BM_Font (benchmark::State &state, hb_draw_funcs_t *draw_funcs = _draw_funcs_create (); for (auto _ : state) for (unsigned gid = 0; gid < num_glyphs; ++gid) - hb_font_get_glyph_shape (font, gid, draw_funcs, nullptr); + hb_font_draw_glyph (font, gid, draw_funcs, nullptr); break; hb_draw_funcs_destroy (draw_funcs); } diff --git a/src/Makefile.sources b/src/Makefile.sources index 6c4fc5de7..d49e6a716 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -69,6 +69,7 @@ HB_BASE_sources = \ hb-ot-cmap-table.hh \ hb-ot-color-cbdt-table.hh \ hb-ot-color-colr-table.hh \ + hb-ot-color-colr-table.cc \ hb-ot-color-cpal-table.hh \ hb-ot-color-sbix-table.hh \ hb-ot-color-svg-table.hh \ @@ -88,6 +89,9 @@ HB_BASE_sources = \ hb-ot-layout-common.hh \ hb-ot-layout-gdef-table.hh \ hb-ot-layout-gpos-table.hh \ + hb-paint.cc \ + hb-paint.hh \ + hb-paint-extents.hh \ hb-ot-layout-gsub-table.hh \ OT/glyf/glyf.hh \ OT/glyf/glyf-helpers.hh \ @@ -290,6 +294,7 @@ HB_BASE_headers = \ hb-ot-shape.h \ hb-ot-var.h \ hb-ot.h \ + hb-paint.h \ hb-set.h \ hb-shape-plan.h \ hb-shape.h \ @@ -301,7 +306,7 @@ HB_BASE_headers = \ # Optional Sources and Headers with external deps -HB_FT_sources = hb-ft.cc +HB_FT_sources = hb-ft.cc hb-ft-colr.hh HB_FT_headers = hb-ft.h HB_GLIB_sources = hb-glib.cc diff --git a/src/OT/glyf/glyf.hh b/src/OT/glyf/glyf.hh index e0c92f0c9..58af9ead5 100644 --- a/src/OT/glyf/glyf.hh +++ b/src/OT/glyf/glyf.hh @@ -7,6 +7,7 @@ #include "../../hb-ot-hmtx-table.hh" #include "../../hb-ot-var-gvar-table.hh" #include "../../hb-draw.hh" +#include "../../hb-paint.hh" #include "glyf-helpers.hh" #include "Glyph.hh" @@ -332,6 +333,15 @@ struct glyf_accelerator_t return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents); } + bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const + { + funcs->push_clip_glyph (data, gid, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; + } + const glyf_impl::Glyph glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const { diff --git a/src/harfbuzz-subset.cc b/src/harfbuzz-subset.cc index a43485e6f..c5c66747a 100644 --- a/src/harfbuzz-subset.cc +++ b/src/harfbuzz-subset.cc @@ -14,6 +14,7 @@ #include "hb-number.cc" #include "hb-ot-cff1-table.cc" #include "hb-ot-cff2-table.cc" +#include "hb-ot-color-colr-table.cc" #include "hb-ot-color.cc" #include "hb-ot-face.cc" #include "hb-ot-font.cc" @@ -40,6 +41,7 @@ #include "hb-ot-shaper-vowel-constraints.cc" #include "hb-ot-tag.cc" #include "hb-ot-var.cc" +#include "hb-paint.cc" #include "hb-set.cc" #include "hb-shape-plan.cc" #include "hb-shape.cc" diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc index fe4e21db0..03196eaa6 100644 --- a/src/harfbuzz.cc +++ b/src/harfbuzz.cc @@ -19,6 +19,7 @@ #include "hb-number.cc" #include "hb-ot-cff1-table.cc" #include "hb-ot-cff2-table.cc" +#include "hb-ot-color-colr-table.cc" #include "hb-ot-color.cc" #include "hb-ot-face.cc" #include "hb-ot-font.cc" @@ -45,6 +46,7 @@ #include "hb-ot-shaper-vowel-constraints.cc" #include "hb-ot-tag.cc" #include "hb-ot-var.cc" +#include "hb-paint.cc" #include "hb-set.cc" #include "hb-shape-plan.cc" #include "hb-shape.cc" diff --git a/src/hb-common.h b/src/hb-common.h index e92feb989..a5da4e76a 100644 --- a/src/hb-common.h +++ b/src/hb-common.h @@ -897,6 +897,32 @@ HB_EXTERN uint8_t hb_color_get_blue (hb_color_t color); #define hb_color_get_blue(color) (((color) >> 24) & 0xFF) +/** + * hb_glyph_extents_t: + * @x_bearing: Distance from the x-origin to the left extremum of the glyph. + * @y_bearing: Distance from the top extremum of the glyph to the y-origin. + * @width: Distance from the left extremum of the glyph to the right extremum. + * @height: Distance from the top extremum of the glyph to the bottom extremum. + * + * Glyph extent values, measured in font units. + * + * Note that @height is negative, in coordinate systems that grow up. + **/ +typedef struct hb_glyph_extents_t { + hb_position_t x_bearing; + hb_position_t y_bearing; + hb_position_t width; + hb_position_t height; +} hb_glyph_extents_t; + +/** + * hb_font_t: + * + * Data type for holding fonts. + * + */ +typedef struct hb_font_t hb_font_t; + HB_END_DECLS #endif /* HB_COMMON_H */ diff --git a/src/hb-config.hh b/src/hb-config.hh index 98b1e9d0c..22ae1613d 100644 --- a/src/hb-config.hh +++ b/src/hb-config.hh @@ -64,6 +64,7 @@ #define HB_NO_CFF #define HB_NO_COLOR #define HB_NO_DRAW +#define HB_NO_PAINT #define HB_NO_ERRNO #define HB_NO_FACE_COLLECT_UNICODES #define HB_NO_GETENV diff --git a/src/hb-cplusplus.hh b/src/hb-cplusplus.hh index a210ab796..531ef1b7c 100644 --- a/src/hb-cplusplus.hh +++ b/src/hb-cplusplus.hh @@ -160,6 +160,8 @@ HB_DEFINE_VTABLE (map); HB_DEFINE_VTABLE (set); HB_DEFINE_VTABLE (shape_plan); HB_DEFINE_VTABLE (unicode_funcs); +HB_DEFINE_VTABLE (draw_funcs); +HB_DEFINE_VTABLE (paint_funcs); #undef HB_DEFINE_VTABLE diff --git a/src/hb-draw.cc b/src/hb-draw.cc index 9d75c6537..9877dd41b 100644 --- a/src/hb-draw.cc +++ b/src/hb-draw.cc @@ -35,6 +35,8 @@ * @include: hb.h * * Functions for drawing (extracting) glyph shapes. + * + * The #hb_draw_funcs_t struct can be used with hb_font_draw_glyph(). **/ static void @@ -198,6 +200,20 @@ DEFINE_NULL_INSTANCE (hb_draw_funcs_t) = } }; +/** + * hb_draw_funcs_get_empty: + * + * Fetches the singleton empty draw-functions structure. + * + * Return value: (transfer full): The empty draw-functions structure + * + * Since: REPLACEME + **/ +hb_draw_funcs_t * +hb_draw_funcs_get_empty () +{ + return const_cast (&Null (hb_draw_funcs_t)); +} /** * hb_draw_funcs_reference: (skip) @@ -248,6 +264,49 @@ hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs) hb_free (dfuncs); } +/** + * hb_draw_funcs_set_user_data: (skip) + * @dfuncs: The draw-functions structure + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified draw-functions structure. + * + * Return value: `true` if success, `false` otherwise + * + * Since: REPLACEME + **/ +hb_bool_t +hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (dfuncs, key, data, destroy, replace); +} + +/** + * hb_draw_funcs_get_user_data: (skip) + * @dfuncs: The draw-functions structure + * @key: The user-data key to query + * + * Fetches the user-data associated with the specified key, + * attached to the specified draw-functions structure. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: REPLACEME + **/ +void * +hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (dfuncs, key); +} + /** * hb_draw_funcs_make_immutable: * @dfuncs: draw functions diff --git a/src/hb-draw.h b/src/hb-draw.h index 9bcdd9947..9ca0b4006 100644 --- a/src/hb-draw.h +++ b/src/hb-draw.h @@ -92,7 +92,7 @@ typedef struct hb_draw_funcs_t hb_draw_funcs_t; /** * hb_draw_move_to_func_t: * @dfuncs: draw functions object - * @draw_data: The data accompanying the draw functions in hb_font_get_glyph_shape() + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() * @st: current draw state * @to_x: X component of target point * @to_y: Y component of target point @@ -112,7 +112,7 @@ typedef void (*hb_draw_move_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data /** * hb_draw_line_to_func_t: * @dfuncs: draw functions object - * @draw_data: The data accompanying the draw functions in hb_font_get_glyph_shape() + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() * @st: current draw state * @to_x: X component of target point * @to_y: Y component of target point @@ -132,7 +132,7 @@ typedef void (*hb_draw_line_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data /** * hb_draw_quadratic_to_func_t: * @dfuncs: draw functions object - * @draw_data: The data accompanying the draw functions in hb_font_get_glyph_shape() + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() * @st: current draw state * @control_x: X component of control point * @control_y: Y component of control point @@ -155,7 +155,7 @@ typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw /** * hb_draw_cubic_to_func_t: * @dfuncs: draw functions object - * @draw_data: The data accompanying the draw functions in hb_font_get_glyph_shape() + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() * @st: current draw state * @control1_x: X component of first control point * @control1_y: Y component of first control point @@ -181,7 +181,7 @@ typedef void (*hb_draw_cubic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_dat /** * hb_draw_close_path_func_t: * @dfuncs: draw functions object - * @draw_data: The data accompanying the draw functions in hb_font_get_glyph_shape() + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() * @st: current draw state * @user_data: User data pointer passed to hb_draw_funcs_set_close_path_func() * @@ -279,12 +279,27 @@ hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *dfuncs, HB_EXTERN hb_draw_funcs_t * hb_draw_funcs_create (void); +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_get_empty (void); + HB_EXTERN hb_draw_funcs_t * hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs); HB_EXTERN void hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs); +HB_EXTERN hb_bool_t +hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key); + HB_EXTERN void hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs); diff --git a/src/hb-font.cc b/src/hb-font.cc index 0ce3e2608..28d3a4b2c 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -30,6 +30,7 @@ #include "hb-font.hh" #include "hb-draw.hh" +#include "hb-paint.hh" #include "hb-machinery.hh" #include "hb-ot.h" @@ -503,23 +504,34 @@ hb_font_get_glyph_from_name_default (hb_font_t *font, } static void -hb_font_get_glyph_shape_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, - void *draw_data, - void *user_data HB_UNUSED) +hb_font_draw_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) { } +static void +hb_font_paint_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_paint_funcs_t *paint_funcs HB_UNUSED, + void *paint_data HB_UNUSED, + unsigned int palette HB_UNUSED, + hb_color_t foreground HB_UNUSED, + void *user_data HB_UNUSED) +{ +} -typedef struct hb_font_get_glyph_shape_default_adaptor_t { +typedef struct hb_font_draw_glyph_default_adaptor_t { hb_draw_funcs_t *draw_funcs; void *draw_data; float x_scale; float y_scale; float slant; -} hb_font_get_glyph_shape_default_adaptor_t; +} hb_font_draw_glyph_default_adaptor_t; static void hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, @@ -528,7 +540,7 @@ hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; float slant = adaptor->slant; @@ -543,7 +555,7 @@ hb_draw_line_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; float slant = adaptor->slant; @@ -562,7 +574,7 @@ hb_draw_quadratic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data float to_x, float to_y, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; float slant = adaptor->slant; @@ -583,7 +595,7 @@ hb_draw_cubic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; float slant = adaptor->slant; @@ -602,7 +614,7 @@ hb_draw_close_path_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, hb_draw_state_t *st, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; adaptor->draw_funcs->emit_close_path (adaptor->draw_data, *st); } @@ -618,14 +630,14 @@ static const hb_draw_funcs_t _hb_draw_funcs_default = { }; static void -hb_font_get_glyph_shape_default (hb_font_t *font, +hb_font_draw_glyph_default (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t glyph, hb_draw_funcs_t *draw_funcs, void *draw_data, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t adaptor = { + hb_font_draw_glyph_default_adaptor_t adaptor = { draw_funcs, draw_data, font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, @@ -634,11 +646,34 @@ hb_font_get_glyph_shape_default (hb_font_t *font, (float) font->x_scale / (float) font->parent->y_scale : 0.f }; - font->parent->get_glyph_shape (glyph, + font->parent->draw_glyph (glyph, const_cast (&_hb_draw_funcs_default), &adaptor); } +static void +hb_font_paint_glyph_default (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, + void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) +{ + paint_funcs->push_transform (paint_data, + font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, + font->parent->y_scale ? (font->slant - font->parent->slant) * + (float) font->x_scale / (float) font->parent->y_scale : 0.f, + 0.f, + font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, + 0.f, 0.f); + + font->parent->paint_glyph (glyph, paint_funcs, paint_data, palette, foreground); + + paint_funcs->pop_transform (paint_data); +} + DEFINE_NULL_INSTANCE (hb_font_funcs_t) = { HB_OBJECT_HEADER_STATIC, @@ -647,7 +682,7 @@ DEFINE_NULL_INSTANCE (hb_font_funcs_t) = nullptr, { { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil, +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_nil, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } @@ -661,7 +696,7 @@ static const hb_font_funcs_t _hb_font_funcs_default = { nullptr, { { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_default, +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_default, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } @@ -739,7 +774,7 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) if (ffuncs->destroy) { -#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy->name) \ +#define HB_FONT_FUNC_IMPLEMENT(get_,name) if (ffuncs->destroy->name) \ ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT @@ -879,11 +914,11 @@ fail: return false; } -#define HB_FONT_FUNC_IMPLEMENT(name) \ +#define HB_FONT_FUNC_IMPLEMENT(get_,name) \ \ void \ hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ - hb_font_get_##name##_func_t func, \ + hb_font_##get_##name##_func_t func, \ void *user_data, \ hb_destroy_func_t destroy) \ { \ @@ -899,7 +934,7 @@ hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ if (func) \ ffuncs->get.f.name = func; \ else \ - ffuncs->get.f.name = hb_font_get_##name##_default; \ + ffuncs->get.f.name = hb_font_##get_##name##_default; \ \ if (ffuncs->user_data) \ ffuncs->user_data->name = user_data; \ @@ -1357,13 +1392,66 @@ hb_font_get_glyph_from_name (hb_font_t *font, * objects, with @draw_data passed to them. * * Since: 4.0.0 - **/ + */ void hb_font_get_glyph_shape (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + hb_font_draw_glyph (font, glyph, dfuncs, draw_data); +} + +/** + * hb_font_draw_glyph: + * @font: #hb_font_t to work upon + * @glyph: : The glyph ID + * @dfuncs: #hb_draw_funcs_t to draw to + * @draw_data: User data to pass to draw callbacks + * + * Draws the outline that corresponds to a glyph in the specified @font. + * + * The outline is returned by way of calls to the callbacks of the @dfuncs + * objects, with @draw_data passed to them. + * + * Since: REPLACEME + **/ +void +hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_draw_funcs_t *dfuncs, void *draw_data) { - font->get_glyph_shape (glyph, dfuncs, draw_data); + font->draw_glyph (glyph, dfuncs, draw_data); +} + +/** + * hb_font_paint_glyph: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @pfuncs: #hb_paint_funcs_t to paint with + * @paint_data: User data to pass to paint callbacks + * @palette: The index of the font's color palette to use + * @foreground: The foreground color, unpremultipled + * + * Paints the glyph. + * + * The painting instructions are returned by way of calls to + * the callbacks of the @funcs object, with @paint_data passed + * to them. + * + * If the font has color palettes (see hb_ot_color_has_palettes()), + * then @palette selects the palette to use. If the font doesn't + * have palettes, passing 0 is fine. + * + * Since: REPLACEME + */ +void +hb_font_paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette, + hb_color_t foreground) +{ + font->paint_glyph (glyph, pfuncs, paint_data, palette, foreground); } /* A bit higher-level, and with fallback */ @@ -2328,9 +2416,8 @@ hb_font_get_ptem (hb_font_t *font) * HarfBuzz needs to know this value to adjust shaping results, * metrics, and style values to match the slanted rendering. * - * Note: The glyph shape fetched via the - * hb_font_get_glyph_shape() is slanted to reflect this value - * as well. + * Note: The glyph shape fetched via the hb_font_draw_glyph() + * function is slanted to reflect this value as well. * * Note: The slant value is a ratio. For example, a * 20% slant would be represented as a 0.2 value. @@ -2754,3 +2841,13 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, trampoline_destroy); } #endif + + +void +hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_shape_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy); +} diff --git a/src/hb-font.h b/src/hb-font.h index e2c3df4a5..a5859c962 100644 --- a/src/hb-font.h +++ b/src/hb-font.h @@ -34,18 +34,10 @@ #include "hb-common.h" #include "hb-face.h" #include "hb-draw.h" +#include "hb-paint.h" HB_BEGIN_DECLS -/** - * hb_font_t: - * - * Data type for holding fonts. - * - */ -typedef struct hb_font_t hb_font_t; - - /* * hb_font_funcs_t */ @@ -97,7 +89,7 @@ HB_EXTERN hb_bool_t hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); -/* font and glyph extents */ +/* font extents */ /** * hb_font_extents_t: @@ -126,24 +118,6 @@ typedef struct hb_font_extents_t { hb_position_t reserved1; } hb_font_extents_t; -/** - * hb_glyph_extents_t: - * @x_bearing: Distance from the x-origin to the left extremum of the glyph. - * @y_bearing: Distance from the top extremum of the glyph to the y-origin. - * @width: Distance from the left extremum of the glyph to the right extremum. - * @height: Distance from the top extremum of the glyph to the bottom extremum. - * - * Glyph extent values, measured in font units. - * - * Note that @height is negative, in coordinate systems that grow up. - **/ -typedef struct hb_glyph_extents_t { - hb_position_t x_bearing; - hb_position_t y_bearing; - hb_position_t width; - hb_position_t height; -} hb_glyph_extents_t; - /* func types */ /** @@ -524,12 +498,53 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void * * * Since: 4.0.0 * + * Deprecated: REPLACEME: Use #hb_font_draw_glyph_func_t instead **/ typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_draw_funcs_t *draw_funcs, void *draw_data, void *user_data); +/** + * hb_font_draw_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @draw_funcs: The draw functions to send the shape data to + * @draw_data: The data accompanying the draw functions + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: REPLACEME + * + **/ +typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); + +/** + * hb_font_paint_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @paint_funcs: The paint functions to use + * @paint_data: The data accompanying the paint functions + * @palette: The color palette to use + * @foreground: The foreground color + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: REPLACEME + */ +typedef void (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data); /* func setters */ @@ -796,15 +811,51 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, * @user_data: Data to pass to @func * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * Sets the implementation function for #hb_font_get_glyph_shape_func_t. + * Sets the implementation function for #hb_font_get_glyph_shape_func_t, + * which is the same as #hb_font_draw_glyph_func_t. * * Since: 4.0.0 + * + * Deprecated: REPLACEME: Use hb_font_set_draw_glyph_func() instead **/ HB_EXTERN void hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_shape_func_t func, void *user_data, hb_destroy_func_t destroy); +/** + * hb_font_funcs_set_draw_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_draw_glyph_func_t, + * which is the same as #hb_font_get_glyph_shape_func_t. + * + * Since: REPLACEME + **/ +HB_EXTERN void +hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_paint_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is no longer needed + * + * Sets the implementation function for #hb_font_paint_glyph_func_t. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_font_funcs_set_paint_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_paint_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + /* func dispatch */ HB_EXTERN hb_bool_t @@ -890,6 +941,17 @@ hb_font_get_glyph_shape (hb_font_t *font, hb_codepoint_t glyph, hb_draw_funcs_t *dfuncs, void *draw_data); +HB_EXTERN void +hb_font_draw_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); + +HB_EXTERN void +hb_font_paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette, + hb_color_t foreground); /* high-level funcs, with fallback */ diff --git a/src/hb-font.hh b/src/hb-font.hh index a8b3ee5c4..83f0d8701 100644 --- a/src/hb-font.hh +++ b/src/hb-font.hh @@ -40,24 +40,25 @@ */ #define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \ - HB_FONT_FUNC_IMPLEMENT (font_h_extents) \ - HB_FONT_FUNC_IMPLEMENT (font_v_extents) \ - HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \ - HB_FONT_FUNC_IMPLEMENT (nominal_glyphs) \ - HB_FONT_FUNC_IMPLEMENT (variation_glyph) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_advances) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_advances) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \ - HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning)) \ - HB_FONT_FUNC_IMPLEMENT (glyph_extents) \ - HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \ - HB_FONT_FUNC_IMPLEMENT (glyph_name) \ - HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \ - HB_FONT_FUNC_IMPLEMENT (glyph_shape) \ + HB_FONT_FUNC_IMPLEMENT (get_,font_h_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,font_v_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,nominal_glyph) \ + HB_FONT_FUNC_IMPLEMENT (get_,nominal_glyphs) \ + HB_FONT_FUNC_IMPLEMENT (get_,variation_glyph) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_advance) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advance) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_advances) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advances) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origin) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origin) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_kerning) \ + HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_kerning)) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_contour_point) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_name) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_from_name) \ + HB_FONT_FUNC_IMPLEMENT (,draw_glyph) \ + HB_FONT_FUNC_IMPLEMENT (,paint_glyph) \ /* ^--- Add new callbacks here */ struct hb_font_funcs_t @@ -65,13 +66,13 @@ struct hb_font_funcs_t hb_object_header_t header; struct { -#define HB_FONT_FUNC_IMPLEMENT(name) void *name; +#define HB_FONT_FUNC_IMPLEMENT(get_,name) void *name; HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } *user_data; struct { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name; +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_destroy_func_t name; HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } *destroy; @@ -79,12 +80,12 @@ struct hb_font_funcs_t /* Don't access these directly. Call font->get_*() instead. */ union get_t { struct get_funcs_t { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name; +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_func_t name; HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } f; void (*array[0 -#define HB_FONT_FUNC_IMPLEMENT(name) +1 +#define HB_FONT_FUNC_IMPLEMENT(get_,name) +1 HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT ]) (); @@ -198,7 +199,7 @@ struct hb_font_t HB_INTERNAL bool has_func_set (unsigned int i); /* has_* ... */ -#define HB_FONT_FUNC_IMPLEMENT(name) \ +#define HB_FONT_FUNC_IMPLEMENT(get_,name) \ bool \ has_##name##_func () \ { \ @@ -392,15 +393,26 @@ struct hb_font_t !klass->user_data ? nullptr : klass->user_data->glyph_from_name); } - void get_glyph_shape (hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data) + void draw_glyph (hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data) { - klass->get.f.glyph_shape (this, user_data, - glyph, - draw_funcs, draw_data, - !klass->user_data ? nullptr : klass->user_data->glyph_shape); + klass->get.f.draw_glyph (this, user_data, + glyph, + draw_funcs, draw_data, + !klass->user_data ? nullptr : klass->user_data->draw_glyph); } + void paint_glyph (hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground) + { + klass->get.f.paint_glyph (this, user_data, + glyph, + paint_funcs, paint_data, + palette, foreground, + !klass->user_data ? nullptr : klass->user_data->paint_glyph); + } /* A bit higher-level, and with fallback */ diff --git a/src/hb-ft-colr.hh b/src/hb-ft-colr.hh new file mode 100644 index 000000000..3fb40e109 --- /dev/null +++ b/src/hb-ft-colr.hh @@ -0,0 +1,547 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_FT_COLR_HH +#define HB_FT_COLR_HH + +#include "hb.hh" + +#include "hb-paint-extents.hh" + +#include FT_COLOR_H + +#ifndef HB_NO_PAINT + +#ifdef TT_SUPPORT_COLRV1 + +static hb_paint_composite_mode_t +_hb_ft_paint_composite_mode (FT_Composite_Mode mode) +{ + switch (mode) + { + case FT_COLR_COMPOSITE_CLEAR: return HB_PAINT_COMPOSITE_MODE_CLEAR; + case FT_COLR_COMPOSITE_SRC: return HB_PAINT_COMPOSITE_MODE_SRC; + case FT_COLR_COMPOSITE_DEST: return HB_PAINT_COMPOSITE_MODE_DEST; + case FT_COLR_COMPOSITE_SRC_OVER: return HB_PAINT_COMPOSITE_MODE_SRC_OVER; + case FT_COLR_COMPOSITE_DEST_OVER: return HB_PAINT_COMPOSITE_MODE_DEST_OVER; + case FT_COLR_COMPOSITE_SRC_IN: return HB_PAINT_COMPOSITE_MODE_SRC_IN; + case FT_COLR_COMPOSITE_DEST_IN: return HB_PAINT_COMPOSITE_MODE_DEST_IN; + case FT_COLR_COMPOSITE_SRC_OUT: return HB_PAINT_COMPOSITE_MODE_SRC_OUT; + case FT_COLR_COMPOSITE_DEST_OUT: return HB_PAINT_COMPOSITE_MODE_DEST_OUT; + case FT_COLR_COMPOSITE_SRC_ATOP: return HB_PAINT_COMPOSITE_MODE_SRC_ATOP; + case FT_COLR_COMPOSITE_DEST_ATOP: return HB_PAINT_COMPOSITE_MODE_DEST_ATOP; + case FT_COLR_COMPOSITE_XOR: return HB_PAINT_COMPOSITE_MODE_XOR; + case FT_COLR_COMPOSITE_PLUS: return HB_PAINT_COMPOSITE_MODE_PLUS; + case FT_COLR_COMPOSITE_SCREEN: return HB_PAINT_COMPOSITE_MODE_SCREEN; + case FT_COLR_COMPOSITE_OVERLAY: return HB_PAINT_COMPOSITE_MODE_OVERLAY; + case FT_COLR_COMPOSITE_DARKEN: return HB_PAINT_COMPOSITE_MODE_DARKEN; + case FT_COLR_COMPOSITE_LIGHTEN: return HB_PAINT_COMPOSITE_MODE_LIGHTEN; + case FT_COLR_COMPOSITE_COLOR_DODGE: return HB_PAINT_COMPOSITE_MODE_COLOR_DODGE; + case FT_COLR_COMPOSITE_COLOR_BURN: return HB_PAINT_COMPOSITE_MODE_COLOR_BURN; + case FT_COLR_COMPOSITE_HARD_LIGHT: return HB_PAINT_COMPOSITE_MODE_HARD_LIGHT; + case FT_COLR_COMPOSITE_SOFT_LIGHT: return HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT; + case FT_COLR_COMPOSITE_DIFFERENCE: return HB_PAINT_COMPOSITE_MODE_DIFFERENCE; + case FT_COLR_COMPOSITE_EXCLUSION: return HB_PAINT_COMPOSITE_MODE_EXCLUSION; + case FT_COLR_COMPOSITE_MULTIPLY: return HB_PAINT_COMPOSITE_MODE_MULTIPLY; + case FT_COLR_COMPOSITE_HSL_HUE: return HB_PAINT_COMPOSITE_MODE_HSL_HUE; + case FT_COLR_COMPOSITE_HSL_SATURATION: return HB_PAINT_COMPOSITE_MODE_HSL_SATURATION; + case FT_COLR_COMPOSITE_HSL_COLOR: return HB_PAINT_COMPOSITE_MODE_HSL_COLOR; + case FT_COLR_COMPOSITE_HSL_LUMINOSITY: return HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY; + + case FT_COLR_COMPOSITE_MAX: HB_FALLTHROUGH; + default: return HB_PAINT_COMPOSITE_MODE_CLEAR; + } +} + +typedef struct hb_ft_paint_context_t hb_ft_paint_context_t; + +static void +_hb_ft_paint (hb_ft_paint_context_t *c, + FT_OpaquePaint opaque_paint); + +struct hb_ft_paint_context_t +{ + hb_ft_paint_context_t (const hb_ft_font_t *ft_font, + hb_font_t *font, + hb_paint_funcs_t *paint_funcs, void *paint_data, + FT_Color *palette, + hb_color_t foreground) : + ft_font (ft_font), font(font), + funcs (paint_funcs), data (paint_data), + palette (palette), foreground (foreground) {} + + void recurse (FT_OpaquePaint paint) + { + if (depth_left <= 0) return; + depth_left--; + _hb_ft_paint (this, paint); + depth_left++; + } + + const hb_ft_font_t *ft_font; + hb_font_t *font; + hb_paint_funcs_t *funcs; + void *data; + FT_Color *palette; + hb_color_t foreground; + int depth_left = 128; +}; + +static unsigned +_hb_ft_color_line_get_color_stops (hb_color_line_t *color_line, + void *color_line_data, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + void *user_data) +{ + FT_ColorLine *cl = (FT_ColorLine *) color_line_data; + hb_ft_paint_context_t *c = (hb_ft_paint_context_t *) user_data; + + if (count) + { + FT_ColorStop stop; + unsigned wrote = 0; + FT_ColorStopIterator iter = cl->color_stop_iterator; + + if (start >= cl->color_stop_iterator.num_color_stops) + { + *count = 0; + return cl->color_stop_iterator.num_color_stops; + } + + while (cl->color_stop_iterator.current_color_stop < start) + FT_Get_Colorline_Stops(c->ft_font->ft_face, + &stop, + &cl->color_stop_iterator); + + while (count && *count && + FT_Get_Colorline_Stops(c->ft_font->ft_face, + &stop, + &cl->color_stop_iterator)) + { + color_stops->offset = stop.stop_offset / 16384.f; + color_stops->is_foreground = stop.color.palette_index == 0xFFFF; + if (color_stops->is_foreground) + color_stops->color = HB_COLOR (hb_color_get_blue (c->foreground), + hb_color_get_green (c->foreground), + hb_color_get_red (c->foreground), + (hb_color_get_alpha (c->foreground) * stop.color.alpha) >> 14); + else + { + FT_Color ft_color = c->palette[stop.color.palette_index]; + color_stops->color = HB_COLOR (ft_color.blue, + ft_color.green, + ft_color.red, + (ft_color.alpha * stop.color.alpha) >> 14); + } + + color_stops++; + wrote++; + } + + *count = wrote; + + // reset the iterator for next time + cl->color_stop_iterator = iter; + } + + return cl->color_stop_iterator.num_color_stops; +} + +static hb_paint_extend_t +_hb_ft_color_line_get_extend (hb_color_line_t *color_line, + void *color_line_data, + void *user_data) +{ + FT_ColorLine *c = (FT_ColorLine *) color_line_data; + switch (c->extend) + { + default: + case FT_COLR_PAINT_EXTEND_PAD: return HB_PAINT_EXTEND_PAD; + case FT_COLR_PAINT_EXTEND_REPEAT: return HB_PAINT_EXTEND_REPEAT; + case FT_COLR_PAINT_EXTEND_REFLECT: return HB_PAINT_EXTEND_REFLECT; + } +} + +void +_hb_ft_paint (hb_ft_paint_context_t *c, + FT_OpaquePaint opaque_paint) +{ + FT_Face ft_face = c->ft_font->ft_face; + FT_COLR_Paint paint; + if (!FT_Get_Paint (ft_face, opaque_paint, &paint)) + return; + + switch (paint.format) + { + case FT_COLR_PAINTFORMAT_COLR_LAYERS: + { + FT_OpaquePaint other_paint = {0}; + while (FT_Get_Paint_Layers (ft_face, + &paint.u.colr_layers.layer_iterator, + &other_paint)) + { + c->funcs->push_group (c->data); + c->recurse (other_paint); + c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); + } + } + break; + case FT_COLR_PAINTFORMAT_SOLID: + { + bool is_foreground = paint.u.solid.color.palette_index == 0xFFFF; + hb_color_t color; + if (is_foreground) + color = HB_COLOR (hb_color_get_blue (c->foreground), + hb_color_get_green (c->foreground), + hb_color_get_red (c->foreground), + (hb_color_get_alpha (c->foreground) * paint.u.solid.color.alpha) >> 14); + else + { + FT_Color ft_color = c->palette[paint.u.solid.color.palette_index]; + color = HB_COLOR (ft_color.blue, + ft_color.green, + ft_color.red, + (ft_color.alpha * paint.u.solid.color.alpha) >> 14); + } + c->funcs->color (c->data, is_foreground, color); + } + break; + case FT_COLR_PAINTFORMAT_LINEAR_GRADIENT: + { + hb_color_line_t cl = { + &paint.u.linear_gradient.colorline, + _hb_ft_color_line_get_color_stops, c, + _hb_ft_color_line_get_extend, nullptr + }; + + c->funcs->linear_gradient (c->data, &cl, + paint.u.linear_gradient.p0.x / 65536.f, + paint.u.linear_gradient.p0.y / 65536.f, + paint.u.linear_gradient.p1.x / 65536.f, + paint.u.linear_gradient.p1.y / 65536.f, + paint.u.linear_gradient.p2.x / 65536.f, + paint.u.linear_gradient.p2.y / 65536.f); + } + break; + case FT_COLR_PAINTFORMAT_RADIAL_GRADIENT: + { + hb_color_line_t cl = { + &paint.u.linear_gradient.colorline, + _hb_ft_color_line_get_color_stops, c, + _hb_ft_color_line_get_extend, nullptr + }; + + c->funcs->radial_gradient (c->data, &cl, + paint.u.radial_gradient.c0.x / 65536.f, + paint.u.radial_gradient.c0.y / 65536.f, + paint.u.radial_gradient.r0 / 65536.f, + paint.u.radial_gradient.c1.x / 65536.f, + paint.u.radial_gradient.c1.y / 65536.f, + paint.u.radial_gradient.r1 / 65536.f); + } + break; + case FT_COLR_PAINTFORMAT_SWEEP_GRADIENT: + { + hb_color_line_t cl = { + &paint.u.linear_gradient.colorline, + _hb_ft_color_line_get_color_stops, c, + _hb_ft_color_line_get_extend, nullptr + }; + + c->funcs->sweep_gradient (c->data, &cl, + paint.u.sweep_gradient.center.x / 65536.f, + paint.u.sweep_gradient.center.y / 65536.f, + (paint.u.sweep_gradient.start_angle / 65536.f + 1) * (float) M_PI, + (paint.u.sweep_gradient.end_angle / 65536.f + 1) * (float) M_PI); + } + break; + case FT_COLR_PAINTFORMAT_GLYPH: + { + c->funcs->push_inverse_root_transform (c->data, c->font); + c->ft_font->lock.unlock (); + c->funcs->push_clip_glyph (c->data, paint.u.glyph.glyphID, c->font); + c->ft_font->lock.lock (); + c->funcs->push_root_transform (c->data, c->font); + c->recurse (paint.u.glyph.paint); + c->funcs->pop_root_transform (c->data); + c->funcs->pop_clip (c->data); + c->funcs->pop_inverse_root_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_COLR_GLYPH: + { + FT_OpaquePaint other_paint = {0}; + if (FT_Get_Color_Glyph_Paint (ft_face, paint.u.colr_glyph.glyphID, + FT_COLOR_NO_ROOT_TRANSFORM, + &other_paint)) + { + bool has_clip_box; + FT_ClipBox clip_box; + has_clip_box = FT_Get_Color_Glyph_ClipBox (ft_face, paint.u.colr_glyph.glyphID, &clip_box); + has_clip_box = 0; + + if (has_clip_box) + c->funcs->push_clip_rectangle (c->data, + clip_box.bottom_left.x / 64.f, + clip_box.bottom_left.y / 64.f, + clip_box.top_right.x / 64.f, + clip_box.top_right.y / 64.f); + c->recurse (other_paint); + if (has_clip_box) + c->funcs->pop_clip (c->data); + } + } + break; + case FT_COLR_PAINTFORMAT_TRANSFORM: + { + c->funcs->push_transform (c->data, + paint.u.transform.affine.xx / 65536.f, + paint.u.transform.affine.yx / 65536.f, + paint.u.transform.affine.xy / 65536.f, + paint.u.transform.affine.yy / 65536.f, + paint.u.transform.affine.dx / 65536.f, + paint.u.transform.affine.dy / 65536.f); + c->recurse (paint.u.transform.paint); + c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_TRANSLATE: + { + c->funcs->push_transform (c->data, + 0.f, 0.f, 0.f, 0.f, + paint.u.translate.dx / 65536.f, + paint.u.translate.dy / 65536.f); + c->recurse (paint.u.translate.paint); + c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_SCALE: + { + bool has_translate = paint.u.scale.center_x != 0 || paint.u.scale.center_y != 0; + if (has_translate) + c->funcs->push_transform (c->data, + 1.f, 0.f, 0.f, 1.f, + +paint.u.scale.center_x / 65536.f, + +paint.u.scale.center_y / 65536.f); + c->funcs->push_transform (c->data, + paint.u.scale.scale_x / 65536.f, + 0.f, 0.f, + paint.u.scale.scale_y / 65536.f, + 0.f, 0.f); + if (has_translate) + c->funcs->push_transform (c->data, + 1.f, 0.f, 0.f, 1.f, + -paint.u.scale.center_x / 65536.f, + -paint.u.scale.center_y / 65536.f); + c->recurse (paint.u.scale.paint); + c->funcs->pop_transform (c->data); + if (has_translate) + { + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + } + } + break; + case FT_COLR_PAINTFORMAT_ROTATE: + { + float a = paint.u.rotate.angle / 65536.f; + float cc = cosf (a * (float) M_PI); + float ss = sinf (a * (float) M_PI); + c->funcs->push_transform (c->data, + 1.f, 0.f, 0.f, 1.f, + +paint.u.rotate.center_x / 65536.f, + +paint.u.rotate.center_y / 65536.f); + c->funcs->push_transform (c->data, cc, ss, -ss, cc, 0., 0.); + c->funcs->push_transform (c->data, + 1.f, 0.f, 0.f, 1.f, + -paint.u.rotate.center_x / 65536.f, + -paint.u.rotate.center_y / 65536.f); + c->recurse (paint.u.rotate.paint); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_SKEW: + { + float x = +tanf (paint.u.skew.x_skew_angle / 65536.f * (float) M_PI); + float y = -tanf (paint.u.skew.y_skew_angle / 65536.f * (float) M_PI); + c->funcs->push_transform (c->data, + 1.f, 0.f, 0.f, 1.f, + +paint.u.skew.center_x / 65536.f, + +paint.u.skew.center_y / 65536.f); + c->funcs->push_transform (c->data, 1., y, x, 1., 0., 0.); + c->funcs->push_transform (c->data, + 1.f, 0.f, 0.f, 1.f, + -paint.u.skew.center_x / 65536.f, + -paint.u.skew.center_y / 65536.f); + c->recurse (paint.u.skew.paint); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + } + break; + case FT_COLR_PAINTFORMAT_COMPOSITE: + { + c->funcs->push_group (c->data); + c->recurse (paint.u.composite.backdrop_paint); + c->funcs->push_group (c->data); + c->recurse (paint.u.composite.source_paint); + c->funcs->pop_group (c->data, _hb_ft_paint_composite_mode (paint.u.composite.composite_mode)); + c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); + } + break; + + case FT_COLR_PAINT_FORMAT_MAX: break; + default: HB_FALLTHROUGH; + case FT_COLR_PAINTFORMAT_UNSUPPORTED: break; + } +} + +#endif + +static bool +hb_ft_paint_glyph_colr (hb_font_t *font, + void *font_data, + hb_codepoint_t gid, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Face ft_face = ft_font->ft_face; + + /* Face is locked. */ + + FT_Error error; + FT_Color* palette; + FT_LayerIterator iterator; + + FT_Bool have_layers; + FT_UInt layer_glyph_index; + FT_UInt layer_color_index; + + error = FT_Palette_Select(ft_face, palette_index, &palette); + if (error) + palette = NULL; + +#ifdef TT_SUPPORT_COLRV1 + /* COLRv1 */ + FT_OpaquePaint paint = {0}; + if (FT_Get_Color_Glyph_Paint (ft_face, gid, + FT_COLOR_NO_ROOT_TRANSFORM, + &paint)) + { + bool is_bounded = true; + FT_ClipBox clip_box; + if (FT_Get_Color_Glyph_ClipBox (ft_face, gid, &clip_box)) + paint_funcs->push_clip_rectangle (paint_data, + clip_box.bottom_left.x - font->slant_xy * clip_box.bottom_left.y, + clip_box.bottom_left.y, + clip_box.top_right.x - font->slant_xy * clip_box.top_right.y, + clip_box.top_right.y); + else + { + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + hb_ft_paint_context_t c (ft_font, font, + extents_funcs, &extents_data, + palette, foreground); + _hb_ft_paint (&c, paint); + hb_extents_t extents = extents_data.get_extents (); + is_bounded = extents_data.is_bounded (); + paint_funcs->push_clip_rectangle (paint_data, + extents.xmin, + extents.ymin, + extents.xmax, + extents.ymax); + + hb_paint_funcs_destroy (extents_funcs); + } + + paint_funcs->push_root_transform (paint_data, font); + + hb_ft_paint_context_t c (ft_font, font, + paint_funcs, paint_data, + palette, foreground); + + if (is_bounded) + _hb_ft_paint (&c, paint); + + paint_funcs->pop_root_transform (paint_data); + paint_funcs->pop_clip (paint_data); + + return true; + } +#endif + + /* COLRv0 */ + iterator.p = NULL; + have_layers = FT_Get_Color_Glyph_Layer(ft_face, + gid, + &layer_glyph_index, + &layer_color_index, + &iterator); + + if (palette && have_layers) + { + do + { + hb_bool_t is_foreground = true; + hb_color_t color = foreground; + + if ( layer_color_index != 0xFFFF ) + { + FT_Color layer_color = palette[layer_color_index]; + color = HB_COLOR (layer_color.blue, + layer_color.green, + layer_color.red, + layer_color.alpha); + is_foreground = false; + } + + ft_font->lock.unlock (); + paint_funcs->push_clip_glyph (paint_data, layer_glyph_index, font); + ft_font->lock.lock (); + paint_funcs->color (paint_data, is_foreground, color); + paint_funcs->pop_clip (paint_data); + + } while (FT_Get_Color_Glyph_Layer(ft_face, + gid, + &layer_glyph_index, + &layer_color_index, + &iterator)); + return true; + } + + return false; +} + +#endif + + +#endif /* HB_FT_COLR_HH */ diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 097c52f7f..70cdcbe81 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -33,17 +33,19 @@ #include "hb-ft.h" +#include "hb-cache.hh" #include "hb-draw.hh" #include "hb-font.hh" #include "hb-machinery.hh" -#include "hb-cache.hh" #include "hb-ot-os2-table.hh" #include "hb-ot-shaper-arabic-pua.hh" +#include "hb-paint.hh" #include FT_ADVANCES_H #include FT_MULTIPLE_MASTERS_H #include FT_OUTLINE_H #include FT_TRUETYPE_TABLES_H +#include FT_COLOR_H /** @@ -777,11 +779,11 @@ _hb_ft_cubic_to (const FT_Vector *control1, } static void -hb_ft_get_glyph_shape (hb_font_t *font HB_UNUSED, - void *font_data, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data, - void *user_data HB_UNUSED) +hb_ft_draw_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; hb_lock_t lock (ft_font->lock); @@ -811,6 +813,88 @@ hb_ft_get_glyph_shape (hb_font_t *font HB_UNUSED, } #endif +#ifndef HB_NO_PAINT + +#include "hb-ft-colr.hh" + +static void +hb_ft_paint_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t gid, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + /* We release the lock before calling into callbacks, such that + * eg. draw API can call back into the face.*/ + + if (unlikely (FT_Load_Glyph (ft_face, gid, + ft_font->load_flags | FT_LOAD_COLOR))) + return; + + if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) + { + if (hb_ft_paint_glyph_colr (font, font_data, gid, + paint_funcs, paint_data, + palette_index, foreground, + user_data)) + return; + + /* Simple outline. */ + ft_font->lock.unlock (); + paint_funcs->push_clip_glyph (paint_data, gid, font); + ft_font->lock.lock (); + paint_funcs->color (paint_data, true, foreground); + paint_funcs->pop_clip (paint_data); + + return; + } + + auto *glyph = ft_face->glyph; + if (glyph->format == FT_GLYPH_FORMAT_BITMAP) + { + auto &bitmap = glyph->bitmap; + if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) + { + if (bitmap.pitch != (signed) bitmap.width * 4) + return; + + ft_font->lock.unlock (); + + hb_blob_t *blob = hb_blob_create ((const char *) bitmap.buffer, + bitmap.pitch * bitmap.rows, + HB_MEMORY_MODE_DUPLICATE, + nullptr, nullptr); + + hb_glyph_extents_t extents; + if (!hb_font_get_glyph_extents (font, gid, &extents)) + goto out; + + paint_funcs->image (paint_data, + blob, + bitmap.width, + bitmap.rows, + HB_PAINT_IMAGE_FORMAT_BGRA, + font->slant_xy, + &extents); + + out: + hb_blob_destroy (blob); + ft_font->lock.lock (); + } + + return; + } + + /* TODO Support image, COLRv0/1. */ +} +#endif + static inline void free_static_ft_funcs (); @@ -844,7 +928,11 @@ static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tpush_clip_glyph (data, glyph, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; +} + bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const { #ifdef HB_NO_OT_FONT_CFF diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh index bb856c9dd..c68763fe4 100644 --- a/src/hb-ot-cff1-table.hh +++ b/src/hb-ot-cff1-table.hh @@ -30,6 +30,7 @@ #include "hb-ot-cff-common.hh" #include "hb-subset-cff1.hh" #include "hb-draw.hh" +#include "hb-paint.hh" #define HB_STRING_ARRAY_NAME cff1_std_strings #define HB_STRING_ARRAY_LIST "hb-ot-cff1-std-str.hh" @@ -1426,6 +1427,7 @@ struct cff1 } HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const; HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; diff --git a/src/hb-ot-cff2-table.cc b/src/hb-ot-cff2-table.cc index d530fc2ee..795556555 100644 --- a/src/hb-ot-cff2-table.cc +++ b/src/hb-ot-cff2-table.cc @@ -143,6 +143,15 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, return true; } +bool OT::cff2::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const +{ + funcs->push_clip_glyph (data, glyph, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; +} + struct cff2_path_param_t { cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_) diff --git a/src/hb-ot-cff2-table.hh b/src/hb-ot-cff2-table.hh index 9081930bb..9a3d2d013 100644 --- a/src/hb-ot-cff2-table.hh +++ b/src/hb-ot-cff2-table.hh @@ -30,6 +30,7 @@ #include "hb-ot-cff-common.hh" #include "hb-subset-cff2.hh" #include "hb-draw.hh" +#include "hb-paint.hh" namespace CFF { @@ -516,6 +517,7 @@ struct cff2 HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const; HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; }; diff --git a/src/hb-ot-color-cbdt-table.hh b/src/hb-ot-color-cbdt-table.hh index 4f8fa0123..c326770a8 100644 --- a/src/hb-ot-color-cbdt-table.hh +++ b/src/hb-ot-color-cbdt-table.hh @@ -28,6 +28,7 @@ #define HB_OT_COLOR_CBDT_TABLE_HH #include "hb-open-type.hh" +#include "hb-paint.hh" /* * CBLC -- Color Bitmap Location @@ -80,14 +81,15 @@ struct SmallGlyphMetrics return_trace (c->check_struct (this)); } - void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scale) const { extents->x_bearing = bearingX; extents->y_bearing = bearingY; extents->width = width; extents->height = -static_cast (height); - font->scale_glyph_extents (extents); + if (scale) + font->scale_glyph_extents (extents); } HBUINT8 height; @@ -309,7 +311,7 @@ struct IndexSubtable } } - bool get_extents (hb_glyph_extents_t *extents HB_UNUSED) const + bool get_extents (hb_glyph_extents_t *extents HB_UNUSED, bool scale HB_UNUSED) const { switch (u.header.indexFormat) { @@ -506,8 +508,8 @@ struct IndexSubtableRecord return num_missing; } - bool get_extents (hb_glyph_extents_t *extents, const void *base) const - { return (base+offsetToSubtable).get_extents (extents); } + bool get_extents (hb_glyph_extents_t *extents, const void *base, bool scale) const + { return (base+offsetToSubtable).get_extents (extents, scale); } bool get_image_data (unsigned int gid, const void *base, @@ -835,7 +837,7 @@ struct CBDT } bool - get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents, bool scale = true) const { const void *base; const BitmapSizeTable &strike = this->cblc->choose_strike (font); @@ -843,7 +845,7 @@ struct CBDT if (!subtable_record || !strike.ppemX || !strike.ppemY) return false; - if (subtable_record->get_extents (extents, base)) + if (subtable_record->get_extents (extents, base, scale)) return true; unsigned int image_offset = 0, image_length = 0, image_format = 0; @@ -860,26 +862,29 @@ struct CBDT if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) return false; auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); - glyphFormat17.glyphMetrics.get_extents (font, extents); + glyphFormat17.glyphMetrics.get_extents (font, extents, scale); break; } case 18: { if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) return false; auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); - glyphFormat18.glyphMetrics.get_extents (font, extents); + glyphFormat18.glyphMetrics.get_extents (font, extents, scale); break; } default: return false; /* TODO: Support other image formats. */ } /* Convert to font units. */ - float x_scale = upem / (float) strike.ppemX; - float y_scale = upem / (float) strike.ppemY; - extents->x_bearing = roundf (extents->x_bearing * x_scale); - extents->y_bearing = roundf (extents->y_bearing * y_scale); - extents->width = roundf (extents->width * x_scale); - extents->height = roundf (extents->height * y_scale); + if (scale) + { + float x_scale = upem / (float) strike.ppemX; + float y_scale = upem / (float) strike.ppemY; + extents->x_bearing = roundf (extents->x_bearing * x_scale); + extents->y_bearing = roundf (extents->y_bearing * y_scale); + extents->width = roundf (extents->width * x_scale); + extents->height = roundf (extents->height * y_scale); + } return true; } @@ -936,6 +941,39 @@ struct CBDT bool has_data () const { return cbdt.get_length (); } + bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + hb_glyph_extents_t extents; + hb_glyph_extents_t pixel_extents; + hb_blob_t *blob = reference_png (font, glyph); + + if (unlikely (blob == hb_blob_get_empty ())) + return false; + + if (unlikely (!hb_font_get_glyph_extents (font, glyph, &extents))) + return false; + + if (unlikely (!get_extents (font, glyph, &pixel_extents, false))) + return false; + + funcs->push_clip_rectangle (data, + extents.x_bearing, extents.y_bearing, + extents.x_bearing + extents.width, + extents.y_bearing + extents.height); + + funcs->image (data, + blob, + pixel_extents.width, -pixel_extents.height, + HB_PAINT_IMAGE_FORMAT_PNG, + font->slant_xy, + &extents); + + funcs->pop_clip (data); + + hb_blob_destroy (blob); + return true; + } + private: hb_blob_ptr_t cblc; hb_blob_ptr_t cbdt; diff --git a/src/hb-ot-color-colr-table.cc b/src/hb-ot-color-colr-table.cc new file mode 100644 index 000000000..f7cf00712 --- /dev/null +++ b/src/hb-ot-color-colr-table.cc @@ -0,0 +1,27 @@ +#include "hb-ot-color-colr-table.hh" + +namespace OT { + +void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const +{ + const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList (); + for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++) + { + const Paint &paint = paint_offset_lists.get_paint (i); + c->funcs->push_group (c->data); + c->recurse (paint); + c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); + } +} + +void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const +{ + const COLR *colr_table = c->get_colr_table (); + const Paint *paint = colr_table->get_base_glyph_paint (gid); + + // TODO apply clipbox + if (paint) + c->recurse (*paint); +} + +} diff --git a/src/hb-ot-color-colr-table.hh b/src/hb-ot-color-colr-table.hh index 79b0b476c..c14033233 100644 --- a/src/hb-ot-color-colr-table.hh +++ b/src/hb-ot-color-colr-table.hh @@ -28,9 +28,12 @@ #ifndef HB_OT_COLOR_COLR_TABLE_HH #define HB_OT_COLOR_COLR_TABLE_HH +#include "hb.hh" #include "hb-open-type.hh" #include "hb-ot-layout-common.hh" #include "hb-ot-var-common.hh" +#include "hb-paint.hh" +#include "hb-paint-extents.hh" /* * COLR -- Color @@ -42,9 +45,77 @@ #define HB_COLRV1_MAX_NESTING_LEVEL 128 #endif + +namespace OT { +struct hb_paint_context_t; +} + namespace OT { struct COLR; + +struct Paint; + +struct hb_paint_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) { obj.paint_glyph (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + + const COLR* get_colr_table () const + { return reinterpret_cast (base); } + +public: + const void *base; + hb_paint_funcs_t *funcs; + void *data; + hb_font_t *font; + unsigned int palette; + hb_color_t foreground; + VarStoreInstancer &instancer; + int depth_left = HB_COLRV1_MAX_NESTING_LEVEL; + + hb_paint_context_t (const void *base_, + hb_paint_funcs_t *funcs_, + void *data_, + hb_font_t *font_, + unsigned int palette_, + hb_color_t foreground_, + VarStoreInstancer &instancer_) : + base (base_), + funcs (funcs_), + data (data_), + font (font_), + palette (palette_), + foreground (foreground_), + instancer (instancer_) + { } + + hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground) + { + hb_color_t color = foreground; + + *is_foreground = true; + + if (color_index != 0xffff) + { + unsigned int clen = 1; + hb_face_t *face = hb_font_get_face (font); + + hb_ot_color_palette_get_colors (face, palette, color_index, &clen, &color); + *is_foreground = false; + } + + return HB_COLOR (hb_color_get_blue (color), + hb_color_get_green (color), + hb_color_get_red (color), + hb_color_get_alpha (color) * alpha); + } + + inline void recurse (const Paint &paint); +}; + struct hb_colrv1_closure_context_t : hb_dispatch_context_t { @@ -160,6 +231,8 @@ struct BaseGlyphRecord template struct Variable { + static constexpr bool is_variable = true; + Variable* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); @@ -182,6 +255,23 @@ struct Variable return_trace (c->check_struct (this) && value.sanitize (c)); } + void paint_glyph (hb_paint_context_t *c) const + { + value.paint_glyph (c, varIdxBase); + } + + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *stop, + const VarStoreInstancer &instancer) const + { + value.get_color_stop (c, stop, varIdxBase, instancer); + } + + hb_paint_extend_t get_extend () const + { + return value.get_extend (); + } + protected: T value; public: @@ -193,6 +283,8 @@ struct Variable template struct NoVariable { + static constexpr bool is_variable = false; + static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION; NoVariable* copy (hb_serialize_context_t *c) const @@ -216,6 +308,23 @@ struct NoVariable return_trace (c->check_struct (this) && value.sanitize (c)); } + void paint_glyph (hb_paint_context_t *c) const + { + value.paint_glyph (c, varIdxBase); + } + + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *stop, + const VarStoreInstancer &instancer) const + { + value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer); + } + + hb_paint_extend_t get_extend () const + { + return value.get_extend (); + } + T value; public: DEFINE_SIZE_STATIC (T::static_size); @@ -243,6 +352,17 @@ struct ColorStop return_trace (c->check_struct (this)); } + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *out, + uint32_t varIdx, + const VarStoreInstancer &instancer) const + { + out->offset = stopOffset.to_float(instancer (varIdx, 0)); + out->color = c->get_color (paletteIndex, + alpha.to_float (instancer (varIdx, 1)), + &out->is_foreground); + } + F2DOT14 stopOffset; HBUINT16 paletteIndex; F2DOT14 alpha; @@ -294,6 +414,52 @@ struct ColorLine stops.sanitize (c)); } + /* get up to count stops from start */ + unsigned int + get_color_stops (hb_paint_context_t *c, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + const VarStoreInstancer &instancer) const + { + unsigned int len = stops.len; + + if (count && color_stops) + { + unsigned int i; + for (i = 0; i < *count && start + i < len; i++) + stops[start + i].get_color_stop (c, &color_stops[i], instancer); + *count = i; + } + + return len; + } + + HB_INTERNAL static unsigned int static_get_color_stops (hb_color_line_t *color_line, + void *color_line_data, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + void *user_data) + { + const ColorLine *thiz = (const ColorLine *) color_line_data; + hb_paint_context_t *c = (hb_paint_context_t *) user_data; + return thiz->get_color_stops (c, start, count, color_stops, c->instancer); + } + + hb_paint_extend_t get_extend () const + { + return (hb_paint_extend_t) (unsigned int) extend; + } + + HB_INTERNAL static hb_paint_extend_t static_get_extend (hb_color_line_t *color_line, + void *color_line_data, + void *user_data) + { + const ColorLine *thiz = (const ColorLine *) color_line_data; + return thiz->get_extend (); + } + Extend extend; Array16Of> stops; public: @@ -357,6 +523,17 @@ struct Affine2x3 return_trace (c->check_struct (this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + c->funcs->push_transform (c->data, + xx.to_float (c->instancer (varIdxBase, 0)), + yx.to_float (c->instancer (varIdxBase, 1)), + xy.to_float (c->instancer (varIdxBase, 2)), + yy.to_float (c->instancer (varIdxBase, 3)), + dx.to_float (c->instancer (varIdxBase, 4)), + dy.to_float (c->instancer (varIdxBase, 5))); + } + F16DOT16 xx; F16DOT16 yx; F16DOT16 xy; @@ -388,6 +565,8 @@ struct PaintColrLayers return_trace (c->check_struct (this)); } + HB_INTERNAL void paint_glyph (hb_paint_context_t *c) const; + HBUINT8 format; /* format = 1 */ HBUINT8 numLayers; HBUINT32 firstLayerIndex; /* index into COLRv1::layerList */ @@ -415,6 +594,17 @@ struct PaintSolid return_trace (c->check_struct (this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_bool_t is_foreground; + hb_color_t color; + + color = c->get_color (paletteIndex, + alpha.to_float (c->instancer (varIdxBase, 0)), + &is_foreground); + c->funcs->color (c->data, is_foreground, color); + } + HBUINT8 format; /* format = 2(noVar) or 3(Var)*/ HBUINT16 paletteIndex; F2DOT14 alpha; @@ -443,6 +633,23 @@ struct PaintLinearGradient return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->linear_gradient (c->data, &cl, + x0 + c->instancer (varIdxBase, 0), + y0 + c->instancer (varIdxBase, 1), + x1 + c->instancer (varIdxBase, 2), + y1 + c->instancer (varIdxBase, 3), + x2 + c->instancer (varIdxBase, 4), + y2 + c->instancer (varIdxBase, 5)); + } + HBUINT8 format; /* format = 4(noVar) or 5 (Var) */ Offset24To> colorLine; /* Offset (from beginning of PaintLinearGradient * table) to ColorLine subtable. */ @@ -477,6 +684,23 @@ struct PaintRadialGradient return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->radial_gradient (c->data, &cl, + x0 + c->instancer (varIdxBase, 0), + y0 + c->instancer (varIdxBase, 1), + radius0 + c->instancer (varIdxBase, 2), + x1 + c->instancer (varIdxBase, 3), + y1 + c->instancer (varIdxBase, 4), + radius1 + c->instancer (varIdxBase, 5)); + } + HBUINT8 format; /* format = 6(noVar) or 7 (Var) */ Offset24To> colorLine; /* Offset (from beginning of PaintRadialGradient * table) to ColorLine subtable. */ @@ -511,6 +735,21 @@ struct PaintSweepGradient return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->sweep_gradient (c->data, &cl, + centerX + c->instancer (varIdxBase, 0), + centerY + c->instancer (varIdxBase, 1), + (startAngle.to_float (c->instancer (varIdxBase, 2)) + 1) * (float) M_PI, + (endAngle.to_float (c->instancer (varIdxBase, 3)) + 1) * (float) M_PI); + } + HBUINT8 format; /* format = 8(noVar) or 9 (Var) */ Offset24To> colorLine; /* Offset (from beginning of PaintSweepGradient * table) to ColorLine subtable. */ @@ -522,8 +761,6 @@ struct PaintSweepGradient DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size + 2 * F2DOT14::static_size); }; -struct Paint; - // Paint a non-COLR glyph, filled as indicated by paint. struct PaintGlyph { @@ -548,6 +785,17 @@ struct PaintGlyph return_trace (c->check_struct (this) && paint.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c) const + { + c->funcs->push_inverse_root_transform (c->data, c->font); + c->funcs->push_clip_glyph (c->data, gid, c->font); + c->funcs->push_root_transform (c->data, c->font); + c->recurse (this+paint); + c->funcs->pop_root_transform (c->data); + c->funcs->pop_clip (c->data); + c->funcs->pop_inverse_root_transform (c->data); + } + HBUINT8 format; /* format = 10 */ Offset24To paint; /* Offset (from beginning of PaintGlyph table) to Paint subtable. */ HBUINT16 gid; @@ -575,6 +823,8 @@ struct PaintColrGlyph return_trace (c->check_struct (this)); } + HB_INTERNAL void paint_glyph (hb_paint_context_t *c) const; + HBUINT8 format; /* format = 11 */ HBUINT16 gid; public: @@ -603,6 +853,13 @@ struct PaintTransform transform.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c) const + { + (this+transform).paint_glyph (c); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 12(noVar) or 13 (Var) */ Offset24To src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */ Offset24To> transform; @@ -629,6 +886,16 @@ struct PaintTranslate return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + c->funcs->push_transform (c->data, + 1., 0., 0., 1., + dx + c->instancer (varIdxBase, 0), + dy + c->instancer (varIdxBase, 0)); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 14(noVar) or 15 (Var) */ Offset24To src; /* Offset (from beginning of PaintTranslate table) to Paint subtable. */ FWORD dx; @@ -656,6 +923,17 @@ struct PaintScale return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + c->funcs->push_transform (c->data, + scaleX.to_float (c->instancer (varIdxBase, 0)), + 0., 0., + scaleY.to_float (c->instancer (varIdxBase, 1)), + 0., 0.); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 16 (noVar) or 17(Var) */ Offset24To src; /* Offset (from beginning of PaintScale table) to Paint subtable. */ F2DOT14 scaleX; @@ -683,6 +961,23 @@ struct PaintScaleAroundCenter return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float tCenterX = centerX + c->instancer (varIdxBase, 2); + float tCenterY = centerY + c->instancer (varIdxBase, 3); + c->funcs->push_transform (c->data, 0., 0., 0., 0., +tCenterX, +tCenterY); + c->funcs->push_transform (c->data, + scaleX.to_float (c->instancer (varIdxBase, 0)), + 0., 0., + scaleY.to_float (c->instancer (varIdxBase, 1)), + 0., 0.); + c->funcs->push_transform (c->data, 0., 0., 0., 0., -tCenterX, -tCenterY); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 18 (noVar) or 19(Var) */ Offset24To src; /* Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable. */ F2DOT14 scaleX; @@ -712,6 +1007,14 @@ struct PaintScaleUniform return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float s = scale + c->instancer (varIdxBase, 0); + c->funcs->push_transform (c->data, s, 0., 0., s, 0., 0.); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 20 (noVar) or 21(Var) */ Offset24To src; /* Offset (from beginning of PaintScaleUniform table) to Paint subtable. */ F2DOT14 scale; @@ -738,6 +1041,20 @@ struct PaintScaleUniformAroundCenter return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float s = scale + c->instancer (varIdxBase, 0); + float tCenterX = centerX + c->instancer (varIdxBase, 1); + float tCenterY = centerY + c->instancer (varIdxBase, 2); + c->funcs->push_transform (c->data, 0., 0., 0., 0., +tCenterX, +tCenterY); + c->funcs->push_transform (c->data, s, 0., 0., s, 0., 0.); + c->funcs->push_transform (c->data, 0., 0., 0., 0., -tCenterX, -tCenterY); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 22 (noVar) or 23(Var) */ Offset24To src; /* Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable. */ F2DOT14 scale; @@ -766,6 +1083,16 @@ struct PaintRotate return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float a = angle.to_float (c->instancer (varIdxBase, 0)); + float cc = cosf (a * (float) M_PI); + float ss = sinf (a * (float) M_PI); + c->funcs->push_transform (c->data, cc, ss, -ss, cc, 0., 0.); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 24 (noVar) or 25(Var) */ Offset24To src; /* Offset (from beginning of PaintRotate table) to Paint subtable. */ F2DOT14 angle; @@ -792,6 +1119,22 @@ struct PaintRotateAroundCenter return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float a = angle.to_float (c->instancer (varIdxBase, 0)); + float cc = cosf (a * (float) M_PI); + float ss = sinf (a * (float) M_PI); + float tCenterX = centerX + c->instancer (varIdxBase, 1); + float tCenterY = centerY + c->instancer (varIdxBase, 2); + c->funcs->push_transform (c->data, 0., 0., 0., 0., +tCenterX, +tCenterY); + c->funcs->push_transform (c->data, cc, ss, -ss, cc, 0., 0.); + c->funcs->push_transform (c->data, 0., 0., 0., 0., -tCenterX, -tCenterY); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 26 (noVar) or 27(Var) */ Offset24To src; /* Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable. */ F2DOT14 angle; @@ -820,6 +1163,15 @@ struct PaintSkew return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float x = +tanf (xSkewAngle.to_float(c->instancer (varIdxBase, 0)) * (float) M_PI); + float y = -tanf (ySkewAngle.to_float(c->instancer (varIdxBase, 1)) * (float) M_PI); + c->funcs->push_transform (c->data, 1., y, x, 1., 0., 0.); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 28(noVar) or 29 (Var) */ Offset24To src; /* Offset (from beginning of PaintSkew table) to Paint subtable. */ F2DOT14 xSkewAngle; @@ -847,6 +1199,21 @@ struct PaintSkewAroundCenter return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float x = +tanf (xSkewAngle.to_float(c->instancer (varIdxBase, 0)) * (float) M_PI); + float y = -tanf (ySkewAngle.to_float(c->instancer (varIdxBase, 1)) * (float) M_PI); + float tCenterX = centerX + c->instancer (varIdxBase, 2); + float tCenterY = centerY + c->instancer (varIdxBase, 3); + c->funcs->push_transform (c->data, 0., 0., 0., 0., +tCenterX, +tCenterY); + c->funcs->push_transform (c->data, 1., y, x, 1., 0., 0.); + c->funcs->push_transform (c->data, 0., 0., 0., 0., -tCenterX, -tCenterY); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 30(noVar) or 31 (Var) */ Offset24To src; /* Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable. */ F2DOT14 xSkewAngle; @@ -879,6 +1246,16 @@ struct PaintComposite backdrop.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c) const + { + c->funcs->push_group (c->data); + c->recurse (this+backdrop); + c->funcs->push_group (c->data); + c->recurse (this+src); + c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode); + c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); + } + HBUINT8 format; /* format = 32 */ Offset24To src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */ CompositeMode mode; /* If mode is unrecognized use COMPOSITE_CLEAR */ @@ -1194,35 +1571,35 @@ struct Paint union { HBUINT8 format; PaintColrLayers paintformat1; - PaintSolid paintformat2; + NoVariable paintformat2; Variable paintformat3; - PaintLinearGradient paintformat4; + NoVariable> paintformat4; Variable> paintformat5; - PaintRadialGradient paintformat6; + NoVariable> paintformat6; Variable> paintformat7; - PaintSweepGradient paintformat8; + NoVariable> paintformat8; Variable> paintformat9; PaintGlyph paintformat10; PaintColrGlyph paintformat11; PaintTransform paintformat12; PaintTransform paintformat13; - PaintTranslate paintformat14; + NoVariable paintformat14; Variable paintformat15; - PaintScale paintformat16; + NoVariable paintformat16; Variable paintformat17; - PaintScaleAroundCenter paintformat18; + NoVariable paintformat18; Variable paintformat19; - PaintScaleUniform paintformat20; + NoVariable paintformat20; Variable paintformat21; - PaintScaleUniformAroundCenter paintformat22; + NoVariable paintformat22; Variable paintformat23; - PaintRotate paintformat24; + NoVariable paintformat24; Variable paintformat25; - PaintRotateAroundCenter paintformat26; + NoVariable paintformat26; Variable paintformat27; - PaintSkew paintformat28; + NoVariable paintformat28; Variable paintformat29; - PaintSkewAroundCenter paintformat30; + NoVariable paintformat30; Variable paintformat31; PaintComposite paintformat32; } u; @@ -1323,7 +1700,8 @@ struct COLR { static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR; - bool has_data () const { return numBaseGlyphs; } + bool has_v0_data () const { return numBaseGlyphs; } + bool has_v1_data () const { return (this+baseGlyphList).len; } unsigned int get_glyph_layers (hb_codepoint_t glyph, unsigned int start_offset, @@ -1589,6 +1967,19 @@ struct COLR return_trace (true); } + const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const + { + const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList; + const BaseGlyphPaintRecord* record = get_base_glyph_paintrecord (glyph); + if (record) + { + const Paint &paint = &baseglyph_paintrecords+record->paint; + return &paint; + } + else + return nullptr; + } + bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const { @@ -1607,6 +1998,115 @@ struct COLR return true; } + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0)); + + hb_extents_t e = extents_data.get_extents (); + extents->x_bearing = e.xmin; + extents->y_bearing = e.ymax; + extents->width = e.xmax - e.xmin; + extents->height = e.ymin - e.ymax; + + hb_paint_funcs_destroy (extents_funcs); + + return true; + } + + bool + paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette, hb_color_t foreground, bool clip = true) const + { + VarStoreInstancer instancer (this+varStore, + this+varIdxMap, + hb_array (font->coords, font->num_coords)); + hb_paint_context_t c (this, funcs, data, font, palette, foreground, instancer); + + if (version == 1) + { + const Paint *paint = get_base_glyph_paint (glyph); + if (paint) + { + // COLRv1 glyph + + VarStoreInstancer instancer (this+varStore, + this+varIdxMap, + hb_array (font->coords, font->num_coords)); + + bool is_bounded = true; + bool pop_clip_first = true; + if (clip) + { + hb_glyph_extents_t extents; + if ((this+clipList).get_extents (glyph, + &extents, + instancer)) + { + c.funcs->push_root_transform (c.data, font); + + c.funcs->push_clip_rectangle (c.data, + extents.x_bearing, + extents.y_bearing + extents.height, + extents.x_bearing + extents.width, + extents.y_bearing); + } + else + { + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + + paint_glyph (font, glyph, + extents_funcs, &extents_data, + palette, foreground, + false); + + hb_extents_t extents = extents_data.get_extents (); + is_bounded = extents_data.is_bounded (); + c.funcs->push_clip_rectangle (c.data, + extents.xmin, + extents.ymin, + extents.xmax, + extents.ymax); + + hb_paint_funcs_destroy (extents_funcs); + + c.funcs->push_root_transform (c.data, font); + + pop_clip_first = false; + } + } + + if (is_bounded) + c.recurse (*paint); + + if (clip && pop_clip_first) + c.funcs->pop_clip (c.data); + + c.funcs->pop_root_transform (c.data); + + if (clip && !pop_clip_first) + c.funcs->pop_clip (c.data); + + return true; + } + } + + const BaseGlyphRecord *record = get_base_glyph_record (glyph); + if (record && ((hb_codepoint_t) record->glyphId == glyph)) + { + // COLRv0 glyph + for (const auto &r : (this+layersZ).as_array (numLayers) + .sub_array (record->firstLayerIdx, record->numLayers)) + { + hb_bool_t is_foreground; + hb_color_t color = c.get_color (r.colorIdx, 1., &is_foreground); + c.funcs->push_clip_glyph (c.data, r.glyphId, c.font); + c.funcs->color (c.data, is_foreground, color); + c.funcs->pop_clip (c.data); + } + + return true; + } + return false; } @@ -1632,7 +2132,15 @@ struct COLR_accelerator_t : COLR::accelerator_t { COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {} }; +void +hb_paint_context_t::recurse (const Paint &paint) +{ + depth_left--; + if (depth_left > 0) + paint.dispatch (this); + depth_left++; +} + } /* namespace OT */ - #endif /* HB_OT_COLOR_COLR_TABLE_HH */ diff --git a/src/hb-ot-color-colrv1-paint.hh b/src/hb-ot-color-colrv1-paint.hh new file mode 100644 index 000000000..75a8539ac --- /dev/null +++ b/src/hb-ot-color-colrv1-paint.hh @@ -0,0 +1,286 @@ +/* + * Copyright © 2022 Matthias Clasen + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef HB_OT_COLR_COLRV1_PAINT_HH +#define HB_OT_COLR_COLRV1_PAINT_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-color-colr-table.hh" + +/* + * COLR -- Color + * https://docs.microsoft.com/en-us/typography/opentype/spec/colr + */ +namespace OT { + +struct hb_colrv1_paint_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) + { + obj.paint (this); + return hb_empty_t (); + } + + const COLR* get_colr_table () const + { return reinterpret_cast (base); } + + public: + const void *base; + hb_paint_funcs_t *funcs; + void *paint_data; + + hb_colrv1_paint_context_t (const void *base_, hb_paint_funcs_t *funcs_, void *paint_data_) + base (base_), funcs (funcs_), paint_data (paint_data_) + {} + + void push_transform (float xx, float yx, + float xy, float yy, + float x0, float y0) + { + funcs->push_transform (paint_data, xx, yx, xy, yy, x0, y0); + } + + void pop_transform () + { + funcs->pop_transform (paint_data); + } + + void push_clip (hb_codepoint_t gid) + { + funcs->push_clip (paint_data, gid); + } + + void pop_clip () + { + funcs->pop_clip (paint_data); + } + + void solid (unsigned int color_index) + { + funcs->solid (paint_data, color_index); + } + + void linear_gradient (hb_color_line *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2) + { + funcs->linear_gradient (paint_data, + color_line, x0, y0, x1, y1, x2, y2); + } + + void radial_gradient (hb_color_line *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1) + { + funcs->radial_gradient (paint_data, + color_line, x0, y0, r0, x1, y1, r1); + } + + void sweep_gradient (hb_color_line *color_line, + float x0, float y0, + float start_angle, float end_angle)' + { + funcs->sweep_gradient (paint_data, + color_line, x0, y0, start_angle, end_angle); + } + + void push_group () + { + funcs->push_group (paint_data); + } + + void pop_group_and_composite (hb_paint_composite_mode_t mode) + { + funcs->pop_group_and_composite (paint_data, mode); + } +} + +HB_INTERNAL void PaintColrLayers::paint (hb_colrv1_paint_context_t *c) const +{ + const COLR *colr_table = c->get_colr_table (); + const LayerList &paint_offset_lists = colr_table->get_layerList (); + for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++) + { + const Paint &paint = std::addressof (paint_offset_lists) + paint_offset_lists[i]; + c->push_group (); + paint.dispatch (c); + c->pop_group_and_composite (HB_PAINT_COMPOSITE_MODE_OVER); + } +} + +HB_INTERNAL void PaintGlyph::paint (hb_colrv1_paint_context_t *c) const +{ + c->push_clip (gid); + (this+paint).dispatch (c); + c->pop_clip (); +} + +HB_INTERNAL void PaintColrGlyph::paint (hb_colrv1_paint_context_t *c) const +{ + const COLR *colr_table = c->get_colr_table (); + const BaseGlyphPaintRecord* baseglyph_paintrecord = colr_table->get_base_glyph_paintrecord (gid); + if (!baseglyph_paintrecord) return; + + c->push_clip (gid); + const BaseGlyphList &baseglyph_list = colr_table->get_baseglyphList (); + (&baseglyph_list+baseglyph_paintrecord->paint).dispatch (c); + c->pop_clip (); +} + +template class Var> +HB_INTERNAL void PaintTransform::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_transform (transform.xx, transform.yx, + transform.xy, transform.yy, + transform.dx, transform.dy); + (this+src).dispatch (c); + c->pop_transform (); +} + +HB_INTERNAL void PaintTranslate::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_transform (0, 0, 0, 0, dx, dy); + (this+src).dispatch (c); + c->pop_transform (); +} + +HB_INTERNAL void PaintScale::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_transform (scaleX, 0, 0, scaleY, 0, 0); + (this+src).dispatch (c); + c->pop_transform (); +} + +HB_INTERNAL void PaintScaleAroundCenter::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_transform (0, 0, 0, 0, - centerX, - centerY); + c->push_transform (scaleX, 0, 0, scaleY, 0, 0); + c->push_transform (0, 0, 0, 0, centerX, centerY); + (this+src).dispatch (c); + c->pop_transform (); + c->pop_transform (); + c->pop_transform (); +} + +HB_INTERNAL void PaintScaleUniform::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_transform (scale, 0, 0, scale, 0, 0); + (this+src).dispatch (c); + c->pop_transform (); +} + +HB_INTERNAL void PaintScaleUniformAroundCenter::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_transform (0, 0, 0, 0, - centerX, - centerY); + c->push_transform (scale, 0, 0, scale, 0, 0); + c->push_transform (0, 0, 0, 0, centerX, centerY); + (this+src).dispatch (c); + c->pop_transform (); + c->pop_transform (); + c->pop_transform (); +} + +HB_INTERNAL void PaintRotate::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_transform (cos (angle), sin (angle), + - sin (angle), cos (angle), + 0, 0); + (this+src).dispatch (c); + c->pop_transform (); +} + +HB_INTERNAL void PaintRotateAroundCenter::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_transform (0, 0, 0, 0, - centerX, - centerY); + c->push_transform (cos (angle), sin (angle), + - sin (angle), cos (angle), + 0, 0); + c->push_transform (0, 0, 0, 0, centerX, centerY); + (this+src).dispatch (c); + c->pop_transform (); + c->pop_transform (); + c->pop_transform (); +} + +HB_INTERNAL void PaintSkew::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_transform (1, tan (ySkewAngle), + - tan (xSkewAngle), 1, + 0, 0); + (this+src).dispatch (c); + c->pop_transform (); +} + +HB_INTERNAL void PaintSkewAroundCenter::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_transform (0, 0, 0, 0, - centerX, - centerY); + c->push_transform (1, tan (ySkewAngle), + - tan (xSkewAngle), 1, + 0, 0); + c->push_transform (0, 0, 0, 0, centerX, centerY); + (this+src).dispatch (c); + c->pop_transform (); + c->pop_transform (); + c->pop_transform (); +} + +HB_INTERNAL void PaintComposite::paint (hb_colrv1_paint_context_t* c) const +{ + c->push_group (); + (this+src).dispatch (c); + c->push_group (); + (this+backdrop).dispatch (c); + c->pop_group_and_composite (mode); + c->pop_group_and_composite (HB_PAINT_COMPOSITE_MODE_OVER); +} + +HB_INTERNAL void PaintSolid::paint (hb_colrv1_paint_context_t *c) const +{ + c->solid (paletteIndex); +} + +HB_INTERNAL void PaintLinearGradient::paint (hb_colrv1_paint_context_t *c) const +{ + c->linear_gradient (color_line, x0, y0, x1, y1, x2, y2); +} + +HB_INTERNAL void PaintRadialGradient::paint (hb_colrv1_paint_context_t *c) const +{ + c->radial_gradient (color_line, x0, y0, radius0, x1, y1, radius1); +} + +HB_INTERNAL void PaintSweepGradient::paint (hb_colrv1_paint_context_t *c) const +{ + c->sweep_gradient (color_line, centerX, centerY, startAngle, endAngle); +} + +} /* namespace OT */ + + +#endif /* HB_OT_COLR_COLRV1_CLOSURE_HH */ diff --git a/src/hb-ot-color-sbix-table.hh b/src/hb-ot-color-sbix-table.hh index 1d1d2e89d..10d4b3a85 100644 --- a/src/hb-ot-color-sbix-table.hh +++ b/src/hb-ot-color-sbix-table.hh @@ -30,6 +30,7 @@ #include "hb-open-type.hh" #include "hb-ot-layout-common.hh" +#include "hb-paint.hh" /* * sbix -- Standard Bitmap Graphics @@ -213,10 +214,11 @@ struct sbix bool get_extents (hb_font_t *font, hb_codepoint_t glyph, - hb_glyph_extents_t *extents) const + hb_glyph_extents_t *extents, + bool scale = true) const { /* We only support PNG right now, and following function checks type. */ - return get_png_extents (font, glyph, extents); + return get_png_extents (font, glyph, extents, scale); } hb_blob_t *reference_png (hb_font_t *font, @@ -231,6 +233,37 @@ struct sbix num_glyphs, available_ppem); } + bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + if (!has_data ()) + return false; + + int x_offset = 0, y_offset = 0; + unsigned int strike_ppem = 0; + hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem); + hb_glyph_extents_t extents; + hb_glyph_extents_t pixel_extents; + + if (blob == hb_blob_get_empty ()) + return false; + + if (!hb_font_get_glyph_extents (font, glyph, &extents)) + return false; + + if (unlikely (!get_extents (font, glyph, &pixel_extents, false))) + return false; + + funcs->image (data, + blob, + pixel_extents.width, -pixel_extents.height, + HB_PAINT_IMAGE_FORMAT_PNG, + font->slant_xy, + &extents); + + hb_blob_destroy (blob); + return true; + } + private: const SBIXStrike &choose_strike (hb_font_t *font) const @@ -285,7 +318,8 @@ struct sbix bool get_png_extents (hb_font_t *font, hb_codepoint_t glyph, - hb_glyph_extents_t *extents) const + hb_glyph_extents_t *extents, + bool scale = true) const { /* Following code is safe to call even without data. * But faster to short-circuit. */ @@ -310,7 +344,7 @@ struct sbix extents->height = -1 * png.IHDR.height; /* Convert to font units. */ - if (strike_ppem) + if (strike_ppem && scale) { float scale = font->face->get_upem () / (float) strike_ppem; extents->x_bearing = roundf (extents->x_bearing * scale); @@ -318,15 +352,9 @@ struct sbix extents->width = roundf (extents->width * scale); extents->height = roundf (extents->height * scale); } - else - { - extents->x_bearing = extents->x_bearing; - extents->y_bearing = extents->y_bearing; - extents->width = extents->width; - extents->height = extents->height; - } - font->scale_glyph_extents (extents); + if (scale) + font->scale_glyph_extents (extents); hb_blob_destroy (blob); diff --git a/src/hb-ot-color-svg-table.hh b/src/hb-ot-color-svg-table.hh index fc649f100..042faa384 100644 --- a/src/hb-ot-color-svg-table.hh +++ b/src/hb-ot-color-svg-table.hh @@ -26,6 +26,8 @@ #define HB_OT_COLOR_SVG_TABLE_HH #include "hb-open-type.hh" +#include "hb-blob.hh" +#include "hb-paint.hh" /* * SVG -- SVG (Scalable Vector Graphics) @@ -91,8 +93,31 @@ struct SVG bool has_data () const { return table->has_data (); } + bool paint_glyph (hb_font_t *font HB_UNUSED, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + if (!has_data ()) + return false; + + hb_blob_t *blob = reference_blob_for_glyph (glyph); + + if (blob == hb_blob_get_empty ()) + return false; + + funcs->image (data, + blob, + 0, 0, + HB_PAINT_IMAGE_FORMAT_SVG, + font->slant_xy, + nullptr); + + hb_blob_destroy (blob); + return true; + } + private: hb_blob_ptr_t table; + public: + DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t)); }; const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const diff --git a/src/hb-ot-color.cc b/src/hb-ot-color.cc index 696ca3e17..4a2707e5a 100644 --- a/src/hb-ot-color.cc +++ b/src/hb-ot-color.cc @@ -167,6 +167,10 @@ hb_ot_color_palette_get_flags (hb_face_t *face, * for allocating a buffer of suitable size before calling * hb_ot_color_palette_get_colors() a second time. * + * The RGBA values in the palette are unpremultiplied. See the + * OpenType spec [CPAL](https://learn.microsoft.com/en-us/typography/opentype/spec/cpal) + * section for details. + * * Return value: the total number of colors in the palette * * Since: 2.1.0 @@ -190,7 +194,8 @@ hb_ot_color_palette_get_colors (hb_face_t *face, * hb_ot_color_has_layers: * @face: #hb_face_t to work upon * - * Tests whether a face includes any `COLR` color layers. + * Tests whether a face includes a `COLR` table + * with data according to COLRv0. * * Return value: `true` if data found, `false` otherwise * @@ -199,7 +204,24 @@ hb_ot_color_palette_get_colors (hb_face_t *face, hb_bool_t hb_ot_color_has_layers (hb_face_t *face) { - return face->table.COLR->has_data (); + return face->table.COLR->has_v0_data (); +} + +/** + * hb_ot_color_has_paint: + * @face: #hb_face_t to work upon + * + * Tests where a face includes a `COLR` table + * with data according to COLRv1. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: REPLACEME + */ +hb_bool_t +hb_ot_color_has_paint (hb_face_t *face) +{ + return face->table.COLR->has_v1_data (); } /** diff --git a/src/hb-ot-color.h b/src/hb-ot-color.h index d11e07e23..f29d7cf5f 100644 --- a/src/hb-ot-color.h +++ b/src/hb-ot-color.h @@ -120,6 +120,11 @@ hb_ot_color_glyph_get_layers (hb_face_t *face, unsigned int *layer_count, /* IN/OUT. May be NULL. */ hb_ot_color_layer_t *layers /* OUT. May be NULL. */); +/* COLRv1 */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_paint (hb_face_t *face); + /* * SVG */ diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 840510342..270710993 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -46,6 +46,7 @@ #include "hb-ot-color-cbdt-table.hh" #include "hb-ot-color-sbix-table.hh" #include "hb-ot-color-colr-table.hh" +#include "hb-ot-color-svg-table.hh" /** @@ -424,11 +425,11 @@ hb_ot_get_font_v_extents (hb_font_t *font, #ifndef HB_NO_DRAW static void -hb_ot_get_glyph_shape (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data, - void *user_data) +hb_ot_draw_glyph (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data) { hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); if (font->face->table.glyf->get_path (font, glyph, draw_session)) return; @@ -439,6 +440,32 @@ hb_ot_get_glyph_shape (hb_font_t *font, } #endif +#ifndef HB_NO_PAINT +static void +hb_ot_paint_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) +{ +#ifndef HB_NO_COLOR + if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return; + if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return; +#ifndef HB_NO_OT_FONT_BITMAP + if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return; + if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return; +#endif +#endif + if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; +#ifndef HB_NO_CFF + if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; + if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; +#endif +} +#endif + static inline void free_static_ot_funcs (); static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t @@ -462,7 +489,11 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t xmax; } + + float xmin = 0.f; + float ymin = 0.f; + float xmax = -1.f; + float ymax = -1.f; +} hb_extents_t; + +typedef struct hb_transform_t +{ + hb_transform_t () {} + hb_transform_t (float xx, float yx, + float xy, float yy, + float x0, float y0) : + xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} + + void multiply (const hb_transform_t &o) + { + /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */ + hb_transform_t r; + + r.xx = o.xx * xx + o.yx * xy; + r.yx = o.xx * yx + o.yx * yy; + + r.xy = o.xy * xx + o.yy * xy; + r.yy = o.xy * yx + o.yy * yy; + + r.x0 = o.x0 * xx + o.y0 * xy + x0; + r.y0 = o.x0 * yx + o.y0 * yy + y0; + + *this = r; + } + + void transform_distance (float &dx, float &dy) + { + float new_x = xx * dx + xy * dy; + float new_y = yx * dx + yy * dy; + dx = new_x; + dy = new_y; + } + + void transform_point (float &x, float &y) + { + transform_distance (x, y); + x += x0; + y += y0; + } + + void transform_extents (hb_extents_t extents) + { + float quad_x[4], quad_y[4]; + + quad_x[0] = extents.xmin; + quad_y[0] = extents.ymin; + quad_x[1] = extents.xmin; + quad_y[1] = extents.ymax; + quad_x[2] = extents.xmax; + quad_y[2] = extents.ymin; + quad_x[3] = extents.xmax; + quad_y[3] = extents.ymax; + + for (unsigned i = 0; i < 4; i++) + transform_point (quad_x[i], quad_y[i]); + + extents.xmin = extents.xmax = quad_x[0]; + extents.ymin = extents.ymax = quad_y[0]; + + for (unsigned i = 1; i < 4; i++) + { + extents.xmin = hb_min (extents.xmin, quad_x[i]); + extents.ymin = hb_min (extents.ymin, quad_y[i]); + extents.xmax = hb_max (extents.xmax, quad_x[i]); + extents.ymax = hb_max (extents.ymax, quad_y[i]); + } + } + + float xx = 1.f; + float yx = 0.f; + float xy = 0.f; + float yy = 1.f; + float x0 = 0.f; + float y0 = 0.f; +} hb_transform_t; + +typedef struct hb_bounds_t +{ + enum status_t { + EMPTY, + BOUNDED, + UNBOUNDED, + }; + + hb_bounds_t (status_t status) : status (status) {} + hb_bounds_t (const hb_extents_t &extents) : + status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} + + status_t status; + hb_extents_t extents; +} hb_bounds_t; + +typedef struct hb_paint_extents_context_t hb_paint_extents_context_t; + +struct hb_paint_extents_context_t { + + hb_paint_extents_context_t () + { + clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED}); + groups.push (hb_bounds_t{hb_bounds_t::EMPTY}); + transforms.push (hb_transform_t{}); + } + + hb_extents_t get_extents () + { + return groups.tail().extents; + } + + bool is_bounded () + { + return groups.tail().status != hb_bounds_t::UNBOUNDED; + } + + void push_transform (const hb_transform_t &trans) + { + hb_transform_t r = transforms.tail (); + r.multiply (trans); + transforms.push (r); + } + + void pop_transform () + { + transforms.pop (); + } + + void push_clip (hb_extents_t extents) + { + /* Transform extents and push a new clip. */ + hb_transform_t &r = transforms.tail (); + r.transform_extents (extents); + + hb_bounds_t b {extents}; + clips.push (b); + } + + void pop_clip () + { + clips.pop (); + } + + void push_group () + { + groups.push (hb_bounds_t{hb_bounds_t::EMPTY}); + } + + void pop_group (hb_paint_composite_mode_t mode) + { + const hb_bounds_t src_bounds = groups.pop (); + hb_bounds_t &backdrop_bounds = groups.tail (); + + switch ((int) mode) + { + case HB_PAINT_COMPOSITE_MODE_CLEAR: + backdrop_bounds.status = hb_bounds_t::EMPTY; + break; + case HB_PAINT_COMPOSITE_MODE_SRC: + case HB_PAINT_COMPOSITE_MODE_SRC_OUT: + backdrop_bounds = src_bounds; + break; + case HB_PAINT_COMPOSITE_MODE_DEST: + case HB_PAINT_COMPOSITE_MODE_DEST_OUT: + break; + case HB_PAINT_COMPOSITE_MODE_SRC_IN: + case HB_PAINT_COMPOSITE_MODE_DEST_IN: + // Intersect + if (src_bounds.status == hb_bounds_t::EMPTY) + backdrop_bounds.status = hb_bounds_t::EMPTY; + else if (src_bounds.status == hb_bounds_t::BOUNDED) + { + if (backdrop_bounds.status == hb_bounds_t::UNBOUNDED) + backdrop_bounds = src_bounds; + else if (backdrop_bounds.status == hb_bounds_t::BOUNDED) + { + backdrop_bounds.extents.xmin = hb_max (backdrop_bounds.extents.xmin, src_bounds.extents.xmin); + backdrop_bounds.extents.ymin = hb_max (backdrop_bounds.extents.ymin, src_bounds.extents.ymin); + backdrop_bounds.extents.xmax = hb_min (backdrop_bounds.extents.xmax, src_bounds.extents.xmax); + backdrop_bounds.extents.ymax = hb_min (backdrop_bounds.extents.ymax, src_bounds.extents.ymax); + if (backdrop_bounds.extents.xmin >= backdrop_bounds.extents.xmax || + backdrop_bounds.extents.ymin >= backdrop_bounds.extents.ymax) + backdrop_bounds.status = hb_bounds_t::EMPTY; + } + } + break; + default: + // Union + if (src_bounds.status == hb_bounds_t::UNBOUNDED) + backdrop_bounds.status = hb_bounds_t::UNBOUNDED; + else if (src_bounds.status == hb_bounds_t::BOUNDED) + { + if (backdrop_bounds.status == hb_bounds_t::EMPTY) + backdrop_bounds = src_bounds; + else if (backdrop_bounds.status == hb_bounds_t::BOUNDED) + { + backdrop_bounds.extents.xmin = hb_min (backdrop_bounds.extents.xmin, src_bounds.extents.xmin); + backdrop_bounds.extents.ymin = hb_min (backdrop_bounds.extents.ymin, src_bounds.extents.ymin); + backdrop_bounds.extents.xmax = hb_max (backdrop_bounds.extents.xmax, src_bounds.extents.xmax); + backdrop_bounds.extents.ymax = hb_max (backdrop_bounds.extents.ymax, src_bounds.extents.ymax); + } + } + break; + } + } + + void paint () + { + /* Union current clip bounds with current group bounds. */ + const hb_bounds_t &clip = clips.tail (); + hb_bounds_t &group = groups.tail (); + + if (clip.status == hb_bounds_t::EMPTY) + return; // Shouldn't happen + + if (group.status == hb_bounds_t::UNBOUNDED) + return; + + if (group.status == hb_bounds_t::EMPTY) + { + group = clip; + return; + } + + /* Group is bounded now. Clip is not empty. */ + + if (clip.status == hb_bounds_t::UNBOUNDED) + { + group.status = hb_bounds_t::UNBOUNDED; + return; + } + + /* Both are bounded. Union. */ + group.extents.xmin = hb_min (group.extents.xmin, clip.extents.xmin); + group.extents.ymin = hb_min (group.extents.ymin, clip.extents.ymin); + group.extents.xmax = hb_max (group.extents.xmax, clip.extents.xmax); + group.extents.ymax = hb_max (group.extents.ymax, clip.extents.ymax); + } + + hb_vector_t clips; + hb_vector_t groups; + hb_vector_t transforms; +}; + +static void +hb_paint_extents_push_transform (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->push_transform (hb_transform_t {xx, yx, xy, yy, dx, dy}); +} + +static void +hb_paint_extents_pop_transform (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->pop_transform (); +} + +static void +add_point (hb_extents_t *extents, + float x, float y) +{ + if (extents->xmax < extents->xmin) + { + extents->xmin = extents->xmax = x; + extents->ymin = extents->ymax = y; + } + else + { + extents->xmin = hb_min (extents->xmin, x); + extents->ymin = hb_min (extents->ymin, y); + extents->xmax = hb_max (extents->xmax, x); + extents->ymax = hb_max (extents->ymax, y); + } +} + +static void +move_to (hb_draw_funcs_t *dfuncs, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *) +{ + hb_extents_t *extents = (hb_extents_t *)data; + add_point (extents, to_x, to_y); +} + +static void +line_to (hb_draw_funcs_t *dfuncs, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *) +{ + hb_extents_t *extents = (hb_extents_t *)data; + add_point (extents, to_x, to_y); +} + +static void +cubic_to (hb_draw_funcs_t *dfuncs, + void *data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *) +{ + hb_extents_t *extents = (hb_extents_t *)data; + add_point (extents, control1_x, control1_y); + add_point (extents, control2_x, control2_y); + add_point (extents, to_x, to_y); +} + +static void +close_path (hb_draw_funcs_t *dfuncs, + void *data, + hb_draw_state_t *st, + void *) +{ +} + +static hb_draw_funcs_t * +hb_draw_extent_get_funcs () +{ + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + hb_draw_funcs_set_move_to_func (funcs, move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, line_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, close_path, nullptr, nullptr); + return funcs; +} + +static void +hb_paint_extents_push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + hb_extents_t extents; + hb_draw_funcs_t *draw_extent_funcs = hb_draw_extent_get_funcs (); + hb_font_draw_glyph (font, glyph, draw_extent_funcs, &extents); + hb_draw_funcs_destroy (draw_extent_funcs); + c->push_clip (extents); +} + +static void +hb_paint_extents_push_clip_rectangle (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + float xmin, float ymin, float xmax, float ymax, + void *user_data) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + hb_extents_t extents = {xmin, ymin, xmax, ymax}; + c->push_clip (extents); +} + +static void +hb_paint_extents_pop_clip (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->pop_clip (); +} + +static void +hb_paint_extents_push_group (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->push_group (); +} + +static void +hb_paint_extents_pop_group (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->pop_group (mode); +} + +static void +hb_paint_extents_paint_image (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_blob_t *blob HB_UNUSED, + unsigned int width HB_UNUSED, + unsigned int height HB_UNUSED, + hb_tag_t format HB_UNUSED, + float slant HB_UNUSED, + hb_glyph_extents_t *glyph_extents, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + hb_extents_t extents = {(float) glyph_extents->x_bearing, + (float) glyph_extents->y_bearing + glyph_extents->height, + (float) glyph_extents->x_bearing + glyph_extents->width, + (float) glyph_extents->y_bearing}; + c->push_clip (extents); + c->paint (); + c->pop_clip (); +} + +static void +hb_paint_extents_paint_color (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_bool_t use_foreground HB_UNUSED, + hb_color_t color HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_extents_paint_linear_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float x0 HB_UNUSED, float y0 HB_UNUSED, + float x1 HB_UNUSED, float y1 HB_UNUSED, + float x2 HB_UNUSED, float y2 HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_extents_paint_radial_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float x0 HB_UNUSED, float y0 HB_UNUSED, float r0 HB_UNUSED, + float x1 HB_UNUSED, float y1 HB_UNUSED, float r1 HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_extents_paint_sweep_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float cx HB_UNUSED, float cy HB_UNUSED, + float start_angle HB_UNUSED, + float end_angle HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->paint (); +} + +static inline hb_paint_funcs_t * +hb_paint_extents_get_funcs () +{ + hb_paint_funcs_t *funcs = hb_paint_funcs_create (); + + hb_paint_funcs_set_push_transform_func (funcs, hb_paint_extents_push_transform, nullptr, nullptr); + hb_paint_funcs_set_pop_transform_func (funcs, hb_paint_extents_pop_transform, nullptr, nullptr); + hb_paint_funcs_set_push_clip_glyph_func (funcs, hb_paint_extents_push_clip_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_rectangle_func (funcs, hb_paint_extents_push_clip_rectangle, nullptr, nullptr); + hb_paint_funcs_set_pop_clip_func (funcs, hb_paint_extents_pop_clip, nullptr, nullptr); + hb_paint_funcs_set_push_group_func (funcs, hb_paint_extents_push_group, nullptr, nullptr); + hb_paint_funcs_set_pop_group_func (funcs, hb_paint_extents_pop_group, nullptr, nullptr); + hb_paint_funcs_set_color_func (funcs, hb_paint_extents_paint_color, nullptr, nullptr); + hb_paint_funcs_set_image_func (funcs, hb_paint_extents_paint_image, nullptr, nullptr); + hb_paint_funcs_set_linear_gradient_func (funcs, hb_paint_extents_paint_linear_gradient, nullptr, nullptr); + hb_paint_funcs_set_radial_gradient_func (funcs, hb_paint_extents_paint_radial_gradient, nullptr, nullptr); + hb_paint_funcs_set_sweep_gradient_func (funcs, hb_paint_extents_paint_sweep_gradient, nullptr, nullptr); + + return funcs; +} + +#endif /* HB_PAINT_EXTENTS_HH */ diff --git a/src/hb-paint.cc b/src/hb-paint.cc new file mode 100644 index 000000000..9ae01a235 --- /dev/null +++ b/src/hb-paint.cc @@ -0,0 +1,429 @@ +/* + * Copyright © 2022 Matthias Clasen + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_PAINT + +#include "hb-paint.hh" + +/** + * SECTION: hb-paint + * @title: hb-paint + * @short_description: Glyph painting + * @include: hb.h + * + * Functions for painting glyphs. + * + * The main purpose of these functions is to paint (extract) color glyph layers + * from the COLRv1 table, but the API works for drawing ordinary outlines and + * images as well. + * + * The #hb_paint_funcs_t struct can be used with hb_font_paint_glyph(). + **/ + +static void +hb_paint_push_transform_nil (hb_paint_funcs_t *funcs, void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data) {} + +static void +hb_paint_pop_transform_nil (hb_paint_funcs_t *funcs, void *paint_data, + void *user_data) {} + +static void +hb_paint_push_clip_glyph_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data) {} + +static void +hb_paint_push_clip_rectangle_nil (hb_paint_funcs_t *funcs, void *paint_data, + float xmin, float ymin, float xmax, float ymax, + void *user_data) {} + +static void +hb_paint_pop_clip_nil (hb_paint_funcs_t *funcs, void *paint_data, + void *user_data) {} + +static void +hb_paint_color_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_bool_t is_foreground, + hb_color_t color, + void *user_data) {} + +static void +hb_paint_image_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_blob_t *image, + unsigned int width, + unsigned int height, + hb_tag_t format, + float slant_xy, + hb_glyph_extents_t *extents, + void *user_data) {} + +static void +hb_paint_linear_gradient_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2, + void *user_data) {} + +static void +hb_paint_radial_gradient_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1, + void *user_data) {} + +static void +hb_paint_sweep_gradient_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, + float end_angle, + void *user_data) {} + +static void +hb_paint_push_group_nil (hb_paint_funcs_t *funcs, void *paint_data, + void *user_data) {} + +static void +hb_paint_pop_group_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data) {} + +static bool +_hb_paint_funcs_set_preamble (hb_paint_funcs_t *funcs, + bool func_is_null, + void **user_data, + hb_destroy_func_t *destroy) +{ + if (hb_object_is_immutable (funcs)) + { + if (*destroy) + (*destroy) (*user_data); + return false; + } + + if (func_is_null) + { + if (*destroy) + (*destroy) (*user_data); + *destroy = nullptr; + *user_data = nullptr; + } + + return true; +} + +static bool +_hb_paint_funcs_set_middle (hb_paint_funcs_t *funcs, + void *user_data, + hb_destroy_func_t destroy) +{ + if (user_data && !funcs->user_data) + { + funcs->user_data = (decltype (funcs->user_data)) hb_calloc (1, sizeof (*funcs->user_data)); + if (unlikely (!funcs->user_data)) + goto fail; + } + if (destroy && !funcs->destroy) + { + funcs->destroy = (decltype (funcs->destroy)) hb_calloc (1, sizeof (*funcs->destroy)); + if (unlikely (!funcs->destroy)) + goto fail; + } + + return true; + +fail: + if (destroy) + (destroy) (user_data); + return false; +} + +#define HB_PAINT_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_paint_funcs_set_##name##_func (hb_paint_funcs_t *funcs, \ + hb_paint_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (!_hb_paint_funcs_set_preamble (funcs, !func, &user_data, &destroy)) \ + return; \ + \ + if (funcs->destroy && funcs->destroy->name) \ + funcs->destroy->name (!funcs->user_data ? nullptr : funcs->user_data->name);\ + \ + if (!_hb_paint_funcs_set_middle (funcs, user_data, destroy)) \ + return; \ + \ + if (func) \ + funcs->func.name = func; \ + else \ + funcs->func.name = hb_paint_##name##_nil; \ + \ + if (funcs->user_data) \ + funcs->user_data->name = user_data; \ + if (funcs->destroy) \ + funcs->destroy->name = destroy; \ +} + +HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + +/** + * hb_paint_funcs_create: + * + * Creates a new #hb_paint_funcs_t structure of paint functions. + * + * The initial reference count of 1 should be released with hb_paint_funcs_destroy() + * when you are done using the #hb_paint_funcs_t. This function never returns + * `NULL`. If memory cannot be allocated, a special singleton #hb_paint_funcs_t + * object will be returned. + * + * Returns value: (transfer full): the paint-functions structure + * + * Since: REPLACEME + */ +hb_paint_funcs_t * +hb_paint_funcs_create () +{ + hb_paint_funcs_t *funcs; + if (unlikely (!(funcs = hb_object_create ()))) + return const_cast (&Null (hb_paint_funcs_t)); + + funcs->func = Null (hb_paint_funcs_t).func; + + return funcs; +} + +DEFINE_NULL_INSTANCE (hb_paint_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + { +#define HB_PAINT_FUNC_IMPLEMENT(name) hb_paint_##name##_nil, + HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + } +}; + +/** + * hb_paint_funcs_get_empty: + * + * Fetches the singleton empty paint-functions structure. + * + * Return value: (transfer full): The empty paint-functions structure + * + * Since: REPLACEME + **/ +hb_paint_funcs_t * +hb_paint_funcs_get_empty () +{ + return const_cast (&Null (hb_paint_funcs_t)); +} + +/** + * hb_paint_funcs_reference: (skip) + * @funcs: The paint-functions structure + * + * Increases the reference count on a paint-functions structure. + * + * This prevents @funcs from being destroyed until a matching + * call to hb_paint_funcs_destroy() is made. + * + * Return value: The paint-functions structure + * + * Since: REPLACEME + */ +hb_paint_funcs_t * +hb_paint_funcs_reference (hb_paint_funcs_t *funcs) +{ + return hb_object_reference (funcs); +} + +/** + * hb_paint_funcs_destroy: (skip) + * @funcs: The paint-functions structure + * + * Decreases the reference count on a paint-functions structure. + * + * When the reference count reaches zero, the structure + * is destroyed, freeing all memory. + * + * Since: REPLACEME + */ +void +hb_paint_funcs_destroy (hb_paint_funcs_t *funcs) +{ + if (!hb_object_destroy (funcs)) return; + + if (funcs->destroy) + { +#define HB_PAINT_FUNC_IMPLEMENT(name) \ + if (funcs->destroy->name) funcs->destroy->name (!funcs->user_data ? nullptr : funcs->user_data->name); + HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + } + + hb_free (funcs->destroy); + hb_free (funcs->user_data); + hb_free (funcs); +} + +/** + * hb_paint_funcs_set_user_data: (skip) + * @funcs: The paint-functions structure + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified paint-functions structure. + * + * Return value: `true` if success, `false` otherwise + * + * Since: REPLACEME + **/ +hb_bool_t +hb_paint_funcs_set_user_data (hb_paint_funcs_t *funcs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (funcs, key, data, destroy, replace); +} + +/** + * hb_paint_funcs_get_user_data: (skip) + * @funcs: The paint-functions structure + * @key: The user-data key to query + * + * Fetches the user-data associated with the specified key, + * attached to the specified paint-functions structure. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: REPLACEME + **/ +void * +hb_paint_funcs_get_user_data (const hb_paint_funcs_t *funcs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (funcs, key); +} + +/** + * hb_paint_funcs_make_immutable: + * @funcs: The paint-functions structure + * + * Makes a paint-functions structure immutable. + * + * After this call, all attempts to set one of the callbacks + * on @funcs will fail. + * + * Since: REPLACEME + */ +void +hb_paint_funcs_make_immutable (hb_paint_funcs_t *funcs) +{ + if (hb_object_is_immutable (funcs)) + return; + + hb_object_make_immutable (funcs); +} + +/** + * hb_paint_funcs_is_immutable: + * @funcs: The paint-functions structure + * + * Tests whether a paint-functions structure is immutable. + * + * Return value: `true` if @funcs is immutable, `false` otherwise + * + * Since: REPLACEME + */ +hb_bool_t +hb_paint_funcs_is_immutable (hb_paint_funcs_t *funcs) +{ + return hb_object_is_immutable (funcs); +} + + +/** + * hb_color_line_get_color_stops: + * @color_line: a #hb_color_line_t object + * @start: the index of the first color stop to return + * @count: (inout) (optional): Input = the maximum number of feature tags to return; + * Output = the actual number of feature tags returned (may be zero) + * @color_stops: (out) (array length=count) (optional): Array of #hb_color_stop_t to populate + * + * Fetches a list of color stops from the given color line object. + * + * Note that due to variations being applied, the returned color stops + * may be out of order. It is the callers responsibility to ensure that + * color stops are sorted by their offset before they are used. + * + * Return value: the total number of color stops in @cl + * + * Since: REPLACEME + */ +unsigned int +hb_color_line_get_color_stops (hb_color_line_t *color_line, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops) +{ + return color_line->get_color_stops (color_line, + color_line->data, + start, count, + color_stops, + color_line->get_color_stops_user_data); +} + +/** + * hb_color_line_get_extend: + * @color_line: a #hb_color_line_t object + * + * Fetches the extend mode of the color line object. + * + * Since: REPLACEME + */ +hb_paint_extend_t +hb_color_line_get_extend (hb_color_line_t *color_line) +{ + return color_line->get_extend (color_line, + color_line->data, + color_line->get_extend_user_data); +} + + +#endif diff --git a/src/hb-paint.h b/src/hb-paint.h new file mode 100644 index 000000000..a76d5392b --- /dev/null +++ b/src/hb-paint.h @@ -0,0 +1,767 @@ +/* + * Copyright © 2022 Matthias Clasen + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_PAINT_H +#define HB_PAINT_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + +/** + * hb_paint_funcs_t: + * + * Glyph paint callbacks. + * + * The callbacks assume that the caller maintains a stack + * of current transforms, clips and intermediate surfaces, + * as evidenced by the pairs of push/pop callbacks. The + * push/pop calls will be properly nested, so it is fine + * to store the different kinds of object on a single stack. + * + * Not all callbacks are required for all kinds of glyphs. + * For rendering COLRv0 or non-color outline glyphs, the + * gradient callbacks are not needed, and the composite + * callback only needs to handle simple alpha compositing + * (#HB_PAINT_COMPOSITE_MODE_SRC_OVER). + * + * The paint-image callback is only needed for glyphs + * with image blobs in the CBDT, sbix or SVG tables. + * + * Since: REPLACEME + **/ +typedef struct hb_paint_funcs_t hb_paint_funcs_t; + +HB_EXTERN hb_paint_funcs_t * +hb_paint_funcs_create (void); + +HB_EXTERN hb_paint_funcs_t * +hb_paint_funcs_get_empty (void); + +HB_EXTERN hb_paint_funcs_t * +hb_paint_funcs_reference (hb_paint_funcs_t *funcs); + +HB_EXTERN void +hb_paint_funcs_destroy (hb_paint_funcs_t *funcs); + +HB_EXTERN hb_bool_t +hb_paint_funcs_set_user_data (hb_paint_funcs_t *funcs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_paint_funcs_get_user_data (const hb_paint_funcs_t *funcs, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_paint_funcs_make_immutable (hb_paint_funcs_t *funcs); + +HB_EXTERN hb_bool_t +hb_paint_funcs_is_immutable (hb_paint_funcs_t *funcs); + +/** + * hb_paint_push_transform_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @xx: xx component of the transform matrix + * @yx: yx component of the transform matrix + * @xy: xy component of the transform matrix + * @yy: yy component of the transform matrix + * @dx: dx component of the transform matrix + * @dy: dy component of the transform matrix + * @user_data: User data pointer passed to hb_paint_funcs_set_push_transform_func() + * + * A virtual method for the #hb_paint_funcs_t to apply + * a transform to subsequent paint calls. + * + * This transform is applied after the current transform, + * and remains in effect until a matching call to + * the #hb_paint_funcs_pop_transform_func_t vfunc. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_push_transform_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data); + +/** + * hb_paint_pop_transform_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @user_data: User data pointer passed to hb_paint_funcs_set_pop_transform_func() + * + * A virtual method for the #hb_paint_funcs_t to undo + * the effect of a prior call to the #hb_paint_funcs_push_transform_func_t + * vfunc. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_pop_transform_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data); + +/** + * hb_paint_push_clip_glyph_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @glyph: the glyph ID + * @font: the font + * @user_data: User data pointer passed to hb_paint_funcs_set_push_clip_glyph_func() + * + * A virtual method for the #hb_paint_funcs_t to clip + * subsequent paint calls to the outline of a glyph. + * + * The coordinates of the glyph outline are interpreted according + * to the current transform. + * + * This clip is applied in addition to the current clip, + * and remains in effect until a matching call to + * the #hb_paint_funcs_pop_clip_func_t vfunc. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_push_clip_glyph_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data); + +/** + * hb_paint_push_clip_rectangle_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @xmin: min X for the rectangle + * @ymin: min Y for the rectangle + * @xmax: max X for the rectangle + * @ymax: max Y for the rectangle + * @user_data: User data pointer passed to hb_paint_funcs_set_push_clip_rectangle_func() + * + * A virtual method for the #hb_paint_funcs_t to clip + * subsequent paint calls to a rectangle. + * + * The coordinates of the rectangle are interpreted according + * to the current transform. + * + * This clip is applied in addition to the current clip, + * and remains in effect until a matching call to + * the #hb_paint_funcs_pop_clip_func_t vfunc. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_push_clip_rectangle_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + float xmin, float ymin, + float xmax, float ymax, + void *user_data); + +/** + * hb_paint_pop_clip_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @user_data: User data pointer passed to hb_paint_funcs_set_pop_clip_func() + * + * A virtual method for the #hb_paint_funcs_t to undo + * the effect of a prior call to the #hb_paint_funcs_push_clip_glyph_func_t + * or #hb_paint_funcs_push_clip_rectangle_func_t vfuncs. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_pop_clip_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data); + +/** + * hb_paint_color_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @is_foreground: whether the color is the foreground + * @color: The color to use, unpremultiplied + * @user_data: User data pointer passed to hb_paint_funcs_set_color_func() + * + * A virtual method for the #hb_paint_funcs_t to paint a + * color everywhere within the current clip. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_color_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_bool_t is_foreground, + hb_color_t color, + void *user_data); + +/** + * HB_PAINT_IMAGE_FORMAT_PNG: + * + * Tag identifying PNG images in #hb_paint_image_func_t callbacks. + * + * Since: REPLACEME + */ +#define HB_PAINT_IMAGE_FORMAT_PNG HB_TAG('p','n','g',' ') + +/** + * HB_PAINT_IMAGE_FORMAT_SVG: + * + * Tag identifying SVG images in #hb_paint_image_func_t callbacks. + * + * Since: REPLACEME + */ +#define HB_PAINT_IMAGE_FORMAT_SVG HB_TAG('s','v','g',' ') + +/** + * HB_PAINT_IMAGE_FORMAT_BGRA: + * + * Tag identifying raw pixel-data images in #hb_paint_image_func_t callbacks. + * The data is in BGRA pre-multiplied sRGBA color-space format. + * + * Since: REPLACEME + */ +#define HB_PAINT_IMAGE_FORMAT_BGRA HB_TAG('B','G','R','A') + +/** + * hb_paint_image_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @image: the image data + * @width: width of the raster image in pixels, or 0 + * @height: height of the raster image in pixels, or 0 + * @format: the image format as a tag + * @slant: the synthetic slant ratio to be applied to the image during rendering + * @extents: (nullable): glyph extents for desired rendering + * @user_data: User data pointer passed to hb_paint_funcs_set_image_func() + * + * A virtual method for the #hb_paint_funcs_t to paint a glyph image. + * + * This method is called for glyphs with image blobs in the CBDT, + * sbix or SVG tables. The @format identifies the kind of data that + * is contained in @image. Possible values include #HB_PAINT_IMAGE_FORMAT_PNG + * #HB_PAINT_IMAGE_FORMAT_SVG and #HB_PAINT_IMAGE_FORMAT_BGRA. + * + * The image dimensions and glyph extents are provided if available, + * and should be used to size and position the image. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_image_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_blob_t *image, + unsigned int width, + unsigned int height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents, + void *user_data); + +/** + * hb_color_stop_t: + * @offset: the offset of the color stop + * @is_foreground: whether the color is the foreground + * @color: the color, unpremultiplied + * + * Information about a color stop on a color line. + * + * Color lines typically have offsets ranging between 0 and 1, + * but that is not required. + * + * Note: despite @color being unpremultiplied here, interpolation in + * gradients shall happen in premultiplied space. See the OpenType spec + * [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details. + * + * Since: REPLACEME + */ +typedef struct { + float offset; + hb_bool_t is_foreground; + hb_color_t color; +} hb_color_stop_t; + +/** + * hb_paint_extend_t: + * + * The values of this enumeration determine how color values + * outside the minimum and maximum defined offset on a #hb_color_line_t + * are determined. + * + * See the OpenType spec [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details. + */ +typedef enum { + HB_PAINT_EXTEND_PAD, + HB_PAINT_EXTEND_REPEAT, + HB_PAINT_EXTEND_REFLECT +} hb_paint_extend_t; + +typedef struct hb_color_line_t hb_color_line_t; + +typedef unsigned int (*hb_color_line_get_color_stops_func_t) (hb_color_line_t *color_line, + void *color_line_data, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + void *user_data); + +typedef hb_paint_extend_t (*hb_color_line_get_extend_func_t) (hb_color_line_t *color_line, + void *color_line_data, + void *user_data); + +/** + * hb_color_line_t: + * + * A struct containing color information for a gradient. + * + * Since: REPLACEME + */ +struct hb_color_line_t { + void *data; + + hb_color_line_get_color_stops_func_t get_color_stops; + void *get_color_stops_user_data; + + hb_color_line_get_extend_func_t get_extend; + void *get_extend_user_data; + + void *reserved0; + void *reserved1; + void *reserved2; + void *reserved3; + void *reserved5; + void *reserved6; + void *reserved7; + void *reserved8; +}; + +HB_EXTERN unsigned int +hb_color_line_get_color_stops (hb_color_line_t *color_line, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops); + +HB_EXTERN hb_paint_extend_t +hb_color_line_get_extend (hb_color_line_t *color_line); + +/** + * hb_paint_linear_gradient_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @color_line: Color information for the gradient + * @x0: X coordinate of the first point + * @y0: Y coordinate of the first point + * @x1: X coordinate of the second point + * @y1: Y coordinate of the second point + * @x2: X coordinate of the third point + * @y2: Y coordinate of the third point + * @user_data: User data pointer passed to hb_paint_funcs_set_linear_gradient_func() + * + * A virtual method for the #hb_paint_funcs_t to paint a linear + * gradient everywhere within the current clip. + * + * The @color_line object contains information about the colors of the gradients. + * It is only valid for the duration of the callback, you cannot keep it around. + * + * The coordinates of the points are interpreted according + * to the current transform. + * + * See the OpenType spec [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details on how the points define the direction + * of the gradient, and how to interpret the @color_line. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_linear_gradient_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2, + void *user_data); + +/** + * hb_paint_radial_gradient_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @color_line: Color information for the gradient + * @x0: X coordinate of the first circle's center + * @y0: Y coordinate of the first circle's center + * @r0: radius of the first circle + * @x1: X coordinate of the second circle's center + * @y1: Y coordinate of the second circle's center + * @r1: radius of the second circle + * @user_data: User data pointer passed to hb_paint_funcs_set_radial_gradient_func() + * + * A virtual method for the #hb_paint_funcs_t to paint a radial + * gradient everywhere within the current clip. + * + * The @color_line object contains information about the colors of the gradients. + * It is only valid for the duration of the callback, you cannot keep it around. + * + * The coordinates of the points are interpreted according + * to the current transform. + * + * See the OpenType spec [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details on how the points define the direction + * of the gradient, and how to interpret the @color_line. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_radial_gradient_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1, + void *user_data); + +/** + * hb_paint_sweep_gradient_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @color_line: Color information for the gradient + * @x0: X coordinate of the circle's center + * @y0: Y coordinate of the circle's center + * @start_angle: the start angle, in radians + * @end_angle: the end angle, in radians + * @user_data: User data pointer passed to hb_paint_funcs_set_sweep_gradient_func() + * + * A virtual method for the #hb_paint_funcs_t to paint a sweep + * gradient everywhere within the current clip. + * + * The @color_line object contains information about the colors of the gradients. + * It is only valid for the duration of the callback, you cannot keep it around. + * + * The coordinates of the points are interpreted according + * to the current transform. + * + * See the OpenType spec [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details on how the points define the direction + * of the gradient, and how to interpret the @color_line. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_sweep_gradient_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, + float end_angle, + void *user_data); + +/** + * hb_paint_composite_mode_t: + * + * The values of this enumeration describe the compositing modes + * that can be used when combining temporary redirected drawing + * with the backdrop. + * + * See the OpenType spec [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details. + */ +typedef enum { + HB_PAINT_COMPOSITE_MODE_CLEAR, + HB_PAINT_COMPOSITE_MODE_SRC, + HB_PAINT_COMPOSITE_MODE_DEST, + HB_PAINT_COMPOSITE_MODE_SRC_OVER, + HB_PAINT_COMPOSITE_MODE_DEST_OVER, + HB_PAINT_COMPOSITE_MODE_SRC_IN, + HB_PAINT_COMPOSITE_MODE_DEST_IN, + HB_PAINT_COMPOSITE_MODE_SRC_OUT, + HB_PAINT_COMPOSITE_MODE_DEST_OUT, + HB_PAINT_COMPOSITE_MODE_SRC_ATOP, + HB_PAINT_COMPOSITE_MODE_DEST_ATOP, + HB_PAINT_COMPOSITE_MODE_XOR, + HB_PAINT_COMPOSITE_MODE_PLUS, + HB_PAINT_COMPOSITE_MODE_SCREEN, + HB_PAINT_COMPOSITE_MODE_OVERLAY, + HB_PAINT_COMPOSITE_MODE_DARKEN, + HB_PAINT_COMPOSITE_MODE_LIGHTEN, + HB_PAINT_COMPOSITE_MODE_COLOR_DODGE, + HB_PAINT_COMPOSITE_MODE_COLOR_BURN, + HB_PAINT_COMPOSITE_MODE_HARD_LIGHT, + HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT, + HB_PAINT_COMPOSITE_MODE_DIFFERENCE, + HB_PAINT_COMPOSITE_MODE_EXCLUSION, + HB_PAINT_COMPOSITE_MODE_MULTIPLY, + HB_PAINT_COMPOSITE_MODE_HSL_HUE, + HB_PAINT_COMPOSITE_MODE_HSL_SATURATION, + HB_PAINT_COMPOSITE_MODE_HSL_COLOR, + HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY, +} hb_paint_composite_mode_t; + +/** + * hb_paint_push_group_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @user_data: User data pointer passed to hb_paint_funcs_set_push_group_func() + * + * A virtual method for the #hb_paint_funcs_t to use + * an intermediate surface for subsequent paint calls. + * + * The drawing will be redirected to an intermediate surface + * until a matching call to the #hb_paint_funcs_pop_group_func_t + * vfunc. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_push_group_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data); + +/** + * hb_paint_pop_group_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @mode: the compositing mode to use + * @user_data: User data pointer passed to hb_paint_funcs_set_pop_group_func() + * + * A virtual method for the #hb_paint_funcs_t to undo + * the effect of a prior call to the #hb_paint_funcs_push_group_func_t + * vfunc. + * + * This call stops the redirection to the intermediate surface, + * and then composites it on the previous surface, using the + * compositing mode passed to this call. + * + * Since: REPLACEME + */ +typedef void (*hb_paint_pop_group_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data); + +/** + * hb_paint_funcs_set_push_transform_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The push-transform callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the push-transform callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_push_transform_func (hb_paint_funcs_t *funcs, + hb_paint_push_transform_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_pop_transform_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The pop-transform callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the pop-transform callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_pop_transform_func (hb_paint_funcs_t *funcs, + hb_paint_pop_transform_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_push_clip_glyph_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The push-clip-glyph callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the push-clip-glyph callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_push_clip_glyph_func (hb_paint_funcs_t *funcs, + hb_paint_push_clip_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_push_clip_rectangle_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The push-clip-rectangle callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the push-clip-rect callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_push_clip_rectangle_func (hb_paint_funcs_t *funcs, + hb_paint_push_clip_rectangle_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_pop_clip_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The pop-clip callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the pop-clip callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_pop_clip_func (hb_paint_funcs_t *funcs, + hb_paint_pop_clip_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_color_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The paint-color callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the paint-color callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_color_func (hb_paint_funcs_t *funcs, + hb_paint_color_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_image_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The paint-image callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the paint-image callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_image_func (hb_paint_funcs_t *funcs, + hb_paint_image_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_linear_gradient_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The linear-gradient callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the linear-gradient callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_linear_gradient_func (hb_paint_funcs_t *funcs, + hb_paint_linear_gradient_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_radial_gradient_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The radial-gradient callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the radial-gradient callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_radial_gradient_func (hb_paint_funcs_t *funcs, + hb_paint_radial_gradient_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_sweep_gradient_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The sweep-gradient callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the sweep-gradient callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_sweep_gradient_func (hb_paint_funcs_t *funcs, + hb_paint_sweep_gradient_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_push_group_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The push-group callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the push-group callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_push_group_func (hb_paint_funcs_t *funcs, + hb_paint_push_group_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_pop_group_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The pop-group callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the pop-group callback on the paint functions struct. + * + * Since: REPLACEME + */ +HB_EXTERN void +hb_paint_funcs_set_pop_group_func (hb_paint_funcs_t *funcs, + hb_paint_pop_group_func_t func, + void *user_data, + hb_destroy_func_t destroy); + + +HB_END_DECLS + +#endif /* HB_PAINT_H */ diff --git a/src/hb-paint.hh b/src/hb-paint.hh new file mode 100644 index 000000000..89be4cb3d --- /dev/null +++ b/src/hb-paint.hh @@ -0,0 +1,178 @@ +/* + * Copyright © 2022 Matthias Clasen + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_PAINT_HH +#define HB_PAINT_HH + +#include "hb.hh" +#include "hb-face.hh" +#include "hb-font.hh" + +#define HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS \ + HB_PAINT_FUNC_IMPLEMENT (push_transform) \ + HB_PAINT_FUNC_IMPLEMENT (pop_transform) \ + HB_PAINT_FUNC_IMPLEMENT (push_clip_glyph) \ + HB_PAINT_FUNC_IMPLEMENT (push_clip_rectangle) \ + HB_PAINT_FUNC_IMPLEMENT (pop_clip) \ + HB_PAINT_FUNC_IMPLEMENT (color) \ + HB_PAINT_FUNC_IMPLEMENT (image) \ + HB_PAINT_FUNC_IMPLEMENT (linear_gradient) \ + HB_PAINT_FUNC_IMPLEMENT (radial_gradient) \ + HB_PAINT_FUNC_IMPLEMENT (sweep_gradient) \ + HB_PAINT_FUNC_IMPLEMENT (push_group) \ + HB_PAINT_FUNC_IMPLEMENT (pop_group) \ + /* ^--- Add new callbacks here */ + +struct hb_paint_funcs_t +{ + hb_object_header_t header; + + struct { +#define HB_PAINT_FUNC_IMPLEMENT(name) hb_paint_##name##_func_t name; + HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + } func; + + struct { +#define HB_PAINT_FUNC_IMPLEMENT(name) void *name; + HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + } *user_data; + + struct { +#define HB_PAINT_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + } *destroy; + + void push_transform (void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy) + { func.push_transform (this, paint_data, + xx, yx, xy, yy, dx, dy, + !user_data ? nullptr : user_data->push_transform); } + void pop_transform (void *paint_data) + { func.pop_transform (this, paint_data, + !user_data ? nullptr : user_data->pop_transform); } + void push_clip_glyph (void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font) + { func.push_clip_glyph (this, paint_data, + glyph, + font, + !user_data ? nullptr : user_data->push_clip_glyph); } + void push_clip_rectangle (void *paint_data, + float xmin, float ymin, float xmax, float ymax) + { func.push_clip_rectangle (this, paint_data, + xmin, ymin, xmax, ymax, + !user_data ? nullptr : user_data->push_clip_rectangle); } + void pop_clip (void *paint_data) + { func.pop_clip (this, paint_data, + !user_data ? nullptr : user_data->pop_clip); } + void color (void *paint_data, + hb_bool_t is_foreground, + hb_color_t color) + { func.color (this, paint_data, + is_foreground, color, + !user_data ? nullptr : user_data->color); } + void image (void *paint_data, + hb_blob_t *image, + unsigned width, unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents) + { func.image (this, paint_data, + image, width, height, format, slant, extents, + !user_data ? nullptr : user_data->image); } + void linear_gradient (void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2) + { func.linear_gradient (this, paint_data, + color_line, x0, y0, x1, y1, x2, y2, + !user_data ? nullptr : user_data->linear_gradient); } + void radial_gradient (void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1) + { func.radial_gradient (this, paint_data, + color_line, x0, y0, r0, x1, y1, r1, + !user_data ? nullptr : user_data->radial_gradient); } + void sweep_gradient (void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, + float end_angle) + { func.sweep_gradient (this, paint_data, + color_line, x0, y0, start_angle, end_angle, + !user_data ? nullptr : user_data->sweep_gradient); } + void push_group (void *paint_data) + { func.push_group (this, paint_data, + !user_data ? nullptr : user_data->push_group); } + void pop_group (void *paint_data, + hb_paint_composite_mode_t mode) + { func.pop_group (this, paint_data, + mode, + !user_data ? nullptr : user_data->pop_group); } + + void push_root_transform (void *paint_data, + const hb_font_t *font) + { + int xscale = font->x_scale, yscale = font->y_scale; + float upem = font->face->get_upem (); + float slant = font->slant_xy; + + func.push_transform (this, paint_data, + xscale/upem, 0, slant * yscale/upem, yscale/upem, 0, 0, + !user_data ? nullptr : user_data->push_transform); + } + void pop_root_transform (void *paint_data) + { + func.pop_transform (this, paint_data, + !user_data ? nullptr : user_data->pop_transform); + } + + void push_inverse_root_transform (void *paint_data, + hb_font_t *font) + { + int xscale = font->x_scale, yscale = font->y_scale; + float upem = font->face->get_upem (); + float slant = font->slant_xy; + + func.push_transform (this, paint_data, + upem/xscale, 0, -slant * upem/xscale, upem/yscale, 0, 0, + !user_data ? nullptr : user_data->push_transform); + } + void pop_inverse_root_transform (void *paint_data) + { + func.pop_transform (this, paint_data, + !user_data ? nullptr : user_data->pop_transform); + } +}; +DECLARE_NULL_INSTANCE (hb_paint_funcs_t); + + +#endif /* HB_PAINT_HH */ diff --git a/src/hb.h b/src/hb.h index 360686ca6..5a6ae6607 100644 --- a/src/hb.h +++ b/src/hb.h @@ -36,6 +36,7 @@ #include "hb-face.h" #include "hb-font.h" #include "hb-map.h" +#include "hb-paint.h" #include "hb-set.h" #include "hb-shape.h" #include "hb-shape-plan.h" diff --git a/src/meson.build b/src/meson.build index 64753cb10..3b09a3441 100644 --- a/src/meson.build +++ b/src/meson.build @@ -44,6 +44,9 @@ hb_base_sources = files( 'hb-dispatch.hh', 'hb-draw.cc', 'hb-draw.hh', + 'hb-paint.cc', + 'hb-paint.hh', + 'hb-paint-extents.hh', 'hb-face.cc', 'hb-face.hh', 'hb-fallback-shape.cc', @@ -73,6 +76,7 @@ hb_base_sources = files( 'hb-ot-cmap-table.hh', 'hb-ot-color-cbdt-table.hh', 'hb-ot-color-colr-table.hh', + 'hb-ot-color-colr-table.cc', 'hb-ot-color-cpal-table.hh', 'hb-ot-color-sbix-table.hh', 'hb-ot-color-svg-table.hh', @@ -276,6 +280,7 @@ hb_base_headers = files( 'hb-cplusplus.hh', 'hb-deprecated.h', 'hb-draw.h', + 'hb-paint.h', 'hb-face.h', 'hb-font.h', 'hb-map.h', @@ -301,7 +306,7 @@ hb_base_headers += hb_version_h # Optional Sources and Headers with external deps -hb_ft_sources = files('hb-ft.cc') +hb_ft_sources = files('hb-ft.cc', 'hb-ft-colr.hh') hb_ft_headers = files('hb-ft.h') hb_glib_sources = files('hb-glib.cc') diff --git a/test/api/Makefile.am b/test/api/Makefile.am index ffd4a0fc6..16c4c0913 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -14,7 +14,10 @@ libs: EXTRA_DIST += meson.build -EXTRA_DIST += fonts +EXTRA_DIST += \ + fonts \ + results \ + $(NULL) LINK = $(CXXLINK) @@ -56,6 +59,7 @@ TEST_PROGS = \ test-ot-tag \ test-ot-extents-cff \ test-ot-metrics-tt-var \ + test-paint \ test-set \ test-shape \ test-style \ @@ -87,6 +91,9 @@ TEST_PROGS = \ test_draw_CPPFLAGS = $(AM_CPPFLAGS) test_draw_LDADD = $(LDADD) +test_paint_CPPFLAGS = $(AM_CPPFLAGS) +test_paint_LDADD = $(LDADD) + test_subset_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_cmap_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_drop_tables_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la @@ -133,6 +140,9 @@ if HAVE_FREETYPE test_draw_CPPFLAGS += $(FREETYPE_CFLAGS) test_draw_LDADD += $(FREETYPE_LIBS) +test_paint_CPPFLAGS += $(FREETYPE_CFLAGS) +test_paint_LDADD += $(FREETYPE_LIBS) + TEST_PROGS += \ test-ot-math \ $(NULL) diff --git a/test/api/fonts/RocherColorGX.abc.ttf b/test/api/fonts/RocherColorGX.abc.ttf new file mode 100644 index 000000000..35fce0923 Binary files /dev/null and b/test/api/fonts/RocherColorGX.abc.ttf differ diff --git a/test/api/fonts/RocherColorGX.abc.ttx b/test/api/fonts/RocherColorGX.abc.ttx new file mode 100644 index 000000000..a9236f86d --- /dev/null +++ b/test/api/fonts/RocherColorGX.abc.ttx @@ -0,0 +1,2714 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright © 2018 by Henrique Beier. All rights reserved. + + + Rocher Color + + + Regular + + + 0.110;HBT ;RocherColor-Regular + + + Rocher Color Regular + + + Version 0.110 + + + RocherColor-Regular + + + Bevel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BVEL + 0x0 + 0.0 + 100.0 + 100.0 + 257 + + + SHDW + 0x0 + 0.0 + 100.0 + 100.0 + 259 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/api/fonts/noto_handwriting-cff2_colr_1.otf b/test/api/fonts/noto_handwriting-cff2_colr_1.otf new file mode 100644 index 000000000..dfb30588e Binary files /dev/null and b/test/api/fonts/noto_handwriting-cff2_colr_1.otf differ diff --git a/test/api/fonts/test_glyphs-glyf_colr_1.ttf b/test/api/fonts/test_glyphs-glyf_colr_1.ttf new file mode 100644 index 000000000..982ccc0a1 Binary files /dev/null and b/test/api/fonts/test_glyphs-glyf_colr_1.ttf differ diff --git a/test/api/fonts/test_glyphs-glyf_colr_1.ttx b/test/api/fonts/test_glyphs-glyf_colr_1.ttx new file mode 100644 index 000000000..fc79abe74 --- /dev/null +++ b/test/api/fonts/test_glyphs-glyf_colr_1.ttx @@ -0,0 +1,8458 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + COLRv1 Static Test Glyphs + + + Regular + + + COLRv1 Static Test Glyphs 2022-08-16T17:28:26.463214 + + + COLRv1 Static Test Glyphs Regular + + + 2022-08-16T17:28:26.463214 + + + COLRv1StaticTestGlyphs-Regular + + + COLRv1 Static Test Glyphs + + + Regular + + + COLRv1 Static Test Glyphs 2022-08-16T17:28:26.463214 + + + COLRv1 Static Test Glyphs Regular + + + 2022-08-16T17:28:26.463214 + + + COLRv1StaticTestGlyphs-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/api/meson.build b/test/api/meson.build index f5cdce805..2bdd1bbac 100644 --- a/test/api/meson.build +++ b/test/api/meson.build @@ -32,6 +32,7 @@ tests = [ 'test-ot-tag.c', 'test-ot-extents-cff.c', 'test-ot-metrics-tt-var.c', + 'test-paint.c', 'test-subset-repacker.c', 'test-set.c', 'test-shape.c', diff --git a/test/api/results/hand-20-0-10 b/test/api/results/hand-20-0-10 new file mode 100644 index 000000000..def53eebb --- /dev/null +++ b/test/api/results/hand-20-0-10 @@ -0,0 +1,117 @@ +start transform 0.019531 0.000000 0.000000 0.019531 0.000000 0.000000 + start clip rectangle 64.000000 -224.000000 1216.000000 928.000000 + push group + start transform 51.200001 0.000000 -0.000000 51.200001 0.000000 0.000000 + start clip glyph 13 + start transform 0.019531 0.000000 0.000000 0.019531 0.000000 0.000000 + start transform 1.000000 0.000000 0.000000 0.976807 0.000000 0.000000 + radial gradient + p0 280.000000 440.000000 radius 0.000000 + p1 280.000000 440.000000 radius 467.000000 + colors + 0.000000 186 141 104 255 + 0.448792 183 138 103 255 + 0.808594 173 130 100 255 + 1.000000 164 123 98 255 + end transform + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -0.000000 51.200001 0.000000 0.000000 + start clip glyph 14 + start transform 0.019531 0.000000 0.000000 0.019531 0.000000 0.000000 + linear gradient + p0 231.000000 -27.000000 + p1 1019.000000 -27.000000 + p2 231.000000 -815.000000 + colors + 0.000000 164 123 98 255 + 1.000000 164 123 98 255 + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -0.000000 51.200001 0.000000 0.000000 + start clip glyph 15 + start transform 0.019531 0.000000 0.000000 0.019531 0.000000 0.000000 + solid 145 103 77 255 + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -0.000000 51.200001 0.000000 0.000000 + start clip glyph 16 + start transform 0.019531 0.000000 0.000000 0.019531 0.000000 0.000000 + solid 30 136 229 255 + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -0.000000 51.200001 0.000000 0.000000 + start clip glyph 21 + start transform 0.019531 0.000000 0.000000 0.019531 0.000000 0.000000 + solid 145 103 77 255 + end transform + end clip + end transform + pop group mode 3 + push group + push group + start transform 51.200001 0.000000 -0.000000 51.200001 0.000000 0.000000 + start clip glyph 16 + start transform 0.019531 0.000000 0.000000 0.019531 0.000000 0.000000 + linear gradient + p0 669.000000 776.000000 + p1 180.000000 -106.000000 + p2 -212.000000 1265.000000 + colors + 0.000000 100 181 246 255 + 1.000000 33 150 243 255 + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -0.000000 51.200001 0.000000 0.000000 + start clip glyph 18 + start transform 0.019531 0.000000 0.000000 0.019531 0.000000 0.000000 + solid 66 66 66 51 + end transform + end clip + end transform + pop group mode 3 + pop group mode 3 + push group + start transform 51.200001 0.000000 -0.000000 51.200001 0.000000 0.000000 + start clip glyph 19 + start transform 0.019531 0.000000 0.000000 0.019531 0.000000 0.000000 + start transform 1.000000 0.000000 0.000000 0.969116 0.000000 0.000000 + radial gradient + p0 588.000000 198.000000 radius 0.000000 + p1 588.000000 198.000000 radius 342.000000 + colors + 0.000000 186 141 104 255 + 0.448792 183 138 103 255 + 0.808594 173 130 100 255 + 1.000000 164 123 98 255 + end transform + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -0.000000 51.200001 0.000000 0.000000 + start clip glyph 20 + start transform 0.019531 0.000000 0.000000 0.019531 0.000000 0.000000 + solid 145 103 77 255 + end transform + end clip + end transform + pop group mode 3 + end clip +end transform diff --git a/test/api/results/hand-20-0.2-10 b/test/api/results/hand-20-0.2-10 new file mode 100644 index 000000000..708cf2c11 --- /dev/null +++ b/test/api/results/hand-20-0.2-10 @@ -0,0 +1,117 @@ +start transform 0.019531 0.000000 0.003906 0.019531 0.000000 0.000000 + start clip rectangle 64.000000 -224.000000 1216.000000 928.000000 + push group + start transform 51.200001 0.000000 -10.240000 51.200001 0.000000 0.000000 + start clip glyph 13 + start transform 0.019531 0.000000 0.003906 0.019531 0.000000 0.000000 + start transform 1.000000 0.000000 0.000000 0.976807 0.000000 0.000000 + radial gradient + p0 280.000000 440.000000 radius 0.000000 + p1 280.000000 440.000000 radius 467.000000 + colors + 0.000000 186 141 104 255 + 0.448792 183 138 103 255 + 0.808594 173 130 100 255 + 1.000000 164 123 98 255 + end transform + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -10.240000 51.200001 0.000000 0.000000 + start clip glyph 14 + start transform 0.019531 0.000000 0.003906 0.019531 0.000000 0.000000 + linear gradient + p0 231.000000 -27.000000 + p1 1019.000000 -27.000000 + p2 231.000000 -815.000000 + colors + 0.000000 164 123 98 255 + 1.000000 164 123 98 255 + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -10.240000 51.200001 0.000000 0.000000 + start clip glyph 15 + start transform 0.019531 0.000000 0.003906 0.019531 0.000000 0.000000 + solid 145 103 77 255 + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -10.240000 51.200001 0.000000 0.000000 + start clip glyph 16 + start transform 0.019531 0.000000 0.003906 0.019531 0.000000 0.000000 + solid 30 136 229 255 + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -10.240000 51.200001 0.000000 0.000000 + start clip glyph 21 + start transform 0.019531 0.000000 0.003906 0.019531 0.000000 0.000000 + solid 145 103 77 255 + end transform + end clip + end transform + pop group mode 3 + push group + push group + start transform 51.200001 0.000000 -10.240000 51.200001 0.000000 0.000000 + start clip glyph 16 + start transform 0.019531 0.000000 0.003906 0.019531 0.000000 0.000000 + linear gradient + p0 669.000000 776.000000 + p1 180.000000 -106.000000 + p2 -212.000000 1265.000000 + colors + 0.000000 100 181 246 255 + 1.000000 33 150 243 255 + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -10.240000 51.200001 0.000000 0.000000 + start clip glyph 18 + start transform 0.019531 0.000000 0.003906 0.019531 0.000000 0.000000 + solid 66 66 66 51 + end transform + end clip + end transform + pop group mode 3 + pop group mode 3 + push group + start transform 51.200001 0.000000 -10.240000 51.200001 0.000000 0.000000 + start clip glyph 19 + start transform 0.019531 0.000000 0.003906 0.019531 0.000000 0.000000 + start transform 1.000000 0.000000 0.000000 0.969116 0.000000 0.000000 + radial gradient + p0 588.000000 198.000000 radius 0.000000 + p1 588.000000 198.000000 radius 342.000000 + colors + 0.000000 186 141 104 255 + 0.448792 183 138 103 255 + 0.808594 173 130 100 255 + 1.000000 164 123 98 255 + end transform + end transform + end clip + end transform + pop group mode 3 + push group + start transform 51.200001 0.000000 -10.240000 51.200001 0.000000 0.000000 + start clip glyph 20 + start transform 0.019531 0.000000 0.003906 0.019531 0.000000 0.000000 + solid 145 103 77 255 + end transform + end clip + end transform + pop group mode 3 + end clip +end transform diff --git a/test/api/results/rocher-120-0-3 b/test/api/results/rocher-120-0-3 new file mode 100644 index 000000000..326fdc4f9 --- /dev/null +++ b/test/api/results/rocher-120-0-3 @@ -0,0 +1,12 @@ +start clip glyph 16 + solid 81 61 50 255 +end clip +start clip glyph 17 + solid 245 185 68 255 +end clip +start clip glyph 18 + solid 224 142 55 255 +end clip +start clip glyph 19 + solid 245 202 86 255 +end clip diff --git a/test/api/results/rocher-120-0.3-1 b/test/api/results/rocher-120-0.3-1 new file mode 100644 index 000000000..9cb0e66e6 --- /dev/null +++ b/test/api/results/rocher-120-0.3-1 @@ -0,0 +1,12 @@ +start clip glyph 8 + solid 81 61 50 255 +end clip +start clip glyph 9 + solid 245 185 68 255 +end clip +start clip glyph 10 + solid 224 142 55 255 +end clip +start clip glyph 11 + solid 245 202 86 255 +end clip diff --git a/test/api/results/rocher-120-0.3-2 b/test/api/results/rocher-120-0.3-2 new file mode 100644 index 000000000..d26bb572b --- /dev/null +++ b/test/api/results/rocher-120-0.3-2 @@ -0,0 +1,12 @@ +start clip glyph 12 + solid 81 61 50 255 +end clip +start clip glyph 13 + solid 245 185 68 255 +end clip +start clip glyph 14 + solid 224 142 55 255 +end clip +start clip glyph 15 + solid 245 202 86 255 +end clip diff --git a/test/api/results/test-20-0-10 b/test/api/results/test-20-0-10 new file mode 100644 index 000000000..27772487c --- /dev/null +++ b/test/api/results/test-20-0-10 @@ -0,0 +1,18 @@ +start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + start clip rectangle 0.000000 0.000000 1000.000000 1000.000000 + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 174 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + sweep gradient + center 500.000000 600.000000 + angles 0.000000 6.283185 + colors + 0.250000 250 240 230 255 + 0.416687 0 0 255 255 + 0.583313 255 0 0 255 + 0.750000 47 79 79 255 + end transform + end clip + end transform + end clip +end transform diff --git a/test/api/results/test-20-0-106 b/test/api/results/test-20-0-106 new file mode 100644 index 000000000..6803913f9 --- /dev/null +++ b/test/api/results/test-20-0-106 @@ -0,0 +1,28 @@ +start clip rectangle 5.000000 5.000000 15.000000 15.000000 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + push group + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 3 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + solid 0 0 255 127 + end transform + end clip + end transform + push group + start transform 0.000000 0.000000 0.000000 0.000000 1000.000000 1000.000000 + start transform 1.000000 -0.363874 -0.176283 1.000000 0.000000 0.000000 + start transform 0.000000 0.000000 0.000000 0.000000 -1000.000000 -1000.000000 + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 3 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + solid 255 165 0 178 + end transform + end clip + end transform + end transform + end transform + end transform + pop group mode 4 + pop group mode 3 + end transform +end clip diff --git a/test/api/results/test-20-0-116 b/test/api/results/test-20-0-116 new file mode 100644 index 000000000..d07157120 --- /dev/null +++ b/test/api/results/test-20-0-116 @@ -0,0 +1,24 @@ +start clip rectangle 5.000000 5.000000 15.000000 15.000000 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + push group + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 3 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + solid 0 0 255 127 + end transform + end clip + end transform + push group + start transform 1.000000 0.000000 0.000000 1.000000 200.000000 200.000000 + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 3 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + solid 255 165 0 178 + end transform + end clip + end transform + end transform + pop group mode 4 + pop group mode 3 + end transform +end clip diff --git a/test/api/results/test-20-0-123 b/test/api/results/test-20-0-123 new file mode 100644 index 000000000..9d3fa7475 --- /dev/null +++ b/test/api/results/test-20-0-123 @@ -0,0 +1,45 @@ +start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + start clip rectangle 0.000000 0.000000 1000.000000 1000.000000 + push group + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 3 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + solid 0 0 0 255 + end transform + end clip + end transform + pop group mode 3 + push group + push group + start transform 0.000000 0.000000 0.000000 0.000000 333.000000 667.000000 + start transform 8192.000000 0.000000 0.000000 8192.000000 0.000000 0.000000 + start transform 0.000000 0.000000 0.000000 0.000000 -333.000000 -667.000000 + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 2 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + solid 255 220 1 255 + end transform + end clip + end transform + end transform + end transform + end transform + push group + start transform 0.000000 0.000000 0.000000 0.000000 667.000000 333.000000 + start transform 8192.000000 0.000000 0.000000 8192.000000 0.000000 0.000000 + start transform 0.000000 0.000000 0.000000 0.000000 -667.000000 -333.000000 + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 2 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + solid 104 199 232 255 + end transform + end clip + end transform + end transform + end transform + end transform + pop group mode 5 + pop group mode 3 + pop group mode 3 + end clip +end transform diff --git a/test/api/results/test-20-0-165 b/test/api/results/test-20-0-165 new file mode 100644 index 000000000..ad5554d80 --- /dev/null +++ b/test/api/results/test-20-0-165 @@ -0,0 +1,18 @@ +start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + start clip rectangle 100.000000 250.000000 1200.000000 950.000000 + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 165 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + linear gradient + p0 100.000000 950.000000 + p1 2300.000000 950.000000 + p2 -1000.000000 250.000000 + colors + 0.000000 255 0 0 255 + 0.500000 0 0 255 255 + 1.000000 255 255 0 255 + end transform + end clip + end transform + end clip +end transform diff --git a/test/api/results/test-20-0-175 b/test/api/results/test-20-0-175 new file mode 100644 index 000000000..ed4903f53 --- /dev/null +++ b/test/api/results/test-20-0-175 @@ -0,0 +1,32 @@ +start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + start clip rectangle 0.000000 0.000000 1000.000000 1000.000000 + push group + start transform 1.000000 0.000000 0.000000 1.000000 150.000000 0.000000 + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 174 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + solid 0 128 0 255 + end transform + end clip + end transform + end transform + pop group mode 3 + push group + start transform 1.000000 0.000000 0.000000 1.000000 -150.000000 0.000000 + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 174 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + linear gradient + p0 500.000000 250.000000 + p1 500.000000 950.000000 + p2 600.000000 250.000000 + colors + 0.000000 255 0 0 255 + 1.000000 0 0 255 255 + end transform + end clip + end transform + end transform + pop group mode 3 + end clip +end transform diff --git a/test/api/results/test-20-0-6 b/test/api/results/test-20-0-6 new file mode 100644 index 000000000..8d4c45c55 --- /dev/null +++ b/test/api/results/test-20-0-6 @@ -0,0 +1,17 @@ +start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + start clip rectangle 100.000000 250.000000 900.000000 950.000000 + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 6 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + linear gradient + p0 100.000000 250.000000 + p1 900.000000 250.000000 + p2 100.000000 300.000000 + colors + 0.000000 255 0 0 255 + 1.000000 0 0 255 255 + end transform + end clip + end transform + end clip +end transform diff --git a/test/api/results/test-20-0-92 b/test/api/results/test-20-0-92 new file mode 100644 index 000000000..463818616 --- /dev/null +++ b/test/api/results/test-20-0-92 @@ -0,0 +1,17 @@ +start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + start clip rectangle 0.000000 0.000000 1000.000000 1000.000000 + start transform 50.000000 0.000000 -0.000000 50.000000 0.000000 0.000000 + start clip glyph 2 + start transform 0.020000 0.000000 0.000000 0.020000 0.000000 0.000000 + radial gradient + p0 166.000000 768.000000 radius 0.000000 + p1 166.000000 768.000000 radius 256.000000 + colors + 0.000000 0 128 0 255 + 0.500000 255 255 255 255 + 1.000000 255 0 0 255 + end transform + end clip + end transform + end clip +end transform diff --git a/test/api/test-draw.c b/test/api/test-draw.c index 32c8e5618..ad1fd1944 100644 --- a/test/api/test-draw.c +++ b/test/api/test-draw.c @@ -182,7 +182,7 @@ static hb_draw_funcs_t *funcs2; /* this one translates quadratic calls to cubic static void test_hb_draw_empty (void) { - hb_font_get_glyph_shape (hb_font_get_empty (), 3, funcs, NULL); + hb_font_draw_glyph (hb_font_get_empty (), 3, funcs, NULL); } static void @@ -200,10 +200,10 @@ test_hb_draw_glyf (void) }; draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 4, funcs, &draw_data); + hb_font_draw_glyph (font, 4, funcs, &draw_data); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 3, funcs, &draw_data); + hb_font_draw_glyph (font, 3, funcs, &draw_data); char expected[] = "M275,442Q232,442 198,420Q164,397 145,353Q126,309 126,245" "Q126,182 147,139Q167,95 204,73Q240,50 287,50Q330,50 367,70" "Q404,90 427,128L451,116Q431,54 384,21Q336,-13 266,-13" @@ -215,7 +215,7 @@ test_hb_draw_glyf (void) /* Test translating quadratic calls to cubic by a _draw_funcs_t that doesn't set the callback */ draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 3, funcs2, &draw_data); + hb_font_draw_glyph (font, 3, funcs2, &draw_data); char expected2[] = "M275,442C246,442 221,435 198,420C175,405 158,382 145,353" "C132,324 126,288 126,245C126,203 133,168 147,139C160,110 179,88 204,73" "C228,58 256,50 287,50C316,50 342,57 367,70C392,83 412,103 427,128" @@ -233,7 +233,7 @@ test_hb_draw_glyf (void) hb_font_set_variations (font, &var, 1); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 3, funcs, &draw_data); + hb_font_draw_glyph (font, 3, funcs, &draw_data); char expected3[] = "M323,448Q297,448 271,430Q244,412 226,371Q209,330 209,261" "Q209,204 225,166Q242,127 272,107Q303,86 344,86Q378,86 404,101" "Q430,115 451,137L488,103Q458,42 404,13Q350,-16 279,-16" @@ -259,7 +259,7 @@ test_hb_draw_cff1 (void) .size = sizeof (str), .consumed = 0 }; - hb_font_get_glyph_shape (font, 3, funcs, &draw_data); + hb_font_draw_glyph (font, 3, funcs, &draw_data); char expected[] = "M203,367C227,440 248,512 268,588L272,588C293,512 314,440 338,367L369,267L172,267L203,367Z" "M3,0L88,0L151,200L390,200L452,0L541,0L319,656L225,656L3,0Z" "M300,653L342,694L201,861L143,806L300,653Z"; @@ -282,7 +282,7 @@ test_hb_draw_cff1_rline (void) .size = sizeof (str), .consumed = 0 }; - hb_font_get_glyph_shape (font, 1, funcs, &draw_data); + hb_font_draw_glyph (font, 1, funcs, &draw_data); char expected[] = "M775,400C705,400 650,343 650,274L650,250L391,250L713,572L392,893" "L287,1000C311,942 296,869 250,823C250,823 286,858 321,823L571,572" "L150,150L750,150L750,276C750,289 761,300 775,300C789,300 800,289 800,276" @@ -307,7 +307,7 @@ test_hb_draw_cff2 (void) }; draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 3, funcs, &draw_data); + hb_font_draw_glyph (font, 3, funcs, &draw_data); char expected[] = "M275,442C303,442 337,435 371,417L325,454L350,366" "C357,341 370,321 403,321C428,321 443,333 448,358" "C435,432 361,487 272,487C153,487 43,393 43,236" @@ -321,7 +321,7 @@ test_hb_draw_cff2 (void) hb_font_set_variations (font, &var, 1); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 3, funcs, &draw_data); + hb_font_draw_glyph (font, 3, funcs, &draw_data); char expected2[] = "M323,448C356,448 380,441 411,427L333,469L339,401" "C343,322 379,297 420,297C458,297 480,314 492,352" "C486,433 412,501 303,501C148,501 25,406 25,241" @@ -347,19 +347,19 @@ test_hb_draw_ttf_parser_tests (void) hb_face_destroy (face); { draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 0, funcs, &draw_data); + hb_font_draw_glyph (font, 0, funcs, &draw_data); char expected[] = "M50,0L50,750L450,750L450,0L50,0Z"; g_assert_cmpmem (str, draw_data.consumed, expected, sizeof (expected) - 1); } { draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 1, funcs, &draw_data); + hb_font_draw_glyph (font, 1, funcs, &draw_data); char expected[] = "M56,416L56,487L514,487L514,416L56,416ZM56,217L56,288L514,288L514,217L56,217Z"; g_assert_cmpmem (str, draw_data.consumed, expected, sizeof (expected) - 1); } { draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 4, funcs, &draw_data); + hb_font_draw_glyph (font, 4, funcs, &draw_data); char expected[] = "M332,468L197,468L197,0L109,0L109,468L15,468L15,509L109,539" "L109,570Q109,674 155,720Q201,765 283,765Q315,765 342,760" "Q368,754 387,747L364,678Q348,683 327,688Q306,693 284,693" @@ -371,13 +371,13 @@ test_hb_draw_ttf_parser_tests (void) } { draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 5, funcs, &draw_data); + hb_font_draw_glyph (font, 5, funcs, &draw_data); char expected[] = "M15,0Q15,0 15,0Z"; g_assert_cmpmem (str, draw_data.consumed, expected, sizeof (expected) - 1); } { draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 6, funcs, &draw_data); + hb_font_draw_glyph (font, 6, funcs, &draw_data); char expected[] = "M346,468L211,468L211,0L123,0L123,468L29,468L29,509L123,539" "L123,570Q123,674 169,720Q215,765 297,765Q329,765 356,760" "Q382,754 401,747L378,678Q362,683 341,688Q320,693 298,693" @@ -394,7 +394,7 @@ test_hb_draw_ttf_parser_tests (void) hb_face_destroy (face); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 1, funcs, &draw_data); + hb_font_draw_glyph (font, 1, funcs, &draw_data); char expected[] = "M0,0C100,0 150,-20 250,-20C350,-20 400,0 500,0C500,100 520,150 520,250" "C520,350 500,400 500,500C400,500 350,520 250,520C150,520 100,500 0,500" "C0,400 -20,350 -20,250C-20,150 0,100 0,0ZM50,50C50,130 34,170 34,250" @@ -411,7 +411,7 @@ test_hb_draw_ttf_parser_tests (void) hb_face_destroy (face); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 1, funcs, &draw_data); + hb_font_draw_glyph (font, 1, funcs, &draw_data); char expected[] = "M82,0L164,0L164,486L82,486L82,0Z" "M124,586C156,586 181,608 181,639C181,671 156,692 124,692" "C92,692 67,671 67,639C67,608 92,586 124,586Z"; @@ -438,7 +438,7 @@ test_hb_draw_font_kit_glyphs_tests (void) /* should get a path for the glyph */ draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 37, funcs, &draw_data); + hb_font_draw_glyph (font, 37, funcs, &draw_data); char expected[] = "M201,1462L614,1462Q905,1462 1035,1375Q1165,1288 1165,1100" "Q1165,970 1093,886Q1020,801 881,776L881,766Q1214,709 1214,416" "Q1214,220 1082,110Q949,0 711,0L201,0L201,1462ZM371,836L651,836" @@ -449,7 +449,7 @@ test_hb_draw_font_kit_glyphs_tests (void) /* should get a path for the glyph */ draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 171, funcs, &draw_data); + hb_font_draw_glyph (font, 171, funcs, &draw_data); char expected2[] = "M639,-20Q396,-20 256,128Q115,276 115,539Q115,804 246,960Q376,1116 596,1116" "Q802,1116 922,981Q1042,845 1042,623L1042,518L287,518Q292,325 385,225" "Q477,125 645,125Q822,125 995,199L995,51Q907,13 829,-3Q750,-20 639,-20Z" @@ -475,7 +475,7 @@ test_hb_draw_font_kit_glyphs_tests (void) /* should resolve composite glyphs recursively */ draw_data.consumed = 0; - hb_font_get_glyph_shape (font, codepoint, funcs, &draw_data); + hb_font_draw_glyph (font, codepoint, funcs, &draw_data); char expected[] = "M581,274L443,274Q409,274 384,259Q359,243 348,219Q336,194 340,166" "Q343,138 365,111L468,-13Q470,-10 473,-6Q475,-3 477,0L253,0Q225,0 203,8" "Q180,15 168,32Q155,48 155,73L155,269L50,269L50,73Q50,24 69,-10" @@ -491,7 +491,7 @@ test_hb_draw_font_kit_glyphs_tests (void) /* should transform points of a composite glyph */ draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 2, funcs, &draw_data); /* 2 == arAlef.fina */ + hb_font_draw_glyph (font, 2, funcs, &draw_data); /* 2 == arAlef.fina */ char expected2[] = "M155,624L155,84Q150,90 146,95Q141,99 136,105" "L292,105L292,0L156,0Q128,0 104,14Q79,27 65,51" "Q50,74 50,104L50,624L155,624ZM282,105L312,105" @@ -507,7 +507,7 @@ test_hb_draw_font_kit_glyphs_tests (void) hb_face_destroy (face); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 5, funcs, &draw_data); + hb_font_draw_glyph (font, 5, funcs, &draw_data); char expected[] = "M90,0L258,0C456,0 564,122 564,331C564,539 456,656 254,656L90,656L90,0Z" "M173,68L173,588L248,588C401,588 478,496 478,331C478,165 401,68 248,68L173,68Z"; g_assert_cmpmem (str, draw_data.consumed, expected, sizeof (expected) - 1); @@ -522,7 +522,7 @@ test_hb_draw_font_kit_glyphs_tests (void) hb_face_destroy (face); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 1, funcs, &draw_data); + hb_font_draw_glyph (font, 1, funcs, &draw_data); char expected[] = "M139,390C175,390 205,419 205,459C205,501 175,530 139,530C103,530 73,501 73,459" "C73,419 103,390 139,390ZM139,-13C175,-13 205,15 205,56C205,97 175,127 139,127" "C103,127 73,97 73,56C73,15 103,-13 139,-13Z"; @@ -567,7 +567,7 @@ test_hb_draw_font_kit_variations_tests (void) hb_buffer_destroy (buffer); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, codepoint, funcs, &draw_data); + hb_font_draw_glyph (font, codepoint, funcs, &draw_data); char expected[] = "M371,-102L371,539L914,539L914,-27Q914,-102 840,-102" "Q796,-102 755,-98L742,-59Q790,-66 836,-66Q871,-66 871,-31L871,504" "L414,504L414,-102L371,-102ZM203,-94Q138,-94 86,-90L74,-52" @@ -612,7 +612,7 @@ test_hb_draw_font_kit_variations_tests (void) hb_buffer_destroy (buffer); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, codepoint, funcs, &draw_data); + hb_font_draw_glyph (font, codepoint, funcs, &draw_data); char expected[] = "M371,-102L371,539L914,539L914,-27Q914,-102 840,-102Q796,-102 755,-98" "L742,-59Q790,-66 836,-66Q871,-66 871,-31L871,504L414,504L414,-102" "L371,-102ZM203,-94Q138,-94 86,-90L74,-52Q137,-59 188,-59Q211,-59 222,-46" @@ -656,7 +656,7 @@ test_hb_draw_font_kit_variations_tests (void) hb_buffer_destroy (buffer); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, codepoint, funcs, &draw_data); + hb_font_draw_glyph (font, codepoint, funcs, &draw_data); char expected[] = "M371,-102L371,539L914,539L914,-27Q914,-102 840,-102Q796,-102 755,-98" "L742,-59Q790,-66 836,-66Q871,-66 871,-31L871,504L414,504L414,-102" "L371,-102ZM203,-94Q138,-94 86,-90L74,-52Q137,-59 188,-59Q211,-59 222,-46" @@ -703,7 +703,7 @@ test_hb_draw_font_kit_variations_tests (void) hb_buffer_destroy (buffer); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, codepoint, funcs, &draw_data); + hb_font_draw_glyph (font, codepoint, funcs, &draw_data); char expected[] = "M246,15C188,15 147,27 101,68L142,23L117,117C111,143 96,149 81,149" "C65,149 56,141 52,126C71,40 137,-13 244,-13C348,-13 436,46 436,156" "C436,229 405,295 271,349L247,359C160,393 119,439 119,506" @@ -727,7 +727,7 @@ test_hb_draw_font_kit_variations_tests (void) hb_buffer_destroy (buffer); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, codepoint, funcs, &draw_data); + hb_font_draw_glyph (font, codepoint, funcs, &draw_data); char expected[] = "M251,36C206,36 165,42 118,61L176,21L161,99C151,152 129,167 101,167" "C78,167 61,155 51,131C54,43 133,-14 247,-14C388,-14 474,64 474,171" "C474,258 430,321 294,370L257,383C188,406 150,438 150,499" @@ -752,7 +752,7 @@ test_hb_draw_font_kit_variations_tests (void) hb_buffer_destroy (buffer); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, codepoint, funcs, &draw_data); + hb_font_draw_glyph (font, codepoint, funcs, &draw_data); char expected[] = "M258,38C197,38 167,48 118,71L192,19L183,103C177,155 155,174 115,174" "C89,174 64,161 51,125C52,36 124,-16 258,-16C417,-16 513,67 513,175" "C513,278 457,328 322,388L289,403C232,429 203,452 203,500C203,562 244,589 301,589" @@ -787,7 +787,7 @@ test_hb_draw_estedad_vf (void) hb_font_set_variations (font, &var, 1); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 156, funcs, &draw_data); + hb_font_draw_glyph (font, 156, funcs, &draw_data); /* Skip empty path where all the points of a path are equal */ char expected[] = "M150,1158L182,1158Q256,1158 317,1170Q377,1182 421,1213L421,430L521,430" "L521,1490L421,1490L421,1320Q393,1279 344,1262Q294,1244 182,1244L150,1244" @@ -797,7 +797,7 @@ test_hb_draw_estedad_vf (void) g_assert_cmpmem (str, draw_data.consumed, expected, sizeof (expected) - 1); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 180, funcs, &draw_data); + hb_font_draw_glyph (font, 180, funcs, &draw_data); /* Skip empty path where all the points of a path are equal */ char expected2[] = "M120,693Q120,545 177,414Q233,282 333,182Q433,81 567,24" "Q701,-33 856,-33Q1010,-33 1144,24Q1277,81 1377,182Q1477,282 1534,414" @@ -817,7 +817,7 @@ test_hb_draw_estedad_vf (void) g_assert_cmpmem (str, draw_data.consumed, expected2, sizeof (expected2) - 1); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 262, funcs, &draw_data); + hb_font_draw_glyph (font, 262, funcs, &draw_data); /* Skip empty path where all the points of a path are equal */ char expected3[] = "M422,598Q495,598 545,548Q595,498 595,426Q595,353 545,303Q494,252 422,252" "Q350,252 300,303Q250,353 250,426Q250,499 300,549Q349,598 422,598ZM422,698" @@ -847,7 +847,7 @@ test_hb_draw_stroking (void) hb_face_destroy (face); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 6, funcs, &draw_data); + hb_font_draw_glyph (font, 6, funcs, &draw_data); /* Skip empty path where all the points of a path are equal */ char expected[] = "M436,1522Q436,1280 531,1060Q625,839 784,680Q943,521 1164,427Q1384,332 1626,332" "Q1868,332 2089,427Q2309,521 2468,680Q2627,839 2722,1060Q2816,1280 2816,1522" @@ -862,7 +862,7 @@ test_hb_draw_stroking (void) g_assert_cmpmem (str, draw_data.consumed, expected, sizeof (expected) - 1); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 7, funcs, &draw_data); + hb_font_draw_glyph (font, 7, funcs, &draw_data); char expected2[] = "M436,1522Q436,1280 531,1060Q625,839 784,680Q943,521 1164,427" "Q1384,332 1626,332Q1868,332 2089,427Q2309,521 2468,680" "Q2627,839 2722,1060Q2816,1280 2816,1522Q2816,1764 2722,1985" @@ -888,14 +888,14 @@ test_hb_draw_stroking (void) hb_face_destroy (face); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 4, funcs, &draw_data); + hb_font_draw_glyph (font, 4, funcs, &draw_data); /* Skip empty path in CFF */ char expected[] = "M106,372C106,532 237,662 397,662C557,662 688,532 688,372C688,212 557,81 397,81C237,81 106,212 106,372Z" "M62,373C62,188 212,39 397,39C582,39 731,188 731,373C731,558 582,708 397,708C212,708 62,558 62,373Z"; g_assert_cmpmem (str, draw_data.consumed, expected, sizeof (expected) - 1); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 5, funcs, &draw_data); + hb_font_draw_glyph (font, 5, funcs, &draw_data); /* Fold consequent move-to commands */ char expected2[] = "M106,372C106,532 237,662 397,662C557,662 688,532 688,372" "C688,212 557,81 397,81C237,81 106,212 106,372ZM62,373" @@ -970,7 +970,7 @@ test_hb_draw_synthetic_slant (void) hb_font_set_synthetic_slant (font, 0.2f); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 37, funcs, &draw_data); + hb_font_draw_glyph (font, 37, funcs, &draw_data); char expected[] = "M493,1462L906,1462Q1197,1462 1310,1375Q1423,1288 1385,1100" "Q1359,970 1270,886Q1180,801 1036,776L1034,766Q1356,709 1297,416" "Q1258,220 1104,110Q949,0 711,0L201,0L493,1462ZM538,836L818,836" @@ -988,7 +988,7 @@ test_hb_draw_synthetic_slant (void) hb_font_set_synthetic_slant (font, 0.2f); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 5, funcs, &draw_data); + hb_font_draw_glyph (font, 5, funcs, &draw_data); char expected[] = "M90,0L258,0C456,0 588,122 630,331C672,539 587,656 385,656L221,656L90,0Z" "M187,68L291,588L366,588C519,588 577,496 544,331C511,165 415,68 262,68L187,68Z"; g_assert_cmpmem (str, draw_data.consumed, expected, sizeof (expected) - 1); @@ -1015,7 +1015,7 @@ test_hb_draw_subfont_scale (void) hb_font_set_scale (font2, x*2, y*2); draw_data.consumed = 0; - hb_font_get_glyph_shape (font1, 37, funcs, &draw_data); + hb_font_draw_glyph (font1, 37, funcs, &draw_data); char expected1[] = "M201,1462L614,1462Q905,1462 1035,1375Q1165,1288 1165,1100" "Q1165,970 1093,886Q1020,801 881,776L881,766Q1214,709 1214,416" "Q1214,220 1082,110Q949,0 711,0L201,0L201,1462ZM371,836L651,836" @@ -1025,7 +1025,7 @@ test_hb_draw_subfont_scale (void) g_assert_cmpmem (str, draw_data.consumed, expected1, sizeof (expected1) - 1); draw_data.consumed = 0; - hb_font_get_glyph_shape (font2, 37, funcs, &draw_data); + hb_font_draw_glyph (font2, 37, funcs, &draw_data); char expected2[] = "M402,2924L1228,2924Q1810,2924 2070,2750Q2330,2576 2330,2200" "Q2330,1940 2185,1771Q2040,1602 1762,1552L1762,1532Q2428,1418 2428,832" "Q2428,440 2163,220Q1898,0 1422,0L402,0L402,2924ZM742,1672L1302,1672" @@ -1047,13 +1047,13 @@ test_hb_draw_subfont_scale (void) hb_font_set_scale (font2, x*2, y*2); draw_data.consumed = 0; - hb_font_get_glyph_shape (font1, 5, funcs, &draw_data); + hb_font_draw_glyph (font1, 5, funcs, &draw_data); char expected1[] = "M90,0L258,0C456,0 564,122 564,331C564,539 456,656 254,656L90,656L90,0Z" "M173,68L173,588L248,588C401,588 478,496 478,331C478,165 401,68 248,68L173,68Z"; g_assert_cmpmem (str, draw_data.consumed, expected1, sizeof (expected1) - 1); draw_data.consumed = 0; - hb_font_get_glyph_shape (font2, 5, funcs, &draw_data); + hb_font_draw_glyph (font2, 5, funcs, &draw_data); char expected2[] = "M180,0L516,0C912,0 1128,244 1128,662C1128,1078 912,1312 508,1312L180,1312L180,0Z" "M346,136L346,1176L496,1176C802,1176 956,992 956,662C956,330 802,136 496,136L346,136Z"; g_assert_cmpmem (str, draw_data.consumed, expected2, sizeof (expected2) - 1); @@ -1089,13 +1089,13 @@ static void test_hb_draw_ft (void) hb_face_destroy (face); { draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 0, funcs, &draw_data); + hb_font_draw_glyph (font, 0, funcs, &draw_data); char expected[] = "M50,0L50,750L450,750L450,0L50,0Z"; g_assert_cmpmem (str, draw_data.consumed, expected, sizeof (expected) - 1); } { draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 5, funcs, &draw_data); + hb_font_draw_glyph (font, 5, funcs, &draw_data); char expected[] = "M15,0Q15,0 15,0Z"; g_assert_cmpmem (str, draw_data.consumed, expected, sizeof (expected) - 1); } @@ -1108,7 +1108,7 @@ static void test_hb_draw_ft (void) hb_face_destroy (face); draw_data.consumed = 0; - hb_font_get_glyph_shape (font, 1, funcs, &draw_data); + hb_font_draw_glyph (font, 1, funcs, &draw_data); char expected[] = "M0,0C100,0 150,-20 250,-20C350,-20 400,0 500,0C500,100 520,150 520,250" "C520,350 500,400 500,500C400,500 350,520 250,520C150,520 100,500 0,500" "C0,400 -20,350 -20,250C-20,150 0,100 0,0ZM50,50C50,130 34,170 34,250" diff --git a/test/api/test-ot-color.c b/test/api/test-ot-color.c index c2bbad2d5..d5b387699 100644 --- a/test/api/test-ot-color.c +++ b/test/api/test-ot-color.c @@ -480,6 +480,7 @@ main (int argc, char **argv) hb_test_add (test_hb_ot_color_has_data); hb_test_add (test_hb_ot_color_png); hb_test_add (test_hb_ot_color_svg); + status = hb_test_run(); hb_face_destroy (cpal_v0); hb_face_destroy (cpal_v1); diff --git a/test/api/test-paint.c b/test/api/test-paint.c new file mode 100644 index 000000000..e1331ca0d --- /dev/null +++ b/test/api/test-paint.c @@ -0,0 +1,569 @@ +/* + * Copyright © 2022 Matthias Clasen + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb-test.h" + +#include +#include + +#ifdef HB_HAS_FREETYPE +#include +#endif + +/* Unit tests for hb-paint.h */ + +/* ---- */ + +typedef struct { + int level; + GString *string; +} paint_data_t; + +static void +print (paint_data_t *data, + const char *format, + ...) +{ + va_list args; + + g_string_append_printf (data->string, "%*s", 2 * data->level, ""); + + va_start (args, format); + g_string_append_vprintf (data->string, format, args); + va_end (args); + + g_string_append (data->string, "\n"); +} + +static void +push_transform (hb_paint_funcs_t *funcs, + void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data) +{ + paint_data_t *data = user_data; + + print (data, "start transform %f %f %f %f %f %f", xx, yx, xy, yy, dx, dy); + data->level++; +} + +static void +pop_transform (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data) +{ + paint_data_t *data = user_data; + + data->level--; + print (data, "end transform"); +} + +static void +push_clip_glyph (hb_paint_funcs_t *funcs, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data) +{ + paint_data_t *data = user_data; + + print (data, "start clip glyph %u", glyph); + data->level++; +} + +static void +push_clip_rectangle (hb_paint_funcs_t *funcs, + void *paint_data, + float xmin, float ymin, float xmax, float ymax, + void *user_data) +{ + paint_data_t *data = user_data; + + print (data, "start clip rectangle %f %f %f %f", xmin, ymin, xmax, ymax); + data->level++; +} + +static void +pop_clip (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data) +{ + paint_data_t *data = user_data; + + data->level--; + print (data, "end clip"); +} + +static void +paint_color (hb_paint_funcs_t *funcs, + void *paint_data, + hb_bool_t use_foreground, + hb_color_t color, + void *user_data) +{ + paint_data_t *data = user_data; + + print (data, "solid %d %d %d %d", + hb_color_get_red (color), + hb_color_get_green (color), + hb_color_get_blue (color), + hb_color_get_alpha (color)); +} + +static void +paint_image (hb_paint_funcs_t *funcs, + void *paint_data, + hb_blob_t *blob, + unsigned int width, + unsigned int height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents, + void *user_data) +{ + paint_data_t *data = user_data; + char buf[5] = { 0, }; + + hb_tag_to_string (format, buf); + print (data, "image type %s size %u %u slant %f extents %d %d %d %d\n", + buf, width, height, slant, + extents->x_bearing, extents->y_bearing, extents->width, extents->height); +} + +static void +print_color_line (paint_data_t *data, + hb_color_line_t *color_line) +{ + hb_color_stop_t *stops; + unsigned int len; + + len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL); + stops = alloca (len * sizeof (hb_color_stop_t)); + hb_color_line_get_color_stops (color_line, 0, &len, stops); + + print (data, "colors"); + data->level += 1; + for (unsigned int i = 0; i < len; i++) + print (data, "%f %d %d %d %d", + stops[i].offset, + hb_color_get_red (stops[i].color), + hb_color_get_green (stops[i].color), + hb_color_get_blue (stops[i].color), + hb_color_get_alpha (stops[i].color)); + data->level -= 1; +} + +static void +paint_linear_gradient (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2, + void *user_data) +{ + paint_data_t *data = user_data; + + print (data, "linear gradient"); + data->level += 1; + print (data, "p0 %f %f", x0, y0); + print (data, "p1 %f %f", x1, y1); + print (data, "p2 %f %f", x2, y2); + + print_color_line (data, color_line); + data->level -= 1; +} + +static void +paint_radial_gradient (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1, + void *user_data) +{ + paint_data_t *data = user_data; + + print (data, "radial gradient"); + data->level += 1; + print (data, "p0 %f %f radius %f", x0, y0, r0); + print (data, "p1 %f %f radius %f", x1, y1, r1); + + print_color_line (data, color_line); + data->level -= 1; +} + +static void +paint_sweep_gradient (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float cx, float cy, + float start_angle, + float end_angle, + void *user_data) +{ + paint_data_t *data = user_data; + + print (data, "sweep gradient"); + data->level++; + print (data, "center %f %f", cx, cy); + print (data, "angles %f %f", start_angle, end_angle); + + print_color_line (data, color_line); + data->level -= 1; +} + +static void +push_group (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data) +{ + paint_data_t *data = user_data; + print (data, "push group"); + data->level++; +} + +static void +pop_group (hb_paint_funcs_t *funcs, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data) +{ + paint_data_t *data = user_data; + data->level--; + print (data, "pop group mode %d", mode); +} + +typedef struct { + const char *font_file; + int scale; + float slant; + hb_codepoint_t glyph; + unsigned int palette; + const char *output; +} paint_test_t; + +#define NOTO_HAND "fonts/noto_handwriting-cff2_colr_1.otf" +#define TEST_GLYPHS "fonts/test_glyphs-glyf_colr_1.ttf" +#define ROCHER_ABC "fonts/RocherColorGX.abc.ttf" + +/* To verify the rendering visually, use + * + * hb-view --font-size SCALE --font-slant SLANT --font-palette PALETTE FONT --glyphs [gidGID=0+1000] + * + * where GID is the glyph value of the test. + */ +static paint_test_t paint_tests[] = { + /* COLRv1 */ + { NOTO_HAND, 20, 0., 10, 0, "hand-20-0-10" }, + { NOTO_HAND, 20, 0.2, 10, 0, "hand-20-0.2-10" }, + { TEST_GLYPHS, 20, 0, 6, 0, "test-20-0-6" }, // linear gradient + { TEST_GLYPHS, 20, 0, 10, 0, "test-20-0-10" }, // sweep gradient + { TEST_GLYPHS, 20, 0, 92, 0, "test-20-0-92" }, // radial gradient + { TEST_GLYPHS, 20, 0, 106, 0, "test-20-0-106" }, + { TEST_GLYPHS, 20, 0, 116, 0, "test-20-0-116" }, // compositing + { TEST_GLYPHS, 20, 0, 123, 0, "test-20-0-123" }, + { TEST_GLYPHS, 20, 0, 165, 0, "test-20-0-165" }, // linear gradient + { TEST_GLYPHS, 20, 0, 175, 0, "test-20-0-175" }, // layers + /* COLRv0 */ + { ROCHER_ABC, 120, 0.3, 1, 0, "rocher-120-0.3-1" }, + { ROCHER_ABC, 120, 0.3, 2, 2, "rocher-120-0.3-2" }, + { ROCHER_ABC, 120, 0, 3, 200, "rocher-120-0-3" }, +}; + +#ifdef HB_HAS_FREETYPE +static FT_Library library; +#endif + +static void +test_hb_paint (gconstpointer d, + hb_bool_t use_ft) +{ + const paint_test_t *test = d; + hb_face_t *face; + hb_font_t *font; + hb_paint_funcs_t *funcs; + paint_data_t data; + char *file; + char *buffer; + gsize len; + GError *error = NULL; + +#ifdef HB_HAS_FREETYPE + if (use_ft) + { + FT_Face ft_face; + char *path; + + path = g_test_build_filename (G_TEST_DIST, test->font_file, NULL); + if (FT_New_Face (library, path, 0, &ft_face) != 0) + { + g_test_message ("Failed to create FT_Face for %s", path); + g_test_fail (); + g_free (path); + return; + } + face = hb_ft_face_create_referenced (ft_face); + FT_Done_Face (ft_face); + g_free (path); + } + else +#endif + { + face = hb_test_open_font_file (test->font_file); + } + + font = hb_font_create (face); + hb_font_set_scale (font, test->scale, test->scale); + hb_font_set_synthetic_slant (font, test->slant); + + funcs = hb_paint_funcs_create (); + hb_paint_funcs_set_push_transform_func (funcs, push_transform, &data, NULL); + hb_paint_funcs_set_pop_transform_func (funcs, pop_transform, &data, NULL); + hb_paint_funcs_set_push_clip_glyph_func (funcs, push_clip_glyph, &data, NULL); + hb_paint_funcs_set_push_clip_rectangle_func (funcs, push_clip_rectangle, &data, NULL); + hb_paint_funcs_set_pop_clip_func (funcs, pop_clip, &data, NULL); + hb_paint_funcs_set_push_group_func (funcs, push_group, &data, NULL); + hb_paint_funcs_set_pop_group_func (funcs, pop_group, &data, NULL); + hb_paint_funcs_set_color_func (funcs, paint_color, &data, NULL); + hb_paint_funcs_set_image_func (funcs, paint_image, &data, NULL); + hb_paint_funcs_set_linear_gradient_func (funcs, paint_linear_gradient, &data, NULL); + hb_paint_funcs_set_radial_gradient_func (funcs, paint_radial_gradient, &data, NULL); + hb_paint_funcs_set_sweep_gradient_func (funcs, paint_sweep_gradient, &data, NULL); + + data.string = g_string_new (""); + data.level = 0; + + hb_font_paint_glyph (font, test->glyph, funcs, &data, 0, HB_COLOR (0, 0, 0, 255)); + + /* Run + * + * GENERATE_DATA=1 G_TEST_SRCDIR=./test/api ./build/test/api/test-ot-color -p TESTCASE > test/api/results/OUTPUT + * + * to produce the expected results file. + */ + if (getenv ("GENERATE_DATA")) + { + g_print ("%s", data.string->str); + exit (0); + } + + file = g_test_build_filename (G_TEST_DIST, "results", test->output, NULL); + if (!g_file_get_contents (file, &buffer, &len, &error)) + { + g_test_message ("File %s not found.", file); + g_test_fail (); + return; + } + + char **lines = g_strsplit (data.string->str, "\n", 0); + char **expected; + if (strstr (buffer, "\r\n")) + expected = g_strsplit (buffer, "\r\n", 0); + else + expected = g_strsplit (buffer, "\n", 0); + + if (g_strv_length (lines) != g_strv_length (expected)) + { + g_test_message ("Unexpected number of lines in output (%d instead of %d)", g_strv_length (lines), g_strv_length (expected)); + g_test_fail (); + } + else + { + unsigned int length = g_strv_length (lines); + for (unsigned int i = 0; i < length; i++) + { + if (strcmp (lines[i], expected[i]) != 0) + { + int pos; + for (pos = 0; lines[i][pos]; pos++) + if (lines[i][pos] != expected[i][pos]) + break; + + g_test_message ("Unxpected output at %d:%d (%#x instead of %#x):\n%s", i, pos, (unsigned int)lines[i][pos], (unsigned int)expected[i][pos], data.string->str); + g_test_fail (); + } + } + } + + g_strfreev (lines); + g_strfreev (expected); + + g_free (buffer); + g_free (file); + + g_string_free (data.string, TRUE); + + hb_paint_funcs_destroy (funcs); + hb_font_destroy (font); + hb_face_destroy (face); +} + +static void +test_hb_paint_ot (gconstpointer data) +{ + test_hb_paint (data, 0); +} + +static void +test_hb_paint_ft (gconstpointer data) +{ +#ifdef HB_HAS_FREETYPE + test_hb_paint (data, 1); +#else + g_test_skip_printf ("freetype support not present"); +#endif +} + +static void +scrutinize_linear_gradient (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2, + void *user_data) +{ + hb_bool_t *result = paint_data; + hb_color_stop_t *stops; + unsigned int len; + hb_color_stop_t *stops2; + unsigned int len2; + + *result = FALSE; + + len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL); + if (len == 0) + return; + + stops = malloc (len * sizeof (hb_color_stop_t)); + stops2 = malloc (len * sizeof (hb_color_stop_t)); + + hb_color_line_get_color_stops (color_line, 0, &len, stops); + hb_color_line_get_color_stops (color_line, 0, &len, stops2); + + // check that we can get stops twice + if (memcmp (stops, stops2, len * sizeof (hb_color_stop_t)) != 0) + { + free (stops); + free (stops2); + return; + } + + // check that we can get a single stop in the middle + len2 = 1; + hb_color_line_get_color_stops (color_line, len - 1, &len2, stops2); + if (memcmp (&stops[len - 1], stops2, sizeof (hb_color_stop_t)) != 0) + { + free (stops); + free (stops2); + return; + } + + free (stops); + free (stops2); + + *result = TRUE; +} + +static void +test_color_stops (hb_bool_t use_ft) +{ + hb_face_t *face; + hb_font_t *font; + hb_paint_funcs_t *funcs; + hb_bool_t result = FALSE; + +#ifdef HB_HAS_FREETYPE + if (use_ft) + { + FT_Face ft_face; + char *path; + + path = g_test_build_filename (G_TEST_DIST, NOTO_HAND, NULL); + if (FT_New_Face (library, path, 0, &ft_face) != 0) + { + g_test_message ("Failed to create FT_Face for %s", path); + g_test_fail (); + g_free (path); + return; + } + face = hb_ft_face_create_referenced (ft_face); + FT_Done_Face (ft_face); + g_free (path); + } + else +#endif + face = hb_test_open_font_file (NOTO_HAND); + + font = hb_font_create (face); + + funcs = hb_paint_funcs_create (); + hb_paint_funcs_set_linear_gradient_func (funcs, scrutinize_linear_gradient, NULL, NULL); + + hb_font_paint_glyph (font, 10, funcs, &result, 0, HB_COLOR (0, 0, 0, 255)); + + g_assert_true (result); + + hb_paint_funcs_destroy (funcs); + hb_font_destroy (font); + hb_face_destroy (face); +} + +static void +test_color_stops_ot (void) +{ + test_color_stops (0); +} + +static void +test_color_stops_ft (void) +{ + test_color_stops (1); +} + +int +main (int argc, char **argv) +{ + int status = 0; + +#ifdef HB_HAS_FREETYPE + FT_Init_FreeType (&library); +#endif + + hb_test_init (&argc, &argv); + for (unsigned int i = 0; i < G_N_ELEMENTS (paint_tests); i++) + { + hb_test_add_data_flavor (&paint_tests[i], paint_tests[i].output, test_hb_paint_ot); + hb_test_add_data_flavor (&paint_tests[i], paint_tests[i].output, test_hb_paint_ft); + } + hb_test_add (test_color_stops_ot); + hb_test_add (test_color_stops_ft); + + status = hb_test_run(); + + return status; +} diff --git a/test/fuzzing/hb-draw-fuzzer.cc b/test/fuzzing/hb-draw-fuzzer.cc index 5c53eb271..782db35b6 100644 --- a/test/fuzzing/hb-draw-fuzzer.cc +++ b/test/fuzzing/hb-draw-fuzzer.cc @@ -151,7 +151,7 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) hb_set_t *set = hb_set_create (); for (unsigned gid = 0; gid < glyph_count; ++gid) { - hb_font_get_glyph_shape (font, gid, funcs, &draw_data); + hb_font_draw_glyph (font, gid, funcs, &draw_data); /* Glyph extents also may practices the similar path, call it now that is related */ hb_glyph_extents_t extents; diff --git a/util/Makefile.sources b/util/Makefile.sources index 628de050e..e17b4fda1 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -2,6 +2,8 @@ HB_VIEW_sources = \ ansi-print.hh \ face-options.hh \ font-options.hh \ + hb-cairo-utils.h \ + hb-cairo-utils.c \ hb-view.cc \ helper-cairo-ansi.hh \ helper-cairo-ft.hh \ diff --git a/util/font-options.hh b/util/font-options.hh index 331efd7da..6dd8e3b12 100644 --- a/util/font-options.hh +++ b/util/font-options.hh @@ -69,6 +69,7 @@ struct font_options_t : face_options_t mutable double font_size_y = DEFAULT_FONT_SIZE; char *font_funcs = nullptr; int ft_load_flags = 2; + unsigned int palette = 0; hb_font_t *font = nullptr; }; @@ -287,6 +288,7 @@ font_options_t::add_options (option_parser_t *parser) G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"}, {"font-slant", 0, 0, G_OPTION_ARG_DOUBLE, &this->slant, "Set synthetic slant (default: 0)", "slant ratio; eg. 0.2"}, + {"font-palette", 0, 0, G_OPTION_ARG_INT, &this->palette, "Set font palette (default: 0)", "index"}, {"font-funcs", 0, 0, G_OPTION_ARG_STRING, &this->font_funcs, text, "impl"}, {"sub-font", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &this->sub_font, "Create a sub-font (default: false)", "boolean"}, diff --git a/util/hb-cairo-utils.c b/util/hb-cairo-utils.c new file mode 100644 index 000000000..27dda47e5 --- /dev/null +++ b/util/hb-cairo-utils.c @@ -0,0 +1,845 @@ +/* + * Copyright © 2022 Red Hat, Inc + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Matthias Clasen + */ + +#include "config.h" + +#include "hb-cairo-utils.h" + +#include +#include +#include +#include +#include +#define _USE_MATH_DEFINES +#include +#include + +#ifndef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif + +#ifndef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#endif + +#ifndef FALSE +#define TRUE 1 +#define FALSE 0 +#endif + +#define PREALLOCATED_COLOR_STOPS 16 + +typedef struct { + float r, g, b, a; +} color_t; + +static inline cairo_extend_t +cairo_extend (hb_paint_extend_t extend) +{ + switch (extend) + { + case HB_PAINT_EXTEND_PAD: return CAIRO_EXTEND_PAD; + case HB_PAINT_EXTEND_REPEAT: return CAIRO_EXTEND_REPEAT; + case HB_PAINT_EXTEND_REFLECT: return CAIRO_EXTEND_REFLECT; + default:; + } + + return CAIRO_EXTEND_PAD; +} + +#ifdef CAIRO_HAS_PNG_FUNCTIONS +typedef struct +{ + hb_blob_t *blob; + unsigned int offset; +} read_blob_data_t; + +static cairo_status_t +read_blob (void *closure, + unsigned char *data, + unsigned int length) +{ + read_blob_data_t *r = (read_blob_data_t *) closure; + const char *d; + unsigned int size; + + d = hb_blob_get_data (r->blob, &size); + + if (r->offset + length > size) + return CAIRO_STATUS_READ_ERROR; + + memcpy (data, d + r->offset, length); + r->offset += length; + + return CAIRO_STATUS_SUCCESS; +} +#endif + +static const cairo_user_data_key_t *_hb_cairo_surface_blob_user_data_key = {0}; + +static void +_hb_cairo_destroy_blob (void *p) +{ + hb_blob_destroy ((hb_blob_t *) p); +} + +void +hb_cairo_paint_glyph_image (cairo_t *cr, + hb_blob_t *blob, + unsigned width, + unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents) +{ + if (!extents) /* SVG currently. */ + return; + + cairo_surface_t *surface = NULL; + +#ifdef CAIRO_HAS_PNG_FUNCTIONS + if (format == HB_PAINT_IMAGE_FORMAT_PNG) + { + read_blob_data_t r; + r.blob = blob; + r.offset = 0; + surface = cairo_image_surface_create_from_png_stream (read_blob, &r); + + /* For PNG, width,height can be unreliable, as is the case for NotoColorEmoji :(. + * Just pull them out of the surface. */ + width = cairo_image_surface_get_width (surface); + height = cairo_image_surface_get_width (surface); + } + else +#endif + if (format == HB_PAINT_IMAGE_FORMAT_BGRA) + { + /* Byte-endian conversion. */ + unsigned data_size = hb_blob_get_length (blob); + if (data_size < width * height * 4) + return; + + unsigned char *data; +#ifdef __BYTE_ORDER + if (__BYTE_ORDER == __BIG_ENDIAN) + { + data = (unsigned char *) hb_blob_get_data_writable (blob, NULL); + if (!data) + return; + + unsigned count = width * height * 4; + for (unsigned i = 0; i < count; i += 4) + { + unsigned char b; + b = data[i]; + data[i] = data[i+3]; + data[i+3] = b; + b = data[i+1]; + data[i+1] = data[i+2]; + data[i+2] = b; + } + } + else +#endif + data = (unsigned char *) hb_blob_get_data (blob, NULL); + + surface = cairo_image_surface_create_for_data (data, + CAIRO_FORMAT_ARGB32, + width, height, + width * 4); + + cairo_surface_set_user_data (surface, + _hb_cairo_surface_blob_user_data_key, + hb_blob_reference (blob), + _hb_cairo_destroy_blob); + } + + if (!surface) + return; + + cairo_save (cr); + /* this clip is here to work around recording surface limitations */ + cairo_rectangle (cr, + extents->x_bearing, + extents->y_bearing, + extents->width, + extents->height); + cairo_clip (cr); + + cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); + + cairo_matrix_t matrix = {(double) width, 0, 0, (double) height, 0, 0}; + cairo_pattern_set_matrix (pattern, &matrix); + + /* Undo slant in the extents and apply it in the context. */ + extents->width -= extents->height * slant; + extents->x_bearing -= extents->y_bearing * slant; + cairo_matrix_t cairo_matrix = {1., 0., slant, 1., 0., 0.}; + cairo_transform (cr, &cairo_matrix); + + cairo_translate (cr, extents->x_bearing, extents->y_bearing); + cairo_scale (cr, extents->width, extents->height); + cairo_set_source (cr, pattern); + + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + cairo_surface_destroy (surface); + + cairo_restore (cr); +} + +static void +reduce_anchors (float x0, float y0, + float x1, float y1, + float x2, float y2, + float *xx0, float *yy0, + float *xx1, float *yy1) +{ + float q1x, q1y, q2x, q2y; + float s; + float k; + + q2x = x2 - x0; + q2y = y2 - y0; + q1x = y1 - x0; + q1y = y1 - y0; + + s = q2x * q2x + q2y * q2y; + if (s < 0.000001f) + { + *xx0 = x0; *yy0 = y0; + *xx1 = x1; *yy1 = y1; + return; + } + + k = (q2x * q1x + q2y * q1y) / s; + *xx0 = x0; + *yy0 = y0; + *xx1 = x1 - k * q2x; + *yy1 = y1 - k * q2y; +} + +static int +cmp_color_stop (const void *p1, + const void *p2) +{ + const hb_color_stop_t *c1 = (const hb_color_stop_t *) p1; + const hb_color_stop_t *c2 = (const hb_color_stop_t *) p2; + + if (c1->offset < c2->offset) + return -1; + else if (c1->offset > c2->offset) + return 1; + else + return 0; +} + +static void +normalize_color_line (hb_color_stop_t *stops, + unsigned int len, + float *omin, + float *omax) +{ + float min, max; + + qsort (stops, len, sizeof (hb_color_stop_t), cmp_color_stop); + + min = max = stops[0].offset; + for (unsigned int i = 0; i < len; i++) + { + min = MIN (min, stops[i].offset); + max = MAX (max, stops[i].offset); + } + + if (min != max) + { + for (unsigned int i = 0; i < len; i++) + stops[i].offset = (stops[i].offset - min) / (max - min); + } + + *omin = min; + *omax = max; +} + +void +hb_cairo_paint_linear_gradient (cairo_t *cr, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2) +{ + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + unsigned int len; + float xx0, yy0, xx1, yy1; + float xxx0, yyy0, xxx1, yyy1; + float min, max; + cairo_pattern_t *pattern; + + len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL); + if (len > PREALLOCATED_COLOR_STOPS) + stops = (hb_color_stop_t *) malloc (len * sizeof (hb_color_stop_t)); + hb_color_line_get_color_stops (color_line, 0, &len, stops); + + reduce_anchors (x0, y0, x1, y1, x2, y2, &xx0, &yy0, &xx1, &yy1); + normalize_color_line (stops, len, &min, &max); + + xxx0 = xx0 + min * (xx1 - xx0); + yyy0 = yy0 + min * (yy1 - yy0); + xxx1 = xx0 + max * (xx1 - xx0); + yyy1 = yy0 + max * (yy1 - yy0); + + pattern = cairo_pattern_create_linear (xxx0, yyy0, xxx1, yyy1); + cairo_pattern_set_extend (pattern, cairo_extend (hb_color_line_get_extend (color_line))); + for (unsigned int i = 0; i < len; i++) + { + double r, g, b, a; + r = hb_color_get_red (stops[i].color) / 255.; + g = hb_color_get_green (stops[i].color) / 255.; + b = hb_color_get_blue (stops[i].color) / 255.; + a = hb_color_get_alpha (stops[i].color) / 255.; + cairo_pattern_add_color_stop_rgba (pattern, stops[i].offset, r, g, b, a); + } + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + if (stops != stops_) + free (stops); +} + +void +hb_cairo_paint_radial_gradient (cairo_t *cr, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1) +{ + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + unsigned int len; + float min, max; + float xx0, yy0, xx1, yy1; + float rr0, rr1; + cairo_pattern_t *pattern; + + len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL); + if (len > PREALLOCATED_COLOR_STOPS) + stops = (hb_color_stop_t *) malloc (len * sizeof (hb_color_stop_t)); + hb_color_line_get_color_stops (color_line, 0, &len, stops); + + normalize_color_line (stops, len, &min, &max); + + xx0 = x0 + min * (x1 - x0); + yy0 = y0 + min * (y1 - y0); + xx1 = x0 + max * (x1 - x0); + yy1 = y0 + max * (y1 - y0); + rr0 = r0 + min * (r1 - r0); + rr1 = r0 + max * (r1 - r0); + + pattern = cairo_pattern_create_radial (xx0, yy0, rr0, xx1, yy1, rr1); + cairo_pattern_set_extend (pattern, cairo_extend (hb_color_line_get_extend (color_line))); + + for (unsigned int i = 0; i < len; i++) + { + double r, g, b, a; + r = hb_color_get_red (stops[i].color) / 255.; + g = hb_color_get_green (stops[i].color) / 255.; + b = hb_color_get_blue (stops[i].color) / 255.; + a = hb_color_get_alpha (stops[i].color) / 255.; + cairo_pattern_add_color_stop_rgba (pattern, stops[i].offset, r, g, b, a); + } + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + if (stops != stops_) + free (stops); +} + +typedef struct { + float x, y; +} Point; + +static inline float +interpolate (float f0, float f1, float f) +{ + return f0 + f * (f1 - f0); +} + +static inline void +premultiply (color_t *c) +{ + c->r *= c->a; + c->g *= c->a; + c->b *= c->a; +} + +static inline void +unpremultiply (color_t *c) +{ + if (c->a != 0.) + { + c->r /= c->a; + c->g /= c->a; + c->b /= c->a; + } +} + +static void +interpolate_colors (color_t *c0, color_t *c1, float k, color_t *c) +{ + // According to the COLR specification, gradients + // should be interpolated in premultiplied form + premultiply (c0); + premultiply (c1); + c->r = c0->r + k * (c1->r - c0->r); + c->g = c0->g + k * (c1->g - c0->g); + c->b = c0->b + k * (c1->b - c0->b); + c->a = c0->a + k * (c1->a - c0->a); + unpremultiply (c); +} + +static inline float +dot (Point p, Point q) +{ + return p.x * q.x + p.y * q.y; +} + +static inline Point +normalize (Point p) +{ + float len = sqrt (dot (p, p)); + + return (Point) { p.x / len, p.y / len }; +} + +static inline Point +sum (Point p, Point q) +{ + return (Point) { p.x + q.x, p.y + q.y }; +} + +static inline Point +difference (Point p, Point q) +{ + return (Point) { p.x - q.x, p.y - q.y }; +} + +static inline Point +scale (Point p, float f) +{ + return (Point) { p.x * f, p.y * f }; +} + +typedef struct { + Point center, p0, c0, c1, p1; + color_t color0, color1; +} Patch; + +static void +add_patch (cairo_pattern_t *pattern, Point *center, Patch *p) +{ + cairo_mesh_pattern_begin_patch (pattern); + cairo_mesh_pattern_move_to (pattern, center->x, center->y); + cairo_mesh_pattern_line_to (pattern, p->p0.x, p->p0.y); + cairo_mesh_pattern_curve_to (pattern, + p->c0.x, p->c0.y, + p->c1.x, p->c1.y, + p->p1.x, p->p1.y); + cairo_mesh_pattern_line_to (pattern, center->x, center->y); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, + p->color0.r, + p->color0.g, + p->color0.b, + p->color0.a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, + p->color0.r, + p->color0.g, + p->color0.b, + p->color0.a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, + p->color1.r, + p->color1.g, + p->color1.b, + p->color1.a); + cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, + p->color1.r, + p->color1.g, + p->color1.b, + p->color1.a); + cairo_mesh_pattern_end_patch (pattern); +} + +#define MAX_ANGLE (M_PI / 8.) + +static void +add_sweep_gradient_patches1 (float cx, float cy, float radius, + float a0, color_t *c0, + float a1, color_t *c1, + cairo_pattern_t *pattern) +{ + Point center = (Point) { cx, cy }; + int num_splits; + Point p0; + color_t color0, color1; + + num_splits = ceilf (fabs (a1 - a0) / MAX_ANGLE); + p0 = (Point) { cosf (a0), sinf (a0) }; + color0 = *c0; + + for (int a = 0; a < num_splits; a++) + { + float k = (a + 1.) / num_splits; + float angle1; + Point p1; + Point A, U; + Point C0, C1; + Patch patch; + + angle1 = interpolate (a0, a1, k); + interpolate_colors (c0, c1, k, &color1); + + patch.color0 = color0; + patch.color1 = color1; + + p1 = (Point) { cosf (angle1), sinf (angle1) }; + patch.p0 = sum (center, scale (p0, radius)); + patch.p1 = sum (center, scale (p1, radius)); + + A = normalize (sum (p0, p1)); + U = (Point) { -A.y, A.x }; + C0 = sum (A, scale (U, dot (difference (p0, A), p0) / dot (U, p0))); + C1 = sum (A, scale (U, dot (difference (p1, A), p1) / dot (U, p1))); + + patch.c0 = sum (center, scale (sum (C0, scale (difference (C0, p0), 0.33333)), radius)); + patch.c1 = sum (center, scale (sum (C1, scale (difference (C1, p1), 0.33333)), radius)); + + add_patch (pattern, ¢er, &patch); + + p0 = p1; + color0 = color1; + } +} + +static void +add_sweep_gradient_patches (hb_color_stop_t *stops, + unsigned int n_stops, + cairo_extend_t extend, + float cx, float cy, + float radius, + float start_angle, + float end_angle, + cairo_pattern_t *pattern) +{ + float angles_[PREALLOCATED_COLOR_STOPS]; + float *angles = angles_; + color_t colors_[PREALLOCATED_COLOR_STOPS]; + color_t *colors = colors_; + color_t color0, color1; + + if (start_angle == end_angle) + { + if (extend == CAIRO_EXTEND_PAD) + { + color_t c; + if (start_angle > 0) + { + c.r = hb_color_get_red (stops[0].color) / 255.; + c.g = hb_color_get_green (stops[0].color) / 255.; + c.b = hb_color_get_blue (stops[0].color) / 255.; + c.a = hb_color_get_alpha (stops[0].color) / 255.; + add_sweep_gradient_patches1 (cx, cy, radius, + 0., &c, + start_angle, &c, + pattern); + } + if (end_angle < 2 * M_PI) + { + c.r = hb_color_get_red (stops[n_stops - 1].color) / 255.; + c.g = hb_color_get_green (stops[n_stops - 1].color) / 255.; + c.b = hb_color_get_blue (stops[n_stops - 1].color) / 255.; + c.a = hb_color_get_alpha (stops[n_stops - 1].color) / 255.; + add_sweep_gradient_patches1 (cx, cy, radius, + end_angle, &c, + 2 * M_PI, &c, + pattern); + } + } + return; + } + + assert (start_angle != end_angle); + + /* handle directions */ + if (end_angle < start_angle) + { + float angle = end_angle; + end_angle = start_angle; + start_angle = angle; + + for (unsigned i = 0; i < n_stops - 1 - i; i++) + { + hb_color_stop_t stop = stops[i]; + stops[i] = stops[n_stops - 1 - i]; + stops[n_stops - 1 - i] = stop; + } + } + + if (n_stops > PREALLOCATED_COLOR_STOPS) + { + angles = (float *) malloc (sizeof (float) * n_stops); + colors = (color_t *) malloc (sizeof (color_t) * n_stops); + } + + for (unsigned i = 0; i < n_stops; i++) + { + angles[i] = start_angle + stops[i].offset * (end_angle - start_angle); + colors[i].r = hb_color_get_red (stops[i].color) / 255.; + colors[i].g = hb_color_get_green (stops[i].color) / 255.; + colors[i].b = hb_color_get_blue (stops[i].color) / 255.; + colors[i].a = hb_color_get_alpha (stops[i].color) / 255.; + } + + if (extend == CAIRO_EXTEND_PAD) + { + unsigned pos; + + color0 = colors[0]; + for (pos = 0; pos < n_stops; pos++) + { + if (angles[pos] >= 0) + { + if (pos > 0) + { + float k = (0 - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); + interpolate_colors (&colors[pos-1], &colors[pos], k, &color0); + } + break; + } + } + if (pos == n_stops) + { + /* everything is below 0 */ + color0 = colors[n_stops-1]; + add_sweep_gradient_patches1 (cx, cy, radius, + 0., &color0, + 2 * M_PI, &color0, + pattern); + goto done; + } + + add_sweep_gradient_patches1 (cx, cy, radius, + 0., &color0, + angles[pos], &colors[pos], + pattern); + + for (pos++; pos < n_stops; pos++) + { + if (angles[pos] <= 2 * M_PI) + { + add_sweep_gradient_patches1 (cx, cy, radius, + angles[pos - 1], &colors[pos-1], + angles[pos], &colors[pos], + pattern); + } + else + { + float k = (2 * M_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); + interpolate_colors (&colors[pos - 1], &colors[pos], k, &color1); + add_sweep_gradient_patches1 (cx, cy, radius, + angles[pos - 1], &colors[pos - 1], + 2 * M_PI, &color1, + pattern); + break; + } + } + + if (pos == n_stops) + { + /* everything is below 2*M_PI */ + color0 = colors[n_stops - 1]; + add_sweep_gradient_patches1 (cx, cy, radius, + angles[n_stops - 1], &color0, + 2 * M_PI, &color0, + pattern); + goto done; + } + } + else + { + int k; + float span; + + span = angles[n_stops - 1] - angles[0]; + k = 0; + if (angles[0] >= 0) + { + float ss = angles[0]; + while (ss > 0) + { + if (span > 0) + { + ss -= span; + k--; + } + else + { + ss += span; + k++; + } + } + } + else if (angles[0] < 0) + { + float ee = angles[n_stops - 1]; + while (ee < 0) + { + if (span > 0) + { + ee += span; + k++; + } + else + { + ee -= span; + k--; + } + } + } + + //assert (angles[0] + k * span <= 0 && 0 < angles[n_stops - 1] + k * span); + + for (unsigned l = k; 1; l++) + { + for (unsigned i = 1; i < n_stops; i++) + { + float a0, a1; + color_t *c0, *c1; + + if ((l % 2 != 0) && (extend == CAIRO_EXTEND_REFLECT)) + { + a0 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - (i-1)] + l * span; + a1 = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - i] + l * span; + c0 = &colors[n_stops - 1 - (i - 1)]; + c1 = &colors[n_stops - 1 - i]; + } + else + { + a0 = angles[i-1] + l * span; + a1 = angles[i] + l * span; + c0 = &colors[i-1]; + c1 = &colors[i]; + } + + if (a1 < 0) + continue; + if (a0 < 0) + { + color_t color; + float f = (0 - a0)/(a1 - a0); + interpolate_colors (c0, c1, f, &color); + add_sweep_gradient_patches1 (cx, cy, radius, + 0, &color, + a1, c1, + pattern); + } + else if (a1 >= 2 * M_PI) + { + color_t color; + float f = (2 * M_PI - a0)/(a1 - a0); + interpolate_colors (c0, c1, f, &color); + add_sweep_gradient_patches1 (cx, cy, radius, + a0, c0, + 2 * M_PI, &color, + pattern); + goto done; + } + else + { + add_sweep_gradient_patches1 (cx, cy, radius, + a0, c0, + a1, c1, + pattern); + } + } + } + } + +done: + + if (angles != angles_) + free (angles); + if (colors != colors_) + free (colors); +} + +void +hb_cairo_paint_sweep_gradient (cairo_t *cr, + hb_color_line_t *color_line, + float cx, float cy, + float start_angle, + float end_angle) +{ + unsigned int len; + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + cairo_extend_t extend; + double x1, y1, x2, y2; + float max_x, max_y, radius; + cairo_pattern_t *pattern; + + len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL); + if (len > PREALLOCATED_COLOR_STOPS) + stops = (hb_color_stop_t *) malloc (len * sizeof (hb_color_stop_t)); + hb_color_line_get_color_stops (color_line, 0, &len, stops); + + qsort (stops, len, sizeof (hb_color_stop_t), cmp_color_stop); + + cairo_clip_extents (cr, &x1, &y1, &x2, &y2); + max_x = MAX ((x1 - cx) * (x1 - cx), (x2 - cx) * (x2 - cx)); + max_y = MAX ((y1 - cy) * (y1 - cy), (y2 - cy) * (y2 - cy)); + radius = sqrt (max_x + max_y); + + extend = cairo_extend (hb_color_line_get_extend (color_line)); + pattern = cairo_pattern_create_mesh (); + + add_sweep_gradient_patches (stops, len, extend, cx, cy, + radius, start_angle, end_angle, pattern); + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_pattern_destroy (pattern); + + if (stops != stops_) + free (stops); +} diff --git a/util/hb-cairo-utils.h b/util/hb-cairo-utils.h new file mode 100644 index 000000000..fdf7c01af --- /dev/null +++ b/util/hb-cairo-utils.h @@ -0,0 +1,97 @@ +/* + * Copyright © 2022 Red Hat, Inc + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Matthias Clasen + */ + +#pragma once + +#include +#include + +HB_BEGIN_DECLS + +static inline cairo_operator_t +hb_paint_composite_mode_to_cairo (hb_paint_composite_mode_t mode) +{ + switch (mode) + { + case HB_PAINT_COMPOSITE_MODE_CLEAR: return CAIRO_OPERATOR_CLEAR; + case HB_PAINT_COMPOSITE_MODE_SRC: return CAIRO_OPERATOR_SOURCE; + case HB_PAINT_COMPOSITE_MODE_DEST: return CAIRO_OPERATOR_DEST; + case HB_PAINT_COMPOSITE_MODE_SRC_OVER: return CAIRO_OPERATOR_OVER; + case HB_PAINT_COMPOSITE_MODE_DEST_OVER: return CAIRO_OPERATOR_DEST_OVER; + case HB_PAINT_COMPOSITE_MODE_SRC_IN: return CAIRO_OPERATOR_IN; + case HB_PAINT_COMPOSITE_MODE_DEST_IN: return CAIRO_OPERATOR_DEST_IN; + case HB_PAINT_COMPOSITE_MODE_SRC_OUT: return CAIRO_OPERATOR_OUT; + case HB_PAINT_COMPOSITE_MODE_DEST_OUT: return CAIRO_OPERATOR_DEST_OUT; + case HB_PAINT_COMPOSITE_MODE_SRC_ATOP: return CAIRO_OPERATOR_ATOP; + case HB_PAINT_COMPOSITE_MODE_DEST_ATOP: return CAIRO_OPERATOR_DEST_ATOP; + case HB_PAINT_COMPOSITE_MODE_XOR: return CAIRO_OPERATOR_XOR; + case HB_PAINT_COMPOSITE_MODE_PLUS: return CAIRO_OPERATOR_ADD; + case HB_PAINT_COMPOSITE_MODE_SCREEN: return CAIRO_OPERATOR_SCREEN; + case HB_PAINT_COMPOSITE_MODE_OVERLAY: return CAIRO_OPERATOR_OVERLAY; + case HB_PAINT_COMPOSITE_MODE_DARKEN: return CAIRO_OPERATOR_DARKEN; + case HB_PAINT_COMPOSITE_MODE_LIGHTEN: return CAIRO_OPERATOR_LIGHTEN; + case HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: return CAIRO_OPERATOR_COLOR_DODGE; + case HB_PAINT_COMPOSITE_MODE_COLOR_BURN: return CAIRO_OPERATOR_COLOR_BURN; + case HB_PAINT_COMPOSITE_MODE_HARD_LIGHT: return CAIRO_OPERATOR_HARD_LIGHT; + case HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT: return CAIRO_OPERATOR_SOFT_LIGHT; + case HB_PAINT_COMPOSITE_MODE_DIFFERENCE: return CAIRO_OPERATOR_DIFFERENCE; + case HB_PAINT_COMPOSITE_MODE_EXCLUSION: return CAIRO_OPERATOR_EXCLUSION; + case HB_PAINT_COMPOSITE_MODE_MULTIPLY: return CAIRO_OPERATOR_MULTIPLY; + case HB_PAINT_COMPOSITE_MODE_HSL_HUE: return CAIRO_OPERATOR_HSL_HUE; + case HB_PAINT_COMPOSITE_MODE_HSL_SATURATION: return CAIRO_OPERATOR_HSL_SATURATION; + case HB_PAINT_COMPOSITE_MODE_HSL_COLOR: return CAIRO_OPERATOR_HSL_COLOR; + case HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY: return CAIRO_OPERATOR_HSL_LUMINOSITY; + default:; + } + + return CAIRO_OPERATOR_SOURCE; +} + +void hb_cairo_paint_glyph_image (cairo_t *cr, + hb_blob_t *blob, + unsigned width, + unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents); + +void hb_cairo_paint_linear_gradient (cairo_t *cr, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2); + +void hb_cairo_paint_radial_gradient (cairo_t *cr, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1); + +void hb_cairo_paint_sweep_gradient (cairo_t *cr, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, float end_angle); + +HB_END_DECLS diff --git a/util/helper-cairo-user.hh b/util/helper-cairo-user.hh index 8c1f9f222..3c54036bc 100644 --- a/util/helper-cairo-user.hh +++ b/util/helper-cairo-user.hh @@ -27,12 +27,40 @@ #ifndef HELPER_CAIRO_USER_HH #define HELPER_CAIRO_USER_HH +#include "config.h" + #include "font-options.hh" #include #include #include "hb-blob.hh" +#include "hb-cairo-utils.h" + +static bool debug = false; +static int level = 0; + +static void print (const char *format, ...) __attribute__((format (printf, 1, 2))); + +static void +print (const char *format, + ...) +{ + va_list args; + + if (!debug) + return; + + printf ("%*s", 2 * level, ""); + + va_start (args, format); + vprintf (format, args); + va_end (args); + + printf ("\n"); +} + +static const cairo_user_data_key_t _hb_font_cairo_user_data_key = {0}; static void move_to (hb_draw_funcs_t *dfuncs, @@ -41,6 +69,8 @@ move_to (hb_draw_funcs_t *dfuncs, float to_x, float to_y, void *) { + print ("move to %f %f", + (double) to_x, (double) to_y); cairo_move_to (cr, (double) to_x, (double) to_y); } @@ -52,6 +82,8 @@ line_to (hb_draw_funcs_t *dfuncs, float to_x, float to_y, void *) { + print ("line to %f %f", + (double) to_x, (double) to_y); cairo_line_to (cr, (double) to_x, (double) to_y); } @@ -65,6 +97,10 @@ cubic_to (hb_draw_funcs_t *dfuncs, float to_x, float to_y, void *) { + print ("cubic to %f %f %f %f %f %f", + (double) control1_x, (double) control1_y, + (double) control2_x, (double) control2_y, + (double) to_x, (double) to_y); cairo_curve_to (cr, (double) control1_x, (double) control1_y, (double) control2_x, (double) control2_y, @@ -77,12 +113,13 @@ close_path (hb_draw_funcs_t *dfuncs, hb_draw_state_t *st, void *) { + print ("close path"); cairo_close_path (cr); } static hb_draw_funcs_t * -get_cairo_draw_funcs () +get_cairo_draw_funcs (void) { static hb_draw_funcs_t *funcs; @@ -98,7 +135,299 @@ get_cairo_draw_funcs () return funcs; } -static const cairo_user_data_key_t _hb_font_cairo_user_data_key = {0}; +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + +static void +push_transform (hb_paint_funcs_t *funcs, + void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + cairo_matrix_t m; + + print ("start transform %f %f %f %f %f %f", + (double) xx, (double) yx, + (double) xy, (double) yy, + (double) dx, (double) dy); + level++; + + cairo_save (cr); + cairo_matrix_init (&m, (double) xx, (double) yx, + (double) xy, (double) yy, + (double) dx, (double) dy); + cairo_transform (cr, &m); +} + +static void +pop_transform (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + cairo_restore (cr); + + level--; + print ("end transform"); +} + +static void +push_clip_glyph (hb_paint_funcs_t *funcs, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + print ("start clip glyph %u", glyph); + level++; + + cairo_save (cr); + cairo_new_path (cr); + hb_font_draw_glyph (font, glyph, get_cairo_draw_funcs (), cr); + cairo_close_path (cr); + cairo_clip (cr); +} + +static void +push_clip_rectangle (hb_paint_funcs_t *funcs, + void *paint_data, + float xmin, float ymin, float xmax, float ymax, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + print ("start clip rectangle %f %f %f %f", + (double) xmin, (double) ymin, + (double) xmax, (double) ymax); + level++; + + cairo_save (cr); + cairo_rectangle (cr, + (double) xmin, (double) ymin, + (double) (xmax - xmin), (double) (ymax - ymin)); + cairo_clip (cr); +} + +static void +pop_clip (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + cairo_restore (cr); + + level--; + print ("end clip"); +} + +static void +push_group (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + print ("push group"); + level++; + + cairo_save (cr); + cairo_push_group (cr); +} + +static void +pop_group (hb_paint_funcs_t *funcs, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + cairo_pop_group_to_source (cr); + cairo_set_operator (cr, hb_paint_composite_mode_to_cairo (mode)); + cairo_paint (cr); + + cairo_restore (cr); + + level--; + print ("pop group mode %d", mode); +} + +static void +paint_color (hb_paint_funcs_t *funcs, + void *paint_data, + hb_bool_t use_foreground, + hb_color_t color, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + print ("solid (fg %d) %d %d %d %d", + use_foreground, + hb_color_get_red (color), + hb_color_get_green (color), + hb_color_get_blue (color), + hb_color_get_alpha (color)); + + cairo_set_source_rgba (cr, + hb_color_get_red (color) / 255., + hb_color_get_green (color) / 255., + hb_color_get_blue (color) / 255., + hb_color_get_alpha (color) / 255.); + cairo_paint (cr); +} + +static void +paint_image (hb_paint_funcs_t *funcs, + void *paint_data, + hb_blob_t *blob, + unsigned width, + unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + char buf[5] = { 0, }; + + hb_tag_to_string (format, buf); + print ("image type '%s' size %u %u slant %f extents %d %d %d %d", + buf, width, height, (double) slant, + extents->x_bearing, extents->y_bearing, extents->width, extents->height); + + hb_cairo_paint_glyph_image (cr, blob, width, height, format, slant, extents); +} + +static void +print_color_line (hb_color_line_t *color_line) +{ + hb_color_stop_t *stops; + unsigned int len; + + len = hb_color_line_get_color_stops (color_line, 0, NULL, NULL); + stops = (hb_color_stop_t *)alloca (len * sizeof (hb_color_stop_t)); + hb_color_line_get_color_stops (color_line, 0, &len, stops); + + print ("colors extend %d", hb_color_line_get_extend (color_line)); + level += 1; + for (unsigned int i = 0; i < len; i++) + print ("%f (fg %d) %d %d %d %d", + (double) stops[i].offset, + stops[i].is_foreground, + hb_color_get_red (stops[i].color), + hb_color_get_green (stops[i].color), + hb_color_get_blue (stops[i].color), + hb_color_get_alpha (stops[i].color)); + level -= 1; +} + +static void +paint_linear_gradient (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + print ("linear gradient"); + level += 1; + print ("p0 %f %f", (double) x0, (double) y0); + print ("p1 %f %f", (double) x1, (double) y1); + print ("p2 %f %f", (double) x2, (double) y2); + + print_color_line (color_line); + level -= 1; + + hb_cairo_paint_linear_gradient (cr, color_line, x0, y0, x1, y1, x2, y2); +} + +static void +paint_radial_gradient (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + print ("radial gradient"); + level += 1; + print ("p0 %f %f radius %f", (double) x0, (double) y0, (double) r0); + print ("p1 %f %f radius %f", (double) x1, (double) y1, (double) r1); + + print_color_line (color_line); + level -= 1; + + hb_cairo_paint_radial_gradient (cr, color_line, x0, y0, r0, x1, y1, r1); +} + +static void +paint_sweep_gradient (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, float end_angle, + void *user_data) +{ + cairo_t *cr = (cairo_t *)paint_data; + + print ("sweep gradient"); + level++; + print ("center %f %f", (double) x0, (double) y0); + print ("angles %f %f", (double) start_angle, (double) end_angle); + + print_color_line (color_line); + level--; + + hb_cairo_paint_sweep_gradient (cr, color_line, x0, y0, start_angle, end_angle); +} + +static hb_paint_funcs_t * +get_cairo_paint_funcs () +{ + static hb_paint_funcs_t *funcs; + + if (!funcs) + { + funcs = hb_paint_funcs_create (); + + hb_paint_funcs_set_push_transform_func (funcs, push_transform, nullptr, nullptr); + hb_paint_funcs_set_pop_transform_func (funcs, pop_transform, nullptr, nullptr); + hb_paint_funcs_set_push_clip_glyph_func (funcs, push_clip_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_rectangle_func (funcs, push_clip_rectangle, nullptr, nullptr); + hb_paint_funcs_set_pop_clip_func (funcs, pop_clip, nullptr, nullptr); + hb_paint_funcs_set_push_group_func (funcs, push_group, nullptr, nullptr); + hb_paint_funcs_set_pop_group_func (funcs, pop_group, nullptr, nullptr); + hb_paint_funcs_set_color_func (funcs, paint_color, nullptr, nullptr); + hb_paint_funcs_set_image_func (funcs, paint_image, nullptr, nullptr); + hb_paint_funcs_set_linear_gradient_func (funcs, paint_linear_gradient, nullptr, nullptr); + hb_paint_funcs_set_radial_gradient_func (funcs, paint_radial_gradient, nullptr, nullptr); + hb_paint_funcs_set_sweep_gradient_func (funcs, paint_sweep_gradient, nullptr, nullptr); + } + + return funcs; +} + +#endif + +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC +static cairo_status_t +render_color_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents); +#endif static cairo_status_t render_glyph (cairo_scaled_font_t *scaled_font, @@ -106,6 +435,12 @@ render_glyph (cairo_scaled_font_t *scaled_font, cairo_t *cr, cairo_text_extents_t *extents) { +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + static char *p = getenv ("HB_DRAW"); + if (p && atoi (p) >= 2) + return render_color_glyph (scaled_font, glyph, cr, extents); +#endif + hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), &_hb_font_cairo_user_data_key)); @@ -113,7 +448,7 @@ render_glyph (cairo_scaled_font_t *scaled_font, hb_font_get_scale (font, &x_scale, &y_scale); cairo_scale (cr, +1./x_scale, -1./y_scale); - hb_font_get_glyph_shape (font, glyph, get_cairo_draw_funcs (), cr); + hb_font_draw_glyph (font, glyph, get_cairo_draw_funcs (), cr); cairo_fill (cr); return CAIRO_STATUS_SUCCESS; @@ -121,158 +456,45 @@ render_glyph (cairo_scaled_font_t *scaled_font, #ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC -#ifdef CAIRO_HAS_PNG_FUNCTIONS -static inline cairo_status_t -_hb_bytes_read_func (hb_bytes_t *src, - char *data, - unsigned length) -{ - if (unlikely (src->length < length)) - return CAIRO_STATUS_READ_ERROR; - - memcpy (data, src->arrayZ, length); - *src += length; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -render_color_glyph_png (cairo_scaled_font_t *scaled_font, - unsigned long glyph, - cairo_t *cr, - cairo_text_extents_t *extents) -{ - hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), - &_hb_font_cairo_user_data_key)); - - hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, glyph); - if (blob == hb_blob_get_empty ()) - return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; - - hb_position_t x_scale, y_scale; - hb_font_get_scale (font, &x_scale, &y_scale); - cairo_scale (cr, +1./x_scale, -1./y_scale); - - /* Draw PNG. */ - hb_bytes_t bytes = blob->as_bytes (); - cairo_surface_t *surface = cairo_image_surface_create_from_png_stream ((cairo_read_func_t) _hb_bytes_read_func, - std::addressof (bytes)); - hb_blob_destroy (blob); - - if (unlikely (cairo_surface_status (surface)) != CAIRO_STATUS_SUCCESS) - { - cairo_surface_destroy (surface); - return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; - } - - int width = cairo_image_surface_get_width (surface); - int height = cairo_image_surface_get_width (surface); - - hb_glyph_extents_t hb_extents; - if (unlikely (!hb_font_get_glyph_extents (font, glyph, &hb_extents))) - { - cairo_surface_destroy (surface); - return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; - } - - cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface); - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); - - cairo_matrix_t matrix = {(double) width, 0, 0, (double) height, 0, 0}; - cairo_pattern_set_matrix (pattern, &matrix); - - cairo_translate (cr, hb_extents.x_bearing, hb_extents.y_bearing); - cairo_scale (cr, hb_extents.width, hb_extents.height); - cairo_set_source (cr, pattern); - - cairo_rectangle (cr, 0, 0, 1, 1); - cairo_fill (cr); - cairo_pattern_destroy (pattern); - - cairo_surface_destroy (surface); - return CAIRO_STATUS_SUCCESS; -} -#endif - -static cairo_status_t -render_color_glyph_layers (cairo_scaled_font_t *scaled_font, - unsigned long glyph, - cairo_t *cr, - cairo_text_extents_t *extents) -{ - hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), - &_hb_font_cairo_user_data_key)); - hb_face_t *face = hb_font_get_face (font); - - unsigned count = hb_ot_color_glyph_get_layers (face, glyph, 0, nullptr, nullptr); - if (!count) - return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; - - hb_ot_color_layer_t layers[16]; - unsigned offset = 0, len; - do { - len = ARRAY_LENGTH (layers); - hb_ot_color_glyph_get_layers (face, glyph, - offset, - &len, - layers); - for (unsigned i = 0; i < len; i++) - { - hb_color_t color; - unsigned clen = 1; - unsigned color_index = layers[i].color_index; - bool is_foreground = color_index == 65535; - - if (!is_foreground) - { - hb_ot_color_palette_get_colors (face, - 0/*palette_index*/, - color_index/*start_offset*/, - &clen/*color_count*/, - &color); - if (clen < 1) - continue; - } - - cairo_save (cr); - { - if (!is_foreground) - cairo_set_source_rgba (cr, - hb_color_get_red (color) / 255., - hb_color_get_green (color) / 255., - hb_color_get_blue (color) / 255., - hb_color_get_alpha (color) / 255.); - - cairo_status_t ret = render_glyph (scaled_font, layers[i].glyph, cr, extents); - if (ret != CAIRO_STATUS_SUCCESS) - return ret; - } - cairo_restore (cr); - } - } - while (len == ARRAY_LENGTH (layers)); - return CAIRO_STATUS_SUCCESS; -} - static cairo_status_t render_color_glyph (cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *extents) { - cairo_status_t ret = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; - -#ifdef CAIRO_HAS_PNG_FUNCTIONS - ret = render_color_glyph_png (scaled_font, glyph, cr, extents); - if (ret != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) - return ret; + hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), + &_hb_font_cairo_user_data_key)); + unsigned int palette = 0; +#ifdef CAIRO_COLOR_PALETTE_DEFAULT + cairo_font_options_t *options = cairo_font_options_create (); + cairo_scaled_font_get_font_options (scaled_font, options); + palette = cairo_font_options_get_color_palette (options); + cairo_font_options_destroy (options); #endif - ret = render_color_glyph_layers (scaled_font, glyph, cr, extents); - if (ret != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) - return ret; + hb_color_t color = HB_COLOR (0, 0, 0, 255); + cairo_pattern_t *pattern = cairo_get_source (cr); - return render_glyph (scaled_font, glyph, cr, extents); + if (cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID) + { + double r, g, b, a; + cairo_pattern_get_rgba (pattern, &r, &g, &b, &a); + color = HB_COLOR ((int)(b * 255.), (int)(g * 255.), (int) (r * 255.), (int)(a * 255.)); + } + + hb_position_t x_scale, y_scale; + hb_font_get_scale (font, &x_scale, &y_scale); + cairo_scale (cr, +1./x_scale, -1./y_scale); + + if (getenv ("HB_PAINT_DEBUG")) + { + debug = atoi (getenv ("HB_PAINT_DEBUG")); + level = 0; + } + + hb_font_paint_glyph (font, glyph, get_cairo_paint_funcs (), cr, palette, color); + + return CAIRO_STATUS_SUCCESS; } #endif @@ -288,10 +510,13 @@ helper_cairo_create_user_font_face (const font_options_t *font_opts) (cairo_destroy_func_t) hb_font_destroy); cairo_user_font_face_set_render_glyph_func (cairo_face, render_glyph); + #ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC hb_face_t *face = hb_font_get_face (font_opts->font); - if (hb_ot_color_has_png (face) || hb_ot_color_has_layers (face)) + if (hb_ot_color_has_png (face) || hb_ot_color_has_layers (face) || hb_ot_color_has_paint (face)) + { cairo_user_font_face_set_render_color_glyph_func (cairo_face, render_color_glyph); + } #endif return cairo_face; @@ -310,7 +535,7 @@ helper_cairo_user_scaled_font_has_color (cairo_scaled_font_t *scaled_font) hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), &_hb_font_cairo_user_data_key)); hb_face_t *face = hb_font_get_face (font); - return hb_ot_color_has_png (face) || hb_ot_color_has_layers (face); + return hb_ot_color_has_png (face) || hb_ot_color_has_layers (face) || hb_ot_color_has_paint (face); } #endif diff --git a/util/helper-cairo.hh b/util/helper-cairo.hh index a8463de4d..a877ed394 100644 --- a/util/helper-cairo.hh +++ b/util/helper-cairo.hh @@ -118,6 +118,9 @@ helper_cairo_create_scaled_font (const font_options_t *font_opts) font_options = cairo_font_options_create (); cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF); +#ifdef CAIRO_COLOR_PALETTE_DEFAULT + cairo_font_options_set_color_palette (font_options, font_opts->palette); +#endif cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face, &font_matrix, diff --git a/util/meson.build b/util/meson.build index 41965994c..23b8f1ca9 100644 --- a/util/meson.build +++ b/util/meson.build @@ -1,5 +1,6 @@ hb_view_sources = [ 'hb-view.cc', + 'hb-cairo-utils.c' ] hb_shape_sources = [