Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/graphite/src/Collider.cpp
Line
Count
Source (jump to first uncovered line)
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
0
#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.
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
0
{
53
0
    int i;
54
0
    float mx, mn;
55
0
    float a, shift;
56
0
    const GlyphCache &gc = seg->getFace()->glyphs();
57
0
    unsigned short gid = aSlot->gid();
58
0
    if (!gc.check(gid))
59
0
        return false;
60
0
    const BBox &bb = gc.getBoundingBBox(gid);
61
0
    const SlantBox &sb = gc.getBoundingSlantBox(gid);
62
    //float sx = aSlot->origin().x + currShift.x;
63
    //float sy = aSlot->origin().y + currShift.y;
64
0
    if (currOffset.x != 0.f || currOffset.y != 0.f)
65
0
        _limit = Rect(limit.bl - currOffset, limit.tr - currOffset);
66
0
    else
67
0
        _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
0
    for (i = 0; i < 4; ++i)
71
0
    {
72
0
        switch (i) {
73
0
            case 0 :  // x direction
74
0
                mn = _limit.bl.x + currOffset.x;
75
0
                mx = _limit.tr.x + currOffset.x;
76
0
                _len[i] = bb.xa - bb.xi;
77
0
                a = currOffset.y + currShift.y;
78
0
                _ranges[i].initialise<XY>(mn, mx, margin, marginWeight, a);
79
0
                break;
80
0
            case 1 :  // y direction
81
0
                mn = _limit.bl.y + currOffset.y;
82
0
                mx = _limit.tr.y + currOffset.y;
83
0
                _len[i] = bb.ya - bb.yi;
84
0
                a = currOffset.x + currShift.x;
85
0
                _ranges[i].initialise<XY>(mn, mx, margin, marginWeight, a);
86
0
                break;
87
0
            case 2 :  // sum (negatively sloped diagonal boundaries)
88
                // pick closest x,y limit boundaries in s direction
89
0
                shift = currOffset.x + currOffset.y + currShift.x + currShift.y;
90
0
                mn = -2 * min(currShift.x - _limit.bl.x, currShift.y - _limit.bl.y) + shift;
91
0
                mx = 2 * min(_limit.tr.x - currShift.x, _limit.tr.y - currShift.y) + shift;
92
0
                _len[i] = sb.sa - sb.si;
93
0
                a = currOffset.x - currOffset.y + currShift.x - currShift.y;
94
0
                _ranges[i].initialise<SD>(mn, mx, margin / ISQRT2, marginWeight, a);
95
0
                break;
96
0
            case 3 :  // diff (positively sloped diagonal boundaries)
97
                // pick closest x,y limit boundaries in d direction
98
0
                shift = currOffset.x - currOffset.y + currShift.x - currShift.y;
99
0
                mn = -2 * min(currShift.x - _limit.bl.x, _limit.tr.y - currShift.y) + shift;
100
0
                mx = 2 * min(_limit.tr.x - currShift.x, currShift.y - _limit.bl.y) + shift;
101
0
                _len[i] = sb.da - sb.di;
102
0
                a = currOffset.x + currOffset.y + currShift.x + currShift.y;
103
0
                _ranges[i].initialise<SD>(mn, mx, margin / ISQRT2, marginWeight, a);
104
0
                break;
105
0
        }
106
0
    }
107
108
0
  _target = aSlot;
109
0
    if ((dir & 1) == 0)
110
0
    {
111
        // For LTR, switch and negate x limits.
112
0
        _limit.bl.x = -1 * limit.tr.x;
113
        //_limit.tr.x = -1 * limit.bl.x;
114
0
    }
115
0
    _currOffset = currOffset;
116
0
    _currShift = currShift;
117
0
    _origin = aSlot->origin() - currOffset;     // the original anchor position of the glyph
118
119
0
  _margin = margin;
120
0
  _marginWt = marginWeight;
121
122
0
    SlotCollision *c = seg->collisionInfo(aSlot);
123
0
    _seqClass = c->seqClass();
124
0
  _seqProxClass = c->seqProxClass();
125
0
    _seqOrder = c->seqOrder();
126
0
    return true;
127
0
}
128
129
template <class O>
130
float sdm(float vi, float va, float mx, float my, O op)
131
0
{
132
0
    float res = 2 * mx - vi;
133
0
    if (op(res, vi + 2 * my))
134
0
    {
135
0
        res = va + 2 * my;
136
0
        if (op(res, 2 * mx - va))
137
0
            res = mx + my;
138
0
    }
139
0
    return res;
140
0
}
Unexecuted instantiation: float sdm<std::__1::greater<float> >(float, float, float, float, std::__1::greater<float>)
Unexecuted instantiation: float sdm<std::__1::less<float> >(float, float, float, float, std::__1::less<float>)
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
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
0
{
145
0
    float a, c;
146
0
    switch (axis) {
147
0
        case 0 :
148
0
             if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0)
149
0
            {
150
0
                a = org.y + 0.5f * (bb.yi + bb.ya);
151
0
                c = 0.5f * (bb.xi + bb.xa);
152
0
                if (isx)
153
0
                    _ranges[axis].weighted<XY>(box.bl.x - c, box.tr.x - c, weight, a, m,
154
0
                                                (minright ? box.tr.x : box.bl.x) - c, a, 0, false);
155
0
                else
156
0
                    _ranges[axis].weighted<XY>(box.bl.x - c, box.tr.x - c, weight, a, 0, 0, org.y,
157
0
                                                m * (a * a + sqr((minright ? box.tr.y : box.bl.y) - 0.5f * (bb.yi + bb.ya))), false);
158
0
            }
159
0
            break;
160
0
        case 1 :
161
0
            if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0)
