Towards a mask allocator
This commit is contained in:
parent
81c5e8724b
commit
0e235d0fc9
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2009 Red Hat, Inc.
|
* Copyright (C) 2009,2010 Red Hat, Inc.
|
||||||
*
|
*
|
||||||
* This is part of HarfBuzz, a text shaping library.
|
* This is part of HarfBuzz, a text shaping library.
|
||||||
*
|
*
|
||||||
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
#include "hb-buffer-private.hh"
|
#include "hb-buffer-private.hh"
|
||||||
|
|
||||||
|
#include "hb-open-type-private.hh"
|
||||||
|
|
||||||
#include "hb-ot-layout.h"
|
#include "hb-ot-layout.h"
|
||||||
|
|
||||||
/* XXX vertical */
|
/* XXX vertical */
|
||||||
|
@ -95,102 +97,159 @@ cmp_lookups (const void *p1, const void *p2)
|
||||||
|
|
||||||
#define MAX_FEATURES 100
|
#define MAX_FEATURES 100
|
||||||
|
|
||||||
struct feature_info {
|
struct hb_mask_allocator_t {
|
||||||
hb_tag_t tag;
|
|
||||||
unsigned int index;
|
|
||||||
unsigned int value;
|
|
||||||
hb_mask_t mask;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct feature_setup_state {
|
struct feature_info_t {
|
||||||
hb_mask_t global_mask;
|
hb_tag_t tag;
|
||||||
unsigned int next_bit;
|
unsigned int value;
|
||||||
unsigned int count;
|
bool global;
|
||||||
feature_info info[MAX_FEATURES];
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
static int
|
||||||
collect_feature_info(hb_buffer_t *buffer,
|
cmp (const void *p1, const void *p2)
|
||||||
hb_face_t *face,
|
|
||||||
hb_tag_t table_tag,
|
|
||||||
unsigned int script_index,
|
|
||||||
unsigned int language_index,
|
|
||||||
const hb_feature_t &feature,
|
|
||||||
feature_setup_state &fss,
|
|
||||||
const hb_feature_t *features,
|
|
||||||
unsigned int num_features)
|
|
||||||
{
|
|
||||||
if (fss.count == MAX_FEATURES)
|
|
||||||
return; // FIXME - make the feature_info array growable?
|
|
||||||
|
|
||||||
unsigned int i, feature_index;
|
|
||||||
if (!hb_ot_layout_language_find_feature (face, table_tag, script_index, language_index,
|
|
||||||
feature.tag, &feature_index))
|
|
||||||
return;
|
|
||||||
|
|
||||||
fss.info[fss.count].tag = feature.tag;
|
|
||||||
fss.info[fss.count].index = feature_index;
|
|
||||||
|
|
||||||
// check if we have already allocated mask bits for this feature tag
|
|
||||||
for (i = 0; i < fss.count; ++i)
|
|
||||||
{
|
|
||||||
if (fss.info[i].tag == feature.tag)
|
|
||||||
{
|
{
|
||||||
fss.info[fss.count].mask = fss.info[i].mask;
|
const feature_info_t *a = (const feature_info_t *) p1;
|
||||||
fss.info[fss.count].value = feature.value << _hb_ctz(fss.info[fss.count].mask);
|
const feature_info_t *b = (const feature_info_t *) p2;
|
||||||
if (feature.start == 0 && feature.end == (unsigned int) -1)
|
|
||||||
fss.global_mask |= fss.info[fss.count].value;
|
if (a->tag != b->tag)
|
||||||
else
|
return a->tag < b->tag ? -1 : 1;
|
||||||
buffer->set_masks(fss.info[fss.count].value, fss.info[fss.count].mask, feature.start, feature.end);
|
|
||||||
++fss.count;
|
return p1 < p2 ? -1 : 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct feature_map_t {
|
||||||
|
hb_tag_t tag; /* should be first */
|
||||||
|
unsigned int index;
|
||||||
|
unsigned int shift;
|
||||||
|
hb_mask_t mask;
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmp (const void *p1, const void *p2)
|
||||||
|
{
|
||||||
|
const feature_map_t *a = (const feature_map_t *) p1;
|
||||||
|
const feature_map_t *b = (const feature_map_t *) p2;
|
||||||
|
|
||||||
|
return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
hb_mask_allocator_t (hb_face_t *face,
|
||||||
|
hb_tag_t table_tag,
|
||||||
|
unsigned int script_index,
|
||||||
|
unsigned int language_index,
|
||||||
|
const hb_feature_t *features,
|
||||||
|
unsigned int num_features) :
|
||||||
|
face (face),
|
||||||
|
table_tag (table_tag),
|
||||||
|
script_index (script_index),
|
||||||
|
language_index (language_index),
|
||||||
|
count (0)
|
||||||
|
{
|
||||||
|
if (!num_features)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Add features in reverse order */
|
||||||
|
for (unsigned int i = num_features - 1, count = 0; count < num_features; i--, count++) {
|
||||||
|
const hb_feature_t *feature = &features[i];
|
||||||
|
feature_info_t *info = &infos[count];
|
||||||
|
|
||||||
|
info->tag = feature->tag;
|
||||||
|
info->value = feature->value;
|
||||||
|
info->global = (feature->start == 0 && feature->end == (unsigned int) -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the feature occurs in remaining user features, as it might have a larger value there
|
void add_binary_feature (hb_tag_t tag,
|
||||||
hb_bool_t always_on = (feature.value == 1 && feature.start == 0 && feature.end == (unsigned int)-1);
|
bool global)
|
||||||
unsigned int max_value = feature.value;
|
|
||||||
for (i = 0; i < num_features; ++i)
|
|
||||||
{
|
{
|
||||||
if (features[i].tag != feature.tag)
|
feature_info_t *info = &infos[count++];
|
||||||
continue;
|
info->tag = tag;
|
||||||
if (features[i].value > max_value)
|
info->value = 1;
|
||||||
max_value = features[i].value;
|
info->global = global;
|
||||||
else if (features[i].value == 0 && features[i].start == 0 && features[i].end == (unsigned int)-1)
|
|
||||||
max_value = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (always_on && max_value == 1)
|
void compile (void)
|
||||||
{
|
{
|
||||||
fss.info[fss.count].value = 1;
|
global_mask = 0;
|
||||||
fss.info[fss.count].mask = 1;
|
next_bit = MASK_BITS_USED;
|
||||||
fss.global_mask |= 1;
|
|
||||||
++fss.count;
|
if (!count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
qsort (infos, count, sizeof (infos[0]), feature_info_t::cmp);
|
||||||
|
|
||||||
|
unsigned int j = 0;
|
||||||
|
for (unsigned int i = 1; i < count; i++)
|
||||||
|
if (infos[i].tag != infos[j].tag)
|
||||||
|
infos[++j] = infos[i];
|
||||||
|
else {
|
||||||
|
if (!infos[j].global)
|
||||||
|
infos[j].value = MAX (infos[j].value, infos[i].value);
|
||||||
|
}
|
||||||
|
count = j + 1;
|
||||||
|
|
||||||
|
/* Allocate bits now */
|
||||||
|
j = 0;
|
||||||
|
for (unsigned int i = 0; i < count; i++) {
|
||||||
|
const feature_info_t *info = &infos[i];
|
||||||
|
|
||||||
|
unsigned int bits_needed;
|
||||||
|
|
||||||
|
if (info->global && info->value == 1)
|
||||||
|
/* Uses the global bit */
|
||||||
|
bits_needed = 0;
|
||||||
|
else
|
||||||
|
bits_needed = _hb_bit_storage (info->value);
|
||||||
|
|
||||||
|
if (!info->value || next_bit + bits_needed > 8 * sizeof (hb_mask_t))
|
||||||
|
continue; /* Feature disabled, or not enough bits. */
|
||||||
|
|
||||||
|
unsigned int feature_index;
|
||||||
|
if (!hb_ot_layout_language_find_feature (face, table_tag, script_index, language_index,
|
||||||
|
info->tag, &feature_index))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
feature_map_t *map = &maps[j++];
|
||||||
|
|
||||||
|
map->tag = info->tag;
|
||||||
|
map->index = feature_index;
|
||||||
|
if (info->global && info->value == 1) {
|
||||||
|
/* Uses the global bit */
|
||||||
|
map->shift = 0;
|
||||||
|
map->mask = 1;
|
||||||
|
} else {
|
||||||
|
map->shift = next_bit;
|
||||||
|
map->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
|
||||||
|
next_bit += bits_needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->global && map->mask != 1)
|
||||||
|
global_mask |= map->mask;
|
||||||
|
}
|
||||||
|
count = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to allocate specific mask bit(s) for this feature
|
hb_mask_t get_global_mask (void) { return global_mask; }
|
||||||
unsigned int bits_needed = _hb_bit_storage(max_value);
|
const feature_map_t *find_feature (hb_tag_t tag) const {
|
||||||
if (!bits_needed || fss.next_bit + bits_needed > 8 * sizeof (hb_mask_t))
|
static const feature_map_t off_map = { HB_TAG_NONE, Index::NOT_FOUND_INDEX, 0, 0 };
|
||||||
{
|
const feature_map_t *map = (const feature_map_t *) bsearch (&tag, maps, count, sizeof (maps[0]), feature_map_t::cmp);
|
||||||
fss.info[fss.count].value = 0;
|
return map ? map : &off_map;
|
||||||
fss.info[fss.count].mask = 0;
|
|
||||||
++fss.count;
|
|
||||||
return; // feature is disabled, just omit it; or
|
|
||||||
// not enough bits available in the mask, ignore this feature :(
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// store the newly-allocated mask (in case further feature requests use the same tag)
|
|
||||||
// and shift the value into the right position
|
private:
|
||||||
fss.info[fss.count].value = feature.value << fss.next_bit;
|
hb_face_t *face;
|
||||||
fss.info[fss.count].mask = (1 << (fss.next_bit + bits_needed)) - (1 << fss.next_bit);
|
hb_tag_t table_tag;
|
||||||
if (feature.start == 0 && feature.end == (unsigned int) -1)
|
unsigned int script_index;
|
||||||
fss.global_mask |= fss.info[fss.count].value;
|
unsigned int language_index;
|
||||||
else
|
|
||||||
buffer->set_masks(fss.info[fss.count].value, fss.info[fss.count].mask, feature.start, feature.end);
|
unsigned int count;
|
||||||
fss.next_bit += bits_needed;
|
feature_info_t infos[MAX_FEATURES];
|
||||||
++fss.count;
|
feature_map_t maps[MAX_FEATURES];
|
||||||
}
|
|
||||||
|
hb_mask_t global_mask;
|
||||||
|
unsigned int next_bit;
|
||||||
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
setup_lookups (hb_face_t *face,
|
setup_lookups (hb_face_t *face,
|
||||||
|
@ -218,29 +277,53 @@ setup_lookups (hb_face_t *face,
|
||||||
&feature_index))
|
&feature_index))
|
||||||
add_feature (face, table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
|
add_feature (face, table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
|
||||||
|
|
||||||
// for features that may be turned on/off or have value > 1,
|
|
||||||
// we need to allocate bits in the mask
|
|
||||||
|
|
||||||
feature_setup_state fss;
|
hb_mask_allocator_t allocator (face, table_tag, script_index, language_index, features, num_features);
|
||||||
fss.global_mask = 0;
|
|
||||||
fss.next_bit = MASK_BITS_USED;
|
|
||||||
fss.count = 0;
|
|
||||||
|
|
||||||
hb_feature_t feature = { 0, 1, 0, (unsigned int)-1 };
|
|
||||||
|
|
||||||
switch (original_direction) {
|
switch (original_direction) {
|
||||||
case HB_DIRECTION_LTR:
|
case HB_DIRECTION_LTR:
|
||||||
feature.tag = HB_TAG ('l','t','r','a');
|
allocator.add_binary_feature (HB_TAG ('l','t','r','a'), true);
|
||||||
collect_feature_info(buffer, face, table_tag, script_index, language_index,
|
allocator.add_binary_feature (HB_TAG ('l','t','r','m'), true);
|
||||||
feature, fss, features, num_features);
|
|
||||||
feature.tag = HB_TAG ('l','t','r','m');
|
|
||||||
collect_feature_info(buffer, face, table_tag, script_index, language_index,
|
|
||||||
feature, fss, features, num_features);
|
|
||||||
break;
|
break;
|
||||||
case HB_DIRECTION_RTL:
|
case HB_DIRECTION_RTL:
|
||||||
feature.tag = HB_TAG ('r','t','l','a');
|
allocator.add_binary_feature (HB_TAG ('r','t','l','a'), true);
|
||||||
collect_feature_info(buffer, face, table_tag, script_index, language_index,
|
//allocator.add_binary_feature (HB_TAG ('r','t','l','m'), false);
|
||||||
feature, fss, features, num_features);
|
allocator.add_binary_feature (HB_TAG ('r','t','l','m'), true);
|
||||||
|
break;
|
||||||
|
case HB_DIRECTION_TTB:
|
||||||
|
case HB_DIRECTION_BTT:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_LENGTH (default_features); i++)
|
||||||
|
allocator.add_binary_feature (default_features[i], true);
|
||||||
|
|
||||||
|
|
||||||
|
/* Compile features */
|
||||||
|
allocator.compile ();
|
||||||
|
|
||||||
|
|
||||||
|
/* Gather lookup indices for features and set buffer masks at the same time */
|
||||||
|
|
||||||
|
const hb_mask_allocator_t::feature_map_t *map;
|
||||||
|
|
||||||
|
hb_mask_t global_mask = allocator.get_global_mask ();
|
||||||
|
if (global_mask)
|
||||||
|
buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1);
|
||||||
|
|
||||||
|
switch (original_direction) {
|
||||||
|
case HB_DIRECTION_LTR:
|
||||||
|
map = allocator.find_feature (HB_TAG ('l','t','r','a'));
|
||||||
|
add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
|
||||||
|
map = allocator.find_feature (HB_TAG ('l','t','r','m'));
|
||||||
|
add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
|
||||||
|
break;
|
||||||
|
case HB_DIRECTION_RTL:
|
||||||
|
map = allocator.find_feature (HB_TAG ('r','t','l','a'));
|
||||||
|
add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
|
||||||
|
//map = allocator.find_feature (HB_TAG ('r','t','l','m'));
|
||||||
|
add_feature (face, table_tag, map->index, MASK_RTLM, lookups, num_lookups, room_lookups);
|
||||||
break;
|
break;
|
||||||
case HB_DIRECTION_TTB:
|
case HB_DIRECTION_TTB:
|
||||||
case HB_DIRECTION_BTT:
|
case HB_DIRECTION_BTT:
|
||||||
|
@ -250,23 +333,21 @@ setup_lookups (hb_face_t *face,
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH (default_features); i++)
|
for (i = 0; i < ARRAY_LENGTH (default_features); i++)
|
||||||
{
|
{
|
||||||
feature.tag = default_features[i];
|
map = allocator.find_feature (default_features[i]);
|
||||||
collect_feature_info(buffer, face, table_tag, script_index, language_index,
|
add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
|
||||||
feature, fss, features, num_features);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < num_features; i++)
|
for (i = 0; i < num_features; i++)
|
||||||
{
|
{
|
||||||
collect_feature_info(buffer, face, table_tag, script_index, language_index,
|
hb_feature_t *feature = &features[i];
|
||||||
features[i], fss, features + i + 1, num_features - i - 1);
|
map = allocator.find_feature (feature->tag);
|
||||||
|
add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
|
||||||
|
if (!(feature->start == 0 && feature->end == (unsigned int)-1))
|
||||||
|
buffer->set_masks (features[i].value << map->shift, map->mask, feature->start, feature->end);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < fss.count; ++i)
|
|
||||||
if (fss.info[i].mask)
|
|
||||||
add_feature (face, table_tag, fss.info[i].index, fss.info[i].mask, lookups, num_lookups, room_lookups);
|
|
||||||
|
|
||||||
if (fss.global_mask > 1) // the 0x01 bit was set by clear_masks()
|
/* Sort lookups and merge duplicates */
|
||||||
buffer->set_masks (fss.global_mask, fss.global_mask, 0, (unsigned int) -1);
|
|
||||||
|
|
||||||
qsort (lookups, *num_lookups, sizeof (lookups[0]), cmp_lookups);
|
qsort (lookups, *num_lookups, sizeof (lookups[0]), cmp_lookups);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue