summaryrefslogtreecommitdiff
path: root/Build/source/libs/graphite2/graphite2-src/src/Collider.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/graphite2/graphite2-src/src/Collider.cpp')
-rw-r--r--Build/source/libs/graphite2/graphite2-src/src/Collider.cpp1115
1 files changed, 0 insertions, 1115 deletions
diff --git a/Build/source/libs/graphite2/graphite2-src/src/Collider.cpp b/Build/source/libs/graphite2/graphite2-src/src/Collider.cpp
deleted file mode 100644
index 1929b39a58e..00000000000
--- a/Build/source/libs/graphite2/graphite2-src/src/Collider.cpp
+++ /dev/null
@@ -1,1115 +0,0 @@
-/* GRAPHITE2 LICENSING
-
- 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 <algorithm>
-#include <limits>
-#include <cmath>
-#include <string>
-#include <functional>
-#include "inc/Collider.h"
-#include "inc/Segment.h"
-#include "inc/Slot.h"
-#include "inc/GlyphCache.h"
-#include "inc/Sparse.h"
-
-#define ISQRT2 0.707106781f
-
-// Possible rounding error for subbox boundaries: 0.016 = 1/64 = 1/256 * 4
-// (values in font range from 0..256)
-// #define SUBBOX_RND_ERR 0.016
-
-using namespace graphite2;
-
-//// SHIFT-COLLIDER ////
-
-// Initialize the Collider to hold the basic movement limits for the
-// target slot, the one we are focusing on fixing.
-bool ShiftCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin, float marginWeight,
- const Position &currShift, const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout)
-{
- int i;
- float mx, mn;
- float a, shift;
- const GlyphCache &gc = seg->getFace()->glyphs();
- unsigned short gid = aSlot->gid();
- if (!gc.check(gid))
- return false;
- const BBox &bb = gc.getBoundingBBox(gid);
- const SlantBox &sb = gc.getBoundingSlantBox(gid);
- //float sx = aSlot->origin().x + currShift.x;
- //float sy = aSlot->origin().y + currShift.y;
- if (currOffset.x != 0.f || currOffset.y != 0.f)
- _limit = Rect(limit.bl - currOffset, limit.tr - currOffset);
- else
- _limit = limit;
- // For a ShiftCollider, these indices indicate which vector we are moving by:
- // each _ranges represents absolute space with respect to the origin of the slot. Thus take into account true origins but subtract the vmin for the slot
- for (i = 0; i < 4; ++i)
- {
- switch (i) {
- case 0 : // x direction
- mn = _limit.bl.x + currOffset.x;
- mx = _limit.tr.x + currOffset.x;
- _len[i] = bb.xa - bb.xi;
- a = currOffset.y + currShift.y;
- _ranges[i].initialise<XY>(mn, mx, margin, marginWeight, a);
- break;
- case 1 : // y direction
- mn = _limit.bl.y + currOffset.y;
- mx = _limit.tr.y + currOffset.y;
- _len[i] = bb.ya - bb.yi;
- a = currOffset.x + currShift.x;
- _ranges[i].initialise<XY>(mn, mx, margin, marginWeight, a);
- break;
- case 2 : // sum (negatively sloped diagonal boundaries)
- // pick closest x,y limit boundaries in s direction
- shift = currOffset.x + currOffset.y + currShift.x + currShift.y;
- mn = -2 * min(currShift.x - _limit.bl.x, currShift.y - _limit.bl.y) + shift;
- mx = 2 * min(_limit.tr.x - currShift.x, _limit.tr.y - currShift.y) + shift;
- _len[i] = sb.sa - sb.si;
- a = currOffset.x - currOffset.y + currShift.x - currShift.y;
- _ranges[i].initialise<SD>(mn, mx, margin / ISQRT2, marginWeight, a);
- break;
- case 3 : // diff (positively sloped diagonal boundaries)
- // pick closest x,y limit boundaries in d direction
- shift = currOffset.x - currOffset.y + currShift.x - currShift.y;
- mn = -2 * min(currShift.x - _limit.bl.x, _limit.tr.y - currShift.y) + shift;
- mx = 2 * min(_limit.tr.x - currShift.x, currShift.y - _limit.bl.y) + shift;
- _len[i] = sb.da - sb.di;
- a = currOffset.x + currOffset.y + currShift.x + currShift.y;
- _ranges[i].initialise<SD>(mn, mx, margin / ISQRT2, marginWeight, a);
- break;
- }
- }
-
- _target = aSlot;
- if ((dir & 1) == 0)
- {
- // For LTR, switch and negate x limits.
- _limit.bl.x = -1 * limit.tr.x;
- //_limit.tr.x = -1 * limit.bl.x;
- }
- _currOffset = currOffset;
- _currShift = currShift;
- _origin = aSlot->origin() - currOffset; // the original anchor position of the glyph
-
- _margin = margin;
- _marginWt = marginWeight;
-
- SlotCollision *c = seg->collisionInfo(aSlot);
- _seqClass = c->seqClass();
- _seqProxClass = c->seqProxClass();
- _seqOrder = c->seqOrder();
- return true;
-}
-
-template <class O>
-float sdm(float vi, float va, float mx, float my, O op)
-{
- float res = 2 * mx - vi;
- if (op(res, vi + 2 * my))
- {
- res = va + 2 * my;
- if (op(res, 2 * mx - va))
- res = mx + my;
- }
- return res;
-}
-
-// Mark an area with a cost that can vary along the x or y axis. The region is expressed in terms of the centre of the target glyph in each axis
-void ShiftCollider::addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int axis)
-{
- float a, c;
- switch (axis) {
- case 0 :
- if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0)
- {
- a = org.y + 0.5f * (bb.yi + bb.ya);
- c = 0.5f * (bb.xi + bb.xa);
- if (isx)
- _ranges[axis].weighted<XY>(box.bl.x - c, box.tr.x - c, weight, a, m,
- (minright ? box.tr.x : box.bl.x) - c, a, 0, false);
- else
- _ranges[axis].weighted<XY>(box.bl.x - c, box.tr.x - c, weight, a, 0, 0, org.y,
- m * (a * a + sqr((minright ? box.tr.y : box.bl.y) - 0.5f * (bb.yi + bb.ya))), false);
- }
- break;
- case 1 :
- if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0)
- {
- a = org.x + 0.5f * (bb.xi + bb.xa);
- c = 0.5f * (bb.yi + bb.ya);
- if (isx)
- _ranges[axis].weighted<XY>(box.bl.y - c, box.tr.y - c, weight, a, 0, 0, org.x,
- m * (a * a + sqr((minright ? box.tr.x : box.bl.x) - 0.5f * (bb.xi + bb.xa))), false);
- else
- _ranges[axis].weighted<XY>(box.bl.y - c, box.tr.y - c, weight, a, m,
- (minright ? box.tr.y : box.bl.y) - c, a, 0, false);
- }
- break;
- case 2 :
- if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di)
- {
- float d = org.x - org.y + 0.5f * (sb.di + sb.da);
- c = 0.5f * (sb.si + sb.sa);
- float smax = min(2 * box.tr.x - d, 2 * box.tr.y + d);
- float smin = max(2 * box.bl.x - d, 2 * box.bl.y + d);
- if (smin > smax) return;
- float si;
- a = d;
- if (isx)
- si = 2 * (minright ? box.tr.x : box.bl.x) - a;
- else
- si = 2 * (minright ? box.tr.y : box.bl.y) + a;
- _ranges[axis].weighted<SD>(smin - c, smax - c, weight / 2, a, m / 2, si, 0, 0, isx);
- }
- break;
- case 3 :
- if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si)
- {
- float s = org.x + org.y + 0.5f * (sb.si + sb.sa);
- c = 0.5f * (sb.di + sb.da);
- float dmax = min(2 * box.tr.x - s, s - 2 * box.bl.y);
- float dmin = max(2 * box.bl.x - s, s - 2 * box.tr.y);
- if (dmin > dmax) return;
- float di;
- a = s;
- if (isx)
- di = 2 * (minright ? box.tr.x : box.bl.x) - a;
- else
- di = 2 * (minright ? box.tr.y : box.bl.y) + a;
- _ranges[axis].weighted<SD>(dmin - c, dmax - c, weight / 2, a, m / 2, di, 0, 0, !isx);
- }
- break;
- default :
- break;
- }
- return;
-}
-
-// Mark an area with an absolute cost, making it completely inaccessible.
-inline void ShiftCollider::removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int axis)
-{
- float c;
- switch (axis) {
- case 0 :
- if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0)
- {
- c = 0.5f * (bb.xi + bb.xa);
- _ranges[axis].exclude(box.bl.x - c, box.tr.x - c);
- }
- break;
- case 1 :
- if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0)
- {
- c = 0.5f * (bb.yi + bb.ya);
- _ranges[axis].exclude(box.bl.y - c, box.tr.y - c);
- }
- break;
- case 2 :
- if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di
- && box.width() > 0 && box.height() > 0)
- {
- float di = org.x - org.y + sb.di;
- float da = org.x - org.y + sb.da;
- float smax = sdm(di, da, box.tr.x, box.tr.y, std::greater<float>());
- float smin = sdm(da, di, box.bl.x, box.bl.y, std::less<float>());
- c = 0.5f * (sb.si + sb.sa);
- _ranges[axis].exclude(smin - c, smax - c);
- }
- break;
- case 3 :
- if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si
- && box.width() > 0 && box.height() > 0)
- {
- float si = org.x + org.y + sb.si;
- float sa = org.x + org.y + sb.sa;
- float dmax = sdm(si, sa, box.tr.x, -box.bl.y, std::greater<float>());
- float dmin = sdm(sa, si, box.bl.x, -box.tr.y, std::less<float>());
- c = 0.5f * (sb.di + sb.da);
- _ranges[axis].exclude(dmin - c, dmax - c);
- }
- break;
- default :
- break;
- }
- return;
-}
-
-// Adjust the movement limits for the target to avoid having it collide
-// with the given neighbor slot. Also determine if there is in fact a collision
-// between the target and the given slot.
-bool ShiftCollider::mergeSlot(Segment *seg, Slot *slot, const SlotCollision *cslot, const Position &currShift,
- bool isAfter, // slot is logically after _target
- bool sameCluster, bool &hasCol, bool isExclusion,
- GR_MAYBE_UNUSED json * const dbgout )
-{
- bool isCol = false;
- const float sx = slot->origin().x - _origin.x + currShift.x;
- const float sy = slot->origin().y - _origin.y + currShift.y;
- const float sd = sx - sy;
- const float ss = sx + sy;
- float vmin, vmax;
- float omin, omax, otmin, otmax;
- float cmin, cmax; // target limits
- float torg;
- const GlyphCache &gc = seg->getFace()->glyphs();
- const unsigned short gid = slot->gid();
- if (!gc.check(gid))
- return false;
- const BBox &bb = gc.getBoundingBBox(gid);
-
- // SlotCollision * cslot = seg->collisionInfo(slot);
- int orderFlags = 0;
- bool sameClass = _seqProxClass == 0 && cslot->seqClass() == _seqClass;
- if (sameCluster && _seqClass
- && (sameClass || (_seqProxClass != 0 && cslot->seqClass() == _seqProxClass)))
- // Force the target glyph to be in the specified direction from the slot we're testing.
- orderFlags = _seqOrder;
-
- // short circuit if only interested in direct collision and we are out of range
- if (orderFlags || (sx + bb.xa + _margin >= _limit.bl.x && sx + bb.xi - _margin <= _limit.tr.x)
- || (sy + bb.ya + _margin >= _limit.bl.y && sy + bb.yi - _margin <= _limit.tr.y))
-
- {
- const float tx = _currOffset.x + _currShift.x;
- const float ty = _currOffset.y + _currShift.y;
- const float td = tx - ty;
- const float ts = tx + ty;
- const SlantBox &sb = gc.getBoundingSlantBox(gid);
- const unsigned short tgid = _target->gid();
- const BBox &tbb = gc.getBoundingBBox(tgid);
- const SlantBox &tsb = gc.getBoundingSlantBox(tgid);
- float seq_above_wt = cslot->seqAboveWt();
- float seq_below_wt = cslot->seqBelowWt();
- float seq_valign_wt = cslot->seqValignWt();
- float lmargin;
- // if isAfter, invert orderFlags for diagonal orders.
- if (isAfter)
- {
- // invert appropriate bits
- orderFlags ^= (sameClass ? 0x3F : 0x3);
- // consider 2 bits at a time, non overlapping. If both bits set, clear them
- orderFlags = orderFlags ^ ((((orderFlags >> 1) & orderFlags) & 0x15) * 3);
- }
-
-#if !defined GRAPHITE2_NTRACING
- if (dbgout)
- dbgout->setenv(0, slot);
-#endif
-
- // Process main bounding octabox.
- for (int i = 0; i < 4; ++i)
- {
- switch (i) {
- case 0 : // x direction
- vmin = max(max(bb.xi - tbb.xa + sx, sb.di - tsb.da + ty + sd), sb.si - tsb.sa - ty + ss);
- vmax = min(min(bb.xa - tbb.xi + sx, sb.da - tsb.di + ty + sd), sb.sa - tsb.si - ty + ss);
- otmin = tbb.yi + ty;
- otmax = tbb.ya + ty;
- omin = bb.yi + sy;
- omax = bb.ya + sy;
- torg = _currOffset.x;
- cmin = _limit.bl.x + torg;
- cmax = _limit.tr.x - tbb.xi + tbb.xa + torg;
- lmargin = _margin;
- break;
- case 1 : // y direction
- vmin = max(max(bb.yi - tbb.ya + sy, tsb.di - sb.da + tx - sd), sb.si - tsb.sa - tx + ss);
- vmax = min(min(bb.ya - tbb.yi + sy, tsb.da - sb.di + tx - sd), sb.sa - tsb.si - tx + ss);
- otmin = tbb.xi + tx;
- otmax = tbb.xa + tx;
- omin = bb.xi + sx;
- omax = bb.xa + sx;
- torg = _currOffset.y;
- cmin = _limit.bl.y + torg;
- cmax = _limit.tr.y - tbb.yi + tbb.ya + torg;
- lmargin = _margin;
- break;
- case 2 : // sum - moving along the positively-sloped vector, so the boundaries are the
- // negatively-sloped boundaries.
- vmin = max(max(sb.si - tsb.sa + ss, 2 * (bb.yi - tbb.ya + sy) + td), 2 * (bb.xi - tbb.xa + sx) - td);
- vmax = min(min(sb.sa - tsb.si + ss, 2 * (bb.ya - tbb.yi + sy) + td), 2 * (bb.xa - tbb.xi + sx) - td);
- otmin = tsb.di + td;
- otmax = tsb.da + td;
- omin = sb.di + sd;
- omax = sb.da + sd;
- torg = _currOffset.x + _currOffset.y;
- cmin = _limit.bl.x + _limit.bl.y + torg;
- cmax = _limit.tr.x + _limit.tr.y - tsb.si + tsb.sa + torg;
- lmargin = _margin / ISQRT2;
- break;
- case 3 : // diff - moving along the negatively-sloped vector, so the boundaries are the
- // positively-sloped boundaries.
- vmin = max(max(sb.di - tsb.da + sd, 2 * (bb.xi - tbb.xa + sx) - ts), -2 * (bb.ya - tbb.yi + sy) + ts);
- vmax = min(min(sb.da - tsb.di + sd, 2 * (bb.xa - tbb.xi + sx) - ts), -2 * (bb.yi - tbb.ya + sy) + ts);
- otmin = tsb.si + ts;
- otmax = tsb.sa + ts;
- omin = sb.si + ss;
- omax = sb.sa + ss;
- torg = _currOffset.x - _currOffset.y;
- cmin = _limit.bl.x - _limit.tr.y + torg;
- cmax = _limit.tr.x - _limit.bl.y - tsb.di + tsb.da + torg;
- lmargin = _margin / ISQRT2;
- break;
- default :
- continue;
- }
-
-#if !defined GRAPHITE2_NTRACING
- if (dbgout)
- dbgout->setenv(1, reinterpret_cast<void *>(-1));
-#define DBGTAG(x) if (dbgout) dbgout->setenv(1, reinterpret_cast<void *>(-x));
-#else
-#define DBGTAG(x)
-#endif
-
- if (orderFlags)
- {
- Position org(tx, ty);
- float xminf = _limit.bl.x + _currOffset.x + tbb.xi;
- float xpinf = _limit.tr.x + _currOffset.x + tbb.xa;
- float ypinf = _limit.tr.y + _currOffset.y + tbb.ya;
- float yminf = _limit.bl.y + _currOffset.y + tbb.yi;
- switch (orderFlags) {
- case SlotCollision::SEQ_ORDER_RIGHTUP :
- {
- float r1Xedge = cslot->seqAboveXoff() + 0.5f * (bb.xi + bb.xa) + sx;
- float r3Xedge = cslot->seqBelowXlim() + bb.xa + sx + 0.5f * (tbb.xa - tbb.xi);
- float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy;
-
- // DBGTAG(1x) means the regions are up and right
- // region 1
- DBGTAG(11)
- addBox_slope(true, Rect(Position(xminf, r2Yedge), Position(r1Xedge, ypinf)),
- tbb, tsb, org, 0, seq_above_wt, true, i);
- // region 2
- DBGTAG(12)
- removeBox(Rect(Position(xminf, yminf), Position(r3Xedge, r2Yedge)), tbb, tsb, org, i);
- // region 3, which end is zero is irrelevant since m weight is 0
- DBGTAG(13)
- addBox_slope(true, Rect(Position(r3Xedge, yminf), Position(xpinf, r2Yedge - cslot->seqValignHt())),
- tbb, tsb, org, seq_below_wt, 0, true, i);
- // region 4
- DBGTAG(14)
- addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge), Position(xpinf, r2Yedge + cslot->seqValignHt())),
- tbb, tsb, org, 0, seq_valign_wt, true, i);
- // region 5
- DBGTAG(15)
- addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge - cslot->seqValignHt()), Position(xpinf, r2Yedge)),
- tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i);
- break;
- }
- case SlotCollision::SEQ_ORDER_LEFTDOWN :
- {
- float r1Xedge = 0.5f * (bb.xi + bb.xa) + cslot->seqAboveXoff() + sx;
- float r3Xedge = bb.xi - cslot->seqBelowXlim() + sx - 0.5f * (tbb.xa - tbb.xi);
- float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy;
- // DBGTAG(2x) means the regions are up and right
- // region 1
- DBGTAG(21)
- addBox_slope(true, Rect(Position(r1Xedge, yminf), Position(xpinf, r2Yedge)),
- tbb, tsb, org, 0, seq_above_wt, false, i);
- // region 2
- DBGTAG(22)
- removeBox(Rect(Position(r3Xedge, r2Yedge), Position(xpinf, ypinf)), tbb, tsb, org, i);
- // region 3
- DBGTAG(23)
- addBox_slope(true, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()), Position(r3Xedge, ypinf)),
- tbb, tsb, org, seq_below_wt, 0, false, i);
- // region 4
- DBGTAG(24)
- addBox_slope(false, Rect(Position(xminf, r2Yedge), Position(sx + bb.xa, r2Yedge + cslot->seqValignHt())),
- tbb, tsb, org, 0, seq_valign_wt, true, i);
- // region 5
- DBGTAG(25)
- addBox_slope(false, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()),
- Position(sx + bb.xa, r2Yedge)), tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i);
- break;
- }
- case SlotCollision::SEQ_ORDER_NOABOVE : // enforce neighboring glyph being above
- DBGTAG(31);
- removeBox(Rect(Position(bb.xi - tbb.xa + sx, sy + bb.ya),
- Position(bb.xa - tbb.xi + sx, ypinf)), tbb, tsb, org, i);
- break;
- case SlotCollision::SEQ_ORDER_NOBELOW : // enforce neighboring glyph being below
- DBGTAG(32);
- removeBox(Rect(Position(bb.xi - tbb.xa + sx, yminf),
- Position(bb.xa - tbb.xi + sx, sy + bb.yi)), tbb, tsb, org, i);
- break;
- case SlotCollision::SEQ_ORDER_NOLEFT : // enforce neighboring glyph being to the left
- DBGTAG(33)
- removeBox(Rect(Position(xminf, bb.yi - tbb.ya + sy),
- Position(bb.xi - tbb.xa + sx, bb.ya - tbb.yi + sy)), tbb, tsb, org, i);
- break;
- case SlotCollision::SEQ_ORDER_NORIGHT : // enforce neighboring glyph being to the right
- DBGTAG(34)
- removeBox(Rect(Position(bb.xa - tbb.xi + sx, bb.yi - tbb.ya + sy),
- Position(xpinf, bb.ya - tbb.yi + sy)), tbb, tsb, org, i);
- break;
- default :
- break;
- }
- }
-
- if (vmax < cmin - lmargin || vmin > cmax + lmargin || omax < otmin - lmargin || omin > otmax + lmargin)
- continue;
-
- // Process sub-boxes that are defined for this glyph.
- // We only need to do this if there was in fact a collision with the main octabox.
- uint8 numsub = gc.numSubBounds(gid);
- if (numsub > 0)
- {
- bool anyhits = false;
- for (int j = 0; j < numsub; ++j)
- {
- const BBox &sbb = gc.getSubBoundingBBox(gid, j);
- const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, j);
- switch (i) {
- case 0 : // x
- vmin = max(max(sbb.xi-tbb.xa+sx, ssb.di-tsb.da+sd+ty), ssb.si-tsb.sa+ss-ty);
- vmax = min(min(sbb.xa-tbb.xi+sx, ssb.da-tsb.di+sd+ty), ssb.sa-tsb.si+ss-ty);
- omin = sbb.yi + sy;
- omax = sbb.ya + sy;
- break;
- case 1 : // y
- vmin = max(max(sbb.yi-tbb.ya+sy, tsb.di-ssb.da-sd+tx), ssb.si-tsb.sa+ss-tx);
- vmax = min(min(sbb.ya-tbb.yi+sy, tsb.da-ssb.di-sd+tx), ssb.sa-tsb.si+ss-tx);
- omin = sbb.xi + sx;
- omax = sbb.xa + sx;
- break;
- case 2 : // sum
- vmin = max(max(ssb.si-tsb.sa+ss, 2*(sbb.yi-tbb.ya+sy)+td), 2*(sbb.xi-tbb.xa+sx)-td);
- vmax = min(min(ssb.sa-tsb.si+ss, 2*(sbb.ya-tbb.yi+sy)+td), 2*(sbb.xa-tbb.xi+sx)-td);
- omin = ssb.di + sd;
- omax = ssb.da + sd;
- break;
- case 3 : // diff
- vmin = max(max(ssb.di-tsb.da+sd, 2*(sbb.xi-tbb.xa+sx)-ts), -2*(sbb.ya-tbb.yi+sy)+ts);
- vmax = min(min(ssb.da-tsb.di+sd, 2*(sbb.xa-tbb.xi+sx)-ts), -2*(sbb.yi-tbb.ya+sy)+ts);
- omin = ssb.si + ss;
- omax = ssb.sa + ss;
- break;
- }
- if (vmax < cmin - lmargin || vmin > cmax + lmargin || omax < otmin - lmargin || omin > otmax + lmargin)
- continue;
-
-#if !defined GRAPHITE2_NTRACING
- if (dbgout)
- dbgout->setenv(1, reinterpret_cast<void *>(j));
-#endif
- if (omin > otmax)
- _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
- sqr(lmargin - omin + otmax) * _marginWt, false);
- else if (omax < otmin)
- _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
- sqr(lmargin - otmin + omax) * _marginWt, false);
- else
- _ranges[i].exclude_with_margins(vmin, vmax, i);
- anyhits = true;
- }
- if (anyhits)
- isCol = true;
- }
- else // no sub-boxes
- {
-#if !defined GRAPHITE2_NTRACING
- if (dbgout)
- dbgout->setenv(1, reinterpret_cast<void *>(-1));
-#endif
- isCol = true;
- if (omin > otmax)
- _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
- sqr(lmargin - omin + otmax) * _marginWt, false);
- else if (omax < otmin)
- _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
- sqr(lmargin - otmin + omax) * _marginWt, false);
- else
- _ranges[i].exclude_with_margins(vmin, vmax, i);
-
- }
- }
- }
- bool res = true;
- if (cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion)
- {
- // Set up the bogus slot representing the exclusion glyph.
- Slot *exclSlot = seg->newSlot();
- if (!exclSlot)
- return res;
- exclSlot->setGlyph(seg, cslot->exclGlyph());
- Position exclOrigin(slot->origin() + cslot->exclOffset());
- exclSlot->origin(exclOrigin);
- SlotCollision exclInfo(seg, exclSlot);
- res &= mergeSlot(seg, exclSlot, &exclInfo, currShift, isAfter, sameCluster, isCol, true, dbgout );
- seg->freeSlot(exclSlot);
- }
- hasCol |= isCol;
- return res;
-
-} // end of ShiftCollider::mergeSlot
-
-
-// Figure out where to move the target glyph to, and return the amount to shift by.
-Position ShiftCollider::resolve(GR_MAYBE_UNUSED Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout)
-{
- float tbase;
- float totalCost = (float)(std::numeric_limits<float>::max() / 2);
- Position resultPos = Position(0, 0);
-#if !defined GRAPHITE2_NTRACING
- int bestAxis = -1;
- if (dbgout)
- {
- outputJsonDbgStartSlot(dbgout, seg);
- *dbgout << "vectors" << json::array;
- }
-#endif
- isCol = true;
- for (int i = 0; i < 4; ++i)
- {
- float bestCost = -1;
- float bestPos;
- // Calculate the margin depending on whether we are moving diagonally or not:
- switch (i) {
- case 0 : // x direction
- tbase = _currOffset.x;
- break;
- case 1 : // y direction
- tbase = _currOffset.y;
- break;
- case 2 : // sum (negatively-sloped diagonals)
- tbase = _currOffset.x + _currOffset.y;
- break;
- case 3 : // diff (positively-sloped diagonals)
- tbase = _currOffset.x - _currOffset.y;
- break;
- }
- Position testp;
- bestPos = _ranges[i].closest(0, bestCost) - tbase; // Get the best relative position
-#if !defined GRAPHITE2_NTRACING
- if (dbgout)
- outputJsonDbgOneVector(dbgout, seg, i, tbase, bestCost, bestPos) ;
-#endif
- if (bestCost >= 0.0f)
- {
- isCol = false;
- switch (i) {
- case 0 : testp = Position(bestPos, _currShift.y); break;
- case 1 : testp = Position(_currShift.x, bestPos); break;
- case 2 : testp = Position(0.5f * (_currShift.x - _currShift.y + bestPos), 0.5f * (_currShift.y - _currShift.x + bestPos)); break;
- case 3 : testp = Position(0.5f * (_currShift.x + _currShift.y + bestPos), 0.5f * (_currShift.x + _currShift.y - bestPos)); break;
- }
- if (bestCost < totalCost - 0.01f)
- {
- totalCost = bestCost;
- resultPos = testp;
-#if !defined GRAPHITE2_NTRACING
- bestAxis = i;
-#endif
- }
- }
- } // end of loop over 4 directions
-
-#if !defined GRAPHITE2_NTRACING
- if (dbgout)
- outputJsonDbgEndSlot(dbgout, resultPos, bestAxis, isCol);
-#endif
-
- return resultPos;
-
-} // end of ShiftCollider::resolve
-
-
-#if !defined GRAPHITE2_NTRACING
-
-void ShiftCollider::outputJsonDbg(json * const dbgout, Segment *seg, int axis)
-{
- int axisMax = axis;
- if (axis < 0) // output all axes
- {
- *dbgout << "gid" << _target->gid()
- << "limit" << _limit
- << "target" << json::object
- << "origin" << _target->origin()
- << "margin" << _margin
- << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
- << "slantbox" << seg->getFace()->glyphs().slant(_target->gid())
- << json::close; // target object
- *dbgout << "ranges" << json::array;
- axis = 0;
- axisMax = 3;
- }
- for (int iAxis = axis; iAxis <= axisMax; ++iAxis)
- {
- *dbgout << json::flat << json::array << _ranges[iAxis].position();
- for (Zones::const_iterator s = _ranges[iAxis].begin(), e = _ranges[iAxis].end(); s != e; ++s)
- *dbgout << json::flat << json::array
- << Position(s->x, s->xm) << s->sm << s->smx << s->c
- << json::close;
- *dbgout << json::close;
- }
- if (axis < axisMax) // looped through the _ranges array for all axes
- *dbgout << json::close; // ranges array
-}
-
-void ShiftCollider::outputJsonDbgStartSlot(json * const dbgout, Segment *seg)
-{
- *dbgout << json::object // slot - not closed till the end of the caller method
- << "slot" << objectid(dslot(seg, _target))
- << "gid" << _target->gid()
- << "limit" << _limit
- << "target" << json::object
- << "origin" << _origin
- << "currShift" << _currShift
- << "currOffset" << seg->collisionInfo(_target)->offset()
- << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
- << "slantBox" << seg->getFace()->glyphs().slant(_target->gid())
- << "fix" << "shift";
- *dbgout << json::close; // target object
-}
-
-void ShiftCollider::outputJsonDbgEndSlot(GR_MAYBE_UNUSED json * const dbgout,
- Position resultPos, int bestAxis, bool isCol)
-{
- *dbgout << json::close // vectors array
- << "result" << resultPos
- //<< "scraping" << _scraping[bestAxis]
- << "bestAxis" << bestAxis
- << "stillBad" << isCol
- << json::close; // slot object
-}
-
-void ShiftCollider::outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis,
- float tleft, float bestCost, float bestVal)
-{
- const char * label;
- switch (axis)
- {
- case 0: label = "x"; break;
- case 1: label = "y"; break;
- case 2: label = "sum (NE-SW)"; break;
- case 3: label = "diff (NW-SE)"; break;
- default: label = "???"; break;
- }
-
- *dbgout << json::object // vector
- << "direction" << label
- << "targetMin" << tleft;
-
- outputJsonDbgRemovals(dbgout, axis, seg);
-
- *dbgout << "ranges";
- outputJsonDbg(dbgout, seg, axis);
-
- *dbgout << "bestCost" << bestCost
- << "bestVal" << bestVal + tleft
- << json::close; // vectors object
-}
-
-void ShiftCollider::outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg)
-{
- *dbgout << "removals" << json::array;
- _ranges[axis].jsonDbgOut(seg);
- *dbgout << json::close; // removals array
-}
-
-#endif // !defined GRAPHITE2_NTRACING
-
-
-//// KERN-COLLIDER ////
-
-inline
-static float localmax (float al, float au, float bl, float bu, float x)
-{
- if (al < bl)
- { if (au < bu) return au < x ? au : x; }
- else if (au > bu) return bl < x ? bl : x;
- return x;
-}
-
-inline
-static float localmin(float al, float au, float bl, float bu, float x)
-{
- if (bl > al)
- { if (bu > au) return bl > x ? bl : x; }
- else if (au > bu) return al > x ? al : x;
- return x;
-}
-
-// Return the given edge of the glyph at height y, taking any slant box into account.
-static float get_edge(Segment *seg, const Slot *s, const Position &shift, float y, float width, float margin, bool isRight)
-{
- const GlyphCache &gc = seg->getFace()->glyphs();
- unsigned short gid = s->gid();
- float sx = s->origin().x + shift.x;
- float sy = s->origin().y + shift.y;
- uint8 numsub = gc.numSubBounds(gid);
- float res = isRight ? (float)-1e38 : (float)1e38;
-
- if (numsub > 0)
- {
- for (int i = 0; i < numsub; ++i)
- {
- const BBox &sbb = gc.getSubBoundingBBox(gid, i);
- const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, i);
- if (sy + sbb.yi - margin > y + width / 2 || sy + sbb.ya + margin < y - width / 2)
- continue;
- if (isRight)
- {
- float x = sx + sbb.xa + margin;
- if (x > res)
- {
- float td = sx - sy + ssb.da + margin + y;
- float ts = sx + sy + ssb.sa + margin - y;
- x = localmax(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x);
- if (x > res)
- res = x;
- }
- }
- else
- {
- float x = sx + sbb.xi - margin;
- if (x < res)
- {
- float td = sx - sy + ssb.di - margin + y;
- float ts = sx + sy + ssb.si - margin - y;
- x = localmin(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x);
- if (x < res)
- res = x;
- }
- }
- }
- }
- else
- {
- const BBox &bb = gc.getBoundingBBox(gid);
- const SlantBox &sb = gc.getBoundingSlantBox(gid);
- if (sy + bb.yi - margin > y + width / 2 || sy + bb.ya + margin < y - width / 2)
- return res;
- float td = sx - sy + y;
- float ts = sx + sy - y;
- if (isRight)
- res = localmax(td + sb.da - width / 2, td + sb.da + width / 2, ts + sb.sa - width / 2, ts + sb.sa + width / 2, sx + bb.xa) + margin;
- else
- res = localmin(td + sb.di - width / 2, td + sb.di + width / 2, ts + sb.si - width / 2, ts + sb.si + width / 2, sx + bb.xi) - margin;
- }
- return res;
-}
-
-
-bool KernCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin,
- const Position &currShift, const Position &offsetPrev, int dir,
- float ymin, float ymax, GR_MAYBE_UNUSED json * const dbgout)
-{
- const GlyphCache &gc = seg->getFace()->glyphs();
- const Slot *base = aSlot;
- // const Slot *last = aSlot;
- const Slot *s;
- int numSlices;
- while (base->attachedTo())
- base = base->attachedTo();
- if (margin < 10) margin = 10;
-
- _limit = limit;
- _offsetPrev = offsetPrev; // kern from a previous pass
-
- // Calculate the height of the glyph and how many horizontal slices to use.
- if (_maxy >= 1e37f)
- {
- _sliceWidth = margin / 1.5f;
- _maxy = ymax + margin;
- _miny = ymin - margin;
- numSlices = int((_maxy - _miny + 2) / (_sliceWidth / 1.5f) + 1.f); // +2 helps with rounding errors
- _edges.clear();
- _edges.insert(_edges.begin(), numSlices, (dir & 1) ? 1e38f : -1e38f);
- _xbound = (dir & 1) ? (float)1e38f : (float)-1e38f;
- }
- else if (_maxy != ymax || _miny != ymin)
- {
- if (_miny != ymin)
- {
- numSlices = int((ymin - margin - _miny) / _sliceWidth - 1);
- _miny += numSlices * _sliceWidth;
- if (numSlices < 0)
- _edges.insert(_edges.begin(), -numSlices, (dir & 1) ? 1e38f : -1e38f);
- else if ((unsigned)numSlices < _edges.size()) // this shouldn't fire since we always grow the range
- {
- Vector<float>::iterator e = _edges.begin();
- while (numSlices--)
- ++e;
- _edges.erase(_edges.begin(), e);
- }
- }
- if (_maxy != ymax)
- {
- numSlices = int((ymax + margin - _miny) / _sliceWidth + 1);
- _maxy = numSlices * _sliceWidth + _miny;
- if (numSlices > (int)_edges.size())
- _edges.insert(_edges.end(), numSlices - _edges.size(), (dir & 1) ? 1e38f : -1e38f);
- else if (numSlices < (int)_edges.size()) // this shouldn't fire since we always grow the range
- {
- while ((int)_edges.size() > numSlices)
- _edges.pop_back();
- }
- }
- goto done;
- }
- numSlices = int(_edges.size());
-
-#if !defined GRAPHITE2_NTRACING
- // Debugging
- _seg = seg;
- _slotNear.clear();
- _slotNear.insert(_slotNear.begin(), numSlices, NULL);
- _nearEdges.clear();
- _nearEdges.insert(_nearEdges.begin(), numSlices, (dir & 1) ? -1e38f : +1e38f);
-#endif
-
- // Determine the trailing edge of each slice (ie, left edge for a RTL glyph).
- for (s = base; s; s = s->nextInCluster(s))
- {
- SlotCollision *c = seg->collisionInfo(s);
- if (!gc.check(s->gid()))
- return false;
- const BBox &bs = gc.getBoundingBBox(s->gid());
- float x = s->origin().x + c->shift().x + ((dir & 1) ? bs.xi : bs.xa);
- // Loop over slices.
- // Note smin might not be zero if glyph s is not at the bottom of the cluster; similarly for smax.
- float toffset = c->shift().y - _miny + 1 + s->origin().y;
- int smin = max(0, int((bs.yi + toffset) / _sliceWidth));
- int smax = min(numSlices - 1, int((bs.ya + toffset) / _sliceWidth + 1));
- for (int i = smin; i <= smax; ++i)
- {
- float t;
- float y = _miny - 1 + (i + .5f) * _sliceWidth; // vertical center of slice
- if ((dir & 1) && x < _edges[i])
- {
- t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, false);
- if (t < _edges[i])
- {
- _edges[i] = t;
- if (t < _xbound)
- _xbound = t;
- }
- }
- else if (!(dir & 1) && x > _edges[i])
- {
- t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, true);
- if (t > _edges[i])
- {
- _edges[i] = t;
- if (t > _xbound)
- _xbound = t;
- }
- }
- }
- }
- done:
- _mingap = (float)1e37; // less than 1e38 s.t. 1e38-_mingap is really big
- _target = aSlot;
- _margin = margin;
- _currShift = currShift;
- return true;
-} // end of KernCollider::initSlot
-
-
-// Determine how much the target slot needs to kern away from the given slot.
-// In other words, merge information from given slot's position with what the target slot knows
-// about how it can kern.
-// Return false if we know there is no collision, true if we think there might be one.
-bool KernCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, GR_MAYBE_UNUSED json * const dbgout)
-{
- int rtl = (dir & 1) * 2 - 1;
- if (!seg->getFace()->glyphs().check(slot->gid()))
- return false;
- const Rect &bb = seg->theGlyphBBoxTemporary(slot->gid());
- const float sx = slot->origin().x + currShift.x;
- float x = (sx + (rtl > 0 ? bb.tr.x : bb.bl.x)) * rtl;
- // this isn't going to reduce _mingap so skip
- if (_hit && x < rtl * (_xbound - _mingap - currSpace))
- return false;
-
- const float sy = slot->origin().y + currShift.y;
- int smin = max(1, int((bb.bl.y + (1 - _miny + sy)) / _sliceWidth + 1)) - 1;
- int smax = min((int)_edges.size() - 2, int((bb.tr.y + (1 - _miny + sy)) / _sliceWidth + 1)) + 1;
- if (smin > smax)
- return false;
- bool collides = false;
- bool nooverlap = true;
-
- for (int i = smin; i <= smax; ++i)
- {
- float here = _edges[i] * rtl;
- if (here > (float)9e37)
- continue;
- if (!_hit || x > here - _mingap - currSpace)
- {
- float y = (float)(_miny - 1 + (i + .5f) * _sliceWidth); // vertical center of slice
- // 2 * currSpace to account for the space that is already separating them and the space we want to add
- float m = get_edge(seg, slot, currShift, y, _sliceWidth, 0., rtl > 0) * rtl + 2 * currSpace;
- if (m < (float)-8e37) // only true if the glyph has a gap in it
- continue;
- nooverlap = false;
- float t = here - m;
- // _mingap is positive to shrink
- if (t < _mingap || (!_hit && !collides))
- {
- _mingap = t;
- collides = true;
- }
-#if !defined GRAPHITE2_NTRACING
- // Debugging - remember the closest neighboring edge for this slice.
- if (m > rtl * _nearEdges[i])
- {
- _slotNear[i] = slot;
- _nearEdges[i] = m * rtl;
- }
-#endif
- }
- else
- nooverlap = false;
- }
- if (nooverlap)
- _mingap = max(_mingap, _xbound - rtl * (currSpace + _margin + x));
- if (collides && !nooverlap)
- _hit = true;
- return collides | nooverlap; // note that true is not a necessarily reliable value
-
-} // end of KernCollider::mergeSlot
-
-
-// Return the amount to kern by.
-Position KernCollider::resolve(GR_MAYBE_UNUSED Segment *seg, GR_MAYBE_UNUSED Slot *slot,
- int dir, GR_MAYBE_UNUSED json * const dbgout)
-{
- float resultNeeded = (1 - 2 * (dir & 1)) * _mingap;
- // float resultNeeded = (1 - 2 * (dir & 1)) * (_mingap - margin);
- float result = min(_limit.tr.x - _offsetPrev.x, max(resultNeeded, _limit.bl.x - _offsetPrev.x));
-
-#if !defined GRAPHITE2_NTRACING
- if (dbgout)
- {
- *dbgout << json::object // slot
- << "slot" << objectid(dslot(seg, _target))
- << "gid" << _target->gid()
- << "limit" << _limit
- << "miny" << _miny
- << "maxy" << _maxy
- << "slicewidth" << _sliceWidth
- << "target" << json::object
- << "origin" << _target->origin()
- //<< "currShift" << _currShift
- << "offsetPrev" << _offsetPrev
- << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
- << "slantBox" << seg->getFace()->glyphs().slant(_target->gid())
- << "fix" << "kern"
- << json::close; // target object
-
- *dbgout << "slices" << json::array;
- for (int is = 0; is < (int)_edges.size(); is++)
- {
- *dbgout << json::flat << json::object
- << "i" << is
- << "targetEdge" << _edges[is]
- << "neighbor" << objectid(dslot(seg, _slotNear[is]))
- << "nearEdge" << _nearEdges[is]
- << json::close;
- }
- *dbgout << json::close; // slices array
-
- *dbgout
- << "xbound" << _xbound
- << "minGap" << _mingap
- << "needed" << resultNeeded
- << "result" << result
- << "stillBad" << (result != resultNeeded)
- << json::close; // slot object
- }
-#endif
-
- return Position(result, 0.);
-
-} // end of KernCollider::resolve
-
-void KernCollider::shift(const Position &mv, int dir)
-{
- for (Vector<float>::iterator e = _edges.begin(); e != _edges.end(); ++e)
- *e += mv.x;
- _xbound += (1 - 2 * (dir & 1)) * mv.x;
-}
-
-//// SLOT-COLLISION ////
-
-// Initialize the collision attributes for the given slot.
-SlotCollision::SlotCollision(Segment *seg, Slot *slot)
-{
- initFromSlot(seg, slot);
-}
-
-void SlotCollision::initFromSlot(Segment *seg, Slot *slot)
-{
- // Initialize slot attributes from glyph attributes.
- // The order here must match the order in the grcompiler code,
- // GrcSymbolTable::AssignInternalGlyphAttrIDs.
- uint16 gid = slot->gid();
- uint16 aCol = seg->silf()->aCollision(); // flags attr ID
- const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(gid);
- if (!glyphFace)
- return;
- const sparse &p = glyphFace->attrs();
- _flags = p[aCol];
- _limit = Rect(Position(int16(p[aCol+1]), int16(p[aCol+2])),
- Position(int16(p[aCol+3]), int16(p[aCol+4])));
- _margin = p[aCol+5];
- _marginWt = p[aCol+6];
-
- _seqClass = p[aCol+7];
- _seqProxClass = p[aCol+8];
- _seqOrder = p[aCol+9];
- _seqAboveXoff = p[aCol+10];
- _seqAboveWt = p[aCol+11];
- _seqBelowXlim = p[aCol+12];
- _seqBelowWt = p[aCol+13];
- _seqValignHt = p[aCol+14];
- _seqValignWt = p[aCol+15];
-
- // These attributes do not have corresponding glyph attribute:
- _exclGlyph = 0;
- _exclOffset = Position(0, 0);
-}
-
-float SlotCollision::getKern(int dir) const
-{
- if ((_flags & SlotCollision::COLL_KERN) != 0)
- return float(_shift.x * ((dir & 1) ? -1 : 1));
- else
- return 0;
-}
-
-bool SlotCollision::ignore() const
-{
- return ((flags() & SlotCollision::COLL_IGNORE) || (flags() & SlotCollision::COLL_ISSPACE));
-}