162
0
            {
163
0
                a = org.x + 0.5f * (bb.xi + bb.xa);
164
0
                c = 0.5f * (bb.yi + bb.ya);
165
0
                if (isx)
166
0
                    _ranges[axis].weighted<XY>(box.bl.y - c, box.tr.y - c, weight, a, 0, 0, org.x,
167
0
                                                m * (a * a + sqr((minright ? box.tr.x : box.bl.x) - 0.5f * (bb.xi + bb.xa))), false);
168
0
                else
169
0
                    _ranges[axis].weighted<XY>(box.bl.y - c, box.tr.y - c, weight, a, m,
170
0
                                                (minright ? box.tr.y : box.bl.y) - c, a, 0, false);
171
0
            }
172
0
            break;
173
0
        case 2 :
174
0
            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
0
            {
176
0
                float d = org.x - org.y + 0.5f * (sb.di + sb.da);
177
0
                c = 0.5f * (sb.si + sb.sa);
178
0
                float smax = min(2 * box.tr.x - d, 2 * box.tr.y + d);
179
0
                float smin = max(2 * box.bl.x - d, 2 * box.bl.y + d);
180
0
                if (smin > smax) return;
181
0
                float si;
182
0
                a = d;
183
0
                if (isx)
184
0
                    si = 2 * (minright ? box.tr.x : box.bl.x) - a;
185
0
                else
186
0
                    si = 2 * (minright ? box.tr.y : box.bl.y) + a;
187
0
                _ranges[axis].weighted<SD>(smin - c, smax - c, weight / 2, a, m / 2, si, 0, 0, isx);
188
0
            }
189
0
            break;
190
0
        case 3 :
191
0
            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
0
            {
193
0
                float s = org.x + org.y + 0.5f * (sb.si + sb.sa);
194
0
                c = 0.5f * (sb.di + sb.da);
195
0
                float dmax = min(2 * box.tr.x - s, s - 2 * box.bl.y);
196
0
                float dmin = max(2 * box.bl.x - s, s - 2 * box.tr.y);
197
0
                if (dmin > dmax) return;
198
0
                float di;
199
0
                a = s;
200
0
                if (isx)
201
0
                    di = 2 * (minright ? box.tr.x : box.bl.x) - a;
202
0
                else
203
0
                    di = 2 * (minright ? box.tr.y : box.bl.y) + a;
204
0
                _ranges[axis].weighted<SD>(dmin - c, dmax - c, weight / 2, a, m / 2, di, 0, 0, !isx);
205
0
            }
206
0
            break;
207
0
        default :
208
0
            break;
209
0
    }
210
0
    return;
211
0
}
212
213
// Mark an area with an absolute cost, making it completely inaccessible.
214
inline void ShiftCollider::removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int axis)
215
0
{
216
0
    float c;
217
0
    switch (axis) {
218
0
        case 0 :
219
0
            if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0)
220
0
            {
221
0
                c = 0.5f * (bb.xi + bb.xa);
222
0
                _ranges[axis].exclude(box.bl.x - c, box.tr.x - c);
223
0
            }
224
0
            break;
225
0
        case 1 :
226
0
            if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0)
227
0
            {
228
0
                c = 0.5f * (bb.yi + bb.ya);
229
0
                _ranges[axis].exclude(box.bl.y - c, box.tr.y - c);
230
0
            }
231
0
            break;
232
0
        case 2 :
233
0
            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
0
                && box.width() > 0 && box.height() > 0)
235
0
            {
236
0
                float di = org.x - org.y + sb.di;
237
0
                float da = org.x - org.y + sb.da;
238
0
                float smax = sdm(di, da, box.tr.x, box.tr.y, std::greater<float>());
239
0
                float smin = sdm(da, di, box.bl.x, box.bl.y, std::less<float>());
240
0
                c = 0.5f * (sb.si + sb.sa);
241
0
                _ranges[axis].exclude(smin - c, smax - c);
242
0
            }
243
0
            break;
244
0
        case 3 :
245
0
            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
0
                && box.width() > 0 && box.height() > 0)
247
0
            {
248
0
                float si = org.x + org.y + sb.si;
249
0
                float sa = org.x + org.y + sb.sa;
250
0
                float dmax = sdm(si, sa, box.tr.x, -box.bl.y, std::greater<float>());
251
0
                float dmin = sdm(sa, si, box.bl.x, -box.tr.y, std::less<float>());
252
0
                c = 0.5f * (sb.di + sb.da);
253
0
                _ranges[axis].exclude(dmin - c, dmax - c);
254
0
            }
255
0
            break;
256
0
        default :
257
0
            break;
258
0
    }
259
0
    return;
