proximity_info.cpp revision bb005f787f4e00bd832e6a78797be10af2994061
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <cassert>
18#include <cmath>
19#include <cstring>
20#include <string>
21
22#define LOG_TAG "LatinIME: proximity_info.cpp"
23
24#include "additional_proximity_chars.h"
25#include "char_utils.h"
26#include "defines.h"
27#include "jni.h"
28#include "proximity_info.h"
29
30namespace latinime {
31
32static inline void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray, jsize len,
33        jint *buffer) {
34    if (jArray && buffer) {
35        env->GetIntArrayRegion(jArray, 0, len, buffer);
36    } else if (buffer) {
37        memset(buffer, 0, len);
38    }
39}
40
41static inline void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray, jsize len,
42        jfloat *buffer) {
43    if (jArray && buffer) {
44        env->GetFloatArrayRegion(jArray, 0, len, buffer);
45    } else if (buffer) {
46        memset(buffer, 0, len);
47    }
48}
49
50ProximityInfo::ProximityInfo(JNIEnv *env, const char *localeCStr, const int maxProximityCharsSize,
51        const int keyboardWidth, const int keyboardHeight, const int gridWidth,
52        const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars,
53        const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates,
54        const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes,
55        const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs,
56        const jfloatArray sweetSpotRadii)
57        : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth),
58          KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight),
59          MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth),
60          CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
61          CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
62          KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)),
63          HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates
64                  && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
65                  && sweetSpotCenterYs && sweetSpotRadii),
66          mLocaleStr(localeCStr) {
67    const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
68    if (DEBUG_PROXIMITY_INFO) {
69        AKLOGI("Create proximity info array %d", proximityGridLength);
70    }
71    mProximityCharsArray = new int32_t[proximityGridLength];
72    safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray);
73    safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates);
74    safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates);
75    safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths);
76    safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights);
77    safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCharCodes);
78    safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs);
79    safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs);
80    safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii);
81    initializeCodeToKeyIndex();
82}
83
84// Build the reversed look up table from the char code to the index in mKeyXCoordinates,
85// mKeyYCoordinates, mKeyWidths, mKeyHeights, mKeyCharCodes.
86void ProximityInfo::initializeCodeToKeyIndex() {
87    memset(mCodeToKeyIndex, -1, (MAX_CHAR_CODE + 1) * sizeof(mCodeToKeyIndex[0]));
88    for (int i = 0; i < KEY_COUNT; ++i) {
89        const int code = mKeyCharCodes[i];
90        if (0 <= code && code <= MAX_CHAR_CODE) {
91            mCodeToKeyIndex[code] = i;
92        }
93    }
94}
95
96ProximityInfo::~ProximityInfo() {
97    delete[] mProximityCharsArray;
98}
99
100inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const {
101    return ((y / CELL_HEIGHT) * GRID_WIDTH + (x / CELL_WIDTH))
102            * MAX_PROXIMITY_CHARS_SIZE;
103}
104
105bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
106    if (x < 0 || y < 0) {
107        if (DEBUG_DICT) {
108            AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y);
109            assert(false);
110        }
111        return false;
112    }
113
114    const int startIndex = getStartIndexFromCoordinates(x, y);
115    if (DEBUG_PROXIMITY_INFO) {
116        AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
117    }
118    int32_t *proximityCharsArray = mProximityCharsArray;
119    for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
120        if (DEBUG_PROXIMITY_INFO) {
121            AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]);
122        }
123        if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
124            return true;
125        }
126    }
127    return false;
128}
129
130int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const {
131    if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
132    const int left = mKeyXCoordinates[keyId];
133    const int top = mKeyYCoordinates[keyId];
134    const int right = left + mKeyWidths[keyId];
135    const int bottom = top + mKeyHeights[keyId];
136    const int edgeX = x < left ? left : (x > right ? right : x);
137    const int edgeY = y < top ? top : (y > bottom ? bottom : y);
138    const int dx = x - edgeX;
139    const int dy = y - edgeY;
140    return dx * dx + dy * dy;
141}
142
143void ProximityInfo::calculateNearbyKeyCodes(
144        const int x, const int y, const int32_t primaryKey, int *inputCodes) const {
145    int32_t *proximityCharsArray = mProximityCharsArray;
146    int insertPos = 0;
147    inputCodes[insertPos++] = primaryKey;
148    const int startIndex = getStartIndexFromCoordinates(x, y);
149    if (startIndex >= 0) {
150        for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
151            const int32_t c = proximityCharsArray[startIndex + i];
152            if (c < KEYCODE_SPACE || c == primaryKey) {
153                continue;
154            }
155            const int keyIndex = getKeyIndex(c);
156            const bool onKey = isOnKey(keyIndex, x, y);
157            const int distance = squaredDistanceToEdge(keyIndex, x, y);
158            if (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE) {
159                inputCodes[insertPos++] = c;
160                if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
161                    if (DEBUG_DICT) {
162                        assert(false);
163                    }
164                    return;
165                }
166            }
167        }
168        const int additionalProximitySize =
169                AdditionalProximityChars::getAdditionalCharsSize(&mLocaleStr, primaryKey);
170        if (additionalProximitySize > 0) {
171            inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
172            if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
173                if (DEBUG_DICT) {
174                    assert(false);
175                }
176                return;
177            }
178
179            const int32_t *additionalProximityChars =
180                    AdditionalProximityChars::getAdditionalChars(&mLocaleStr, primaryKey);
181            for (int j = 0; j < additionalProximitySize; ++j) {
182                const int32_t ac = additionalProximityChars[j];
183                int k = 0;
184                for (; k < insertPos; ++k) {
185                    if ((int)ac == inputCodes[k]) {
186                        break;
187                    }
188                }
189                if (k < insertPos) {
190                    continue;
191                }
192                inputCodes[insertPos++] = ac;
193                if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
194                    if (DEBUG_DICT) {
195                        assert(false);
196                    }
197                    return;
198                }
199            }
200        }
201    }
202    // Add a delimiter for the proximity characters
203    for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
204        inputCodes[i] = NOT_A_CODE;
205    }
206}
207
208int ProximityInfo::getKeyIndex(const int c) const {
209    if (KEY_COUNT == 0) {
210        // We do not have the coordinate data
211        return NOT_AN_INDEX;
212    }
213    const unsigned short baseLowerC = toBaseLowerCase(c);
214    if (baseLowerC > MAX_CHAR_CODE) {
215        return NOT_AN_INDEX;
216    }
217    return mCodeToKeyIndex[baseLowerC];
218}
219
220// TODO: [Staging] Optimize
221void ProximityInfo::getCenters(int *centerXs, int *centerYs, int *codeToKeyIndex,
222        int *keyToCodeIndex, int *keyCount, int *keyWidth) const {
223    *keyCount = KEY_COUNT;
224    *keyWidth = sqrt(static_cast<float>(MOST_COMMON_KEY_WIDTH_SQUARE));
225
226    for (int i = 0; i < KEY_COUNT; ++i) {
227        const int code = mKeyCharCodes[i];
228        const int lowerCode = toBaseLowerCase(code);
229        centerXs[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
230        centerYs[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
231        codeToKeyIndex[code] = i;
232        if (code != lowerCode && lowerCode >= 0 && lowerCode <= MAX_CHAR_CODE) {
233            codeToKeyIndex[lowerCode] = i;
234            keyToCodeIndex[i] = lowerCode;
235        } else {
236            keyToCodeIndex[i] = code;
237        }
238    }
239}
240} // namespace latinime
241