Skip to content

Commit b2ab7d5

Browse files
committed
Refactor GlyphGroupings to use separate exclusive glyphs grouping.
Prior to this change exclusive glyphs were being kep in the 'and glyph' groupings and down stream users needed to know that 'and glyph' groupings of size 1 are exclusive patches. This change moves exclusive groupings to their own map to make it clear and reduce the need for special casing in various other places.
1 parent 821154a commit b2ab7d5

File tree

8 files changed

+92
-81
lines changed

8 files changed

+92
-81
lines changed

ift/encoder/candidate_merge.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,7 @@ StatusOr<double> CandidateMerge::ComputeCostDelta(
258258
modified_conditions));
259259

260260
double cost_delta = 0.0;
261-
VLOG(1) << "cost_delta for merge of " << merged_segments.ToString()
262-
<< " =";
261+
VLOG(1) << "cost_delta for merge of " << merged_segments.ToString() << " =";
263262
if (!moving_to_init_font) {
264263
// Merge will introduce a new patch (merged_segment) with size
265264
// "new_patch_size", add the associated cost.

ift/encoder/closure_glyph_segmenter.cc

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,11 @@ StatusOr<std::optional<GlyphSet>> TryMergingABaseSegment(
202202

203203
StatusOr<std::optional<GlyphSet>> MergeSegmentWithHeuristic(
204204
SegmentationContext& context, uint32_t base_segment_index) {
205-
auto base_segment_glyphs = context.glyph_groupings.AndGlyphGroups().find(
206-
SegmentSet{base_segment_index});
207-
if (base_segment_glyphs == context.glyph_groupings.AndGlyphGroups().end() ||
205+
auto base_segment_glyphs =
206+
context.glyph_groupings.ExclusiveGlyphs(base_segment_index);
207+
if (base_segment_glyphs.empty() ||
208208
!TRY(CandidateMerge::IsPatchTooSmall(context, base_segment_index,
209-
base_segment_glyphs->second))) {
209+
base_segment_glyphs))) {
210210
// Patch is big enough, no merge is needed.
211211
return std::nullopt;
212212
}
@@ -293,16 +293,15 @@ Status CollectExclusiveCandidateMerges(
293293
return absl::OkStatus();
294294
}
295295

296-
SegmentSet triggering_segments{segment_index};
297296
auto segment_glyphs =
298-
context.glyph_groupings.AndGlyphGroups().find(triggering_segments);
299-
if (segment_glyphs == context.glyph_groupings.AndGlyphGroups().end() ||
300-
segment_glyphs->second.empty()) {
297+
context.glyph_groupings.ExclusiveGlyphs(segment_index);
298+
if (segment_glyphs.empty()) {
301299
// This segment has no exclusive glyphs, so no need to consider it for a
302300
// merge.
303301
continue;
304302
}
305303

304+
SegmentSet triggering_segments{segment_index};
306305
auto candidate_merge = TRY(CandidateMerge::AssessMerge(
307306
context, base_segment_index, triggering_segments,
308307
smallest_candidate_merge));
@@ -361,10 +360,9 @@ StatusOr<std::optional<GlyphSet>> MergeSegmentWithCosts(
361360
// would be to gather some frequency data, test this approach as is, and then
362361
// refine it potentially using some of the proposals noted above.
363362

364-
auto base_segment_glyphs = context.glyph_groupings.AndGlyphGroups().find(
365-
SegmentSet{base_segment_index});
366-
if (base_segment_glyphs == context.glyph_groupings.AndGlyphGroups().end() ||
367-
base_segment_glyphs->second.empty()) {
363+
auto base_segment_glyphs =
364+
context.glyph_groupings.ExclusiveGlyphs(base_segment_index);
365+
if (base_segment_glyphs.empty()) {
368366
// This base segment has no exclusive glyphs, there's no need to to compute
369367
// merges.
370368
return std::nullopt;
@@ -379,8 +377,8 @@ StatusOr<std::optional<GlyphSet>> MergeSegmentWithCosts(
379377
// If min group size is met, then we will no longer consider merge's that
380378
// have a positive cost delta so start with an existing smallest candidate
381379
// set to cost delta 0 which will filter out positive cost delta candidates.
382-
unsigned base_size = TRY(
383-
context.patch_size_cache->GetPatchSize(base_segment_glyphs->second));
380+
unsigned base_size =
381+
TRY(context.patch_size_cache->GetPatchSize(base_segment_glyphs));
384382
smallest_candidate_merge = CandidateMerge::BaselineCandidate(
385383
base_segment_index, 0.0, base_size, base_segment.Probability(),
386384
context.GetMergeStrategy().NetworkOverheadCost());
@@ -572,14 +570,8 @@ Status ValidateIncrementalGroupings(hb_face_t* face,
572570
return absl::FailedPreconditionError("glyph_condition_set isn't correct.");
573571
}
574572

575-
if (non_incremental_context.glyph_groupings.AndGlyphGroups() !=
576-
context.glyph_groupings.AndGlyphGroups()) {
577-
return absl::FailedPreconditionError("and_glyph groups aren't correct.");
578-
}
579-
580-
if (non_incremental_context.glyph_groupings.OrGlyphGroups() !=
581-
context.glyph_groupings.OrGlyphGroups()) {
582-
return absl::FailedPreconditionError("or_glyph groups aren't correct.");
573+
if (non_incremental_context.glyph_groupings != context.glyph_groupings) {
574+
return absl::FailedPreconditionError("glyph groups aren't correct.");
583575
}
584576

585577
return absl::OkStatus();
@@ -694,18 +686,18 @@ Status ClosureGlyphSegmenter::MoveSegmentsToInitFont(
694686
continue;
695687
}
696688

697-
for (const auto& [candidate_segments, _] :
698-
context.glyph_groupings.AndGlyphGroups()) {
699-
if (candidate_segments.size() <= 1 ||
700-
candidate_segments.intersects(excluded)) {
701-
// All size 1 segments handled in the previous loop.
702-
// Since this is conjunction, having an excluded segment in it's
689+
for (const auto& [condition, _] :
690+
context.glyph_groupings.ConditionsAndGlyphs()) {
691+
if (condition.conditions().size() <= 1 ||
692+
condition.TriggeringSegments().intersects(excluded)) {
693+
// All size 1 conditions are disjunctive and handled in the previous
694+
// loop. Since this is conjunction, having an excluded segment in it's
703695
// condition makes the probability near 0.
704696
continue;
705697
}
706698

707-
change_made = TRY(CheckAndApplyInitFontMove(candidate_segments, context,
708-
initial_segment));
699+
change_made = TRY(CheckAndApplyInitFontMove(
700+
condition.TriggeringSegments(), context, initial_segment));
709701
if (change_made) {
710702
break;
711703
}

ift/encoder/closure_glyph_segmenter_test.cc

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,8 @@ TEST_F(ClosureGlyphSegmenterTest, TotalCost) {
738738

739739
// Basic no segment case.
740740
GlyphSegmentation segmentation1({'a', 'b', 'c'}, {}, {});
741-
auto sc = GlyphSegmentation::GroupsToSegmentation({}, {}, {}, segmentation1);
741+
auto sc =
742+
GlyphSegmentation::GroupsToSegmentation({}, {}, {}, {}, segmentation1);
742743
ASSERT_TRUE(sc.ok()) << sc;
743744

744745
ClosureGlyphSegmenter segmenter;
@@ -750,12 +751,12 @@ TEST_F(ClosureGlyphSegmenterTest, TotalCost) {
750751

751752
// Add some patches
752753
GlyphSegmentation segmentation2({'a', 'b', 'c'}, {}, {});
753-
sc = GlyphSegmentation::GroupsToSegmentation(
754-
{
755-
{{0}, {100, 101, 102}},
756-
{{1}, {103, 104, 105}},
757-
},
758-
{}, {}, segmentation2);
754+
sc = GlyphSegmentation::GroupsToSegmentation({}, {},
755+
{
756+
{0, {100, 101, 102}},
757+
{1, {103, 104, 105}},
758+
},
759+
{}, segmentation2);
759760
ASSERT_TRUE(sc.ok()) << sc;
760761

761762
std::vector<SubsetDefinition> segments{

ift/encoder/glyph_groupings.cc

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "ift/encoder/glyph_closure_cache.h"
77
#include "ift/encoder/glyph_condition_set.h"
88
#include "ift/encoder/requested_segmentation_information.h"
9+
#include "ift/encoder/types.h"
910

1011
using absl::btree_set;
1112
using absl::Status;
@@ -16,15 +17,24 @@ namespace ift::encoder {
1617

1718
void GlyphGroupings::InvalidateGlyphInformation(
1819
const GlyphConditions& condition, uint32_t gid) {
20+
if (condition.and_segments.size() == 1) {
21+
segment_index_t s = *condition.and_segments.begin();
22+
exclusive_glyph_group_[s].erase(gid);
23+
ActivationCondition activation_condition =
24+
ActivationCondition::exclusive_segment(s, 0);
25+
conditions_and_glyphs_[activation_condition].erase(gid);
26+
27+
if (exclusive_glyph_group_[s].empty()) {
28+
exclusive_glyph_group_.erase(s);
29+
RemoveConditionAndGlyphs(activation_condition);
30+
}
31+
}
32+
1933
auto it = and_glyph_groups_.find(condition.and_segments);
2034
if (it != and_glyph_groups_.end()) {
2135
it->second.erase(gid);
2236
ActivationCondition activation_condition =
2337
ActivationCondition::and_segments(condition.and_segments, 0);
24-
if (condition.and_segments.size() == 1) {
25-
activation_condition = ActivationCondition::exclusive_segment(
26-
*condition.and_segments.begin(), 0);
27-
}
2838
conditions_and_glyphs_[activation_condition].erase(gid);
2939

3040
if (it->second.empty()) {
@@ -55,15 +65,23 @@ Status GlyphGroupings::GroupGlyphs(
5565
GlyphClosureCache& closure_cache, const GlyphSet& glyphs) {
5666
const auto& initial_closure = segmentation_info.InitFontGlyphs();
5767

68+
SegmentSet modified_exclusive_segments;
5869
btree_set<SegmentSet> modified_and_groups;
5970
btree_set<SegmentSet> modified_or_groups;
6071
for (glyph_id_t gid : glyphs) {
6172
const auto& condition = glyph_condition_set.ConditionsFor(gid);
6273

6374
if (!condition.and_segments.empty()) {
64-
and_glyph_groups_[condition.and_segments].insert(gid);
65-
modified_and_groups.insert(condition.and_segments);
75+
if (condition.and_segments.size() == 1) {
76+
segment_index_t s = *condition.and_segments.begin();
77+
exclusive_glyph_group_[s].insert(gid);
78+
modified_exclusive_segments.insert(s);
79+
} else {
80+
and_glyph_groups_[condition.and_segments].insert(gid);
81+
modified_and_groups.insert(condition.and_segments);
82+
}
6683
}
84+
6785
if (!condition.or_segments.empty()) {
6886
or_glyph_groups_[condition.or_segments].insert(gid);
6987
modified_or_groups.insert(condition.or_segments);
@@ -76,14 +94,12 @@ Status GlyphGroupings::GroupGlyphs(
7694
}
7795
}
7896

79-
for (const auto& and_group : modified_and_groups) {
80-
if (and_group.size() == 1) {
81-
auto condition =
82-
ActivationCondition::exclusive_segment(*and_group.begin(), 0);
83-
AddConditionAndGlyphs(condition, and_glyph_groups_[and_group]);
84-
continue;
85-
}
97+
for (segment_index_t s : modified_exclusive_segments) {
98+
auto condition = ActivationCondition::exclusive_segment(s, 0);
99+
AddConditionAndGlyphs(condition, exclusive_glyph_group_[s]);
100+
}
86101

102+
for (const auto& and_group : modified_and_groups) {
87103
auto condition = ActivationCondition::and_segments(and_group, 0);
88104
AddConditionAndGlyphs(condition, and_glyph_groups_[and_group]);
89105
}

ift/encoder/glyph_groupings.h

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ class GlyphGroupings {
3333
}
3434
}
3535

36+
bool operator==(const GlyphGroupings& other) {
37+
return and_glyph_groups_ == other.and_glyph_groups_ &&
38+
or_glyph_groups_ == other.or_glyph_groups_ &&
39+
exclusive_glyph_group_ == other.exclusive_glyph_group_;
40+
}
41+
42+
bool operator!=(const GlyphGroupings& other) { return !(*this == other); }
43+
3644
const absl::btree_map<ActivationCondition, common::GlyphSet>&
3745
ConditionsAndGlyphs() const {
3846
return conditions_and_glyphs_;
@@ -54,14 +62,13 @@ class GlyphGroupings {
5462
return result;
5563
}
5664

57-
const absl::btree_map<common::SegmentSet, common::GlyphSet>& AndGlyphGroups()
58-
const {
59-
return and_glyph_groups_;
60-
}
61-
62-
const absl::btree_map<common::SegmentSet, common::GlyphSet>& OrGlyphGroups()
63-
const {
64-
return or_glyph_groups_;
65+
const common::GlyphSet& ExclusiveGlyphs(segment_index_t s) const {
66+
static const common::GlyphSet empty{};
67+
auto it = exclusive_glyph_group_.find(s);
68+
if (it != exclusive_glyph_group_.end()) {
69+
return it->second;
70+
}
71+
return empty;
6572
}
6673

6774
// Returns a list of conditions which include segment.
@@ -95,8 +102,8 @@ class GlyphGroupings {
95102
// segment).
96103
void AddGlyphsToExclusiveGroup(segment_index_t exclusive_segment,
97104
const common::GlyphSet& glyphs) {
98-
auto& and_glyphs = and_glyph_groups_[common::SegmentSet{exclusive_segment}];
99-
and_glyphs.union_set(glyphs);
105+
auto& exc_glyphs = exclusive_glyph_group_[exclusive_segment];
106+
exc_glyphs.union_set(glyphs);
100107

101108
ActivationCondition condition =
102109
ActivationCondition::exclusive_segment(exclusive_segment, 0);
@@ -119,8 +126,11 @@ class GlyphGroupings {
119126
segmentation_info.InitFontSegmentWithoutDefaults(),
120127
segmentation_info.InitFontGlyphs(), unmapped_glyphs_);
121128
segmentation.CopySegments(segmentation_info.SegmentSubsetDefinitions());
129+
122130
TRYV(GlyphSegmentation::GroupsToSegmentation(
123-
and_glyph_groups_, or_glyph_groups_, fallback_segments_, segmentation));
131+
and_glyph_groups_, or_glyph_groups_, exclusive_glyph_group_,
132+
fallback_segments_, segmentation));
133+
124134
return segmentation;
125135
}
126136

@@ -143,6 +153,7 @@ class GlyphGroupings {
143153

144154
absl::btree_map<common::SegmentSet, common::GlyphSet> and_glyph_groups_;
145155
absl::btree_map<common::SegmentSet, common::GlyphSet> or_glyph_groups_;
156+
absl::btree_map<segment_index_t, common::GlyphSet> exclusive_glyph_group_;
146157

147158
// An alternate representation of and/or_glyph_groups_, derived from them.
148159
absl::btree_map<ActivationCondition, common::GlyphSet> conditions_and_glyphs_;

ift/encoder/glyph_segmentation.cc

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,30 +35,20 @@ namespace ift::encoder {
3535
Status GlyphSegmentation::GroupsToSegmentation(
3636
const btree_map<SegmentSet, GlyphSet>& and_glyph_groups,
3737
const btree_map<SegmentSet, GlyphSet>& or_glyph_groups,
38+
const btree_map<segment_index_t, GlyphSet>& exclusive_glyph_groups,
3839
const SegmentSet& fallback_group, GlyphSegmentation& segmentation) {
3940
patch_id_t next_id = 0;
4041
segmentation.patches_.clear();
4142
segmentation.conditions_.clear();
4243

4344
// Map segments into patch ids
44-
for (const auto& [and_segments, glyphs] : and_glyph_groups) {
45-
if (and_segments.size() != 1) {
46-
continue;
47-
}
48-
49-
segment_index_t segment = *and_segments.begin();
45+
for (const auto& [segment, glyphs] : exclusive_glyph_groups) {
5046
segmentation.patches_.insert(std::pair(next_id, glyphs));
51-
// All 1 segment and conditions are considered to be exclusive
5247
segmentation.conditions_.insert(
5348
ActivationCondition::exclusive_segment(segment, next_id++));
5449
}
5550

5651
for (const auto& [and_segments, glyphs] : and_glyph_groups) {
57-
if (and_segments.size() == 1) {
58-
// already processed above
59-
continue;
60-
}
61-
6252
segmentation.patches_.insert(std::pair(next_id, glyphs));
6353
segmentation.conditions_.insert(
6454
ActivationCondition::and_segments(and_segments, next_id));

ift/encoder/glyph_segmentation.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class GlyphSegmentation {
9696
and_glyph_groups,
9797
const absl::btree_map<common::SegmentSet, common::GlyphSet>&
9898
or_glyph_groups,
99+
const absl::btree_map<segment_index_t, common::GlyphSet>&
100+
exclusive_glyph_groups,
99101
const common::SegmentSet& fallback_group,
100102
GlyphSegmentation& segmentation);
101103

ift/encoder/segmentation_context.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,12 @@ StatusOr<segment_index_t> SegmentationContext::ComputeSegmentCutoff() const {
9696
double total_cost = 0.0;
9797
double overhead = merge_strategy_.NetworkOverheadCost();
9898
for (segment_index_t s : active_segments_) {
99-
auto segment_glyphs = glyph_groupings.AndGlyphGroups().find(SegmentSet{s});
100-
if (segment_glyphs == glyph_groupings.AndGlyphGroups().end()) {
99+
auto segment_glyphs = glyph_groupings.ExclusiveGlyphs(s);
100+
if (segment_glyphs.empty()) {
101101
continue;
102102
}
103103

104-
double size = TRY(patch_size_cache->GetPatchSize(segment_glyphs->second));
104+
double size = TRY(patch_size_cache->GetPatchSize(segment_glyphs));
105105
double probability = segmentation_info_.Segments()[s].Probability();
106106
total_cost += probability * (size + overhead);
107107
}
@@ -112,12 +112,12 @@ StatusOr<segment_index_t> SegmentationContext::ComputeSegmentCutoff() const {
112112
for (auto it = active_segments_.rbegin(); it != active_segments_.rend();
113113
it++) {
114114
segment_index_t s = *it;
115-
auto segment_glyphs = glyph_groupings.AndGlyphGroups().find(SegmentSet{s});
116-
if (segment_glyphs == glyph_groupings.AndGlyphGroups().end()) {
115+
auto segment_glyphs = glyph_groupings.ExclusiveGlyphs(s);
116+
if (segment_glyphs.empty()) {
117117
continue;
118118
}
119119

120-
double size = TRY(patch_size_cache->GetPatchSize(segment_glyphs->second));
120+
double size = TRY(patch_size_cache->GetPatchSize(segment_glyphs));
121121
double probability = segmentation_info_.Segments()[s].Probability();
122122
cutoff_tail_cost -= probability * (size + overhead);
123123
if (cutoff_tail_cost < 0.0) {

0 commit comments

Comments
 (0)