260
0
}
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.
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
0
{
270
0
    bool isCol = false;
271
0
    const float sx = slot->origin().x - _origin.x + currShift.x;
272
0
    const float sy = slot->origin().y - _origin.y + currShift.y;
273
0
    const float sd = sx - sy;
274
0
    const float ss = sx + sy;
275
0
    float vmin, vmax;
276
0
    float omin, omax, otmin, otmax;
277
0
    float cmin, cmax;   // target limits
278
0
    float torg;
279
0
    const GlyphCache &gc = seg->getFace()->glyphs();
280
0
    const unsigned short gid = slot->gid();
281
0
    if (!gc.check(gid))
282
0
        return false;
283
0
    const BBox &bb = gc.getBoundingBBox(gid);
284
285
    // SlotCollision * cslot = seg->collisionInfo(slot);
286
0
    int orderFlags = 0;
287
0
    bool sameClass = _seqProxClass == 0 && cslot->seqClass() == _seqClass;
288
0
    if (sameCluster && _seqClass
289
0
        && (sameClass || (_seqProxClass != 0 && cslot->seqClass() == _seqProxClass)))
290
    // Force the target glyph to be in the specified direction from the slot we're testing.
291
0
        orderFlags = _seqOrder;
292
293
    // short circuit if only interested in direct collision and we are out of range
294
0
    if (orderFlags || (sx + bb.xa + _margin >= _limit.bl.x && sx + bb.xi - _margin <= _limit.tr.x)
295
0
                    || (sy + bb.ya + _margin >= _limit.bl.y && sy + bb.yi - _margin <= _limit.tr.y))
296
297
0
    {
298
0
        const float tx = _currOffset.x + _currShift.x;
299
0
        const float ty = _currOffset.y + _currShift.y;
300
0
        const float td = tx - ty;
301
0
        const float ts = tx + ty;
302
0
        const SlantBox &sb = gc.getBoundingSlantBox(gid);
303
0
        const unsigned short tgid = _target->gid();
304
0
        const BBox &tbb = gc.getBoundingBBox(tgid);
305
0
        const SlantBox &tsb = gc.getBoundingSlantBox(tgid);
306
0
        float seq_above_wt = cslot->seqAboveWt();
307
0
        float seq_below_wt = cslot->seqBelowWt();
308
0
        float seq_valign_wt = cslot->seqValignWt();
309
0
        float lmargin;
310
        // if isAfter, invert orderFlags for diagonal orders.
311
0
        if (isAfter)
312
0
        {
313
            // invert appropriate bits
314
0
            orderFlags ^= (sameClass ? 0x3F : 0x3);
315
            // consider 2 bits at a time, non overlapping. If both bits set, clear them
316
0
            orderFlags = orderFlags ^ ((((orderFlags >> 1) & orderFlags) & 0x15) * 3);
317
0
        }
318
319
#if !defined GRAPHITE2_NTRACING
320
        if (dbgout)
321
            dbgout->setenv(0, slot);
322
#endif
323
324
        // Process main bounding octabox.
325
0
        for (int i = 0; i < 4; ++i)
326
0
        {
327
0
            switch (i) {
328
0
                case 0 :  // x direction
329
0
                    vmin = max(max(bb.xi - tbb.xa + sx, sb.di - tsb.da + ty + sd), sb.si - tsb.sa - ty + ss);
330
0
                    vmax = min(min(bb.xa - tbb.xi + sx, sb.da - tsb.di + ty + sd), sb.sa - tsb.si - ty + ss);
331
0
                    otmin = tbb.yi + ty;
332
0
                    otmax = tbb.ya + ty;
333
0
                    omin = bb.yi + sy;
334
0
                    omax = bb.ya + sy;
335
0
                    torg = _currOffset.x;
336
0
                    cmin = _limit.bl.x + torg;
337
0
                    cmax = _limit.tr.x - tbb.xi + tbb.xa + torg;
338
0
                    lmargin = _margin;
339
0
                    break;
340
0
                case 1 :  // y direction
341
0
                    vmin = max(max(bb.yi - tbb.ya + sy, tsb.di - sb.da + tx - sd), sb.si - tsb.sa - tx + ss);
342
0
                    vmax = min(min(bb.ya - tbb.yi + sy, tsb.da - sb.di + tx - sd), sb.sa - tsb.si - tx + ss);
343
0
                    otmin = tbb.xi + tx;
344
0
                    otmax = tbb.xa + tx;
345
0
                    omin = bb.xi + sx;
346
0
                    omax = bb.xa + sx;
347
0
                    torg = _currOffset.y;
348
0
                    cmin = _limit.bl.y + torg;
349
0
                    cmax = _limit.tr.y - tbb.yi + tbb.ya + torg;
350
0
                    lmargin = _margin;
351
0
                    break;
352
0
                case 2 :    // sum - moving along the positively-sloped vector, so the boundaries are the
353
                            // negatively-sloped boundaries.
354
0
                    vmin = max(max(sb.si - tsb.sa + ss, 2 * (bb.yi - tbb.ya + sy) + td), 2 * (bb.xi - tbb.xa + sx) - td);
355
0
                    vmax = min(min(sb.sa - tsb.si + ss, 2 * (bb.ya - tbb.yi + sy) + td), 2 * (bb.xa - tbb.xi + sx) - td);
356
0
                    otmin = tsb.di + td;
357
0
                    otmax = tsb.da + td;
358
0
                    omin = sb.di + sd;
359
0
                    omax = sb.da + sd;
360
0
                    torg = _currOffset.x + _currOffset.y;
361
0
                    cmin = _limit.bl.x + _limit.bl.y + torg;
362
0
                    cmax = _limit.tr.x + _limit.tr.y - tsb.si + tsb.sa + torg;
363
0
                    lmargin = _margin / ISQRT2;
364
0
                    break;
365
0
                case 3 :    // diff - moving along the negatively-sloped vector, so the boundaries are the
366
                            // positively-sloped boundaries.
367
0
                    vmin = max(max(sb.di - tsb.da + sd, 2 * (bb.xi - tbb.xa + sx) - ts), -2 * (bb.ya - tbb.yi + sy) + ts);
368
0
                    vmax = min(min(sb.da - tsb.di + sd, 2 * (bb.xa - tbb.xi + sx) - ts), -2 * (bb.yi - tbb.ya + sy) + ts);
369
0
                    otmin = tsb.si + ts;
370
0
                    otmax = tsb.sa + ts;
371
0
                    omin = sb.si + ss;
372
0
                    omax = sb.sa + ss;
373
0
                    torg = _currOffset.x - _currOffset.y;
374
0
                    cmin = _limit.bl.x - _limit.tr.y + torg;
375
0
                    cmax = _limit.tr.x - _limit.bl.y - tsb.di + tsb.da + torg;
376
0
                    lmargin = _margin / ISQRT2;
377
0
                    break;
378
0
                default :
379
0
                    continue;
380
0
            }
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
0
#define DBGTAG(x)
388
0
#endif
389
390
0
            if (orderFlags)
391
0
            {
392
0
                Position org(tx, ty);
393
0
                float xminf = _limit.bl.x + _currOffset.x + tbb.xi;
394
0
                float xpinf = _limit.tr.x + _currOffset.x + tbb.xa;
395
0
                float ypinf = _limit.tr.y + _currOffset.y + tbb.ya;
396
0
                float yminf = _limit.bl.y + _currOffset.y + tbb.yi;
397
0
                switch (orderFlags) {
398
0
                    case SlotCollision::SEQ_ORDER_RIGHTUP :
399
0
                    {
400
0
                        float r1Xedge = cslot->seqAboveXoff() + 0.5f * (bb.xi + bb.xa) + sx;
401
0
                        float r3Xedge = cslot->seqBelowXlim() + bb.xa + sx + 0.5f * (tbb.xa - tbb.xi);
402
0
                        float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy;
403
404
                        // DBGTAG(1x) means the regions are up and right
405
                        // region 1
406
0
                        DBGTAG(11)
407
0
                        addBox_slope(true, Rect(Position(xminf, r2Yedge), Position(r1Xedge, ypinf)),
408
0
                                        tbb, tsb, org, 0, seq_above_wt, true, i);
409
                        // region 2
410
0
                        DBGTAG(12)
411
0
                        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
0
                        DBGTAG(13)
414
0
                        addBox_slope(true, Rect(Position(r3Xedge, yminf), Position(xpinf, r2Yedge - cslot->seqValignHt())),
415
0
                                        tbb, tsb, org, seq_below_wt, 0, true, i);
416
                        // region 4
417
0
                        DBGTAG(14)
418
0
                        addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge), Position(xpinf, r2Yedge + cslot->seqValignHt())),
419
0
                                        tbb, tsb, org, 0, seq_valign_wt, true, i);
420
                        // region 5
421
0
                        DBGTAG(15)
422
0
                        addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge - cslot->seqValignHt()), Position(xpinf, r2Yedge)),
423
0
                                        tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i);
