gfx/graphite2/src/Justifier.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:f6b205d53ad8
1 /* GRAPHITE2 LICENSING
2
3 Copyright 2012, SIL International
4 All rights reserved.
5
6 This library is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should also have received a copy of the GNU Lesser General Public
17 License along with this library in the file named "LICENSE".
18 If not, write to the Free Software Foundation, 51 Franklin Street,
19 Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
20 internet at http://www.fsf.org/licenses/lgpl.html.
21
22 Alternatively, the contents of this file may be used under the terms of the
23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
24 License, as published by the Free Software Foundation, either version 2
25 of the License or (at your option) any later version.
26 */
27
28 #include "inc/Segment.h"
29 #include "graphite2/Font.h"
30 #include "inc/debug.h"
31 #include "inc/CharInfo.h"
32 #include "inc/Slot.h"
33 #include "inc/Main.h"
34 #include <math.h>
35
36 using namespace graphite2;
37
38 class JustifyTotal {
39 public:
40 JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {}
41 void accumulate(Slot *s, Segment *seg, int level);
42 int weight() const { return m_tWeight; }
43
44 CLASS_NEW_DELETE
45
46 private:
47 int m_numGlyphs;
48 int m_tStretch;
49 int m_tShrink;
50 int m_tStep;
51 int m_tWeight;
52 };
53
54 void JustifyTotal::accumulate(Slot *s, Segment *seg, int level)
55 {
56 ++m_numGlyphs;
57 m_tStretch += s->getJustify(seg, level, 0);
58 m_tShrink += s->getJustify(seg, level, 1);
59 m_tStep += s->getJustify(seg, level, 2);
60 m_tWeight += s->getJustify(seg, level, 3);
61 }
62
63 float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags flags, Slot *pFirst, Slot *pLast)
64 {
65 Slot *s, *end;
66 float currWidth = 0.0;
67 const float scale = font ? font->scale() : 1.0f;
68 Position res;
69
70 if (width < 0 && !(silf()->flags()))
71 return width;
72
73 if (!pFirst) pFirst = pSlot;
74 while (!pFirst->isBase()) pFirst = pFirst->attachedTo();
75 if (!pLast) pLast = last();
76 while (!pLast->isBase()) pLast = pLast->attachedTo();
77 const float base = pFirst->origin().x / scale;
78 width = width / scale;
79 if ((flags & gr_justEndInline) == 0)
80 {
81 do {
82 Rect bbox = theGlyphBBoxTemporary(pLast->glyph());
83 if (bbox.bl.x != 0. || bbox.bl.y != 0. || bbox.tr.x != 0. || bbox.tr.y == 0.)
84 break;
85 pLast = pLast->prev();
86 } while (pLast != pFirst);
87 }
88
89 end = pLast->nextSibling();
90 pFirst = pFirst->nextSibling();
91
92 int icount = 0;
93 int numLevels = silf()->numJustLevels();
94 if (!numLevels)
95 {
96 for (s = pSlot; s != end; s = s->next())
97 {
98 CharInfo *c = charinfo(s->before());
99 if (isWhitespace(c->unicodeChar()))
100 {
101 s->setJustify(this, 0, 3, 1);
102 s->setJustify(this, 0, 2, 1);
103 s->setJustify(this, 0, 0, -1);
104 ++icount;
105 }
106 }
107 if (!icount)
108 {
109 for (s = pSlot; s != end; s = s->nextSibling())
110 {
111 s->setJustify(this, 0, 3, 1);
112 s->setJustify(this, 0, 2, 1);
113 s->setJustify(this, 0, 0, -1);
114 }
115 }
116 ++numLevels;
117 }
118
119 JustifyTotal *stats = new JustifyTotal[numLevels];
120 if (!stats) return -1.0;
121 for (s = pFirst; s != end; s = s->nextSibling())
122 {
123 float w = s->origin().x / scale + s->advance() - base;
124 if (w > currWidth) currWidth = w;
125 for (int j = 0; j < numLevels; ++j)
126 stats[j].accumulate(s, this, j);
127 s->just(0);
128 }
129
130 for (int i = (width < 0.0) ? -1 : numLevels - 1; i >= 0; --i)
131 {
132 float diff;
133 float error = 0.;
134 float diffpw;
135 int tWeight = stats[i].weight();
136
137 do {
138 error = 0.;
139 diff = width - currWidth;
140 diffpw = diff / tWeight;
141 tWeight = 0;
142 for (s = pFirst; s != end; s = s->nextSibling()) // don't include final glyph
143 {
144 int w = s->getJustify(this, i, 3);
145 float pref = diffpw * w + error;
146 int step = s->getJustify(this, i, 2);
147 if (!step) step = 1; // handle lazy font developers
148 if (pref > 0)
149 {
150 float max = uint16(s->getJustify(this, i, 0));
151 if (i == 0) max -= s->just();
152 if (pref > max) pref = max;
153 else tWeight += w;
154 }
155 else
156 {
157 float max = uint16(s->getJustify(this, i, 1));
158 if (i == 0) max += s->just();
159 if (-pref > max) pref = -max;
160 else tWeight += w;
161 }
162 int actual = step ? int(pref / step) * step : int(pref);
163
164 if (actual)
165 {
166 error += diffpw * w - actual;
167 if (i == 0)
168 s->just(s->just() + actual);
169 else
170 s->setJustify(this, i, 4, actual);
171 }
172 }
173 currWidth += diff - error;
174 } while (i == 0 && int(abs(error)) > 0 && tWeight);
175 }
176
177 Slot *oldFirst = m_first;
178 Slot *oldLast = m_last;
179 if (silf()->flags() & 1)
180 {
181 m_first = pSlot = addLineEnd(pSlot);
182 m_last = pLast = addLineEnd(end);
183 if (!m_first || !m_last) return -1.0;
184 }
185 else
186 {
187 m_first = pSlot;
188 m_last = pLast;
189 }
190
191 // run justification passes here
192 #if !defined GRAPHITE2_NTRACING
193 json * const dbgout = m_face->logger();
194 if (dbgout)
195 *dbgout << json::object
196 << "justifies" << objectid(this)
197 << "passes" << json::array;
198 #endif
199
200 if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0. || (silf()->flags() & 1)))
201 m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass());
202
203 #if !defined GRAPHITE2_NTRACING
204 if (dbgout)
205 {
206 *dbgout << json::item << json::close; // Close up the passes array
207 positionSlots(NULL, pSlot, pLast);
208 Slot *lEnd = pLast->nextSibling();
209 *dbgout << "output" << json::array;
210 for(Slot * t = pSlot; t != lEnd; t = t->next())
211 *dbgout << dslot(this, t);
212 *dbgout << json::close << json::close;
213 }
214 #endif
215
216 res = positionSlots(font, pSlot, pLast);
217
218 if (silf()->flags() & 1)
219 {
220 delLineEnd(m_first);
221 delLineEnd(m_last);
222 }
223 m_first = oldFirst;
224 m_last = oldLast;
225 return res.x;
226 }
227
228 Slot *Segment::addLineEnd(Slot *nSlot)
229 {
230 Slot *eSlot = newSlot();
231 if (!eSlot) return NULL;
232 const uint16 gid = silf()->endLineGlyphid();
233 const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
234 eSlot->setGlyph(this, gid, theGlyph);
235 if (nSlot)
236 {
237 eSlot->next(nSlot);
238 eSlot->prev(nSlot->prev());
239 nSlot->prev(eSlot);
240 eSlot->before(nSlot->before());
241 if (eSlot->prev())
242 eSlot->after(eSlot->prev()->after());
243 else
244 eSlot->after(nSlot->before());
245 }
246 else
247 {
248 nSlot = m_last;
249 eSlot->prev(nSlot);
250 nSlot->next(eSlot);
251 eSlot->after(eSlot->prev()->after());
252 eSlot->before(nSlot->after());
253 }
254 return eSlot;
255 }
256
257 void Segment::delLineEnd(Slot *s)
258 {
259 Slot *nSlot = s->next();
260 if (nSlot)
261 {
262 nSlot->prev(s->prev());
263 if (s->prev())
264 s->prev()->next(nSlot);
265 }
266 else
267 s->prev()->next(NULL);
268 freeSlot(s);
269 }
270

mercurial