diff options
-rw-r--r-- | core/java/android/view/GLES20Canvas.java | 32 | ||||
-rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 13 | ||||
-rw-r--r-- | libs/hwui/Android.mk | 1 | ||||
-rw-r--r-- | libs/hwui/Caches.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/Caches.h | 3 | ||||
-rw-r--r-- | libs/hwui/Debug.h | 3 | ||||
-rw-r--r-- | libs/hwui/DisplayListRenderer.cpp | 26 | ||||
-rw-r--r-- | libs/hwui/DisplayListRenderer.h | 5 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 90 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 5 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 2 | ||||
-rw-r--r-- | libs/hwui/ShapeCache.cpp | 62 | ||||
-rw-r--r-- | libs/hwui/ShapeCache.h | 464 | ||||
-rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 9 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java | 92 |
15 files changed, 776 insertions, 35 deletions
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 99b686e..d82f051 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -452,11 +452,14 @@ class GLES20Canvas extends HardwareCanvas { @Override public int saveLayer(float left, float top, float right, float bottom, Paint paint, int saveFlags) { - boolean hasColorFilter = paint != null && setupColorFilter(paint); - final int nativePaint = paint == null ? 0 : paint.mNativePaint; - int count = nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags); - if (hasColorFilter) nResetModifiers(mRenderer); - return count; + if (left < right && top < bottom) { + boolean hasColorFilter = paint != null && setupColorFilter(paint); + final int nativePaint = paint == null ? 0 : paint.mNativePaint; + int count = nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags); + if (hasColorFilter) nResetModifiers(mRenderer); + return count; + } + return save(saveFlags); } private native int nSaveLayer(int renderer, float left, float top, float right, float bottom, @@ -471,7 +474,10 @@ class GLES20Canvas extends HardwareCanvas { @Override public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, int saveFlags) { - return nSaveLayerAlpha(mRenderer, left, top, right, bottom, alpha, saveFlags); + if (left < right && top < bottom) { + return nSaveLayerAlpha(mRenderer, left, top, right, bottom, alpha, saveFlags); + } + return save(saveFlags); } private native int nSaveLayerAlpha(int renderer, float left, float top, float right, @@ -629,9 +635,13 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawCircle(float cx, float cy, float radius, Paint paint) { - throw new UnsupportedOperationException(); + boolean hasModifier = setupModifiers(paint); + nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint); + if (hasModifier) nResetModifiers(mRenderer); } + private native void nDrawCircle(int renderer, float cx, float cy, float radius, int paint); + @Override public void drawColor(int color) { drawColor(color, PorterDuff.Mode.SRC_OVER); @@ -767,9 +777,15 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) { - // TODO: Implement + boolean hasModifier = setupModifiers(paint); + nDrawRoundRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, + rx, ry, paint.mNativePaint); + if (hasModifier) nResetModifiers(mRenderer); } + private native void nDrawRoundRect(int renderer, float left, float top, + float right, float bottom, float rx, float y, int paint); + @Override public void drawText(char[] text, int index, int count, float x, float y, Paint paint) { if ((index | count | (index + count) | (text.length - index - count)) < 0) { diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index ac491ea..bfd2b58 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -299,6 +299,17 @@ static void android_view_GLES20Canvas_drawRect(JNIEnv* env, jobject canvas, renderer->drawRect(left, top, right, bottom, paint); } +static void android_view_GLES20Canvas_drawRoundRect(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom, + jfloat rx, jfloat ry, SkPaint* paint) { + renderer->drawRoundRect(left, top, right, bottom, rx, ry, paint); +} + +static void android_view_GLES20Canvas_drawCircle(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat x, jfloat y, jfloat radius, SkPaint* paint) { + renderer->drawCircle(x, y, radius, paint); +} + static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject canvas, OpenGLRenderer* renderer, SkRegion* region, SkPaint* paint) { SkRegion::Iterator it(*region); @@ -570,6 +581,8 @@ static JNINativeMethod gMethods[] = { { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor }, { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect }, { "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRects }, + { "nDrawRoundRect", "(IFFFFFFI)V", (void*) android_view_GLES20Canvas_drawRoundRect }, + { "nDrawCircle", "(IFFFI)V", (void*) android_view_GLES20Canvas_drawCircle }, { "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath }, { "nDrawLines", "(I[FIII)V", (void*) android_view_GLES20Canvas_drawLines }, 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 @@ ifeq ($(USE_OPENGL_RENDERER),true) 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 @@ void Caches::dumpMemoryUsage() { 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 @@ public: 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 @@ const char* DisplayList::OP_NAMES[] = { "DrawPatch", "DrawColor", "DrawRect", + "DrawRoundRect", + "DrawCircle", "DrawPath", "DrawLines", "DrawText", @@ -332,6 +334,15 @@ void DisplayList::replay(OpenGLRenderer& renderer, uint32_t level) { 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 @@ void DisplayListRenderer::drawRect(float left, float top, float right, float bot 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 @@ public: DrawPatch, DrawColor, DrawRect, + DrawRoundRect, + DrawCircle, DrawPath, DrawLines, DrawText, @@ -270,6 +272,9 @@ public: 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 @@ void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { 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::drawText(const char* text, int bytesCount, int count, 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 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { 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 @@ void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) { // 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 @@ public: 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 @@ private: 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 @@ enum DebugLevel { #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 @@ enum DebugLevel { #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 diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 7a61b3c..1d67964 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -25,6 +25,15 @@ android:hardwareAccelerated="true"> <activity + android:name="ShapesActivity" + android:label="_Shapes"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name="SimplePatchActivity" android:label="_SimplePatch" android:theme="@android:style/Theme.Translucent.NoTitleBar"> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java new file mode 100644 index 0000000..536a669 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 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. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.os.Bundle; +import android.view.View; + +@SuppressWarnings({"UnusedDeclaration"}) +public class ShapesActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(new ShapesView(this)); + } + + static class ShapesView extends View { + private Paint mNormalPaint; + private Paint mStrokePaint; + private Paint mFillPaint; + private RectF mRect; + + ShapesView(Context c) { + super(c); + + mRect = new RectF(0.0f, 0.0f, 160.0f, 90.0f); + + mNormalPaint = new Paint(); + mNormalPaint.setAntiAlias(true); + mNormalPaint.setColor(0xff0000ff); + mNormalPaint.setStrokeWidth(6.0f); + mNormalPaint.setStyle(Paint.Style.FILL_AND_STROKE); + + mStrokePaint = new Paint(); + mStrokePaint.setAntiAlias(true); + mStrokePaint.setColor(0xff0000ff); + mStrokePaint.setStrokeWidth(6.0f); + mStrokePaint.setStyle(Paint.Style.STROKE); + + mFillPaint = new Paint(); + mFillPaint.setAntiAlias(true); + mFillPaint.setColor(0xff0000ff); + mFillPaint.setStyle(Paint.Style.FILL); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.save(); + canvas.translate(50.0f, 50.0f); + canvas.drawRoundRect(mRect, 6.0f, 6.0f, mNormalPaint); + + canvas.translate(0.0f, 110.0f); + canvas.drawRoundRect(mRect, 6.0f, 6.0f, mStrokePaint); + + canvas.translate(0.0f, 110.0f); + canvas.drawRoundRect(mRect, 6.0f, 6.0f, mFillPaint); + canvas.restore(); + + canvas.save(); + canvas.translate(250.0f, 50.0f); + canvas.drawCircle(80.0f, 45.0f, 45.0f, mNormalPaint); + + canvas.translate(0.0f, 110.0f); + canvas.drawCircle(80.0f, 45.0f, 45.0f, mStrokePaint); + + canvas.translate(0.0f, 110.0f); + canvas.drawCircle(80.0f, 45.0f, 45.0f, mFillPaint); + canvas.restore(); + } + } +} |