1/* 2 * Copyright (C) 2011 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#ifndef ANDROID_HWUI_SHAPE_CACHE_H 18#define ANDROID_HWUI_SHAPE_CACHE_H 19 20#include <GLES2/gl2.h> 21 22#include <SkBitmap.h> 23#include <SkCanvas.h> 24#include <SkPaint.h> 25#include <SkPath.h> 26#include <SkRect.h> 27 28#include "Debug.h" 29#include "Properties.h" 30#include "Texture.h" 31#include "utils/Compare.h" 32#include "utils/GenerationCache.h" 33 34namespace android { 35namespace uirenderer { 36 37/////////////////////////////////////////////////////////////////////////////// 38// Defines 39/////////////////////////////////////////////////////////////////////////////// 40 41// Debug 42#if DEBUG_SHAPES 43 #define SHAPE_LOGD(...) ALOGD(__VA_ARGS__) 44#else 45 #define SHAPE_LOGD(...) 46#endif 47 48/////////////////////////////////////////////////////////////////////////////// 49// Classes 50/////////////////////////////////////////////////////////////////////////////// 51 52/** 53 * Alpha texture used to represent a path. 54 */ 55struct PathTexture: public Texture { 56 PathTexture(): Texture() { 57 } 58 59 /** 60 * Left coordinate of the path bounds. 61 */ 62 float left; 63 /** 64 * Top coordinate of the path bounds. 65 */ 66 float top; 67 /** 68 * Offset to draw the path at the correct origin. 69 */ 70 float offset; 71}; // struct PathTexture 72 73/** 74 * Describe a shape in the shape cache. 75 */ 76struct ShapeCacheEntry { 77 enum ShapeType { 78 kShapeNone, 79 kShapeRect, 80 kShapeRoundRect, 81 kShapeCircle, 82 kShapeOval, 83 kShapeArc, 84 kShapePath 85 }; 86 87 ShapeCacheEntry() { 88 shapeType = kShapeNone; 89 join = SkPaint::kDefault_Join; 90 cap = SkPaint::kDefault_Cap; 91 style = SkPaint::kFill_Style; 92 float v = 4.0f; 93 miter = *(uint32_t*) &v; 94 v = 1.0f; 95 strokeWidth = *(uint32_t*) &v; 96 pathEffect = NULL; 97 } 98 99 ShapeCacheEntry(ShapeType type, SkPaint* paint) { 100 shapeType = type; 101 join = paint->getStrokeJoin(); 102 cap = paint->getStrokeCap(); 103 float v = paint->getStrokeMiter(); 104 miter = *(uint32_t*) &v; 105 v = paint->getStrokeWidth(); 106 strokeWidth = *(uint32_t*) &v; 107 style = paint->getStyle(); 108 pathEffect = paint->getPathEffect(); 109 } 110 111 virtual ~ShapeCacheEntry() { 112 } 113 114 ShapeType shapeType; 115 SkPaint::Join join; 116 SkPaint::Cap cap; 117 SkPaint::Style style; 118 uint32_t miter; 119 uint32_t strokeWidth; 120 SkPathEffect* pathEffect; 121 122 bool operator<(const ShapeCacheEntry& rhs) const { 123 LTE_INT(shapeType) { 124 LTE_INT(join) { 125 LTE_INT(cap) { 126 LTE_INT(style) { 127 LTE_INT(miter) { 128 LTE_INT(strokeWidth) { 129 LTE_INT(pathEffect) { 130 return lessThan(rhs); 131 } 132 } 133 } 134 } 135 } 136 } 137 } 138 return false; 139 } 140 141protected: 142 virtual bool lessThan(const ShapeCacheEntry& rhs) const { 143 return false; 144 } 145}; // struct ShapeCacheEntry 146 147 148struct RoundRectShapeCacheEntry: public ShapeCacheEntry { 149 RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint): 150 ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) { 151 mWidth = *(uint32_t*) &width; 152 mHeight = *(uint32_t*) &height; 153 mRx = *(uint32_t*) ℞ 154 mRy = *(uint32_t*) &ry; 155 } 156 157 RoundRectShapeCacheEntry(): ShapeCacheEntry() { 158 mWidth = 0; 159 mHeight = 0; 160 mRx = 0; 161 mRy = 0; 162 } 163 164 bool lessThan(const ShapeCacheEntry& r) const { 165 const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r; 166 LTE_INT(mWidth) { 167 LTE_INT(mHeight) { 168 LTE_INT(mRx) { 169 LTE_INT(mRy) { 170 return false; 171 } 172 } 173 } 174 } 175 return false; 176 } 177 178private: 179 uint32_t mWidth; 180 uint32_t mHeight; 181 uint32_t mRx; 182 uint32_t mRy; 183}; // RoundRectShapeCacheEntry 184 185struct CircleShapeCacheEntry: public ShapeCacheEntry { 186 CircleShapeCacheEntry(float radius, SkPaint* paint): 187 ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) { 188 mRadius = *(uint32_t*) &radius; 189 } 190 191 CircleShapeCacheEntry(): ShapeCacheEntry() { 192 mRadius = 0; 193 } 194 195 bool lessThan(const ShapeCacheEntry& r) const { 196 const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r; 197 LTE_INT(mRadius) { 198 return false; 199 } 200 return false; 201 } 202 203private: 204 uint32_t mRadius; 205}; // CircleShapeCacheEntry 206 207struct OvalShapeCacheEntry: public ShapeCacheEntry { 208 OvalShapeCacheEntry(float width, float height, SkPaint* paint): 209 ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) { 210 mWidth = *(uint32_t*) &width; 211 mHeight = *(uint32_t*) &height; 212 } 213 214 OvalShapeCacheEntry(): ShapeCacheEntry() { 215 mWidth = mHeight = 0; 216 } 217 218 bool lessThan(const ShapeCacheEntry& r) const { 219 const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r; 220 LTE_INT(mWidth) { 221 LTE_INT(mHeight) { 222 return false; 223 } 224 } 225 return false; 226 } 227 228private: 229 uint32_t mWidth; 230 uint32_t mHeight; 231}; // OvalShapeCacheEntry 232 233struct RectShapeCacheEntry: public ShapeCacheEntry { 234 RectShapeCacheEntry(float width, float height, SkPaint* paint): 235 ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) { 236 mWidth = *(uint32_t*) &width; 237 mHeight = *(uint32_t*) &height; 238 } 239 240 RectShapeCacheEntry(): ShapeCacheEntry() { 241 mWidth = mHeight = 0; 242 } 243 244 bool lessThan(const ShapeCacheEntry& r) const { 245 const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r; 246 LTE_INT(mWidth) { 247 LTE_INT(mHeight) { 248 return false; 249 } 250 } 251 return false; 252 } 253 254private: 255 uint32_t mWidth; 256 uint32_t mHeight; 257}; // RectShapeCacheEntry 258 259struct ArcShapeCacheEntry: public ShapeCacheEntry { 260 ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle, 261 bool useCenter, SkPaint* paint): 262 ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) { 263 mWidth = *(uint32_t*) &width; 264 mHeight = *(uint32_t*) &height; 265 mStartAngle = *(uint32_t*) &startAngle; 266 mSweepAngle = *(uint32_t*) &sweepAngle; 267 mUseCenter = useCenter ? 1 : 0; 268 } 269 270 ArcShapeCacheEntry(): ShapeCacheEntry() { 271 mWidth = 0; 272 mHeight = 0; 273 mStartAngle = 0; 274 mSweepAngle = 0; 275 mUseCenter = 0; 276 } 277 278 bool lessThan(const ShapeCacheEntry& r) const { 279 const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r; 280 LTE_INT(mWidth) { 281 LTE_INT(mHeight) { 282 LTE_INT(mStartAngle) { 283 LTE_INT(mSweepAngle) { 284 LTE_INT(mUseCenter) { 285 return false; 286 } 287 } 288 } 289 } 290 } 291 return false; 292 } 293 294private: 295 uint32_t mWidth; 296 uint32_t mHeight; 297 uint32_t mStartAngle; 298 uint32_t mSweepAngle; 299 uint32_t mUseCenter; 300}; // ArcShapeCacheEntry 301 302/** 303 * A simple LRU shape cache. The cache has a maximum size expressed in bytes. 304 * Any texture added to the cache causing the cache to grow beyond the maximum 305 * allowed size will also cause the oldest texture to be kicked out. 306 */ 307template<typename Entry> 308class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> { 309public: 310 ShapeCache(const char* name, const char* propertyName, float defaultSize); 311 ~ShapeCache(); 312 313 /** 314 * Used as a callback when an entry is removed from the cache. 315 * Do not invoke directly. 316 */ 317 void operator()(Entry& path, PathTexture*& texture); 318 319 /** 320 * Clears the cache. This causes all textures to be deleted. 321 */ 322 void clear(); 323 324 /** 325 * Sets the maximum size of the cache in bytes. 326 */ 327 void setMaxSize(uint32_t maxSize); 328 /** 329 * Returns the maximum size of the cache in bytes. 330 */ 331 uint32_t getMaxSize(); 332 /** 333 * Returns the current size of the cache in bytes. 334 */ 335 uint32_t getSize(); 336 337protected: 338 PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint); 339 PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap); 340 void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture); 341 342 /** 343 * Ensures there is enough space in the cache for a texture of the specified 344 * dimensions. 345 */ 346 void purgeCache(uint32_t width, uint32_t height); 347 348 void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height); 349 void initPaint(SkPaint& paint); 350 351 bool checkTextureSize(uint32_t width, uint32_t height); 352 353 PathTexture* get(Entry entry) { 354 return mCache.get(entry); 355 } 356 357 void removeTexture(PathTexture* texture); 358 359 GenerationCache<Entry, PathTexture*> mCache; 360 uint32_t mSize; 361 uint32_t mMaxSize; 362 GLuint mMaxTextureSize; 363 364 char* mName; 365 bool mDebugEnabled; 366 367private: 368 /** 369 * Generates the texture from a bitmap into the specified texture structure. 370 */ 371 void generateTexture(SkBitmap& bitmap, Texture* texture); 372 373 void init(); 374}; // class ShapeCache 375 376class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> { 377public: 378 RoundRectShapeCache(); 379 380 PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint); 381}; // class RoundRectShapeCache 382 383class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> { 384public: 385 CircleShapeCache(); 386 387 PathTexture* getCircle(float radius, SkPaint* paint); 388}; // class CircleShapeCache 389 390class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> { 391public: 392 OvalShapeCache(); 393 394 PathTexture* getOval(float width, float height, SkPaint* paint); 395}; // class OvalShapeCache 396 397class RectShapeCache: public ShapeCache<RectShapeCacheEntry> { 398public: 399 RectShapeCache(); 400 401 PathTexture* getRect(float width, float height, SkPaint* paint); 402}; // class RectShapeCache 403 404class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> { 405public: 406 ArcShapeCache(); 407 408 PathTexture* getArc(float width, float height, float startAngle, float sweepAngle, 409 bool useCenter, SkPaint* paint); 410}; // class ArcShapeCache 411 412/////////////////////////////////////////////////////////////////////////////// 413// Constructors/destructor 414/////////////////////////////////////////////////////////////////////////////// 415 416template<class Entry> 417ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize): 418 mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity), 419 mSize(0), mMaxSize(MB(defaultSize)) { 420 char property[PROPERTY_VALUE_MAX]; 421 if (property_get(propertyName, property, NULL) > 0) { 422 INIT_LOGD(" Setting %s cache size to %sMB", name, property); 423 setMaxSize(MB(atof(property))); 424 } else { 425 INIT_LOGD(" Using default %s cache size of %.2fMB", name, defaultSize); 426 } 427 428 size_t len = strlen(name); 429 mName = new char[len + 1]; 430 strcpy(mName, name); 431 mName[len] = '\0'; 432 433 init(); 434} 435 436template<class Entry> 437ShapeCache<Entry>::~ShapeCache() { 438 mCache.clear(); 439 delete[] mName; 440} 441 442template<class Entry> 443void ShapeCache<Entry>::init() { 444 mCache.setOnEntryRemovedListener(this); 445 446 GLint maxTextureSize; 447 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); 448 mMaxTextureSize = maxTextureSize; 449 450 mDebugEnabled = readDebugLevel() & kDebugCaches; 451} 452 453/////////////////////////////////////////////////////////////////////////////// 454// Size management 455/////////////////////////////////////////////////////////////////////////////// 456 457template<class Entry> 458uint32_t ShapeCache<Entry>::getSize() { 459 return mSize; 460} 461 462template<class Entry> 463uint32_t ShapeCache<Entry>::getMaxSize() { 464 return mMaxSize; 465} 466 467template<class Entry> 468void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) { 469 mMaxSize = maxSize; 470 while (mSize > mMaxSize) { 471 mCache.removeOldest(); 472 } 473} 474 475/////////////////////////////////////////////////////////////////////////////// 476// Callbacks 477/////////////////////////////////////////////////////////////////////////////// 478 479template<class Entry> 480void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) { 481 removeTexture(texture); 482} 483 484/////////////////////////////////////////////////////////////////////////////// 485// Caching 486/////////////////////////////////////////////////////////////////////////////// 487 488template<class Entry> 489void ShapeCache<Entry>::removeTexture(PathTexture* texture) { 490 if (texture) { 491 const uint32_t size = texture->width * texture->height; 492 mSize -= size; 493 494 SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d", 495 mName, texture->id, size, mSize); 496 if (mDebugEnabled) { 497 ALOGD("Shape %s deleted, size = %d", mName, size); 498 } 499 500 glDeleteTextures(1, &texture->id); 501 delete texture; 502 } 503} 504 505void computePathBounds(const SkPath* path, const SkPaint* paint, 506 float& left, float& top, float& offset, uint32_t& width, uint32_t& height); 507void computeBounds(const SkRect& bounds, const SkPaint* paint, 508 float& left, float& top, float& offset, uint32_t& width, uint32_t& height); 509 510static PathTexture* createTexture(float left, float top, float offset, 511 uint32_t width, uint32_t height, uint32_t id) { 512 PathTexture* texture = new PathTexture; 513 texture->left = left; 514 texture->top = top; 515 texture->offset = offset; 516 texture->width = width; 517 texture->height = height; 518 texture->generation = id; 519 return texture; 520} 521 522template<class Entry> 523void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) { 524 const uint32_t size = width * height; 525 // Don't even try to cache a bitmap that's bigger than the cache 526 if (size < mMaxSize) { 527 while (mSize + size > mMaxSize) { 528 mCache.removeOldest(); 529 } 530 } 531} 532 533template<class Entry> 534void ShapeCache<Entry>::initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) { 535 bitmap.setConfig(SkBitmap::kA8_Config, width, height); 536 bitmap.allocPixels(); 537 bitmap.eraseColor(0); 538} 539 540template<class Entry> 541void ShapeCache<Entry>::initPaint(SkPaint& paint) { 542 // Make sure the paint is opaque, color, alpha, filter, etc. 543 // will be applied later when compositing the alpha8 texture 544 paint.setColor(0xff000000); 545 paint.setAlpha(255); 546 paint.setColorFilter(NULL); 547 paint.setMaskFilter(NULL); 548 paint.setShader(NULL); 549 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode); 550 SkSafeUnref(paint.setXfermode(mode)); 551} 552 553template<class Entry> 554bool ShapeCache<Entry>::checkTextureSize(uint32_t width, uint32_t height) { 555 if (width > mMaxTextureSize || height > mMaxTextureSize) { 556 ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)", 557 mName, width, height, mMaxTextureSize, mMaxTextureSize); 558 return false; 559 } 560 return true; 561} 562 563template<class Entry> 564PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path, 565 const SkPaint* paint) { 566 567 float left, top, offset; 568 uint32_t width, height; 569 computePathBounds(path, paint, left, top, offset, width, height); 570 571 if (!checkTextureSize(width, height)) return NULL; 572 573 purgeCache(width, height); 574 575 SkBitmap bitmap; 576 initBitmap(bitmap, width, height); 577 578 SkPaint pathPaint(*paint); 579 initPaint(pathPaint); 580 581 SkCanvas canvas(bitmap); 582 canvas.translate(-left + offset, -top + offset); 583 canvas.drawPath(*path, pathPaint); 584 585 PathTexture* texture = createTexture(left, top, offset, width, height, path->getGenerationID()); 586 addTexture(entry, &bitmap, texture); 587 588 return texture; 589} 590 591template<class Entry> 592void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) { 593 generateTexture(*bitmap, texture); 594 595 uint32_t size = texture->width * texture->height; 596 if (size < mMaxSize) { 597 mSize += size; 598 SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d", 599 mName, texture->id, size, mSize); 600 if (mDebugEnabled) { 601 ALOGD("Shape %s created, size = %d", mName, size); 602 } 603 mCache.put(entry, texture); 604 } else { 605 texture->cleanup = true; 606 } 607} 608 609template<class Entry> 610void ShapeCache<Entry>::clear() { 611 mCache.clear(); 612} 613 614template<class Entry> 615void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) { 616 SkAutoLockPixels alp(bitmap); 617 if (!bitmap.readyToDraw()) { 618 ALOGE("Cannot generate texture from bitmap"); 619 return; 620 } 621 622 glGenTextures(1, &texture->id); 623 624 glBindTexture(GL_TEXTURE_2D, texture->id); 625 // Textures are Alpha8 626 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 627 628 texture->blend = true; 629 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, 630 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels()); 631 632 texture->setFilter(GL_LINEAR); 633 texture->setWrap(GL_CLAMP_TO_EDGE); 634} 635 636}; // namespace uirenderer 637}; // namespace android 638 639#endif // ANDROID_HWUI_SHAPE_CACHE_H 640