Merge pull request #3411 from harfbuzz/draw

[draw] Finish and release draw API
This commit is contained in:
Behdad Esfahbod 2022-02-15 18:47:55 -06:00 committed by GitHub
commit a396543ae1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2068 additions and 710 deletions

View File

@ -3,7 +3,6 @@ set -e
meson --cross-file=.ci/win32-cross-file.txt \
--wrap-mode=forcefallback \
--buildtype=release \
-Dtests=disabled \
-Dcairo=enabled \
-Dcairo:fontconfig=disabled \

View File

@ -3,7 +3,6 @@ set -e
meson --cross-file=.ci/win64-cross-file.txt \
--wrap-mode=forcefallback \
--buildtype=release \
-Dtests=disabled \
-Dcairo=enabled \
-Dcairo:fontconfig=disabled \

View File

@ -194,6 +194,10 @@ AC_ARG_WITH(cairo,
have_cairo=false
if test "x$with_cairo" = "xyes" -o "x$with_cairo" = "xauto"; then
PKG_CHECK_MODULES(CAIRO, cairo >= 1.8.0, have_cairo=true, :)
save_libs=$LIBS
LIBS="$LIBS $CAIRO_LIBS"
AC_CHECK_FUNCS(cairo_user_font_face_set_render_color_glyph_func)
LIBS=$save_libs
fi
if test "x$with_cairo" = "xyes" -a "x$have_cairo" != "xtrue"; then
AC_MSG_ERROR([cairo support requested but not found])

View File

@ -56,6 +56,7 @@
<xi:include href="xml/hb-blob.xml"/>
<xi:include href="xml/hb-buffer.xml"/>
<xi:include href="xml/hb-common.xml"/>
<xi:include href="xml/hb-draw.xml"/>
<xi:include href="xml/hb-deprecated.xml"/>
<xi:include href="xml/hb-face.xml"/>
<xi:include href="xml/hb-font.xml"/>

View File

@ -169,6 +169,7 @@ HB_FEATURE_GLOBAL_START
HB_BEGIN_DECLS
HB_END_DECLS
hb_var_int_t
hb_var_num_t
int16_t
int32_t
int64_t
@ -183,6 +184,33 @@ HB_DEPRECATED
HB_DEPRECATED_FOR
</SECTION>
<SECTION>
<FILE>hb-draw</FILE>
hb_draw_funcs_t
hb_draw_funcs_create
hb_draw_funcs_destroy
hb_draw_funcs_reference
hb_draw_funcs_is_immutable
hb_draw_funcs_make_immutable
hb_draw_move_to_func_t
hb_draw_funcs_set_move_to_func
hb_draw_line_to_func_t
hb_draw_funcs_set_line_to_func
hb_draw_quadratic_to_func_t
hb_draw_funcs_set_quadratic_to_func
hb_draw_cubic_to_func_t
hb_draw_funcs_set_cubic_to_func
hb_draw_close_path_func_t
hb_draw_funcs_set_close_path_func
hb_draw_state_t
HB_DRAW_STATE_DEFAULT
hb_draw_move_to
hb_draw_line_to
hb_draw_quadratic_to
hb_draw_cubic_to
hb_draw_close_path
</SECTION>
<SECTION>
<FILE>hb-deprecated</FILE>
HB_BUFFER_FLAGS_DEFAULT
@ -281,6 +309,7 @@ hb_font_funcs_set_glyph_h_advances_func
hb_font_funcs_set_glyph_h_kerning_func
hb_font_funcs_set_glyph_h_origin_func
hb_font_funcs_set_glyph_name_func
hb_font_funcs_set_glyph_shape_func
hb_font_funcs_set_glyph_v_advance_func
hb_font_funcs_set_glyph_v_advances_func
hb_font_funcs_set_glyph_v_origin_func
@ -318,6 +347,8 @@ hb_font_get_glyph_name
hb_font_get_glyph_name_func_t
hb_font_get_glyph_origin_for_direction
hb_font_get_glyph_origin_func_t
hb_font_get_glyph_shape
hb_font_get_glyph_shape_func_t
hb_font_get_glyph_v_advance
hb_font_get_glyph_v_advance_func_t
hb_font_get_glyph_v_advances

View File

@ -186,6 +186,11 @@ endif
if cairo_dep.found()
conf.set('HAVE_CAIRO', 1)
if cairo_dep.type_name() == 'internal'
conf.set('HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC', 1)
else
check_funcs += [['cairo_user_font_face_set_render_color_glyph_func', {'deps': cairo_dep}]]
endif
endif
if cairo_ft_dep.found()

View File

@ -8,9 +8,7 @@
#ifdef HAVE_FREETYPE
enum backend_t { HARFBUZZ, FREETYPE, TTF_PARSER };
#include "perf-extents.hh"
#ifdef HB_EXPERIMENTAL_API
#include "perf-draw.hh"
#endif
#endif
BENCHMARK_MAIN ();

View File

@ -19,23 +19,7 @@ symbols = sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re
if '--experimental-api' not in sys.argv:
# Move these to harfbuzz-sections.txt when got stable
experimental_symbols = \
"""hb_font_draw_glyph
hb_draw_funcs_t
hb_draw_close_path_func_t
hb_draw_cubic_to_func_t
hb_draw_line_to_func_t
hb_draw_move_to_func_t
hb_draw_quadratic_to_func_t
hb_draw_funcs_create
hb_draw_funcs_destroy
hb_draw_funcs_is_immutable
hb_draw_funcs_make_immutable
hb_draw_funcs_reference
hb_draw_funcs_set_close_path_func
hb_draw_funcs_set_cubic_to_func
hb_draw_funcs_set_line_to_func
hb_draw_funcs_set_move_to_func
hb_draw_funcs_set_quadratic_to_func""".splitlines ()
"""""".splitlines ()
symbols = [x for x in symbols if x not in experimental_symbols]
symbols = "\n".join (symbols)

View File

@ -130,6 +130,16 @@ typedef union _hb_var_int_t {
int8_t i8[4];
} hb_var_int_t;
typedef union _hb_var_num_t {
float f;
uint32_t u32;
int32_t i32;
uint16_t u16[2];
int16_t i16[2];
uint8_t u8[4];
int8_t i8[4];
} hb_var_num_t;
/* hb_tag_t */

View File

@ -25,237 +25,313 @@
#include "hb.hh"
#ifndef HB_NO_DRAW
#ifdef HB_EXPERIMENTAL_API
#include "hb-draw.hh"
#include "hb-ot.h"
#include "hb-ot-glyf-table.hh"
#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
/**
* hb_draw_funcs_set_move_to_func:
* @funcs: draw functions object
* @move_to: move-to callback
* SECTION:hb-draw
* @title: hb-draw
* @short_description: Glyph drawing
* @include: hb.h
*
* Sets move-to callback to the draw functions object.
*
* Since: EXPERIMENTAL
* Functions for drawing (extracting) glyph shapes.
**/
void
hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *funcs,
hb_draw_move_to_func_t move_to)
{
if (unlikely (hb_object_is_immutable (funcs))) return;
funcs->move_to = move_to;
}
/**
* hb_draw_funcs_set_line_to_func:
* @funcs: draw functions object
* @line_to: line-to callback
*
* Sets line-to callback to the draw functions object.
*
* Since: EXPERIMENTAL
**/
void
hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *funcs,
hb_draw_line_to_func_t line_to)
{
if (unlikely (hb_object_is_immutable (funcs))) return;
funcs->line_to = line_to;
}
static void
hb_draw_move_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
hb_draw_state_t *st HB_UNUSED,
float to_x HB_UNUSED, float to_y HB_UNUSED,
void *user_data HB_UNUSED) {}
/**
* hb_draw_funcs_set_quadratic_to_func:
* @funcs: draw functions object
* @move_to: quadratic-to callback
*
* Sets quadratic-to callback to the draw functions object.
*
* Since: EXPERIMENTAL
**/
void
hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *funcs,
hb_draw_quadratic_to_func_t quadratic_to)
{
if (unlikely (hb_object_is_immutable (funcs))) return;
funcs->quadratic_to = quadratic_to;
funcs->is_quadratic_to_set = true;
}
static void
hb_draw_line_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
hb_draw_state_t *st HB_UNUSED,
float to_x HB_UNUSED, float to_y HB_UNUSED,
void *user_data HB_UNUSED) {}
/**
* hb_draw_funcs_set_cubic_to_func:
* @funcs: draw functions
* @cubic_to: cubic-to callback
*
* Sets cubic-to callback to the draw functions object.
*
* Since: EXPERIMENTAL
**/
void
hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *funcs,
hb_draw_cubic_to_func_t cubic_to)
static void
hb_draw_quadratic_to_nil (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float control_x, float control_y,
float to_x, float to_y,
void *user_data HB_UNUSED)
{
if (unlikely (hb_object_is_immutable (funcs))) return;
funcs->cubic_to = cubic_to;
}
/**
* hb_draw_funcs_set_close_path_func:
* @funcs: draw functions object
* @close_path: close-path callback
*
* Sets close-path callback to the draw functions object.
*
* Since: EXPERIMENTAL
**/
void
hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *funcs,
hb_draw_close_path_func_t close_path)
{
if (unlikely (hb_object_is_immutable (funcs))) return;
funcs->close_path = close_path;
dfuncs->emit_cubic_to (draw_data, *st,
(st->current_x + 2.f * control_x) / 3.f,
(st->current_y + 2.f * control_y) / 3.f,
(to_x + 2.f * control_x) / 3.f,
(to_y + 2.f * control_y) / 3.f,
to_x, to_y);
}
static void
_move_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {}
hb_draw_cubic_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
hb_draw_state_t *st HB_UNUSED,
float control1_x HB_UNUSED, float control1_y HB_UNUSED,
float control2_x HB_UNUSED, float control2_y HB_UNUSED,
float to_x HB_UNUSED, float to_y HB_UNUSED,
void *user_data HB_UNUSED) {}
static void
_line_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {}
hb_draw_close_path_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED,
hb_draw_state_t *st HB_UNUSED,
void *user_data HB_UNUSED) {}
static void
_quadratic_to_nil (hb_position_t control_x HB_UNUSED, hb_position_t control_y HB_UNUSED,
hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED,
void *user_data HB_UNUSED) {}
static void
_cubic_to_nil (hb_position_t control1_x HB_UNUSED, hb_position_t control1_y HB_UNUSED,
hb_position_t control2_x HB_UNUSED, hb_position_t control2_y HB_UNUSED,
hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED,
void *user_data HB_UNUSED) {}
#define HB_DRAW_FUNC_IMPLEMENT(name) \
\
void \
hb_draw_funcs_set_##name##_func (hb_draw_funcs_t *dfuncs, \
hb_draw_##name##_func_t func, \
void *user_data, \
hb_destroy_func_t destroy) \
{ \
if (hb_object_is_immutable (dfuncs)) \
return; \
\
if (dfuncs->destroy.name) \
dfuncs->destroy.name (dfuncs->user_data.name); \
\
if (func) { \
dfuncs->func.name = func; \
dfuncs->user_data.name = user_data; \
dfuncs->destroy.name = destroy; \
} else { \
dfuncs->func.name = hb_draw_##name##_nil; \
dfuncs->user_data.name = nullptr; \
dfuncs->destroy.name = nullptr; \
} \
}
static void
_close_path_nil (void *user_data HB_UNUSED) {}
HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_DRAW_FUNC_IMPLEMENT
/**
* hb_draw_funcs_create:
* hb_draw_funcs_create: (Xconstructor)
*
* Creates a new draw callbacks object.
*
* Since: EXPERIMENTAL
* Return value: (transfer full):
* A newly allocated #hb_draw_funcs_t with a reference count of 1. The initial
* reference count should be released with hb_draw_funcs_destroy when you are
* done using the #hb_draw_funcs_t. This function never returns %NULL. If
* memory cannot be allocated, a special singleton #hb_draw_funcs_t object will
* be returned.
*
* Since: REPLACEME
**/
hb_draw_funcs_t *
hb_draw_funcs_create ()
{
hb_draw_funcs_t *funcs;
if (unlikely (!(funcs = hb_object_create<hb_draw_funcs_t> ())))
hb_draw_funcs_t *dfuncs;
if (unlikely (!(dfuncs = hb_object_create<hb_draw_funcs_t> ())))
return const_cast<hb_draw_funcs_t *> (&Null (hb_draw_funcs_t));
funcs->move_to = (hb_draw_move_to_func_t) _move_to_nil;
funcs->line_to = (hb_draw_line_to_func_t) _line_to_nil;
funcs->quadratic_to = (hb_draw_quadratic_to_func_t) _quadratic_to_nil;
funcs->is_quadratic_to_set = false;
funcs->cubic_to = (hb_draw_cubic_to_func_t) _cubic_to_nil;
funcs->close_path = (hb_draw_close_path_func_t) _close_path_nil;
return funcs;
dfuncs->func = Null (hb_draw_funcs_t).func;
return dfuncs;
}
DEFINE_NULL_INSTANCE (hb_draw_funcs_t) =
{
HB_OBJECT_HEADER_STATIC,
{
#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_nil,
HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_DRAW_FUNC_IMPLEMENT
}
};
/**
* hb_draw_funcs_reference:
* @funcs: draw functions
* hb_draw_funcs_reference: (skip)
* @dfuncs: draw functions
*
* Add to callbacks object refcount.
* Increases the reference count on @dfuncs by one. This prevents @buffer from
* being destroyed until a matching call to hb_draw_funcs_destroy() is made.
*
* Returns: The same object.
* Since: EXPERIMENTAL
* Return value: (transfer full):
* The referenced #hb_draw_funcs_t.
*
* Since: REPLACEME
**/
hb_draw_funcs_t *
hb_draw_funcs_reference (hb_draw_funcs_t *funcs)
hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs)
{
return hb_object_reference (funcs);
return hb_object_reference (dfuncs);
}
/**
* hb_draw_funcs_destroy:
* @funcs: draw functions
* hb_draw_funcs_destroy: (skip)
* @dfuncs: draw functions
*
* Decreases refcount of callbacks object and deletes the object if it reaches
* to zero.
* Deallocate the @dfuncs.
* Decreases the reference count on @dfuncs by one. If the result is zero, then
* @dfuncs and all associated resources are freed. See hb_draw_funcs_reference().
*
* Since: EXPERIMENTAL
* Since: REPLACEME
**/
void
hb_draw_funcs_destroy (hb_draw_funcs_t *funcs)
hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs)
{
if (!hb_object_destroy (funcs)) return;
if (!hb_object_destroy (dfuncs)) return;
hb_free (funcs);
#define HB_DRAW_FUNC_IMPLEMENT(name) \
if (dfuncs->destroy.name) dfuncs->destroy.name (dfuncs->user_data.name);
HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_DRAW_FUNC_IMPLEMENT
hb_free (dfuncs);
}
/**
* hb_draw_funcs_make_immutable:
* @funcs: draw functions
* @dfuncs: draw functions
*
* Makes funcs object immutable.
* Makes @dfuncs object immutable.
*
* Since: EXPERIMENTAL
* Since: REPLACEME
**/
void
hb_draw_funcs_make_immutable (hb_draw_funcs_t *funcs)
hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs)
{
if (hb_object_is_immutable (funcs))
if (hb_object_is_immutable (dfuncs))
return;
hb_object_make_immutable (funcs);
hb_object_make_immutable (dfuncs);
}
/**
* hb_draw_funcs_is_immutable:
* @funcs: draw functions
* @dfuncs: draw functions
*
* Checks whether funcs is immutable.
* Checks whether @dfuncs is immutable.
*
* Returns: If is immutable.
* Since: EXPERIMENTAL
* Return value: %true if @dfuncs is immutable, %false otherwise
*
* Since: REPLACEME
**/
hb_bool_t
hb_draw_funcs_is_immutable (hb_draw_funcs_t *funcs)
hb_draw_funcs_is_immutable (hb_draw_funcs_t *dfuncs)
{
return hb_object_is_immutable (funcs);
return hb_object_is_immutable (dfuncs);
}
/**
* hb_draw_move_to:
* @dfuncs: draw functions
* @draw_data: associated draw data passed by the caller
* @st: current draw state
* @to_x: X component of target point
* @to_y: Y component of target point
*
* Perform a "move-to" draw operation.
*
* Since: REPLACEME
**/
void
hb_draw_move_to (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float to_x, float to_y)
{
dfuncs->move_to (draw_data, *st,
to_x, to_y);
}
/**
* hb_font_draw_glyph:
* @font: a font object
* @glyph: a glyph id
* @funcs: draw callbacks object
* @user_data: parameter you like be passed to the callbacks when are called
* hb_draw_line_to:
* @dfuncs: draw functions
* @draw_data: associated draw data passed by the caller
* @st: current draw state
* @to_x: X component of target point
* @to_y: Y component of target point
*
* Draw a glyph.
* Perform a "line-to" draw operation.
*
* Returns: Whether the font had the glyph and the operation completed successfully.
* Since: EXPERIMENTAL
* Since: REPLACEME
**/
hb_bool_t
hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph,
const hb_draw_funcs_t *funcs,
void *user_data)
void
hb_draw_line_to (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float to_x, float to_y)
{
if (unlikely (funcs == &Null (hb_draw_funcs_t) ||
glyph >= font->face->get_num_glyphs ()))
return false;
draw_helper_t draw_helper (funcs, user_data);
if (font->face->table.glyf->get_path (font, glyph, draw_helper)) return true;
#ifndef HB_NO_CFF
if (font->face->table.cff1->get_path (font, glyph, draw_helper)) return true;
if (font->face->table.cff2->get_path (font, glyph, draw_helper)) return true;
#endif
return false;
dfuncs->line_to (draw_data, *st,
to_x, to_y);
}
#endif
/**
* hb_draw_quadratic_to:
* @dfuncs: draw functions
* @draw_data: associated draw data passed by the caller
* @st: current draw state
* @control_x: X component of control point
* @control_y: Y component of control point
* @to_x: X component of target point
* @to_y: Y component of target point
*
* Perform a "quadratic-to" draw operation.
*
* Since: REPLACEME
**/
void
hb_draw_quadratic_to (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float control_x, float control_y,
float to_x, float to_y)
{
dfuncs->quadratic_to (draw_data, *st,
control_x, control_y,
to_x, to_y);
}
/**
* hb_draw_cubic_to:
* @dfuncs: draw functions
* @draw_data: associated draw data passed by the caller
* @st: current draw state
* @control1_x: X component of first control point
* @control1_y: Y component of first control point
* @control2_x: X component of second control point
* @control2_y: Y component of second control point
* @to_x: X component of target point
* @to_y: Y component of target point
*
* Perform a "cubic-to" draw operation.
*
* Since: REPLACEME
**/
void
hb_draw_cubic_to (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y)
{
dfuncs->cubic_to (draw_data, *st,
control1_x, control1_y,
control2_x, control2_y,
to_x, to_y);
}
/**
* hb_draw_close_path:
* @dfuncs: draw functions
* @draw_data: associated draw data passed by the caller
* @st: current draw state
*
* Perform a "close-path" draw operation.
*
* Since: REPLACEME
**/
void
hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st)
{
dfuncs->close_path (draw_data, *st);
}
#endif

