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