Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2010 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 #include <utils/JenkinsHash.h>
     18 
     19 #include "Caches.h"
     20 #include "Debug.h"
     21 #include "FontRenderer.h"
     22 #include "Properties.h"
     23 #include "TextDropShadowCache.h"
     24 
     25 namespace android {
     26 namespace uirenderer {
     27 
     28 ///////////////////////////////////////////////////////////////////////////////
     29 // Cache support
     30 ///////////////////////////////////////////////////////////////////////////////
     31 
     32 hash_t ShadowText::hash() const {
     33     uint32_t hash = JenkinsHashMix(0, glyphCount);
     34     hash = JenkinsHashMix(hash, android::hash_type(radius));
     35     hash = JenkinsHashMix(hash, android::hash_type(textSize));
     36     hash = JenkinsHashMix(hash, android::hash_type(typeface));
     37     hash = JenkinsHashMix(hash, flags);
     38     hash = JenkinsHashMix(hash, android::hash_type(italicStyle));
     39     hash = JenkinsHashMix(hash, android::hash_type(scaleX));
     40     if (glyphs) {
     41         hash = JenkinsHashMixShorts(hash, reinterpret_cast<const uint16_t*>(glyphs), glyphCount);
     42     }
     43     if (positions) {
     44         for (uint32_t i = 0; i < glyphCount * 2; i++) {
     45             hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
     46         }
     47     }
     48     return JenkinsHashWhiten(hash);
     49 }
     50 
     51 int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) {
     52     int deltaInt = int(lhs.glyphCount) - int(rhs.glyphCount);
     53     if (deltaInt != 0) return deltaInt;
     54 
     55     deltaInt = lhs.flags - rhs.flags;
     56     if (deltaInt != 0) return deltaInt;
     57 
     58     if (lhs.radius < rhs.radius) return -1;
     59     if (lhs.radius > rhs.radius) return +1;
     60 
     61     if (lhs.typeface < rhs.typeface) return -1;
     62     if (lhs.typeface > rhs.typeface) return +1;
     63 
     64     if (lhs.textSize < rhs.textSize) return -1;
     65     if (lhs.textSize > rhs.textSize) return +1;
     66 
     67     if (lhs.italicStyle < rhs.italicStyle) return -1;
     68     if (lhs.italicStyle > rhs.italicStyle) return +1;
     69 
     70     if (lhs.scaleX < rhs.scaleX) return -1;
     71     if (lhs.scaleX > rhs.scaleX) return +1;
     72 
     73     if (lhs.glyphs != rhs.glyphs) {
     74         if (!lhs.glyphs) return -1;
     75         if (!rhs.glyphs) return +1;
     76 
     77         deltaInt = memcmp(lhs.glyphs, rhs.glyphs, lhs.glyphCount * sizeof(glyph_t));
     78         if (deltaInt != 0) return deltaInt;
     79     }
     80 
     81     if (lhs.positions != rhs.positions) {
     82         if (!lhs.positions) return -1;
     83         if (!rhs.positions) return +1;
     84 
     85         return memcmp(lhs.positions, rhs.positions, lhs.glyphCount * sizeof(float) * 2);
     86     }
     87 
     88     return 0;
     89 }
     90 
     91 ///////////////////////////////////////////////////////////////////////////////
     92 // Constructors/destructor
     93 ///////////////////////////////////////////////////////////////////////////////
     94 
     95 TextDropShadowCache::TextDropShadowCache()
     96         : TextDropShadowCache(DeviceInfo::multiplyByResolution(2)) {}
     97 
     98 TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize)
     99         : mCache(LruCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity)
    100         , mSize(0)
    101         , mMaxSize(maxByteSize) {
    102     mCache.setOnEntryRemovedListener(this);
    103     mDebugEnabled = Properties::debugLevel & kDebugMoreCaches;
    104 }
    105 
    106 TextDropShadowCache::~TextDropShadowCache() {
    107     mCache.clear();
    108 }
    109 
    110 ///////////////////////////////////////////////////////////////////////////////
    111 // Size management
    112 ///////////////////////////////////////////////////////////////////////////////
    113 
    114 uint32_t TextDropShadowCache::getSize() {
    115     return mSize;
    116 }
    117 
    118 uint32_t TextDropShadowCache::getMaxSize() {
    119     return mMaxSize;
    120 }
    121 
    122 ///////////////////////////////////////////////////////////////////////////////
    123 // Callbacks
    124 ///////////////////////////////////////////////////////////////////////////////
    125 
    126 void TextDropShadowCache::operator()(ShadowText&, ShadowTexture*& texture) {
    127     if (texture) {
    128         mSize -= texture->objectSize();
    129 
    130         if (mDebugEnabled) {
    131             ALOGD("Shadow texture deleted, size = %d", texture->bitmapSize);
    132         }
    133 
    134         texture->deleteTexture();
    135         delete texture;
    136     }
    137 }
    138 
    139 ///////////////////////////////////////////////////////////////////////////////
    140 // Caching
    141 ///////////////////////////////////////////////////////////////////////////////
    142 
    143 void TextDropShadowCache::clear() {
    144     mCache.clear();
    145 }
    146 
    147 ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
    148                                         float radius, const float* positions) {
    149     ShadowText entry(paint, radius, numGlyphs, glyphs, positions);
    150     ShadowTexture* texture = mCache.get(entry);
    151 
    152     if (!texture) {
    153         SkPaint paintCopy(*paint);
    154         paintCopy.setTextAlign(SkPaint::kLeft_Align);
    155         FontRenderer::DropShadow shadow =
    156                 mRenderer->renderDropShadow(&paintCopy, glyphs, numGlyphs, radius, positions);
    157 
    158         if (!shadow.image) {
    159             return nullptr;
    160         }
    161 
    162         Caches& caches = Caches::getInstance();
    163 
    164         texture = new ShadowTexture(caches);
    165         texture->left = shadow.penX;
    166         texture->top = shadow.penY;
    167         texture->generation = 0;
    168         texture->blend = true;
    169 
    170         const uint32_t size = shadow.width * shadow.height;
    171 
    172         // Don't even try to cache a bitmap that's bigger than the cache
    173         if (size < mMaxSize) {
    174             while (mSize + size > mMaxSize) {
    175                 LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(),
    176                                     "Failed to remove oldest from cache. mSize = %" PRIu32
    177                                     ", mCache.size() = %zu",
    178                                     mSize, mCache.size());
    179             }
    180         }
    181 
    182         // Textures are Alpha8
    183         texture->upload(GL_ALPHA, shadow.width, shadow.height, GL_ALPHA, GL_UNSIGNED_BYTE,
    184                         shadow.image);
    185         texture->setFilter(GL_LINEAR);
    186         texture->setWrap(GL_CLAMP_TO_EDGE);
    187 
    188         if (size < mMaxSize) {
    189             if (mDebugEnabled) {
    190                 ALOGD("Shadow texture created, size = %d", texture->bitmapSize);
    191             }
    192 
    193             entry.copyTextLocally();
    194 
    195             mSize += texture->objectSize();
    196             mCache.put(entry, texture);
    197         } else {
    198             texture->cleanup = true;
    199         }
    200 
    201         // Cleanup shadow
    202         free(shadow.image);
    203     }
    204 
    205     return texture;
    206 }
    207 
    208 };  // namespace uirenderer
    209 };  // namespace android
    210