Copyright 2010, SIL International All rights reserved.
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should also have received a copy of the GNU Lesser General Public License along with this library in the file named "LICENSE". If not, write to the Free Software Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA or visit their web page on the internet at http://www.fsf.org/licenses/lgpl.html.
Alternatively, the contents of this file may be used under the terms of the Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public License, as published by the Free Software Foundation, either version 2 of the License or (at your option) any later version.
*/ #include"inc/Main.h" #include"inc/debug.h" #include"inc/Endian.h" #include"inc/Pass.h" #include <cstring> #include <cstdlib> #include <cassert> #include <cmath> #include"inc/Segment.h" #include"inc/Code.h" #include"inc/Rule.h" #include"inc/Error.h" #include"inc/Collider.h"
usingnamespace graphite2; using vm::Machine; typedef Machine::Code Code;
m.slotMap().highwater(currHigh); int lc = m_iMaxLoop; do
{
findNDoRule(s, m, fsm); if (m.status() != Machine::finished) returnfalse; if (s && (s == m.slotMap().highwater() || m.slotMap().highpassed() || --lc == 0)) { if (!lc)
s = m.slotMap().highwater();
lc = m_iMaxLoop; if (s)
m.slotMap().highwater(s->next());
}
} while (s);
} //TODO: Use enums for flags constbool collisions = m_numCollRuns || m_kernColls;
if (!collisions || !m.slotMap().segment.hasCollisionInfo()) returntrue;
if (m_numCollRuns)
{ if (!(m.slotMap().segment.flags() & Segment::SEG_INITCOLLISIONS))
{
m.slotMap().segment.positionSlots(0, 0, 0, m.slotMap().dir(), true); // m.slotMap().segment.flags(m.slotMap().segment.flags() | Segment::SEG_INITCOLLISIONS);
} if (!collisionShift(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout)) returnfalse;
} if ((m_kernColls) && !collisionKern(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout)) returnfalse; if (collisions && !collisionFinish(&m.slotMap().segment, fsm.dbgout)) returnfalse; returntrue;
}
if (runFSM(fsm, slot))
{ // Search for the first rule which passes the constraint const RuleEntry * r = fsm.rules.begin(),
* const re = fsm.rules.end(); while (r != re && !testConstraint(*r->rule, m))
{
++r; if (m.status() != Machine::finished) return;
}
#if !defined GRAPHITE2_NTRACING if (fsm.dbgout)
{ if (fsm.rules.size() != 0)
{
*fsm.dbgout << json::item << json::object;
dumpRuleEventConsidered(fsm, *r); if (r != re)
{ constint adv = doAction(r->rule->action, slot, m);
dumpRuleEventOutput(fsm, *r->rule, slot); if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
adjustSlot(adv, slot, fsm.slots);
*fsm.dbgout << "cursor" << objectid(dslot(&fsm.slots.segment, slot))
<< json::close; // Close RuelEvent object
if (!*r.constraint) returntrue;
assert(r.constraint->constraint()); for (int n = r.sort; n && map; --n, ++map)
{ if (!*map) continue; const int32 ret = r.constraint->run(m, map); if (!ret || m.status() != Machine::finished) returnfalse;
}
// phase 2 : loop until happy. for (int i = 0; i < m_numCollRuns - 1; ++i)
{ if (hasCollisions || moved)
{
#if !defined GRAPHITE2_NTRACING if (dbgout)
*dbgout << json::object << "phase" << "2a" << "loop" << i << "moves" << json::array; #endif // phase 2a : if any shiftable glyphs are in collision, iterate backwards, // fixing them and ignoring other non-collided glyphs. Note that this handles ONLY // glyphs that are actually in collision from phases 1 or 2b, and working backwards // has the intended effect of breaking logjams. if (hasCollisions)
{
hasCollisions = false; #if 0
moved = true; for (Slot *s = start; s != end; s = s->next())
{
SlotCollision * c = seg->collisionInfo(s);
c->setShift(Position(0, 0));
} #endif
Slot *lend = end ? end->prev() : seg->last();
Slot *lstart = start->prev(); for (Slot *s = lend; s != lstart; s = s->prev())
{
SlotCollision * c = seg->collisionInfo(s); if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN | SlotCollision::COLL_ISCOL))
== (SlotCollision::COLL_FIX | SlotCollision::COLL_ISCOL)) // ONLY if this glyph is still colliding
{ if (!resolveCollisions(seg, s, lend, shiftcoll, true, dir, moved, hasCollisions, dbgout)) returnfalse;
c->setFlags(c->flags() | SlotCollision::COLL_TEMPLOCK);
}
}
}
// phase 2b : redo basic diacritic positioning pass for ALL glyphs. Each successive loop adjusts // glyphs from their current adjusted position, which has the effect of gradually minimizing the // resulting adjustment; ie, the final result will be gradually closer to the original location. // Also it allows more flexibility in the final adjustment, since it is moving along the // possible 8 vectors from successively different starting locations. if (moved)
{
moved = false; for (Slot *s = start; s != end; s = s->next())
{
SlotCollision * c = seg->collisionInfo(s); if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_TEMPLOCK
| SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
&& !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout)) returnfalse; elseif (c->flags() & SlotCollision::COLL_TEMPLOCK)
c->setFlags(c->flags() & ~SlotCollision::COLL_TEMPLOCK);
}
} // if (!hasCollisions) // no, don't leave yet because phase 2b will continue to improve things // break; #if !defined GRAPHITE2_NTRACING if (dbgout)
*dbgout << json::close << json::close; // phase 2 #endif
}
} if (!end) break;
start = NULL; for (Slot *s = end->prev(); s; s = s->next())
{ if (seg->collisionInfo(s)->flags() & SlotCollision::COLL_START)
{
start = s; break;
}
}
} returntrue;
}
// Can slot s be kerned, or is it attached to something that can be kerned? staticbool inKernCluster(Segment *seg, Slot *s)
{
SlotCollision *c = seg->collisionInfo(s); if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ ) returntrue; while (s->attachedTo())
{
s = s->attachedTo();
c = seg->collisionInfo(s); if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ ) returntrue;
} returnfalse;
}
// Fix collisions for the given slot. // Return true if everything was fixed, false if there are still collisions remaining. // isRev means be we are processing backwards. bool Pass::resolveCollisions(Segment *seg, Slot *slotFix, Slot *start,
ShiftCollider &coll, GR_MAYBE_UNUSED bool isRev, int dir, bool &moved, bool &hasCol,
json * const dbgout) const
{
Slot * nbor; // neighboring slot
SlotCollision *cFix = seg->collisionInfo(slotFix); if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(), cFix->marginWt(),
cFix->shift(), cFix->offset(), dir, dbgout)) returnfalse; bool collides = false; // When we're processing forward, ignore kernable glyphs that preceed the target glyph. // When processing backward, don't ignore these until we pass slotFix. bool ignoreForKern = !isRev; bool rtl = dir & 1;
Slot *base = slotFix; while (base->attachedTo())
base = base->attachedTo();
Position zero(0., 0.);
// Look for collisions with the neighboring glyphs. for (nbor = start; nbor; nbor = isRev ? nbor->prev() : nbor->next())
{
SlotCollision *cNbor = seg->collisionInfo(nbor); bool sameCluster = nbor->isChildOf(base); if (nbor != slotFix // don't process if this is the slot of interest
&& !(cNbor->ignore()) // don't process if ignoring
&& (nbor == base || sameCluster // process if in the same cluster as slotFix
|| !inKernCluster(seg, nbor)) // or this cluster is not to be kerned // || (rtl ^ ignoreForKern)) // or it comes before(ltr) or after(rtl)
&& (!isRev // if processing forwards then good to merge otherwise only:
|| !(cNbor->flags() & SlotCollision::COLL_FIX) // merge in immovable stuff
|| ((cNbor->flags() & SlotCollision::COLL_KERN) && !sameCluster) // ignore other kernable clusters
|| (cNbor->flags() & SlotCollision::COLL_ISCOL)) // test against other collided glyphs
&& !coll.mergeSlot(seg, nbor, cNbor, cNbor->shift(), !ignoreForKern, sameCluster, collides, false, dbgout)) returnfalse; elseif (nbor == slotFix) // Switching sides of this glyph - if we were ignoring kernable stuff before, don't anymore.
ignoreForKern = !ignoreForKern;
if (nbor != start && (cNbor->flags() & (isRev ? SlotCollision::COLL_START : SlotCollision::COLL_END))) break;
} bool isCol = false; if (collides || cFix->shift().x != 0.f || cFix->shift().y != 0.f)
{
Position shift = coll.resolve(seg, isCol, dbgout); // isCol has been set to true if a collision remains. if (std::fabs(shift.x) < 1e38f && std::fabs(shift.y) < 1e38f)
{ if (sqr(shift.x-cFix->shift().x) + sqr(shift.y-cFix->shift().y) >= m_colThreshold * m_colThreshold)
moved = true;
cFix->setShift(shift); if (slotFix->firstChild())
{
Rect bbox;
Position here = slotFix->origin() + shift; float clusterMin = here.x;
slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, rtl, false);
}
}
} else
{ // This glyph is not colliding with anything. #if !defined GRAPHITE2_NTRACING if (dbgout)
{
*dbgout << json::object
<< "missed" << objectid(dslot(seg, slotFix));
coll.outputJsonDbg(dbgout, seg, -1);
*dbgout << json::close;
} #endif
}
// Set the is-collision flag bit. if (isCol)
{ cFix->setFlags(cFix->flags() | SlotCollision::COLL_ISCOL | SlotCollision::COLL_KNOWN); } else
{ cFix->setFlags((cFix->flags() & ~SlotCollision::COLL_ISCOL) | SlotCollision::COLL_KNOWN); }
hasCol |= isCol; returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.