blob: 2564c8a67d39e913f0b6b891b754977c649d0171 [file] [log] [blame]
satok8fbd5522011-02-22 17:28:55 +09001/*
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
Ken Wakasaf1008c52012-07-31 17:56:40 +090017#include <cassert>
18#include <cmath>
19#include <cstring>
satok8fbd5522011-02-22 17:28:55 +090020
satok817e5172011-03-04 06:06:45 -080021#define LOG_TAG "LatinIME: proximity_info.cpp"
22
satok552c3c22012-03-13 16:33:47 +090023#include "additional_proximity_chars.h"
Ken Wakasa77e8e812012-08-02 19:48:08 +090024#include "char_utils.h"
Ken Wakasa3b088a22012-05-16 23:05:32 +090025#include "defines.h"
Ken Wakasabb005f72012-08-08 20:43:47 +090026#include "jni.h"
satok8fbd5522011-02-22 17:28:55 +090027#include "proximity_info.h"
28
29namespace latinime {
Ken Wakasace9e52a2011-06-18 13:09:55 +090030
Ken Wakasabb005f72012-08-08 20:43:47 +090031static inline void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray, jsize len,
32 jint *buffer) {
33 if (jArray && buffer) {
34 env->GetIntArrayRegion(jArray, 0, len, buffer);
35 } else if (buffer) {
36 memset(buffer, 0, len);
Yusuke Nojimade2f8422011-09-27 11:15:18 +090037 }
38}
39
Ken Wakasabb005f72012-08-08 20:43:47 +090040static inline void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray, jsize len,
41 jfloat *buffer) {
42 if (jArray && buffer) {
43 env->GetFloatArrayRegion(jArray, 0, len, buffer);
44 } else if (buffer) {
45 memset(buffer, 0, len);
46 }
47}
48
Ken Wakasa01511452012-08-09 15:58:15 +090049ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int maxProximityCharsSize,
satok552c3c22012-03-13 16:33:47 +090050 const int keyboardWidth, const int keyboardHeight, const int gridWidth,
Ken Wakasabb005f72012-08-08 20:43:47 +090051 const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars,
52 const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates,
53 const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes,
54 const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs,
55 const jfloatArray sweetSpotRadii)
satok817e5172011-03-04 06:06:45 -080056 : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth),
57 KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight),
satoka70ee6e2012-03-07 15:12:22 +090058 MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth),
satok817e5172011-03-04 06:06:45 -080059 CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090060 CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
Yusuke Nojima258bfe62011-09-28 12:59:43 +090061 KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)),
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +090062 HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates
63 && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
Ken Wakasa01511452012-08-09 15:58:15 +090064 && sweetSpotCenterYs && sweetSpotRadii) {
satok1035bc92012-06-13 16:07:54 -070065 const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
satok817e5172011-03-04 06:06:45 -080066 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +090067 AKLOGI("Create proximity info array %d", proximityGridLength);
satok817e5172011-03-04 06:06:45 -080068 }
Ken Wakasa01511452012-08-09 15:58:15 +090069 const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr);
Ken Wakasa9e0c7112012-08-09 22:26:58 +090070 if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) {
71 AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length);
72 assert(false);
73 }
74 memset(mLocaleStr, 0, sizeof(mLocaleStr));
75 env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr);
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +090076 mProximityCharsArray = new int32_t[proximityGridLength];
Ken Wakasabb005f72012-08-08 20:43:47 +090077 safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray);
78 safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates);
79 safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates);
80 safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths);
81 safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights);
82 safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCharCodes);
83 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs);
84 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs);
85 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii);
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090086 initializeCodeToKeyIndex();
87}
88
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090089// Build the reversed look up table from the char code to the index in mKeyXCoordinates,
90// mKeyYCoordinates, mKeyWidths, mKeyHeights, mKeyCharCodes.
91void ProximityInfo::initializeCodeToKeyIndex() {
Yusuke Nojimaad358352011-09-29 16:44:54 +090092 memset(mCodeToKeyIndex, -1, (MAX_CHAR_CODE + 1) * sizeof(mCodeToKeyIndex[0]));
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090093 for (int i = 0; i < KEY_COUNT; ++i) {
94 const int code = mKeyCharCodes[i];
Yusuke Nojimaad358352011-09-29 16:44:54 +090095 if (0 <= code && code <= MAX_CHAR_CODE) {
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090096 mCodeToKeyIndex[code] = i;
Yusuke Nojimaad358352011-09-29 16:44:54 +090097 }
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090098 }
satok8fbd5522011-02-22 17:28:55 +090099}
100
101ProximityInfo::~ProximityInfo() {
102 delete[] mProximityCharsArray;
103}
satok817e5172011-03-04 06:06:45 -0800104
105inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const {
satok3c4bb772011-03-04 22:50:19 -0800106 return ((y / CELL_HEIGHT) * GRID_WIDTH + (x / CELL_WIDTH))
satok817e5172011-03-04 06:06:45 -0800107 * MAX_PROXIMITY_CHARS_SIZE;
satok8fbd5522011-02-22 17:28:55 +0900108}
satok817e5172011-03-04 06:06:45 -0800109
110bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
satok744dab62011-12-15 22:29:05 +0900111 if (x < 0 || y < 0) {
112 if (DEBUG_DICT) {
satok9fb6f472012-01-13 18:01:22 +0900113 AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y);
satok1a6da632011-12-16 23:15:06 +0900114 assert(false);
satok744dab62011-12-15 22:29:05 +0900115 }
116 return false;
117 }
118
satok817e5172011-03-04 06:06:45 -0800119 const int startIndex = getStartIndexFromCoordinates(x, y);
120 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900121 AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
satok817e5172011-03-04 06:06:45 -0800122 }
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900123 int32_t *proximityCharsArray = mProximityCharsArray;
satok817e5172011-03-04 06:06:45 -0800124 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
125 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900126 AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]);
satok817e5172011-03-04 06:06:45 -0800127 }
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900128 if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
satok817e5172011-03-04 06:06:45 -0800129 return true;
130 }
131 }
132 return false;
133}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900134
satok9df4a452012-03-23 16:05:18 +0900135int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const {
Jean Chalard081616c2012-03-22 17:39:27 +0900136 if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
satoka70ee6e2012-03-07 15:12:22 +0900137 const int left = mKeyXCoordinates[keyId];
138 const int top = mKeyYCoordinates[keyId];
satok1caff472012-03-14 23:17:12 +0900139 const int right = left + mKeyWidths[keyId];
satoka70ee6e2012-03-07 15:12:22 +0900140 const int bottom = top + mKeyHeights[keyId];
141 const int edgeX = x < left ? left : (x > right ? right : x);
142 const int edgeY = y < top ? top : (y > bottom ? bottom : y);
143 const int dx = x - edgeX;
144 const int dy = y - edgeY;
145 return dx * dx + dy * dy;
146}
147
148void ProximityInfo::calculateNearbyKeyCodes(
satok9df4a452012-03-23 16:05:18 +0900149 const int x, const int y, const int32_t primaryKey, int *inputCodes) const {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900150 int32_t *proximityCharsArray = mProximityCharsArray;
satoka70ee6e2012-03-07 15:12:22 +0900151 int insertPos = 0;
152 inputCodes[insertPos++] = primaryKey;
153 const int startIndex = getStartIndexFromCoordinates(x, y);
Jean Chalard52612a02012-03-23 19:38:23 +0900154 if (startIndex >= 0) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900155 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900156 const int32_t c = proximityCharsArray[startIndex + i];
Jean Chalard88ec1252012-03-23 19:25:10 +0900157 if (c < KEYCODE_SPACE || c == primaryKey) {
158 continue;
159 }
160 const int keyIndex = getKeyIndex(c);
161 const bool onKey = isOnKey(keyIndex, x, y);
162 const int distance = squaredDistanceToEdge(keyIndex, x, y);
163 if (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE) {
164 inputCodes[insertPos++] = c;
165 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
166 if (DEBUG_DICT) {
167 assert(false);
168 }
169 return;
satok0cb20972012-03-13 22:07:56 +0900170 }
satoka70ee6e2012-03-07 15:12:22 +0900171 }
172 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900173 const int additionalProximitySize =
Ken Wakasa01511452012-08-09 15:58:15 +0900174 AdditionalProximityChars::getAdditionalCharsSize(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900175 if (additionalProximitySize > 0) {
176 inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
177 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
Jean Chalard3094d122012-03-23 19:36:07 +0900178 if (DEBUG_DICT) {
179 assert(false);
180 }
181 return;
satok1caff472012-03-14 23:17:12 +0900182 }
satok1caff472012-03-14 23:17:12 +0900183
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900184 const int32_t *additionalProximityChars =
Ken Wakasa01511452012-08-09 15:58:15 +0900185 AdditionalProximityChars::getAdditionalChars(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900186 for (int j = 0; j < additionalProximitySize; ++j) {
187 const int32_t ac = additionalProximityChars[j];
188 int k = 0;
189 for (; k < insertPos; ++k) {
190 if ((int)ac == inputCodes[k]) {
191 break;
192 }
satok5eec5742012-03-13 18:26:23 +0900193 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900194 if (k < insertPos) {
195 continue;
satok0cb20972012-03-13 22:07:56 +0900196 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900197 inputCodes[insertPos++] = ac;
198 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
199 if (DEBUG_DICT) {
200 assert(false);
201 }
202 return;
Jean Chalard3094d122012-03-23 19:36:07 +0900203 }
satok0cb20972012-03-13 22:07:56 +0900204 }
satok5eec5742012-03-13 18:26:23 +0900205 }
Jean Chalard52612a02012-03-23 19:38:23 +0900206 }
satok0cb20972012-03-13 22:07:56 +0900207 // Add a delimiter for the proximity characters
satok1caff472012-03-14 23:17:12 +0900208 for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
209 inputCodes[i] = NOT_A_CODE;
210 }
satoka70ee6e2012-03-07 15:12:22 +0900211}
212
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900213int ProximityInfo::getKeyIndex(const int c) const {
satok1caff472012-03-14 23:17:12 +0900214 if (KEY_COUNT == 0) {
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900215 // We do not have the coordinate data
Jean Chalardbbc25602012-03-23 17:05:03 +0900216 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900217 }
Tadashi G. Takaoka6e3cb272011-11-11 14:26:13 +0900218 const unsigned short baseLowerC = toBaseLowerCase(c);
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900219 if (baseLowerC > MAX_CHAR_CODE) {
Jean Chalardbbc25602012-03-23 17:05:03 +0900220 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900221 }
222 return mCodeToKeyIndex[baseLowerC];
223}
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900224
225// TODO: [Staging] Optimize
226void ProximityInfo::getCenters(int *centerXs, int *centerYs, int *codeToKeyIndex,
227 int *keyToCodeIndex, int *keyCount, int *keyWidth) const {
228 *keyCount = KEY_COUNT;
Ken Wakasa77e8e812012-08-02 19:48:08 +0900229 *keyWidth = sqrt(static_cast<float>(MOST_COMMON_KEY_WIDTH_SQUARE));
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900230
231 for (int i = 0; i < KEY_COUNT; ++i) {
232 const int code = mKeyCharCodes[i];
233 const int lowerCode = toBaseLowerCase(code);
234 centerXs[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
235 centerYs[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
236 codeToKeyIndex[code] = i;
237 if (code != lowerCode && lowerCode >= 0 && lowerCode <= MAX_CHAR_CODE) {
238 codeToKeyIndex[lowerCode] = i;
239 keyToCodeIndex[i] = lowerCode;
240 } else {
241 keyToCodeIndex[i] = code;
242 }
243 }
244}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900245} // namespace latinime