View File

@ -33,65 +33,292 @@
HB_BEGIN_DECLS
#ifdef HB_EXPERIMENTAL_API
typedef void (*hb_draw_move_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data);
typedef void (*hb_draw_line_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data);
typedef void (*hb_draw_quadratic_to_func_t) (hb_position_t control_x, hb_position_t control_y,
hb_position_t to_x, hb_position_t to_y,
void *user_data);
typedef void (*hb_draw_cubic_to_func_t) (hb_position_t control1_x, hb_position_t control1_y,
hb_position_t control2_x, hb_position_t control2_y,
hb_position_t to_x, hb_position_t to_y,
void *user_data);
typedef void (*hb_draw_close_path_func_t) (void *user_data);
/**
* hb_draw_state_t
* @path_open: Whether there is an open path
* @path_start_x: X component of the start of current path
* @path_start_y: Y component of the start of current path
* @current_x: X component of current point
* @current_y: Y component of current point
*
* Current drawing state.
*
* Since: REPLACEME
**/
typedef struct hb_draw_state_t {
hb_bool_t path_open;
float path_start_x;
float path_start_y;
float current_x;
float current_y;
/*< private >*/
hb_var_num_t reserved1;
hb_var_num_t reserved2;
hb_var_num_t reserved3;
hb_var_num_t reserved4;
hb_var_num_t reserved5;
hb_var_num_t reserved6;
hb_var_num_t reserved7;
} hb_draw_state_t;
/**
* HB_DRAW_STATE_DEFAULT:
*
* The default #hb_draw_state_t at the start of glyph drawing.
*/
#define HB_DRAW_STATE_DEFAULT {0, 0.f, 0.f, 0.f, 0.f, {0.}, {0.}, {0.}}
/**
* hb_draw_funcs_t:
*
* Glyph draw callbacks.
*
* _move_to, _line_to and _cubic_to calls are necessary to be defined but we
* translate _quadratic_to calls to _cubic_to if the callback isn't defined.
* #hb_draw_move_to_func_t, #hb_draw_line_to_func_t and
* #hb_draw_cubic_to_func_t calls are necessary to be defined but we translate
* #hb_draw_quadratic_to_func_t calls to #hb_draw_cubic_to_func_t if the
* callback isn't defined.
*
* Since: EXPERIMENTAL
* Since: REPLACEME
**/
typedef struct hb_draw_funcs_t hb_draw_funcs_t;
HB_EXTERN void
hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *funcs,
hb_draw_move_to_func_t move_to);
HB_EXTERN void
hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *funcs,
hb_draw_line_to_func_t line_to);
/**
* hb_draw_move_to_func_t:
* @dfuncs: draw functions object
* @draw_data: The data accompanying the draw functions
* @st: current draw state
* @to_x: X component of target point
* @to_y: Y component of target point
* @user_data: User data pointer passed by the caller
*
* A virtual method for the #hb_draw_funcs_t to perform a "move-to" draw
* operation.
*
* Since: REPLACEME
*
**/
typedef void (*hb_draw_move_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float to_x, float to_y,
void *user_data);
HB_EXTERN void
hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *funcs,
hb_draw_quadratic_to_func_t quadratic_to);
/**
* hb_draw_line_to_func_t:
* @dfuncs: draw functions object
* @draw_data: The data accompanying the draw functions
* @st: current draw state
* @to_x: X component of target point
* @to_y: Y component of target point
* @user_data: User data pointer passed by the caller
*
* A virtual method for the #hb_draw_funcs_t to perform a "line-to" draw
* operation.
*
* Since: REPLACEME
*
**/
typedef void (*hb_draw_line_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float to_x, float to_y,
void *user_data);
HB_EXTERN void
hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *funcs,
hb_draw_cubic_to_func_t cubic_to);
/**
* hb_draw_quadratic_to_func_t:
* @dfuncs: draw functions object
* @draw_data: The data accompanying the draw functions
* @st: current draw state
* @control_x: X component of control point
* @control_y: Y component of control point
* @to_x: X component of target point
* @to_y: Y component of target point
* @user_data: User data pointer passed by the caller
*
* A virtual method for the #hb_draw_funcs_t to perform a "quadratic-to" draw
* operation.
*
* Since: REPLACEME
*
**/
typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float control_x, float control_y,
float to_x, float to_y,
void *user_data);
/**
* hb_draw_cubic_to_func_t:
* @dfuncs: draw functions object
* @draw_data: The data accompanying the draw functions
* @st: current draw state
* @control1_x: X component of first control point
* @control1_y: Y component of first control point
* @control2_x: X component of second control point
* @control2_y: Y component of second control point
* @to_x: X component of target point
* @to_y: Y component of target point
* @user_data: User data pointer passed by the caller
*
* A virtual method for the #hb_draw_funcs_t to perform a "cubic-to" draw
* operation.
*
* Since: REPLACEME
*
**/
typedef void (*hb_draw_cubic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_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 *user_data);
/**
* hb_draw_close_path_func_t:
* @dfuncs: draw functions object
* @draw_data: The data accompanying the draw functions
* @st: current draw state
* @user_data: User data pointer passed by the caller
*
* A virtual method for the #hb_draw_funcs_t to perform a "close-path" draw
* operation.
*
* Since: REPLACEME
*
**/
typedef void (*hb_draw_close_path_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
void *user_data);
/**
* hb_draw_funcs_set_move_to_func:
* @dfuncs: draw functions object
* @func: (closure user_data) (destroy destroy) (scope notified): move-to callback
* @user_data: Data to pass to @func
* @destroy: (nullable): The function to call when @user_data is not needed anymore
*
* Sets move-to callback to the draw functions object.
*
* Since: REPLACEME
**/
HB_EXTERN void
hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *funcs,
hb_draw_close_path_func_t close_path);
hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *dfuncs,
hb_draw_move_to_func_t func,
void *user_data, hb_destroy_func_t destroy);
/**
* hb_draw_funcs_set_line_to_func:
* @dfuncs: draw functions object
* @func: (closure user_data) (destroy destroy) (scope notified): line-to callback
* @user_data: Data to pass to @func
* @destroy: (nullable): The function to call when @user_data is not needed anymore
*
* Sets line-to callback to the draw functions object.
*
* Since: REPLACEME
**/
HB_EXTERN void
hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *dfuncs,
hb_draw_line_to_func_t func,
void *user_data, hb_destroy_func_t destroy);
/**
* hb_draw_funcs_set_quadratic_to_func:
* @dfuncs: draw functions object
* @func: (closure user_data) (destroy destroy) (scope notified): quadratic-to callback
* @user_data: Data to pass to @func
* @destroy: (nullable): The function to call when @user_data is not needed anymore
*
* Sets quadratic-to callback to the draw functions object.
*
* Since: REPLACEME
**/
HB_EXTERN void
hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *dfuncs,
hb_draw_quadratic_to_func_t func,
void *user_data, hb_destroy_func_t destroy);
/**
* hb_draw_funcs_set_cubic_to_func:
* @dfuncs: draw functions
* @func: (closure user_data) (destroy destroy) (scope notified): cubic-to callback
* @user_data: Data to pass to @func
* @destroy: (nullable): The function to call when @user_data is not needed anymore
*
* Sets cubic-to callback to the draw functions object.
*
* Since: REPLACEME
**/
HB_EXTERN void
hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *dfuncs,
hb_draw_cubic_to_func_t func,
void *user_data, hb_destroy_func_t destroy);
/**
* hb_draw_funcs_set_close_path_func:
* @dfuncs: draw functions object
* @func: (closure user_data) (destroy destroy) (scope notified): close-path callback
* @user_data: Data to pass to @func
* @destroy: (nullable): The function to call when @user_data is not needed anymore
*
* Sets close-path callback to the draw functions object.
*
* Since: REPLACEME
**/
HB_EXTERN void
hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *dfuncs,
hb_draw_close_path_func_t func,
void *user_data, hb_destroy_func_t destroy);
HB_EXTERN hb_draw_funcs_t *
hb_draw_funcs_create (void);
HB_EXTERN hb_draw_funcs_t *
hb_draw_funcs_reference (hb_draw_funcs_t *funcs);
hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs);
HB_EXTERN void
hb_draw_funcs_destroy (hb_draw_funcs_t *funcs);
hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs);
HB_EXTERN void
hb_draw_funcs_make_immutable (hb_draw_funcs_t *funcs);
hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs);
HB_EXTERN hb_bool_t
hb_draw_funcs_is_immutable (hb_draw_funcs_t *funcs);
#endif
hb_draw_funcs_is_immutable (hb_draw_funcs_t *dfuncs);
HB_EXTERN void
hb_draw_move_to (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float to_x, float to_y);
HB_EXTERN void
hb_draw_line_to (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float to_x, float to_y);
HB_EXTERN void
hb_draw_quadratic_to (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float control_x, float control_y,
float to_x, float to_y);
HB_EXTERN void
hb_draw_cubic_to (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st,
float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y);
HB_EXTERN void
hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data,
hb_draw_state_t *st);
HB_END_DECLS

