1/* 2 * Copyright (C) 2015 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#define LOG_TAG "Minikin" 18 19#include <cmath> 20#include <unicode/uchar.h> 21 22#include <android/log.h> 23 24#include <minikin/GraphemeBreak.h> 25#include <minikin/Measurement.h> 26 27namespace minikin { 28 29// These could be considered helper methods of layout, but need only be loosely coupled, so 30// are separate. 31 32static float getRunAdvance(const float* advances, const uint16_t* buf, size_t layoutStart, 33 size_t start, size_t count, size_t offset) { 34 float advance = 0.0f; 35 size_t lastCluster = start; 36 float clusterWidth = 0.0f; 37 for (size_t i = start; i < offset; i++) { 38 float charAdvance = advances[i - layoutStart]; 39 if (charAdvance != 0.0f) { 40 advance += charAdvance; 41 lastCluster = i; 42 clusterWidth = charAdvance; 43 } 44 } 45 if (offset < start + count && advances[offset - layoutStart] == 0.0f) { 46 // In the middle of a cluster, distribute width of cluster so that each grapheme cluster 47 // gets an equal share. 48 // TODO: get caret information out of font when that's available 49 size_t nextCluster; 50 for (nextCluster = offset + 1; nextCluster < start + count; nextCluster++) { 51 if (advances[nextCluster - layoutStart] != 0.0f) break; 52 } 53 int numGraphemeClusters = 0; 54 int numGraphemeClustersAfter = 0; 55 for (size_t i = lastCluster; i < nextCluster; i++) { 56 bool isAfter = i >= offset; 57 if (GraphemeBreak::isGraphemeBreak( 58 advances + (start - layoutStart), buf, start, count, i)) { 59 numGraphemeClusters++; 60 if (isAfter) { 61 numGraphemeClustersAfter++; 62 } 63 } 64 } 65 if (numGraphemeClusters > 0) { 66 advance -= clusterWidth * numGraphemeClustersAfter / numGraphemeClusters; 67 } 68 } 69 return advance; 70} 71 72float getRunAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count, 73 size_t offset) { 74 return getRunAdvance(advances, buf, start, start, count, offset); 75} 76 77/** 78 * Essentially the inverse of getRunAdvance. Compute the value of offset for which the 79 * measured caret comes closest to the provided advance param, and which is on a grapheme 80 * cluster boundary. 81 * 82 * The actual implementation fast-forwards through clusters to get "close", then does a finer-grain 83 * search within the cluster and grapheme breaks. 84 */ 85size_t getOffsetForAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count, 86 float advance) { 87 float x = 0.0f, xLastClusterStart = 0.0f, xSearchStart = 0.0f; 88 size_t lastClusterStart = start, searchStart = start; 89 for (size_t i = start; i < start + count; i++) { 90 if (GraphemeBreak::isGraphemeBreak(advances, buf, start, count, i)) { 91 searchStart = lastClusterStart; 92 xSearchStart = xLastClusterStart; 93 } 94 float width = advances[i - start]; 95 if (width != 0.0f) { 96 lastClusterStart = i; 97 xLastClusterStart = x; 98 x += width; 99 if (x > advance) { 100 break; 101 } 102 } 103 } 104 size_t best = searchStart; 105 float bestDist = FLT_MAX; 106 for (size_t i = searchStart; i <= start + count; i++) { 107 if (GraphemeBreak::isGraphemeBreak(advances, buf, start, count, i)) { 108 // "getRunAdvance(layout, buf, start, count, i) - advance" but more efficient 109 float delta = getRunAdvance(advances, buf, start, searchStart, count - searchStart, i) 110 111 + xSearchStart - advance; 112 if (std::abs(delta) < bestDist) { 113 bestDist = std::abs(delta); 114 best = i; 115 } 116 if (delta >= 0.0f) { 117 break; 118 } 119 } 120 } 121 return best; 122} 123 124} // namespace minikin 125