424
0
                        break;
425
0
                    }
426
0
                    case SlotCollision::SEQ_ORDER_LEFTDOWN :
427
0
                    {
428
0
                        float r1Xedge = 0.5f * (bb.xi + bb.xa) + cslot->seqAboveXoff() + sx;
429
0
                        float r3Xedge = bb.xi - cslot->seqBelowXlim() + sx - 0.5f * (tbb.xa - tbb.xi);
430
0
                        float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy;
431
                        // DBGTAG(2x) means the regions are up and right
432
                        // region 1
433
0
                        DBGTAG(21)
434
0
                        addBox_slope(true, Rect(Position(r1Xedge, yminf), Position(xpinf, r2Yedge)),
435
0
                                        tbb, tsb, org, 0, seq_above_wt, false, i);
436
                        // region 2
437
0
                        DBGTAG(22)
438
0
                        removeBox(Rect(Position(r3Xedge, r2Yedge), Position(xpinf, ypinf)), tbb, tsb, org, i);
439
                        // region 3
440
0
                        DBGTAG(23)
441
0
                        addBox_slope(true, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()), Position(r3Xedge, ypinf)),
442
0
                                        tbb, tsb, org, seq_below_wt, 0, false, i);
443
                        // region 4
444
0
                        DBGTAG(24)
445
0
                        addBox_slope(false, Rect(Position(xminf, r2Yedge), Position(sx + bb.xa, r2Yedge + cslot->seqValignHt())),
446
0
                                        tbb, tsb, org, 0, seq_valign_wt, true, i);
447
                        // region 5
448
0
                        DBGTAG(25)
449
0
                        addBox_slope(false, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()),
450
0
                                        Position(sx + bb.xa, r2Yedge)), tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i);
451
0
                        break;
452
0
                    }
453
0
                    case SlotCollision::SEQ_ORDER_NOABOVE : // enforce neighboring glyph being above
454
0
                        DBGTAG(31);
455
0
                        removeBox(Rect(Position(bb.xi - tbb.xa + sx, sy + bb.ya),
456
0
                                        Position(bb.xa - tbb.xi + sx, ypinf)), tbb, tsb, org, i);
457
0
                        break;
458
0
                    case SlotCollision::SEQ_ORDER_NOBELOW : // enforce neighboring glyph being below
459
0
                        DBGTAG(32);
460
0
                        removeBox(Rect(Position(bb.xi - tbb.xa + sx, yminf),
461
0
                                        Position(bb.xa - tbb.xi + sx, sy + bb.yi)), tbb, tsb, org, i);
