/*
Port from c++ is protected by a GNU Lesser GPLv3
Copyright © 2013 Sylvain BERTRAND <sylvain.bertrand@gmail.com>
<sylware@legeek.net>
*/
#include <stddef.h>
#include <assert.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "hb.h"
#include "hb-private.h"
#include "hb-atomic-private.h"
#include "hb-buffer-private.h"
#include "hb-shaper-private.h"
#include "hb-face-private.h"
#include "hb-font-private.h"
#include "hb-shape-plan-private.h"
/*XXX:should go in lib "global init"*/
static hb_shape_plan_t hb_shape_plan_nil = {
REF_CNT_INVALID_VAL,
TRUE, /*default_shaper_list */
NULL, /*face */
HB_SEGMENT_PROPERTIES_DEFAULT, /*props */
NULL, /*shaper_func */
NULL, /*shaper_name */
{
#ifdef HAVE_GRAPHITE2
HB_SHAPER_DATA_INVALID,
#endif
#ifdef HAVE_OT
HB_SHAPER_DATA_INVALID,
#endif
HB_SHAPER_DATA_INVALID /*fallback */
}
};
/*TODO no user-feature caching for now.*/
struct hb_shape_plan_proposal_t {
const hb_segment_properties_t props;
const char *const *shaper_list;
hb_shape_func_t *shaper_func;
};
hb_shape_plan_t *hb_shape_plan_get_empty(void)
{
return &hb_shape_plan_nil;
}
#ifdef HAVE_GRAPHITE2
static INLINE hb_bool_t hb_graphite2_shaper_face_data_ensure(hb_face_t * face)
{
while (1) {
struct hb_graphite2_shaper_face_data_t *data;
void *expected;
data = hb_atomic_ptr_get(&face->shaper_data.graphite2);
if (data)
return !HB_SHAPER_DATA_IS_INVALID(data);
if (!data)
data = hb_graphite2_shaper_face_data_create(face);
if (!data)
data = HB_SHAPER_DATA_INVALID;
expected = NULL;
if (hb_atomic_ptr_cmpexch
(&face->shaper_data.graphite2, &expected, &data))
return !HB_SHAPER_DATA_IS_INVALID(data);
if (data != HB_SHAPER_DATA_INVALID
&& data != HB_SHAPER_DATA_SUCCEEDED)
hb_graphite2_shaper_face_data_destroy(data);
}
}
static INLINE hb_bool_t hb_graphite2_shaper_font_data_ensure(hb_font_t * font)
{
while (1) {
struct hb_graphite2_shaper_font_data_t *data;
void *expected;
data = hb_atomic_ptr_get(&font->shaper_data.graphite2);
if (data)
return !HB_SHAPER_DATA_IS_INVALID(data);
if (!data)
data = hb_graphite2_shaper_font_data_create(font);
if (!data)
data = HB_SHAPER_DATA_INVALID;
expected = NULL;
if (hb_atomic_ptr_cmpexch
(&font->shaper_data.graphite2, &expected, &data))
return !HB_SHAPER_DATA_IS_INVALID(data);
if (data != HB_SHAPER_DATA_INVALID
&& data != HB_SHAPER_DATA_SUCCEEDED)
hb_graphite2_shaper_font_data_destroy(data);
}
}
#endif
#ifdef HAVE_OT
static INLINE hb_bool_t hb_ot_shaper_face_data_ensure(hb_face_t * face)
{
while (1) {
struct hb_ot_shaper_face_data_t *data;
void *expected;
data = hb_atomic_ptr_get(&face->shaper_data.ot);
if (data)
return !HB_SHAPER_DATA_IS_INVALID(data);
if (!data)
data = hb_ot_shaper_face_data_create(face);
if (!data)
data = HB_SHAPER_DATA_INVALID;
expected = NULL;
if (hb_atomic_ptr_cmpexch
(&face->shaper_data.ot, &expected, &data))
return !HB_SHAPER_DATA_IS_INVALID(data);
if (data != HB_SHAPER_DATA_INVALID
&& data != HB_SHAPER_DATA_SUCCEEDED)
hb_ot_shaper_face_data_destroy(data);
}
}
static INLINE hb_bool_t hb_ot_shaper_font_data_ensure(hb_font_t * font)
{
while (1) {
struct hb_ot_shaper_font_data_t *data;
void *expected;
data = hb_atomic_ptr_get(&font->shaper_data.ot);
if (data)
return !HB_SHAPER_DATA_IS_INVALID(data);
if (!data)
data = hb_ot_shaper_font_data_create(font);
if (!data)
data = HB_SHAPER_DATA_INVALID;
expected = NULL;
if (hb_atomic_ptr_cmpexch
(&font->shaper_data.ot, &expected, &data))
return !HB_SHAPER_DATA_IS_INVALID(data);
if (data != HB_SHAPER_DATA_INVALID
&& data != HB_SHAPER_DATA_SUCCEEDED)
hb_ot_shaper_font_data_destroy(data);
}
}
#endif
static INLINE hb_bool_t hb_fallback_shaper_face_data_ensure(hb_face_t * face)
{
while (1) {
struct hb_fallback_shaper_face_data_t *data;
void *expected;
data = hb_atomic_ptr_get(&face->shaper_data.fallback);
if (data)
return !HB_SHAPER_DATA_IS_INVALID(data);
if (!data)
data = hb_fallback_shaper_face_data_create(face);
if (!data)
data = HB_SHAPER_DATA_INVALID;
expected = NULL;
if (hb_atomic_ptr_cmpexch
(&face->shaper_data.fallback, &expected, &data))
return !HB_SHAPER_DATA_IS_INVALID(data);
if (data != HB_SHAPER_DATA_INVALID
&& data != HB_SHAPER_DATA_SUCCEEDED)
hb_fallback_shaper_face_data_destroy(data);
}
}
static INLINE hb_bool_t hb_fallback_shaper_font_data_ensure(hb_font_t * font)
{
while (1) {
struct hb_fallback_shaper_font_data_t *data;
void *expected;
data = hb_atomic_ptr_get(&font->shaper_data.fallback);
if (data)
return !HB_SHAPER_DATA_IS_INVALID(data);
if (!data)
data = hb_fallback_shaper_font_data_create(font);
if (!data)
data = HB_SHAPER_DATA_INVALID;
expected = NULL;
if (hb_atomic_ptr_cmpexch
(&font->shaper_data.fallback, &expected, &data))
return !HB_SHAPER_DATA_IS_INVALID(data);
if (data != HB_SHAPER_DATA_INVALID
&& data != HB_SHAPER_DATA_SUCCEEDED)
hb_fallback_shaper_font_data_destroy(data);
}
}
static void hb_shape_plan_plan(hb_shape_plan_t * shape_plan,
const hb_feature_t * user_features,
unsigned num_user_features,
const char *const *shaper_list)
{
struct hb_shaper_pair_t *shapers;
unsigned i;
shapers = hb_shapers_get();
if (!shaper_list) {
for (i = 0; i < HB_SHAPERS_COUNT; ++i)
if (0) ;
#ifdef HAVE_GRAPHITE2
else if (shapers[i].func == hb_graphite2_shape) {
if (hb_graphite2_shaper_face_data_ensure
(shape_plan->face)) {
shape_plan->shaper_data.graphite2 =
hb_graphite2_shaper_shape_plan_data_create
(shape_plan, user_features,
num_user_features);
shape_plan->shaper_func =
hb_graphite2_shape;
shape_plan->shaper_name = "graphite2";
return;
}
}
#endif
#ifdef HAVE_OT
else if (shapers[i].func == hb_ot_shape) {
if (hb_ot_shaper_face_data_ensure
(shape_plan->face)) {
shape_plan->shaper_data.ot =
hb_ot_shaper_shape_plan_data_create
(shape_plan, user_features,
num_user_features);
shape_plan->shaper_func = hb_ot_shape;
shape_plan->shaper_name = "ot";
return;
}
}
#endif
else if (shapers[i].func == hb_fallback_shape) {
if (hb_fallback_shaper_face_data_ensure
(shape_plan->face)) {
shape_plan->shaper_data.fallback =
hb_fallback_shaper_shape_plan_data_create
(shape_plan, user_features,
num_user_features);
shape_plan->shaper_func =
hb_fallback_shape;
shape_plan->shaper_name = "fallback";
return;
}
}
} else {
for (; *shaper_list; ++shaper_list)
if (0) ;
#ifdef HAVE_GRAPHITE2
else if (0 == strcmp(*shaper_list, "graphite2")) {
if (hb_graphite2_shaper_face_data_ensure
(shape_plan->face)) {
shape_plan->shaper_data.graphite2 =
hb_graphite2_shaper_shape_plan_data_create
(shape_plan, user_features,
num_user_features);
shape_plan->shaper_func =
hb_graphite2_shape;
shape_plan->shaper_name = "graphite2";
return;
}
}
#endif
#ifdef HAVE_OT
else if (0 == strcmp(*shaper_list, "ot")) {
if (hb_ot_shaper_face_data_ensure
(shape_plan->face)) {
shape_plan->shaper_data.ot =
hb_ot_shaper_shape_plan_data_create
(shape_plan, user_features,
num_user_features);
shape_plan->shaper_func = hb_ot_shape;
shape_plan->shaper_name = "ot";
return;
}
}
#endif
else if (0 == strcmp(*shaper_list, "fallback")) {
if (hb_fallback_shaper_face_data_ensure
(shape_plan->face)) {
shape_plan->shaper_data.fallback =
hb_fallback_shaper_shape_plan_data_create
(shape_plan, user_features,
num_user_features);
shape_plan->shaper_func =
hb_fallback_shape;
shape_plan->shaper_name = "fallback";
return;
}
}
}
}
hb_shape_plan_t *hb_shape_plan_create(hb_face_t * face,
const hb_segment_properties_t * props,
const hb_feature_t * user_features,
unsigned int num_user_features,
const char *const *shaper_list)
{
hb_shape_plan_t *shape_plan;
assert(props->direction != HB_DIRECTION_INVALID);
if (!face)
face = hb_face_get_empty();
if (!props
|| hb_atomic_int32_get(&face->ref_cnt) == REF_CNT_INVALID_VAL)
return hb_shape_plan_get_empty();
shape_plan = calloc(1, sizeof(*shape_plan));
if (!shape_plan)
return hb_shape_plan_get_empty();
hb_atomic_int32_set(&shape_plan->ref_cnt, 1);
shape_plan->default_shaper_list = shaper_list == NULL;
hb_face_make_immutable(face);
shape_plan->face = hb_face_reference(face);
shape_plan->props = *props;
hb_shape_plan_plan(shape_plan, user_features, num_user_features,
shaper_list);
return shape_plan;
}
static hb_bool_t hb_shape_plan_matches(hb_shape_plan_t * shape_plan,
struct hb_shape_plan_proposal_t
*proposal)
{
return hb_segment_properties_equal(&shape_plan->props, &proposal->props)
&&
((shape_plan->default_shaper_list && proposal->shaper_list == NULL)
|| (shape_plan->shaper_func == proposal->shaper_func));
}
hb_shape_plan_t *hb_shape_plan_reference(hb_shape_plan_t * shape_plan)
{
if (hb_atomic_int32_get(&shape_plan->ref_cnt) != REF_CNT_INVALID_VAL)
hb_atomic_int32_add(&shape_plan->ref_cnt, 1);
return shape_plan;
}
void hb_shape_plan_destroy(hb_shape_plan_t * shape_plan)
{
if (!shape_plan)
return;
if (hb_atomic_int32_get(&shape_plan->ref_cnt) == REF_CNT_INVALID_VAL)
return;
hb_atomic_int32_add(&shape_plan->ref_cnt, -1);
if (hb_atomic_int32_get(&shape_plan->ref_cnt) > 0)
return;
hb_atomic_int32_set(&shape_plan->ref_cnt, REF_CNT_INVALID_VAL);
#ifdef HAVE_GRAPHITE2
if (shape_plan->shaper_data.graphite2
&& shape_plan->shaper_data.graphite2 != HB_SHAPER_DATA_INVALID
&& shape_plan->shaper_data.graphite2 != HB_SHAPER_DATA_SUCCEEDED)
hb_graphite2_shaper_shape_plan_data_destroy
(shape_plan->shaper_data.graphite2);
#endif
#ifdef HAVE_OT
if (shape_plan->shaper_data.ot
&& shape_plan->shaper_data.ot != HB_SHAPER_DATA_INVALID
&& shape_plan->shaper_data.ot != HB_SHAPER_DATA_SUCCEEDED)
hb_ot_shaper_shape_plan_data_destroy(shape_plan->shaper_data.
ot);
#endif
if (shape_plan->shaper_data.fallback
&& shape_plan->shaper_data.fallback != HB_SHAPER_DATA_INVALID
&& shape_plan->shaper_data.fallback != HB_SHAPER_DATA_SUCCEEDED)
hb_fallback_shaper_shape_plan_data_destroy
(shape_plan->shaper_data.fallback);
hb_face_destroy(shape_plan->face);
free(shape_plan);
}
hb_shape_plan_t *hb_shape_plan_create_cached(hb_face_t * face,
const hb_segment_properties_t *
props,
const hb_feature_t * user_features,
unsigned int num_user_features,
const char *const *shaper_list)
{
struct hb_shape_plan_proposal_t proposal;
const char *const *shaper_item;
struct plan_node_t *cached_plan_nodes;
hb_shape_plan_t *shape_plan;
if (num_user_features)
return hb_shape_plan_create(face, props, user_features,
num_user_features, shaper_list);
/*XXX:proposal.props = *props;*/
memcpy((void*)&proposal.props,(void*)props,sizeof(proposal.props));
proposal.shaper_list = shaper_list;
proposal.shaper_func = NULL;
if (shaper_list) {
/*Choose shaper. Adapted from hb_shape_plan_plan(). */
for (shaper_item = shaper_list; *shaper_item; shaper_item++)
if (0) ;
#ifdef HAVE_GRAPHITE2
else if (0 == strcmp(*shaper_item, "graphite2")) {
if (hb_graphite2_shaper_face_data_ensure(face))
proposal.shaper_func =
hb_graphite2_shape;
}
#endif
#ifdef HAVE_OT
else if (0 == strcmp(*shaper_item, "ot")) {
if (hb_ot_shaper_face_data_ensure(face))
proposal.shaper_func = hb_ot_shape;
}
#endif
else if (0 == strcmp(*shaper_item, "fallback")) {
if (hb_fallback_shaper_face_data_ensure(face))
proposal.shaper_func =
hb_fallback_shape;
}
if (!proposal.shaper_list)
return hb_shape_plan_get_empty();
}
cached_plan_nodes = hb_atomic_ptr_get(&face->shape_plans);
while (1) {
struct plan_node_t *node;
for (node = cached_plan_nodes; node; node = node->next)
if (hb_shape_plan_matches(node->shape_plan, &proposal))
return
hb_shape_plan_reference(node->shape_plan);
/*Not found. */
shape_plan =
hb_shape_plan_create(face, props, user_features,
num_user_features, shaper_list);
node = calloc(1, sizeof(*node));
if (!node)
return shape_plan;
node->shape_plan = shape_plan;
node->next = cached_plan_nodes;
if (hb_atomic_ptr_cmpexch
(&face->shape_plans, &cached_plan_nodes, &node))
break;
hb_shape_plan_destroy(shape_plan);
free(node);
}
/*Release our reference on face. */
hb_face_destroy(face);
return hb_shape_plan_reference(shape_plan);
}
hb_bool_t hb_shape_plan_execute(hb_shape_plan_t * shape_plan, hb_font_t * font,
hb_buffer_t * buffer,
const hb_feature_t * features,
unsigned num_features)
{
if (hb_atomic_int32_get(&shape_plan->ref_cnt) == REF_CNT_INVALID_VAL
|| hb_atomic_int32_get(&font->ref_cnt) == REF_CNT_INVALID_VAL
|| hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
return FALSE;
assert(shape_plan->face == font->face);
assert(hb_segment_properties_equal(&shape_plan->props, &buffer->props));
if (0) ;
#ifdef HAVE_GRAPHITE2
else if (shape_plan->shaper_func == hb_graphite2_shape)
return shape_plan->shaper_data.graphite2
&& hb_graphite2_shaper_font_data_ensure(font)
&& hb_graphite2_shape(shape_plan, font, buffer, features,
num_features);
#endif
#ifdef HAVE_OT
else if (shape_plan->shaper_func == hb_ot_shape)
return shape_plan->shaper_data.ot
&& hb_ot_shaper_font_data_ensure(font)
&& hb_ot_shape(shape_plan, font, buffer, features,
num_features);
#endif
else if (shape_plan->shaper_func == hb_fallback_shape)
return shape_plan->shaper_data.fallback
&& hb_fallback_shaper_font_data_ensure(font)
&& hb_fallback_shape(shape_plan, font, buffer, features,
num_features);
return FALSE;
}