Coverage Report

Created: 2026-06-30 11:14

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