diff options
-rw-r--r-- | api/current.xml | 2 | ||||
-rw-r--r-- | core/java/android/view/GLES20Canvas.java | 10 | ||||
-rw-r--r-- | core/jni/android/graphics/Paint.cpp | 2 | ||||
-rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 7 | ||||
-rw-r--r-- | graphics/java/android/graphics/Paint.java | 36 | ||||
-rw-r--r-- | libs/hwui/Android.mk | 3 | ||||
-rw-r--r-- | libs/hwui/FontRenderer.cpp | 39 | ||||
-rw-r--r-- | libs/hwui/FontRenderer.h | 16 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 164 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 40 | ||||
-rw-r--r-- | libs/hwui/PathCache.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 1 | ||||
-rw-r--r-- | libs/hwui/TextDropShadowCache.cpp | 134 | ||||
-rw-r--r-- | libs/hwui/TextDropShadowCache.h | 140 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java | 10 |
15 files changed, 530 insertions, 76 deletions
diff --git a/api/current.xml b/api/current.xml index 4ec1c3e913c6..d35037ed8c04 100644 --- a/api/current.xml +++ b/api/current.xml @@ -73993,7 +73993,7 @@ <method name="setShadowLayer" return="void" abstract="false" - native="true" + native="false" synchronized="false" static="false" final="false" diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 91dbe1ff8cb0..4c72e9502cef 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -756,6 +756,12 @@ class GLES20Canvas extends Canvas { private boolean setupModifiers(Paint paint) { boolean hasModifier = false; + if (paint.hasShadow) { + nSetupShadow(mRenderer, paint.shadowRadius, paint.shadowDx, paint.shadowDy, + paint.shadowColor); + hasModifier = true; + } + final Shader shader = paint.getShader(); if (shader != null) { nSetupShader(mRenderer, shader.native_shader); @@ -770,7 +776,7 @@ class GLES20Canvas extends Canvas { return hasModifier; } - + private boolean setupColorFilter(Paint paint) { final ColorFilter filter = paint.getColorFilter(); if (filter != null) { @@ -782,5 +788,7 @@ class GLES20Canvas extends Canvas { private native void nSetupShader(int renderer, int shader); private native void nSetupColorFilter(int renderer, int colorFilter); + private native void nSetupShadow(int renderer, float radius, float dx, float dy, int color); + private native void nResetModifiers(int renderer); } diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index e4d48503eda1..ca9f371fc078 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -738,7 +738,7 @@ static JNINativeMethod methods[] = { (void*) SkPaintGlue::getStringBounds }, {"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V", (void*) SkPaintGlue::getCharArrayBounds }, - {"setShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer} + {"nSetShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer} }; static jfieldID req_fieldID(jfieldID id) { diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 9f94af9e30ce..4c6ecedff6ae 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -259,6 +259,7 @@ static void android_view_GLES20Canvas_resetModifiers(JNIEnv* env, jobject canvas OpenGLRenderer* renderer) { renderer->resetShader(); renderer->resetColorFilter(); + renderer->resetShadow(); } static void android_view_GLES20Canvas_setupShader(JNIEnv* env, jobject canvas, @@ -271,6 +272,11 @@ static void android_view_GLES20Canvas_setupColorFilter(JNIEnv* env, jobject canv renderer->setupColorFilter(filter); } +static void android_view_GLES20Canvas_setupShadow(JNIEnv* env, jobject canvas, + OpenGLRenderer* renderer, jfloat radius, jfloat dx, jfloat dy, jint color) { + renderer->setupShadow(radius, dx, dy, color); +} + // ---------------------------------------------------------------------------- // Text // ---------------------------------------------------------------------------- @@ -402,6 +408,7 @@ static JNINativeMethod gMethods[] = { { "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers }, { "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader }, { "nSetupColorFilter", "(II)V", (void*) android_view_GLES20Canvas_setupColorFilter }, + { "nSetupShadow", "(IFFFI)V", (void*) android_view_GLES20Canvas_setupShadow }, { "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray }, { "nDrawText", "(ILjava/lang/String;IIFFII)V", diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 6349cb33bda0..62fbfb45668a 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -43,6 +43,28 @@ public class Paint { private boolean mHasCompatScaling; private float mCompatScaling; private float mInvCompatScaling; + + /** + * @hide + */ + public boolean hasShadow; + /** + * @hide + */ + public float shadowDx; + /** + * @hide + */ + public float shadowDy; + /** + * @hide + */ + public float shadowRadius; + /** + * @hide + */ + public int shadowColor; + /** * @hide */ @@ -935,13 +957,23 @@ public class Paint { * offset and color, and blur radius. If radius is 0, then the shadow * layer is removed. */ - public native void setShadowLayer(float radius, float dx, float dy, int color); + public void setShadowLayer(float radius, float dx, float dy, int color) { + hasShadow = radius > 0.0f; + shadowRadius = radius; + shadowDx = dx; + shadowDy = dy; + shadowColor = color; + nSetShadowLayer(radius, dx, dy, color); + } + + private native void nSetShadowLayer(float radius, float dx, float dy, int color); /** * Clear the shadow layer. */ public void clearShadowLayer() { - setShadowLayer(0, 0, 0, 0); + hasShadow = false; + nSetShadowLayer(0, 0, 0, 0); } /** diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 1efe6b55384c..8ed3d7bf3c96 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -17,7 +17,8 @@ ifeq ($(USE_OPENGL_RENDERER),true) ProgramCache.cpp \ SkiaColorFilter.cpp \ SkiaShader.cpp \ - TextureCache.cpp + TextureCache.cpp \ + TextDropShadowCache.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index e807aba12a9c..ccc92ebdfe18 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -128,8 +128,11 @@ void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y, } Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) { - CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(utfChar); - if (cachedGlyph == NULL) { + CachedGlyphInfo* cachedGlyph = NULL; + ssize_t index = mCachedGlyphs.indexOfKey(utfChar); + if (index >= 0) { + cachedGlyph = mCachedGlyphs.valueAt(index); + } else { cachedGlyph = cacheGlyph(paint, utfChar); } @@ -510,10 +513,10 @@ void FontRenderer::checkTextureUpdate() { uint32_t yOffset = cl->mCurrentRow; uint32_t width = mCacheWidth; uint32_t height = cl->mMaxHeight; - void* textureData = mTextTexture + yOffset*width; + void* textureData = mTextTexture + yOffset*width; glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, - GL_ALPHA, GL_UNSIGNED_BYTE, textureData); + GL_ALPHA, GL_UNSIGNED_BYTE, textureData); cl->mDirty = false; } @@ -617,21 +620,33 @@ void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { } } FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { + uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { + checkInit(); + + if (!mCurrentFont) { + DropShadow image; + image.width = 0; + image.height = 0; + image.image = NULL; + image.penX = 0; + image.penY = 0; + return image; + } Rect bounds; mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds); - uint32_t paddedWidth = (uint32_t)(bounds.right - bounds.left) + 2*radius; - uint32_t paddedHeight = (uint32_t)(bounds.top - bounds.bottom) + 2*radius; + uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; + uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; - for(uint32_t i = 0; i < paddedWidth * paddedHeight; i ++) { + for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { dataBuffer[i] = 0; } + int penX = radius - bounds.left; int penY = radius - bounds.bottom; mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY, - dataBuffer, paddedWidth, paddedHeight); + dataBuffer, paddedWidth, paddedHeight); blurImage(dataBuffer, paddedWidth, paddedHeight, radius); DropShadow image; @@ -699,8 +714,7 @@ void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { } void FontRenderer::horizontalBlur(float* weights, int32_t radius, - const uint8_t* source, uint8_t* dest, - int32_t width, int32_t height) { + const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { float blurredPixel = 0.0f; float currentPixel = 0.0f; @@ -744,8 +758,7 @@ void FontRenderer::horizontalBlur(float* weights, int32_t radius, } void FontRenderer::verticalBlur(float* weights, int32_t radius, - const uint8_t* source, uint8_t* dest, - int32_t width, int32_t height) { + const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { float blurredPixel = 0.0f; float currentPixel = 0.0f; diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 6346ded270b6..96c92d5096cf 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -129,6 +129,14 @@ public: uint32_t len, int numGlyphs, int x, int y); struct DropShadow { + DropShadow() { }; + + DropShadow(const DropShadow& dropShadow): + width(dropShadow.width), height(dropShadow.height), + image(dropShadow.image), penX(dropShadow.penX), + penY(dropShadow.penY) { + } + uint32_t width; uint32_t height; uint8_t* image; @@ -139,7 +147,7 @@ public: // After renderDropShadow returns, the called owns the memory in DropShadow.image // and is responsible for releasing it when it's done with it DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex, - uint32_t len, int numGlyphs, uint32_t radius); + uint32_t len, int numGlyphs, uint32_t radius); GLuint getTexture() { checkInit(); @@ -154,7 +162,7 @@ protected: uint16_t mMaxWidth; uint32_t mCurrentRow; uint32_t mCurrentCol; - bool mDirty; + bool mDirty; CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow, uint32_t currentCol): @@ -237,9 +245,9 @@ protected: void computeGaussianWeights(float* weights, int32_t radius); void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest, - int32_t width, int32_t height); + int32_t width, int32_t height); void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest, - int32_t width, int32_t height); + int32_t width, int32_t height); void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius); }; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 3c1fe2a8090a..da5b9dd0097a 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -41,6 +41,7 @@ namespace uirenderer { #define DEFAULT_PATH_CACHE_SIZE 6.0f #define DEFAULT_PATCH_CACHE_SIZE 100 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f +#define DEFAULT_DROP_SHADOW_CACHE_SIZE 1.0f #define REQUIRED_TEXTURE_UNITS_COUNT 3 @@ -107,7 +108,8 @@ OpenGLRenderer::OpenGLRenderer(): mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)), mGradientCache(MB(DEFAULT_GRADIENT_CACHE_SIZE)), mPathCache(MB(DEFAULT_PATH_CACHE_SIZE)), - mPatchCache(DEFAULT_PATCH_CACHE_SIZE) { + mPatchCache(DEFAULT_PATCH_CACHE_SIZE), + mDropShadowCache(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) { LOGD("Create OpenGLRenderer"); char property[PROPERTY_VALUE_MAX]; @@ -139,9 +141,18 @@ OpenGLRenderer::OpenGLRenderer(): LOGD(" Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE); } + if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting drop shadow cache size to %sMB", property); + mDropShadowCache.setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE); + } + mDropShadowCache.setFontRenderer(mFontRenderer); + mCurrentProgram = NULL; mShader = NULL; mColorFilter = NULL; + mHasShadow = false; memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices)); @@ -163,6 +174,7 @@ OpenGLRenderer::~OpenGLRenderer() { mPathCache.clear(); mPatchCache.clear(); mProgramCache.clear(); + mDropShadowCache.clear(); } /////////////////////////////////////////////////////////////////////////////// @@ -550,6 +562,78 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, drawColorRect(left, top, right, bottom, color, mode); } +void OpenGLRenderer::renderShadow(const ShadowTexture* texture, float x, float y, + SkXfermode::Mode mode) { + const float sx = x - texture->left + mShadowDx; + const float sy = y - texture->top + mShadowDy; + + const GLfloat a = ((mShadowColor >> 24) & 0xFF) / 255.0f; + const GLfloat r = a * ((mShadowColor >> 16) & 0xFF) / 255.0f; + const GLfloat g = a * ((mShadowColor >> 8) & 0xFF) / 255.0f; + const GLfloat b = a * ((mShadowColor ) & 0xFF) / 255.0f; + + GLuint textureUnit = 0; + renderTextureAlpha8(texture, textureUnit, sx, sy, r, g, b, a, mode, false); +} + +void OpenGLRenderer::renderTextureAlpha8(const Texture* texture, GLuint& textureUnit, + float x, float y, float r, float g, float b, float a, SkXfermode::Mode mode, + bool applyFilters) { + // Describe the required shaders + ProgramDescription description; + description.hasTexture = true; + description.hasAlpha8Texture = true; + + if (applyFilters) { + if (mShader) { + mShader->describe(description, mExtensions); + } + if (mColorFilter) { + mColorFilter->describe(description, mExtensions); + } + } + + // Build and use the appropriate shader + useProgram(mProgramCache.get(description)); + + // Setup the blending mode + chooseBlending(true, mode); + bindTexture(texture->id, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit); + glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit); + + int texCoordsSlot = mCurrentProgram->getAttrib("texCoords"); + glEnableVertexAttribArray(texCoordsSlot); + + // Setup attributes + glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE, + gMeshStride, &mMeshVertices[0].position[0]); + glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, + gMeshStride, &mMeshVertices[0].texture[0]); + + // Setup uniforms + mModelView.loadTranslate(x, y, 0.0f); + mModelView.scale(texture->width, texture->height, 1.0f); + mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform); + + glUniform4f(mCurrentProgram->color, r, g, b, a); + + textureUnit++; + if (applyFilters) { + // Setup attributes and uniforms required by the shaders + if (mShader) { + mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit); + } + if (mColorFilter) { + mColorFilter->setupProgram(mCurrentProgram); + } + } + + // Draw the mesh + glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + + glDisableVertexAttribArray(texCoordsSlot); +} + #define kStdStrikeThru_Offset (-6.0f / 21.0f) #define kStdUnderline_Offset (1.0f / 9.0f) #define kStdUnderline_Thickness (1.0f / 18.0f) @@ -578,6 +662,15 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); + mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize()); + if (mHasShadow) { + glActiveTexture(gTextureUnits[0]); + const ShadowTexture* shadow = mDropShadowCache.get(paint, text, bytesCount, + count, mShadowRadius); + const AutoTexture autoCleanup(shadow); + renderShadow(shadow, x, y, mode); + } + uint32_t color = paint->getColor(); const GLfloat a = alpha / 255.0f; const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f; @@ -624,7 +717,6 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, } const Rect& clip = mSnapshot->getLocalClip(); - mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize()); mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -693,55 +785,9 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f; const GLfloat b = a * ((color ) & 0xFF) / 255.0f; - // Describe the required shaders - ProgramDescription description; - description.hasTexture = true; - description.hasAlpha8Texture = true; - if (mShader) { - mShader->describe(description, mExtensions); - } - if (mColorFilter) { - mColorFilter->describe(description, mExtensions); - } - - // Build and use the appropriate shader - useProgram(mProgramCache.get(description)); - - // Setup the blending mode - chooseBlending(true, mode); - bindTexture(texture->id, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit); - glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit); - - int texCoordsSlot = mCurrentProgram->getAttrib("texCoords"); - glEnableVertexAttribArray(texCoordsSlot); - - // Setup attributes - glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE, - gMeshStride, &mMeshVertices[0].position[0]); - glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, - gMeshStride, &mMeshVertices[0].texture[0]); - - // Setup uniforms - mModelView.loadTranslate(texture->left - texture->offset, - texture->top - texture->offset, 0.0f); - mModelView.scale(texture->width, texture->height, 1.0f); - mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform); - - glUniform4f(mCurrentProgram->color, r, g, b, a); - - textureUnit++; - // Setup attributes and uniforms required by the shaders - if (mShader) { - mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit); - } - if (mColorFilter) { - mColorFilter->setupProgram(mCurrentProgram); - } - - // Draw the mesh - glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); - - glDisableVertexAttribArray(texCoordsSlot); + const float x = texture->left - texture->offset; + const float y = texture->top - texture->offset; + renderTextureAlpha8(texture, textureUnit, x, y, r, g, b, a, mode, true); } /////////////////////////////////////////////////////////////////////////////// @@ -772,6 +818,22 @@ void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) { } /////////////////////////////////////////////////////////////////////////////// +// Drop shadow +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::resetShadow() { + mHasShadow = false; +} + +void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) { + mHasShadow = true; + mShadowRadius = radius; + mShadowDx = dx; + mShadowDy = dy; + mShadowColor = color; +} + +/////////////////////////////////////////////////////////////////////////////// // Drawing implementation /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 76783e9089bb..948ff1336595 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -45,6 +45,7 @@ #include "SkiaShader.h" #include "SkiaColorFilter.h" #include "PathCache.h" +#include "TextDropShadowCache.h" namespace android { namespace uirenderer { @@ -101,6 +102,9 @@ public: void resetColorFilter(); void setupColorFilter(SkiaColorFilter* filter); + void resetShadow(); + void setupShadow(float radius, float dx, float dy, int color); + void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint); private: @@ -222,6 +226,34 @@ private: GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0); /** + * Renders the specified shadow. + * + * @param texture The shadow texture + * @param x The x coordinate of the shadow + * @param y The y coordinate of the shadow + * @param mode The blending mode + */ + void renderShadow(const ShadowTexture* texture, float x, float y, SkXfermode::Mode mode); + + /** + * Renders the specified Alpha8 texture as a rectangle. + * + * @param texture The texture to render with + * @param textureUnit The texture unit to use, may be modified + * @param x The x coordinate of the rectangle to draw + * @param y The y coordinate of the rectangle to draw + * @param r The red component of the color + * @param g The green component of the color + * @param b The blue component of the color + * @param a The alpha component of the color + * @param mode The blending mode + * @param applyFilters Whether or not to take color filters and + * shaders into account + */ + void renderTextureAlpha8(const Texture* texture, GLuint& textureUnit, float x, float y, + float r, float g, float b, float a, SkXfermode::Mode mode, bool applyFilters); + + /** * Resets the texture coordinates stored in mMeshVertices. Setting the values * back to default is achieved by calling: * @@ -304,6 +336,13 @@ private: // Font renderer FontRenderer mFontRenderer; + // Drop shadow + bool mHasShadow; + float mShadowRadius; + float mShadowDx; + float mShadowDy; + int mShadowColor; + // Various caches TextureCache mTextureCache; LayerCache mLayerCache; @@ -311,6 +350,7 @@ private: ProgramCache mProgramCache; PathCache mPathCache; PatchCache mPatchCache; + TextDropShadowCache mDropShadowCache; }; // class OpenGLRenderer }; // namespace uirenderer diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 9a22dc0a41e0..10440ea3cebf 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -166,7 +166,7 @@ void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); texture->blend = true; - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bitmap.rowBytesAsPixels(), texture->height, 0, + 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); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 915b0be34f71..7514b6f0d905 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -27,6 +27,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_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size" // These properties are defined in pixels #define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width" diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp new file mode 100644 index 000000000000..aab5bd4d347d --- /dev/null +++ b/libs/hwui/TextDropShadowCache.cpp @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "TextDropShadowCache.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize): + mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize) { + mCache.setOnEntryRemovedListener(this); +} + +TextDropShadowCache::~TextDropShadowCache() { + mCache.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t TextDropShadowCache::getSize() { + return mSize; +} + +uint32_t TextDropShadowCache::getMaxSize() { + return mMaxSize; +} + +void TextDropShadowCache::setMaxSize(uint32_t maxSize) { + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture) { + const uint32_t size = texture->width * texture->height; + mSize -= size; + + if (texture) { + glDeleteTextures(1, &texture->id); + delete texture; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void TextDropShadowCache::clear() { + mCache.clear(); +} + +ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len, + int numGlyphs, uint32_t radius) { + ShadowText entry(paint, radius, len, text); + ShadowTexture* texture = mCache.get(entry); + + if (!texture) { + FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(paint, text, 0, + len, numGlyphs, radius); + + texture = new ShadowTexture; + texture->left = shadow.penX; + texture->top = shadow.penY; + texture->width = shadow.width; + texture->height = shadow.height; + texture->generation = 0; + texture->blend = true; + + const uint32_t size = shadow.width * shadow.height; + // Don't even try to cache a bitmap that's bigger than the cache + if (size < mMaxSize) { + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + } + + glGenTextures(1, &texture->id); + + glBindTexture(GL_TEXTURE_2D, texture->id); + // Textures are Alpha8 + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image); + + 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); + + if (size < mMaxSize) { + mSize += size; + mCache.put(entry, texture); + } else { + texture->cleanup = true; + } + + // Cleanup shadow + delete[] shadow.image; + } + + return texture; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h new file mode 100644 index 000000000000..9c861870320d --- /dev/null +++ b/libs/hwui/TextDropShadowCache.h @@ -0,0 +1,140 @@ +/* + * 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. + */ + +#ifndef ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H +#define ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H + +#include <GLES2/gl2.h> + +#include <SkPaint.h> + +#include "GenerationCache.h" +#include "FontRenderer.h" +#include "Texture.h" + +namespace android { +namespace uirenderer { + +struct ShadowText { + ShadowText() { } + + ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText): + paint(paint), radius(radius), len(len) { + text = new char[len]; + memcpy(text, srcText, len); + + hash = 0; + uint32_t multiplier = 1; + for (uint32_t i = 0; i < len; i++) { + hash += text[i] * multiplier; + uint32_t shifted = multiplier << 5; + multiplier = shifted - multiplier; + } + } + + ShadowText(const ShadowText& shadow): + paint(shadow.paint), radius(shadow.radius), len(shadow.len), hash(shadow.hash) { + text = new char[len]; + memcpy(text, shadow.text, shadow.len); + } + + ~ShadowText() { + delete[] text; + } + + SkPaint* paint; + uint32_t radius; + uint32_t len; + uint32_t hash; + char *text; + + bool operator<(const ShadowText& rhs) const { + if (len < rhs.len) return true; + else if (len == rhs.len) { + if (radius < rhs.radius) return true; + else if (radius == rhs.radius) { + if (paint < rhs.paint) return true; + else if (paint == rhs.paint) { + if (hash < rhs.hash) return true; + if (hash == rhs.hash) { + return strncmp(text, rhs.text, len) < 0; + } + } + } + } + return false; + } +}; // struct ShadowText + +/** + * Alpha texture used to represent a shadow. + */ +struct ShadowTexture: public Texture { + ShadowTexture(): Texture() { + } + + float left; + float top; +}; // struct ShadowTexture + +class TextDropShadowCache: public OnEntryRemoved<ShadowText, ShadowTexture*> { +public: + TextDropShadowCache(uint32_t maxByteSize); + ~TextDropShadowCache(); + + /** + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. + */ + void operator()(ShadowText& text, ShadowTexture*& texture); + + ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len, + int numGlyphs, uint32_t radius); + + /** + * Clears the cache. This causes all textures to be deleted. + */ + void clear(); + + void setFontRenderer(FontRenderer& fontRenderer) { + mRenderer = &fontRenderer; + } + + /** + * 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(); + +private: + GenerationCache<ShadowText, ShadowTexture*> mCache; + + uint32_t mSize; + uint32_t mMaxSize; + FontRenderer* mRenderer; +}; // class TextDropShadowCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java index 59f665cad96a..8af1b7b2e298 100644 --- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java +++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java @@ -65,8 +65,16 @@ public class TextActivity extends Activity { canvas.drawText("Hello OpenGL renderer!", 100, 60, mMediumPaint); mMediumPaint.setTextAlign(Paint.Align.LEFT); canvas.drawText("Hello OpenGL renderer!", 100, 100, mMediumPaint); + mMediumPaint.setShadowLayer(2.5f, 0.0f, 0.0f, 0xff000000); + canvas.drawText("Hello OpenGL renderer!", 100, 150, mMediumPaint); + mMediumPaint.clearShadowLayer(); canvas.drawText("Hello OpenGL renderer!", 100, 200, mLargePaint); - + + mLargePaint.setShadowLayer(2.5f, 3.0f, 3.0f, 0xff000000); + canvas.drawText("Hello OpenGL renderer!", 100, 400, mLargePaint); + mLargePaint.setShadowLayer(3.0f, 3.0f, 3.0f, 0xff00ff00); + canvas.drawText("Hello OpenGL renderer!", 100, 500, mLargePaint); + mLargePaint.clearShadowLayer(); canvas.drawText("Hello OpenGL renderer!", 500, 40, mStrikePaint); mStrikePaint.setStrikeThruText(true); |