Add rounded rects and circles support to OpenGLRenderer.
Change-Id: I6cedf2b495d58de7c0437096809fa9e4518a1b8c
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index c49be93..38e0848 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -23,6 +23,7 @@
Program.cpp \
ProgramCache.cpp \
ResourceCache.cpp \
+ ShapeCache.cpp \
SkiaColorFilter.cpp \
SkiaShader.cpp \
TextureCache.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index fde4f96..bffab95 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -73,6 +73,10 @@
LOGD(" LayerCache %8d / %8d", layerCache.getSize(), layerCache.getMaxSize());
LOGD(" GradientCache %8d / %8d", gradientCache.getSize(), gradientCache.getMaxSize());
LOGD(" PathCache %8d / %8d", pathCache.getSize(), pathCache.getMaxSize());
+ LOGD(" CircleShapeCache %8d / %8d",
+ circleShapeCache.getSize(), circleShapeCache.getMaxSize());
+ LOGD(" RoundRectShapeCache %8d / %8d",
+ roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize());
LOGD(" TextDropShadowCache %8d / %8d", dropShadowCache.getSize(),
dropShadowCache.getMaxSize());
for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index a11b6bc..c03c347 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -32,6 +32,7 @@
#include "PatchCache.h"
#include "ProgramCache.h"
#include "PathCache.h"
+#include "ShapeCache.h"
#include "TextDropShadowCache.h"
#include "FboCache.h"
#include "ResourceCache.h"
@@ -159,6 +160,8 @@
GradientCache gradientCache;
ProgramCache programCache;
PathCache pathCache;
+ RoundRectShapeCache roundRectShapeCache;
+ CircleShapeCache circleShapeCache;
PatchCache patchCache;
TextDropShadowCache dropShadowCache;
FboCache fboCache;
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 71ec760..014519e 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -45,6 +45,9 @@
// Turn on to display debug info about paths
#define DEBUG_PATHS 0
+// Turn on to display debug info about shapes
+#define DEBUG_SHAPES 0
+
// Turn on to display debug info about textures
#define DEBUG_TEXTURES 0
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index ade85e5..a74a95f 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -103,6 +103,8 @@
"DrawPatch",
"DrawColor",
"DrawRect",
+ "DrawRoundRect",
+ "DrawCircle",
"DrawPath",
"DrawLines",
"DrawText",
@@ -332,6 +334,15 @@
renderer.drawRect(getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
}
break;
+ case DrawRoundRect: {
+ renderer.drawRoundRect(getFloat(), getFloat(), getFloat(), getFloat(),
+ getFloat(), getFloat(), getPaint());
+ }
+ break;
+ case DrawCircle: {
+ renderer.drawCircle(getFloat(), getFloat(), getFloat(), getPaint());
+ }
+ break;
case DrawPath: {
renderer.drawPath(getPath(), getPaint());
}
@@ -601,6 +612,21 @@
addPaint(paint);
}
+void DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, SkPaint* paint) {
+ addOp(DisplayList::DrawRoundRect);
+ addBounds(left, top, right, bottom);
+ addPoint(rx, ry);
+ addPaint(paint);
+}
+
+void DisplayListRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
+ addOp(DisplayList::DrawCircle);
+ addPoint(x, y);
+ addFloat(radius);
+ addPaint(paint);
+}
+
void DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) {
addOp(DisplayList::DrawPath);
addPath(path);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 05864ec..4b727f6 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -110,6 +110,8 @@
DrawPatch,
DrawColor,
DrawRect,
+ DrawRoundRect,
+ DrawCircle,
DrawPath,
DrawLines,
DrawText,
@@ -270,6 +272,9 @@
float left, float top, float right, float bottom, SkPaint* paint);
void drawColor(int color, SkXfermode::Mode mode);
void drawRect(float left, float top, float right, float bottom, SkPaint* paint);
+ void drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, SkPaint* paint);
+ void drawCircle(float x, float y, float radius, SkPaint* paint);
void drawPath(SkPath* path, SkPaint* paint);
void drawLines(float* points, int count, SkPaint* paint);
void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2c50ac3..91ac166 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1303,6 +1303,38 @@
drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
}
+void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, SkPaint* paint) {
+ if (mSnapshot->isIgnored()) return;
+
+ glActiveTexture(gTextureUnits[0]);
+
+ const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect(
+ right - left, bottom - top, rx, ry, paint);
+ if (!texture) return;
+ const AutoTexture autoCleanup(texture);
+
+ const float x = left + texture->left - texture->offset;
+ const float y = top + texture->top - texture->offset;
+
+ drawPathTexture(texture, x, y, paint);
+}
+
+void OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
+ if (mSnapshot->isIgnored()) return;
+
+ glActiveTexture(gTextureUnits[0]);
+
+ const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, paint);
+ if (!texture) return;
+ const AutoTexture autoCleanup(texture);
+
+ const float left = (x - radius) + texture->left - texture->offset;
+ const float top = (y - radius) + texture->top - texture->offset;
+
+ drawPathTexture(texture, left, top, paint);
+}
+
void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) {
if (quickReject(left, top, right, bottom)) {
return;
@@ -1451,8 +1483,7 @@
void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
if (mSnapshot->isIgnored()) return;
- GLuint textureUnit = 0;
- glActiveTexture(gTextureUnits[textureUnit]);
+ glActiveTexture(gTextureUnits[0]);
const PathTexture* texture = mCaches.pathCache.get(path, paint);
if (!texture) return;
@@ -1461,31 +1492,7 @@
const float x = texture->left - texture->offset;
const float y = texture->top - texture->offset;
- if (quickReject(x, y, x + texture->width, y + texture->height)) {
- return;
- }
-
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- setupDraw();
- setupDrawWithTexture(true);
- setupDrawAlpha8Color(paint->getColor(), alpha);
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(true, mode);
- setupDrawProgram();
- setupDrawModelView(x, y, x + texture->width, y + texture->height);
- setupDrawTexture(texture->id);
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawShaderUniforms();
- setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-
- finishDrawTexture();
+ drawPathTexture(texture, x, y, paint);
}
void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) {
@@ -1581,6 +1588,35 @@
// Drawing implementation
///////////////////////////////////////////////////////////////////////////////
+void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
+ float x, float y, SkPaint* paint) {
+ if (quickReject(x, y, x + texture->width, y + texture->height)) {
+ return;
+ }
+
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ setupDraw();
+ setupDrawWithTexture(true);
+ setupDrawAlpha8Color(paint->getColor(), alpha);
+ setupDrawColorFilter();
+ setupDrawShader();
+ setupDrawBlending(true, mode);
+ setupDrawProgram();
+ setupDrawModelView(x, y, x + texture->width, y + texture->height);
+ setupDrawTexture(texture->id);
+ setupDrawPureColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawShaderUniforms();
+ setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+
+ finishDrawTexture();
+}
+
// Same values used by Skia
#define kStdStrikeThru_Offset (-6.0f / 21.0f)
#define kStdUnderline_Offset (1.0f / 9.0f)
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 4150ddc..a43660b 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -107,6 +107,9 @@
float left, float top, float right, float bottom, SkPaint* paint);
virtual void drawColor(int color, SkXfermode::Mode mode);
virtual void drawRect(float left, float top, float right, float bottom, SkPaint* paint);
+ virtual void drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, SkPaint* paint);
+ virtual void drawCircle(float x, float y, float radius, SkPaint* paint);
virtual void drawPath(SkPath* path, SkPaint* paint);
virtual void drawLines(float* points, int count, SkPaint* paint);
virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
@@ -343,6 +346,8 @@
void drawTextDecorations(const char* text, int bytesCount, float length,
float x, float y, SkPaint* paint);
+ void drawPathTexture(const PathTexture* texture, float x, float y, SkPaint* paint);
+
/**
* Resets the texture coordinates stored in mMeshVertices. Setting the values
* back to default is achieved by calling:
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 6a0d7ea..2f230b5 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -48,6 +48,7 @@
#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_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_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
@@ -66,6 +67,7 @@
#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
#define DEFAULT_LAYER_CACHE_SIZE 8.0f
#define DEFAULT_PATH_CACHE_SIZE 4.0f
+#define DEFAULT_SHAPE_CACHE_SIZE 1.0f
#define DEFAULT_PATCH_CACHE_SIZE 512
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
diff --git a/libs/hwui/ShapeCache.cpp b/libs/hwui/ShapeCache.cpp
new file mode 100644
index 0000000..ffa18e3
--- /dev/null
+++ b/libs/hwui/ShapeCache.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 {
+
+RoundRectShapeCache::RoundRectShapeCache(): ShapeCache<RoundRectShapeCacheEntry>() {
+}
+
+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;
+}
+
+CircleShapeCache::CircleShapeCache(): ShapeCache<CircleShapeCacheEntry>() {
+}
+
+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;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h
new file mode 100644
index 0000000..910d01d
--- /dev/null
+++ b/libs/hwui/ShapeCache.h
@@ -0,0 +1,464 @@
+/*
+ * 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
+
+#include <GLES2/gl2.h>
+
+#include <SkCanvas.h>
+#include <SkRect.h>
+
+#include "PathCache.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#if DEBUG_SHAPES
+ #define SHAPE_LOGD(...) LOGD(__VA_ARGS__)
+#else
+ #define SHAPE_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Describe a shape in the shape cache.
+ */
+struct ShapeCacheEntry {
+ enum ShapeType {
+ kShapeNone,
+ kShapeRoundRect,
+ kShapeCircle,
+ kShapeOval,
+ kShapeArc
+ };
+
+ ShapeCacheEntry() {
+ shapeType = kShapeNone;
+ join = SkPaint::kDefault_Join;
+ cap = SkPaint::kDefault_Cap;
+ style = SkPaint::kFill_Style;
+ miter = 4.0f;
+ strokeWidth = 1.0f;
+ }
+
+ ShapeCacheEntry(const ShapeCacheEntry& entry):
+ shapeType(entry.shapeType), join(entry.join), cap(entry.cap),
+ style(entry.style), miter(entry.miter),
+ strokeWidth(entry.strokeWidth) {
+ }
+
+ ShapeCacheEntry(ShapeType type, SkPaint* paint) {
+ shapeType = type;
+ join = paint->getStrokeJoin();
+ cap = paint->getStrokeCap();
+ float v = paint->getStrokeMiter();
+ miter = *(uint32_t*) &v;
+ v = paint->getStrokeWidth();
+ strokeWidth = *(uint32_t*) &v;
+ style = paint->getStyle();
+ }
+
+ virtual ~ShapeCacheEntry() {
+ }
+
+ // shapeType must be checked in subclasses operator<
+ ShapeType shapeType;
+ SkPaint::Join join;
+ SkPaint::Cap cap;
+ SkPaint::Style style;
+ uint32_t miter;
+ uint32_t strokeWidth;
+
+ bool operator<(const ShapeCacheEntry& rhs) const {
+ LTE_INT(shapeType) {
+ LTE_INT(join) {
+ LTE_INT(cap) {
+ LTE_INT(style) {
+ LTE_INT(miter) {
+ LTE_INT(strokeWidth) {
+ return lessThan(rhs);
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+protected:
+ virtual bool lessThan(const ShapeCacheEntry& rhs) const {
+ return false;
+ }
+}; // struct ShapeCacheEntry
+
+
+struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
+ RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
+ ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
+ mWidth = *(uint32_t*) &width;
+ mHeight = *(uint32_t*) &height;
+ mRx = *(uint32_t*) ℞
+ mRy = *(uint32_t*) &ry;
+ }
+
+ RoundRectShapeCacheEntry(): ShapeCacheEntry() {
+ mWidth = 0;
+ mHeight = 0;
+ mRx = 0;
+ mRy = 0;
+ }
+
+ RoundRectShapeCacheEntry(const RoundRectShapeCacheEntry& entry):
+ ShapeCacheEntry(entry) {
+ mWidth = entry.mWidth;
+ mHeight = entry.mHeight;
+ mRx = entry.mRx;
+ mRy = entry.mRy;
+ }
+
+ bool lessThan(const ShapeCacheEntry& r) const {
+ const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
+ LTE_INT(mWidth) {
+ LTE_INT(mHeight) {
+ LTE_INT(mRx) {
+ LTE_INT(mRy) {
+ return false;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+private:
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mRx;
+ uint32_t mRy;
+}; // RoundRectShapeCacheEntry
+
+struct CircleShapeCacheEntry: public ShapeCacheEntry {
+ CircleShapeCacheEntry(float radius, SkPaint* paint):
+ ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
+ mRadius = *(uint32_t*) &radius;
+ }
+
+ CircleShapeCacheEntry(): ShapeCacheEntry() {
+ mRadius = 0;
+ }
+
+ CircleShapeCacheEntry(const CircleShapeCacheEntry& entry):
+ ShapeCacheEntry(entry) {
+ mRadius = entry.mRadius;
+ }
+
+ bool lessThan(const ShapeCacheEntry& r) const {
+ const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
+ LTE_INT(mRadius) {
+ return false;
+ }
+ return false;
+ }
+
+private:
+ uint32_t mRadius;
+}; // CircleShapeCacheEntry
+
+/**
+ * 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();
+ ShapeCache(uint32_t maxByteSize);
+ ~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();
+
+protected:
+ PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
+
+ PathTexture* get(Entry entry) {
+ return mCache.get(entry);
+ }
+
+private:
+ /**
+ * Generates the texture from a bitmap into the specified texture structure.
+ */
+ void generateTexture(SkBitmap& bitmap, Texture* texture);
+
+ void removeTexture(PathTexture* texture);
+
+ void init();
+
+ GenerationCache<Entry, PathTexture*> mCache;
+ uint32_t mSize;
+ uint32_t mMaxSize;
+ GLuint mMaxTextureSize;
+
+ bool mDebugEnabled;
+}; // 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 RoundRectShapeCache
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+template<class Entry>
+ShapeCache<Entry>::ShapeCache():
+ mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
+ mSize(0), mMaxSize(MB(DEFAULT_SHAPE_CACHE_SIZE)) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_SHAPE_CACHE_SIZE, property, NULL) > 0) {
+ LOGD(" Setting shape cache size to %sMB", property);
+ setMaxSize(MB(atof(property)));
+ } else {
+ LOGD(" Using default shape cache size of %.2fMB", DEFAULT_SHAPE_CACHE_SIZE);
+ }
+ init();
+}
+
+template<class Entry>
+ShapeCache<Entry>::ShapeCache(uint32_t maxByteSize):
+ mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
+ mSize(0), mMaxSize(maxByteSize) {
+ init();
+}
+
+template<class Entry>
+ShapeCache<Entry>::~ShapeCache() {
+ mCache.clear();
+}
+
+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 path: name, size, mSize = %d, %d, %d",
+ texture->id, size, mSize);
+ if (mDebugEnabled) {
+ LOGD("Path deleted, size = %d", size);
+ }
+
+ glDeleteTextures(1, &texture->id);
+ delete texture;
+ }
+}
+
+template<class Entry>
+PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
+ const SkPaint* paint) {
+ const SkRect& bounds = path->getBounds();
+
+ const float pathWidth = fmax(bounds.width(), 1.0f);
+ const float pathHeight = fmax(bounds.height(), 1.0f);
+
+ if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) {
+ LOGW("Shape too large to be rendered into a texture");
+ return NULL;
+ }
+
+ const float offset = paint->getStrokeWidth() * 1.5f;
+ const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+ const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+
+ 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();
+ }
+ }
+
+ PathTexture* texture = new PathTexture;
+ texture->left = bounds.fLeft;
+ texture->top = bounds.fTop;
+ texture->offset = offset;
+ texture->width = width;
+ texture->height = height;
+ texture->generation = path->getGenerationID();
+
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kA8_Config, width, height);
+ bitmap.allocPixels();
+ bitmap.eraseColor(0);
+
+ SkPaint pathPaint(*paint);
+
+ // Make sure the paint is opaque, color, alpha, filter, etc.
+ // will be applied later when compositing the alpha8 texture
+ pathPaint.setColor(0xff000000);
+ pathPaint.setAlpha(255);
+ pathPaint.setColorFilter(NULL);
+ pathPaint.setMaskFilter(NULL);
+ pathPaint.setShader(NULL);
+ SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
+ pathPaint.setXfermode(mode)->safeUnref();
+
+ SkCanvas canvas(bitmap);
+ canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset);
+ canvas.drawPath(*path, pathPaint);
+
+ generateTexture(bitmap, texture);
+
+ if (size < mMaxSize) {
+ mSize += size;
+ SHAPE_LOGD("ShapeCache::get: create path: name, size, mSize = %d, %d, %d",
+ texture->id, size, mSize);
+ if (mDebugEnabled) {
+ LOGD("Shape created, size = %d", size);
+ }
+ mCache.put(entry, texture);
+ } else {
+ texture->cleanup = true;
+ }
+
+ return texture;
+}
+
+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()) {
+ LOGE("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());
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_SHAPE_CACHE_H