LCOV - code coverage report
Current view: top level - gfx/graphite2/src - Collider.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 525 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 18 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13