View File

@ -27,113 +27,205 @@
#include "hb.hh"
#ifdef HB_EXPERIMENTAL_API
/*
* hb_draw_funcs_t
*/
#define HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS \
HB_DRAW_FUNC_IMPLEMENT (move_to) \
HB_DRAW_FUNC_IMPLEMENT (line_to) \
HB_DRAW_FUNC_IMPLEMENT (quadratic_to) \
HB_DRAW_FUNC_IMPLEMENT (cubic_to) \
HB_DRAW_FUNC_IMPLEMENT (close_path) \
/* ^--- Add new callbacks here */
struct hb_draw_funcs_t
{
hb_object_header_t header;
hb_draw_move_to_func_t move_to;
hb_draw_line_to_func_t line_to;
hb_draw_quadratic_to_func_t quadratic_to;
bool is_quadratic_to_set;
hb_draw_cubic_to_func_t cubic_to;
hb_draw_close_path_func_t close_path;
};
struct {
#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_func_t name;
HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_DRAW_FUNC_IMPLEMENT
} func;
struct draw_helper_t
{
draw_helper_t (const hb_draw_funcs_t *funcs_, void *user_data_)
{
funcs = funcs_;
user_data = user_data_;
path_open = false;
path_start_x = current_x = path_start_y = current_y = 0;
}
~draw_helper_t () { end_path (); }
struct {
#define HB_DRAW_FUNC_IMPLEMENT(name) void *name;
HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_DRAW_FUNC_IMPLEMENT
} user_data;
void move_to (hb_position_t x, hb_position_t y)
struct {
#define HB_DRAW_FUNC_IMPLEMENT(name) hb_destroy_func_t name;
HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_DRAW_FUNC_IMPLEMENT
} destroy;
void emit_move_to (void *draw_data, hb_draw_state_t &st,
float to_x, float to_y)
{ func.move_to (this, draw_data, &st,
to_x, to_y,
user_data.move_to); }
void emit_line_to (void *draw_data, hb_draw_state_t &st,
float to_x, float to_y)
{ func.line_to (this, draw_data, &st,
to_x, to_y,
user_data.line_to); }
void emit_quadratic_to (void *draw_data, hb_draw_state_t &st,
float control_x, float control_y,
float to_x, float to_y)
{ func.quadratic_to (this, draw_data, &st,
control_x, control_y,
to_x, to_y,
user_data.quadratic_to); }
void emit_cubic_to (void *draw_data, hb_draw_state_t &st,
float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y)
{ func.cubic_to (this, draw_data, &st,
control1_x, control1_y,
control2_x, control2_y,
to_x, to_y,
user_data.cubic_to); }
void emit_close_path (void *draw_data, hb_draw_state_t &st)
{ func.close_path (this, draw_data, &st,
user_data.close_path); }
void move_to (void *draw_data, hb_draw_state_t &st,
float to_x, float to_y)
{
if (path_open) end_path ();
current_x = path_start_x = x;
current_y = path_start_y = y;
if (st.path_open) close_path (draw_data, st);
st.current_x = to_x;
st.current_y = to_y;
}
void line_to (hb_position_t x, hb_position_t y)
void line_to (void *draw_data, hb_draw_state_t &st,
float to_x, float to_y)
{
if (equal_to_current (x, y)) return;
if (!path_open) start_path ();
funcs->line_to (x, y, user_data);
current_x = x;
current_y = y;
if (!st.path_open) start_path (draw_data, st);
emit_line_to (draw_data, st, to_x, to_y);
st.current_x = to_x;
st.current_y = to_y;
}
void
quadratic_to (hb_position_t control_x, hb_position_t control_y,
hb_position_t to_x, hb_position_t to_y)
quadratic_to (void *draw_data, hb_draw_state_t &st,
float control_x, float control_y,
float to_x, float to_y)
{
if (equal_to_current (control_x, control_y) && equal_to_current (to_x, to_y))
return;
if (!path_open) start_path ();
if (funcs->is_quadratic_to_set)
funcs->quadratic_to (control_x, control_y, to_x, to_y, user_data);
else
funcs->cubic_to (roundf ((current_x + 2.f * control_x) / 3.f),
roundf ((current_y + 2.f * control_y) / 3.f),
roundf ((to_x + 2.f * control_x) / 3.f),
roundf ((to_y + 2.f * control_y) / 3.f),
to_x, to_y, user_data);
current_x = to_x;
current_y = to_y;
if (!st.path_open) start_path (draw_data, st);
emit_quadratic_to (draw_data, st, control_x, control_y, to_x, to_y);
st.current_x = to_x;
st.current_y = to_y;
}
void
cubic_to (hb_position_t control1_x, hb_position_t control1_y,
hb_position_t control2_x, hb_position_t control2_y,
hb_position_t to_x, hb_position_t to_y)
cubic_to (void *draw_data, hb_draw_state_t &st,
float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y)
{
if (equal_to_current (control1_x, control1_y) &&
equal_to_current (control2_x, control2_y) &&
equal_to_current (to_x, to_y))
return;
if (!path_open) start_path ();
funcs->cubic_to (control1_x, control1_y, control2_x, control2_y, to_x, to_y, user_data);
current_x = to_x;
current_y = to_y;
if (!st.path_open) start_path (draw_data, st);
emit_cubic_to (draw_data, st, control1_x, control1_y, control2_x, control2_y, to_x, to_y);
st.current_x = to_x;
st.current_y = to_y;
}
void end_path ()
void
close_path (void *draw_data, hb_draw_state_t &st)
{
if (path_open)
if (st.path_open)
{
if ((path_start_x != current_x) || (path_start_y != current_y))
funcs->line_to (path_start_x, path_start_y, user_data);
funcs->close_path (user_data);
if ((st.path_start_x != st.current_x) || (st.path_start_y != st.current_y))
emit_line_to (draw_data, st, st.path_start_x, st.path_start_y);
emit_close_path (draw_data, st);
}
path_open = false;
path_start_x = current_x = path_start_y = current_y = 0;
st.path_open = false;
st.path_start_x = st.current_x = st.path_start_y = st.current_y = 0;
}
protected:
bool equal_to_current (hb_position_t x, hb_position_t y)
{ return current_x == x && current_y == y; }
void start_path ()
void start_path (void *draw_data, hb_draw_state_t &st)
{
if (path_open) end_path ();
path_open = true;
funcs->move_to (path_start_x, path_start_y, user_data);
assert (!st.path_open);
emit_move_to (draw_data, st, st.current_x, st.current_y);
st.path_open = true;
st.path_start_x = st.current_x;
st.path_start_y = st.current_y;
}
};
DECLARE_NULL_INSTANCE (hb_draw_funcs_t);
struct hb_draw_session_t
{
hb_draw_session_t (hb_draw_funcs_t *funcs_, void *draw_data_, float slant_ = 0.f)
: slant {slant_}, not_slanted {slant == 0.f},
funcs {funcs_}, draw_data {draw_data_}, st HB_DRAW_STATE_DEFAULT
{}
~hb_draw_session_t () { close_path (); }
void move_to (float to_x, float to_y)
{
if (likely (not_slanted))
funcs->move_to (draw_data, st,
to_x, to_y);
else
funcs->move_to (draw_data, st,
to_x + to_y * slant, to_y);
}
void line_to (float to_x, float to_y)
{
if (likely (not_slanted))
funcs->line_to (draw_data, st,
to_x, to_y);
else
funcs->line_to (draw_data, st,
to_x + to_y * slant, to_y);
}
void
quadratic_to (float control_x, float control_y,
float to_x, float to_y)
{
if (likely (not_slanted))
funcs->quadratic_to (draw_data, st,
control_x, control_y,
to_x, to_y);
else
funcs->quadratic_to (draw_data, st,
control_x + control_y * slant, control_y,
to_x + to_y * slant, to_y);
}
void
cubic_to (float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y)
{
if (likely (not_slanted))
funcs->cubic_to (draw_data, st,
control1_x, control1_y,
control2_x, control2_y,
to_x, to_y);
else
funcs->cubic_to (draw_data, st,
control1_x + control1_y * slant, control1_y,
control2_x + control2_y * slant, control2_y,
to_x + to_y * slant, to_y);
}
void close_path ()
{
funcs->close_path (draw_data, st);
}
hb_position_t path_start_x;
hb_position_t path_start_y;
hb_position_t current_x;
hb_position_t current_y;
bool path_open;
const hb_draw_funcs_t *funcs;
void *user_data;
protected:
float slant;
bool not_slanted;
hb_draw_funcs_t *funcs;
void *draw_data;
hb_draw_state_t st;
};
#endif
#endif /* HB_DRAW_HH */

