Measurement.cpp revision 32c12c0ac825453f4c3bba07718cfe684ec90bec
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(buf, start, count, i)) { 58 numGraphemeClusters++; 59 if (isAfter) { 60 numGraphemeClustersAfter++; 61 } 62 } 63 } 64 if (numGraphemeClusters > 0) { 65 advance -= clusterWidth * numGraphemeClustersAfter / numGraphemeClusters; 66 } 67 } 68 return advance; 69} 70 71float getRunAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count, 72 size_t offset) { 73 return getRunAdvance(advances, buf, start, start, count, offset); 74} 75 76/** 77 * Essentially the inverse of getRunAdvance. Compute the value of offset for which the 78 * measured caret comes closest to the provided advance param, and which is on a grapheme 79 * cluster boundary. 80 * 81 * The actual implementation fast-forwards through clusters to get "close", then does a finer-grain 82 * search within the cluster and grapheme breaks. 83 */ 84size_t getOffsetForAdvance(const float* advances, const uint16_t* buf, size_t start, size_t count, 85 float advance) { 86 float x = 0.0f, xLastClusterStart = 0.0f, xSearchStart = 0.0f; 87 size_t lastClusterStart = start, searchStart = start; 88 for (size_t i = start; i < start + count; i++) { 89 if (GraphemeBreak::isGraphemeBreak(buf, start, count, i)) { 90 searchStart = lastClusterStart; 91 xSearchStart = xLastClusterStart; 92 } 93 float width = advances[i - start]; 94 if (width != 0.0f) { 95 lastClusterStart = i; 96 xLastClusterStart = x; 97 x += width; 98 if (x > advance) { 99 break; 100 } 101 } 102 } 103 size_t best = searchStart; 104 float bestDist = FLT_MAX; 105 for (size_t i = searchStart; i <= start + count; i++) { 106 if (GraphemeBreak::isGraphemeBreak(buf, start, count, i)) { 107 // "getRunAdvance(layout, buf, start, count, i) - advance" but more efficient 108 float delta = getRunAdvance(advances, buf, start, searchStart, count - searchStart, i) 109 110 + xSearchStart - advance; 111 if (std::abs(delta) < bestDist) { 112 bestDist = std::abs(delta); 113 best = i; 114 } 115 if (delta >= 0.0f) { 116 break; 117 } 118 } 119 } 120 return best; 121} 122 123} // namespace minikin 124