Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/graphite/src/Justifier.cpp
9903 views
1
// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
2
// Copyright 2012, SIL International, All rights reserved.
3
4
5
#include "inc/Segment.h"
6
#include "graphite2/Font.h"
7
#include "inc/debug.h"
8
#include "inc/CharInfo.h"
9
#include "inc/Slot.h"
10
#include "inc/Main.h"
11
#include <cmath>
12
13
using namespace graphite2;
14
15
class JustifyTotal {
16
public:
17
JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {}
18
void accumulate(Slot *s, Segment *seg, int level);
19
int weight() const { return m_tWeight; }
20
21
CLASS_NEW_DELETE
22
23
private:
24
int m_numGlyphs;
25
int m_tStretch;
26
int m_tShrink;
27
int m_tStep;
28
int m_tWeight;
29
};
30
31
void JustifyTotal::accumulate(Slot *s, Segment *seg, int level)
32
{
33
++m_numGlyphs;
34
m_tStretch += s->getJustify(seg, level, 0);
35
m_tShrink += s->getJustify(seg, level, 1);
36
m_tStep += s->getJustify(seg, level, 2);
37
m_tWeight += s->getJustify(seg, level, 3);
38
}
39
40
float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast)
41
{
42
Slot *end = last();
43
float currWidth = 0.0;
44
const float scale = font ? font->scale() : 1.0f;
45
Position res;
46
47
if (width < 0 && !(silf()->flags()))
48
return width;
49
50
if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
51
{
52
reverseSlots();
53
std::swap(pFirst, pLast);
54
}
55
if (!pFirst) pFirst = pSlot;
56
while (!pFirst->isBase()) pFirst = pFirst->attachedTo();
57
if (!pLast) pLast = last();
58
while (!pLast->isBase()) pLast = pLast->attachedTo();
59
const float base = pFirst->origin().x / scale;
60
width = width / scale;
61
if ((jflags & gr_justEndInline) == 0)
62
{
63
while (pLast != pFirst && pLast)
64
{
65
Rect bbox = theGlyphBBoxTemporary(pLast->glyph());
66
if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f)
67
break;
68
pLast = pLast->prev();
69
}
70
}
71
72
if (pLast)
73
end = pLast->nextSibling();
74
if (pFirst)
75
pFirst = pFirst->nextSibling();
76
77
int icount = 0;
78
int numLevels = silf()->numJustLevels();
79
if (!numLevels)
80
{
81
for (Slot *s = pSlot; s && s != end; s = s->nextSibling())
82
{
83
CharInfo *c = charinfo(s->before());
84
if (isWhitespace(c->unicodeChar()))
85
{
86
s->setJustify(this, 0, 3, 1);
87
s->setJustify(this, 0, 2, 1);
88
s->setJustify(this, 0, 0, -1);
89
++icount;
90
}
91
}
92
if (!icount)
93
{
94
for (Slot *s = pSlot; s && s != end; s = s->nextSibling())
95
{
96
s->setJustify(this, 0, 3, 1);
97
s->setJustify(this, 0, 2, 1);
98
s->setJustify(this, 0, 0, -1);
99
}
100
}
101
++numLevels;
102
}
103
104
Vector<JustifyTotal> stats(numLevels);
105
for (Slot *s = pFirst; s && s != end; s = s->nextSibling())
106
{
107
float w = s->origin().x / scale + s->advance() - base;
108
if (w > currWidth) currWidth = w;
109
for (int j = 0; j < numLevels; ++j)
110
stats[j].accumulate(s, this, j);
111
s->just(0);
112
}
113
114
for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i)
115
{
116
float diff;
117
float error = 0.;
118
float diffpw;
119
int tWeight = stats[i].weight();
120
if (tWeight == 0) continue;
121
122
do {
123
error = 0.;
124
diff = width - currWidth;
125
diffpw = diff / tWeight;
126
tWeight = 0;
127
for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) // don't include final glyph
128
{
129
int w = s->getJustify(this, i, 3);
130
float pref = diffpw * w + error;
131
int step = s->getJustify(this, i, 2);
132
if (!step) step = 1; // handle lazy font developers
133
if (pref > 0)
134
{
135
float max = uint16(s->getJustify(this, i, 0));
136
if (i == 0) max -= s->just();
137
if (pref > max) pref = max;
138
else tWeight += w;
139
}
140
else
141
{
142
float max = uint16(s->getJustify(this, i, 1));
143
if (i == 0) max += s->just();
144
if (-pref > max) pref = -max;
145
else tWeight += w;
146
}
147
int actual = int(pref / step) * step;
148
149
if (actual)
150
{
151
error += diffpw * w - actual;
152
if (i == 0)
153
s->just(s->just() + actual);
154
else
155
s->setJustify(this, i, 4, actual);
156
}
157
}
158
currWidth += diff - error;
159
} while (i == 0 && int(std::abs(error)) > 0 && tWeight);
160
}
161
162
Slot *oldFirst = m_first;
163
Slot *oldLast = m_last;
164
if (silf()->flags() & 1)
165
{
166
m_first = pSlot = addLineEnd(pSlot);
167
m_last = pLast = addLineEnd(end);
168
if (!m_first || !m_last) return -1.0;
169
}
170
else
171
{
172
m_first = pSlot;
173
m_last = pLast;
174
}
175
176
// run justification passes here
177
#if !defined GRAPHITE2_NTRACING
178
json * const dbgout = m_face->logger();
179
if (dbgout)
180
*dbgout << json::object
181
<< "justifies" << objectid(this)
182
<< "passes" << json::array;
183
#endif
184
185
if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1)))
186
m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass());
187
188
#if !defined GRAPHITE2_NTRACING
189
if (dbgout)
190
{
191
*dbgout << json::item << json::close; // Close up the passes array
192
positionSlots(NULL, pSlot, pLast, m_dir);
193
Slot *lEnd = pLast->nextSibling();
194
*dbgout << "output" << json::array;
195
for(Slot * t = pSlot; t != lEnd; t = t->next())
196
*dbgout << dslot(this, t);
197
*dbgout << json::close << json::close;
198
}
199
#endif
200
201
res = positionSlots(font, pSlot, pLast, m_dir);
202
203
if (silf()->flags() & 1)
204
{
205
if (m_first)
206
delLineEnd(m_first);
207
if (m_last)
208
delLineEnd(m_last);
209
}
210
m_first = oldFirst;
211
m_last = oldLast;
212
213
if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
214
reverseSlots();
215
return res.x;
216
}
217
218
Slot *Segment::addLineEnd(Slot *nSlot)
219
{
220
Slot *eSlot = newSlot();
221
if (!eSlot) return NULL;
222
const uint16 gid = silf()->endLineGlyphid();
223
const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
224
eSlot->setGlyph(this, gid, theGlyph);
225
if (nSlot)
226
{
227
eSlot->next(nSlot);
228
eSlot->prev(nSlot->prev());
229
nSlot->prev(eSlot);
230
eSlot->before(nSlot->before());
231
if (eSlot->prev())
232
eSlot->after(eSlot->prev()->after());
233
else
234
eSlot->after(nSlot->before());
235
}
236
else
237
{
238
nSlot = m_last;
239
eSlot->prev(nSlot);
240
nSlot->next(eSlot);
241
eSlot->after(eSlot->prev()->after());
242
eSlot->before(nSlot->after());
243
}
244
return eSlot;
245
}
246
247
void Segment::delLineEnd(Slot *s)
248
{
249
Slot *nSlot = s->next();
250
if (nSlot)
251
{
252
nSlot->prev(s->prev());
253
if (s->prev())
254
s->prev()->next(nSlot);
255
}
256
else
257
s->prev()->next(NULL);
258
freeSlot(s);
259
}
260
261