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