View File

@ -29,6 +29,7 @@
#include "hb.hh"
#include "hb-font.hh"
#include "hb-draw.hh"
#include "hb-machinery.hh"
#include "hb-ot.h"
@ -501,6 +502,136 @@ hb_font_get_glyph_from_name_default (hb_font_t *font,
return font->parent->get_glyph_from_name (name, len, glyph);
}
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)
{
}
typedef struct hb_font_get_glyph_shape_default_adaptor_t {
hb_draw_funcs_t *draw_funcs;
void *draw_data;
float x_scale;
float y_scale;
} hb_font_get_glyph_shape_default_adaptor_t;
static void
hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED,
void *draw_data,
hb_draw_state_t *st,
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;
float x_scale = adaptor->x_scale;
float y_scale = adaptor->y_scale;
adaptor->draw_funcs->emit_move_to (adaptor->draw_data, *st,
x_scale * to_x, y_scale * to_y);
}
static void
hb_draw_line_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data,
hb_draw_state_t *st,
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;
float x_scale = adaptor->x_scale;
float y_scale = adaptor->y_scale;
st->current_x *= x_scale;
st->current_y *= y_scale;
adaptor->draw_funcs->emit_line_to (adaptor->draw_data, *st,
x_scale * to_x, y_scale * to_y);
}
static void
hb_draw_quadratic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data,
hb_draw_state_t *st,
float control_x, float control_y,
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;
float x_scale = adaptor->x_scale;
float y_scale = adaptor->y_scale;
st->current_x *= x_scale;
st->current_y *= y_scale;
adaptor->draw_funcs->emit_quadratic_to (adaptor->draw_data, *st,
x_scale * control_x, y_scale * control_y,
x_scale * to_x, y_scale * to_y);
}
static void
hb_draw_cubic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_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 *user_data HB_UNUSED)
{
hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data;
float x_scale = adaptor->x_scale;
float y_scale = adaptor->y_scale;
st->current_x *= x_scale;
st->current_y *= y_scale;
adaptor->draw_funcs->emit_cubic_to (adaptor->draw_data, *st,
x_scale * control1_x, y_scale * control1_y,
x_scale * control2_x, y_scale * control2_y,
x_scale * to_x, y_scale * to_y);
}
static void
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;
adaptor->draw_funcs->emit_close_path (adaptor->draw_data, *st);
}
static const hb_draw_funcs_t _hb_draw_funcs_default = {
HB_OBJECT_HEADER_STATIC,
{
#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_default,
HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_DRAW_FUNC_IMPLEMENT
}
};
static void
hb_font_get_glyph_shape_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 = {
draw_funcs,
draw_data,
(float) font->x_scale / (float) font->parent->x_scale,
(float) font->y_scale / (float) font->parent->y_scale
};
font->parent->get_glyph_shape (glyph,
const_cast<hb_draw_funcs_t *> (&_hb_draw_funcs_default),
&adaptor);
}
DEFINE_NULL_INSTANCE (hb_font_funcs_t) =
{
HB_OBJECT_HEADER_STATIC,
@ -1168,6 +1299,26 @@ hb_font_get_glyph_from_name (hb_font_t *font,
return font->get_glyph_from_name (name, len, glyph);
}
/**
* hb_font_get_glyph_shape:
* @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
*
* Fetches the glyph shape that corresponds to a glyph in the specified @font.
* The shape is returned by way of calls to the callsbacks of the @dfuncs
* objects, with @draw_data passed to them.
*
* Since: REPLACEME
**/
void
hb_font_get_glyph_shape (hb_font_t *font,
hb_codepoint_t glyph,
hb_draw_funcs_t *dfuncs, void *draw_data)
{
font->get_glyph_shape (glyph, dfuncs, draw_data);
}
/* A bit higher-level, and with fallback */
@ -2044,12 +2195,16 @@ hb_font_get_ptem (hb_font_t *font)
* @slant: synthetic slant value.
*
* Sets the "synthetic slant" of a font. By default is zero.
* Synthetic slant is the graphical skew that the renderer
* applies to the font at rendering time.
* Synthetic slant is the graphical skew applied to the font
* at rendering time.
*
* HarfBuzz needs to know this value to adjust shaping results,
* metrics, and style values to match the slanted rendering.
*
* <note>Note: The glyph shape fetched via the
* hb_font_get_glyph_shape() is slanted to reflect this value
* as well.</note>
*
* <note>Note: The slant value is a ratio. For example, a
* 20% slant would be represented as a 0.2 value.</note>
*

View File

@ -511,6 +511,25 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *
hb_codepoint_t *glyph,
void *user_data);
/**
* hb_font_get_glyph_shape_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_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);
/* func setters */
@ -770,6 +789,22 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs,
hb_font_get_glyph_from_name_func_t func,
void *user_data, hb_destroy_func_t destroy);
/**
* hb_font_funcs_set_glyph_shape_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_get_glyph_shape_func_t.
*
* Since: REPLACEME
**/
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);
/* func dispatch */
HB_EXTERN hb_bool_t
@ -850,6 +885,11 @@ hb_font_get_glyph_from_name (hb_font_t *font,
const char *name, int len, /* -1 means nul-terminated */
hb_codepoint_t *glyph);
HB_EXTERN void
hb_font_get_glyph_shape (hb_font_t *font,
hb_codepoint_t glyph,
hb_draw_funcs_t *dfuncs, void *draw_data);
/* high-level funcs, with fallback */
@ -1056,11 +1096,6 @@ HB_EXTERN void
hb_font_set_var_named_instance (hb_font_t *font,
unsigned instance_index);
#ifdef HB_EXPERIMENTAL_API
HB_EXTERN hb_bool_t
hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph,
const hb_draw_funcs_t *funcs, void *user_data);
#endif
HB_END_DECLS

