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