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 34 namespace android { 35 namespace 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 */ 55 struct 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 */ 76 struct 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 141 protected: 142 virtual bool lessThan(const ShapeCacheEntry& rhs) const { 143 return false; 144 } 145 }; // struct ShapeCacheEntry 146 147 148 struct 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 178 private: 179 uint32_t mWidth; 180 uint32_t mHeight; 181 uint32_t mRx; 182 uint32_t mRy; 183 }; // RoundRectShapeCacheEntry 184 185 struct 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 203 private: 204 uint32_t mRadius; 205 }; // CircleShapeCacheEntry 206 207 struct 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 228 private: 229 uint32_t mWidth; 230 uint32_t mHeight; 231 }; // OvalShapeCacheEntry 232 233 struct 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 254 private: 255 uint32_t mWidth; 256 uint32_t mHeight; 257 }; // RectShapeCacheEntry 258 259 struct 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 294 private: 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 */ 307 template<typename Entry> 308 class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> { 309 public: 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 337 protected: 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 367 private: 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 376 class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> { 377 public: 378 RoundRectShapeCache(); 379 380 PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint); 381 }; // class RoundRectShapeCache 382 383 class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> { 384 public: 385 CircleShapeCache(); 386 387 PathTexture* getCircle(float radius, SkPaint* paint); 388 }; // class CircleShapeCache 389 390 class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> { 391 public: 392 OvalShapeCache(); 393 394 PathTexture* getOval(float width, float height, SkPaint* paint); 395 }; // class OvalShapeCache 396 397 class RectShapeCache: public ShapeCache<RectShapeCacheEntry> { 398 public: 399 RectShapeCache(); 400 401 PathTexture* getRect(float width, float height, SkPaint* paint); 402 }; // class RectShapeCache 403 404 class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> { 405 public: 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 416 template<class Entry> 417 ShapeCache<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 436 template<class Entry> 437 ShapeCache<Entry>::~ShapeCache() { 438 mCache.clear(); 439 delete[] mName; 440 } 441 442 template<class Entry> 443 void 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 457 template<class Entry> 458 uint32_t ShapeCache<Entry>::getSize() { 459 return mSize; 460 } 461 462 template<class Entry> 463 uint32_t ShapeCache<Entry>::getMaxSize() { 464 return mMaxSize; 465 } 466 467 template<class Entry> 468 void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) { 469 mMaxSize = maxSize; 470 while (mSize > mMaxSize) { 471 mCache.removeOldest(); 472 } 473 } 474 475 /////////////////////////////////////////////////////////////////////////////// 476 // Callbacks 477 /////////////////////////////////////////////////////////////////////////////// 478 479 template<class Entry> 480 void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) { 481 removeTexture(texture); 482 } 483 484 /////////////////////////////////////////////////////////////////////////////// 485 // Caching 486 /////////////////////////////////////////////////////////////////////////////// 487 488 template<class Entry> 489 void 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 505 void computePathBounds(const SkPath* path, const SkPaint* paint, 506 float& left, float& top, float& offset, uint32_t& width, uint32_t& height); 507 void computeBounds(const SkRect& bounds, const SkPaint* paint, 508 float& left, float& top, float& offset, uint32_t& width, uint32_t& height); 509 510 static 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 522 template<class Entry> 523 void 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 533 template<class Entry> 534 void 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 540 template<class Entry> 541 void 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 553 template<class Entry> 554 bool 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 563 template<class Entry> 564 PathTexture* 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 591 template<class Entry> 592 void 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 609 template<class Entry> 610 void ShapeCache<Entry>::clear() { 611 mCache.clear(); 612 } 613 614 template<class Entry> 615 void 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