View File

@ -57,6 +57,7 @@
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) \
/* ^--- Add new callbacks here */
struct hb_font_funcs_t
@ -140,6 +141,8 @@ struct hb_font_t
hb_position_t em_scalef_y (float v) { return em_scalef (v, y_scale); }
float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); }
float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); }
float em_fscalef_x (float v) { return em_fscalef (v, x_scale); }
float em_fscalef_y (float v) { return em_fscalef (v, y_scale); }
hb_position_t em_scale_dir (int16_t v, hb_direction_t direction)
{ return em_mult (v, dir_mult (direction)); }
@ -373,6 +376,15 @@ struct hb_font_t
klass->user_data.glyph_from_name);
}
void get_glyph_shape (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.glyph_shape);
}
/* A bit higher-level, and with fallback */
@ -625,7 +637,9 @@ struct hb_font_t
hb_position_t em_mult (int16_t v, int64_t mult)
{ return (hb_position_t) ((v * mult + 32768) >> 16); }
hb_position_t em_scalef (float v, int scale)
{ return (hb_position_t) roundf (v * scale / face->get_upem ()); }
{ return (hb_position_t) roundf (em_fscalef (v, scale)); }
float em_fscalef (float v, int scale)
{ return v * scale / face->get_upem (); }
float em_fscale (int16_t v, int scale)
{ return (float) v * scale / face->get_upem (); }
};

View File

@ -33,12 +33,14 @@
#include "hb-ft.h"
#include "hb-draw.hh"
#include "hb-font.hh"
#include "hb-machinery.hh"
#include "hb-cache.hh"
#include FT_ADVANCES_H
#include FT_MULTIPLE_MASTERS_H
#include FT_OUTLINE_H
#include FT_TRUETYPE_TABLES_H
@ -565,6 +567,82 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED,
return true;
}
#ifndef HB_NO_DRAW
static int
_hb_ft_move_to (const FT_Vector *to,
hb_draw_session_t *drawing)
{
drawing->move_to (to->x, to->y);
return FT_Err_Ok;
}
static int
_hb_ft_line_to (const FT_Vector *to,
hb_draw_session_t *drawing)
{
drawing->line_to (to->x, to->y);
return FT_Err_Ok;
}
static int
_hb_ft_conic_to (const FT_Vector *control,
const FT_Vector *to,
hb_draw_session_t *drawing)
{
drawing->quadratic_to (control->x, control->y,
to->x, to->y);
return FT_Err_Ok;
}
static int
_hb_ft_cubic_to (const FT_Vector *control1,
const FT_Vector *control2,
const FT_Vector *to,
hb_draw_session_t *drawing)
{
drawing->cubic_to (control1->x, control1->y,
control2->x, control2->y,
to->x, to->y);
return FT_Err_Ok;
}
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)
{
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;
if (unlikely (FT_Load_Glyph (ft_face, glyph,
FT_LOAD_NO_BITMAP | ft_font->load_flags)))
return;
if (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
return;
const FT_Outline_Funcs outline_funcs = {
(FT_Outline_MoveToFunc) _hb_ft_move_to,
(FT_Outline_LineToFunc) _hb_ft_line_to,
(FT_Outline_ConicToFunc) _hb_ft_conic_to,
(FT_Outline_CubicToFunc) _hb_ft_cubic_to,
0, /* shift */
0, /* delta */
};
hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy);
FT_Outline_Decompose (&ft_face->glyph->outline,
&outline_funcs,
&draw_session);
}
#endif
static inline void free_static_ft_funcs ();
static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ft_font_funcs_lazy_loader_t>
@ -596,6 +674,10 @@ static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ft
hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, nullptr, nullptr);
hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, nullptr, nullptr);
#ifndef HB_NO_DRAW
hb_font_funcs_set_glyph_shape_func (funcs, hb_ft_get_glyph_shape, nullptr, nullptr);
#endif
hb_font_funcs_make_immutable (funcs);
hb_atexit (free_static_ft_funcs);

View File

@ -90,6 +90,7 @@ hb_gobject_##name##_get_type () \
HB_DEFINE_OBJECT_TYPE (buffer)
HB_DEFINE_OBJECT_TYPE (blob)
HB_DEFINE_OBJECT_TYPE (draw_funcs)
HB_DEFINE_OBJECT_TYPE (face)
HB_DEFINE_OBJECT_TYPE (font)
HB_DEFINE_OBJECT_TYPE (font_funcs)

View File

@ -48,6 +48,10 @@ HB_EXTERN GType
hb_gobject_buffer_get_type (void);
#define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ())
HB_EXTERN GType
hb_gobject_draw_funcs_get_type (void);
#define HB_GOBJECT_TYPE_DRAW_FUNCS (hb_gobject_draw_funcs_get_type ())
HB_EXTERN GType
hb_gobject_face_get_type (void);
#define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ())

View File

@ -442,13 +442,12 @@ bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph
return true;
}
#ifdef HB_EXPERIMENTAL_API
struct cff1_path_param_t
{
cff1_path_param_t (const OT::cff1::accelerator_t *cff_, hb_font_t *font_,
draw_helper_t &draw_helper_, point_t *delta_)
hb_draw_session_t &draw_session_, point_t *delta_)
{
draw_helper = &draw_helper_;
draw_session = &draw_session_;
cff = cff_;
font = font_;
delta = delta_;
@ -458,14 +457,14 @@ struct cff1_path_param_t
{
point_t point = p;
if (delta) point.move (*delta);
draw_helper->move_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ()));
draw_session->move_to (font->em_fscalef_x (point.x.to_real ()), font->em_fscalef_y (point.y.to_real ()));
}
void line_to (const point_t &p)
{
point_t point = p;
if (delta) point.move (*delta);
draw_helper->line_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ()));
draw_session->line_to (font->em_fscalef_x (point.x.to_real ()), font->em_fscalef_y (point.y.to_real ()));
}
void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3)
@ -477,15 +476,15 @@ struct cff1_path_param_t
point2.move (*delta);
point3.move (*delta);
}
draw_helper->cubic_to (font->em_scalef_x (point1.x.to_real ()), font->em_scalef_y (point1.y.to_real ()),
font->em_scalef_x (point2.x.to_real ()), font->em_scalef_y (point2.y.to_real ()),
font->em_scalef_x (point3.x.to_real ()), font->em_scalef_y (point3.y.to_real ()));
draw_session->cubic_to (font->em_fscalef_x (point1.x.to_real ()), font->em_fscalef_y (point1.y.to_real ()),
font->em_fscalef_x (point2.x.to_real ()), font->em_fscalef_y (point2.y.to_real ()),
font->em_fscalef_x (point3.x.to_real ()), font->em_fscalef_y (point3.y.to_real ()));
}
void end_path () { draw_helper->end_path (); }
void end_path () { draw_session->close_path (); }
hb_font_t *font;
draw_helper_t *draw_helper;
hb_draw_session_t *draw_session;
point_t *delta;
const OT::cff1::accelerator_t *cff;
@ -513,7 +512,7 @@ struct cff1_path_procs_path_t : path_procs_t<cff1_path_procs_path_t, cff1_cs_int
};
static bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph,
draw_helper_t &draw_helper, bool in_seac = false, point_t *delta = nullptr);
hb_draw_session_t &draw_session, bool in_seac = false, point_t *delta = nullptr);
struct cff1_cs_opset_path_t : cff1_cs_opset_t<cff1_cs_opset_path_t, cff1_path_param_t, cff1_path_procs_path_t>
{
@ -530,14 +529,14 @@ struct cff1_cs_opset_path_t : cff1_cs_opset_t<cff1_cs_opset_path_t, cff1_path_pa
hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ());
if (unlikely (!(!env.in_seac && base && accent
&& _get_path (param.cff, param.font, base, *param.draw_helper, true)
&& _get_path (param.cff, param.font, accent, *param.draw_helper, true, &delta))))
&& _get_path (param.cff, param.font, base, *param.draw_session, true)
&& _get_path (param.cff, param.font, accent, *param.draw_session, true, &delta))))
env.set_error ();
}
};
bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph,
draw_helper_t &draw_helper, bool in_seac, point_t *delta)
hb_draw_session_t &draw_session, bool in_seac, point_t *delta)
{
if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false;
@ -546,7 +545,7 @@ bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoin
const byte_str_t str = (*cff->charStrings)[glyph];
interp.env.init (str, *cff, fd);
interp.env.set_in_seac (in_seac);
cff1_path_param_t param (cff, font, draw_helper, delta);
cff1_path_param_t param (cff, font, draw_session, delta);
if (unlikely (!interp.interpret (param))) return false;
/* Let's end the path specially since it is called inside seac also */
@ -555,16 +554,15 @@ bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoin
return true;
}
bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const
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
/* XXX Remove check when this code moves to .hh file. */
return true;
#endif
return _get_path (this, font, glyph, draw_helper);
return _get_path (this, font, glyph, draw_session);
}
#endif
struct get_seac_param_t
{

View File

@ -1347,9 +1347,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 get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const;
#ifdef HB_EXPERIMENTAL_API
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const;
#endif
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
private:
struct gname_t

View File

@ -143,30 +143,29 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
return true;
}
#ifdef HB_EXPERIMENTAL_API
struct cff2_path_param_t
{
cff2_path_param_t (hb_font_t *font_, draw_helper_t &draw_helper_)
cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_)
{
draw_helper = &draw_helper_;
draw_session = &draw_session_;
font = font_;
}
void move_to (const point_t &p)
{ draw_helper->move_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ())); }
{ draw_session->move_to (font->em_fscalef_x (p.x.to_real ()), font->em_fscalef_y (p.y.to_real ())); }
void line_to (const point_t &p)
{ draw_helper->line_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ())); }
{ draw_session->line_to (font->em_fscalef_x (p.x.to_real ()), font->em_fscalef_y (p.y.to_real ())); }
void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3)
{
draw_helper->cubic_to (font->em_scalef_x (p1.x.to_real ()), font->em_scalef_y (p1.y.to_real ()),
font->em_scalef_x (p2.x.to_real ()), font->em_scalef_y (p2.y.to_real ()),
font->em_scalef_x (p3.x.to_real ()), font->em_scalef_y (p3.y.to_real ()));
draw_session->cubic_to (font->em_fscalef_x (p1.x.to_real ()), font->em_fscalef_y (p1.y.to_real ()),
font->em_fscalef_x (p2.x.to_real ()), font->em_fscalef_y (p2.y.to_real ()),
font->em_fscalef_x (p3.x.to_real ()), font->em_fscalef_y (p3.y.to_real ()));
}
protected:
draw_helper_t *draw_helper;
hb_draw_session_t *draw_session;
hb_font_t *font;
};
@ -193,7 +192,7 @@ struct cff2_path_procs_path_t : path_procs_t<cff2_path_procs_path_t, cff2_cs_int
struct cff2_cs_opset_path_t : cff2_cs_opset_t<cff2_cs_opset_path_t, cff2_path_param_t, cff2_path_procs_path_t> {};
bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const
bool OT::cff2::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
/* XXX Remove check when this code moves to .hh file. */
@ -206,10 +205,9 @@ bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, d
cff2_cs_interpreter_t<cff2_cs_opset_path_t, cff2_path_param_t> interp;
const byte_str_t str = (*charStrings)[glyph];
interp.env.init (str, *this, fd, font->coords, font->num_coords);
cff2_path_param_t param (font, draw_helper);
cff2_path_param_t param (font, draw_session);
if (unlikely (!interp.interpret (param))) return false;
return true;
}
#endif
#endif

