1 /* GRAPHITE2 LICENSING
2
3 Copyright 2010, 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 #include <algorithm>
28 #include <limits>
29 #include <cmath>
30 #include <string>
31 #include <functional>
32 #include "inc/Collider.h"
33 #include "inc/Segment.h"
34 #include "inc/Slot.h"
35 #include "inc/GlyphCache.h"
36 #include "inc/Sparse.h"
37
38 #define ISQRT2 0.707106781f
39
40 // Possible rounding error for subbox boundaries: 0.016 = 1/64 = 1/256 * 4
41 // (values in font range from 0..256)
42 // #define SUBBOX_RND_ERR 0.016
43
44 using namespace graphite2;
45
46 //// SHIFT-COLLIDER ////
47
48 // Initialize the Collider to hold the basic movement limits for the
49 // target slot, the one we are focusing on fixing.
initSlot(Segment * seg,Slot * aSlot,const Rect & limit,float margin,float marginWeight,const Position & currShift,const Position & currOffset,int dir,GR_MAYBE_UNUSED json * const dbgout)50 bool ShiftCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin, float marginWeight,
51 const Position &currShift, const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout)
52 {
53 int i;
54 float mx, mn;
55 float a, shift;
56 const GlyphCache &gc = seg->getFace()->glyphs();
57 unsigned short gid = aSlot->gid();
58 if (!gc.check(gid))
59 return false;
60 const BBox &bb = gc.getBoundingBBox(gid);
61 const SlantBox &sb = gc.getBoundingSlantBox(gid);
62 //float sx = aSlot->origin().x + currShift.x;
63 //float sy = aSlot->origin().y + currShift.y;
64 if (currOffset.x != 0.f || currOffset.y != 0.f)
65 _limit = Rect(limit.bl - currOffset, limit.tr - currOffset);
66 else
67 _limit = limit;
68 // For a ShiftCollider, these indices indicate which vector we are moving by:
69 // each _ranges represents absolute space with respect to the origin of the slot. Thus take into account true origins but subtract the vmin for the slot
70 for (i = 0; i < 4; ++i)
71 {
72 switch (i) {
73 case 0 : // x direction
74 mn = _limit.bl.x + currOffset.x;
75 mx = _limit.tr.x + currOffset.x;
76 _len[i] = bb.xa - bb.xi;
77 a = currOffset.y + currShift.y;
78 _ranges[i].initialise<XY>(mn, mx, margin, marginWeight, a);
79 break;
80 case 1 : // y direction
81 mn = _limit.bl.y + currOffset.y;
82 mx = _limit.tr.y + currOffset.y;
83 _len[i] = bb.ya - bb.yi;
84 a = currOffset.x + currShift.x;
85 _ranges[i].initialise<XY>(mn, mx, margin, marginWeight, a);
86 break;
87 case 2 : // sum (negatively sloped diagonal boundaries)
88 // pick closest x,y limit boundaries in s direction
89 shift = currOffset.x + currOffset.y + currShift.x + currShift.y;
90 mn = -2 * min(currShift.x - _limit.bl.x, currShift.y - _limit.bl.y) + shift;
91 mx = 2 * min(_limit.tr.x - currShift.x, _limit.tr.y - currShift.y) + shift;
92 _len[i] = sb.sa - sb.si;
93 a = currOffset.x - currOffset.y + currShift.x - currShift.y;
94 _ranges[i].initialise<SD>(mn, mx, margin / ISQRT2, marginWeight, a);
95 break;
96 case 3 : // diff (positively sloped diagonal boundaries)
97 // pick closest x,y limit boundaries in d direction
98 shift = currOffset.x - currOffset.y + currShift.x - currShift.y;
99 mn = -2 * min(currShift.x - _limit.bl.x, _limit.tr.y - currShift.y) + shift;
100 mx = 2 * min(_limit.tr.x - currShift.x, currShift.y - _limit.bl.y) + shift;
101 _len[i] = sb.da - sb.di;
102 a = currOffset.x + currOffset.y + currShift.x + currShift.y;
103 _ranges[i].initialise<SD>(mn, mx, margin / ISQRT2, marginWeight, a);
104 break;
105 }
106 }
107
108 _target = aSlot;
109 if ((dir & 1) == 0)
110 {
111 // For LTR, switch and negate x limits.
112 _limit.bl.x = -1 * limit.tr.x;
113 //_limit.tr.x = -1 * limit.bl.x;
114 }
115 _currOffset = currOffset;
116 _currShift = currShift;
117 _origin = aSlot->origin() - currOffset; // the original anchor position of the glyph
118
119 _margin = margin;
120 _marginWt = marginWeight;
121
122 SlotCollision *c = seg->collisionInfo(aSlot);
123 _seqClass = c->seqClass();
124 _seqProxClass = c->seqProxClass();
125 _seqOrder = c->seqOrder();
126 return true;
127 }
128
129 template <class O>
sdm(float vi,float va,float mx,float my,O op)130 float sdm(float vi, float va, float mx, float my, O op)
131 {
132 float res = 2 * mx - vi;
133 if (op(res, vi + 2 * my))
134 {
135 res = va + 2 * my;
136 if (op(res, 2 * mx - va))
137 res = mx + my;
138 }
139 return res;
140 }
141
142 // Mark an area with a cost that can vary along the x or y axis. The region is expressed in terms of the centre of the target glyph in each axis
addBox_slope(bool isx,const Rect & box,const BBox & bb,const SlantBox & sb,const Position & org,float weight,float m,bool minright,int axis)143 void ShiftCollider::addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int axis)
144 {
145 float a, c;
146 switch (axis) {
147 case 0 :
148 if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0)
149 {
150 a = org.y + 0.5f * (bb.yi + bb.ya);
151 c = 0.5f * (bb.xi + bb.xa);
152 if (isx)
153 _ranges[axis].weighted<XY>(box.bl.x - c, box.tr.x - c, weight, a, m,
154 (minright ? box.tr.x : box.bl.x) - c, a, 0, false);
155 else
156 _ranges[axis].weighted<XY>(box.bl.x - c, box.tr.x - c, weight, a, 0, 0, org.y,
157 m * (a * a + sqr((minright ? box.tr.y : box.bl.y) - 0.5f * (bb.yi + bb.ya))), false);
158 }
159 break;
160 case 1 :
161 if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0)
162 {
163 a = org.x + 0.5f * (bb.xi + bb.xa);
164 c = 0.5f * (bb.yi + bb.ya);
165 if (isx)
166 _ranges[axis].weighted<XY>(box.bl.y - c, box.tr.y - c, weight, a, 0, 0, org.x,
167 m * (a * a + sqr((minright ? box.tr.x : box.bl.x) - 0.5f * (bb.xi + bb.xa))), false);
168 else
169 _ranges[axis].weighted<XY>(box.bl.y - c, box.tr.y - c, weight, a, m,
170 (minright ? box.tr.y : box.bl.y) - c, a, 0, false);
171 }
172 break;
173 case 2 :
174 if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di)
175 {
176 float d = org.x - org.y + 0.5f * (sb.di + sb.da);
177 c = 0.5f * (sb.si + sb.sa);
178 float smax = min(2 * box.tr.x - d, 2 * box.tr.y + d);
179 float smin = max(2 * box.bl.x - d, 2 * box.bl.y + d);
180 if (smin > smax) return;
181 float si;
182 a = d;
183 if (isx)
184 si = 2 * (minright ? box.tr.x : box.bl.x) - a;
185 else
186 si = 2 * (minright ? box.tr.y : box.bl.y) + a;
187 _ranges[axis].weighted<SD>(smin - c, smax - c, weight / 2, a, m / 2, si, 0, 0, isx);
188 }
189 break;
190 case 3 :
191 if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si)
192 {
193 float s = org.x + org.y + 0.5f * (sb.si + sb.sa);
194 c = 0.5f * (sb.di + sb.da);
195 float dmax = min(2 * box.tr.x - s, s - 2 * box.bl.y);
196 float dmin = max(2 * box.bl.x - s, s - 2 * box.tr.y);
197 if (dmin > dmax) return;
198 float di;
199 a = s;
200 if (isx)
201 di = 2 * (minright ? box.tr.x : box.bl.x) - a;
202 else
203 di = 2 * (minright ? box.tr.y : box.bl.y) + a;
204 _ranges[axis].weighted<SD>(dmin - c, dmax - c, weight / 2, a, m / 2, di, 0, 0, !isx);
205 }
206 break;
207 default :
208 break;
209 }
210 return;
211 }
212
213 // Mark an area with an absolute cost, making it completely inaccessible.
removeBox(const Rect & box,const BBox & bb,const SlantBox & sb,const Position & org,int axis)214 inline void ShiftCollider::removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int axis)
215 {
216 float c;
217 switch (axis) {
218 case 0 :
219 if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0)
220 {
221 c = 0.5f * (bb.xi + bb.xa);
222 _ranges[axis].exclude(box.bl.x - c, box.tr.x - c);
223 }
224 break;
225 case 1 :
226 if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0)
227 {
228 c = 0.5f * (bb.yi + bb.ya);
229 _ranges[axis].exclude(box.bl.y - c, box.tr.y - c);
230 }
231 break;
232 case 2 :
233 if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di
234 && box.width() > 0 && box.height() > 0)
235 {
236 float di = org.x - org.y + sb.di;
237 float da = org.x - org.y + sb.da;
238 float smax = sdm(di, da, box.tr.x, box.tr.y, std::greater<float>());
239 float smin = sdm(da, di, box.bl.x, box.bl.y, std::less<float>());
240 c = 0.5f * (sb.si + sb.sa);
241 _ranges[axis].exclude(smin - c, smax - c);
242 }
243 break;
244 case 3 :
245 if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si
246 && box.width() > 0 && box.height() > 0)
247 {
248 float si = org.x + org.y + sb.si;
249 float sa = org.x + org.y + sb.sa;
250 float dmax = sdm(si, sa, box.tr.x, -box.bl.y, std::greater<float>());
251 float dmin = sdm(sa, si, box.bl.x, -box.tr.y, std::less<float>());
252 c = 0.5f * (sb.di + sb.da);
253 _ranges[axis].exclude(dmin - c, dmax - c);
254 }
255 break;
256 default :
257 break;
258 }
259 return;
260 }
261
262 // Adjust the movement limits for the target to avoid having it collide
263 // with the given neighbor slot. Also determine if there is in fact a collision
264 // between the target and the given slot.
mergeSlot(Segment * seg,Slot * slot,const SlotCollision * cslot,const Position & currShift,bool isAfter,bool sameCluster,bool & hasCol,bool isExclusion,GR_MAYBE_UNUSED json * const dbgout)265 bool ShiftCollider::mergeSlot(Segment *seg, Slot *slot, const SlotCollision *cslot, const Position &currShift,
266 bool isAfter, // slot is logically after _target
267 bool sameCluster, bool &hasCol, bool isExclusion,
268 GR_MAYBE_UNUSED json * const dbgout )
269 {
270 bool isCol = false;
271 const float sx = slot->origin().x - _origin.x + currShift.x;
272 const float sy = slot->origin().y - _origin.y + currShift.y;
273 const float sd = sx - sy;
274 const float ss = sx + sy;
275 float vmin, vmax;
276 float omin, omax, otmin, otmax;
277 float cmin, cmax; // target limits
278 float torg;
279 const GlyphCache &gc = seg->getFace()->glyphs();
280 const unsigned short gid = slot->gid();
281 if (!gc.check(gid))
282 return false;
283 const BBox &bb = gc.getBoundingBBox(gid);
284
285 // SlotCollision * cslot = seg->collisionInfo(slot);
286 int orderFlags = 0;
287 bool sameClass = _seqProxClass == 0 && cslot->seqClass() == _seqClass;
288 if (sameCluster && _seqClass
289 && (sameClass || (_seqProxClass != 0 && cslot->seqClass() == _seqProxClass)))
290 // Force the target glyph to be in the specified direction from the slot we're testing.
291 orderFlags = _seqOrder;
292
293 // short circuit if only interested in direct collision and we are out of range
294 if (orderFlags || (sx + bb.xa + _margin >= _limit.bl.x && sx + bb.xi - _margin <= _limit.tr.x)
295 || (sy + bb.ya + _margin >= _limit.bl.y && sy + bb.yi - _margin <= _limit.tr.y))
296
297 {
298 const float tx = _currOffset.x + _currShift.x;
299 const float ty = _currOffset.y + _currShift.y;
300 const float td = tx - ty;
301 const float ts = tx + ty;
302 const SlantBox &sb = gc.getBoundingSlantBox(gid);
303 const unsigned short tgid = _target->gid();
304 const BBox &tbb = gc.getBoundingBBox(tgid);
305 const SlantBox &tsb = gc.getBoundingSlantBox(tgid);
306 float seq_above_wt = cslot->seqAboveWt();
307 float seq_below_wt = cslot->seqBelowWt();
308 float seq_valign_wt = cslot->seqValignWt();
309 float lmargin;
310 // if isAfter, invert orderFlags for diagonal orders.
311 if (isAfter)
312 {
313 // invert appropriate bits
314 orderFlags ^= (sameClass ? 0x3F : 0x3);
315 // consider 2 bits at a time, non overlapping. If both bits set, clear them
316 orderFlags = orderFlags ^ ((((orderFlags >> 1) & orderFlags) & 0x15) * 3);
317 }
318
319 #if !defined GRAPHITE2_NTRACING
320 if (dbgout)
321 dbgout->setenv(0, slot);
322 #endif
323
324 // Process main bounding octabox.
325 for (int i = 0; i < 4; ++i)
326 {
327 switch (i) {
328 case 0 : // x direction
329 vmin = max(max(bb.xi - tbb.xa + sx, sb.di - tsb.da + ty + sd), sb.si - tsb.sa - ty + ss);
330 vmax = min(min(bb.xa - tbb.xi + sx, sb.da - tsb.di + ty + sd), sb.sa - tsb.si - ty + ss);
331 otmin = tbb.yi + ty;
332 otmax = tbb.ya + ty;
333 omin = bb.yi + sy;
334 omax = bb.ya + sy;
335 torg = _currOffset.x;
336 cmin = _limit.bl.x + torg;
337 cmax = _limit.tr.x - tbb.xi + tbb.xa + torg;
338 lmargin = _margin;
339 break;
340 case 1 : // y direction
341 vmin = max(max(bb.yi - tbb.ya + sy, tsb.di - sb.da + tx - sd), sb.si - tsb.sa - tx + ss);
342 vmax = min(min(bb.ya - tbb.yi + sy, tsb.da - sb.di + tx - sd), sb.sa - tsb.si - tx + ss);
343 otmin = tbb.xi + tx;
344 otmax = tbb.xa + tx;
345 omin = bb.xi + sx;
346 omax = bb.xa + sx;
347 torg = _currOffset.y;
348 cmin = _limit.bl.y + torg;
349 cmax = _limit.tr.y - tbb.yi + tbb.ya + torg;
350 lmargin = _margin;
351 break;
352 case 2 : // sum - moving along the positively-sloped vector, so the boundaries are the
353 // negatively-sloped boundaries.
354 vmin = max(max(sb.si - tsb.sa + ss, 2 * (bb.yi - tbb.ya + sy) + td), 2 * (bb.xi - tbb.xa + sx) - td);
355 vmax = min(min(sb.sa - tsb.si + ss, 2 * (bb.ya - tbb.yi + sy) + td), 2 * (bb.xa - tbb.xi + sx) - td);
356 otmin = tsb.di + td;
357 otmax = tsb.da + td;
358 omin = sb.di + sd;
359 omax = sb.da + sd;
360 torg = _currOffset.x + _currOffset.y;
361 cmin = _limit.bl.x + _limit.bl.y + torg;
362 cmax = _limit.tr.x + _limit.tr.y - tsb.si + tsb.sa + torg;
363 lmargin = _margin / ISQRT2;
364 break;
365 case 3 : // diff - moving along the negatively-sloped vector, so the boundaries are the
366 // positively-sloped boundaries.
367 vmin = max(max(sb.di - tsb.da + sd, 2 * (bb.xi - tbb.xa + sx) - ts), -2 * (bb.ya - tbb.yi + sy) + ts);
368 vmax = min(min(sb.da - tsb.di + sd, 2 * (bb.xa - tbb.xi + sx) - ts), -2 * (bb.yi - tbb.ya + sy) + ts);
369 otmin = tsb.si + ts;
370 otmax = tsb.sa + ts;
371 omin = sb.si + ss;
372 omax = sb.sa + ss;
373 torg = _currOffset.x - _currOffset.y;
374 cmin = _limit.bl.x - _limit.tr.y + torg;
375 cmax = _limit.tr.x - _limit.bl.y - tsb.di + tsb.da + torg;
376 lmargin = _margin / ISQRT2;
377 break;
378 default :
379 continue;
380 }
381
382 #if !defined GRAPHITE2_NTRACING
383 if (dbgout)
384 dbgout->setenv(1, reinterpret_cast<void *>(-1));
385 #define DBGTAG(x) if (dbgout) dbgout->setenv(1, reinterpret_cast<void *>(-x));
386 #else
387 #define DBGTAG(x)
388 #endif
389
390 if (orderFlags)
391 {
392 Position org(tx, ty);
393 float xminf = _limit.bl.x + _currOffset.x + tbb.xi;
394 float xpinf = _limit.tr.x + _currOffset.x + tbb.xa;
395 float ypinf = _limit.tr.y + _currOffset.y + tbb.ya;
396 float yminf = _limit.bl.y + _currOffset.y + tbb.yi;
397 switch (orderFlags) {
398 case SlotCollision::SEQ_ORDER_RIGHTUP :
399 {
400 float r1Xedge = cslot->seqAboveXoff() + 0.5f * (bb.xi + bb.xa) + sx;
401 float r3Xedge = cslot->seqBelowXlim() + bb.xa + sx + 0.5f * (tbb.xa - tbb.xi);
402 float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy;
403
404 // DBGTAG(1x) means the regions are up and right
405 // region 1
406 DBGTAG(11)
407 addBox_slope(true, Rect(Position(xminf, r2Yedge), Position(r1Xedge, ypinf)),
408 tbb, tsb, org, 0, seq_above_wt, true, i);
409 // region 2
410 DBGTAG(12)
411 removeBox(Rect(Position(xminf, yminf), Position(r3Xedge, r2Yedge)), tbb, tsb, org, i);
412 // region 3, which end is zero is irrelevant since m weight is 0
413 DBGTAG(13)
414 addBox_slope(true, Rect(Position(r3Xedge, yminf), Position(xpinf, r2Yedge - cslot->seqValignHt())),
415 tbb, tsb, org, seq_below_wt, 0, true, i);
416 // region 4
417 DBGTAG(14)
418 addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge), Position(xpinf, r2Yedge + cslot->seqValignHt())),
419 tbb, tsb, org, 0, seq_valign_wt, true, i);
420 // region 5
421 DBGTAG(15)
422 addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge - cslot->seqValignHt()), Position(xpinf, r2Yedge)),
423 tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i);
424 break;
425 }
426 case SlotCollision::SEQ_ORDER_LEFTDOWN :
427 {
428 float r1Xedge = 0.5f * (bb.xi + bb.xa) + cslot->seqAboveXoff() + sx;
429 float r3Xedge = bb.xi - cslot->seqBelowXlim() + sx - 0.5f * (tbb.xa - tbb.xi);
430 float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy;
431 // DBGTAG(2x) means the regions are up and right
432 // region 1
433 DBGTAG(21)
434 addBox_slope(true, Rect(Position(r1Xedge, yminf), Position(xpinf, r2Yedge)),
435 tbb, tsb, org, 0, seq_above_wt, false, i);
436 // region 2
437 DBGTAG(22)
438 removeBox(Rect(Position(r3Xedge, r2Yedge), Position(xpinf, ypinf)), tbb, tsb, org, i);
439 // region 3
440 DBGTAG(23)
441 addBox_slope(true, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()), Position(r3Xedge, ypinf)),
442 tbb, tsb, org, seq_below_wt, 0, false, i);
443 // region 4
444 DBGTAG(24)
445 addBox_slope(false, Rect(Position(xminf, r2Yedge), Position(sx + bb.xa, r2Yedge + cslot->seqValignHt())),
446 tbb, tsb, org, 0, seq_valign_wt, true, i);
447 // region 5
448 DBGTAG(25)
449 addBox_slope(false, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()),
450 Position(sx + bb.xa, r2Yedge)), tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i);
451 break;
452 }
453 case SlotCollision::SEQ_ORDER_NOABOVE : // enforce neighboring glyph being above
454 DBGTAG(31);
455 removeBox(Rect(Position(bb.xi - tbb.xa + sx, sy + bb.ya),
456 Position(bb.xa - tbb.xi + sx, ypinf)), tbb, tsb, org, i);
457 break;
458 case SlotCollision::SEQ_ORDER_NOBELOW : // enforce neighboring glyph being below
459 DBGTAG(32);
460 removeBox(Rect(Position(bb.xi - tbb.xa + sx, yminf),
461 Position(bb.xa - tbb.xi + sx, sy + bb.yi)), tbb, tsb, org, i);
462 break;
463 case SlotCollision::SEQ_ORDER_NOLEFT : // enforce neighboring glyph being to the left
464 DBGTAG(33)
465 removeBox(Rect(Position(xminf, bb.yi - tbb.ya + sy),
466 Position(bb.xi - tbb.xa + sx, bb.ya - tbb.yi + sy)), tbb, tsb, org, i);
467 break;
468 case SlotCollision::SEQ_ORDER_NORIGHT : // enforce neighboring glyph being to the right
469 DBGTAG(34)
470 removeBox(Rect(Position(bb.xa - tbb.xi + sx, bb.yi - tbb.ya + sy),
471 Position(xpinf, bb.ya - tbb.yi + sy)), tbb, tsb, org, i);
472 break;
473 default :
474 break;
475 }
476 }
477
478 if (vmax < cmin - lmargin || vmin > cmax + lmargin || omax < otmin - lmargin || omin > otmax + lmargin)
479 continue;
480
481 // Process sub-boxes that are defined for this glyph.
482 // We only need to do this if there was in fact a collision with the main octabox.
483 uint8 numsub = gc.numSubBounds(gid);
484 if (numsub > 0)
485 {
486 bool anyhits = false;
487 for (int j = 0; j < numsub; ++j)
488 {
489 const BBox &sbb = gc.getSubBoundingBBox(gid, j);
490 const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, j);
491 switch (i) {
492 case 0 : // x
493 vmin = max(max(sbb.xi-tbb.xa+sx, ssb.di-tsb.da+sd+ty), ssb.si-tsb.sa+ss-ty);
494 vmax = min(min(sbb.xa-tbb.xi+sx, ssb.da-tsb.di+sd+ty), ssb.sa-tsb.si+ss-ty);
495 omin = sbb.yi + sy;
496 omax = sbb.ya + sy;
497 break;
498 case 1 : // y
499 vmin = max(max(sbb.yi-tbb.ya+sy, tsb.di-ssb.da-sd+tx), ssb.si-tsb.sa+ss-tx);
500 vmax = min(min(sbb.ya-tbb.yi+sy, tsb.da-ssb.di-sd+tx), ssb.sa-tsb.si+ss-tx);
501 omin = sbb.xi + sx;
502 omax = sbb.xa + sx;
503 break;
504 case 2 : // sum
505 vmin = max(max(ssb.si-tsb.sa+ss, 2*(sbb.yi-tbb.ya+sy)+td), 2*(sbb.xi-tbb.xa+sx)-td);
506 vmax = min(min(ssb.sa-tsb.si+ss, 2*(sbb.ya-tbb.yi+sy)+td), 2*(sbb.xa-tbb.xi+sx)-td);
507 omin = ssb.di + sd;
508 omax = ssb.da + sd;
509 break;
510 case 3 : // diff
511 vmin = max(max(ssb.di-tsb.da+sd, 2*(sbb.xi-tbb.xa+sx)-ts), -2*(sbb.ya-tbb.yi+sy)+ts);
512 vmax = min(min(ssb.da-tsb.di+sd, 2*(sbb.xa-tbb.xi+sx)-ts), -2*(sbb.yi-tbb.ya+sy)+ts);
513 omin = ssb.si + ss;
514 omax = ssb.sa + ss;
515 break;
516 }
517 if (vmax < cmin - lmargin || vmin > cmax + lmargin || omax < otmin - lmargin || omin > otmax + lmargin)
518 continue;
519
520 #if !defined GRAPHITE2_NTRACING
521 if (dbgout)
522 dbgout->setenv(1, reinterpret_cast<void *>(j));
523 #endif
524 if (omin > otmax)
525 _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
526 sqr(lmargin - omin + otmax) * _marginWt, false);
527 else if (omax < otmin)
528 _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
529 sqr(lmargin - otmin + omax) * _marginWt, false);
530 else
531 _ranges[i].exclude_with_margins(vmin, vmax, i);
532 anyhits = true;
533 }
534 if (anyhits)
535 isCol = true;
536 }
537 else // no sub-boxes
538 {
539 #if !defined GRAPHITE2_NTRACING
540 if (dbgout)
541 dbgout->setenv(1, reinterpret_cast<void *>(-1));
542 #endif
543 isCol = true;
544 if (omin > otmax)
545 _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
546 sqr(lmargin - omin + otmax) * _marginWt, false);
547 else if (omax < otmin)
548 _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
549 sqr(lmargin - otmin + omax) * _marginWt, false);
550 else
551 _ranges[i].exclude_with_margins(vmin, vmax, i);
552
553 }
554 }
555 }
556 bool res = true;
557 if (cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion)
558 {
559 // Set up the bogus slot representing the exclusion glyph.
560 Slot *exclSlot = seg->newSlot();
561 if (!exclSlot)
562 return res;
563 exclSlot->setGlyph(seg, cslot->exclGlyph());
564 Position exclOrigin(slot->origin() + cslot->exclOffset());
565 exclSlot->origin(exclOrigin);
566 SlotCollision exclInfo(seg, exclSlot);
567 res &= mergeSlot(seg, exclSlot, &exclInfo, currShift, isAfter, sameCluster, isCol, true, dbgout );
568 seg->freeSlot(exclSlot);
569 }
570 hasCol |= isCol;
571 return res;
572
573 } // end of ShiftCollider::mergeSlot
574
575
576 // Figure out where to move the target glyph to, and return the amount to shift by.
resolve(GR_MAYBE_UNUSED Segment * seg,bool & isCol,GR_MAYBE_UNUSED json * const dbgout)577 Position ShiftCollider::resolve(GR_MAYBE_UNUSED Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout)
578 {
579 float tbase;
580 float totalCost = (float)(std::numeric_limits<float>::max() / 2);
581 Position resultPos = Position(0, 0);
582 #if !defined GRAPHITE2_NTRACING
583 int bestAxis = -1;
584 if (dbgout)
585 {
586 outputJsonDbgStartSlot(dbgout, seg);
587 *dbgout << "vectors" << json::array;
588 }
589 #endif
590 isCol = true;
591 for (int i = 0; i < 4; ++i)
592 {
593 float bestCost = -1;
594 float bestPos;
595 // Calculate the margin depending on whether we are moving diagonally or not:
596 switch (i) {
597 case 0 : // x direction
598 tbase = _currOffset.x;
599 break;
600 case 1 : // y direction
601 tbase = _currOffset.y;
602 break;
603 case 2 : // sum (negatively-sloped diagonals)
604 tbase = _currOffset.x + _currOffset.y;
605 break;
606 case 3 : // diff (positively-sloped diagonals)
607 tbase = _currOffset.x - _currOffset.y;
608 break;
609 }
610 Position testp;
611 bestPos = _ranges[i].closest(0, bestCost) - tbase; // Get the best relative position
612 #if !defined GRAPHITE2_NTRACING
613 if (dbgout)
614 outputJsonDbgOneVector(dbgout, seg, i, tbase, bestCost, bestPos) ;
615 #endif
616 if (bestCost >= 0.0f)
617 {
618 isCol = false;
619 switch (i) {
620 case 0 : testp = Position(bestPos, _currShift.y); break;
621 case 1 : testp = Position(_currShift.x, bestPos); break;
622 case 2 : testp = Position(0.5f * (_currShift.x - _currShift.y + bestPos), 0.5f * (_currShift.y - _currShift.x + bestPos)); break;
623 case 3 : testp = Position(0.5f * (_currShift.x + _currShift.y + bestPos), 0.5f * (_currShift.x + _currShift.y - bestPos)); break;
624 }
625 if (bestCost < totalCost - 0.01f)
626 {
627 totalCost = bestCost;
628 resultPos = testp;
629 #if !defined GRAPHITE2_NTRACING
630 bestAxis = i;
631 #endif
632 }
633 }
634 } // end of loop over 4 directions
635
636 #if !defined GRAPHITE2_NTRACING
637 if (dbgout)
638 outputJsonDbgEndSlot(dbgout, resultPos, bestAxis, isCol);
639 #endif
640
641 return resultPos;
642
643 } // end of ShiftCollider::resolve
644
645
646 #if !defined GRAPHITE2_NTRACING
647
outputJsonDbg(json * const dbgout,Segment * seg,int axis)648 void ShiftCollider::outputJsonDbg(json * const dbgout, Segment *seg, int axis)
649 {
650 int axisMax = axis;
651 if (axis < 0) // output all axes
652 {
653 *dbgout << "gid" << _target->gid()
654 << "limit" << _limit
655 << "target" << json::object
656 << "origin" << _target->origin()
657 << "margin" << _margin
658 << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
659 << "slantbox" << seg->getFace()->glyphs().slant(_target->gid())
660 << json::close; // target object
661 *dbgout << "ranges" << json::array;
662 axis = 0;
663 axisMax = 3;
664 }
665 for (int iAxis = axis; iAxis <= axisMax; ++iAxis)
666 {
667 *dbgout << json::flat << json::array << _ranges[iAxis].position();
668 for (Zones::const_iterator s = _ranges[iAxis].begin(), e = _ranges[iAxis].end(); s != e; ++s)
669 *dbgout << json::flat << json::array
670 << Position(s->x, s->xm) << s->sm << s->smx << s->c
671 << json::close;
672 *dbgout << json::close;
673 }
674 if (axis < axisMax) // looped through the _ranges array for all axes
675 *dbgout << json::close; // ranges array
676 }
677
outputJsonDbgStartSlot(json * const dbgout,Segment * seg)678 void ShiftCollider::outputJsonDbgStartSlot(json * const dbgout, Segment *seg)
679 {
680 *dbgout << json::object // slot - not closed till the end of the caller method
681 << "slot" << objectid(dslot(seg, _target))
682 << "gid" << _target->gid()
683 << "limit" << _limit
684 << "target" << json::object
685 << "origin" << _origin
686 << "currShift" << _currShift
687 << "currOffset" << seg->collisionInfo(_target)->offset()
688 << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
689 << "slantBox" << seg->getFace()->glyphs().slant(_target->gid())
690 << "fix" << "shift";
691 *dbgout << json::close; // target object
692 }
693
outputJsonDbgEndSlot(GR_MAYBE_UNUSED json * const dbgout,Position resultPos,int bestAxis,bool isCol)694 void ShiftCollider::outputJsonDbgEndSlot(GR_MAYBE_UNUSED json * const dbgout,
695 Position resultPos, int bestAxis, bool isCol)
696 {
697 *dbgout << json::close // vectors array
698 << "result" << resultPos
699 //<< "scraping" << _scraping[bestAxis]
700 << "bestAxis" << bestAxis
701 << "stillBad" << isCol
702 << json::close; // slot object
703 }
704
outputJsonDbgOneVector(json * const dbgout,Segment * seg,int axis,float tleft,float bestCost,float bestVal)705 void ShiftCollider::outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis,
706 float tleft, float bestCost, float bestVal)
707 {
708 const char * label;
709 switch (axis)
710 {
711 case 0: label = "x"; break;
712 case 1: label = "y"; break;
713 case 2: label = "sum (NE-SW)"; break;
714 case 3: label = "diff (NW-SE)"; break;
715 default: label = "???"; break;
716 }
717
718 *dbgout << json::object // vector
719 << "direction" << label
720 << "targetMin" << tleft;
721
722 outputJsonDbgRemovals(dbgout, axis, seg);
723
724 *dbgout << "ranges";
725 outputJsonDbg(dbgout, seg, axis);
726
727 *dbgout << "bestCost" << bestCost
728 << "bestVal" << bestVal + tleft
729 << json::close; // vectors object
730 }
731
outputJsonDbgRemovals(json * const dbgout,int axis,Segment * seg)732 void ShiftCollider::outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg)
733 {
734 *dbgout << "removals" << json::array;
735 _ranges[axis].jsonDbgOut(seg);
736 *dbgout << json::close; // removals array
737 }
738
739 #endif // !defined GRAPHITE2_NTRACING
740
741
742 //// KERN-COLLIDER ////
743
744 inline
localmax(float al,float au,float bl,float bu,float x)745 static float localmax (float al, float au, float bl, float bu, float x)
746 {
747 if (al < bl)
748 { if (au < bu) return au < x ? au : x; }
749 else if (au > bu) return bl < x ? bl : x;
750 return x;
751 }
752
753 inline
localmin(float al,float au,float bl,float bu,float x)754 static float localmin(float al, float au, float bl, float bu, float x)
755 {
756 if (bl > al)
757 { if (bu > au) return bl > x ? bl : x; }
758 else if (au > bu) return al > x ? al : x;
759 return x;
760 }
761
762 // Return the given edge of the glyph at height y, taking any slant box into account.
get_edge(Segment * seg,const Slot * s,const Position & shift,float y,float width,float margin,bool isRight)763 static float get_edge(Segment *seg, const Slot *s, const Position &shift, float y, float width, float margin, bool isRight)
764 {
765 const GlyphCache &gc = seg->getFace()->glyphs();
766 unsigned short gid = s->gid();
767 float sx = s->origin().x + shift.x;
768 float sy = s->origin().y + shift.y;
769 uint8 numsub = gc.numSubBounds(gid);
770 float res = isRight ? (float)-1e38 : (float)1e38;
771
772 if (numsub > 0)
773 {
774 for (int i = 0; i < numsub; ++i)
775 {
776 const BBox &sbb = gc.getSubBoundingBBox(gid, i);
777 const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, i);
778 if (sy + sbb.yi - margin > y + width / 2 || sy + sbb.ya + margin < y - width / 2)
779 continue;
780 if (isRight)
781 {
782 float x = sx + sbb.xa + margin;
783 if (x > res)
784 {
785 float td = sx - sy + ssb.da + margin + y;
786 float ts = sx + sy + ssb.sa + margin - y;
787 x = localmax(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x);
788 if (x > res)
789 res = x;
790 }
791 }
792 else
793 {
794 float x = sx + sbb.xi - margin;
795 if (x < res)
796 {
797 float td = sx - sy + ssb.di - margin + y;
798 float ts = sx + sy + ssb.si - margin - y;
799 x = localmin(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x);
800 if (x < res)
801 res = x;
802 }
803 }
804 }
805 }
806 else
807 {
808 const BBox &bb = gc.getBoundingBBox(gid);
809 const SlantBox &sb = gc.getBoundingSlantBox(gid);
810 if (sy + bb.yi - margin > y + width / 2 || sy + bb.ya + margin < y - width / 2)
811 return res;
812 float td = sx - sy + y;
813 float ts = sx + sy - y;
814 if (isRight)
815 res = localmax(td + sb.da - width / 2, td + sb.da + width / 2, ts + sb.sa - width / 2, ts + sb.sa + width / 2, sx + bb.xa) + margin;
816 else
817 res = localmin(td + sb.di - width / 2, td + sb.di + width / 2, ts + sb.si - width / 2, ts + sb.si + width / 2, sx + bb.xi) - margin;
818 }
819 return res;
820 }
821
822
initSlot(Segment * seg,Slot * aSlot,const Rect & limit,float margin,const Position & currShift,const Position & offsetPrev,int dir,float ymin,float ymax,GR_MAYBE_UNUSED json * const dbgout)823 bool KernCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin,
824 const Position &currShift, const Position &offsetPrev, int dir,
825 float ymin, float ymax, GR_MAYBE_UNUSED json * const dbgout)
826 {
827 const GlyphCache &gc = seg->getFace()->glyphs();
828 const Slot *base = aSlot;
829 // const Slot *last = aSlot;
830 const Slot *s;
831 int numSlices;
832 while (base->attachedTo())
833 base = base->attachedTo();
834 if (margin < 10) margin = 10;
835
836 _limit = limit;
837 _offsetPrev = offsetPrev; // kern from a previous pass
838
839 // Calculate the height of the glyph and how many horizontal slices to use.
840 if (_maxy >= 1e37f)
841 {
842 _sliceWidth = margin / 1.5f;
843 _maxy = ymax + margin;
844 _miny = ymin - margin;
845 numSlices = int((_maxy - _miny + 2) / (_sliceWidth / 1.5f) + 1.f); // +2 helps with rounding errors
846 _edges.clear();
847 _edges.insert(_edges.begin(), numSlices, (dir & 1) ? 1e38f : -1e38f);
848 _xbound = (dir & 1) ? (float)1e38f : (float)-1e38f;
849 }
850 else if (_maxy != ymax || _miny != ymin)
851 {
852 if (_miny != ymin)
853 {
854 numSlices = int((ymin - margin - _miny) / _sliceWidth - 1);
855 _miny += numSlices * _sliceWidth;
856 if (numSlices < 0)
857 _edges.insert(_edges.begin(), -numSlices, (dir & 1) ? 1e38f : -1e38f);
858 else if ((unsigned)numSlices < _edges.size()) // this shouldn't fire since we always grow the range
859 {
860 Vector<float>::iterator e = _edges.begin();
861 while (numSlices--)
862 ++e;
863 _edges.erase(_edges.begin(), e);
864 }
865 }
866 if (_maxy != ymax)
867 {
868 numSlices = int((ymax + margin - _miny) / _sliceWidth + 1);
869 _maxy = numSlices * _sliceWidth + _miny;
870 if (numSlices > (int)_edges.size())
871 _edges.insert(_edges.end(), numSlices - _edges.size(), (dir & 1) ? 1e38f : -1e38f);
872 else if (numSlices < (int)_edges.size()) // this shouldn't fire since we always grow the range
873 {
874 while ((int)_edges.size() > numSlices)
875 _edges.pop_back();
876 }
877 }
878 goto done;
879 }
880 numSlices = int(_edges.size());
881
882 #if !defined GRAPHITE2_NTRACING
883 // Debugging
884 _seg = seg;
885 _slotNear.clear();
886 _slotNear.insert(_slotNear.begin(), numSlices, NULL);
887 _nearEdges.clear();
888 _nearEdges.insert(_nearEdges.begin(), numSlices, (dir & 1) ? -1e38f : +1e38f);
889 #endif
890
891 // Determine the trailing edge of each slice (ie, left edge for a RTL glyph).
892 for (s = base; s; s = s->nextInCluster(s))
893 {
894 SlotCollision *c = seg->collisionInfo(s);
895 if (!gc.check(s->gid()))
896 return false;
897 const BBox &bs = gc.getBoundingBBox(s->gid());
898 float x = s->origin().x + c->shift().x + ((dir & 1) ? bs.xi : bs.xa);
899 // Loop over slices.
900 // Note smin might not be zero if glyph s is not at the bottom of the cluster; similarly for smax.
901 float toffset = c->shift().y - _miny + 1 + s->origin().y;
902 int smin = max(0, int((bs.yi + toffset) / _sliceWidth));
903 int smax = min(numSlices - 1, int((bs.ya + toffset) / _sliceWidth + 1));
904 for (int i = smin; i <= smax; ++i)
905 {
906 float t;
907 float y = _miny - 1 + (i + .5f) * _sliceWidth; // vertical center of slice
908 if ((dir & 1) && x < _edges[i])
909 {
910 t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, false);
911 if (t < _edges[i])
912 {
913 _edges[i] = t;
914 if (t < _xbound)
915 _xbound = t;
916 }
917 }
918 else if (!(dir & 1) && x > _edges[i])
919 {
920 t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, true);
921 if (t > _edges[i])
922 {
923 _edges[i] = t;
924 if (t > _xbound)
925 _xbound = t;
926 }
927 }
928 }
929 }
930 done:
931 _mingap = (float)1e37; // less than 1e38 s.t. 1e38-_mingap is really big
932 _target = aSlot;
933 _margin = margin;
934 _currShift = currShift;
935 return true;
936 } // end of KernCollider::initSlot
937
938
939 // Determine how much the target slot needs to kern away from the given slot.
940 // In other words, merge information from given slot's position with what the target slot knows
941 // about how it can kern.
942 // Return false if we know there is no collision, true if we think there might be one.
mergeSlot(Segment * seg,Slot * slot,const Position & currShift,float currSpace,int dir,GR_MAYBE_UNUSED json * const dbgout)943 bool KernCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, GR_MAYBE_UNUSED json * const dbgout)
944 {
945 int rtl = (dir & 1) * 2 - 1;
946 if (!seg->getFace()->glyphs().check(slot->gid()))
947 return false;
948 const Rect &bb = seg->theGlyphBBoxTemporary(slot->gid());
949 const float sx = slot->origin().x + currShift.x;
950 float x = (sx + (rtl > 0 ? bb.tr.x : bb.bl.x)) * rtl;
951 // this isn't going to reduce _mingap so skip
952 if (_hit && x < rtl * (_xbound - _mingap - currSpace))
953 return false;
954
955 const float sy = slot->origin().y + currShift.y;
956 int smin = max(1, int((bb.bl.y + (1 - _miny + sy)) / _sliceWidth + 1)) - 1;
957 int smax = min((int)_edges.size() - 2, int((bb.tr.y + (1 - _miny + sy)) / _sliceWidth + 1)) + 1;
958 if (smin > smax)
959 return false;
960 bool collides = false;
961 bool nooverlap = true;
962
963 for (int i = smin; i <= smax; ++i)
964 {
965 float here = _edges[i] * rtl;
966 if (here > (float)9e37)
967 continue;
968 if (!_hit || x > here - _mingap - currSpace)
969 {
970 float y = (float)(_miny - 1 + (i + .5f) * _sliceWidth); // vertical center of slice
971 // 2 * currSpace to account for the space that is already separating them and the space we want to add
972 float m = get_edge(seg, slot, currShift, y, _sliceWidth, 0., rtl > 0) * rtl + 2 * currSpace;
973 if (m < (float)-8e37) // only true if the glyph has a gap in it
974 continue;
975 nooverlap = false;
976 float t = here - m;
977 // _mingap is positive to shrink
978 if (t < _mingap || (!_hit && !collides))
979 {
980 _mingap = t;
981 collides = true;
982 }
983 #if !defined GRAPHITE2_NTRACING
984 // Debugging - remember the closest neighboring edge for this slice.
985 if (m > rtl * _nearEdges[i])
986 {
987 _slotNear[i] = slot;
988 _nearEdges[i] = m * rtl;
989 }
990 #endif
991 }
992 else
993 nooverlap = false;
994 }
995 if (nooverlap)
996 _mingap = max(_mingap, _xbound - rtl * (currSpace + _margin + x));
997 if (collides && !nooverlap)
998 _hit = true;
999 return collides | nooverlap; // note that true is not a necessarily reliable value
1000
1001 } // end of KernCollider::mergeSlot
1002
1003
1004 // Return the amount to kern by.
resolve(GR_MAYBE_UNUSED Segment * seg,GR_MAYBE_UNUSED Slot * slot,int dir,GR_MAYBE_UNUSED json * const dbgout)1005 Position KernCollider::resolve(GR_MAYBE_UNUSED Segment *seg, GR_MAYBE_UNUSED Slot *slot,
1006 int dir, GR_MAYBE_UNUSED json * const dbgout)
1007 {
1008 float resultNeeded = (1 - 2 * (dir & 1)) * _mingap;
1009 // float resultNeeded = (1 - 2 * (dir & 1)) * (_mingap - margin);
1010 float result = min(_limit.tr.x - _offsetPrev.x, max(resultNeeded, _limit.bl.x - _offsetPrev.x));
1011
1012 #if !defined GRAPHITE2_NTRACING
1013 if (dbgout)
1014 {
1015 *dbgout << json::object // slot
1016 << "slot" << objectid(dslot(seg, _target))
1017 << "gid" << _target->gid()
1018 << "limit" << _limit
1019 << "miny" << _miny
1020 << "maxy" << _maxy
1021 << "slicewidth" << _sliceWidth
1022 << "target" << json::object
1023 << "origin" << _target->origin()
1024 //<< "currShift" << _currShift
1025 << "offsetPrev" << _offsetPrev
1026 << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
1027 << "slantBox" << seg->getFace()->glyphs().slant(_target->gid())
1028 << "fix" << "kern"
1029 << json::close; // target object
1030
1031 *dbgout << "slices" << json::array;
1032 for (int is = 0; is < (int)_edges.size(); is++)
1033 {
1034 *dbgout << json::flat << json::object
1035 << "i" << is
1036 << "targetEdge" << _edges[is]
1037 << "neighbor" << objectid(dslot(seg, _slotNear[is]))
1038 << "nearEdge" << _nearEdges[is]
1039 << json::close;
1040 }
1041 *dbgout << json::close; // slices array
1042
1043 *dbgout
1044 << "xbound" << _xbound
1045 << "minGap" << _mingap
1046 << "needed" << resultNeeded
1047 << "result" << result
1048 << "stillBad" << (result != resultNeeded)
1049 << json::close; // slot object
1050 }
1051 #endif
1052
1053 return Position(result, 0.);
1054
1055 } // end of KernCollider::resolve
1056
shift(const Position & mv,int dir)1057 void KernCollider::shift(const Position &mv, int dir)
1058 {
1059 for (Vector<float>::iterator e = _edges.begin(); e != _edges.end(); ++e)
1060 *e += mv.x;
1061 _xbound += (1 - 2 * (dir & 1)) * mv.x;
1062 }
1063
1064 //// SLOT-COLLISION ////
1065
1066 // Initialize the collision attributes for the given slot.
SlotCollision(Segment * seg,Slot * slot)1067 SlotCollision::SlotCollision(Segment *seg, Slot *slot)
1068 {
1069 initFromSlot(seg, slot);
1070 }
1071
initFromSlot(Segment * seg,Slot * slot)1072 void SlotCollision::initFromSlot(Segment *seg, Slot *slot)
1073 {
1074 // Initialize slot attributes from glyph attributes.
1075 // The order here must match the order in the grcompiler code,
1076 // GrcSymbolTable::AssignInternalGlyphAttrIDs.
1077 uint16 gid = slot->gid();
1078 uint16 aCol = seg->silf()->aCollision(); // flags attr ID
1079 const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(gid);
1080 if (!glyphFace)
1081 return;
1082 const sparse &p = glyphFace->attrs();
1083 _flags = p[aCol];
1084 _limit = Rect(Position(int16(p[aCol+1]), int16(p[aCol+2])),
1085 Position(int16(p[aCol+3]), int16(p[aCol+4])));
1086 _margin = p[aCol+5];
1087 _marginWt = p[aCol+6];
1088
1089 _seqClass = p[aCol+7];
1090 _seqProxClass = p[aCol+8];
1091 _seqOrder = p[aCol+9];
1092 _seqAboveXoff = p[aCol+10];
1093 _seqAboveWt = p[aCol+11];
1094 _seqBelowXlim = p[aCol+12];
1095 _seqBelowWt = p[aCol+13];
1096 _seqValignHt = p[aCol+14];
1097 _seqValignWt = p[aCol+15];
1098
1099 // These attributes do not have corresponding glyph attribute:
1100 _exclGlyph = 0;
1101 _exclOffset = Position(0, 0);
1102 }
1103
getKern(int dir) const1104 float SlotCollision::getKern(int dir) const
1105 {
1106 if ((_flags & SlotCollision::COLL_KERN) != 0)
1107 return float(_shift.x * ((dir & 1) ? -1 : 1));
1108 else
1109 return 0;
1110 }
1111
ignore() const1112 bool SlotCollision::ignore() const
1113 {
1114 return ((flags() & SlotCollision::COLL_IGNORE) || (flags() & SlotCollision::COLL_ISSPACE));
1115 }
1116