462
0
                        break;
463
0
                    case SlotCollision::SEQ_ORDER_NOLEFT :  // enforce neighboring glyph being to the left
464
0
                        DBGTAG(33)
465
0
                        removeBox(Rect(Position(xminf, bb.yi - tbb.ya + sy),
466
0
                                        Position(bb.xi - tbb.xa + sx, bb.ya - tbb.yi + sy)), tbb, tsb, org, i);
467
0
                        break;
468
0
                    case SlotCollision::SEQ_ORDER_NORIGHT : // enforce neighboring glyph being to the right
469
0
                        DBGTAG(34)
470
0
                        removeBox(Rect(Position(bb.xa - tbb.xi + sx, bb.yi - tbb.ya + sy),
471
0
                                        Position(xpinf, bb.ya - tbb.yi + sy)), tbb, tsb, org, i);
472
0
                        break;
473
0
                    default :
474
0
                        break;
475
0
                }
476
0
            }
477
478
0
            if (vmax < cmin - lmargin || vmin > cmax + lmargin || omax < otmin - lmargin || omin > otmax + lmargin)
479
0
                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
0
            uint8 numsub = gc.numSubBounds(gid);
484
0
            if (numsub > 0)
485
0
            {
486
0
                bool anyhits = false;
487
0
                for (int j = 0; j < numsub; ++j)
488
0
                {
489
0
                    const BBox &sbb = gc.getSubBoundingBBox(gid, j);
490
0
                    const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, j);
491
0
                    switch (i) {
492
0
                        case 0 :    // x
493
0
                            vmin = max(max(sbb.xi-tbb.xa+sx, ssb.di-tsb.da+sd+ty), ssb.si-tsb.sa+ss-ty);
494
0
                            vmax = min(min(sbb.xa-tbb.xi+sx, ssb.da-tsb.di+sd+ty), ssb.sa-tsb.si+ss-ty);
495
0
                            omin = sbb.yi + sy;
496
0
                            omax = sbb.ya + sy;
497
0
                            break;
498
0
                        case 1 :    // y
499
0
                            vmin = max(max(sbb.yi-tbb.ya+sy, tsb.di-ssb.da-sd+tx), ssb.si-tsb.sa+ss-tx);
500
0
                            vmax = min(min(sbb.ya-tbb.yi+sy, tsb.da-ssb.di-sd+tx), ssb.sa-tsb.si+ss-tx);
501
0
                            omin = sbb.xi + sx;
502
0
                            omax = sbb.xa + sx;
503
0
                            break;
504
0
                        case 2 :    // sum
505
0
                            vmin = max(max(ssb.si-tsb.sa+ss, 2*(sbb.yi-tbb.ya+sy)+td), 2*(sbb.xi-tbb.xa+sx)-td);
506
0
                            vmax = min(min(ssb.sa-tsb.si+ss, 2*(sbb.ya-tbb.yi+sy)+td), 2*(sbb.xa-tbb.xi+sx)-td);
507
0
                            omin = ssb.di + sd;
508
0
                            omax = ssb.da + sd;
509
0
                            break;
510
0
                        case 3 :    // diff
511
0
                            vmin = max(max(ssb.di-tsb.da+sd, 2*(sbb.xi-tbb.xa+sx)-ts), -2*(sbb.ya-tbb.yi+sy)+ts);
512
0
                            vmax = min(min(ssb.da-tsb.di+sd, 2*(sbb.xa-tbb.xi+sx)-ts), -2*(sbb.yi-tbb.ya+sy)+ts);
513
0
                            omin = ssb.si + ss;
514
0
                            omax = ssb.sa + ss;
515
0
                            break;
516
0
                    }
517
0
                    if (vmax < cmin - lmargin || vmin > cmax + lmargin || omax < otmin - lmargin || omin > otmax + lmargin)
518
0
                        continue;
519
520
#if !defined GRAPHITE2_NTRACING
521
                    if (dbgout)
522
                        dbgout->setenv(1, reinterpret_cast<void *>(j));
523
#endif
524
0
                    if (omin > otmax)
525
0
                        _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
526
0
                                                sqr(lmargin - omin + otmax) * _marginWt, false);
527
0
                    else if (omax < otmin)
528
0
                        _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
529
0
                                                sqr(lmargin - otmin + omax) * _marginWt, false);
530
0
                    else
531
0
                        _ranges[i].exclude_with_margins(vmin, vmax, i);
532
0
                    anyhits = true;
533
0
                }
534
0
                if (anyhits)
535
0
                    isCol = true;
536
0
            }
537
0
            else // no sub-boxes
538
0
            {
539
#if !defined GRAPHITE2_NTRACING
540
                    if (dbgout)
541
                        dbgout->setenv(1, reinterpret_cast<void *>(-1));
542
#endif
543
0
                isCol = true;
544
0
                if (omin > otmax)
545
0
                    _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
546
0
                                            sqr(lmargin - omin + otmax) * _marginWt, false);
547
0
                else if (omax < otmin)
548
0
                    _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
549
0
                                            sqr(lmargin - otmin + omax) * _marginWt, false);
550
0
                else
551
0
                    _ranges[i].exclude_with_margins(vmin, vmax, i);
552
553
0
            }
554
0
        }
555
0
    }
556
0
    bool res = true;
557
0
    if (cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion)