View File

@ -515,9 +515,7 @@ struct cff2
HB_INTERNAL bool get_extents (hb_font_t *font,
hb_codepoint_t glyph,
hb_glyph_extents_t *extents) const;
#ifdef HB_EXPERIMENTAL_API
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const;
#endif
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
};
typedef accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t> accelerator_subset_t;

View File

@ -257,6 +257,23 @@ hb_ot_get_font_v_extents (hb_font_t *font,
}
#endif
#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_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy);
if (font->face->table.glyf->get_path (font, glyph, draw_session)) return;
#ifndef HB_NO_CFF
if (font->face->table.cff1->get_path (font, glyph, draw_session)) return;
if (font->face->table.cff2->get_path (font, glyph, draw_session)) 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<hb_ot_font_funcs_lazy_loader_t>
@ -279,6 +296,10 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ot
hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr);
#endif
#ifndef HB_NO_DRAW
hb_font_funcs_set_glyph_shape_func (funcs, hb_ot_get_glyph_shape, nullptr, nullptr);
#endif
hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, nullptr, nullptr);
//hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, nullptr, nullptr);

View File

@ -1152,11 +1152,10 @@ struct glyf
return operation_count;
}
#ifdef HB_EXPERIMENTAL_API
struct path_builder_t
{
hb_font_t *font;
draw_helper_t *draw_helper;
hb_draw_session_t *draw_session;
struct optional_point_t
{
@ -1171,10 +1170,10 @@ struct glyf
{ return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); }
} first_oncurve, first_offcurve, last_offcurve;
path_builder_t (hb_font_t *font_, draw_helper_t &draw_helper_)
path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_)
{
font = font_;
draw_helper = &draw_helper_;
draw_session = &draw_session_;
first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
}
@ -1184,10 +1183,6 @@ struct glyf
* https://stackoverflow.com/a/20772557 */
void consume_point (const contour_point_t &point)
{
/* Skip empty contours */
if (unlikely (point.is_end_point && !first_oncurve.has_data && !first_offcurve.has_data))
return;
bool is_on_curve = point.flag & Glyph::FLAG_ON_CURVE;
optional_point_t p (point.x, point.y);
if (!first_oncurve.has_data)
@ -1195,7 +1190,7 @@ struct glyf
if (is_on_curve)
{
first_oncurve = p;
draw_helper->move_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y));
draw_session->move_to (font->em_fscalef_x (p.x), font->em_fscalef_y (p.y));
}
else
{
@ -1204,7 +1199,7 @@ struct glyf
optional_point_t mid = first_offcurve.lerp (p, .5f);
first_oncurve = mid;
last_offcurve = p;
draw_helper->move_to (font->em_scalef_x (mid.x), font->em_scalef_y (mid.y));
draw_session->move_to (font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y));
}
else
first_offcurve = p;
@ -1216,22 +1211,22 @@ struct glyf
{
if (is_on_curve)
{
draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
font->em_scalef_x (p.x), font->em_scalef_y (p.y));
draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y),
font->em_fscalef_x (p.x), font->em_fscalef_y (p.y));
last_offcurve = optional_point_t ();
}
else
{
optional_point_t mid = last_offcurve.lerp (p, .5f);
draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
font->em_scalef_x (mid.x), font->em_scalef_y (mid.y));
draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y),
font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y));
last_offcurve = p;
}
}
else
{
if (is_on_curve)
draw_helper->line_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y));
draw_session->line_to (font->em_fscalef_x (p.x), font->em_fscalef_y (p.y));
else
last_offcurve = p;
}
@ -1242,24 +1237,30 @@ struct glyf
if (first_offcurve.has_data && last_offcurve.has_data)
{
optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f);
draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
font->em_scalef_x (mid.x), font->em_scalef_y (mid.y));
draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y),
font->em_fscalef_x (mid.x), font->em_fscalef_y (mid.y));
last_offcurve = optional_point_t ();
/* now check the rest */
}
if (first_offcurve.has_data && first_oncurve.has_data)
draw_helper->quadratic_to (font->em_scalef_x (first_offcurve.x), font->em_scalef_y (first_offcurve.y),
font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y));
draw_session->quadratic_to (font->em_fscalef_x (first_offcurve.x), font->em_fscalef_y (first_offcurve.y),
font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y));
else if (last_offcurve.has_data && first_oncurve.has_data)
draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y));
draw_session->quadratic_to (font->em_fscalef_x (last_offcurve.x), font->em_fscalef_y (last_offcurve.y),
font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y));
else if (first_oncurve.has_data)
draw_helper->line_to (font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y));
draw_session->line_to (font->em_fscalef_x (first_oncurve.x), font->em_fscalef_y (first_oncurve.y));
else if (first_offcurve.has_data)
{
float x = font->em_fscalef_x (first_offcurve.x), y = font->em_fscalef_x (first_offcurve.y);
draw_session->move_to (x, y);
draw_session->quadratic_to (x, y, x, y);
}
/* Getting ready for the next contour */
first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
draw_helper->end_path ();
draw_session->close_path ();
}
}
void points_end () {}
@ -1269,9 +1270,8 @@ struct glyf
};
bool
get_path (hb_font_t *font, hb_codepoint_t gid, draw_helper_t &draw_helper) const
{ return get_points (font, gid, path_builder_t (font, draw_helper)); }
#endif
get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
{ return get_points (font, gid, path_builder_t (font, draw_session)); }
#ifndef HB_NO_VAR
const gvar_accelerator_t *gvar;

View File

@ -42,7 +42,7 @@
#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty ()
#endif
#if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW) && defined(HB_EXPERIMENTAL_API)
#if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW)
static void
svg_dump (hb_face_t *face, unsigned face_index)
{
@ -129,48 +129,60 @@ png_dump (hb_face_t *face, unsigned face_index)
hb_font_destroy (font);
}
struct user_data_t
struct draw_data_t
{
FILE *f;
hb_position_t ascender;
};
static void
move_to (hb_position_t to_x, hb_position_t to_y, user_data_t &user_data)
move_to (hb_draw_funcs_t *dfuncs, draw_data_t *draw_data,
hb_draw_state_t *st,
float to_x, float to_y,
void *)
{
fprintf (user_data.f, "M%d,%d", to_x, user_data.ascender - to_y);
fprintf (draw_data->f, "M%g,%g", to_x, draw_data->ascender - to_y);
}
static void
line_to (hb_position_t to_x, hb_position_t to_y, user_data_t &user_data)
line_to (hb_draw_funcs_t *dfuncs, draw_data_t *draw_data,
hb_draw_state_t *st,
float to_x, float to_y,
void *)
{
fprintf (user_data.f, "L%d,%d", to_x, user_data.ascender - to_y);
fprintf (draw_data->f, "L%g,%g", to_x, draw_data->ascender - to_y);
}
static void
quadratic_to (hb_position_t control_x, hb_position_t control_y,
hb_position_t to_x, hb_position_t to_y,
user_data_t &user_data)
quadratic_to (hb_draw_funcs_t *dfuncs, draw_data_t *draw_data,
hb_draw_state_t *st,
float control_x, float control_y,
float to_x, float to_y,
void *)
{
fprintf (user_data.f, "Q%d,%d %d,%d", control_x, user_data.ascender - control_y,
to_x, user_data.ascender - to_y);
fprintf (draw_data->f, "Q%g,%g %g,%g", control_x, draw_data->ascender - control_y,
to_x, draw_data->ascender - to_y);
}
static void
cubic_to (hb_position_t control1_x, hb_position_t control1_y,
hb_position_t control2_x, hb_position_t control2_y,
hb_position_t to_x, hb_position_t to_y,
user_data_t &user_data)
cubic_to (hb_draw_funcs_t *dfuncs, draw_data_t *draw_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 *)
{
fprintf (user_data.f, "C%d,%d %d,%d %d,%d", control1_x, user_data.ascender - control1_y,
control2_x, user_data.ascender - control2_y,
to_x, user_data.ascender - to_y);
fprintf (draw_data->f, "C%g,%g %g,%g %g,%g", control1_x, draw_data->ascender - control1_y,
control2_x, draw_data->ascender - control2_y,
to_x, draw_data->ascender - to_y);
}
static void
close_path (user_data_t &user_data)
close_path (hb_draw_funcs_t *dfuncs, draw_data_t *draw_data,
hb_draw_state_t *st,
void *)
{
fprintf (user_data.f, "Z");
fprintf (draw_data->f, "Z");
}
static void
@ -218,9 +230,9 @@ layered_glyph_dump (hb_font_t *font, hb_draw_funcs_t *funcs, unsigned face_index
" viewBox=\"%d %d %d %d\">\n",
extents.x_bearing, 0,
extents.x_bearing + extents.width, -extents.height);
user_data_t user_data;
user_data.ascender = extents.y_bearing;
user_data.f = f;
draw_data_t draw_data;
draw_data.ascender = extents.y_bearing;
draw_data.f = f;
for (unsigned layer = 0; layer < num_layers; ++layer)
{
@ -232,8 +244,7 @@ layered_glyph_dump (hb_font_t *font, hb_draw_funcs_t *funcs, unsigned face_index
if (hb_color_get_alpha (color) != 255)
fprintf (f, "fill-opacity=\"%.3f\"", (double) hb_color_get_alpha (color) / 255.);
fprintf (f, "d=\"");
if (!hb_font_draw_glyph (font, layers[layer].glyph, funcs, &user_data))
printf ("Failed to decompose layer %d while %d\n", layers[layer].glyph, gid);
hb_font_get_glyph_shape (font, layers[layer].glyph, funcs, &draw_data);
fprintf (f, "\"/>\n");
}
@ -269,11 +280,10 @@ dump_glyphs (hb_font_t *font, hb_draw_funcs_t *funcs, unsigned face_index)
" viewBox=\"%d %d %d %d\"><path d=\"",
extents.x_bearing, 0,
extents.x_bearing + extents.width, font_extents.ascender - font_extents.descender);
user_data_t user_data;
user_data.ascender = font_extents.ascender;
user_data.f = f;
if (!hb_font_draw_glyph (font, gid, funcs, &user_data))
printf ("Failed to decompose gid: %d\n", gid);
draw_data_t draw_data;
draw_data.ascender = font_extents.ascender;
draw_data.f = f;
hb_font_get_glyph_shape (font, gid, funcs, &draw_data);
fprintf (f, "\"/></svg>");
fclose (f);
}
@ -300,11 +310,11 @@ dump_glyphs (hb_blob_t *blob, const char *font_name)
fclose (font_name_file);
hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to);
hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to);
hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) quadratic_to);
hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to);
hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path);
hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to, nullptr, nullptr);
hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to, nullptr, nullptr);
hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) quadratic_to, nullptr, nullptr);
hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to, nullptr, nullptr);
hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path, nullptr, nullptr);
unsigned num_faces = hb_face_count (blob);
for (unsigned face_index = 0; face_index < num_faces; ++face_index)
@ -512,7 +522,7 @@ main (int argc, char **argv)
#ifndef MAIN_CC_NO_PRIVATE_API
print_layout_info_using_private_api (blob);
#endif
#if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW) && defined(HB_EXPERIMENTAL_API)
#if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW)
dump_glyphs (blob, argv[1]);
#endif
hb_blob_destroy (blob);

