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