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