558
0
    {
559
        // Set up the bogus slot representing the exclusion glyph.
560
0
        Slot *exclSlot = seg->newSlot();
561
0
        if (!exclSlot)
562
0
            return res;
563
0
        exclSlot->setGlyph(seg, cslot->exclGlyph());
564
0
        Position exclOrigin(slot->origin() + cslot->exclOffset());
565
0
        exclSlot->origin(exclOrigin);
566
0
        SlotCollision exclInfo(seg, exclSlot);
567
0
        res &= mergeSlot(seg, exclSlot, &exclInfo, currShift, isAfter, sameCluster, isCol, true, dbgout );
568
0
        seg->freeSlot(exclSlot);
569
0
    }
570
0
    hasCol |= isCol;
571
0
    return res;
572
573
0
}   // end of ShiftCollider::mergeSlot
574
575
576
// Figure out where to move the target glyph to, and return the amount to shift by.
577
Position ShiftCollider::resolve(GR_MAYBE_UNUSED Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout)
578
0
{
579
0
    float tbase;
580
0
    float totalCost = (float)(std::numeric_limits<float>::max() / 2);
581
0
    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
0
    isCol = true;
591
0
    for (int i = 0; i < 4; ++i)
592
0
    {
593
0
        float bestCost = -1;
594
0
        float bestPos;
595
        // Calculate the margin depending on whether we are moving diagonally or not:
596
0
        switch (i) {
597
0
            case 0 :  // x direction
598
0
                tbase = _currOffset.x;
599
0
                break;
600
0
            case 1 :  // y direction
601
0
                tbase = _currOffset.y;
602
0
                break;
603
0
            case 2 :  // sum (negatively-sloped diagonals)
604
0
                tbase = _currOffset.x + _currOffset.y;
605
0
                break;
606
0
            case 3 :  // diff (positively-sloped diagonals)
607
0
                tbase = _currOffset.x - _currOffset.y;
608
0
                break;
609
0
        }
610
0
        Position testp;
611
0
        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
0
        if (bestCost >= 0.0f)
617
0
        {
618
0
            isCol = false;
619
0
            switch (i) {
620
0
                case 0 : testp = Position(bestPos, _currShift.y); break;
621
0
                case 1 : testp = Position(_currShift.x, bestPos); break;
622
0
                case 2 : testp = Position(0.5f * (_currShift.x - _currShift.y + bestPos), 0.5f * (_currShift.y - _currShift.x + bestPos)); break;
623
0
                case 3 : testp = Position(0.5f * (_currShift.x + _currShift.y + bestPos), 0.5f * (_currShift.x + _currShift.y - bestPos)); break;
624
0
            }
625
0
            if (bestCost < totalCost - 0.01f)
626
0
            {
627
0
                totalCost = bestCost;
628
0
                resultPos = testp;
629
#if !defined GRAPHITE2_NTRACING
630
                bestAxis = i;
631
#endif
632
0
            }
633
0
        }
634
0
    }  // 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
0
    return resultPos;
642
643
0
}   // end of ShiftCollider::resolve
644
645
646
#if !defined GRAPHITE2_NTRACING
647
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
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
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
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
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
745
static float localmax (float al, float au, float bl, float bu, float x)
746
0
{
747
0
    if (al < bl)
748
0
    { if (au < bu) return au < x ? au : x; }
749
0
    else if (au > bu) return bl < x ? bl : x;
750
0
    return x;
751
0
}
752
753
inline
754
static float localmin(float al, float au, float bl, float bu, float x)
755
0
{
756
0
    if (bl > al)
757
0
    { if (bu > au) return bl > x ? bl : x; }
758
0
    else if (au > bu) return al > x ? al : x;
759
0
    return x;
760
0
}
761
762
// Return the given edge of the glyph at height y, taking any slant box into account.
763
static float get_edge(Segment *seg, const Slot *s, const Position &shift, float y, float width, float margin, bool isRight)
764
0
{
765
0
    const GlyphCache &gc = seg->getFace()->glyphs();
766
0
    unsigned short gid = s->gid();
767
0
    float sx = s->origin().x + shift.x;
768
0
    float sy = s->origin().y + shift.y;
769
0
    uint8 numsub = gc.numSubBounds(gid);
770
0
    float res = isRight ? (float)-1e38 : (float)1e38;
771
772
0
    if (numsub > 0)
773
0
    {
774
0
        for (int i = 0; i < numsub; ++i)
775
0
        {
776
0
            const BBox &sbb = gc.getSubBoundingBBox(gid, i);
777
0
            const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, i);
778
0
            if (sy + sbb.yi - margin > y + width / 2 || sy + sbb.ya + margin < y - width / 2)
779
0
                continue;
780
0
            if (isRight)
781
0
            {
782
0
                float x = sx + sbb.xa + margin;
783
0
                if (x > res)
784
0
                {
785
0
                    float td = sx - sy + ssb.da + margin + y;
786
0
                    float ts = sx + sy + ssb.sa + margin - y;
787
0
                    x = localmax(td - width / 2, td + width / 2,  ts - width / 2, ts + width / 2, x);
788
0
                    if (x > res)
789
0
                        res = x;
790
0
                }
791
0
            }
792
0
            else
793
0
            {
794
0
                float x = sx + sbb.xi - margin;
795
0
                if (x < res)
796
0
                {
797
0
                    float td = sx - sy + ssb.di - margin + y;
798
0
                    float ts = sx + sy + ssb.si - margin - y;
799
0
                    x = localmin(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x);
800
0
                    if (x < res)
801
0
                        res = x;
802
0
                }
803
0
            }
804
0
        }
805
0
    }
