/work/workdir/UnpackedTarball/graphite/src/Collider.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #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 | 0 | { |
53 | 0 | int i; |
54 | 0 | float mx, mn; |
55 | 0 | 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 | 0 | 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 | 0 | { |
72 | 0 | switch (i) { |
73 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | } |
106 | 0 | } |
107 | | |
108 | 0 | _target = aSlot; |
109 | 0 | if ((dir & 1) == 0) |
110 | 0 | { |
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 | 0 | } |
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 | 0 | } |
128 | | |
129 | | template <class O> |
130 | | float sdm(float vi, float va, float mx, float my, O op) |
131 | 0 | { |
132 | 0 | float res = 2 * mx - vi; |
133 | 0 | if (op(res, vi + 2 * my)) |
134 | 0 | { |
135 | 0 | res = va + 2 * my; |
136 | 0 | if (op(res, 2 * mx - va)) |
137 | 0 | res = mx + my; |
138 | 0 | } |
139 | 0 | return res; |
140 | 0 | } Unexecuted instantiation: float sdm<std::__1::greater<float> >(float, float, float, float, std::__1::greater<float>) Unexecuted instantiation: float sdm<std::__1::less<float> >(float, float, float, float, std::__1::less<float>) |
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 | 0 | { |
145 | 0 | float a, c; |
146 | 0 | switch (axis) { |
147 | 0 | case 0 : |
148 | 0 | if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0) |
149 | 0 | { |
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 | 0 | 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 | 0 | } |
159 | 0 | break; |
160 | 0 | case 1 : |
161 | 0 | if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0) |
162 | 0 | { |
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 | 0 | 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 | 0 | } |
172 | 0 | break; |
173 | 0 | 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 | 0 | { |
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 | 0 | float si; |
182 | 0 | a = d; |
183 | 0 | if (isx) |
184 | 0 | si = 2 * (minright ? box.tr.x : box.bl.x) - a; |
185 | 0 | 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 | 0 | } |
189 | 0 | break; |
190 | 0 | 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 | 0 | { |
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 | 0 | float di; |
199 | 0 | a = s; |
200 | 0 | if (isx) |
201 | 0 | di = 2 * (minright ? box.tr.x : box.bl.x) - a; |
202 | 0 | 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 | 0 | } |
206 | 0 | break; |
207 | 0 | default : |
208 | 0 | break; |
209 | 0 | } |
210 | 0 | return; |
211 | 0 | } |
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 | 0 | { |
216 | 0 | float c; |
217 | 0 | switch (axis) { |
218 | 0 | case 0 : |
219 | 0 | if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0) |
220 | 0 | { |
221 | 0 | c = 0.5f * (bb.xi + bb.xa); |
222 | 0 | _ranges[axis].exclude(box.bl.x - c, box.tr.x - c); |
223 | 0 | } |
224 | 0 | break; |
225 | 0 | case 1 : |
226 | 0 | if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0) |
227 | 0 | { |
228 | 0 | c = 0.5f * (bb.yi + bb.ya); |
229 | 0 | _ranges[axis].exclude(box.bl.y - c, box.tr.y - c); |
230 | 0 | } |
231 | 0 | break; |
232 | 0 | 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 | 0 | { |
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 | 0 | } |
243 | 0 | break; |
244 | 0 | 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 | 0 | { |
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 | 0 | } |
255 | 0 | break; |
256 | 0 | default : |
257 | 0 | break; |
258 | 0 | } |
259 | 0 | return; |
260 | 0 | } |
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 | 0 | { |
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 | 0 | float vmin, vmax; |
276 | 0 | float omin, omax, otmin, otmax; |
277 | 0 | float cmin, cmax; // target limits |
278 | 0 | 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 | 0 | { |
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; |
310 | | // if isAfter, invert orderFlags for diagonal orders. |
311 | 0 | if (isAfter) |
312 | 0 | { |
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 | 0 | } |
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 | 0 | { |
327 | 0 | switch (i) { |
328 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | default : |
379 | 0 | continue; |
380 | 0 | } |
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 | 0 | #define DBGTAG(x) |
388 | 0 | #endif |
389 | | |
390 | 0 | if (orderFlags) |
391 | 0 | { |
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 | 0 | case SlotCollision::SEQ_ORDER_RIGHTUP : |
399 | 0 | { |
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 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | } |
426 | 0 | case SlotCollision::SEQ_ORDER_LEFTDOWN : |
427 | 0 | { |
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 | 0 | 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 | 0 | DBGTAG(22) |
438 | 0 | removeBox(Rect(Position(r3Xedge, r2Yedge), Position(xpinf, ypinf)), tbb, tsb, org, i); |
439 | | // region 3 |
440 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | } |
453 | 0 | case SlotCollision::SEQ_ORDER_NOABOVE : // enforce neighboring glyph being above |
454 | 0 | 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 | 0 | case SlotCollision::SEQ_ORDER_NOBELOW : // enforce neighboring glyph being below |
459 | 0 | 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 | 0 | case SlotCollision::SEQ_ORDER_NOLEFT : // enforce neighboring glyph being to the left |
464 | 0 | 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 | 0 | case SlotCollision::SEQ_ORDER_NORIGHT : // enforce neighboring glyph being to the right |
469 | 0 | 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 | 0 | default : |
474 | 0 | break; |
475 | 0 | } |
476 | 0 | } |
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 | 0 | { |
486 | 0 | bool anyhits = false; |
487 | 0 | for (int j = 0; j < numsub; ++j) |
488 | 0 | { |
489 | 0 | const BBox &sbb = gc.getSubBoundingBBox(gid, j); |
490 | 0 | const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, j); |
491 | 0 | switch (i) { |
492 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | } |
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 | 0 | else |
531 | 0 | _ranges[i].exclude_with_margins(vmin, vmax, i); |
532 | 0 | anyhits = true; |
533 | 0 | } |
534 | 0 | if (anyhits) |
535 | 0 | isCol = true; |
536 | 0 | } |
537 | 0 | else // no sub-boxes |
538 | 0 | { |
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 | 0 | else |
551 | 0 | _ranges[i].exclude_with_margins(vmin, vmax, i); |
552 | |
|
553 | 0 | } |
554 | 0 | } |
555 | 0 | } |
556 | 0 | bool res = true; |
557 | 0 | if (cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion) |
558 | 0 | { |
559 | | // Set up the bogus slot representing the exclusion glyph. |
560 | 0 | Slot *exclSlot = seg->newSlot(); |
561 | 0 | if (!exclSlot) |
562 | 0 | return res; |
563 | 0 | exclSlot->setGlyph(seg, cslot->exclGlyph()); |
564 | 0 | Position exclOrigin(slot->origin() + cslot->exclOffset()); |
565 | 0 | exclSlot->origin(exclOrigin); |
566 | 0 | SlotCollision exclInfo(seg, exclSlot); |
567 | 0 | res &= mergeSlot(seg, exclSlot, &exclInfo, currShift, isAfter, sameCluster, isCol, true, dbgout ); |
568 | 0 | seg->freeSlot(exclSlot); |
569 | 0 | } |
570 | 0 | hasCol |= isCol; |
571 | 0 | return res; |
572 | |
|
573 | 0 | } // 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 | 0 | { |
579 | 0 | float tbase; |
580 | 0 | float totalCost = (float)(std::numeric_limits<float>::max() / 2); |
581 | 0 | 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 | 0 | isCol = true; |
591 | 0 | for (int i = 0; i < 4; ++i) |
592 | 0 | { |
593 | 0 | float bestCost = -1; |
594 | 0 | float bestPos; |
595 | | // Calculate the margin depending on whether we are moving diagonally or not: |
596 | 0 | switch (i) { |
597 | 0 | case 0 : // x direction |
598 | 0 | tbase = _currOffset.x; |
599 | 0 | break; |
600 | 0 | case 1 : // y direction |
601 | 0 | tbase = _currOffset.y; |
602 | 0 | break; |
603 | 0 | case 2 : // sum (negatively-sloped diagonals) |
604 | 0 | tbase = _currOffset.x + _currOffset.y; |
605 | 0 | break; |
606 | 0 | case 3 : // diff (positively-sloped diagonals) |
607 | 0 | tbase = _currOffset.x - _currOffset.y; |
608 | 0 | break; |
609 | 0 | } |
610 | 0 | Position testp; |
611 | 0 | 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 | 0 | if (bestCost >= 0.0f) |
617 | 0 | { |
618 | 0 | isCol = false; |
619 | 0 | switch (i) { |
620 | 0 | case 0 : testp = Position(bestPos, _currShift.y); break; |
621 | 0 | case 1 : testp = Position(_currShift.x, bestPos); break; |
622 | 0 | case 2 : testp = Position(0.5f * (_currShift.x - _currShift.y + bestPos), 0.5f * (_currShift.y - _currShift.x + bestPos)); break; |
623 | 0 | case 3 : testp = Position(0.5f * (_currShift.x + _currShift.y + bestPos), 0.5f * (_currShift.x + _currShift.y - bestPos)); break; |
624 | 0 | } |
625 | 0 | if (bestCost < totalCost - 0.01f) |
626 | 0 | { |
627 | 0 | totalCost = bestCost; |
628 | 0 | resultPos = testp; |
629 | | #if !defined GRAPHITE2_NTRACING |
630 | | bestAxis = i; |
631 | | #endif |
632 | 0 | } |
633 | 0 | } |
634 | 0 | } // 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 | 0 | return resultPos; |
642 | |
|
643 | 0 | } // 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 | 0 | { |
747 | 0 | if (al < bl) |
748 | 0 | { if (au < bu) return au < x ? au : x; } |
749 | 0 | else if (au > bu) return bl < x ? bl : x; |
750 | 0 | return x; |
751 | 0 | } |
752 | | |
753 | | inline |
754 | | static float localmin(float al, float au, float bl, float bu, float x) |
755 | 0 | { |
756 | 0 | if (bl > al) |
757 | 0 | { if (bu > au) return bl > x ? bl : x; } |
758 | 0 | else if (au > bu) return al > x ? al : x; |
759 | 0 | return x; |
760 | 0 | } |
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 | 0 | { |
765 | 0 | const GlyphCache &gc = seg->getFace()->glyphs(); |
766 | 0 | unsigned short gid = s->gid(); |
767 | 0 | float sx = s->origin().x + shift.x; |
768 | 0 | float sy = s->origin().y + shift.y; |
769 | 0 | uint8 numsub = gc.numSubBounds(gid); |
770 | 0 | float res = isRight ? (float)-1e38 : (float)1e38; |
771 | |
|
772 | 0 | if (numsub > 0) |
773 | 0 | { |
774 | 0 | for (int i = 0; i < numsub; ++i) |
775 | 0 | { |
776 | 0 | const BBox &sbb = gc.getSubBoundingBBox(gid, i); |
777 | 0 | const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, i); |
778 | 0 | if (sy + sbb.yi - margin > y + width / 2 || sy + sbb.ya + margin < y - width / 2) |
779 | 0 | continue; |
780 | 0 | if (isRight) |
781 | 0 | { |
782 | 0 | float x = sx + sbb.xa + margin; |
783 | 0 | if (x > res) |
784 | 0 | { |
785 | 0 | float td = sx - sy + ssb.da + margin + y; |
786 | 0 | float ts = sx + sy + ssb.sa + margin - y; |
787 | 0 | x = localmax(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x); |
788 | 0 | if (x > res) |
789 | 0 | res = x; |
790 | 0 | } |
791 | 0 | } |
792 | 0 | else |
793 | 0 | { |
794 | 0 | float x = sx + sbb.xi - margin; |
795 | 0 | if (x < res) |
796 | 0 | { |
797 | 0 | float td = sx - sy + ssb.di - margin + y; |
798 | 0 | float ts = sx + sy + ssb.si - margin - y; |
799 | 0 | x = localmin(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x); |
800 | 0 | if (x < res) |
801 | 0 | res = x; |
802 | 0 | } |
803 | 0 | } |
804 | 0 | } |
805 | 0 | } |
806 | 0 | else |
807 | 0 | { |
808 | 0 | const BBox &bb = gc.getBoundingBBox(gid); |
809 | 0 | const SlantBox &sb = gc.getBoundingSlantBox(gid); |
810 | 0 | if (sy + bb.yi - margin > y + width / 2 || sy + bb.ya + margin < y - width / 2) |
811 | 0 | return res; |
812 | 0 | float td = sx - sy + y; |
813 | 0 | float ts = sx + sy - y; |
814 | 0 | if (isRight) |
815 | 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; |
816 | 0 | else |
817 | 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; |
818 | 0 | } |
819 | 0 | return res; |
820 | 0 | } |
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 | 0 | { |
827 | 0 | const GlyphCache &gc = seg->getFace()->glyphs(); |
828 | 0 | const Slot *base = aSlot; |
829 | | // const Slot *last = aSlot; |
830 | 0 | const Slot *s; |
831 | 0 | int numSlices; |
832 | 0 | while (base->attachedTo()) |
833 | 0 | base = base->attachedTo(); |
834 | 0 | if (margin < 10) margin = 10; |
835 | |
|
836 | 0 | _limit = limit; |
837 | 0 | _offsetPrev = offsetPrev; // kern from a previous pass |
838 | | |
839 | | // Calculate the height of the glyph and how many horizontal slices to use. |
840 | 0 | if (_maxy >= 1e37f) |
841 | 0 | { |
842 | 0 | _sliceWidth = margin / 1.5f; |
843 | 0 | _maxy = ymax + margin; |
844 | 0 | _miny = ymin - margin; |
845 | 0 | numSlices = int((_maxy - _miny + 2) / (_sliceWidth / 1.5f) + 1.f); // +2 helps with rounding errors |
846 | 0 | _edges.clear(); |
847 | 0 | _edges.insert(_edges.begin(), numSlices, (dir & 1) ? 1e38f : -1e38f); |
848 | 0 | _xbound = (dir & 1) ? (float)1e38f : (float)-1e38f; |
849 | 0 | } |
850 | 0 | else if (_maxy != ymax || _miny != ymin) |
851 | 0 | { |
852 | 0 | if (_miny != ymin) |
853 | 0 | { |
854 | 0 | numSlices = int((ymin - margin - _miny) / _sliceWidth - 1); |
855 | 0 | _miny += numSlices * _sliceWidth; |
856 | 0 | if (numSlices < 0) |
857 | 0 | _edges.insert(_edges.begin(), -numSlices, (dir & 1) ? 1e38f : -1e38f); |
858 | 0 | else if ((unsigned)numSlices < _edges.size()) // this shouldn't fire since we always grow the range |
859 | 0 | { |
860 | 0 | Vector<float>::iterator e = _edges.begin(); |
861 | 0 | while (numSlices--) |
862 | 0 | ++e; |
863 | 0 | _edges.erase(_edges.begin(), e); |
864 | 0 | } |
865 | 0 | } |
866 | 0 | if (_maxy != ymax) |
867 | 0 | { |
868 | 0 | numSlices = int((ymax + margin - _miny) / _sliceWidth + 1); |
869 | 0 | _maxy = numSlices * _sliceWidth + _miny; |
870 | 0 | if (numSlices > (int)_edges.size()) |
871 | 0 | _edges.insert(_edges.end(), numSlices - _edges.size(), (dir & 1) ? 1e38f : -1e38f); |
872 | 0 | else if (numSlices < (int)_edges.size()) // this shouldn't fire since we always grow the range |
873 | 0 | { |
874 | 0 | while ((int)_edges.size() > numSlices) |
875 | 0 | _edges.pop_back(); |
876 | 0 | } |
877 | 0 | } |
878 | 0 | goto done; |
879 | 0 | } |
880 | 0 | 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 | 0 | for (s = base; s; s = s->nextInCluster(s)) |
893 | 0 | { |
894 | 0 | SlotCollision *c = seg->collisionInfo(s); |
895 | 0 | if (!gc.check(s->gid())) |
896 | 0 | return false; |
897 | 0 | const BBox &bs = gc.getBoundingBBox(s->gid()); |
898 | 0 | 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 | 0 | float toffset = c->shift().y - _miny + 1 + s->origin().y; |
902 | 0 | int smin = max(0, int((bs.yi + toffset) / _sliceWidth)); |
903 | 0 | int smax = min(numSlices - 1, int((bs.ya + toffset) / _sliceWidth + 1)); |
904 | 0 | for (int i = smin; i <= smax; ++i) |
905 | 0 | { |
906 | 0 | float t; |
907 | 0 | float y = _miny - 1 + (i + .5f) * _sliceWidth; // vertical center of slice |
908 | 0 | if ((dir & 1) && x < _edges[i]) |
909 | 0 | { |
910 | 0 | t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, false); |
911 | 0 | if (t < _edges[i]) |
912 | 0 | { |
913 | 0 | _edges[i] = t; |
914 | 0 | if (t < _xbound) |
915 | 0 | _xbound = t; |
916 | 0 | } |
917 | 0 | } |
918 | 0 | else if (!(dir & 1) && x > _edges[i]) |
919 | 0 | { |
920 | 0 | t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, true); |
921 | 0 | if (t > _edges[i]) |
922 | 0 | { |
923 | 0 | _edges[i] = t; |
924 | 0 | if (t > _xbound) |
925 | 0 | _xbound = t; |
926 | 0 | } |
927 | 0 | } |
928 | 0 | } |
929 | 0 | } |
930 | 0 | done: |
931 | 0 | _mingap = (float)1e37; // less than 1e38 s.t. 1e38-_mingap is really big |
932 | 0 | _target = aSlot; |
933 | 0 | _margin = margin; |
934 | 0 | _currShift = currShift; |
935 | 0 | return true; |
936 | 0 | } // 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 | 0 | { |
945 | 0 | int rtl = (dir & 1) * 2 - 1; |
946 | 0 | if (!seg->getFace()->glyphs().check(slot->gid())) |
947 | 0 | return false; |
948 | 0 | const Rect &bb = seg->theGlyphBBoxTemporary(slot->gid()); |
949 | 0 | const float sx = slot->origin().x + currShift.x; |
950 | 0 | float x = (sx + (rtl > 0 ? bb.tr.x : bb.bl.x)) * rtl; |
951 | | // this isn't going to reduce _mingap so skip |
952 | 0 | if (_hit && x < rtl * (_xbound - _mingap - currSpace)) |
953 | 0 | return false; |
954 | | |
955 | 0 | const float sy = slot->origin().y + currShift.y; |
956 | 0 | int smin = max(1, int((bb.bl.y + (1 - _miny + sy)) / _sliceWidth + 1)) - 1; |
957 | 0 | int smax = min((int)_edges.size() - 2, int((bb.tr.y + (1 - _miny + sy)) / _sliceWidth + 1)) + 1; |
958 | 0 | if (smin > smax) |
959 | 0 | return false; |
960 | 0 | bool collides = false; |
961 | 0 | bool nooverlap = true; |
962 | |
|
963 | 0 | for (int i = smin; i <= smax; ++i) |
964 | 0 | { |
965 | 0 | float here = _edges[i] * rtl; |
966 | 0 | if (here > (float)9e37) |
967 | 0 | continue; |
968 | 0 | if (!_hit || x > here - _mingap - currSpace) |
969 | 0 | { |
970 | 0 | 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 | 0 | float m = get_edge(seg, slot, currShift, y, _sliceWidth, 0., rtl > 0) * rtl + 2 * currSpace; |
973 | 0 | if (m < (float)-8e37) // only true if the glyph has a gap in it |
974 | 0 | continue; |
975 | 0 | nooverlap = false; |
976 | 0 | float t = here - m; |
977 | | // _mingap is positive to shrink |
978 | 0 | if (t < _mingap || (!_hit && !collides)) |
979 | 0 | { |
980 | 0 | _mingap = t; |
981 | 0 | collides = true; |
982 | 0 | } |
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 | 0 | } |
992 | 0 | else |
993 | 0 | nooverlap = false; |
994 | 0 | } |
995 | 0 | if (nooverlap) |
996 | 0 | _mingap = max(_mingap, _xbound - rtl * (currSpace + _margin + x)); |
997 | 0 | if (collides && !nooverlap) |
998 | 0 | _hit = true; |
999 | 0 | return collides | nooverlap; // note that true is not a necessarily reliable value |
1000 | |
|
1001 | 0 | } // 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 | 0 | { |
1008 | 0 | float resultNeeded = (1 - 2 * (dir & 1)) * _mingap; |
1009 | | // float resultNeeded = (1 - 2 * (dir & 1)) * (_mingap - margin); |
1010 | 0 | 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 | 0 | return Position(result, 0.); |
1054 | |
|
1055 | 0 | } // end of KernCollider::resolve |
1056 | | |
1057 | | void KernCollider::shift(const Position &mv, int dir) |
1058 | 0 | { |
1059 | 0 | for (Vector<float>::iterator e = _edges.begin(); e != _edges.end(); ++e) |
1060 | 0 | *e += mv.x; |
1061 | 0 | _xbound += (1 - 2 * (dir & 1)) * mv.x; |
1062 | 0 | } |
1063 | | |
1064 | | //// SLOT-COLLISION //// |
1065 | | |
1066 | | // Initialize the collision attributes for the given slot. |
1067 | | SlotCollision::SlotCollision(Segment *seg, Slot *slot) |
1068 | 0 | { |
1069 | 0 | initFromSlot(seg, slot); |
1070 | 0 | } |
1071 | | |
1072 | | void SlotCollision::initFromSlot(Segment *seg, Slot *slot) |
1073 | 0 | { |
1074 | | // Initialize slot attributes from glyph attributes. |
1075 | | // The order here must match the order in the grcompiler code, |
1076 | | // GrcSymbolTable::AssignInternalGlyphAttrIDs. |
1077 | 0 | uint16 gid = slot->gid(); |
1078 | 0 | uint16 aCol = seg->silf()->aCollision(); // flags attr ID |
1079 | 0 | const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(gid); |
1080 | 0 | if (!glyphFace) |
1081 | 0 | return; |
1082 | 0 | const sparse &p = glyphFace->attrs(); |
1083 | 0 | _flags = p[aCol]; |
1084 | 0 | _limit = Rect(Position(int16(p[aCol+1]), int16(p[aCol+2])), |
1085 | 0 | Position(int16(p[aCol+3]), int16(p[aCol+4]))); |
1086 | 0 | _margin = p[aCol+5]; |
1087 | 0 | _marginWt = p[aCol+6]; |
1088 | |
|
1089 | 0 | _seqClass = p[aCol+7]; |
1090 | 0 | _seqProxClass = p[aCol+8]; |
1091 | 0 | _seqOrder = p[aCol+9]; |
1092 | 0 | _seqAboveXoff = p[aCol+10]; |
1093 | 0 | _seqAboveWt = p[aCol+11]; |
1094 | 0 | _seqBelowXlim = p[aCol+12]; |
1095 | 0 | _seqBelowWt = p[aCol+13]; |
1096 | 0 | _seqValignHt = p[aCol+14]; |
1097 | 0 | _seqValignWt = p[aCol+15]; |
1098 | | |
1099 | | // These attributes do not have corresponding glyph attribute: |
1100 | 0 | _exclGlyph = 0; |
1101 | 0 | _exclOffset = Position(0, 0); |
1102 | 0 | } |
1103 | | |
1104 | | float SlotCollision::getKern(int dir) const |
1105 | 0 | { |
1106 | 0 | if ((_flags & SlotCollision::COLL_KERN) != 0) |
1107 | 0 | return float(_shift.x * ((dir & 1) ? -1 : 1)); |
1108 | 0 | else |
1109 | 0 | return 0; |
1110 | 0 | } |
1111 | | |
1112 | | bool SlotCollision::ignore() const |
1113 | 0 | { |
1114 | 0 | return ((flags() & SlotCollision::COLL_IGNORE) || (flags() & SlotCollision::COLL_ISSPACE)); |
1115 | 0 | } |