am 16b7dc9c: Merge "Merge all shapes/paths caches to PathCache" into jb-mr2-dev
* commit '16b7dc9c1ca4336b29afbd2f56f580014863add0': Merge all shapes/paths caches to PathCache
This commit is contained in:
@@ -34,7 +34,6 @@ ifeq ($(USE_OPENGL_RENDERER),true)
|
|||||||
ProgramCache.cpp \
|
ProgramCache.cpp \
|
||||||
RenderBufferCache.cpp \
|
RenderBufferCache.cpp \
|
||||||
ResourceCache.cpp \
|
ResourceCache.cpp \
|
||||||
ShapeCache.cpp \
|
|
||||||
SkiaColorFilter.cpp \
|
SkiaColorFilter.cpp \
|
||||||
SkiaShader.cpp \
|
SkiaShader.cpp \
|
||||||
Snapshot.cpp \
|
Snapshot.cpp \
|
||||||
|
|||||||
@@ -224,16 +224,6 @@ void Caches::dumpMemoryUsage(String8 &log) {
|
|||||||
gradientCache.getSize(), gradientCache.getMaxSize());
|
gradientCache.getSize(), gradientCache.getMaxSize());
|
||||||
log.appendFormat(" PathCache %8d / %8d\n",
|
log.appendFormat(" PathCache %8d / %8d\n",
|
||||||
pathCache.getSize(), pathCache.getMaxSize());
|
pathCache.getSize(), pathCache.getMaxSize());
|
||||||
log.appendFormat(" CircleShapeCache %8d / %8d\n",
|
|
||||||
circleShapeCache.getSize(), circleShapeCache.getMaxSize());
|
|
||||||
log.appendFormat(" OvalShapeCache %8d / %8d\n",
|
|
||||||
ovalShapeCache.getSize(), ovalShapeCache.getMaxSize());
|
|
||||||
log.appendFormat(" RoundRectShapeCache %8d / %8d\n",
|
|
||||||
roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize());
|
|
||||||
log.appendFormat(" RectShapeCache %8d / %8d\n",
|
|
||||||
rectShapeCache.getSize(), rectShapeCache.getMaxSize());
|
|
||||||
log.appendFormat(" ArcShapeCache %8d / %8d\n",
|
|
||||||
arcShapeCache.getSize(), arcShapeCache.getMaxSize());
|
|
||||||
log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
|
log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
|
||||||
dropShadowCache.getMaxSize());
|
dropShadowCache.getMaxSize());
|
||||||
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
|
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
|
||||||
@@ -253,11 +243,6 @@ void Caches::dumpMemoryUsage(String8 &log) {
|
|||||||
total += gradientCache.getSize();
|
total += gradientCache.getSize();
|
||||||
total += pathCache.getSize();
|
total += pathCache.getSize();
|
||||||
total += dropShadowCache.getSize();
|
total += dropShadowCache.getSize();
|
||||||
total += roundRectShapeCache.getSize();
|
|
||||||
total += circleShapeCache.getSize();
|
|
||||||
total += ovalShapeCache.getSize();
|
|
||||||
total += rectShapeCache.getSize();
|
|
||||||
total += arcShapeCache.getSize();
|
|
||||||
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
|
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
|
||||||
total += fontRenderer->getFontRendererSize(i);
|
total += fontRenderer->getFontRendererSize(i);
|
||||||
}
|
}
|
||||||
@@ -325,11 +310,6 @@ void Caches::flush(FlushMode mode) {
|
|||||||
fontRenderer->flush();
|
fontRenderer->flush();
|
||||||
textureCache.flush();
|
textureCache.flush();
|
||||||
pathCache.clear();
|
pathCache.clear();
|
||||||
roundRectShapeCache.clear();
|
|
||||||
circleShapeCache.clear();
|
|
||||||
ovalShapeCache.clear();
|
|
||||||
rectShapeCache.clear();
|
|
||||||
arcShapeCache.clear();
|
|
||||||
// fall through
|
// fall through
|
||||||
case kFlushMode_Layers:
|
case kFlushMode_Layers:
|
||||||
layerCache.clear();
|
layerCache.clear();
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
#include "GradientCache.h"
|
#include "GradientCache.h"
|
||||||
#include "PatchCache.h"
|
#include "PatchCache.h"
|
||||||
#include "ProgramCache.h"
|
#include "ProgramCache.h"
|
||||||
#include "ShapeCache.h"
|
|
||||||
#include "PathCache.h"
|
#include "PathCache.h"
|
||||||
#include "TextDropShadowCache.h"
|
#include "TextDropShadowCache.h"
|
||||||
#include "FboCache.h"
|
#include "FboCache.h"
|
||||||
@@ -269,11 +268,6 @@ public:
|
|||||||
GradientCache gradientCache;
|
GradientCache gradientCache;
|
||||||
ProgramCache programCache;
|
ProgramCache programCache;
|
||||||
PathCache pathCache;
|
PathCache pathCache;
|
||||||
RoundRectShapeCache roundRectShapeCache;
|
|
||||||
CircleShapeCache circleShapeCache;
|
|
||||||
OvalShapeCache ovalShapeCache;
|
|
||||||
RectShapeCache rectShapeCache;
|
|
||||||
ArcShapeCache arcShapeCache;
|
|
||||||
PatchCache patchCache;
|
PatchCache patchCache;
|
||||||
TextDropShadowCache dropShadowCache;
|
TextDropShadowCache dropShadowCache;
|
||||||
FboCache fboCache;
|
FboCache fboCache;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
#define DEBUG_PATCHES_EMPTY_VERTICES 0
|
#define DEBUG_PATCHES_EMPTY_VERTICES 0
|
||||||
|
|
||||||
// Turn on to display debug info about shapes
|
// Turn on to display debug info about shapes
|
||||||
#define DEBUG_SHAPES 0
|
#define DEBUG_PATHS 0
|
||||||
|
|
||||||
// Turn on to display debug info about textures
|
// Turn on to display debug info about textures
|
||||||
#define DEBUG_TEXTURES 0
|
#define DEBUG_TEXTURES 0
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
#define LOG_TAG "OpenGLRenderer"
|
#define LOG_TAG "OpenGLRenderer"
|
||||||
#define ATRACE_TAG ATRACE_TAG_VIEW
|
#define ATRACE_TAG ATRACE_TAG_VIEW
|
||||||
|
|
||||||
|
#include <SkCanvas.h>
|
||||||
|
|
||||||
#include <utils/Trace.h>
|
#include <utils/Trace.h>
|
||||||
|
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <SkCanvas.h>
|
||||||
|
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
#include "DisplayList.h"
|
#include "DisplayList.h"
|
||||||
#include "DisplayListOp.h"
|
#include "DisplayListOp.h"
|
||||||
@@ -121,9 +123,7 @@ void DisplayList::clearResources() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < mPaths.size(); i++) {
|
for (size_t i = 0; i < mPaths.size(); i++) {
|
||||||
SkPath* path = mPaths.itemAt(i);
|
delete mPaths.itemAt(i);
|
||||||
caches.pathCache.remove(path);
|
|
||||||
delete path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < mMatrices.size(); i++) {
|
for (size_t i = 0; i < mMatrices.size(); i++) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#define LOG_TAG "OpenGLRenderer"
|
#define LOG_TAG "OpenGLRenderer"
|
||||||
|
|
||||||
#include <SkCamera.h>
|
#include <SkCamera.h>
|
||||||
|
#include <SkCanvas.h>
|
||||||
|
|
||||||
#include <private/hwui/DrawGlInfo.h>
|
#include <private/hwui/DrawGlInfo.h>
|
||||||
|
|
||||||
|
|||||||
@@ -2257,9 +2257,11 @@ status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) {
|
|||||||
// TODO: try clipping large paths to viewport
|
// TODO: try clipping large paths to viewport
|
||||||
PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer);
|
PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer);
|
||||||
|
|
||||||
SkRect bounds = path.getBounds();
|
if (hasLayer()) {
|
||||||
PathTessellator::expandBoundsForStroke(bounds, paint, false);
|
SkRect bounds = path.getBounds();
|
||||||
dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
|
PathTessellator::expandBoundsForStroke(bounds, paint, false);
|
||||||
|
dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
|
||||||
|
}
|
||||||
|
|
||||||
return drawVertexBuffer(vertexBuffer, paint);
|
return drawVertexBuffer(vertexBuffer, paint);
|
||||||
}
|
}
|
||||||
@@ -2389,7 +2391,7 @@ status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float
|
|||||||
|
|
||||||
if (p->getPathEffect() != 0) {
|
if (p->getPathEffect() != 0) {
|
||||||
mCaches.activeTexture(0);
|
mCaches.activeTexture(0);
|
||||||
const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect(
|
const PathTexture* texture = mCaches.pathCache.getRoundRect(
|
||||||
right - left, bottom - top, rx, ry, p);
|
right - left, bottom - top, rx, ry, p);
|
||||||
return drawShape(left, top, texture, p);
|
return drawShape(left, top, texture, p);
|
||||||
}
|
}
|
||||||
@@ -2413,7 +2415,7 @@ status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p)
|
|||||||
}
|
}
|
||||||
if (p->getPathEffect() != 0) {
|
if (p->getPathEffect() != 0) {
|
||||||
mCaches.activeTexture(0);
|
mCaches.activeTexture(0);
|
||||||
const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, p);
|
const PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
|
||||||
return drawShape(x - radius, y - radius, texture, p);
|
return drawShape(x - radius, y - radius, texture, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2434,7 +2436,7 @@ status_t OpenGLRenderer::drawOval(float left, float top, float right, float bott
|
|||||||
|
|
||||||
if (p->getPathEffect() != 0) {
|
if (p->getPathEffect() != 0) {
|
||||||
mCaches.activeTexture(0);
|
mCaches.activeTexture(0);
|
||||||
const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, p);
|
const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
|
||||||
return drawShape(left, top, texture, p);
|
return drawShape(left, top, texture, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2460,7 +2462,7 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto
|
|||||||
// TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
|
// TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
|
||||||
if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
|
if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
|
||||||
mCaches.activeTexture(0);
|
mCaches.activeTexture(0);
|
||||||
const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top,
|
const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
|
||||||
startAngle, sweepAngle, useCenter, p);
|
startAngle, sweepAngle, useCenter, p);
|
||||||
return drawShape(left, top, texture, p);
|
return drawShape(left, top, texture, p);
|
||||||
}
|
}
|
||||||
@@ -2495,7 +2497,7 @@ status_t OpenGLRenderer::drawRect(float left, float top, float right, float bott
|
|||||||
p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
|
p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
|
||||||
mCaches.activeTexture(0);
|
mCaches.activeTexture(0);
|
||||||
const PathTexture* texture =
|
const PathTexture* texture =
|
||||||
mCaches.rectShapeCache.getRect(right - left, bottom - top, p);
|
mCaches.pathCache.getRect(right - left, bottom - top, p);
|
||||||
return drawShape(left, top, texture, p);
|
return drawShape(left, top, texture, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -15,18 +15,300 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define LOG_TAG "OpenGLRenderer"
|
#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 "Caches.h"
|
||||||
#include "PathCache.h"
|
#include "PathCache.h"
|
||||||
#include "Properties.h"
|
|
||||||
|
#include "thread/Signal.h"
|
||||||
|
#include "thread/Task.h"
|
||||||
|
#include "thread/TaskProcessor.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace uirenderer {
|
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
|
// Path precaching
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -52,7 +334,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
|
|||||||
|
|
||||||
if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
|
if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
|
||||||
SkBitmap* bitmap = new SkBitmap();
|
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);
|
t->setResult(bitmap);
|
||||||
} else {
|
} else {
|
||||||
texture->width = 0;
|
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) {
|
void PathCache::remove(SkPath* path) {
|
||||||
Vector<PathCacheEntry> pathsToRemove;
|
Vector<PathDescription> pathsToRemove;
|
||||||
LruCache<PathCacheEntry, PathTexture*>::Iterator i(mCache);
|
LruCache<PathDescription, PathTexture*>::Iterator i(mCache);
|
||||||
|
|
||||||
while (i.next()) {
|
while (i.next()) {
|
||||||
const PathCacheEntry& key = i.key();
|
const PathDescription& key = i.key();
|
||||||
if (key.path == path || key.path == path->getSourcePath()) {
|
if (key.type == kShapePath &&
|
||||||
|
(key.shape.path.mPath == path || key.shape.path.mPath == path->getSourcePath())) {
|
||||||
pathsToRemove.push(key);
|
pathsToRemove.push(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,7 +397,9 @@ static SkPath* getSourcePath(SkPath* path) {
|
|||||||
PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
|
PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
|
||||||
path = getSourcePath(path);
|
path = getSourcePath(path);
|
||||||
|
|
||||||
PathCacheEntry entry(path, paint);
|
PathDescription entry(kShapePath, paint);
|
||||||
|
entry.shape.path.mPath = path;
|
||||||
|
|
||||||
PathTexture* texture = mCache.get(entry);
|
PathTexture* texture = mCache.get(entry);
|
||||||
|
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
@@ -159,7 +437,9 @@ void PathCache::precache(SkPath* path, SkPaint* paint) {
|
|||||||
|
|
||||||
path = getSourcePath(path);
|
path = getSourcePath(path);
|
||||||
|
|
||||||
PathCacheEntry entry(path, paint);
|
PathDescription entry(kShapePath, paint);
|
||||||
|
entry.shape.path.mPath = path;
|
||||||
|
|
||||||
PathTexture* texture = mCache.get(entry);
|
PathTexture* texture = mCache.get(entry);
|
||||||
|
|
||||||
bool generate = false;
|
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 uirenderer
|
||||||
}; // namespace android
|
}; // namespace android
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,74 +17,203 @@
|
|||||||
#ifndef ANDROID_HWUI_PATH_CACHE_H
|
#ifndef ANDROID_HWUI_PATH_CACHE_H
|
||||||
#define ANDROID_HWUI_PATH_CACHE_H
|
#define ANDROID_HWUI_PATH_CACHE_H
|
||||||
|
|
||||||
#include <utils/Thread.h>
|
#include <GLES2/gl2.h>
|
||||||
|
|
||||||
|
#include <utils/LruCache.h>
|
||||||
|
#include <utils/Mutex.h>
|
||||||
#include <utils/Vector.h>
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
#include "ShapeCache.h"
|
#include "Properties.h"
|
||||||
#include "thread/Signal.h"
|
#include "Texture.h"
|
||||||
#include "thread/Task.h"
|
|
||||||
#include "thread/TaskProcessor.h"
|
|
||||||
|
|
||||||
|
class SkBitmap;
|
||||||
|
class SkCanvas;
|
||||||
class SkPaint;
|
class SkPaint;
|
||||||
class SkPath;
|
class SkPath;
|
||||||
|
class SkRect;
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace uirenderer {
|
namespace uirenderer {
|
||||||
|
|
||||||
class Caches;
|
class Caches;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Defines
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
#if DEBUG_PATHS
|
||||||
|
#define PATH_LOGD(...) ALOGD(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define PATH_LOGD(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Classes
|
// Classes
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
struct PathCacheEntry: public ShapeCacheEntry {
|
/**
|
||||||
PathCacheEntry(SkPath* path, SkPaint* paint):
|
* Alpha texture used to represent a path.
|
||||||
ShapeCacheEntry(ShapeCacheEntry::kShapePath, paint) {
|
*/
|
||||||
this->path = path;
|
struct PathTexture: public Texture {
|
||||||
|
PathTexture(): Texture() {
|
||||||
}
|
}
|
||||||
|
|
||||||
PathCacheEntry(): ShapeCacheEntry() {
|
~PathTexture() {
|
||||||
path = NULL;
|
clearTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
hash_t hash() const {
|
/**
|
||||||
uint32_t hash = ShapeCacheEntry::hash();
|
* Left coordinate of the path bounds.
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(path));
|
*/
|
||||||
return JenkinsHashWhiten(hash);
|
float left;
|
||||||
|
/**
|
||||||
|
* Top coordinate of the path bounds.
|
||||||
|
*/
|
||||||
|
float top;
|
||||||
|
/**
|
||||||
|
* Offset to draw the path at the correct origin.
|
||||||
|
*/
|
||||||
|
float offset;
|
||||||
|
|
||||||
|
sp<Task<SkBitmap*> > task() const {
|
||||||
|
return mTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
int compare(const ShapeCacheEntry& r) const {
|
void setTask(const sp<Task<SkBitmap*> >& task) {
|
||||||
int deltaInt = ShapeCacheEntry::compare(r);
|
mTask = task;
|
||||||
if (deltaInt != 0) return deltaInt;
|
|
||||||
|
|
||||||
const PathCacheEntry& rhs = (const PathCacheEntry&) r;
|
|
||||||
return path - rhs.path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SkPath* path;
|
void clearTask() {
|
||||||
|
if (mTask != NULL) {
|
||||||
|
mTask.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}; // PathCacheEntry
|
private:
|
||||||
|
sp<Task<SkBitmap*> > mTask;
|
||||||
|
}; // struct PathTexture
|
||||||
|
|
||||||
inline hash_t hash_type(const PathCacheEntry& entry) {
|
enum ShapeType {
|
||||||
return entry.hash();
|
kShapeNone,
|
||||||
}
|
kShapeRect,
|
||||||
|
kShapeRoundRect,
|
||||||
|
kShapeCircle,
|
||||||
|
kShapeOval,
|
||||||
|
kShapeArc,
|
||||||
|
kShapePath
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PathDescription {
|
||||||
|
ShapeType type;
|
||||||
|
SkPaint::Join join;
|
||||||
|
SkPaint::Cap cap;
|
||||||
|
SkPaint::Style style;
|
||||||
|
float miter;
|
||||||
|
float strokeWidth;
|
||||||
|
SkPathEffect* pathEffect;
|
||||||
|
union Shape {
|
||||||
|
struct Path {
|
||||||
|
SkPath* mPath;
|
||||||
|
} path;
|
||||||
|
struct RoundRect {
|
||||||
|
float mWidth;
|
||||||
|
float mHeight;
|
||||||
|
float mRx;
|
||||||
|
float mRy;
|
||||||
|
} roundRect;
|
||||||
|
struct Circle {
|
||||||
|
float mRadius;
|
||||||
|
} circle;
|
||||||
|
struct Oval {
|
||||||
|
float mWidth;
|
||||||
|
float mHeight;
|
||||||
|
} oval;
|
||||||
|
struct Rect {
|
||||||
|
float mWidth;
|
||||||
|
float mHeight;
|
||||||
|
} rect;
|
||||||
|
struct Arc {
|
||||||
|
float mWidth;
|
||||||
|
float mHeight;
|
||||||
|
float mStartAngle;
|
||||||
|
float mSweepAngle;
|
||||||
|
bool mUseCenter;
|
||||||
|
} arc;
|
||||||
|
} shape;
|
||||||
|
|
||||||
|
PathDescription();
|
||||||
|
PathDescription(ShapeType shapeType, SkPaint* paint);
|
||||||
|
|
||||||
|
hash_t hash() const;
|
||||||
|
|
||||||
|
int compare(const PathDescription& rhs) const;
|
||||||
|
|
||||||
|
bool operator==(const PathDescription& other) const {
|
||||||
|
return compare(other) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const PathDescription& other) const {
|
||||||
|
return compare(other) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline int strictly_order_type(
|
||||||
|
const PathDescription& lhs, const PathDescription& rhs) {
|
||||||
|
return lhs.compare(rhs) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline int compare_type(const PathDescription& lhs, const PathDescription& rhs) {
|
||||||
|
return lhs.compare(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline hash_t hash_type(const PathDescription& entry) {
|
||||||
|
return entry.hash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple LRU path cache. The cache has a maximum size expressed in bytes.
|
* A simple LRU shape cache. The cache has a maximum size expressed in bytes.
|
||||||
* Any texture added to the cache causing the cache to grow beyond the maximum
|
* Any texture added to the cache causing the cache to grow beyond the maximum
|
||||||
* allowed size will also cause the oldest texture to be kicked out.
|
* allowed size will also cause the oldest texture to be kicked out.
|
||||||
*/
|
*/
|
||||||
class PathCache: public ShapeCache<PathCacheEntry> {
|
class PathCache: public OnEntryRemoved<PathDescription, PathTexture*> {
|
||||||
public:
|
public:
|
||||||
PathCache();
|
PathCache();
|
||||||
~PathCache();
|
~PathCache();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the texture associated with the specified path. If the texture
|
* Used as a callback when an entry is removed from the cache.
|
||||||
* cannot be found in the cache, a new texture is generated.
|
* Do not invoke directly.
|
||||||
*/
|
*/
|
||||||
|
void operator()(PathDescription& path, PathTexture*& texture);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the cache. This causes all textures to be deleted.
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum size of the cache in bytes.
|
||||||
|
*/
|
||||||
|
void setMaxSize(uint32_t maxSize);
|
||||||
|
/**
|
||||||
|
* Returns the maximum size of the cache in bytes.
|
||||||
|
*/
|
||||||
|
uint32_t getMaxSize();
|
||||||
|
/**
|
||||||
|
* Returns the current size of the cache in bytes.
|
||||||
|
*/
|
||||||
|
uint32_t getSize();
|
||||||
|
|
||||||
|
PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
|
||||||
|
PathTexture* getCircle(float radius, SkPaint* paint);
|
||||||
|
PathTexture* getOval(float width, float height, SkPaint* paint);
|
||||||
|
PathTexture* getRect(float width, float height, SkPaint* paint);
|
||||||
|
PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
|
||||||
|
bool useCenter, SkPaint* paint);
|
||||||
PathTexture* get(SkPath* path, SkPaint* paint);
|
PathTexture* get(SkPath* path, SkPaint* paint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an entry.
|
* Removes an entry.
|
||||||
*/
|
*/
|
||||||
@@ -98,10 +227,63 @@ public:
|
|||||||
* Process deferred removals.
|
* Process deferred removals.
|
||||||
*/
|
*/
|
||||||
void clearGarbage();
|
void clearGarbage();
|
||||||
|
/**
|
||||||
|
* Trims the contents of the cache, removing items until it's under its
|
||||||
|
* specified limit.
|
||||||
|
*
|
||||||
|
* Trimming is used for caches that support pre-caching from a worker
|
||||||
|
* thread. During pre-caching the maximum limit of the cache can be
|
||||||
|
* exceeded for the duration of the frame. It is therefore required to
|
||||||
|
* trim the cache at the end of the frame to keep the total amount of
|
||||||
|
* memory used under control.
|
||||||
|
*/
|
||||||
|
void trim();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Precaches the specified path using background threads.
|
||||||
|
*/
|
||||||
void precache(SkPath* path, SkPaint* paint);
|
void precache(SkPath* path, SkPaint* paint);
|
||||||
|
|
||||||
|
static bool canDrawAsConvexPath(SkPath* path, SkPaint* paint);
|
||||||
|
static void computePathBounds(const SkPath* path, const SkPaint* paint,
|
||||||
|
float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
|
||||||
|
static void computeBounds(const SkRect& bounds, const SkPaint* paint,
|
||||||
|
float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
PathTexture* addTexture(const PathDescription& entry,
|
||||||
|
const SkPath *path, const SkPaint* paint);
|
||||||
|
PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
|
||||||
|
void addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture);
|
||||||
|
|
||||||
|
PathTexture* get(const PathDescription& entry) {
|
||||||
|
return mCache.get(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures there is enough space in the cache for a texture of the specified
|
||||||
|
* dimensions.
|
||||||
|
*/
|
||||||
|
void purgeCache(uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
void removeTexture(PathTexture* texture);
|
||||||
|
|
||||||
|
bool checkTextureSize(uint32_t width, uint32_t height) {
|
||||||
|
if (width > mMaxTextureSize || height > mMaxTextureSize) {
|
||||||
|
ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)",
|
||||||
|
width, height, mMaxTextureSize, mMaxTextureSize);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the texture from a bitmap into the specified texture structure.
|
||||||
|
*/
|
||||||
|
void generateTexture(SkBitmap& bitmap, Texture* texture);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
class PathTask: public Task<SkBitmap*> {
|
class PathTask: public Task<SkBitmap*> {
|
||||||
public:
|
public:
|
||||||
PathTask(SkPath* path, SkPaint* paint, PathTexture* texture):
|
PathTask(SkPath* path, SkPaint* paint, PathTexture* texture):
|
||||||
@@ -128,6 +310,13 @@ private:
|
|||||||
uint32_t mMaxTextureSize;
|
uint32_t mMaxTextureSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
LruCache<PathDescription, PathTexture*> mCache;
|
||||||
|
uint32_t mSize;
|
||||||
|
uint32_t mMaxSize;
|
||||||
|
GLuint mMaxTextureSize;
|
||||||
|
|
||||||
|
bool mDebugEnabled;
|
||||||
|
|
||||||
sp<PathProcessor> mProcessor;
|
sp<PathProcessor> mProcessor;
|
||||||
Vector<SkPath*> mGarbage;
|
Vector<SkPath*> mGarbage;
|
||||||
mutable Mutex mLock;
|
mutable Mutex mLock;
|
||||||
|
|||||||
@@ -115,7 +115,6 @@ enum DebugLevel {
|
|||||||
#define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
|
#define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
|
||||||
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
|
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
|
||||||
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
|
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
|
||||||
#define PROPERTY_SHAPE_CACHE_SIZE "ro.hwui.shape_cache_size"
|
|
||||||
#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
|
#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
|
||||||
#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
|
#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
|
||||||
|
|
||||||
@@ -159,8 +158,7 @@ enum DebugLevel {
|
|||||||
#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
|
#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
|
||||||
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
|
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
|
||||||
#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
|
#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
|
||||||
#define DEFAULT_PATH_CACHE_SIZE 4.0f
|
#define DEFAULT_PATH_CACHE_SIZE 10.0f
|
||||||
#define DEFAULT_SHAPE_CACHE_SIZE 1.0f
|
|
||||||
#define DEFAULT_PATCH_CACHE_SIZE 512
|
#define DEFAULT_PATCH_CACHE_SIZE 512
|
||||||
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
|
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
|
||||||
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
|
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
|
||||||
|
|||||||
@@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 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.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "OpenGLRenderer"
|
|
||||||
|
|
||||||
#include "ShapeCache.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace uirenderer {
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Rounded rects
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
RoundRectShapeCache::RoundRectShapeCache(): ShapeCache<RoundRectShapeCacheEntry>(
|
|
||||||
"round rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PathTexture* RoundRectShapeCache::getRoundRect(float width, float height,
|
|
||||||
float rx, float ry, SkPaint* paint) {
|
|
||||||
RoundRectShapeCacheEntry entry(width, height, rx, ry, paint);
|
|
||||||
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
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
CircleShapeCache::CircleShapeCache(): ShapeCache<CircleShapeCacheEntry>(
|
|
||||||
"circle", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PathTexture* CircleShapeCache::getCircle(float radius, SkPaint* paint) {
|
|
||||||
CircleShapeCacheEntry entry(radius, paint);
|
|
||||||
PathTexture* texture = get(entry);
|
|
||||||
|
|
||||||
if (!texture) {
|
|
||||||
SkPath path;
|
|
||||||
path.addCircle(radius, radius, radius, SkPath::kCW_Direction);
|
|
||||||
|
|
||||||
texture = addTexture(entry, &path, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Ovals
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
OvalShapeCache::OvalShapeCache(): ShapeCache<OvalShapeCacheEntry>(
|
|
||||||
"oval", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PathTexture* OvalShapeCache::getOval(float width, float height, SkPaint* paint) {
|
|
||||||
OvalShapeCacheEntry entry(width, height, paint);
|
|
||||||
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
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
RectShapeCache::RectShapeCache(): ShapeCache<RectShapeCacheEntry>(
|
|
||||||
"rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PathTexture* RectShapeCache::getRect(float width, float height, SkPaint* paint) {
|
|
||||||
RectShapeCacheEntry entry(width, height, paint);
|
|
||||||
PathTexture* texture = get(entry);
|
|
||||||
|
|
||||||
if (!texture) {
|
|
||||||
SkRect bounds;
|
|
||||||
bounds.set(0.0f, 0.0f, width, height);
|
|
||||||
|
|
||||||
float left, top, offset;
|
|
||||||
uint32_t rectWidth, rectHeight;
|
|
||||||
computeBounds(bounds, paint, left, top, offset, rectWidth, rectHeight);
|
|
||||||
|
|
||||||
if (!checkTextureSize(rectWidth, rectHeight)) return NULL;
|
|
||||||
|
|
||||||
purgeCache(rectWidth, rectHeight);
|
|
||||||
|
|
||||||
SkBitmap bitmap;
|
|
||||||
initBitmap(bitmap, rectWidth, rectHeight);
|
|
||||||
|
|
||||||
SkPaint pathPaint(*paint);
|
|
||||||
initPaint(pathPaint);
|
|
||||||
|
|
||||||
SkCanvas canvas(bitmap);
|
|
||||||
canvas.translate(-left + offset, -top + offset);
|
|
||||||
canvas.drawRect(bounds, pathPaint);
|
|
||||||
|
|
||||||
texture = createTexture(0, 0, offset, rectWidth, rectHeight, 0);
|
|
||||||
addTexture(entry, &bitmap, texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Arcs
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
ArcShapeCache::ArcShapeCache(): ShapeCache<ArcShapeCacheEntry>(
|
|
||||||
"arc", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PathTexture* ArcShapeCache::getArc(float width, float height,
|
|
||||||
float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
|
|
||||||
ArcShapeCacheEntry entry(width, height, startAngle, sweepAngle, useCenter, paint);
|
|
||||||
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
|
|
||||||
@@ -1,816 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 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.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ANDROID_HWUI_SHAPE_CACHE_H
|
|
||||||
#define ANDROID_HWUI_SHAPE_CACHE_H
|
|
||||||
|
|
||||||
#define ATRACE_TAG ATRACE_TAG_VIEW
|
|
||||||
|
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
|
|
||||||
#include <SkBitmap.h>
|
|
||||||
#include <SkCanvas.h>
|
|
||||||
#include <SkPaint.h>
|
|
||||||
#include <SkPath.h>
|
|
||||||
#include <SkRect.h>
|
|
||||||
|
|
||||||
#include <utils/JenkinsHash.h>
|
|
||||||
#include <utils/LruCache.h>
|
|
||||||
#include <utils/Trace.h>
|
|
||||||
|
|
||||||
#include "Debug.h"
|
|
||||||
#include "Properties.h"
|
|
||||||
#include "Texture.h"
|
|
||||||
#include "thread/Task.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace uirenderer {
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Defines
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Debug
|
|
||||||
#if DEBUG_SHAPES
|
|
||||||
#define SHAPE_LOGD(...) ALOGD(__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define SHAPE_LOGD(...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Classes
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Alpha texture used to represent a path.
|
|
||||||
*/
|
|
||||||
struct PathTexture: public Texture {
|
|
||||||
PathTexture(): Texture() {
|
|
||||||
}
|
|
||||||
|
|
||||||
~PathTexture() {
|
|
||||||
clearTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Left coordinate of the path bounds.
|
|
||||||
*/
|
|
||||||
float left;
|
|
||||||
/**
|
|
||||||
* Top coordinate of the path bounds.
|
|
||||||
*/
|
|
||||||
float top;
|
|
||||||
/**
|
|
||||||
* Offset to draw the path at the correct origin.
|
|
||||||
*/
|
|
||||||
float offset;
|
|
||||||
|
|
||||||
sp<Task<SkBitmap*> > task() const {
|
|
||||||
return mTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTask(const sp<Task<SkBitmap*> >& task) {
|
|
||||||
mTask = task;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearTask() {
|
|
||||||
if (mTask != NULL) {
|
|
||||||
mTask.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
sp<Task<SkBitmap*> > mTask;
|
|
||||||
}; // struct PathTexture
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describe a shape in the shape cache.
|
|
||||||
*/
|
|
||||||
struct ShapeCacheEntry {
|
|
||||||
enum ShapeType {
|
|
||||||
kShapeNone,
|
|
||||||
kShapeRect,
|
|
||||||
kShapeRoundRect,
|
|
||||||
kShapeCircle,
|
|
||||||
kShapeOval,
|
|
||||||
kShapeArc,
|
|
||||||
kShapePath
|
|
||||||
};
|
|
||||||
|
|
||||||
ShapeCacheEntry() {
|
|
||||||
shapeType = kShapeNone;
|
|
||||||
join = SkPaint::kDefault_Join;
|
|
||||||
cap = SkPaint::kDefault_Cap;
|
|
||||||
style = SkPaint::kFill_Style;
|
|
||||||
miter = 4.0f;
|
|
||||||
strokeWidth = 1.0f;
|
|
||||||
pathEffect = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeCacheEntry(ShapeType type, SkPaint* paint) {
|
|
||||||
shapeType = type;
|
|
||||||
join = paint->getStrokeJoin();
|
|
||||||
cap = paint->getStrokeCap();
|
|
||||||
miter = paint->getStrokeMiter();
|
|
||||||
strokeWidth = paint->getStrokeWidth();
|
|
||||||
style = paint->getStyle();
|
|
||||||
pathEffect = paint->getPathEffect();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ShapeCacheEntry() {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual hash_t hash() const {
|
|
||||||
uint32_t hash = JenkinsHashMix(0, shapeType);
|
|
||||||
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));
|
|
||||||
return JenkinsHashWhiten(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int compare(const ShapeCacheEntry& rhs) const {
|
|
||||||
int deltaInt = shapeType - rhs.shapeType;
|
|
||||||
if (deltaInt != 0) return deltaInt;
|
|
||||||
|
|
||||||
deltaInt = join - rhs.join;
|
|
||||||
if (deltaInt != 0) return deltaInt;
|
|
||||||
|
|
||||||
deltaInt = cap - rhs.cap;
|
|
||||||
if (deltaInt != 0) return deltaInt;
|
|
||||||
|
|
||||||
deltaInt = style - rhs.style;
|
|
||||||
if (deltaInt != 0) return deltaInt;
|
|
||||||
|
|
||||||
if (miter < rhs.miter) return -1;
|
|
||||||
if (miter > rhs.miter) return +1;
|
|
||||||
|
|
||||||
if (strokeWidth < rhs.strokeWidth) return -1;
|
|
||||||
if (strokeWidth > rhs.strokeWidth) return +1;
|
|
||||||
|
|
||||||
if (pathEffect < rhs.pathEffect) return -1;
|
|
||||||
if (pathEffect > rhs.pathEffect) return +1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const ShapeCacheEntry& other) const {
|
|
||||||
return compare(other) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const ShapeCacheEntry& other) const {
|
|
||||||
return compare(other) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapeType shapeType;
|
|
||||||
SkPaint::Join join;
|
|
||||||
SkPaint::Cap cap;
|
|
||||||
SkPaint::Style style;
|
|
||||||
float miter;
|
|
||||||
float strokeWidth;
|
|
||||||
SkPathEffect* pathEffect;
|
|
||||||
}; // struct ShapeCacheEntry
|
|
||||||
|
|
||||||
// Cache support
|
|
||||||
|
|
||||||
inline int strictly_order_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
|
|
||||||
return lhs.compare(rhs) < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int compare_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
|
|
||||||
return lhs.compare(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline hash_t hash_type(const ShapeCacheEntry& entry) {
|
|
||||||
return entry.hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
|
|
||||||
RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
|
|
||||||
ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
|
|
||||||
mWidth = width;
|
|
||||||
mHeight = height;
|
|
||||||
mRx = rx;
|
|
||||||
mRy = ry;
|
|
||||||
}
|
|
||||||
|
|
||||||
RoundRectShapeCacheEntry(): ShapeCacheEntry() {
|
|
||||||
mWidth = 0;
|
|
||||||
mHeight = 0;
|
|
||||||
mRx = 0;
|
|
||||||
mRy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_t hash() const {
|
|
||||||
uint32_t hash = ShapeCacheEntry::hash();
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mWidth));
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mHeight));
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mRx));
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mRy));
|
|
||||||
return JenkinsHashWhiten(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare(const ShapeCacheEntry& r) const {
|
|
||||||
int deltaInt = ShapeCacheEntry::compare(r);
|
|
||||||
if (deltaInt != 0) return deltaInt;
|
|
||||||
|
|
||||||
const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
|
|
||||||
|
|
||||||
if (mWidth < rhs.mWidth) return -1;
|
|
||||||
if (mWidth > rhs.mWidth) return +1;
|
|
||||||
|
|
||||||
if (mHeight < rhs.mHeight) return -1;
|
|
||||||
if (mHeight > rhs.mHeight) return +1;
|
|
||||||
|
|
||||||
if (mRx < rhs.mRx) return -1;
|
|
||||||
if (mRx > rhs.mRx) return +1;
|
|
||||||
|
|
||||||
if (mRy < rhs.mRy) return -1;
|
|
||||||
if (mRy > rhs.mRy) return +1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
float mWidth;
|
|
||||||
float mHeight;
|
|
||||||
float mRx;
|
|
||||||
float mRy;
|
|
||||||
}; // RoundRectShapeCacheEntry
|
|
||||||
|
|
||||||
inline hash_t hash_type(const RoundRectShapeCacheEntry& entry) {
|
|
||||||
return entry.hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CircleShapeCacheEntry: public ShapeCacheEntry {
|
|
||||||
CircleShapeCacheEntry(float radius, SkPaint* paint):
|
|
||||||
ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
|
|
||||||
mRadius = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
CircleShapeCacheEntry(): ShapeCacheEntry() {
|
|
||||||
mRadius = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_t hash() const {
|
|
||||||
uint32_t hash = ShapeCacheEntry::hash();
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mRadius));
|
|
||||||
return JenkinsHashWhiten(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare(const ShapeCacheEntry& r) const {
|
|
||||||
int deltaInt = ShapeCacheEntry::compare(r);
|
|
||||||
if (deltaInt != 0) return deltaInt;
|
|
||||||
|
|
||||||
const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
|
|
||||||
|
|
||||||
if (mRadius < rhs.mRadius) return -1;
|
|
||||||
if (mRadius > rhs.mRadius) return +1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
float mRadius;
|
|
||||||
}; // CircleShapeCacheEntry
|
|
||||||
|
|
||||||
inline hash_t hash_type(const CircleShapeCacheEntry& entry) {
|
|
||||||
return entry.hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OvalShapeCacheEntry: public ShapeCacheEntry {
|
|
||||||
OvalShapeCacheEntry(float width, float height, SkPaint* paint):
|
|
||||||
ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
|
|
||||||
mWidth = width;
|
|
||||||
mHeight = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
OvalShapeCacheEntry(): ShapeCacheEntry() {
|
|
||||||
mWidth = mHeight = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_t hash() const {
|
|
||||||
uint32_t hash = ShapeCacheEntry::hash();
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mWidth));
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mHeight));
|
|
||||||
return JenkinsHashWhiten(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare(const ShapeCacheEntry& r) const {
|
|
||||||
int deltaInt = ShapeCacheEntry::compare(r);
|
|
||||||
if (deltaInt != 0) return deltaInt;
|
|
||||||
|
|
||||||
const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r;
|
|
||||||
|
|
||||||
if (mWidth < rhs.mWidth) return -1;
|
|
||||||
if (mWidth > rhs.mWidth) return +1;
|
|
||||||
|
|
||||||
if (mHeight < rhs.mHeight) return -1;
|
|
||||||
if (mHeight > rhs.mHeight) return +1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
float mWidth;
|
|
||||||
float mHeight;
|
|
||||||
}; // OvalShapeCacheEntry
|
|
||||||
|
|
||||||
inline hash_t hash_type(const OvalShapeCacheEntry& entry) {
|
|
||||||
return entry.hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RectShapeCacheEntry: public ShapeCacheEntry {
|
|
||||||
RectShapeCacheEntry(float width, float height, SkPaint* paint):
|
|
||||||
ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
|
|
||||||
mWidth = width;
|
|
||||||
mHeight = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
RectShapeCacheEntry(): ShapeCacheEntry() {
|
|
||||||
mWidth = mHeight = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_t hash() const {
|
|
||||||
uint32_t hash = ShapeCacheEntry::hash();
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mWidth));
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mHeight));
|
|
||||||
return JenkinsHashWhiten(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare(const ShapeCacheEntry& r) const {
|
|
||||||
int deltaInt = ShapeCacheEntry::compare(r);
|
|
||||||
if (deltaInt != 0) return deltaInt;
|
|
||||||
|
|
||||||
const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r;
|
|
||||||
|
|
||||||
if (mWidth < rhs.mWidth) return -1;
|
|
||||||
if (mWidth > rhs.mWidth) return +1;
|
|
||||||
|
|
||||||
if (mHeight < rhs.mHeight) return -1;
|
|
||||||
if (mHeight > rhs.mHeight) return +1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
float mWidth;
|
|
||||||
float mHeight;
|
|
||||||
}; // RectShapeCacheEntry
|
|
||||||
|
|
||||||
inline hash_t hash_type(const RectShapeCacheEntry& entry) {
|
|
||||||
return entry.hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ArcShapeCacheEntry: public ShapeCacheEntry {
|
|
||||||
ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle,
|
|
||||||
bool useCenter, SkPaint* paint):
|
|
||||||
ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) {
|
|
||||||
mWidth = width;
|
|
||||||
mHeight = height;
|
|
||||||
mStartAngle = startAngle;
|
|
||||||
mSweepAngle = sweepAngle;
|
|
||||||
mUseCenter = useCenter ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArcShapeCacheEntry(): ShapeCacheEntry() {
|
|
||||||
mWidth = 0;
|
|
||||||
mHeight = 0;
|
|
||||||
mStartAngle = 0;
|
|
||||||
mSweepAngle = 0;
|
|
||||||
mUseCenter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_t hash() const {
|
|
||||||
uint32_t hash = ShapeCacheEntry::hash();
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mWidth));
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mHeight));
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mStartAngle));
|
|
||||||
hash = JenkinsHashMix(hash, android::hash_type(mSweepAngle));
|
|
||||||
hash = JenkinsHashMix(hash, mUseCenter);
|
|
||||||
return JenkinsHashWhiten(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare(const ShapeCacheEntry& r) const {
|
|
||||||
int deltaInt = ShapeCacheEntry::compare(r);
|
|
||||||
if (deltaInt != 0) return deltaInt;
|
|
||||||
|
|
||||||
const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r;
|
|
||||||
|
|
||||||
if (mWidth < rhs.mWidth) return -1;
|
|
||||||
if (mWidth > rhs.mWidth) return +1;
|
|
||||||
|
|
||||||
if (mHeight < rhs.mHeight) return -1;
|
|
||||||
if (mHeight > rhs.mHeight) return +1;
|
|
||||||
|
|
||||||
if (mStartAngle < rhs.mStartAngle) return -1;
|
|
||||||
if (mStartAngle > rhs.mStartAngle) return +1;
|
|
||||||
|
|
||||||
if (mSweepAngle < rhs.mSweepAngle) return -1;
|
|
||||||
if (mSweepAngle > rhs.mSweepAngle) return +1;
|
|
||||||
|
|
||||||
return mUseCenter - rhs.mUseCenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
float mWidth;
|
|
||||||
float mHeight;
|
|
||||||
float mStartAngle;
|
|
||||||
float mSweepAngle;
|
|
||||||
uint32_t mUseCenter;
|
|
||||||
}; // ArcShapeCacheEntry
|
|
||||||
|
|
||||||
inline hash_t hash_type(const ArcShapeCacheEntry& entry) {
|
|
||||||
return entry.hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple LRU shape cache. The cache has a maximum size expressed in bytes.
|
|
||||||
* Any texture added to the cache causing the cache to grow beyond the maximum
|
|
||||||
* allowed size will also cause the oldest texture to be kicked out.
|
|
||||||
*/
|
|
||||||
template<typename Entry>
|
|
||||||
class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
|
|
||||||
public:
|
|
||||||
ShapeCache(const char* name, const char* propertyName, float defaultSize);
|
|
||||||
~ShapeCache();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used as a callback when an entry is removed from the cache.
|
|
||||||
* Do not invoke directly.
|
|
||||||
*/
|
|
||||||
void operator()(Entry& path, PathTexture*& texture);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the cache. This causes all textures to be deleted.
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the maximum size of the cache in bytes.
|
|
||||||
*/
|
|
||||||
void setMaxSize(uint32_t maxSize);
|
|
||||||
/**
|
|
||||||
* Returns the maximum size of the cache in bytes.
|
|
||||||
*/
|
|
||||||
uint32_t getMaxSize();
|
|
||||||
/**
|
|
||||||
* Returns the current size of the cache in bytes.
|
|
||||||
*/
|
|
||||||
uint32_t getSize();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trims the contents of the cache, removing items until it's under its
|
|
||||||
* specified limit.
|
|
||||||
*
|
|
||||||
* Trimming is used for caches that support pre-caching from a worker
|
|
||||||
* thread. During pre-caching the maximum limit of the cache can be
|
|
||||||
* exceeded for the duration of the frame. It is therefore required to
|
|
||||||
* trim the cache at the end of the frame to keep the total amount of
|
|
||||||
* memory used under control.
|
|
||||||
*
|
|
||||||
* Only the PathCache currently supports pre-caching.
|
|
||||||
*/
|
|
||||||
void trim();
|
|
||||||
|
|
||||||
static void computePathBounds(const SkPath* path, const SkPaint* paint,
|
|
||||||
float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
|
|
||||||
const SkRect& bounds = path->getBounds();
|
|
||||||
computeBounds(bounds, paint, left, top, offset, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void 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 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
|
|
||||||
PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
|
|
||||||
void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures there is enough space in the cache for a texture of the specified
|
|
||||||
* dimensions.
|
|
||||||
*/
|
|
||||||
void purgeCache(uint32_t width, uint32_t height);
|
|
||||||
|
|
||||||
PathTexture* get(Entry entry) {
|
|
||||||
return mCache.get(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeTexture(PathTexture* texture);
|
|
||||||
|
|
||||||
bool checkTextureSize(uint32_t width, uint32_t height) {
|
|
||||||
if (width > mMaxTextureSize || height > mMaxTextureSize) {
|
|
||||||
ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
|
|
||||||
mName, width, height, mMaxTextureSize, mMaxTextureSize);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
LruCache<Entry, PathTexture*> mCache;
|
|
||||||
uint32_t mSize;
|
|
||||||
uint32_t mMaxSize;
|
|
||||||
GLuint mMaxTextureSize;
|
|
||||||
|
|
||||||
char* mName;
|
|
||||||
bool mDebugEnabled;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* Generates the texture from a bitmap into the specified texture structure.
|
|
||||||
*/
|
|
||||||
void generateTexture(SkBitmap& bitmap, Texture* texture);
|
|
||||||
|
|
||||||
void init();
|
|
||||||
}; // class ShapeCache
|
|
||||||
|
|
||||||
class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
|
|
||||||
public:
|
|
||||||
RoundRectShapeCache();
|
|
||||||
|
|
||||||
PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
|
|
||||||
}; // class RoundRectShapeCache
|
|
||||||
|
|
||||||
class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
|
|
||||||
public:
|
|
||||||
CircleShapeCache();
|
|
||||||
|
|
||||||
PathTexture* getCircle(float radius, SkPaint* paint);
|
|
||||||
}; // class CircleShapeCache
|
|
||||||
|
|
||||||
class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
|
|
||||||
public:
|
|
||||||
OvalShapeCache();
|
|
||||||
|
|
||||||
PathTexture* getOval(float width, float height, SkPaint* paint);
|
|
||||||
}; // class OvalShapeCache
|
|
||||||
|
|
||||||
class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
|
|
||||||
public:
|
|
||||||
RectShapeCache();
|
|
||||||
|
|
||||||
PathTexture* getRect(float width, float height, SkPaint* paint);
|
|
||||||
}; // class RectShapeCache
|
|
||||||
|
|
||||||
class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
|
|
||||||
public:
|
|
||||||
ArcShapeCache();
|
|
||||||
|
|
||||||
PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
|
|
||||||
bool useCenter, SkPaint* paint);
|
|
||||||
}; // class ArcShapeCache
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructors/destructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
|
|
||||||
mCache(LruCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
|
|
||||||
mSize(0), mMaxSize(MB(defaultSize)) {
|
|
||||||
char property[PROPERTY_VALUE_MAX];
|
|
||||||
if (property_get(propertyName, 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, defaultSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t len = strlen(name);
|
|
||||||
mName = new char[len + 1];
|
|
||||||
strcpy(mName, name);
|
|
||||||
mName[len] = '\0';
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
ShapeCache<Entry>::~ShapeCache() {
|
|
||||||
mCache.clear();
|
|
||||||
delete[] mName;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
void ShapeCache<Entry>::init() {
|
|
||||||
mCache.setOnEntryRemovedListener(this);
|
|
||||||
|
|
||||||
GLint maxTextureSize;
|
|
||||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
|
||||||
mMaxTextureSize = maxTextureSize;
|
|
||||||
|
|
||||||
mDebugEnabled = readDebugLevel() & kDebugCaches;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Size management
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
uint32_t ShapeCache<Entry>::getSize() {
|
|
||||||
return mSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
uint32_t ShapeCache<Entry>::getMaxSize() {
|
|
||||||
return mMaxSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
|
|
||||||
mMaxSize = maxSize;
|
|
||||||
while (mSize > mMaxSize) {
|
|
||||||
mCache.removeOldest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Callbacks
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
|
|
||||||
removeTexture(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Caching
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
|
|
||||||
if (texture) {
|
|
||||||
const uint32_t size = texture->width * texture->height;
|
|
||||||
mSize -= size;
|
|
||||||
|
|
||||||
SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
|
|
||||||
mName, texture->id, size, mSize);
|
|
||||||
if (mDebugEnabled) {
|
|
||||||
ALOGD("Shape %s deleted, size = %d", mName, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture->id) {
|
|
||||||
glDeleteTextures(1, &texture->id);
|
|
||||||
}
|
|
||||||
delete texture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
void ShapeCache<Entry>::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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
void ShapeCache<Entry>::trim() {
|
|
||||||
while (mSize > mMaxSize) {
|
|
||||||
mCache.removeOldest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
PathTexture* ShapeCache<Entry>::addTexture(const Entry& 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) {
|
|
||||||
generateTexture(*bitmap, texture);
|
|
||||||
|
|
||||||
uint32_t size = texture->width * texture->height;
|
|
||||||
if (size < mMaxSize) {
|
|
||||||
mSize += size;
|
|
||||||
SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
|
|
||||||
mName, texture->id, size, mSize);
|
|
||||||
if (mDebugEnabled) {
|
|
||||||
ALOGD("Shape %s created, size = %d", mName, size);
|
|
||||||
}
|
|
||||||
mCache.put(entry, texture);
|
|
||||||
} else {
|
|
||||||
texture->cleanup = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
void ShapeCache<Entry>::clear() {
|
|
||||||
mCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Entry>
|
|
||||||
void ShapeCache<Entry>::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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // namespace uirenderer
|
|
||||||
}; // namespace android
|
|
||||||
|
|
||||||
#endif // ANDROID_HWUI_SHAPE_CACHE_H
|
|
||||||
@@ -20,6 +20,7 @@ import android.app.Activity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -34,12 +35,13 @@ public class ShapesActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class ShapesView extends View {
|
static class ShapesView extends View {
|
||||||
private Paint mNormalPaint;
|
private final Paint mNormalPaint;
|
||||||
private Paint mStrokePaint;
|
private final Paint mStrokePaint;
|
||||||
private Paint mFillPaint;
|
private final Paint mFillPaint;
|
||||||
private RectF mRect;
|
private final RectF mRect;
|
||||||
private RectF mOval;
|
private final RectF mOval;
|
||||||
private RectF mArc;
|
private final RectF mArc;
|
||||||
|
private final Path mTriangle;
|
||||||
|
|
||||||
ShapesView(Context c) {
|
ShapesView(Context c) {
|
||||||
super(c);
|
super(c);
|
||||||
@@ -65,6 +67,12 @@ public class ShapesActivity extends Activity {
|
|||||||
|
|
||||||
mOval = new RectF(0.0f, 0.0f, 80.0f, 45.0f);
|
mOval = new RectF(0.0f, 0.0f, 80.0f, 45.0f);
|
||||||
mArc = new RectF(0.0f, 0.0f, 100.0f, 120.0f);
|
mArc = new RectF(0.0f, 0.0f, 100.0f, 120.0f);
|
||||||
|
|
||||||
|
mTriangle = new Path();
|
||||||
|
mTriangle.moveTo(0.0f, 90.0f);
|
||||||
|
mTriangle.lineTo(45.0f, 0.0f);
|
||||||
|
mTriangle.lineTo(90.0f, 90.0f);
|
||||||
|
mTriangle.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -136,6 +144,17 @@ public class ShapesActivity extends Activity {
|
|||||||
canvas.translate(0.0f, 110.0f);
|
canvas.translate(0.0f, 110.0f);
|
||||||
canvas.drawArc(mArc, 30.0f, 100.0f, false, mFillPaint);
|
canvas.drawArc(mArc, 30.0f, 100.0f, false, mFillPaint);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
|
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(50.0f, 400.0f);
|
||||||
|
canvas.drawPath(mTriangle, mNormalPaint);
|
||||||
|
|
||||||
|
canvas.translate(110.0f, 0.0f);
|
||||||
|
canvas.drawPath(mTriangle, mStrokePaint);
|
||||||
|
|
||||||
|
canvas.translate(110.0f, 0.0f);
|
||||||
|
canvas.drawPath(mTriangle, mFillPaint);
|
||||||
|
canvas.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user