Add drop shadows.
Change-Id: Ic6a72409d4785968d1fbdff229f17ee5c00b240b
diff --git a/api/current.xml b/api/current.xml
index 4ec1c3e..d35037e 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 91dbe1f..4c72e95 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -756,6 +756,12 @@
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 @@
return hasModifier;
}
-
+
private boolean setupColorFilter(Paint paint) {
final ColorFilter filter = paint.getColorFilter();
if (filter != null) {
@@ -782,5 +788,7 @@
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 e4d4850..ca9f371 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -738,7 +738,7 @@
(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 9f94af9..4c6eced 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -259,6 +259,7 @@
OpenGLRenderer* renderer) {
renderer->resetShader();
renderer->resetColorFilter();
+ renderer->resetShadow();
}
static void android_view_GLES20Canvas_setupShader(JNIEnv* env, jobject canvas,
@@ -271,6 +272,11 @@
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 @@
{ "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 6349cb3..62fbfb4 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -43,6 +43,28 @@
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 @@
* 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 1efe6b5..8ed3d7b 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -17,7 +17,8 @@
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 e807aba..ccc92eb 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -128,8 +128,11 @@
}
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 @@
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 @@
}
}
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::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::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 6346ded..96c92d5 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -129,6 +129,14 @@
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 @@
// 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 @@
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 @@
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 3c1fe2a..da5b9dd 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -41,6 +41,7 @@
#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 @@
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 @@
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 @@
mPathCache.clear();
mPatchCache.clear();
mProgramCache.clear();
+ mDropShadowCache.clear();
}
///////////////////////////////////////////////////////////////////////////////
@@ -550,6 +562,78 @@
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 @@
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 @@
}
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 @@
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 @@
}
///////////////////////////////////////////////////////////////////////////////
+// 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 76783e9..948ff13 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 @@
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 @@
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 @@
// 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 @@
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 9a22dc0..10440ea 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -166,7 +166,7 @@
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 915b0be..7514b6f 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 0000000..aab5bd4
--- /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 0000000..9c86187
--- /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 59f665c..8af1b7b 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 @@
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);