View File

@ -2,7 +2,7 @@
directory=cairo
url=https://gitlab.freedesktop.org/cairo/cairo.git
depth=1
revision=1.17.4
revision=c90faeb7492b1b778d18a796afe5c2e4b32a6356
[provide]
dependency_names = cairo, cairo-ft

File diff suppressed because it is too large Load Diff

View File

@ -163,11 +163,9 @@ test_font (hb_font_t *font, hb_codepoint_t cp)
hb_ot_var_normalize_variations (face, NULL, 0, NULL, 0);
hb_ot_var_normalize_coords (face, 0, NULL, NULL);
#ifdef HB_EXPERIMENTAL_API
hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
hb_font_draw_glyph (font, cp, funcs, NULL);
hb_font_get_glyph_shape (font, cp, funcs, NULL);
hb_draw_funcs_destroy (funcs);
#endif
hb_set_destroy (set);

View File

@ -31,7 +31,6 @@
static void
test_get_var_coords (void)
{
#ifdef HB_EXPERIMENTAL_API
#ifndef G_APPROX_VALUE
#define G_APPROX_VALUE(a, b, epsilon) \
(((a) > (b) ? (a) - (b) : (b) - (a)) < (epsilon))
@ -66,7 +65,6 @@ test_get_var_coords (void)
hb_font_destroy (font);
hb_face_destroy (face);
#endif
}
static void

View File

@ -5,77 +5,80 @@
#include "hb-fuzzer.hh"
#ifdef HB_EXPERIMENTAL_API
struct _user_data_t
struct _draw_data_t
{
bool is_open;
unsigned path_len;
hb_position_t path_start_x;
hb_position_t path_start_y;
hb_position_t path_last_x;
hb_position_t path_last_y;
float path_start_x;
float path_start_y;
float path_last_x;
float path_last_y;
};
static void
_move_to (hb_position_t to_x, hb_position_t to_y, void *user_data_)
_move_to (hb_draw_funcs_t *dfuncs, void *draw_data_,
hb_draw_state_t *st,
float to_x, float to_y,
void *user_data)
{
_user_data_t *user_data = (_user_data_t *) user_data_;
assert (!user_data->is_open);
user_data->is_open = true;
user_data->path_start_x = user_data->path_last_x = to_x;
user_data->path_start_y = user_data->path_last_y = to_y;
_draw_data_t *draw_data = (_draw_data_t *) draw_data_;
assert (!st->path_open);
draw_data->path_start_x = draw_data->path_last_x = to_x;
draw_data->path_start_y = draw_data->path_last_y = to_y;
}
static void
_line_to (hb_position_t to_x, hb_position_t to_y, void *user_data_)
_line_to (hb_draw_funcs_t *dfuncs, void *draw_data_,
hb_draw_state_t *st,
float to_x, float to_y,
void *user_data)
{
_user_data_t *user_data = (_user_data_t *) user_data_;
assert (user_data->is_open);
assert (user_data->path_last_x != to_x || user_data->path_last_y != to_y);
++user_data->path_len;
user_data->path_last_x = to_x;
user_data->path_last_y = to_y;
_draw_data_t *draw_data = (_draw_data_t *) draw_data_;
assert (st->path_open);
++draw_data->path_len;
draw_data->path_last_x = to_x;
draw_data->path_last_y = to_y;
}
static void
_quadratic_to (hb_position_t control_x, hb_position_t control_y,
hb_position_t to_x, hb_position_t to_y, void *user_data_)
_quadratic_to (hb_draw_funcs_t *dfuncs, void *draw_data_,
hb_draw_state_t *st,
float control_x, float control_y,
float to_x, float to_y,
void *user_data)
{
_user_data_t *user_data = (_user_data_t *) user_data_;
assert (user_data->is_open);
assert (user_data->path_last_x != control_x || user_data->path_last_y != control_y ||
user_data->path_last_x != to_x || user_data->path_last_y != to_y);
++user_data->path_len;
user_data->path_last_x = to_x;
user_data->path_last_y = to_y;
_draw_data_t *draw_data = (_draw_data_t *) draw_data_;
assert (st->path_open);
++draw_data->path_len;
draw_data->path_last_x = to_x;
draw_data->path_last_y = to_y;
}
static void
_cubic_to (hb_position_t control1_x, hb_position_t control1_y,
hb_position_t control2_x, hb_position_t control2_y,
hb_position_t to_x, hb_position_t to_y, void *user_data_)
_cubic_to (hb_draw_funcs_t *dfuncs, void *draw_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 *user_data)
{
_user_data_t *user_data = (_user_data_t *) user_data_;
assert (user_data->is_open);
assert (user_data->path_last_x != control1_x || user_data->path_last_y != control1_y ||
user_data->path_last_x != control2_x || user_data->path_last_y != control2_y ||
user_data->path_last_x != to_x || user_data->path_last_y != to_y);
++user_data->path_len;
user_data->path_last_x = to_x;
user_data->path_last_y = to_y;
_draw_data_t *draw_data = (_draw_data_t *) draw_data_;
assert (st->path_open);
++draw_data->path_len;
draw_data->path_last_x = to_x;
draw_data->path_last_y = to_y;
}
static void
_close_path (void *user_data_)
_close_path (hb_draw_funcs_t *dfuncs, void *draw_data_,
hb_draw_state_t *st,
void *user_data)
{
_user_data_t *user_data = (_user_data_t *) user_data_;
assert (user_data->is_open && user_data->path_len != 0);
user_data->path_len = 0;
user_data->is_open = false;
assert (user_data->path_start_x == user_data->path_last_x &&
user_data->path_start_y == user_data->path_last_y);
_draw_data_t *draw_data = (_draw_data_t *) draw_data_;
assert (st->path_open && draw_data->path_len != 0);
draw_data->path_len = 0;
assert (draw_data->path_start_x == draw_data->path_last_x &&
draw_data->path_start_y == draw_data->path_last_y);
}
#endif
/* Similar to test-ot-face.c's #test_font() */
static void misc_calls_for_gid (hb_face_t *face, hb_font_t *font, hb_set_t *set, hb_codepoint_t cp)
@ -135,24 +138,19 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size)
unsigned glyph_count = hb_face_get_glyph_count (face);
glyph_count = glyph_count > 16 ? 16 : glyph_count;
#ifdef HB_EXPERIMENTAL_API
_user_data_t user_data = {false, 0, 0, 0, 0, 0};
_draw_data_t draw_data = {0, 0, 0, 0, 0};
hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) _move_to);
hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) _line_to);
hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) _quadratic_to);
hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) _cubic_to);
hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) _close_path);
#endif
hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) _move_to, nullptr, nullptr);
hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) _line_to, nullptr, nullptr);
hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) _quadratic_to, nullptr, nullptr);
hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) _cubic_to, nullptr, nullptr);
hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) _close_path, nullptr, nullptr);
volatile unsigned counter = !glyph_count;
hb_set_t *set = hb_set_create ();
for (unsigned gid = 0; gid < glyph_count; ++gid)
{
#ifdef HB_EXPERIMENTAL_API
hb_font_draw_glyph (font, gid, funcs, &user_data);
assert (!user_data.is_open);
#endif
hb_font_get_glyph_shape (font, gid, funcs, &draw_data);
/* Glyph extents also may practices the similar path, call it now that is related */
hb_glyph_extents_t extents;
@ -166,9 +164,7 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size)
}
hb_set_destroy (set);
assert (counter);
#ifdef HB_EXPERIMENTAL_API
hb_draw_funcs_destroy (funcs);
#endif
hb_font_destroy (font);
hb_face_destroy (face);

View File

@ -4,6 +4,8 @@ HB_VIEW_sources = \
font-options.hh \
hb-view.cc \
helper-cairo-ansi.hh \
helper-cairo-ft.hh \
helper-cairo-user.hh \
helper-cairo.hh \
main-font-text.hh \
options.hh \

View File

