PathCache.cpp revision fb8b763f762ae21923c58d64caa729b012f40e05
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 <GLES2/gl2.h> 20 21#include <SkCanvas.h> 22#include <SkRect.h> 23 24#include "PathCache.h" 25#include "Properties.h" 26 27namespace android { 28namespace uirenderer { 29 30/////////////////////////////////////////////////////////////////////////////// 31// Constructors/destructor 32/////////////////////////////////////////////////////////////////////////////// 33 34PathCache::PathCache(): 35 mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity), 36 mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) { 37 char property[PROPERTY_VALUE_MAX]; 38 if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) { 39 LOGD(" Setting path cache size to %sMB", property); 40 setMaxSize(MB(atof(property))); 41 } else { 42 LOGD(" Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE); 43 } 44 init(); 45} 46 47PathCache::PathCache(uint32_t maxByteSize): 48 mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity), 49 mSize(0), mMaxSize(maxByteSize) { 50 init(); 51} 52 53PathCache::~PathCache() { 54 mCache.clear(); 55} 56 57void PathCache::init() { 58 mCache.setOnEntryRemovedListener(this); 59 60 GLint maxTextureSize; 61 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); 62 mMaxTextureSize = maxTextureSize; 63} 64 65/////////////////////////////////////////////////////////////////////////////// 66// Size management 67/////////////////////////////////////////////////////////////////////////////// 68 69uint32_t PathCache::getSize() { 70 return mSize; 71} 72 73uint32_t PathCache::getMaxSize() { 74 return mMaxSize; 75} 76 77void PathCache::setMaxSize(uint32_t maxSize) { 78 mMaxSize = maxSize; 79 while (mSize > mMaxSize) { 80 mCache.removeOldest(); 81 } 82} 83 84/////////////////////////////////////////////////////////////////////////////// 85// Callbacks 86/////////////////////////////////////////////////////////////////////////////// 87 88void PathCache::operator()(PathCacheEntry& path, PathTexture*& texture) { 89 const uint32_t size = texture->width * texture->height; 90 mSize -= size; 91 92 if (texture) { 93 glDeleteTextures(1, &texture->id); 94 delete texture; 95 } 96} 97 98/////////////////////////////////////////////////////////////////////////////// 99// Caching 100/////////////////////////////////////////////////////////////////////////////// 101 102PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { 103 PathCacheEntry entry(path, paint); 104 PathTexture* texture = mCache.get(entry); 105 106 if (!texture) { 107 texture = addTexture(entry, path, paint); 108 } else if (path->getGenerationID() != texture->generation) { 109 mCache.remove(entry); 110 texture = addTexture(entry, path, paint); 111 } 112 113 return texture; 114} 115 116PathTexture* PathCache::addTexture(const PathCacheEntry& entry, 117 const SkPath *path, const SkPaint* paint) { 118 const SkRect& bounds = path->getBounds(); 119 120 const float pathWidth = bounds.width(); 121 const float pathHeight = bounds.height(); 122 123 if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) { 124 LOGW("Path too large to be rendered into a texture"); 125 return NULL; 126 } 127 128 const float offset = entry.strokeWidth * 1.5f; 129 const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5); 130 const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5); 131 132 const uint32_t size = width * height; 133 // Don't even try to cache a bitmap that's bigger than the cache 134 if (size < mMaxSize) { 135 while (mSize + size > mMaxSize) { 136 mCache.removeOldest(); 137 } 138 } 139 140 PathTexture* texture = new PathTexture; 141 texture->left = bounds.fLeft; 142 texture->top = bounds.fTop; 143 texture->offset = offset; 144 texture->width = width; 145 texture->height = height; 146 texture->generation = path->getGenerationID(); 147 148 SkBitmap bitmap; 149 bitmap.setConfig(SkBitmap::kA8_Config, width, height); 150 bitmap.allocPixels(); 151 bitmap.eraseColor(0); 152 153 SkCanvas canvas(bitmap); 154 canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset); 155 canvas.drawPath(*path, *paint); 156 157 generateTexture(bitmap, texture); 158 159 if (size < mMaxSize) { 160 mSize += size; 161 mCache.put(entry, texture); 162 } else { 163 texture->cleanup = true; 164 } 165 166 return texture; 167} 168 169void PathCache::clear() { 170 mCache.clear(); 171} 172 173void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { 174 SkAutoLockPixels alp(bitmap); 175 if (!bitmap.readyToDraw()) { 176 LOGE("Cannot generate texture from bitmap"); 177 return; 178 } 179 180 glGenTextures(1, &texture->id); 181 182 glBindTexture(GL_TEXTURE_2D, texture->id); 183 // Textures are Alpha8 184 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 185 186 texture->blend = true; 187 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, 188 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels()); 189 190 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 191 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 192 193 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 194 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 195} 196 197}; // namespace uirenderer 198}; // namespace android 199