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 <cstring> 18 #include <cmath> 19 20 #define LOG_TAG "LatinIME: proximity_info.cpp" 21 22 #include "additional_proximity_chars.h" 23 #include "char_utils.h" 24 #include "defines.h" 25 #include "geometry_utils.h" 26 #include "jni.h" 27 #include "proximity_info.h" 28 #include "proximity_info_params.h" 29 30 namespace latinime { 31 32 static AK_FORCE_INLINE void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray, 33 jsize len, jint *buffer) { 34 if (jArray && buffer) { 35 env->GetIntArrayRegion(jArray, 0, len, buffer); 36 } else if (buffer) { 37 memset(buffer, 0, len * sizeof(buffer[0])); 38 } 39 } 40 41 static AK_FORCE_INLINE void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray, 42 jsize len, jfloat *buffer) { 43 if (jArray && buffer) { 44 env->GetFloatArrayRegion(jArray, 0, len, buffer); 45 } else if (buffer) { 46 memset(buffer, 0, len * sizeof(buffer[0])); 47 } 48 } 49 50 ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, 51 const int keyboardWidth, const int keyboardHeight, const int gridWidth, 52 const int gridHeight, const int mostCommonKeyWidth, const int mostCommonKeyHeight, 53 const jintArray proximityChars, const int keyCount, const jintArray keyXCoordinates, 54 const jintArray keyYCoordinates, const jintArray keyWidths, const jintArray keyHeights, 55 const jintArray keyCharCodes, const jfloatArray sweetSpotCenterXs, 56 const jfloatArray sweetSpotCenterYs, const jfloatArray sweetSpotRadii) 57 : GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth), 58 MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth), 59 MOST_COMMON_KEY_HEIGHT(mostCommonKeyHeight), 60 NORMALIZED_SQUARED_MOST_COMMON_KEY_HYPOTENUSE(1.0f + 61 SQUARE_FLOAT(static_cast<float>(mostCommonKeyHeight) / 62 static_cast<float>(mostCommonKeyWidth))), 63 CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), 64 CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), 65 KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), 66 KEYBOARD_WIDTH(keyboardWidth), KEYBOARD_HEIGHT(keyboardHeight), 67 KEYBOARD_HYPOTENUSE(hypotf(KEYBOARD_WIDTH, KEYBOARD_HEIGHT)), 68 HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates 69 && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs 70 && sweetSpotCenterYs && sweetSpotRadii), 71 mProximityCharsArray(new int[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE 72 /* proximityCharsLength */]), 73 mCodeToKeyMap() { 74 /* Let's check the input array length here to make sure */ 75 const jsize proximityCharsLength = env->GetArrayLength(proximityChars); 76 if (proximityCharsLength != GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE) { 77 AKLOGE("Invalid proximityCharsLength: %d", proximityCharsLength); 78 ASSERT(false); 79 return; 80 } 81 if (DEBUG_PROXIMITY_INFO) { 82 AKLOGI("Create proximity info array %d", proximityCharsLength); 83 } 84 const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr); 85 if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) { 86 AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length); 87 ASSERT(false); 88 } 89 memset(mLocaleStr, 0, sizeof(mLocaleStr)); 90 env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr); 91 safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityCharsLength, 92 mProximityCharsArray); 93 safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates); 94 safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates); 95 safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths); 96 safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights); 97 safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCodePoints); 98 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs); 99 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs); 100 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii); 101 initializeG(); 102 } 103 104 ProximityInfo::~ProximityInfo() { 105 delete[] mProximityCharsArray; 106 } 107 108 bool ProximityInfo::hasSpaceProximity(const int x, const int y) const { 109 if (x < 0 || y < 0) { 110 if (DEBUG_DICT) { 111 AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y); 112 // TODO: Enable this assertion. 113 //ASSERT(false); 114 } 115 return false; 116 } 117 118 const int startIndex = ProximityInfoUtils::getStartIndexFromCoordinates(x, y, 119 CELL_HEIGHT, CELL_WIDTH, GRID_WIDTH); 120 if (DEBUG_PROXIMITY_INFO) { 121 AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y); 122 } 123 int *proximityCharsArray = mProximityCharsArray; 124 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) { 125 if (DEBUG_PROXIMITY_INFO) { 126 AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]); 127 } 128 if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) { 129 return true; 130 } 131 } 132 return false; 133 } 134 135 float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG( 136 const int keyId, const int x, const int y, const float verticalScale) const { 137 const bool correctTouchPosition = hasTouchPositionCorrectionData(); 138 const float centerX = static_cast<float>(correctTouchPosition ? getSweetSpotCenterXAt(keyId) 139 : getKeyCenterXOfKeyIdG(keyId)); 140 const float visualKeyCenterY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId)); 141 float centerY; 142 if (correctTouchPosition) { 143 const float sweetSpotCenterY = static_cast<float>(getSweetSpotCenterYAt(keyId)); 144 const float gapY = sweetSpotCenterY - visualKeyCenterY; 145 centerY = visualKeyCenterY + gapY * verticalScale; 146 } else { 147 centerY = visualKeyCenterY; 148 } 149 const float touchX = static_cast<float>(x); 150 const float touchY = static_cast<float>(y); 151 const float keyWidth = static_cast<float>(getMostCommonKeyWidth()); 152 return ProximityInfoUtils::getSquaredDistanceFloat(centerX, centerY, touchX, touchY) 153 / SQUARE_FLOAT(keyWidth); 154 } 155 156 int ProximityInfo::getCodePointOf(const int keyIndex) const { 157 if (keyIndex < 0 || keyIndex >= KEY_COUNT) { 158 return NOT_A_CODE_POINT; 159 } 160 return mKeyIndexToCodePointG[keyIndex]; 161 } 162 163 void ProximityInfo::initializeG() { 164 // TODO: Optimize 165 for (int i = 0; i < KEY_COUNT; ++i) { 166 const int code = mKeyCodePoints[i]; 167 const int lowerCode = toLowerCase(code); 168 mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2; 169 mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2; 170 mCodeToKeyMap[lowerCode] = i; 171 mKeyIndexToCodePointG[i] = lowerCode; 172 } 173 for (int i = 0; i < KEY_COUNT; i++) { 174 mKeyKeyDistancesG[i][i] = 0; 175 for (int j = i + 1; j < KEY_COUNT; j++) { 176 mKeyKeyDistancesG[i][j] = getDistanceInt( 177 mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]); 178 mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j]; 179 } 180 } 181 } 182 183 int ProximityInfo::getKeyCenterXOfCodePointG(int charCode) const { 184 return getKeyCenterXOfKeyIdG( 185 ProximityInfoUtils::getKeyIndexOf(KEY_COUNT, charCode, &mCodeToKeyMap)); 186 } 187 188 int ProximityInfo::getKeyCenterYOfCodePointG(int charCode) const { 189 return getKeyCenterYOfKeyIdG( 190 ProximityInfoUtils::getKeyIndexOf(KEY_COUNT, charCode, &mCodeToKeyMap)); 191 } 192 193 int ProximityInfo::getKeyCenterXOfKeyIdG(int keyId) const { 194 if (keyId >= 0) { 195 return mCenterXsG[keyId]; 196 } 197 return 0; 198 } 199 200 int ProximityInfo::getKeyCenterYOfKeyIdG(int keyId) const { 201 if (keyId >= 0) { 202 return mCenterYsG[keyId]; 203 } 204 return 0; 205 } 206 207 int ProximityInfo::getKeyKeyDistanceG(const int keyId0, const int keyId1) const { 208 if (keyId0 >= 0 && keyId1 >= 0) { 209 return mKeyKeyDistancesG[keyId0][keyId1]; 210 } 211 return MAX_VALUE_FOR_WEIGHTING; 212 } 213 } // namespace latinime 214