806
0
    else
807
0
    {
808
0
        const BBox &bb = gc.getBoundingBBox(gid);
809
0
        const SlantBox &sb = gc.getBoundingSlantBox(gid);
810
0
        if (sy + bb.yi - margin > y + width / 2 || sy + bb.ya + margin < y - width / 2)
811
0
            return res;
812
0
        float td = sx - sy + y;
813
0
        float ts = sx + sy - y;
814
0
        if (isRight)
815
0
            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
0
        else
817
0
            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
0
    }
819
0
    return res;
820
0
}
821
822
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
0
{
827
0
    const GlyphCache &gc = seg->getFace()->glyphs();
828
0
    const Slot *base = aSlot;
829
    // const Slot *last = aSlot;
830
0
    const Slot *s;
831
0
    int numSlices;
832
0
    while (base->attachedTo())
833
0
        base = base->attachedTo();
834
0
    if (margin < 10) margin = 10;
835
836
0
    _limit = limit;
837
0
    _offsetPrev = offsetPrev; // kern from a previous pass
838
839
    // Calculate the height of the glyph and how many horizontal slices to use.
840
0
    if (_maxy >= 1e37f)
841
0
    {
842
0
        _sliceWidth = margin / 1.5f;
843
0
        _maxy = ymax + margin;
844
0
        _miny = ymin - margin;
845
0
        numSlices = int((_maxy - _miny + 2) / (_sliceWidth / 1.5f) + 1.f);  // +2 helps with rounding errors
846
0
        _edges.clear();
847
0
        _edges.insert(_edges.begin(), numSlices, (dir & 1) ? 1e38f : -1e38f);
848
0
        _xbound = (dir & 1) ? (float)1e38f : (float)-1e38f;
849
0
    }
850
0
    else if (_maxy != ymax || _miny != ymin)
851
0
    {
852
0
        if (_miny != ymin)
853
0
        {
854
0
            numSlices = int((ymin - margin - _miny) / _sliceWidth - 1);
855
0
            _miny += numSlices * _sliceWidth;
856
0
            if (numSlices < 0)
857
0
                _edges.insert(_edges.begin(), -numSlices, (dir & 1) ? 1e38f : -1e38f);
858
0
            else if ((unsigned)numSlices < _edges.size())    // this shouldn't fire since we always grow the range
859
0
            {
860
0
                Vector<float>::iterator e = _edges.begin();
861
0
                while (numSlices--)
862
0
                    ++e;
863
0
                _edges.erase(_edges.begin(), e);
864
0
            }
865
0
        }
866
0
        if (_maxy != ymax)
867
0
        {
868
0
            numSlices = int((ymax + margin - _miny) / _sliceWidth + 1);
869
0
            _maxy = numSlices * _sliceWidth + _miny;
870
0
            if (numSlices > (int)_edges.size())
871
0
                _edges.insert(_edges.end(), numSlices - _edges.size(), (dir & 1) ? 1e38f : -1e38f);
872
0
            else if (numSlices < (int)_edges.size())   // this shouldn't fire since we always grow the range
873
0
            {
874
0
                while ((int)_edges.size() > numSlices)
875
0
                    _edges.pop_back();
876
0
            }
877
0
        }
878
0
        goto done;
879
0
    }
880
0
    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
0
    for (s = base; s; s = s->nextInCluster(s))
893
0
    {
894
0
        SlotCollision *c = seg->collisionInfo(s);
895
0
        if (!gc.check(s->gid()))
896
0
            return false;
897
0
        const BBox &bs = gc.getBoundingBBox(s->gid());
898
0
        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
0
        float toffset = c->shift().y - _miny + 1 + s->origin().y;
902
0
        int smin = max(0, int((bs.yi + toffset) / _sliceWidth));
903
0
        int smax = min(numSlices - 1, int((bs.ya + toffset) / _sliceWidth + 1));
904
0
        for (int i = smin; i <= smax; ++i)
905
0
        {
906
0
            float t;
907
0
            float y = _miny - 1 + (i + .5f) * _sliceWidth; // vertical center of slice
908
0
            if ((dir & 1) && x < _edges[i])
909
0
            {
910
0
                t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, false);
911
0
                if (t < _edges[i])
912
0
                {
913
0
                    _edges[i] = t;
914
0
                    if (t < _xbound)
915
0
                        _xbound = t;
916
0
                }
917
0
            }
918
0
            else if (!(dir & 1) && x > _edges[i])
919
0
            {
920
0
                t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, true);
921
0
                if (t > _edges[i])
922
0
                {
923
0
                    _edges[i] = t;
924
0
                    if (t > _xbound)
925
0
                        _xbound = t;
926
0
                }
927
0
            }
928
0
        }
929
0
    }
930
0
    done:
931
0
    _mingap = (float)1e37;      // less than 1e38 s.t. 1e38-_mingap is really big
932
0
    _target = aSlot;
933
0
    _margin = margin;
934
0
    _currShift = currShift;
935
0
    return true;
