tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Collider.cpp (46624B)


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