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