blob: 7bae41362d40b28ca35dfe04d2bd421d4bbbef52 [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>
satok552c3c22012-03-13 16:33:47 +090020#include <string>
satok8fbd5522011-02-22 17:28:55 +090021
satok817e5172011-03-04 06:06:45 -080022#define LOG_TAG "LatinIME: proximity_info.cpp"
23
satok552c3c22012-03-13 16:33:47 +090024#include "additional_proximity_chars.h"
Ken Wakasa77e8e812012-08-02 19:48:08 +090025#include "char_utils.h"
Ken Wakasa3b088a22012-05-16 23:05:32 +090026#include "defines.h"
Ken Wakasabb005f72012-08-08 20:43:47 +090027#include "jni.h"
satok8fbd5522011-02-22 17:28:55 +090028#include "proximity_info.h"
29
30namespace latinime {
Ken Wakasace9e52a2011-06-18 13:09:55 +090031
Ken Wakasabb005f72012-08-08 20:43:47 +090032static 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);
Yusuke Nojimade2f8422011-09-27 11:15:18 +090038 }
39}
40
Ken Wakasabb005f72012-08-08 20:43:47 +090041static 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,
satok552c3c22012-03-13 16:33:47 +090051 const int keyboardWidth, const int keyboardHeight, const int gridWidth,
Ken Wakasabb005f72012-08-08 20:43:47 +090052 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)
satok817e5172011-03-04 06:06:45 -080057 : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth),
58 KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight),
satoka70ee6e2012-03-07 15:12:22 +090059 MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth),
satok817e5172011-03-04 06:06:45 -080060 CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090061 CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
Yusuke Nojima258bfe62011-09-28 12:59:43 +090062 KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)),
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +090063 HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates
64 && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
65 && sweetSpotCenterYs && sweetSpotRadii),
Ken Wakasa9b392362012-08-08 16:46:16 +090066 mLocaleStr(localeCStr) {
satok1035bc92012-06-13 16:07:54 -070067 const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
satok817e5172011-03-04 06:06:45 -080068 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +090069 AKLOGI("Create proximity info array %d", proximityGridLength);
satok817e5172011-03-04 06:06:45 -080070 }
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +090071 mProximityCharsArray = new int32_t[proximityGridLength];
Ken Wakasabb005f72012-08-08 20:43:47 +090072 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);
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090081 initializeCodeToKeyIndex();
82}
83
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090084// Build the reversed look up table from the char code to the index in mKeyXCoordinates,
85// mKeyYCoordinates, mKeyWidths, mKeyHeights, mKeyCharCodes.
86void ProximityInfo::initializeCodeToKeyIndex() {
Yusuke Nojimaad358352011-09-29 16:44:54 +090087 memset(mCodeToKeyIndex, -1, (MAX_CHAR_CODE + 1) * sizeof(mCodeToKeyIndex[0]));
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090088 for (int i = 0; i < KEY_COUNT; ++i) {
89 const int code = mKeyCharCodes[i];
Yusuke Nojimaad358352011-09-29 16:44:54 +090090 if (0 <= code && code <= MAX_CHAR_CODE) {
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090091 mCodeToKeyIndex[code] = i;
Yusuke Nojimaad358352011-09-29 16:44:54 +090092 }
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090093 }
satok8fbd5522011-02-22 17:28:55 +090094}
95
96ProximityInfo::~ProximityInfo() {
97 delete[] mProximityCharsArray;
98}
satok817e5172011-03-04 06:06:45 -080099
100inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const {
satok3c4bb772011-03-04 22:50:19 -0800101 return ((y / CELL_HEIGHT) * GRID_WIDTH + (x / CELL_WIDTH))
satok817e5172011-03-04 06:06:45 -0800102 * MAX_PROXIMITY_CHARS_SIZE;
satok8fbd5522011-02-22 17:28:55 +0900103}
satok817e5172011-03-04 06:06:45 -0800104
105bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
satok744dab62011-12-15 22:29:05 +0900106 if (x < 0 || y < 0) {
107 if (DEBUG_DICT) {
satok9fb6f472012-01-13 18:01:22 +0900108 AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y);
satok1a6da632011-12-16 23:15:06 +0900109 assert(false);
satok744dab62011-12-15 22:29:05 +0900110 }
111 return false;
112 }
113
satok817e5172011-03-04 06:06:45 -0800114 const int startIndex = getStartIndexFromCoordinates(x, y);
115 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900116 AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
satok817e5172011-03-04 06:06:45 -0800117 }
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900118 int32_t *proximityCharsArray = mProximityCharsArray;
satok817e5172011-03-04 06:06:45 -0800119 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
120 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900121 AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]);
satok817e5172011-03-04 06:06:45 -0800122 }
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900123 if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
satok817e5172011-03-04 06:06:45 -0800124 return true;
125 }
126 }
127 return false;
128}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900129
satok9df4a452012-03-23 16:05:18 +0900130int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const {
Jean Chalard081616c2012-03-22 17:39:27 +0900131 if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
satoka70ee6e2012-03-07 15:12:22 +0900132 const int left = mKeyXCoordinates[keyId];
133 const int top = mKeyYCoordinates[keyId];
satok1caff472012-03-14 23:17:12 +0900134 const int right = left + mKeyWidths[keyId];
satoka70ee6e2012-03-07 15:12:22 +0900135 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(
satok9df4a452012-03-23 16:05:18 +0900144 const int x, const int y, const int32_t primaryKey, int *inputCodes) const {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900145 int32_t *proximityCharsArray = mProximityCharsArray;
satoka70ee6e2012-03-07 15:12:22 +0900146 int insertPos = 0;
147 inputCodes[insertPos++] = primaryKey;
148 const int startIndex = getStartIndexFromCoordinates(x, y);
Jean Chalard52612a02012-03-23 19:38:23 +0900149 if (startIndex >= 0) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900150 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900151 const int32_t c = proximityCharsArray[startIndex + i];
Jean Chalard88ec1252012-03-23 19:25:10 +0900152 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;
satok0cb20972012-03-13 22:07:56 +0900165 }
satoka70ee6e2012-03-07 15:12:22 +0900166 }
167 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900168 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) {
Jean Chalard3094d122012-03-23 19:36:07 +0900173 if (DEBUG_DICT) {
174 assert(false);
175 }
176 return;
satok1caff472012-03-14 23:17:12 +0900177 }
satok1caff472012-03-14 23:17:12 +0900178
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900179 const int32_t *additionalProximityChars =
Jean Chalard88ec1252012-03-23 19:25:10 +0900180 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 }
satok5eec5742012-03-13 18:26:23 +0900188 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900189 if (k < insertPos) {
190 continue;
satok0cb20972012-03-13 22:07:56 +0900191 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900192 inputCodes[insertPos++] = ac;
193 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
194 if (DEBUG_DICT) {
195 assert(false);
196 }
197 return;
Jean Chalard3094d122012-03-23 19:36:07 +0900198 }
satok0cb20972012-03-13 22:07:56 +0900199 }
satok5eec5742012-03-13 18:26:23 +0900200 }
Jean Chalard52612a02012-03-23 19:38:23 +0900201 }
satok0cb20972012-03-13 22:07:56 +0900202 // Add a delimiter for the proximity characters
satok1caff472012-03-14 23:17:12 +0900203 for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
204 inputCodes[i] = NOT_A_CODE;
205 }
satoka70ee6e2012-03-07 15:12:22 +0900206}
207
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900208int ProximityInfo::getKeyIndex(const int c) const {
satok1caff472012-03-14 23:17:12 +0900209 if (KEY_COUNT == 0) {
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900210 // We do not have the coordinate data
Jean Chalardbbc25602012-03-23 17:05:03 +0900211 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900212 }
Tadashi G. Takaoka6e3cb272011-11-11 14:26:13 +0900213 const unsigned short baseLowerC = toBaseLowerCase(c);
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900214 if (baseLowerC > MAX_CHAR_CODE) {
Jean Chalardbbc25602012-03-23 17:05:03 +0900215 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900216 }
217 return mCodeToKeyIndex[baseLowerC];
218}
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900219
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;
Ken Wakasa77e8e812012-08-02 19:48:08 +0900224 *keyWidth = sqrt(static_cast<float>(MOST_COMMON_KEY_WIDTH_SQUARE));
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900225
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}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900240} // namespace latinime