936
0
}   // 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.
943
bool KernCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, GR_MAYBE_UNUSED json * const dbgout)
944
0
{
945
0
    int rtl = (dir & 1) * 2 - 1;
946
0
    if (!seg->getFace()->glyphs().check(slot->gid()))
947
0
        return false;
948
0
    const Rect &bb = seg->theGlyphBBoxTemporary(slot->gid());
949
0
    const float sx = slot->origin().x + currShift.x;
950
0
    float x = (sx + (rtl > 0 ? bb.tr.x : bb.bl.x)) * rtl;
951
    // this isn't going to reduce _mingap so skip
952
0
    if (_hit && x < rtl * (_xbound - _mingap - currSpace))
953
0
        return false;
954
955
0
    const float sy = slot->origin().y + currShift.y;
956
0
    int smin = max(1, int((bb.bl.y + (1 - _miny + sy)) / _sliceWidth + 1)) - 1;
957
0
    int smax = min((int)_edges.size() - 2, int((bb.tr.y + (1 - _miny + sy)) / _sliceWidth + 1)) + 1;
958
0
    if (smin > smax)
959
0
        return false;
960
0
    bool collides = false;
961
0
    bool nooverlap = true;
962
963
0
    for (int i = smin; i <= smax; ++i)
964
0
    {
965
0
        float here = _edges[i] * rtl;
966
0
        if (here > (float)9e37)
967
0
            continue;
968
0
        if (!_hit || x > here - _mingap - currSpace)
969
0
        {
970
0
            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
0
            float m = get_edge(seg, slot, currShift, y, _sliceWidth, 0., rtl > 0) * rtl + 2 * currSpace;
973
0
            if (m < (float)-8e37)       // only true if the glyph has a gap in it
974
0
                continue;
975
0
            nooverlap = false;
976
0
            float t = here - m;
977
            // _mingap is positive to shrink
978
0
            if (t < _mingap || (!_hit && !collides))
979
0
            {
980
0
                _mingap = t;
981
0
                collides = true;
982
0
            }
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
0
        }
992
0
        else
993
0
            nooverlap = false;
994
0
    }
995
0
    if (nooverlap)
996
0
        _mingap = max(_mingap, _xbound - rtl * (currSpace + _margin + x));
997
0
    if (collides && !nooverlap)
998
0
        _hit = true;
999
0
    return collides | nooverlap;   // note that true is not a necessarily reliable value
1000
1001
0
}   // end of KernCollider::mergeSlot
1002
1003
1004
// Return the amount to kern by.
1005
Position KernCollider::resolve(GR_MAYBE_UNUSED Segment *seg, GR_MAYBE_UNUSED Slot *slot,
1006
        int dir, GR_MAYBE_UNUSED json * const dbgout)
1007
0
{
1008
0
    float resultNeeded = (1 - 2 * (dir & 1)) * _mingap;
1009
    // float resultNeeded = (1 - 2 * (dir & 1)) * (_mingap - margin);
1010
0
    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
0
    return Position(result, 0.);
1054
1055
0
}   // end of KernCollider::resolve
1056
1057
void KernCollider::shift(const Position &mv, int dir)
1058
0
{
1059
0
    for (Vector<float>::iterator e = _edges.begin(); e != _edges.end(); ++e)
1060
0
        *e += mv.x;
1061
0
    _xbound += (1 - 2 * (dir & 1)) * mv.x;
1062
0
}
1063
1064
////    SLOT-COLLISION    ////
1065
1066
// Initialize the collision attributes for the given slot.
1067
SlotCollision::SlotCollision(Segment *seg, Slot *slot)
1068
0
{
1069
0
    initFromSlot(seg, slot);
1070
0
}
1071
1072
void SlotCollision::initFromSlot(Segment *seg, Slot *slot)
1073
0
{
1074
    // Initialize slot attributes from glyph attributes.
1075
  // The order here must match the order in the grcompiler code,
1076
  // GrcSymbolTable::AssignInternalGlyphAttrIDs.
1077
0
    uint16 gid = slot->gid();
1078
0
    uint16 aCol = seg->silf()->aCollision(); // flags attr ID
1079
0
    const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(gid);
1080
0
    if (!glyphFace)
1081
0
        return;
1082
0
    const sparse &p = glyphFace->attrs();
1083
0
    _flags = p[aCol];
1084
0
    _limit = Rect(Position(int16(p[aCol+1]), int16(p[aCol+2])),
1085
0
                  Position(int16(p[aCol+3]), int16(p[aCol+4])));
1086
0
    _margin = p[aCol+5];
1087
0
    _marginWt = p[aCol+6];
1088
1089
0
    _seqClass = p[aCol+7];
1090
0
  _seqProxClass = p[aCol+8];
1091
0
    _seqOrder = p[aCol+9];
1092
0
  _seqAboveXoff = p[aCol+10];
1093
0
  _seqAboveWt = p[aCol+11];
1094
0
  _seqBelowXlim = p[aCol+12];
1095
0
  _seqBelowWt = p[aCol+13];
1096
0
  _seqValignHt = p[aCol+14];
1097
0
  _seqValignWt = p[aCol+15];
1098
1099
    // These attributes do not have corresponding glyph attribute:
1100
0
    _exclGlyph = 0;
1101
0
    _exclOffset = Position(0, 0);
1102
0
}
1103
1104
float SlotCollision::getKern(int dir) const
1105
0
{
1106
0
    if ((_flags & SlotCollision::COLL_KERN) != 0)
1107
0
        return float(_shift.x * ((dir & 1) ? -1 : 1));
1108
0
    else
1109
0
      return 0;
1110
0
}
1111
1112
bool SlotCollision::ignore() const
1113
0
{
1114
0
  return ((flags() & SlotCollision::COLL_IGNORE) || (flags() & SlotCollision::COLL_ISSPACE));
1115
0
}