Measurement.cpp revision 4ea1cc82b0e8170d6ab6a7e9ee274c81315cd59e
140beb7744a61248de82a6077996c83c14e0122c2Raph Levien/* 240beb7744a61248de82a6077996c83c14e0122c2Raph Levien * Copyright (C) 2015 The Android Open Source Project 340beb7744a61248de82a6077996c83c14e0122c2Raph Levien * 440beb7744a61248de82a6077996c83c14e0122c2Raph Levien * Licensed under the Apache License, Version 2.0 (the "License"); 540beb7744a61248de82a6077996c83c14e0122c2Raph Levien * you may not use this file except in compliance with the License. 640beb7744a61248de82a6077996c83c14e0122c2Raph Levien * You may obtain a copy of the License at 740beb7744a61248de82a6077996c83c14e0122c2Raph Levien * 840beb7744a61248de82a6077996c83c14e0122c2Raph Levien * http://www.apache.org/licenses/LICENSE-2.0 940beb7744a61248de82a6077996c83c14e0122c2Raph Levien * 1040beb7744a61248de82a6077996c83c14e0122c2Raph Levien * Unless required by applicable law or agreed to in writing, software 1140beb7744a61248de82a6077996c83c14e0122c2Raph Levien * distributed under the License is distributed on an "AS IS" BASIS, 1240beb7744a61248de82a6077996c83c14e0122c2Raph Levien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1340beb7744a61248de82a6077996c83c14e0122c2Raph Levien * See the License for the specific language governing permissions and 1440beb7744a61248de82a6077996c83c14e0122c2Raph Levien * limitations under the License. 1540beb7744a61248de82a6077996c83c14e0122c2Raph Levien */ 1640beb7744a61248de82a6077996c83c14e0122c2Raph Levien 1740beb7744a61248de82a6077996c83c14e0122c2Raph Levien#define LOG_TAG "Minikin" 1840beb7744a61248de82a6077996c83c14e0122c2Raph Levien#include <cutils/log.h> 1940beb7744a61248de82a6077996c83c14e0122c2Raph Levien 2040beb7744a61248de82a6077996c83c14e0122c2Raph Levien#include <cmath> 2140beb7744a61248de82a6077996c83c14e0122c2Raph Levien#include <unicode/uchar.h> 2240beb7744a61248de82a6077996c83c14e0122c2Raph Levien 2340beb7744a61248de82a6077996c83c14e0122c2Raph Levien#include <minikin/GraphemeBreak.h> 2440beb7744a61248de82a6077996c83c14e0122c2Raph Levien#include <minikin/Measurement.h> 2540beb7744a61248de82a6077996c83c14e0122c2Raph Levien 2640beb7744a61248de82a6077996c83c14e0122c2Raph Leviennamespace android { 2740beb7744a61248de82a6077996c83c14e0122c2Raph Levien 2840beb7744a61248de82a6077996c83c14e0122c2Raph Levien// These could be considered helper methods of layout, but need only be loosely coupled, so 2940beb7744a61248de82a6077996c83c14e0122c2Raph Levien// are separate. 3040beb7744a61248de82a6077996c83c14e0122c2Raph Levien 314ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagistatic float getRunAdvance(Layout& layout, const uint16_t* buf, size_t layoutStart, size_t start, 324ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi size_t count, size_t offset) { 3340beb7744a61248de82a6077996c83c14e0122c2Raph Levien float advance = 0.0f; 3440beb7744a61248de82a6077996c83c14e0122c2Raph Levien size_t lastCluster = start; 3540beb7744a61248de82a6077996c83c14e0122c2Raph Levien float clusterWidth = 0.0f; 3640beb7744a61248de82a6077996c83c14e0122c2Raph Levien for (size_t i = start; i < offset; i++) { 374ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi float charAdvance = layout.getCharAdvance(i - layoutStart); 3840beb7744a61248de82a6077996c83c14e0122c2Raph Levien if (charAdvance != 0.0f) { 3940beb7744a61248de82a6077996c83c14e0122c2Raph Levien advance += charAdvance; 4040beb7744a61248de82a6077996c83c14e0122c2Raph Levien lastCluster = i; 4140beb7744a61248de82a6077996c83c14e0122c2Raph Levien clusterWidth = charAdvance; 4240beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 4340beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 444ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi if (offset < start + count && layout.getCharAdvance(offset - layoutStart) == 0.0f) { 4540beb7744a61248de82a6077996c83c14e0122c2Raph Levien // In the middle of a cluster, distribute width of cluster so that each grapheme cluster 4640beb7744a61248de82a6077996c83c14e0122c2Raph Levien // gets an equal share. 4740beb7744a61248de82a6077996c83c14e0122c2Raph Levien // TODO: get caret information out of font when that's available 4840beb7744a61248de82a6077996c83c14e0122c2Raph Levien size_t nextCluster; 4940beb7744a61248de82a6077996c83c14e0122c2Raph Levien for (nextCluster = offset + 1; nextCluster < start + count; nextCluster++) { 504ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi if (layout.getCharAdvance(nextCluster - layoutStart) != 0.0f) break; 5140beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 5240beb7744a61248de82a6077996c83c14e0122c2Raph Levien int numGraphemeClusters = 0; 5340beb7744a61248de82a6077996c83c14e0122c2Raph Levien int numGraphemeClustersAfter = 0; 5440beb7744a61248de82a6077996c83c14e0122c2Raph Levien for (size_t i = lastCluster; i < nextCluster; i++) { 5540beb7744a61248de82a6077996c83c14e0122c2Raph Levien bool isAfter = i >= offset; 5640beb7744a61248de82a6077996c83c14e0122c2Raph Levien if (GraphemeBreak::isGraphemeBreak(buf, start, count, i)) { 5740beb7744a61248de82a6077996c83c14e0122c2Raph Levien numGraphemeClusters++; 5840beb7744a61248de82a6077996c83c14e0122c2Raph Levien if (isAfter) { 5940beb7744a61248de82a6077996c83c14e0122c2Raph Levien numGraphemeClustersAfter++; 6040beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 6140beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 6240beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 6340beb7744a61248de82a6077996c83c14e0122c2Raph Levien if (numGraphemeClusters > 0) { 6440beb7744a61248de82a6077996c83c14e0122c2Raph Levien advance -= clusterWidth * numGraphemeClustersAfter / numGraphemeClusters; 6540beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 6640beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 6740beb7744a61248de82a6077996c83c14e0122c2Raph Levien return advance; 6840beb7744a61248de82a6077996c83c14e0122c2Raph Levien} 6940beb7744a61248de82a6077996c83c14e0122c2Raph Levien 704ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagifloat getRunAdvance(Layout& layout, const uint16_t* buf, size_t start, size_t count, 714ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi size_t offset) { 724ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi return getRunAdvance(layout, buf, start, start, count, offset); 734ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi} 744ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi 7540beb7744a61248de82a6077996c83c14e0122c2Raph Levien/** 7640beb7744a61248de82a6077996c83c14e0122c2Raph Levien * Essentially the inverse of getRunAdvance. Compute the value of offset for which the 7740beb7744a61248de82a6077996c83c14e0122c2Raph Levien * measured caret comes closest to the provided advance param, and which is on a grapheme 7840beb7744a61248de82a6077996c83c14e0122c2Raph Levien * cluster boundary. 7940beb7744a61248de82a6077996c83c14e0122c2Raph Levien * 8040beb7744a61248de82a6077996c83c14e0122c2Raph Levien * The actual implementation fast-forwards through clusters to get "close", then does a finer-grain 8140beb7744a61248de82a6077996c83c14e0122c2Raph Levien * search within the cluster and grapheme breaks. 8240beb7744a61248de82a6077996c83c14e0122c2Raph Levien */ 8340beb7744a61248de82a6077996c83c14e0122c2Raph Leviensize_t getOffsetForAdvance(Layout& layout, const uint16_t* buf, size_t start, size_t count, 8440beb7744a61248de82a6077996c83c14e0122c2Raph Levien float advance) { 8540beb7744a61248de82a6077996c83c14e0122c2Raph Levien float x = 0.0f, xLastClusterStart = 0.0f, xSearchStart = 0.0f; 8640beb7744a61248de82a6077996c83c14e0122c2Raph Levien size_t lastClusterStart = start, searchStart = start; 8740beb7744a61248de82a6077996c83c14e0122c2Raph Levien for (size_t i = start; i < start + count; i++) { 8840beb7744a61248de82a6077996c83c14e0122c2Raph Levien if (GraphemeBreak::isGraphemeBreak(buf, start, count, i)) { 8940beb7744a61248de82a6077996c83c14e0122c2Raph Levien searchStart = lastClusterStart; 9040beb7744a61248de82a6077996c83c14e0122c2Raph Levien xSearchStart = xLastClusterStart; 9140beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 9240beb7744a61248de82a6077996c83c14e0122c2Raph Levien float width = layout.getCharAdvance(i - start); 9340beb7744a61248de82a6077996c83c14e0122c2Raph Levien if (width != 0.0f) { 9440beb7744a61248de82a6077996c83c14e0122c2Raph Levien lastClusterStart = i; 9540beb7744a61248de82a6077996c83c14e0122c2Raph Levien xLastClusterStart = x; 9640beb7744a61248de82a6077996c83c14e0122c2Raph Levien x += width; 9740beb7744a61248de82a6077996c83c14e0122c2Raph Levien if (x > advance) { 9840beb7744a61248de82a6077996c83c14e0122c2Raph Levien break; 9940beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 10040beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 10140beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 10240beb7744a61248de82a6077996c83c14e0122c2Raph Levien size_t best = searchStart; 10340beb7744a61248de82a6077996c83c14e0122c2Raph Levien float bestDist = FLT_MAX; 10440beb7744a61248de82a6077996c83c14e0122c2Raph Levien for (size_t i = searchStart; i <= start + count; i++) { 10540beb7744a61248de82a6077996c83c14e0122c2Raph Levien if (GraphemeBreak::isGraphemeBreak(buf, start, count, i)) { 1064ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi // "getRunAdvance(layout, buf, start, count, i) - advance" but more efficient 1074ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi float delta = getRunAdvance(layout, buf, start, searchStart, count - searchStart, i) 1084ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi 1094ea1cc82b0e8170d6ab6a7e9ee274c81315cd59eKeisuke Kuroyanagi + xSearchStart - advance; 11040beb7744a61248de82a6077996c83c14e0122c2Raph Levien if (std::abs(delta) < bestDist) { 11140beb7744a61248de82a6077996c83c14e0122c2Raph Levien bestDist = std::abs(delta); 11240beb7744a61248de82a6077996c83c14e0122c2Raph Levien best = i; 11340beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 11440beb7744a61248de82a6077996c83c14e0122c2Raph Levien if (delta >= 0.0f) { 11540beb7744a61248de82a6077996c83c14e0122c2Raph Levien break; 11640beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 11740beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 11840beb7744a61248de82a6077996c83c14e0122c2Raph Levien } 11940beb7744a61248de82a6077996c83c14e0122c2Raph Levien return best; 12040beb7744a61248de82a6077996c83c14e0122c2Raph Levien} 12140beb7744a61248de82a6077996c83c14e0122c2Raph Levien 12240beb7744a61248de82a6077996c83c14e0122c2Raph Levien} 123