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