Home | History | Annotate | Download | only in minikin
      1 /*
      2  * Copyright (C) 2013 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 VERBOSE_DEBUG
     18 
     19 #define LOG_TAG "Minikin"
     20 #include <cutils/log.h>
     21 
     22 #include "unicode/unistr.h"
     23 #include "unicode/unorm2.h"
     24 
     25 #include "MinikinInternal.h"
     26 #include <minikin/FontCollection.h>
     27 
     28 using std::vector;
     29 
     30 namespace android {
     31 
     32 template <typename T>
     33 static inline T max(T a, T b) {
     34     return a>b ? a : b;
     35 }
     36 
     37 uint32_t FontCollection::sNextId = 0;
     38 
     39 FontCollection::FontCollection(const vector<FontFamily*>& typefaces) :
     40     mMaxChar(0) {
     41     AutoMutex _l(gMinikinLock);
     42     mId = sNextId++;
     43     vector<uint32_t> lastChar;
     44     size_t nTypefaces = typefaces.size();
     45 #ifdef VERBOSE_DEBUG
     46     ALOGD("nTypefaces = %d\n", nTypefaces);
     47 #endif
     48     const FontStyle defaultStyle;
     49     for (size_t i = 0; i < nTypefaces; i++) {
     50         FontFamily* family = typefaces[i];
     51         MinikinFont* typeface = family->getClosestMatch(defaultStyle).font;
     52         if (typeface == NULL) {
     53             continue;
     54         }
     55         family->RefLocked();
     56         mFamilies.push_back(family);  // emplace_back would be better
     57         const SparseBitSet* coverage = family->getCoverage();
     58         mMaxChar = max(mMaxChar, coverage->length());
     59         lastChar.push_back(coverage->nextSetBit(0));
     60     }
     61     nTypefaces = mFamilies.size();
     62     LOG_ALWAYS_FATAL_IF(nTypefaces == 0,
     63         "Font collection must have at least one valid typeface");
     64     size_t nPages = (mMaxChar + kPageMask) >> kLogCharsPerPage;
     65     size_t offset = 0;
     66     for (size_t i = 0; i < nPages; i++) {
     67         Range dummy;
     68         mRanges.push_back(dummy);
     69         Range* range = &mRanges.back();
     70 #ifdef VERBOSE_DEBUG
     71         ALOGD("i=%d: range start = %d\n", i, offset);
     72 #endif
     73         range->start = offset;
     74         for (size_t j = 0; j < nTypefaces; j++) {
     75             if (lastChar[j] < (i + 1) << kLogCharsPerPage) {
     76                 FontFamily* family = mFamilies[j];
     77                 mFamilyVec.push_back(family);
     78                 offset++;
     79                 uint32_t nextChar = family->getCoverage()->nextSetBit((i + 1) << kLogCharsPerPage);
     80 #ifdef VERBOSE_DEBUG
     81                 ALOGD("nextChar = %d (j = %d)\n", nextChar, j);
     82 #endif
     83                 lastChar[j] = nextChar;
     84             }
     85         }
     86         range->end = offset;
     87     }
     88 }
     89 
     90 FontCollection::~FontCollection() {
     91     for (size_t i = 0; i < mFamilies.size(); i++) {
     92         mFamilies[i]->UnrefLocked();
     93     }
     94 }
     95 
     96 // Implement heuristic for choosing best-match font. Here are the rules:
     97 // 1. If first font in the collection has the character, it wins.
     98 // 2. If a font matches both language and script, it gets a score of 4.
     99 // 3. If a font matches just language, it gets a score of 2.
    100 // 4. Matching the "compact" or "elegant" variant adds one to the score.
    101 // 5. Highest score wins, with ties resolved to the first font.
    102 FontFamily* FontCollection::getFamilyForChar(uint32_t ch,
    103             FontLanguage lang, int variant) const {
    104     if (ch >= mMaxChar) {
    105         return NULL;
    106     }
    107     const Range& range = mRanges[ch >> kLogCharsPerPage];
    108 #ifdef VERBOSE_DEBUG
    109     ALOGD("querying range %d:%d\n", range.start, range.end);
    110 #endif
    111     FontFamily* bestFamily = NULL;
    112     int bestScore = -1;
    113     for (size_t i = range.start; i < range.end; i++) {
    114         FontFamily* family = mFamilyVec[i];
    115         if (family->getCoverage()->get(ch)) {
    116             // First font family in collection always matches
    117             if (mFamilies[0] == family) {
    118                 return family;
    119             }
    120             int score = lang.match(family->lang()) * 2;
    121             if (variant != 0 && variant == family->variant()) {
    122                 score++;
    123             }
    124             if (score > bestScore) {
    125                 bestScore = score;
    126                 bestFamily = family;
    127             }
    128         }
    129     }
    130     if (bestFamily == NULL && !mFamilyVec.empty()) {
    131         UErrorCode errorCode = U_ZERO_ERROR;
    132         const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode);
    133         if (U_SUCCESS(errorCode)) {
    134             UChar decomposed[4];
    135             int len = unorm2_getRawDecomposition(normalizer, ch, decomposed, 4, &errorCode);
    136             if (U_SUCCESS(errorCode) && len > 0) {
    137                 int off = 0;
    138                 U16_NEXT_UNSAFE(decomposed, off, ch);
    139                 return getFamilyForChar(ch, lang, variant);
    140             }
    141         }
    142         bestFamily = mFamilies[0];
    143     }
    144     return bestFamily;
    145 }
    146 
    147 const uint32_t NBSP = 0xa0;
    148 const uint32_t ZWJ = 0x200c;
    149 const uint32_t ZWNJ = 0x200d;
    150 const uint32_t KEYCAP = 0x20e3;
    151 
    152 // Characters where we want to continue using existing font run instead of
    153 // recomputing the best match in the fallback list.
    154 static const uint32_t stickyWhitelist[] = { '!', ',', '.', ':', ';', '?', NBSP, ZWJ, ZWNJ, KEYCAP };
    155 
    156 static bool isStickyWhitelisted(uint32_t c) {
    157     for (size_t i = 0; i < sizeof(stickyWhitelist) / sizeof(stickyWhitelist[0]); i++) {
    158         if (stickyWhitelist[i] == c) return true;
    159     }
    160     return false;
    161 }
    162 
    163 void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style,
    164         vector<Run>* result) const {
    165     FontLanguage lang = style.getLanguage();
    166     int variant = style.getVariant();
    167     FontFamily* lastFamily = NULL;
    168     Run* run = NULL;
    169     int nShorts;
    170     for (size_t i = 0; i < string_size; i += nShorts) {
    171         nShorts = 1;
    172         uint32_t ch = string[i];
    173         // sigh, decode UTF-16 by hand here
    174         if ((ch & 0xfc00) == 0xd800) {
    175             if ((i + 1) < string_size) {
    176                 ch = 0x10000 + ((ch & 0x3ff) << 10) + (string[i + 1] & 0x3ff);
    177                 nShorts = 2;
    178             }
    179         }
    180         // Continue using existing font as long as it has coverage and is whitelisted
    181         if (lastFamily == NULL
    182                 || !(isStickyWhitelisted(ch) && lastFamily->getCoverage()->get(ch))) {
    183             FontFamily* family = getFamilyForChar(ch, lang, variant);
    184             if (i == 0 || family != lastFamily) {
    185                 size_t start = i;
    186                 // Workaround for Emoji keycap until we implement per-cluster font
    187                 // selection: if keycap is found in a different font that also
    188                 // supports previous char, attach previous char to the new run.
    189                 // Only handles non-surrogate characters.
    190                 // Bug 7557244.
    191                 if (ch == KEYCAP && i && family && family->getCoverage()->get(string[i - 1])) {
    192                     run->end--;
    193                     if (run->start == run->end) {
    194                         result->pop_back();
    195                     }
    196                     start--;
    197                 }
    198                 Run dummy;
    199                 result->push_back(dummy);
    200                 run = &result->back();
    201                 if (family == NULL) {
    202                     run->fakedFont.font = NULL;
    203                 } else {
    204                     run->fakedFont = family->getClosestMatch(style);
    205                 }
    206                 lastFamily = family;
    207                 run->start = start;
    208             }
    209         }
    210         run->end = i + nShorts;
    211     }
    212 }
    213 
    214 MinikinFont* FontCollection::baseFont(FontStyle style) {
    215     return baseFontFaked(style).font;
    216 }
    217 
    218 FakedFont FontCollection::baseFontFaked(FontStyle style) {
    219     if (mFamilies.empty()) {
    220         return FakedFont();
    221     }
    222     return mFamilies[0]->getClosestMatch(style);
    223 }
    224 
    225 uint32_t FontCollection::getId() const {
    226     return mId;
    227 }
    228 
    229 }  // namespace android
    230