diff options
Diffstat (limited to 'libs/hwui/PathCache.cpp')
-rw-r--r-- | libs/hwui/PathCache.cpp | 443 |
1 files changed, 424 insertions, 19 deletions
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index fb687cd16ff5..490c22a00553 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,19 +15,301 @@ */ #define LOG_TAG "OpenGLRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW -#include <utils/Mutex.h> +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkPaint.h> +#include <SkPath.h> +#include <SkRect.h> -#include <sys/sysinfo.h> +#include <utils/JenkinsHash.h> +#include <utils/Trace.h> #include "Caches.h" #include "PathCache.h" -#include "Properties.h" + +#include "thread/Signal.h" +#include "thread/Task.h" +#include "thread/TaskProcessor.h" namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// +// Cache entries +/////////////////////////////////////////////////////////////////////////////// + +PathDescription::PathDescription(): + type(kShapeNone), + join(SkPaint::kDefault_Join), + cap(SkPaint::kDefault_Cap), + style(SkPaint::kFill_Style), + miter(4.0f), + strokeWidth(1.0f), + pathEffect(NULL) { + memset(&shape, 0, sizeof(Shape)); +} + +PathDescription::PathDescription(ShapeType type, SkPaint* paint): + type(type), + join(paint->getStrokeJoin()), + cap(paint->getStrokeCap()), + style(paint->getStyle()), + miter(paint->getStrokeMiter()), + strokeWidth(paint->getStrokeWidth()), + pathEffect(paint->getPathEffect()) { + memset(&shape, 0, sizeof(Shape)); +} + +hash_t PathDescription::hash() const { + uint32_t hash = JenkinsHashMix(0, type); + hash = JenkinsHashMix(hash, join); + hash = JenkinsHashMix(hash, cap); + hash = JenkinsHashMix(hash, style); + hash = JenkinsHashMix(hash, android::hash_type(miter)); + hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); + hash = JenkinsHashMix(hash, android::hash_type(pathEffect)); + hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape)); + return JenkinsHashWhiten(hash); +} + +int PathDescription::compare(const PathDescription& rhs) const { + return memcmp(this, &rhs, sizeof(PathDescription)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Utilities +/////////////////////////////////////////////////////////////////////////////// + +bool PathCache::canDrawAsConvexPath(SkPath* path, SkPaint* paint) { + // NOTE: This should only be used after PathTessellator handles joins properly + return paint->getPathEffect() == NULL && path->getConvexity() == SkPath::kConvex_Convexity; +} + +void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint, + float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { + const SkRect& bounds = path->getBounds(); + PathCache::computeBounds(bounds, paint, left, top, offset, width, height); +} + +void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint, + float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { + const float pathWidth = fmax(bounds.width(), 1.0f); + const float pathHeight = fmax(bounds.height(), 1.0f); + + left = bounds.fLeft; + top = bounds.fTop; + + offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); + + width = uint32_t(pathWidth + offset * 2.0 + 0.5); + height = uint32_t(pathHeight + offset * 2.0 + 0.5); +} + +static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) { + bitmap.setConfig(SkBitmap::kA8_Config, width, height); + bitmap.allocPixels(); + bitmap.eraseColor(0); +} + +static void initPaint(SkPaint& paint) { + // Make sure the paint is opaque, color, alpha, filter, etc. + // will be applied later when compositing the alpha8 texture + paint.setColor(0xff000000); + paint.setAlpha(255); + paint.setColorFilter(NULL); + paint.setMaskFilter(NULL); + paint.setShader(NULL); + SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode); + SkSafeUnref(paint.setXfermode(mode)); +} + +static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap, + float left, float top, float offset, uint32_t width, uint32_t height) { + initBitmap(bitmap, width, height); + + SkPaint pathPaint(*paint); + initPaint(pathPaint); + + SkCanvas canvas(bitmap); + canvas.translate(-left + offset, -top + offset); + canvas.drawPath(*path, pathPaint); +} + +static PathTexture* createTexture(float left, float top, float offset, + uint32_t width, uint32_t height, uint32_t id) { + PathTexture* texture = new PathTexture(); + texture->left = left; + texture->top = top; + texture->offset = offset; + texture->width = width; + texture->height = height; + texture->generation = id; + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Cache constructor/destructor +/////////////////////////////////////////////////////////////////////////////// + +PathCache::PathCache(): + mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) { + INIT_LOGD(" Setting %s cache size to %sMB", name, property); + setMaxSize(MB(atof(property))); + } else { + INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE); + } + init(); +} + +PathCache::~PathCache() { + mCache.clear(); +} + +void PathCache::init() { + mCache.setOnEntryRemovedListener(this); + + GLint maxTextureSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + mMaxTextureSize = maxTextureSize; + + mDebugEnabled = readDebugLevel() & kDebugCaches; +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t PathCache::getSize() { + return mSize; +} + +uint32_t PathCache::getMaxSize() { + return mMaxSize; +} + +void PathCache::setMaxSize(uint32_t maxSize) { + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void PathCache::operator()(PathDescription& entry, PathTexture*& texture) { + removeTexture(texture); +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void PathCache::removeTexture(PathTexture* texture) { + if (texture) { + const uint32_t size = texture->width * texture->height; + mSize -= size; + + PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d", + texture->id, size, mSize); + if (mDebugEnabled) { + ALOGD("Shape deleted, size = %d", size); + } + + if (texture->id) { + glDeleteTextures(1, &texture->id); + } + delete texture; + } +} + +void PathCache::purgeCache(uint32_t width, uint32_t height) { + const uint32_t size = width * height; + // Don't even try to cache a bitmap that's bigger than the cache + if (size < mMaxSize) { + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + } +} + +void PathCache::trim() { + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path, + const SkPaint* paint) { + ATRACE_CALL(); + + float left, top, offset; + uint32_t width, height; + computePathBounds(path, paint, left, top, offset, width, height); + + if (!checkTextureSize(width, height)) return NULL; + + purgeCache(width, height); + + SkBitmap bitmap; + drawPath(path, paint, bitmap, left, top, offset, width, height); + + PathTexture* texture = createTexture(left, top, offset, width, height, + path->getGenerationID()); + addTexture(entry, &bitmap, texture); + + return texture; +} + +void PathCache::addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture) { + generateTexture(*bitmap, texture); + + uint32_t size = texture->width * texture->height; + if (size < mMaxSize) { + mSize += size; + PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d", + texture->id, size, mSize); + if (mDebugEnabled) { + ALOGD("Shape created, size = %d", size); + } + mCache.put(entry, texture); + } else { + texture->cleanup = true; + } +} + +void PathCache::clear() { + mCache.clear(); +} + +void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { + SkAutoLockPixels alp(bitmap); + if (!bitmap.readyToDraw()) { + ALOGE("Cannot generate texture from bitmap"); + return; + } + + glGenTextures(1, &texture->id); + + glBindTexture(GL_TEXTURE_2D, texture->id); + // Textures are Alpha8 + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + texture->blend = true; + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels()); + + texture->setFilter(GL_LINEAR); + texture->setWrap(GL_CLAMP_TO_EDGE); +} + +/////////////////////////////////////////////////////////////////////////////// // Path precaching /////////////////////////////////////////////////////////////////////////////// @@ -52,7 +334,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { if (width <= mMaxTextureSize && height <= mMaxTextureSize) { SkBitmap* bitmap = new SkBitmap(); - PathCache::drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height); + drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height); t->setResult(bitmap); } else { texture->width = 0; @@ -62,23 +344,17 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { } /////////////////////////////////////////////////////////////////////////////// -// Path cache +// Paths /////////////////////////////////////////////////////////////////////////////// -PathCache::PathCache(): ShapeCache<PathCacheEntry>("path", - PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) { -} - -PathCache::~PathCache() { -} - void PathCache::remove(SkPath* path) { - Vector<PathCacheEntry> pathsToRemove; - LruCache<PathCacheEntry, PathTexture*>::Iterator i(mCache); + Vector<PathDescription> pathsToRemove; + LruCache<PathDescription, PathTexture*>::Iterator i(mCache); while (i.next()) { - const PathCacheEntry& key = i.key(); - if (key.path == path || key.path == path->getSourcePath()) { + const PathDescription& key = i.key(); + if (key.type == kShapePath && + (key.shape.path.mPath == path || key.shape.path.mPath == path->getSourcePath())) { pathsToRemove.push(key); } } @@ -121,7 +397,9 @@ static SkPath* getSourcePath(SkPath* path) { PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { path = getSourcePath(path); - PathCacheEntry entry(path, paint); + PathDescription entry(kShapePath, paint); + entry.shape.path.mPath = path; + PathTexture* texture = mCache.get(entry); if (!texture) { @@ -159,7 +437,9 @@ void PathCache::precache(SkPath* path, SkPaint* paint) { path = getSourcePath(path); - PathCacheEntry entry(path, paint); + PathDescription entry(kShapePath, paint); + entry.shape.path.mPath = path; + PathTexture* texture = mCache.get(entry); bool generate = false; @@ -193,5 +473,130 @@ void PathCache::precache(SkPath* path, SkPaint* paint) { } } +/////////////////////////////////////////////////////////////////////////////// +// Rounded rects +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::getRoundRect(float width, float height, + float rx, float ry, SkPaint* paint) { + PathDescription entry(kShapeRoundRect, paint); + entry.shape.roundRect.mWidth = width; + entry.shape.roundRect.mHeight = height; + entry.shape.roundRect.mRx = rx; + entry.shape.roundRect.mRy = ry; + + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + path.addRoundRect(r, rx, ry, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Circles +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::getCircle(float radius, SkPaint* paint) { + PathDescription entry(kShapeCircle, paint); + entry.shape.circle.mRadius = radius; + + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + path.addCircle(radius, radius, radius, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Ovals +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) { + PathDescription entry(kShapeOval, paint); + entry.shape.oval.mWidth = width; + entry.shape.oval.mHeight = height; + + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + path.addOval(r, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Rects +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) { + PathDescription entry(kShapeRect, paint); + entry.shape.rect.mWidth = width; + entry.shape.rect.mHeight = height; + + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + path.addRect(r, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Arcs +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::getArc(float width, float height, + float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { + PathDescription entry(kShapeArc, paint); + entry.shape.arc.mWidth = width; + entry.shape.arc.mHeight = height; + entry.shape.arc.mStartAngle = startAngle; + entry.shape.arc.mSweepAngle = sweepAngle; + entry.shape.arc.mUseCenter = useCenter; + + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + if (useCenter) { + path.moveTo(r.centerX(), r.centerY()); + } + path.arcTo(r, startAngle, sweepAngle, !useCenter); + if (useCenter) { + path.close(); + } + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + }; // namespace uirenderer }; // namespace android |