LCOV - code coverage report
Current view: top level - gfx/graphite2/src - Pass.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 526 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 21 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 "inc/Main.h"
      28             : #include "inc/debug.h"
      29             : #include "inc/Endian.h"
      30             : #include "inc/Pass.h"
      31             : #include <cstring>
      32             : #include <cstdlib>
      33             : #include <cassert>
      34             : #include <cmath>
      35             : #include "inc/Segment.h"
      36             : #include "inc/Code.h"
      37             : #include "inc/Rule.h"
      38             : #include "inc/Error.h"
      39             : #include "inc/Collider.h"
      40             : 
      41             : using namespace graphite2;
      42             : using vm::Machine;
      43             : typedef Machine::Code  Code;
      44             : 
      45             : enum KernCollison
      46             : {
      47             :     None       = 0,
      48             :     CrossSpace = 1,
      49             :     InWord     = 2,
      50             :     reserved   = 3
      51             : };
      52             : 
      53           0 : Pass::Pass()
      54             : : m_silf(0),
      55             :   m_cols(0),
      56             :   m_rules(0),
      57             :   m_ruleMap(0),
      58             :   m_startStates(0),
      59             :   m_transitions(0),
      60             :   m_states(0),
      61             :   m_codes(0),
      62             :   m_progs(0),
      63             :   m_numCollRuns(0),
      64             :   m_kernColls(0),
      65             :   m_iMaxLoop(0),
      66             :   m_numGlyphs(0),
      67             :   m_numRules(0),
      68             :   m_numStates(0),
      69             :   m_numTransition(0),
      70             :   m_numSuccess(0),
      71             :   m_successStart(0),
      72             :   m_numColumns(0),
      73             :   m_minPreCtxt(0),
      74             :   m_maxPreCtxt(0),
      75             :   m_colThreshold(0),
      76           0 :   m_isReverseDir(false)
      77             : {
      78           0 : }
      79             : 
      80           0 : Pass::~Pass()
      81             : {
      82           0 :     free(m_cols);
      83           0 :     free(m_startStates);
      84           0 :     free(m_transitions);
      85           0 :     free(m_states);
      86           0 :     free(m_ruleMap);
      87             : 
      88           0 :     if (m_rules) delete [] m_rules;
      89           0 :     if (m_codes) delete [] m_codes;
      90           0 :     free(m_progs);
      91           0 : }
      92             : 
      93           0 : bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base,
      94             :         GR_MAYBE_UNUSED Face & face, passtype pt, GR_MAYBE_UNUSED uint32 version, Error &e)
      95             : {
      96           0 :     const byte * p              = pass_start,
      97           0 :                * const pass_end = p + pass_length;
      98             :     size_t numRanges;
      99             : 
     100           0 :     if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e); 
     101             :     // Read in basic values
     102           0 :     const byte flags = be::read<byte>(p);
     103           0 :     if (e.test((flags & 0x1f) && 
     104           0 :             (pt < PASS_TYPE_POSITIONING || !m_silf->aCollision() || !face.glyphs().hasBoxes() || !(m_silf->flags() & 0x20)),
     105             :             E_BADCOLLISIONPASS))
     106           0 :         return face.error(e);
     107           0 :     m_numCollRuns = flags & 0x7;
     108           0 :     m_kernColls   = (flags >> 3) & 0x3;
     109           0 :     m_isReverseDir = (flags >> 5) & 0x1;
     110           0 :     m_iMaxLoop = be::read<byte>(p);
     111           0 :     if (m_iMaxLoop < 1) m_iMaxLoop = 1;
     112           0 :     be::skip<byte>(p,2); // skip maxContext & maxBackup
     113           0 :     m_numRules = be::read<uint16>(p);
     114           0 :     if (e.test(!m_numRules && m_numCollRuns == 0, E_BADEMPTYPASS)) return face.error(e);
     115           0 :     be::skip<uint16>(p);   // fsmOffset - not sure why we would want this
     116           0 :     const byte * const pcCode = pass_start + be::read<uint32>(p) - subtable_base,
     117           0 :                * const rcCode = pass_start + be::read<uint32>(p) - subtable_base,
     118           0 :                * const aCode  = pass_start + be::read<uint32>(p) - subtable_base;
     119           0 :     be::skip<uint32>(p);
     120           0 :     m_numStates = be::read<uint16>(p);
     121           0 :     m_numTransition = be::read<uint16>(p);
     122           0 :     m_numSuccess = be::read<uint16>(p);
     123           0 :     m_numColumns = be::read<uint16>(p);
     124           0 :     numRanges = be::read<uint16>(p);
     125           0 :     be::skip<uint16>(p, 3); // skip searchRange, entrySelector & rangeShift.
     126           0 :     assert(p - pass_start == 40);
     127             :     // Perform some sanity checks.
     128           0 :     if ( e.test(m_numTransition > m_numStates, E_BADNUMTRANS)
     129           0 :             || e.test(m_numSuccess > m_numStates, E_BADNUMSUCCESS)
     130           0 :             || e.test(m_numSuccess + m_numTransition < m_numStates, E_BADNUMSTATES)
     131           0 :             || e.test(m_numRules && numRanges == 0, E_NORANGES)
     132           0 :             || e.test(m_numColumns > 0x7FFF, E_BADNUMCOLUMNS))
     133           0 :         return face.error(e);
     134             : 
     135           0 :     m_successStart = m_numStates - m_numSuccess;
     136             :     // test for beyond end - 1 to account for reading uint16
     137           0 :     if (e.test(p + numRanges * 6 - 2 > pass_end, E_BADPASSLENGTH)) return face.error(e);
     138           0 :     m_numGlyphs = be::peek<uint16>(p + numRanges * 6 - 4) + 1;
     139             :     // Calculate the start of various arrays.
     140           0 :     const byte * const ranges = p;
     141           0 :     be::skip<uint16>(p, numRanges*3);
     142           0 :     const byte * const o_rule_map = p;
     143           0 :     be::skip<uint16>(p, m_numSuccess + 1);
     144             : 
     145             :     // More sanity checks
     146           0 :     if (e.test(reinterpret_cast<const byte *>(o_rule_map + m_numSuccess*sizeof(uint16)) > pass_end
     147           0 :             || p > pass_end, E_BADRULEMAPLEN))
     148           0 :         return face.error(e);
     149           0 :     const size_t numEntries = be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16));
     150           0 :     const byte * const   rule_map = p;
     151           0 :     be::skip<uint16>(p, numEntries);
     152             : 
     153           0 :     if (e.test(p + 2*sizeof(uint8) > pass_end, E_BADPASSLENGTH)) return face.error(e);
     154           0 :     m_minPreCtxt = be::read<uint8>(p);
     155           0 :     m_maxPreCtxt = be::read<uint8>(p);
     156           0 :     if (e.test(m_minPreCtxt > m_maxPreCtxt, E_BADCTXTLENBOUNDS)) return face.error(e);
     157           0 :     const byte * const start_states = p;
     158           0 :     be::skip<int16>(p, m_maxPreCtxt - m_minPreCtxt + 1);
     159           0 :     const uint16 * const sort_keys = reinterpret_cast<const uint16 *>(p);
     160           0 :     be::skip<uint16>(p, m_numRules);
     161           0 :     const byte * const precontext = p;
     162           0 :     be::skip<byte>(p, m_numRules);
     163             : 
     164           0 :     if (e.test(p + sizeof(uint16) + sizeof(uint8) > pass_end, E_BADCTXTLENS)) return face.error(e);
     165           0 :     m_colThreshold = be::read<uint8>(p);
     166           0 :     if (m_colThreshold == 0) m_colThreshold = 10;       // A default
     167           0 :     const size_t pass_constraint_len = be::read<uint16>(p);
     168             : 
     169           0 :     const uint16 * const o_constraint = reinterpret_cast<const uint16 *>(p);
     170           0 :     be::skip<uint16>(p, m_numRules + 1);
     171           0 :     const uint16 * const o_actions = reinterpret_cast<const uint16 *>(p);
     172           0 :     be::skip<uint16>(p, m_numRules + 1);
     173           0 :     const byte * const states = p;
     174           0 :     if (e.test(2u*m_numTransition*m_numColumns >= (unsigned)(pass_end - p), E_BADPASSLENGTH)) return face.error(e);
     175           0 :     be::skip<int16>(p, m_numTransition*m_numColumns);
     176           0 :     be::skip<uint8>(p);
     177           0 :     if (e.test(p != pcCode, E_BADPASSCCODEPTR)) return face.error(e);
     178           0 :     be::skip<byte>(p, pass_constraint_len);
     179           0 :     if (e.test(p != rcCode, E_BADRULECCODEPTR)
     180           0 :         || e.test(size_t(rcCode - pcCode) != pass_constraint_len, E_BADCCODELEN)) return face.error(e);
     181           0 :     be::skip<byte>(p, be::peek<uint16>(o_constraint + m_numRules));
     182           0 :     if (e.test(p != aCode, E_BADACTIONCODEPTR)) return face.error(e);
     183           0 :     be::skip<byte>(p, be::peek<uint16>(o_actions + m_numRules));
     184             : 
     185             :     // We should be at the end or within the pass
     186           0 :     if (e.test(p > pass_end, E_BADPASSLENGTH)) return face.error(e);
     187             : 
     188             :     // Load the pass constraint if there is one.
     189           0 :     if (pass_constraint_len)
     190             :     {
     191           0 :         face.error_context(face.error_context() + 1);
     192           0 :         m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len, 
     193           0 :                                   precontext[0], be::peek<uint16>(sort_keys), *m_silf, face, PASS_TYPE_UNKNOWN);
     194           0 :         if (e.test(!m_cPConstraint, E_OUTOFMEM)
     195           0 :                 || e.test(m_cPConstraint.status() != Code::loaded, m_cPConstraint.status() + E_CODEFAILURE))
     196           0 :             return face.error(e);
     197           0 :         face.error_context(face.error_context() - 1);
     198             :     }
     199           0 :     if (m_numRules)
     200             :     {
     201           0 :         if (!readRanges(ranges, numRanges, e)) return face.error(e);
     202           0 :         if (!readRules(rule_map, numEntries,  precontext, sort_keys,
     203           0 :                    o_constraint, rcCode, o_actions, aCode, face, pt, e)) return false;
     204             :     }
     205             : #ifdef GRAPHITE2_TELEMETRY
     206             :     telemetry::category _states_cat(face.tele.states);
     207             : #endif
     208           0 :     return m_numRules ? readStates(start_states, states, o_rule_map, face, e) : true;
     209             : }
     210             : 
     211             : 
     212           0 : bool Pass::readRules(const byte * rule_map, const size_t num_entries,
     213             :                      const byte *precontext, const uint16 * sort_key,
     214             :                      const uint16 * o_constraint, const byte *rc_data,
     215             :                      const uint16 * o_action,     const byte * ac_data,
     216             :                      Face & face, passtype pt, Error &e)
     217             : {
     218           0 :     const byte * const ac_data_end = ac_data + be::peek<uint16>(o_action + m_numRules);
     219           0 :     const byte * const rc_data_end = rc_data + be::peek<uint16>(o_constraint + m_numRules);
     220             : 
     221           0 :     precontext += m_numRules;
     222           0 :     sort_key   += m_numRules;
     223           0 :     o_constraint += m_numRules;
     224           0 :     o_action += m_numRules;
     225             : 
     226             :     // Load rules.
     227           0 :     const byte * ac_begin = 0, * rc_begin = 0,
     228           0 :                * ac_end = ac_data + be::peek<uint16>(o_action),
     229           0 :                * rc_end = rc_data + be::peek<uint16>(o_constraint);
     230             : 
     231             :     // Allocate pools
     232           0 :     m_rules = new Rule [m_numRules];
     233           0 :     m_codes = new Code [m_numRules*2];
     234           0 :     int totalSlots = 0;
     235           0 :     const uint16 *tsort = sort_key;
     236           0 :     for (int i = 0; i < m_numRules; ++i)
     237           0 :         totalSlots += be::peek<uint16>(--tsort);
     238           0 :     const size_t prog_pool_sz = vm::Machine::Code::estimateCodeDataOut(ac_end - ac_data + rc_end - rc_data, 2 * m_numRules, totalSlots);
     239           0 :     m_progs = gralloc<byte>(prog_pool_sz);
     240           0 :     byte * prog_pool_free = m_progs,
     241           0 :          * prog_pool_end  = m_progs + prog_pool_sz;
     242           0 :     if (e.test(!(m_rules && m_codes && m_progs), E_OUTOFMEM)) return face.error(e);
     243             : 
     244           0 :     Rule * r = m_rules + m_numRules - 1;
     245           0 :     for (size_t n = m_numRules; r >= m_rules; --n, --r, ac_end = ac_begin, rc_end = rc_begin)
     246             :     {
     247           0 :         face.error_context((face.error_context() & 0xFFFF00) + EC_ARULE + ((n - 1) << 24));
     248           0 :         r->preContext = *--precontext;
     249           0 :         r->sort       = be::peek<uint16>(--sort_key);
     250             : #ifndef NDEBUG
     251           0 :         r->rule_idx   = n - 1;
     252             : #endif
     253           0 :         if (r->sort > 63 || r->preContext >= r->sort || r->preContext > m_maxPreCtxt || r->preContext < m_minPreCtxt)
     254           0 :             return false;
     255           0 :         ac_begin      = ac_data + be::peek<uint16>(--o_action);
     256           0 :         --o_constraint;
     257           0 :         rc_begin      = be::peek<uint16>(o_constraint) ? rc_data + be::peek<uint16>(o_constraint) : rc_end;
     258             : 
     259           0 :         if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end
     260           0 :                 || rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end
     261           0 :                 || vm::Machine::Code::estimateCodeDataOut(ac_end - ac_begin + rc_end - rc_begin, 2, r->sort) > size_t(prog_pool_end - prog_pool_free))
     262           0 :             return false;
     263           0 :         r->action     = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
     264           0 :         r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true,  rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
     265             : 
     266           0 :         if (e.test(!r->action || !r->constraint, E_OUTOFMEM)
     267           0 :                 || e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE)
     268           0 :                 || e.test(r->constraint->status() != Code::loaded, r->constraint->status() + E_CODEFAILURE)
     269           0 :                 || e.test(!r->constraint->immutable(), E_MUTABLECCODE))
     270           0 :             return face.error(e);
     271             :     }
     272             : 
     273           0 :     byte * moved_progs = static_cast<byte *>(realloc(m_progs, prog_pool_free - m_progs));
     274           0 :     if (e.test(!moved_progs, E_OUTOFMEM))
     275             :     {
     276           0 :         if (prog_pool_free - m_progs == 0) m_progs = 0;
     277           0 :         return face.error(e);
     278             :     }
     279             : 
     280           0 :     if (moved_progs != m_progs)
     281             :     {
     282           0 :         for (Code * c = m_codes, * const ce = c + m_numRules*2; c != ce; ++c)
     283             :         {
     284           0 :             c->externalProgramMoved(moved_progs - m_progs);
     285             :         }
     286           0 :         m_progs = moved_progs;
     287             :     }
     288             : 
     289             :     // Load the rule entries map
     290           0 :     face.error_context((face.error_context() & 0xFFFF00) + EC_APASS);
     291             :     //TODO: Coverty: 1315804: FORWARD_NULL
     292           0 :     RuleEntry * re = m_ruleMap = gralloc<RuleEntry>(num_entries);
     293           0 :     if (e.test(!re, E_OUTOFMEM)) return face.error(e);
     294           0 :     for (size_t n = num_entries; n; --n, ++re)
     295             :     {
     296           0 :         const ptrdiff_t rn = be::read<uint16>(rule_map);
     297           0 :         if (e.test(rn >= m_numRules, E_BADRULENUM))  return face.error(e);
     298           0 :         re->rule = m_rules + rn;
     299             :     }
     300             : 
     301           0 :     return true;
     302             : }
     303             : 
     304           0 : static int cmpRuleEntry(const void *a, const void *b) { return (*(RuleEntry *)a < *(RuleEntry *)b ? -1 :
     305           0 :                                                                 (*(RuleEntry *)b < *(RuleEntry *)a ? 1 : 0)); }
     306             : 
     307           0 : bool Pass::readStates(const byte * starts, const byte *states, const byte * o_rule_map, GR_MAYBE_UNUSED Face & face, Error &e)
     308             : {
     309             : #ifdef GRAPHITE2_TELEMETRY
     310             :     telemetry::category _states_cat(face.tele.starts);
     311             : #endif
     312           0 :     m_startStates = gralloc<uint16>(m_maxPreCtxt - m_minPreCtxt + 1);
     313             : #ifdef GRAPHITE2_TELEMETRY
     314             :     telemetry::set_category(face.tele.states);
     315             : #endif
     316           0 :     m_states      = gralloc<State>(m_numStates);
     317             : #ifdef GRAPHITE2_TELEMETRY
     318             :     telemetry::set_category(face.tele.transitions);
     319             : #endif
     320           0 :     m_transitions      = gralloc<uint16>(m_numTransition * m_numColumns);
     321             : 
     322           0 :     if (e.test(!m_startStates || !m_states || !m_transitions, E_OUTOFMEM)) return face.error(e);
     323             :     // load start states
     324           0 :     for (uint16 * s = m_startStates,
     325           0 :                 * const s_end = s + m_maxPreCtxt - m_minPreCtxt + 1; s != s_end; ++s)
     326             :     {
     327           0 :         *s = be::read<uint16>(starts);
     328           0 :         if (e.test(*s >= m_numStates, E_BADSTATE))
     329             :         {
     330           0 :             face.error_context((face.error_context() & 0xFFFF00) + EC_ASTARTS + ((s - m_startStates) << 24));
     331           0 :             return face.error(e); // true;
     332             :         }
     333             :     }
     334             : 
     335             :     // load state transition table.
     336           0 :     for (uint16 * t = m_transitions,
     337           0 :                 * const t_end = t + m_numTransition*m_numColumns; t != t_end; ++t)
     338             :     {
     339           0 :         *t = be::read<uint16>(states);
     340           0 :         if (e.test(*t >= m_numStates, E_BADSTATE))
     341             :         {
     342           0 :             face.error_context((face.error_context() & 0xFFFF00) + EC_ATRANS + (((t - m_transitions) / m_numColumns) << 8));
     343           0 :             return face.error(e);
     344             :         }
     345             :     }
     346             : 
     347           0 :     State * s = m_states,
     348           0 :           * const success_begin = m_states + m_numStates - m_numSuccess;
     349           0 :     const RuleEntry * rule_map_end = m_ruleMap + be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16));
     350           0 :     for (size_t n = m_numStates; n; --n, ++s)
     351             :     {
     352           0 :         RuleEntry * const begin = s < success_begin ? 0 : m_ruleMap + be::read<uint16>(o_rule_map),
     353           0 :                   * const end   = s < success_begin ? 0 : m_ruleMap + be::peek<uint16>(o_rule_map);
     354             : 
     355           0 :         if (e.test(begin >= rule_map_end || end > rule_map_end || begin > end, E_BADRULEMAPPING))
     356             :         {
     357           0 :             face.error_context((face.error_context() & 0xFFFF00) + EC_ARULEMAP + (n << 24));
     358           0 :             return face.error(e);
     359             :         }
     360           0 :         s->rules = begin;
     361           0 :         s->rules_end = (end - begin <= FiniteStateMachine::MAX_RULES)? end :
     362             :             begin + FiniteStateMachine::MAX_RULES;
     363           0 :         if (begin)      // keep UBSan happy can't call qsort with null begin
     364           0 :             qsort(begin, end - begin, sizeof(RuleEntry), &cmpRuleEntry);
     365             :     }
     366             : 
     367           0 :     return true;
     368             : }
     369             : 
     370           0 : bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e)
     371             : {
     372           0 :     m_cols = gralloc<uint16>(m_numGlyphs);
     373           0 :     if (e.test(!m_cols, E_OUTOFMEM)) return false;
     374           0 :     memset(m_cols, 0xFF, m_numGlyphs * sizeof(uint16));
     375           0 :     for (size_t n = num_ranges; n; --n)
     376             :     {
     377           0 :         uint16     * ci     = m_cols + be::read<uint16>(ranges),
     378           0 :                    * ci_end = m_cols + be::read<uint16>(ranges) + 1,
     379           0 :                      col    = be::read<uint16>(ranges);
     380             : 
     381           0 :         if (e.test(ci >= ci_end || ci_end > m_cols+m_numGlyphs || col >= m_numColumns, E_BADRANGE))
     382           0 :             return false;
     383             : 
     384             :         // A glyph must only belong to one column at a time
     385           0 :         while (ci != ci_end && *ci == 0xffff)
     386           0 :             *ci++ = col;
     387             : 
     388           0 :         if (e.test(ci != ci_end, E_BADRANGE))
     389           0 :             return false;
     390             :     }
     391           0 :     return true;
     392             : }
     393             : 
     394             : 
     395           0 : bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm, bool reverse) const
     396             : {
     397           0 :     Slot *s = m.slotMap().segment.first();
     398           0 :     if (!s || !testPassConstraint(m)) return true;
     399           0 :     if (reverse)
     400             :     {
     401           0 :         m.slotMap().segment.reverseSlots();
     402           0 :         s = m.slotMap().segment.first();
     403             :     }
     404           0 :     if (m_numRules)
     405             :     {
     406           0 :         Slot *currHigh = s->next();
     407             : 
     408             : #if !defined GRAPHITE2_NTRACING
     409             :         if (fsm.dbgout)  *fsm.dbgout << "rules" << json::array;
     410             :         json::closer rules_array_closer(fsm.dbgout);
     411             : #endif
     412             : 
     413           0 :         m.slotMap().highwater(currHigh);
     414           0 :         int lc = m_iMaxLoop;
     415           0 :         do
     416             :         {
     417           0 :             findNDoRule(s, m, fsm);
     418           0 :             if (m.status() != Machine::finished) return false;
     419           0 :             if (s && (s == m.slotMap().highwater() || m.slotMap().highpassed() || --lc == 0)) {
     420           0 :                 if (!lc)
     421           0 :                     s = m.slotMap().highwater();
     422           0 :                 lc = m_iMaxLoop;
     423           0 :                 if (s)
     424           0 :                     m.slotMap().highwater(s->next());
     425             :             }
     426           0 :         } while (s);
     427             :     }
     428             :     //TODO: Use enums for flags
     429           0 :     const bool collisions = m_numCollRuns || m_kernColls;
     430             : 
     431           0 :     if (!collisions || !m.slotMap().segment.hasCollisionInfo())
     432           0 :         return true;
     433             : 
     434           0 :     if (m_numCollRuns)
     435             :     {
     436           0 :         if (!(m.slotMap().segment.flags() & Segment::SEG_INITCOLLISIONS))
     437             :         {
     438           0 :             m.slotMap().segment.positionSlots(0, 0, 0, m.slotMap().dir(), true);
     439             : //            m.slotMap().segment.flags(m.slotMap().segment.flags() | Segment::SEG_INITCOLLISIONS);
     440             :         }
     441           0 :         if (!collisionShift(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout))
     442           0 :             return false;
     443             :     }
     444           0 :     if ((m_kernColls) && !collisionKern(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout))
     445           0 :         return false;
     446           0 :     if (collisions && !collisionFinish(&m.slotMap().segment, fsm.dbgout))
     447           0 :         return false;
     448           0 :     return true;
     449             : }
     450             : 
     451           0 : bool Pass::runFSM(FiniteStateMachine& fsm, Slot * slot) const
     452             : {
     453           0 :     fsm.reset(slot, m_maxPreCtxt);
     454           0 :     if (fsm.slots.context() < m_minPreCtxt)
     455           0 :         return false;
     456             : 
     457           0 :     uint16 state = m_startStates[m_maxPreCtxt - fsm.slots.context()];
     458           0 :     uint8  free_slots = SlotMap::MAX_SLOTS;
     459           0 :     do
     460             :     {
     461           0 :         fsm.slots.pushSlot(slot);
     462           0 :         if (slot->gid() >= m_numGlyphs
     463           0 :          || m_cols[slot->gid()] == 0xffffU
     464           0 :          || --free_slots == 0
     465           0 :          || state >= m_numTransition)
     466           0 :             return free_slots != 0;
     467             : 
     468           0 :         const uint16 * transitions = m_transitions + state*m_numColumns;
     469           0 :         state = transitions[m_cols[slot->gid()]];
     470           0 :         if (state >= m_successStart)
     471           0 :             fsm.rules.accumulate_rules(m_states[state]);
     472             : 
     473           0 :         slot = slot->next();
     474           0 :     } while (state != 0 && slot);
     475             : 
     476           0 :     fsm.slots.pushSlot(slot);
     477           0 :     return true;
     478             : }
     479             : 
     480             : #if !defined GRAPHITE2_NTRACING
     481             : 
     482             : inline
     483             : Slot * input_slot(const SlotMap &  slots, const int n)
     484             : {
     485             :     Slot * s = slots[slots.context() + n];
     486             :     if (!s->isCopied())     return s;
     487             : 
     488             :     return s->prev() ? s->prev()->next() : (s->next() ? s->next()->prev() : slots.segment.last());
     489             : }
     490             : 
     491             : inline
     492             : Slot * output_slot(const SlotMap &  slots, const int n)
     493             : {
     494             :     Slot * s = slots[slots.context() + n - 1];
     495             :     return s ? s->next() : slots.segment.first();
     496             : }
     497             : 
     498             : #endif //!defined GRAPHITE2_NTRACING
     499             : 
     500           0 : void Pass::findNDoRule(Slot * & slot, Machine &m, FiniteStateMachine & fsm) const
     501             : {
     502           0 :     assert(slot);
     503             : 
     504           0 :     if (runFSM(fsm, slot))
     505             :     {
     506             :         // Search for the first rule which passes the constraint
     507           0 :         const RuleEntry *        r = fsm.rules.begin(),
     508           0 :                         * const re = fsm.rules.end();
     509           0 :         while (r != re && !testConstraint(*r->rule, m))
     510             :         {
     511           0 :             ++r;
     512           0 :             if (m.status() != Machine::finished)
     513           0 :                 return;
     514             :         }
     515             : 
     516             : #if !defined GRAPHITE2_NTRACING
     517             :         if (fsm.dbgout)
     518             :         {
     519             :             if (fsm.rules.size() != 0)
     520             :             {
     521             :                 *fsm.dbgout << json::item << json::object;
     522             :                 dumpRuleEventConsidered(fsm, *r);
     523             :                 if (r != re)
     524             :                 {
     525             :                     const int adv = doAction(r->rule->action, slot, m);
     526             :                     dumpRuleEventOutput(fsm, *r->rule, slot);
     527             :                     if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
     528             :                     adjustSlot(adv, slot, fsm.slots);
     529             :                     *fsm.dbgout << "cursor" << objectid(dslot(&fsm.slots.segment, slot))
     530             :                             << json::close; // Close RuelEvent object
     531             : 
     532             :                     return;
     533             :                 }
     534             :                 else
     535             :                 {
     536             :                     *fsm.dbgout << json::close  // close "considered" array
     537             :                             << "output" << json::null
     538             :                             << "cursor" << objectid(dslot(&fsm.slots.segment, slot->next()))
     539             :                             << json::close;
     540             :                 }
     541             :             }
     542             :         }
     543             :         else
     544             : #endif
     545             :         {
     546           0 :             if (r != re)
     547             :             {
     548           0 :                 const int adv = doAction(r->rule->action, slot, m);
     549           0 :                 if (m.status() != Machine::finished) return;
     550           0 :                 if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
     551           0 :                 adjustSlot(adv, slot, fsm.slots);
     552           0 :                 return;
     553             :             }
     554             :         }
     555             :     }
     556             : 
     557           0 :     slot = slot->next();
     558           0 :     return;
     559             : }
     560             : 
     561             : #if !defined GRAPHITE2_NTRACING
     562             : 
     563             : void Pass::dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const
     564             : {
     565             :     *fsm.dbgout << "considered" << json::array;
     566             :     for (const RuleEntry *r = fsm.rules.begin(); r != &re; ++r)
     567             :     {
     568             :         if (r->rule->preContext > fsm.slots.context())
     569             :             continue;
     570             :         *fsm.dbgout << json::flat << json::object
     571             :                     << "id" << r->rule - m_rules
     572             :                     << "failed" << true
     573             :                     << "input" << json::flat << json::object
     574             :                         << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, -r->rule->preContext)))
     575             :                         << "length" << r->rule->sort
     576             :                         << json::close  // close "input"
     577             :                     << json::close; // close Rule object
     578             :     }
     579             : }
     580             : 
     581             : 
     582             : void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * const last_slot) const
     583             : {
     584             :     *fsm.dbgout     << json::item << json::flat << json::object
     585             :                         << "id"     << &r - m_rules
     586             :                         << "failed" << false
     587             :                         << "input" << json::flat << json::object
     588             :                             << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0)))
     589             :                             << "length" << r.sort - r.preContext
     590             :                             << json::close // close "input"
     591             :                         << json::close  // close Rule object
     592             :                 << json::close // close considered array
     593             :                 << "output" << json::object
     594             :                     << "range" << json::flat << json::object
     595             :                         << "start"  << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0)))
     596             :                         << "end"    << objectid(dslot(&fsm.slots.segment, last_slot))
     597             :                     << json::close // close "input"
     598             :                     << "slots"  << json::array;
     599             :     const Position rsb_prepos = last_slot ? last_slot->origin() : fsm.slots.segment.advance();
     600             :     fsm.slots.segment.positionSlots(0, 0, 0, fsm.slots.segment.currdir());
     601             : 
     602             :     for(Slot * slot = output_slot(fsm.slots, 0); slot != last_slot; slot = slot->next())
     603             :         *fsm.dbgout     << dslot(&fsm.slots.segment, slot);
     604             :     *fsm.dbgout         << json::close  // close "slots"
     605             :                     << "postshift"  << (last_slot ? last_slot->origin() : fsm.slots.segment.advance()) - rsb_prepos
     606             :                 << json::close;         // close "output" object
     607             : 
     608             : }
     609             : 
     610             : #endif
     611             : 
     612             : 
     613             : inline
     614           0 : bool Pass::testPassConstraint(Machine & m) const
     615             : {
     616           0 :     if (!m_cPConstraint) return true;
     617             : 
     618           0 :     assert(m_cPConstraint.constraint());
     619             : 
     620           0 :     m.slotMap().reset(*m.slotMap().segment.first(), 0);
     621           0 :     m.slotMap().pushSlot(m.slotMap().segment.first());
     622           0 :     vm::slotref * map = m.slotMap().begin();
     623           0 :     const uint32 ret = m_cPConstraint.run(m, map);
     624             : 
     625             : #if !defined GRAPHITE2_NTRACING
     626             :     json * const dbgout = m.slotMap().segment.getFace()->logger();
     627             :     if (dbgout)
     628             :         *dbgout << "constraint" << (ret && m.status() == Machine::finished);
     629             : #endif
     630             : 
     631           0 :     return ret && m.status() == Machine::finished;
     632             : }
     633             : 
     634             : 
     635           0 : bool Pass::testConstraint(const Rule & r, Machine & m) const
     636             : {
     637           0 :     const uint16 curr_context = m.slotMap().context();
     638           0 :     if (unsigned(r.sort + curr_context - r.preContext) > m.slotMap().size()
     639           0 :         || curr_context - r.preContext < 0) return false;
     640             : 
     641           0 :     vm::slotref * map = m.slotMap().begin() + curr_context - r.preContext;
     642           0 :     if (map[r.sort - 1] == 0)
     643           0 :         return false;
     644             : 
     645           0 :     if (!*r.constraint) return true;
     646           0 :     assert(r.constraint->constraint());
     647           0 :     for (int n = r.sort; n && map; --n, ++map)
     648             :     {
     649           0 :         if (!*map) continue;
     650           0 :         const int32 ret = r.constraint->run(m, map);
     651           0 :         if (!ret || m.status() != Machine::finished)
     652           0 :             return false;
     653             :     }
     654             : 
     655           0 :     return true;
     656             : }
     657             : 
     658             : 
     659           0 : void SlotMap::collectGarbage(Slot * &aSlot)
     660             : {
     661           0 :     for(Slot **s = begin(), *const *const se = end() - 1; s != se; ++s) {
     662           0 :         Slot *& slot = *s;
     663           0 :         if(slot && (slot->isDeleted() || slot->isCopied()))
     664             :         {
     665           0 :             if (slot == aSlot)
     666           0 :                 aSlot = slot->prev() ? slot->prev() : slot->next();
     667           0 :             segment.freeSlot(slot);
     668             :         }
     669             :     }
     670           0 : }
     671             : 
     672             : 
     673             : 
     674           0 : int Pass::doAction(const Code *codeptr, Slot * & slot_out, vm::Machine & m) const
     675             : {
     676           0 :     assert(codeptr);
     677           0 :     if (!*codeptr) return 0;
     678           0 :     SlotMap   & smap = m.slotMap();
     679           0 :     vm::slotref * map = &smap[smap.context()];
     680           0 :     smap.highpassed(false);
     681             : 
     682           0 :     int32 ret = codeptr->run(m, map);
     683             : 
     684           0 :     if (m.status() != Machine::finished)
     685             :     {
     686           0 :         slot_out = NULL;
     687           0 :         smap.highwater(0);
     688           0 :         return 0;
     689             :     }
     690             : 
     691           0 :     slot_out = *map;
     692           0 :     return ret;
     693             : }
     694             : 
     695             : 
     696           0 : void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const
     697             : {
     698           0 :     if (!slot_out)
     699             :     {
     700           0 :         if (smap.highpassed() || slot_out == smap.highwater())
     701             :         {
     702           0 :             slot_out = smap.segment.last();
     703           0 :             ++delta;
     704           0 :             if (!smap.highwater())
     705           0 :                 smap.highpassed(false);
     706             :         }
     707             :         else
     708             :         {
     709           0 :             slot_out = smap.segment.first();
     710           0 :             --delta;
     711             :         }
     712             :     }
     713           0 :     if (delta < 0)
     714             :     {
     715           0 :         while (++delta <= 0 && slot_out)
     716             :         {
     717           0 :             if (smap.highpassed() && smap.highwater() == slot_out)
     718           0 :                 smap.highpassed(false);
     719           0 :             slot_out = slot_out->prev();
     720             :         }
     721             :     }
     722           0 :     else if (delta > 0)
     723             :     {
     724           0 :         while (--delta >= 0 && slot_out)
     725             :         {
     726           0 :             slot_out = slot_out->next();
     727           0 :             if (slot_out == smap.highwater() && slot_out)
     728           0 :                 smap.highpassed(true);
     729             :         }
     730             :     }
     731           0 : }
     732             : 
     733           0 : bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const
     734             : {
     735           0 :     ShiftCollider shiftcoll(dbgout);
     736             :     // bool isfirst = true;
     737           0 :     bool hasCollisions = false;
     738           0 :     Slot *start = seg->first();      // turn on collision fixing for the first slot
     739           0 :     Slot *end = NULL;
     740           0 :     bool moved = false;
     741             : 
     742             : #if !defined GRAPHITE2_NTRACING
     743             :     if (dbgout)
     744             :         *dbgout << "collisions" << json::array
     745             :             << json::flat << json::object << "num-loops" << m_numCollRuns << json::close;
     746             : #endif
     747             : 
     748           0 :     while (start)
     749             :     {
     750             : #if !defined GRAPHITE2_NTRACING
     751             :         if (dbgout)  *dbgout << json::object << "phase" << "1" << "moves" << json::array;
     752             : #endif
     753           0 :         hasCollisions = false;
     754           0 :         end = NULL;
     755             :         // phase 1 : position shiftable glyphs, ignoring kernable glyphs
     756           0 :         for (Slot *s = start; s; s = s->next())
     757             :         {
     758           0 :             const SlotCollision * c = seg->collisionInfo(s);
     759           0 :             if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
     760           0 :                       && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
     761           0 :                 return false;
     762           0 :             if (s != start && (c->flags() & SlotCollision::COLL_END))
     763             :             {
     764           0 :                 end = s->next();
     765           0 :                 break;
     766             :             }
     767             :         }
     768             : 
     769             : #if !defined GRAPHITE2_NTRACING
     770             :         if (dbgout)
     771             :             *dbgout << json::close << json::close; // phase-1
     772             : #endif
     773             : 
     774             :         // phase 2 : loop until happy. 
     775           0 :         for (int i = 0; i < m_numCollRuns - 1; ++i)
     776             :         {
     777           0 :             if (hasCollisions || moved)
     778             :             {
     779             : 
     780             : #if !defined GRAPHITE2_NTRACING
     781             :                 if (dbgout)
     782             :                     *dbgout << json::object << "phase" << "2a" << "loop" << i << "moves" << json::array;
     783             : #endif
     784             :                 // phase 2a : if any shiftable glyphs are in collision, iterate backwards,
     785             :                 // fixing them and ignoring other non-collided glyphs. Note that this handles ONLY
     786             :                 // glyphs that are actually in collision from phases 1 or 2b, and working backwards
     787             :                 // has the intended effect of breaking logjams.
     788           0 :                 if (hasCollisions)
     789             :                 {
     790           0 :                     hasCollisions = false;
     791             :                     #if 0
     792             :                     moved = true;
     793             :                     for (Slot *s = start; s != end; s = s->next())
     794             :                     {
     795             :                         SlotCollision * c = seg->collisionInfo(s);
     796             :                         c->setShift(Position(0, 0));
     797             :                     }
     798             :                     #endif
     799           0 :                     Slot *lend = end ? end->prev() : seg->last();
     800           0 :                     Slot *lstart = start->prev();
     801           0 :                     for (Slot *s = lend; s != lstart; s = s->prev())
     802             :                     {
     803           0 :                         SlotCollision * c = seg->collisionInfo(s);
     804           0 :                         if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN | SlotCollision::COLL_ISCOL))
     805             :                                         == (SlotCollision::COLL_FIX | SlotCollision::COLL_ISCOL)) // ONLY if this glyph is still colliding
     806             :                         {
     807           0 :                             if (!resolveCollisions(seg, s, lend, shiftcoll, true, dir, moved, hasCollisions, dbgout))
     808           0 :                                 return false;
     809           0 :                             c->setFlags(c->flags() | SlotCollision::COLL_TEMPLOCK);
     810             :                         }
     811             :                     }
     812             :                 }
     813             : 
     814             : #if !defined GRAPHITE2_NTRACING
     815             :                 if (dbgout)
     816             :                     *dbgout << json::close << json::close // phase 2a
     817             :                         << json::object << "phase" << "2b" << "loop" << i << "moves" << json::array;
     818             : #endif
     819             : 
     820             :                 // phase 2b : redo basic diacritic positioning pass for ALL glyphs. Each successive loop adjusts 
     821             :                 // glyphs from their current adjusted position, which has the effect of gradually minimizing the  
     822             :                 // resulting adjustment; ie, the final result will be gradually closer to the original location.  
     823             :                 // Also it allows more flexibility in the final adjustment, since it is moving along the  
     824             :                 // possible 8 vectors from successively different starting locations.
     825           0 :                 if (moved)
     826             :                 {
     827           0 :                     moved = false;
     828           0 :                     for (Slot *s = start; s != end; s = s->next())
     829             :                     {
     830           0 :                         SlotCollision * c = seg->collisionInfo(s);
     831           0 :                         if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_TEMPLOCK
     832             :                                                         | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
     833           0 :                                   && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
     834           0 :                             return false;
     835           0 :                         else if (c->flags() & SlotCollision::COLL_TEMPLOCK)
     836           0 :                             c->setFlags(c->flags() & ~SlotCollision::COLL_TEMPLOCK);
     837             :                     }
     838             :                 }
     839             :         //      if (!hasCollisions) // no, don't leave yet because phase 2b will continue to improve things
     840             :         //          break;
     841             : #if !defined GRAPHITE2_NTRACING
     842             :                 if (dbgout)
     843             :                     *dbgout << json::close << json::close; // phase 2
     844             : #endif
     845             :             }
     846             :         }
     847           0 :         if (!end)
     848           0 :             break;
     849           0 :         start = NULL;
     850           0 :         for (Slot *s = end->prev(); s; s = s->next())
     851             :         {
     852           0 :             if (seg->collisionInfo(s)->flags() & SlotCollision::COLL_START)
     853             :             {
     854           0 :                 start = s;
     855           0 :                 break;
     856             :             }
     857             :         }
     858             :     }
     859           0 :     return true;
     860             : }
     861             : 
     862           0 : bool Pass::collisionKern(Segment *seg, int dir, json * const dbgout) const
     863             : {
     864           0 :     Slot *start = seg->first();
     865           0 :     float ymin = 1e38f;
     866           0 :     float ymax = -1e38f;
     867           0 :     const GlyphCache &gc = seg->getFace()->glyphs();
     868             : 
     869             :     // phase 3 : handle kerning of clusters
     870             : #if !defined GRAPHITE2_NTRACING
     871             :     if (dbgout)
     872             :         *dbgout << json::object << "phase" << "3" << "moves" << json::array;
     873             : #endif
     874             : 
     875           0 :     for (Slot *s = seg->first(); s; s = s->next())
     876             :     {
     877           0 :         if (!gc.check(s->gid()))
     878           0 :             return false;
     879           0 :         const SlotCollision * c = seg->collisionInfo(s);
     880           0 :         const Rect &bbox = seg->theGlyphBBoxTemporary(s->gid());
     881           0 :         float y = s->origin().y + c->shift().y;
     882           0 :         if (!(c->flags() & SlotCollision::COLL_ISSPACE))
     883             :         {
     884           0 :             ymax = max(y + bbox.tr.y, ymax);
     885           0 :             ymin = min(y + bbox.bl.y, ymin);
     886             :         }
     887           0 :         if (start && (c->flags() & (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX))
     888             :                         == (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX))
     889           0 :             resolveKern(seg, s, start, dir, ymin, ymax, dbgout);
     890           0 :         if (c->flags() & SlotCollision::COLL_END)
     891           0 :             start = NULL;
     892           0 :         if (c->flags() & SlotCollision::COLL_START)
     893           0 :             start = s;
     894             :     }
     895             : 
     896             : #if !defined GRAPHITE2_NTRACING
     897             :     if (dbgout)
     898             :         *dbgout << json::close << json::close; // phase 3
     899             : #endif
     900           0 :     return true;
     901             : }
     902             : 
     903           0 : bool Pass::collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const
     904             : {
     905           0 :     for (Slot *s = seg->first(); s; s = s->next())
     906             :     {
     907           0 :         SlotCollision *c = seg->collisionInfo(s);
     908           0 :         if (c->shift().x != 0 || c->shift().y != 0)
     909             :         {
     910           0 :             const Position newOffset = c->shift();
     911           0 :             const Position nullPosition(0, 0);
     912           0 :             c->setOffset(newOffset + c->offset());
     913           0 :             c->setShift(nullPosition);
     914             :         }
     915             :     }
     916             : //    seg->positionSlots();
     917             : 
     918             : #if !defined GRAPHITE2_NTRACING
     919             :         if (dbgout)
     920             :             *dbgout << json::close;
     921             : #endif
     922           0 :     return true;
     923             : }
     924             : 
     925             : // Can slot s be kerned, or is it attached to something that can be kerned?
     926           0 : static bool inKernCluster(Segment *seg, Slot *s)
     927             : {
     928           0 :     SlotCollision *c = seg->collisionInfo(s);
     929           0 :     if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ )
     930           0 :         return true;
     931           0 :     while (s->attachedTo())
     932             :     {
     933           0 :         s = s->attachedTo();
     934           0 :         c = seg->collisionInfo(s);
     935           0 :         if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ )
     936           0 :             return true;
     937             :     }
     938           0 :     return false;
     939             : }
     940             : 
     941             : // Fix collisions for the given slot.
     942             : // Return true if everything was fixed, false if there are still collisions remaining.
     943             : // isRev means be we are processing backwards.
     944           0 : bool Pass::resolveCollisions(Segment *seg, Slot *slotFix, Slot *start,
     945             :         ShiftCollider &coll, GR_MAYBE_UNUSED bool isRev, int dir, bool &moved, bool &hasCol,
     946             :         json * const dbgout) const
     947             : {
     948             :     Slot * nbor;  // neighboring slot
     949           0 :     SlotCollision *cFix = seg->collisionInfo(slotFix);
     950           0 :     if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(), cFix->marginWt(),
     951             :             cFix->shift(), cFix->offset(), dir, dbgout))
     952           0 :         return false;
     953           0 :     bool collides = false;
     954             :     // When we're processing forward, ignore kernable glyphs that preceed the target glyph.
     955             :     // When processing backward, don't ignore these until we pass slotFix.
     956           0 :     bool ignoreForKern = !isRev;
     957           0 :     bool rtl = dir & 1;
     958           0 :     Slot *base = slotFix;
     959           0 :     while (base->attachedTo())
     960           0 :         base = base->attachedTo();
     961           0 :     Position zero(0., 0.);
     962             :     
     963             :     // Look for collisions with the neighboring glyphs.
     964           0 :     for (nbor = start; nbor; nbor = isRev ? nbor->prev() : nbor->next())
     965             :     {
     966           0 :         SlotCollision *cNbor = seg->collisionInfo(nbor);
     967           0 :         bool sameCluster = nbor->isChildOf(base);
     968           0 :         if (nbor != slotFix                                                     // don't process if this is the slot of interest
     969           0 :                       && !(cNbor->ignore())                                  // don't process if ignoring
     970           0 :                       && (nbor == base || sameCluster       // process if in the same cluster as slotFix
     971           0 :                             || !inKernCluster(seg, nbor)    // or this cluster is not to be kerned
     972           0 :                             || (rtl ^ ignoreForKern))       // or it comes before(ltr) or after(rtl)
     973           0 :                       && (!isRev    // if processing forwards then good to merge otherwise only:
     974           0 :                             || !(cNbor->flags() & SlotCollision::COLL_FIX)     // merge in immovable stuff
     975           0 :                             || ((cNbor->flags() & SlotCollision::COLL_KERN) && !sameCluster)     // ignore other kernable clusters
     976           0 :                             || (cNbor->flags() & SlotCollision::COLL_ISCOL))   // test against other collided glyphs
     977           0 :                       && !coll.mergeSlot(seg, nbor, cNbor, cNbor->shift(), !ignoreForKern, sameCluster, collides, false, dbgout))
     978           0 :             return false;
     979           0 :         else if (nbor == slotFix)
     980             :             // Switching sides of this glyph - if we were ignoring kernable stuff before, don't anymore.
     981           0 :             ignoreForKern = !ignoreForKern;
     982             :             
     983           0 :         if (nbor != start && (cNbor->flags() & (isRev ? SlotCollision::COLL_START : SlotCollision::COLL_END)))
     984           0 :             break;
     985             :     }
     986           0 :     bool isCol = false;
     987           0 :     if (collides || cFix->shift().x != 0.f || cFix->shift().y != 0.f)
     988             :     {
     989           0 :         Position shift = coll.resolve(seg, isCol, dbgout);
     990             :         // isCol has been set to true if a collision remains.
     991           0 :         if (std::fabs(shift.x) < 1e38f && std::fabs(shift.y) < 1e38f)
     992             :         {
     993           0 :             if (sqr(shift.x-cFix->shift().x) + sqr(shift.y-cFix->shift().y) >= m_colThreshold * m_colThreshold)
     994           0 :                 moved = true;
     995           0 :             cFix->setShift(shift);
     996           0 :             if (slotFix->firstChild())
     997             :             {
     998           0 :                 Rect bbox;
     999           0 :                 Position here = slotFix->origin() + shift;
    1000           0 :                 float clusterMin = here.x;
    1001           0 :                 slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, rtl, false);
    1002             :             }
    1003             :         }
    1004             :     }
    1005             :     else
    1006             :     {
    1007             :         // This glyph is not colliding with anything.
    1008             : #if !defined GRAPHITE2_NTRACING
    1009             :         if (dbgout)
    1010             :         {
    1011             :             *dbgout << json::object 
    1012             :                             << "missed" << objectid(dslot(seg, slotFix));
    1013             :             coll.outputJsonDbg(dbgout, seg, -1);
    1014             :             *dbgout << json::close;
    1015             :         }
    1016             : #endif
    1017             :     }
    1018             : 
    1019             :     // Set the is-collision flag bit.
    1020           0 :     if (isCol)
    1021           0 :     { cFix->setFlags(cFix->flags() | SlotCollision::COLL_ISCOL | SlotCollision::COLL_KNOWN); }
    1022             :     else
    1023           0 :     { cFix->setFlags((cFix->flags() & ~SlotCollision::COLL_ISCOL) | SlotCollision::COLL_KNOWN); }
    1024           0 :     hasCol |= isCol;
    1025           0 :     return true;
    1026             : }
    1027             : 
    1028           0 : float Pass::resolveKern(Segment *seg, Slot *slotFix, GR_MAYBE_UNUSED Slot *start, int dir,
    1029             :     float &ymin, float &ymax, json *const dbgout) const
    1030             : {
    1031             :     Slot *nbor; // neighboring slot
    1032           0 :     float currSpace = 0.;
    1033           0 :     bool collides = false;
    1034           0 :     unsigned int space_count = 0;
    1035           0 :     Slot *base = slotFix;
    1036           0 :     while (base->attachedTo())
    1037           0 :         base = base->attachedTo();
    1038           0 :     SlotCollision *cFix = seg->collisionInfo(base);
    1039           0 :     const GlyphCache &gc = seg->getFace()->glyphs();
    1040             : 
    1041           0 :     if (base != slotFix)
    1042             :     {
    1043           0 :         cFix->setFlags(cFix->flags() | SlotCollision::COLL_KERN | SlotCollision::COLL_FIX);
    1044           0 :         return 0;
    1045             :     }
    1046           0 :     bool seenEnd = (cFix->flags() & SlotCollision::COLL_END) != 0;
    1047           0 :     bool isInit = false;
    1048           0 :     KernCollider coll(dbgout);
    1049             : 
    1050           0 :     for (nbor = slotFix->next(); nbor; nbor = nbor->next())
    1051             :     {
    1052           0 :         if (nbor->isChildOf(base))
    1053           0 :             continue;
    1054           0 :         if (!gc.check(nbor->gid()))
    1055           0 :             return 0.;
    1056           0 :         const Rect &bb = seg->theGlyphBBoxTemporary(nbor->gid());
    1057           0 :         SlotCollision *cNbor = seg->collisionInfo(nbor);
    1058           0 :         if ((bb.bl.y == 0.f && bb.tr.y == 0.f) || (cNbor->flags() & SlotCollision::COLL_ISSPACE))
    1059             :         {
    1060           0 :             if (m_kernColls == InWord)
    1061           0 :                 break;
    1062             :             // Add space for a space glyph.
    1063           0 :             currSpace += nbor->advance();
    1064           0 :             ++space_count;
    1065             :         }
    1066             :         else
    1067             :         {
    1068           0 :             space_count = 0; 
    1069           0 :             float y = nbor->origin().y + cNbor->shift().y;
    1070           0 :             ymax = max(y + bb.tr.y, ymax);
    1071           0 :             ymin = min(y + bb.bl.y, ymin);
    1072           0 :             if (nbor != slotFix && !cNbor->ignore())
    1073             :             {
    1074           0 :                 seenEnd = true;
    1075           0 :                 if (!isInit)
    1076             :                 {
    1077           0 :                     if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(),
    1078             :                                     cFix->shift(), cFix->offset(), dir, ymin, ymax, dbgout))
    1079           0 :                         return 0.;
    1080           0 :                     isInit = true;
    1081             :                 }
    1082           0 :                 collides |= coll.mergeSlot(seg, nbor, cNbor->shift(), currSpace, dir, dbgout);
    1083             :             }
    1084             :         }
    1085           0 :         if (cNbor->flags() & SlotCollision::COLL_END)
    1086             :         {
    1087           0 :             if (seenEnd && space_count < 2)
    1088             :                 break;
    1089             :             else
    1090           0 :                 seenEnd = true;
    1091             :         }
    1092             :     }
    1093           0 :     if (collides)
    1094             :     {
    1095           0 :         Position mv = coll.resolve(seg, slotFix, dir, dbgout);
    1096           0 :         coll.shift(mv, dir);
    1097           0 :         Position delta = slotFix->advancePos() + mv - cFix->shift();
    1098           0 :         slotFix->advance(delta);
    1099           0 :         cFix->setShift(mv);
    1100           0 :         return mv.x;
    1101             :     }
    1102           0 :     return 0.;
    1103             : }
    1104             : 

Generated by: LCOV version 1.13