@ -53,11 +53,13 @@ struct font_options_t : face_options_t
void post_parse (GError **error);
hb_bool_t sub_font = false;
hb_variation_t *variations = nullptr;
unsigned int num_variations = 0;
int x_ppem = 0;
int y_ppem = 0;
double ptem = 0.;
double slant = 0.;
unsigned int subpixel_bits = SUBPIXEL_BITS;
mutable double font_size_x = DEFAULT_FONT_SIZE;
mutable double font_size_y = DEFAULT_FONT_SIZE;
@ -73,10 +75,10 @@ static struct supported_font_funcs_t {
void (*func) (hb_font_t *);
} supported_font_funcs[] =
{
{"ot", hb_ot_font_set_funcs},
#ifdef HAVE_FREETYPE
{"ft", hb_ft_font_set_funcs},
#endif
{"ot", hb_ot_font_set_funcs},
};
@ -94,6 +96,8 @@ font_options_t::post_parse (GError **error)
hb_font_set_ppem (font, x_ppem, y_ppem);
hb_font_set_ptem (font, ptem);
hb_font_set_synthetic_slant (font, slant);
int scale_x = (int) scalbnf (font_size_x, subpixel_bits);
int scale_y = (int) scalbnf (font_size_y, subpixel_bits);
hb_font_set_scale (font, scale_x, scale_y);
@ -137,6 +141,14 @@ font_options_t::post_parse (GError **error)
#ifdef HAVE_FREETYPE
hb_ft_font_set_load_flags (font, ft_load_flags);
#endif
if (sub_font)
{
hb_font_t *old_font = font;
font = hb_font_create_sub_font (old_font);
hb_font_set_scale (old_font, scale_x * 2, scale_y * 2);
hb_font_destroy (old_font);
}
}
@ -265,7 +277,11 @@ font_options_t::add_options (option_parser_t *parser)
G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_ppem, "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"},
{"font-ptem", 0, 0,
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-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"},
{"ft-load-flags", 0, 0, G_OPTION_ARG_INT, &this->ft_load_flags, "Set FreeType load-flags (default: 2)", "integer"},
{nullptr}
};

120
util/helper-cairo-ft.hh Normal file
View File

@ -0,0 +1,120 @@
/*
* Copyright © 2022 Google, 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): Behdad Esfahbod
*/
#ifndef HELPER_CAIRO_FT_HH
#define HELPER_CAIRO_FT_HH
#include "font-options.hh"
#include <cairo-ft.h>
#include <hb-ft.h>
#include FT_MULTIPLE_MASTERS_H
static FT_Library ft_library;
#ifdef HAVE_ATEXIT
static inline
void free_ft_library ()
{
FT_Done_FreeType (ft_library);
}
#endif
static inline cairo_font_face_t *
helper_cairo_create_ft_font_face (const font_options_t *font_opts)
{
cairo_font_face_t *cairo_face;
/* We cannot use the FT_Face from hb_font_t, as doing so will confuse hb_font_t because
* cairo will reset the face size. As such, create new face...
* TODO Perhaps add API to hb-ft to encapsulate this code. */
FT_Face ft_face = nullptr;//hb_ft_font_get_face (font);
if (!ft_face)
{
if (!ft_library)
{
FT_Init_FreeType (&ft_library);
#ifdef HAVE_ATEXIT
atexit (free_ft_library);
#endif
}
unsigned int blob_length;
const char *blob_data = hb_blob_get_data (font_opts->blob, &blob_length);
if (FT_New_Memory_Face (ft_library,
(const FT_Byte *) blob_data,
blob_length,
font_opts->face_index,
&ft_face))
fail (false, "FT_New_Memory_Face fail");
}
if (!ft_face)
{
/* This allows us to get some boxes at least... */
cairo_face = cairo_toy_font_face_create ("@cairo:sans",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
}
else
{
#ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES
unsigned int num_coords;
const int *coords = hb_font_get_var_coords_normalized (font_opts->font, &num_coords);
if (num_coords)
{
FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed));
if (ft_coords)
{
for (unsigned int i = 0; i < num_coords; i++)
ft_coords[i] = coords[i] << 2;
FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
free (ft_coords);
}
}
#endif
cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, font_opts->ft_load_flags);
}
return cairo_face;
}
static inline bool
helper_cairo_ft_scaled_font_has_color (cairo_scaled_font_t *scaled_font)
{
bool ret = false;
#ifdef FT_HAS_COLOR
FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
if (ft_face)
{
if (FT_HAS_COLOR (ft_face))
ret = true;
cairo_ft_scaled_font_unlock_face (scaled_font);
}
#endif
return ret;
}
#endif

316
util/helper-cairo-user.hh Normal file
View File

@ -0,0 +1,316 @@
/*
* Copyright © 2022 Google, 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): Behdad Esfahbod
*/
#ifndef HELPER_CAIRO_USER_HH
#define HELPER_CAIRO_USER_HH
#include "font-options.hh"
#include <cairo.h>
#include <hb.h>
#include "hb-blob.hh"
static void
move_to (hb_draw_funcs_t *dfuncs,
cairo_t *cr,
hb_draw_state_t *st,
float to_x, float to_y,
void *)
{
cairo_move_to (cr,
(double) to_x, (double) to_y);
}
static void
line_to (hb_draw_funcs_t *dfuncs,
cairo_t *cr,
hb_draw_state_t *st,
float to_x, float to_y,
void *)
{
cairo_line_to (cr,
(double) to_x, (double) to_y);
}
static void
cubic_to (hb_draw_funcs_t *dfuncs,
cairo_t *cr,
hb_draw_state_t *st,
float control1_x, float control1_y,
float control2_x, float control2_y,
float to_x, float to_y,
void *)
{
cairo_curve_to (cr,
(double) control1_x, (double) control1_y,
(double) control2_x, (double) control2_y,
(double) to_x, (double) to_y);
}
static void
close_path (hb_draw_funcs_t *dfuncs,
cairo_t *cr,
hb_draw_state_t *st,
void *)
{
cairo_close_path (cr);
}
static hb_draw_funcs_t *
get_cairo_draw_funcs ()
{
static hb_draw_funcs_t *funcs;
if (!funcs)
{
funcs = hb_draw_funcs_create ();
hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to, nullptr, nullptr);
hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to, nullptr, nullptr);
hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to, nullptr, nullptr);
hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path, nullptr, nullptr);
}
return funcs;
}
static const cairo_user_data_key_t _hb_font_cairo_user_data_key = {0};
static cairo_status_t
render_glyph (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_position_t x_scale, y_scale;
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);
cairo_fill (cr);
return CAIRO_STATUS_SUCCESS;
}
#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;
#endif
ret = render_color_glyph_layers (scaled_font, glyph, cr, extents);
if (ret != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
return ret;
return render_glyph (scaled_font, glyph, cr, extents);
}
#endif
static inline cairo_font_face_t *
helper_cairo_create_user_font_face (const font_options_t *font_opts)
{
cairo_font_face_t *cairo_face = cairo_user_font_face_create ();
cairo_font_face_set_user_data (cairo_face,
&_hb_font_cairo_user_data_key,
hb_font_reference (font_opts->font),
(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))
cairo_user_font_face_set_render_color_glyph_func (cairo_face, render_color_glyph);
#endif
return cairo_face;
}
static inline bool
helper_cairo_user_font_face_has_data (cairo_font_face_t *font_face)
{
return cairo_font_face_get_user_data (font_face, &_hb_font_cairo_user_data_key);
}
static inline bool
helper_cairo_user_scaled_font_has_color (cairo_scaled_font_t *scaled_font)
{
/* Ignoring SVG for now, since we cannot render it. */
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);
}
#endif

View File

@ -29,10 +29,11 @@
#include "view-options.hh"
#include "output-options.hh"
#include "helper-cairo-ft.hh"
#include "helper-cairo-user.hh"
#include <cairo-ft.h>
#include <hb-ft.h>
#include FT_MULTIPLE_MASTERS_H
#include <cairo.h>
#include <hb.h>
#include "helper-cairo-ansi.hh"
#ifdef CAIRO_HAS_SVG_SURFACE
@ -65,74 +66,27 @@ _cairo_eps_surface_create_for_stream (cairo_write_func_t write_func,
# endif
#endif
static FT_Library ft_library;
#ifdef HAVE_ATEXIT
static inline
void free_ft_library ()
static inline bool
helper_cairo_use_hb_draw (const font_options_t *font_opts)
{
FT_Done_FreeType (ft_library);
const char *env = getenv ("HB_DRAW");
if (!env)
return cairo_version () >= CAIRO_VERSION_ENCODE (1, 17, 5);
return atoi (env);
}
#endif
static inline cairo_scaled_font_t *
helper_cairo_create_scaled_font (const font_options_t *font_opts)
{
bool use_hb_draw = helper_cairo_use_hb_draw (font_opts);
hb_font_t *font = hb_font_reference (font_opts->font);
cairo_font_face_t *cairo_face;
/* We cannot use the FT_Face from hb_font_t, as doing so will confuse hb_font_t because
* cairo will reset the face size. As such, create new face...
* TODO Perhaps add API to hb-ft to encapsulate this code. */
FT_Face ft_face = nullptr;//hb_ft_font_get_face (font);
if (!ft_face)
{
if (!ft_library)
{
FT_Init_FreeType (&ft_library);
#ifdef HAVE_ATEXIT
atexit (free_ft_library);
#endif
}
unsigned int blob_length;
const char *blob_data = hb_blob_get_data (font_opts->blob, &blob_length);
if (FT_New_Memory_Face (ft_library,
(const FT_Byte *) blob_data,
blob_length,
font_opts->face_index,
&ft_face))
fail (false, "FT_New_Memory_Face fail");
}
if (!ft_face)
{
/* This allows us to get some boxes at least... */
cairo_face = cairo_toy_font_face_create ("@cairo:sans",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
}
if (use_hb_draw)
cairo_face = helper_cairo_create_user_font_face (font_opts);
else
{
#ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES
unsigned int num_coords;
const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
if (num_coords)
{
FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed));
if (ft_coords)
{
for (unsigned int i = 0; i < num_coords; i++)
ft_coords[i] = coords[i] << 2;
FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
free (ft_coords);
}
}
#endif
cairo_face = helper_cairo_create_ft_font_face (font_opts);
cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, font_opts->ft_load_flags);
}
cairo_matrix_t ctm, font_matrix;
cairo_font_options_t *font_options;
@ -140,6 +94,9 @@ helper_cairo_create_scaled_font (const font_options_t *font_opts)
cairo_matrix_init_scale (&font_matrix,
font_opts->font_size_x,
font_opts->font_size_y);
if (use_hb_draw)
font_matrix.xy = -font_opts->slant * font_opts->font_size_x;
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);
@ -165,17 +122,10 @@ helper_cairo_create_scaled_font (const font_options_t *font_opts)
static inline bool
helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font)
{
bool ret = false;
#ifdef FT_HAS_COLOR
FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
if (ft_face)
{
if (FT_HAS_COLOR (ft_face))
ret = true;
cairo_ft_scaled_font_unlock_face (scaled_font);
}
#endif
return ret;
if (helper_cairo_user_font_face_has_data (cairo_scaled_font_get_font_face (scaled_font)))
return helper_cairo_user_scaled_font_has_color (scaled_font);
else
return helper_cairo_ft_scaled_font_has_color (scaled_font);
}