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 #define LOG_TAG "OpenGLRenderer" 18 19 #include <utils/JenkinsHash.h> 20 21 #include "Caches.h" 22 #include "Debug.h" 23 #include "TextDropShadowCache.h" 24 #include "Properties.h" 25 26 namespace android { 27 namespace uirenderer { 28 29 /////////////////////////////////////////////////////////////////////////////// 30 // Cache support 31 /////////////////////////////////////////////////////////////////////////////// 32 33 hash_t ShadowText::hash() const { 34 uint32_t charCount = len / sizeof(char16_t); 35 uint32_t hash = JenkinsHashMix(0, len); 36 hash = JenkinsHashMix(hash, android::hash_type(radius)); 37 hash = JenkinsHashMix(hash, android::hash_type(textSize)); 38 hash = JenkinsHashMix(hash, android::hash_type(typeface)); 39 hash = JenkinsHashMix(hash, flags); 40 hash = JenkinsHashMix(hash, android::hash_type(italicStyle)); 41 hash = JenkinsHashMix(hash, android::hash_type(scaleX)); 42 if (text) { 43 hash = JenkinsHashMixShorts(hash, text, charCount); 44 } 45 if (positions) { 46 for (uint32_t i = 0; i < charCount * 2; i++) { 47 hash = JenkinsHashMix(hash, android::hash_type(positions[i])); 48 } 49 } 50 return JenkinsHashWhiten(hash); 51 } 52 53 int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) { 54 int deltaInt = int(lhs.len) - int(rhs.len); 55 if (deltaInt != 0) return deltaInt; 56 57 deltaInt = lhs.flags - rhs.flags; 58 if (deltaInt != 0) return deltaInt; 59 60 if (lhs.radius < rhs.radius) return -1; 61 if (lhs.radius > rhs.radius) return +1; 62 63 if (lhs.typeface < rhs.typeface) return -1; 64 if (lhs.typeface > rhs.typeface) return +1; 65 66 if (lhs.textSize < rhs.textSize) return -1; 67 if (lhs.textSize > rhs.textSize) return +1; 68 69 if (lhs.italicStyle < rhs.italicStyle) return -1; 70 if (lhs.italicStyle > rhs.italicStyle) return +1; 71 72 if (lhs.scaleX < rhs.scaleX) return -1; 73 if (lhs.scaleX > rhs.scaleX) return +1; 74 75 if (lhs.text != rhs.text) { 76 if (!lhs.text) return -1; 77 if (!rhs.text) return +1; 78 79 deltaInt = memcmp(lhs.text, rhs.text, lhs.len); 80 if (deltaInt != 0) return deltaInt; 81 } 82 83 if (lhs.positions != rhs.positions) { 84 if (!lhs.positions) return -1; 85 if (!rhs.positions) return +1; 86 87 return memcmp(lhs.positions, rhs.positions, lhs.len << 2); 88 } 89 90 return 0; 91 } 92 93 /////////////////////////////////////////////////////////////////////////////// 94 // Constructors/destructor 95 /////////////////////////////////////////////////////////////////////////////// 96 97 TextDropShadowCache::TextDropShadowCache(): 98 mCache(LruCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity), 99 mSize(0), mMaxSize(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) { 100 char property[PROPERTY_VALUE_MAX]; 101 if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) { 102 INIT_LOGD(" Setting drop shadow cache size to %sMB", property); 103 setMaxSize(MB(atof(property))); 104 } else { 105 INIT_LOGD(" Using default drop shadow cache size of %.2fMB", 106 DEFAULT_DROP_SHADOW_CACHE_SIZE); 107 } 108 109 init(); 110 } 111 112 TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize): 113 mCache(LruCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity), 114 mSize(0), mMaxSize(maxByteSize) { 115 init(); 116 } 117 118 TextDropShadowCache::~TextDropShadowCache() { 119 mCache.clear(); 120 } 121 122 void TextDropShadowCache::init() { 123 mCache.setOnEntryRemovedListener(this); 124 mDebugEnabled = readDebugLevel() & kDebugMoreCaches; 125 } 126 127 /////////////////////////////////////////////////////////////////////////////// 128 // Size management 129 /////////////////////////////////////////////////////////////////////////////// 130 131 uint32_t TextDropShadowCache::getSize() { 132 return mSize; 133 } 134 135 uint32_t TextDropShadowCache::getMaxSize() { 136 return mMaxSize; 137 } 138 139 void TextDropShadowCache::setMaxSize(uint32_t maxSize) { 140 mMaxSize = maxSize; 141 while (mSize > mMaxSize) { 142 mCache.removeOldest(); 143 } 144 } 145 146 /////////////////////////////////////////////////////////////////////////////// 147 // Callbacks 148 /////////////////////////////////////////////////////////////////////////////// 149 150 void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture) { 151 if (texture) { 152 mSize -= texture->bitmapSize; 153 154 if (mDebugEnabled) { 155 ALOGD("Shadow texture deleted, size = %d", texture->bitmapSize); 156 } 157 158 texture->deleteTexture(); 159 delete texture; 160 } 161 } 162 163 /////////////////////////////////////////////////////////////////////////////// 164 // Caching 165 /////////////////////////////////////////////////////////////////////////////// 166 167 void TextDropShadowCache::clear() { 168 mCache.clear(); 169 } 170 171 ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len, 172 int numGlyphs, float radius, const float* positions) { 173 ShadowText entry(paint, radius, len, text, positions); 174 ShadowTexture* texture = mCache.get(entry); 175 176 if (!texture) { 177 SkPaint paintCopy(*paint); 178 paintCopy.setTextAlign(SkPaint::kLeft_Align); 179 FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, text, 0, 180 len, numGlyphs, radius, positions); 181 182 if (!shadow.image) { 183 return NULL; 184 } 185 186 Caches& caches = Caches::getInstance(); 187 188 texture = new ShadowTexture(caches); 189 texture->left = shadow.penX; 190 texture->top = shadow.penY; 191 texture->width = shadow.width; 192 texture->height = shadow.height; 193 texture->generation = 0; 194 texture->blend = true; 195 196 const uint32_t size = shadow.width * shadow.height; 197 texture->bitmapSize = size; 198 199 // Don't even try to cache a bitmap that's bigger than the cache 200 if (size < mMaxSize) { 201 while (mSize + size > mMaxSize) { 202 mCache.removeOldest(); 203 } 204 } 205 206 glGenTextures(1, &texture->id); 207 208 caches.bindTexture(texture->id); 209 // Textures are Alpha8 210 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 211 212 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, 213 GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image); 214 215 texture->setFilter(GL_LINEAR); 216 texture->setWrap(GL_CLAMP_TO_EDGE); 217 218 if (size < mMaxSize) { 219 if (mDebugEnabled) { 220 ALOGD("Shadow texture created, size = %d", texture->bitmapSize); 221 } 222 223 entry.copyTextLocally(); 224 225 mSize += size; 226 mCache.put(entry, texture); 227 } else { 228 texture->cleanup = true; 229 } 230 231 // Cleanup shadow 232 free(shadow.image); 233 } 234 235 return texture; 236 } 237 238 }; // namespace uirenderer 239 }; // namespace android 240