1 /* 2 * Copyright (C) 2013 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 #include <SkBitmap.h> 18 #include <SkCanvas.h> 19 #include <SkColor.h> 20 #include <SkPaint.h> 21 #include <SkPath.h> 22 #include <SkPathEffect.h> 23 #include <SkRect.h> 24 25 #include <utils/JenkinsHash.h> 26 #include <utils/Trace.h> 27 28 #include "Caches.h" 29 #include "PathCache.h" 30 31 #include "thread/Signal.h" 32 #include "thread/TaskProcessor.h" 33 34 #include <cutils/properties.h> 35 36 namespace android { 37 namespace uirenderer { 38 39 template <class T> 40 static bool compareWidthHeight(const T& lhs, const T& rhs) { 41 return (lhs.mWidth == rhs.mWidth) && (lhs.mHeight == rhs.mHeight); 42 } 43 44 static bool compareRoundRects(const PathDescription::Shape::RoundRect& lhs, 45 const PathDescription::Shape::RoundRect& rhs) { 46 return compareWidthHeight(lhs, rhs) && lhs.mRx == rhs.mRx && lhs.mRy == rhs.mRy; 47 } 48 49 static bool compareArcs(const PathDescription::Shape::Arc& lhs, const PathDescription::Shape::Arc& rhs) { 50 return compareWidthHeight(lhs, rhs) && lhs.mStartAngle == rhs.mStartAngle && 51 lhs.mSweepAngle == rhs.mSweepAngle && lhs.mUseCenter == rhs.mUseCenter; 52 } 53 54 /////////////////////////////////////////////////////////////////////////////// 55 // Cache entries 56 /////////////////////////////////////////////////////////////////////////////// 57 58 PathDescription::PathDescription() 59 : type(ShapeType::None) 60 , join(SkPaint::kDefault_Join) 61 , cap(SkPaint::kDefault_Cap) 62 , style(SkPaint::kFill_Style) 63 , miter(4.0f) 64 , strokeWidth(1.0f) 65 , pathEffect(nullptr) { 66 // Shape bits should be set to zeroes, because they are used for hash calculation. 67 memset(&shape, 0, sizeof(Shape)); 68 } 69 70 PathDescription::PathDescription(ShapeType type, const SkPaint* paint) 71 : type(type) 72 , join(paint->getStrokeJoin()) 73 , cap(paint->getStrokeCap()) 74 , style(paint->getStyle()) 75 , miter(paint->getStrokeMiter()) 76 , strokeWidth(paint->getStrokeWidth()) 77 , pathEffect(paint->getPathEffect()) { 78 // Shape bits should be set to zeroes, because they are used for hash calculation. 79 memset(&shape, 0, sizeof(Shape)); 80 } 81 82 hash_t PathDescription::hash() const { 83 uint32_t hash = JenkinsHashMix(0, static_cast<int>(type)); 84 hash = JenkinsHashMix(hash, join); 85 hash = JenkinsHashMix(hash, cap); 86 hash = JenkinsHashMix(hash, style); 87 hash = JenkinsHashMix(hash, android::hash_type(miter)); 88 hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); 89 hash = JenkinsHashMix(hash, android::hash_type(pathEffect)); 90 hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape)); 91 return JenkinsHashWhiten(hash); 92 } 93 94 bool PathDescription::operator==(const PathDescription& rhs) const { 95 if (type != rhs.type) return false; 96 if (join != rhs.join) return false; 97 if (cap != rhs.cap) return false; 98 if (style != rhs.style) return false; 99 if (miter != rhs.miter) return false; 100 if (strokeWidth != rhs.strokeWidth) return false; 101 if (pathEffect != rhs.pathEffect) return false; 102 switch (type) { 103 case ShapeType::None: 104 return 0; 105 case ShapeType::Rect: 106 return compareWidthHeight(shape.rect, rhs.shape.rect); 107 case ShapeType::RoundRect: 108 return compareRoundRects(shape.roundRect, rhs.shape.roundRect); 109 case ShapeType::Circle: 110 return shape.circle.mRadius == rhs.shape.circle.mRadius; 111 case ShapeType::Oval: 112 return compareWidthHeight(shape.oval, rhs.shape.oval); 113 case ShapeType::Arc: 114 return compareArcs(shape.arc, rhs.shape.arc); 115 case ShapeType::Path: 116 return shape.path.mGenerationID == rhs.shape.path.mGenerationID; 117 } 118 } 119 120 /////////////////////////////////////////////////////////////////////////////// 121 // Utilities 122 /////////////////////////////////////////////////////////////////////////////// 123 124 bool PathCache::canDrawAsConvexPath(SkPath* path, const SkPaint* paint) { 125 // NOTE: This should only be used after PathTessellator handles joins properly 126 return paint->getPathEffect() == nullptr && path->getConvexity() == SkPath::kConvex_Convexity; 127 } 128 129 void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint, 130 float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { 131 const SkRect& bounds = path->getBounds(); 132 PathCache::computeBounds(bounds, paint, left, top, offset, width, height); 133 } 134 135 void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint, 136 float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { 137 const float pathWidth = std::max(bounds.width(), 1.0f); 138 const float pathHeight = std::max(bounds.height(), 1.0f); 139 140 left = bounds.fLeft; 141 top = bounds.fTop; 142 143 offset = (int) floorf(std::max(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); 144 145 width = uint32_t(pathWidth + offset * 2.0 + 0.5); 146 height = uint32_t(pathHeight + offset * 2.0 + 0.5); 147 } 148 149 static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) { 150 bitmap.allocPixels(SkImageInfo::MakeA8(width, height)); 151 bitmap.eraseColor(0); 152 } 153 154 static void initPaint(SkPaint& paint) { 155 // Make sure the paint is opaque, color, alpha, filter, etc. 156 // will be applied later when compositing the alpha8 texture 157 paint.setColor(SK_ColorBLACK); 158 paint.setAlpha(255); 159 paint.setColorFilter(nullptr); 160 paint.setMaskFilter(nullptr); 161 paint.setShader(nullptr); 162 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode); 163 SkSafeUnref(paint.setXfermode(mode)); 164 } 165 166 static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap, 167 float left, float top, float offset, uint32_t width, uint32_t height) { 168 initBitmap(bitmap, width, height); 169 170 SkPaint pathPaint(*paint); 171 initPaint(pathPaint); 172 173 SkCanvas canvas(bitmap); 174 canvas.translate(-left + offset, -top + offset); 175 canvas.drawPath(*path, pathPaint); 176 } 177 178 /////////////////////////////////////////////////////////////////////////////// 179 // Cache constructor/destructor 180 /////////////////////////////////////////////////////////////////////////////// 181 182 PathCache::PathCache() 183 : mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity) 184 , mSize(0) 185 , mMaxSize(Properties::pathCacheSize) { 186 mCache.setOnEntryRemovedListener(this); 187 188 GLint maxTextureSize; 189 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); 190 mMaxTextureSize = maxTextureSize; 191 192 mDebugEnabled = Properties::debugLevel & kDebugCaches; 193 } 194 195 PathCache::~PathCache() { 196 mCache.clear(); 197 } 198 199 /////////////////////////////////////////////////////////////////////////////// 200 // Size management 201 /////////////////////////////////////////////////////////////////////////////// 202 203 uint32_t PathCache::getSize() { 204 return mSize; 205 } 206 207 uint32_t PathCache::getMaxSize() { 208 return mMaxSize; 209 } 210 211 /////////////////////////////////////////////////////////////////////////////// 212 // Callbacks 213 /////////////////////////////////////////////////////////////////////////////// 214 215 void PathCache::operator()(PathDescription& entry, PathTexture*& texture) { 216 removeTexture(texture); 217 } 218 219 /////////////////////////////////////////////////////////////////////////////// 220 // Caching 221 /////////////////////////////////////////////////////////////////////////////// 222 223 void PathCache::removeTexture(PathTexture* texture) { 224 if (texture) { 225 const uint32_t size = texture->width() * texture->height(); 226 227 // If there is a pending task we must wait for it to return 228 // before attempting our cleanup 229 const sp<Task<SkBitmap*> >& task = texture->task(); 230 if (task != nullptr) { 231 task->getResult(); 232 texture->clearTask(); 233 } else { 234 // If there is a pending task, the path was not added 235 // to the cache and the size wasn't increased 236 if (size > mSize) { 237 ALOGE("Removing path texture of size %d will leave " 238 "the cache in an inconsistent state", size); 239 } 240 mSize -= size; 241 } 242 243 PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d", 244 texture->id, size, mSize); 245 if (mDebugEnabled) { 246 ALOGD("Shape deleted, size = %d", size); 247 } 248 249 texture->deleteTexture(); 250 delete texture; 251 } 252 } 253 254 void PathCache::purgeCache(uint32_t width, uint32_t height) { 255 const uint32_t size = width * height; 256 // Don't even try to cache a bitmap that's bigger than the cache 257 if (size < mMaxSize) { 258 while (mSize + size > mMaxSize) { 259 mCache.removeOldest(); 260 } 261 } 262 } 263 264 void PathCache::trim() { 265 while (mSize > mMaxSize) { 266 mCache.removeOldest(); 267 } 268 } 269 270 PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path, 271 const SkPaint* paint) { 272 ATRACE_NAME("Generate Path Texture"); 273 274 float left, top, offset; 275 uint32_t width, height; 276 computePathBounds(path, paint, left, top, offset, width, height); 277 278 if (!checkTextureSize(width, height)) return nullptr; 279 280 purgeCache(width, height); 281 282 SkBitmap bitmap; 283 drawPath(path, paint, bitmap, left, top, offset, width, height); 284 285 PathTexture* texture = new PathTexture(Caches::getInstance(), 286 left, top, offset, path->getGenerationID()); 287 generateTexture(entry, &bitmap, texture); 288 289 return texture; 290 } 291 292 void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap, 293 PathTexture* texture, bool addToCache) { 294 generateTexture(*bitmap, texture); 295 296 // Note here that we upload to a texture even if it's bigger than mMaxSize. 297 // Such an entry in mCache will only be temporary, since it will be evicted 298 // immediately on trim, or on any other Path entering the cache. 299 uint32_t size = texture->width() * texture->height(); 300 mSize += size; 301 PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d", 302 texture->id, size, mSize); 303 if (mDebugEnabled) { 304 ALOGD("Shape created, size = %d", size); 305 } 306 if (addToCache) { 307 mCache.put(entry, texture); 308 } 309 } 310 311 void PathCache::clear() { 312 mCache.clear(); 313 } 314 315 void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { 316 ATRACE_NAME("Upload Path Texture"); 317 texture->upload(bitmap); 318 texture->setFilter(GL_LINEAR); 319 } 320 321 /////////////////////////////////////////////////////////////////////////////// 322 // Path precaching 323 /////////////////////////////////////////////////////////////////////////////// 324 325 PathCache::PathProcessor::PathProcessor(Caches& caches): 326 TaskProcessor<SkBitmap*>(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) { 327 } 328 329 void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { 330 PathTask* t = static_cast<PathTask*>(task.get()); 331 ATRACE_NAME("pathPrecache"); 332 333 float left, top, offset; 334 uint32_t width, height; 335 PathCache::computePathBounds(&t->path, &t->paint, left, top, offset, width, height); 336 337 PathTexture* texture = t->texture; 338 texture->left = left; 339 texture->top = top; 340 texture->offset = offset; 341 342 if (width <= mMaxTextureSize && height <= mMaxTextureSize) { 343 SkBitmap* bitmap = new SkBitmap(); 344 drawPath(&t->path, &t->paint, *bitmap, left, top, offset, width, height); 345 t->setResult(bitmap); 346 } else { 347 t->setResult(nullptr); 348 } 349 } 350 351 /////////////////////////////////////////////////////////////////////////////// 352 // Paths 353 /////////////////////////////////////////////////////////////////////////////// 354 355 void PathCache::removeDeferred(const SkPath* path) { 356 Mutex::Autolock l(mLock); 357 mGarbage.push_back(path->getGenerationID()); 358 } 359 360 void PathCache::clearGarbage() { 361 Vector<PathDescription> pathsToRemove; 362 363 { // scope for the mutex 364 Mutex::Autolock l(mLock); 365 for (const uint32_t generationID : mGarbage) { 366 LruCache<PathDescription, PathTexture*>::Iterator iter(mCache); 367 while (iter.next()) { 368 const PathDescription& key = iter.key(); 369 if (key.type == ShapeType::Path && key.shape.path.mGenerationID == generationID) { 370 pathsToRemove.push(key); 371 } 372 } 373 } 374 mGarbage.clear(); 375 } 376 377 for (size_t i = 0; i < pathsToRemove.size(); i++) { 378 mCache.remove(pathsToRemove.itemAt(i)); 379 } 380 } 381 382 PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) { 383 PathDescription entry(ShapeType::Path, paint); 384 entry.shape.path.mGenerationID = path->getGenerationID(); 385 386 PathTexture* texture = mCache.get(entry); 387 388 if (!texture) { 389 texture = addTexture(entry, path, paint); 390 } else { 391 // A bitmap is attached to the texture, this means we need to 392 // upload it as a GL texture 393 const sp<Task<SkBitmap*> >& task = texture->task(); 394 if (task != nullptr) { 395 // But we must first wait for the worker thread to be done 396 // producing the bitmap, so let's wait 397 SkBitmap* bitmap = task->getResult(); 398 if (bitmap) { 399 generateTexture(entry, bitmap, texture, false); 400 texture->clearTask(); 401 } else { 402 ALOGW("Path too large to be rendered into a texture"); 403 texture->clearTask(); 404 texture = nullptr; 405 mCache.remove(entry); 406 } 407 } 408 } 409 410 return texture; 411 } 412 413 void PathCache::remove(const SkPath* path, const SkPaint* paint) { 414 PathDescription entry(ShapeType::Path, paint); 415 entry.shape.path.mGenerationID = path->getGenerationID(); 416 mCache.remove(entry); 417 } 418 419 void PathCache::precache(const SkPath* path, const SkPaint* paint) { 420 if (!Caches::getInstance().tasks.canRunTasks()) { 421 return; 422 } 423 424 PathDescription entry(ShapeType::Path, paint); 425 entry.shape.path.mGenerationID = path->getGenerationID(); 426 427 PathTexture* texture = mCache.get(entry); 428 429 bool generate = false; 430 if (!texture) { 431 generate = true; 432 } 433 434 if (generate) { 435 // It is important to specify the generation ID so we do not 436 // attempt to precache the same path several times 437 texture = new PathTexture(Caches::getInstance(), path->getGenerationID()); 438 sp<PathTask> task = new PathTask(path, paint, texture); 439 texture->setTask(task); 440 441 // During the precaching phase we insert path texture objects into 442 // the cache that do not point to any GL texture. They are instead 443 // treated as a task for the precaching worker thread. This is why 444 // we do not check the cache limit when inserting these objects. 445 // The conversion into GL texture will happen in get(), when a client 446 // asks for a path texture. This is also when the cache limit will 447 // be enforced. 448 mCache.put(entry, texture); 449 450 if (mProcessor == nullptr) { 451 mProcessor = new PathProcessor(Caches::getInstance()); 452 } 453 mProcessor->add(task); 454 } 455 } 456 457 /////////////////////////////////////////////////////////////////////////////// 458 // Rounded rects 459 /////////////////////////////////////////////////////////////////////////////// 460 461 PathTexture* PathCache::getRoundRect(float width, float height, 462 float rx, float ry, const SkPaint* paint) { 463 PathDescription entry(ShapeType::RoundRect, paint); 464 entry.shape.roundRect.mWidth = width; 465 entry.shape.roundRect.mHeight = height; 466 entry.shape.roundRect.mRx = rx; 467 entry.shape.roundRect.mRy = ry; 468 469 PathTexture* texture = get(entry); 470 471 if (!texture) { 472 SkPath path; 473 SkRect r; 474 r.set(0.0f, 0.0f, width, height); 475 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction); 476 477 texture = addTexture(entry, &path, paint); 478 } 479 480 return texture; 481 } 482 483 /////////////////////////////////////////////////////////////////////////////// 484 // Circles 485 /////////////////////////////////////////////////////////////////////////////// 486 487 PathTexture* PathCache::getCircle(float radius, const SkPaint* paint) { 488 PathDescription entry(ShapeType::Circle, paint); 489 entry.shape.circle.mRadius = radius; 490 491 PathTexture* texture = get(entry); 492 493 if (!texture) { 494 SkPath path; 495 path.addCircle(radius, radius, radius, SkPath::kCW_Direction); 496 497 texture = addTexture(entry, &path, paint); 498 } 499 500 return texture; 501 } 502 503 /////////////////////////////////////////////////////////////////////////////// 504 // Ovals 505 /////////////////////////////////////////////////////////////////////////////// 506 507 PathTexture* PathCache::getOval(float width, float height, const SkPaint* paint) { 508 PathDescription entry(ShapeType::Oval, paint); 509 entry.shape.oval.mWidth = width; 510 entry.shape.oval.mHeight = height; 511 512 PathTexture* texture = get(entry); 513 514 if (!texture) { 515 SkPath path; 516 SkRect r; 517 r.set(0.0f, 0.0f, width, height); 518 path.addOval(r, SkPath::kCW_Direction); 519 520 texture = addTexture(entry, &path, paint); 521 } 522 523 return texture; 524 } 525 526 /////////////////////////////////////////////////////////////////////////////// 527 // Rects 528 /////////////////////////////////////////////////////////////////////////////// 529 530 PathTexture* PathCache::getRect(float width, float height, const SkPaint* paint) { 531 PathDescription entry(ShapeType::Rect, paint); 532 entry.shape.rect.mWidth = width; 533 entry.shape.rect.mHeight = height; 534 535 PathTexture* texture = get(entry); 536 537 if (!texture) { 538 SkPath path; 539 SkRect r; 540 r.set(0.0f, 0.0f, width, height); 541 path.addRect(r, SkPath::kCW_Direction); 542 543 texture = addTexture(entry, &path, paint); 544 } 545 546 return texture; 547 } 548 549 /////////////////////////////////////////////////////////////////////////////// 550 // Arcs 551 /////////////////////////////////////////////////////////////////////////////// 552 553 PathTexture* PathCache::getArc(float width, float height, 554 float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) { 555 PathDescription entry(ShapeType::Arc, paint); 556 entry.shape.arc.mWidth = width; 557 entry.shape.arc.mHeight = height; 558 entry.shape.arc.mStartAngle = startAngle; 559 entry.shape.arc.mSweepAngle = sweepAngle; 560 entry.shape.arc.mUseCenter = useCenter; 561 562 PathTexture* texture = get(entry); 563 564 if (!texture) { 565 SkPath path; 566 SkRect r; 567 r.set(0.0f, 0.0f, width, height); 568 if (useCenter) { 569 path.moveTo(r.centerX(), r.centerY()); 570 } 571 path.arcTo(r, startAngle, sweepAngle, !useCenter); 572 if (useCenter) { 573 path.close(); 574 } 575 576 texture = addTexture(entry, &path, paint); 577 } 578 579 return texture; 580 } 581 582 }; // namespace uirenderer 583 }; // namespace android 584