summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/AssetManager.cpp18
-rw-r--r--libs/androidfw/BackupData.cpp1
-rw-r--r--libs/androidfw/ResourceTypes.cpp40
-rw-r--r--libs/androidfw/tests/ObbFile_test.cpp4
-rw-r--r--libs/diskusage/dirsize.c7
-rw-r--r--libs/hwui/Android.mk19
-rw-r--r--libs/hwui/Caches.cpp166
-rw-r--r--libs/hwui/Caches.h37
-rw-r--r--libs/hwui/Debug.h3
-rw-r--r--libs/hwui/DisplayListRenderer.cpp384
-rw-r--r--libs/hwui/DisplayListRenderer.h50
-rwxr-xr-xlibs/hwui/Dither.cpp86
-rwxr-xr-xlibs/hwui/Dither.h47
-rw-r--r--libs/hwui/Extensions.h21
-rw-r--r--libs/hwui/FontRenderer.cpp858
-rw-r--r--libs/hwui/FontRenderer.h275
-rw-r--r--libs/hwui/GammaFontRenderer.cpp126
-rw-r--r--libs/hwui/GammaFontRenderer.h148
-rw-r--r--libs/hwui/GradientCache.cpp148
-rw-r--r--libs/hwui/GradientCache.h45
-rw-r--r--libs/hwui/Layer.cpp79
-rw-r--r--libs/hwui/Layer.h55
-rw-r--r--libs/hwui/LayerCache.cpp11
-rw-r--r--libs/hwui/LayerRenderer.cpp60
-rw-r--r--libs/hwui/LayerRenderer.h14
-rw-r--r--libs/hwui/Matrix.cpp13
-rw-r--r--libs/hwui/Matrix.h9
-rw-r--r--libs/hwui/OpenGLRenderer.cpp1060
-rw-r--r--libs/hwui/OpenGLRenderer.h280
-rw-r--r--libs/hwui/Patch.cpp9
-rw-r--r--libs/hwui/PathRenderer.cpp488
-rw-r--r--libs/hwui/PathRenderer.h106
-rw-r--r--libs/hwui/Program.cpp11
-rw-r--r--libs/hwui/Program.h37
-rw-r--r--libs/hwui/ProgramCache.cpp352
-rw-r--r--libs/hwui/Properties.h72
-rw-r--r--libs/hwui/Rect.h4
-rw-r--r--libs/hwui/ResourceCache.cpp178
-rw-r--r--libs/hwui/ResourceCache.h49
-rw-r--r--libs/hwui/SkiaColorFilter.cpp15
-rw-r--r--libs/hwui/SkiaShader.cpp88
-rw-r--r--libs/hwui/SkiaShader.h16
-rw-r--r--libs/hwui/Snapshot.cpp80
-rw-r--r--libs/hwui/Snapshot.h14
-rw-r--r--libs/hwui/Stencil.cpp92
-rw-r--r--libs/hwui/Stencil.h99
-rw-r--r--libs/hwui/TextDropShadowCache.cpp10
-rw-r--r--libs/hwui/TextDropShadowCache.h21
-rw-r--r--libs/hwui/Texture.h2
-rw-r--r--libs/hwui/font/CacheTexture.cpp195
-rw-r--r--libs/hwui/font/CacheTexture.h211
-rw-r--r--libs/hwui/font/CachedGlyphInfo.h58
-rw-r--r--libs/hwui/font/Font.cpp443
-rw-r--r--libs/hwui/font/Font.h130
-rw-r--r--libs/hwui/font/FontUtil.h60
-rw-r--r--libs/storage/IMountService.cpp5
56 files changed, 4806 insertions, 2103 deletions
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 8d59d8e9d088..8bd805cc9934 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -98,6 +98,24 @@ namespace {
return path;
}
+
+ /*
+ * Like strdup(), but uses C++ "new" operator instead of malloc.
+ */
+ static char* strdupNew(const char* str)
+ {
+ char* newStr;
+ int len;
+
+ if (str == NULL)
+ return NULL;
+
+ len = strlen(str);
+ newStr = new char[len+1];
+ memcpy(newStr, str, len+1);
+
+ return newStr;
+ }
}
/*
diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp
index 7b1bcba2d130..4e3b52201110 100644
--- a/libs/androidfw/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -327,6 +327,7 @@ BackupDataReader::SkipEntityData()
if (pos == -1) {
return errno;
}
+ m_pos = pos;
}
SKIP_PADDING();
return NO_ERROR;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 8cce191e3e8b..0107da4eea57 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1470,6 +1470,9 @@ int ResTable_config::compareLogical(const ResTable_config& o) const {
if (country[1] != o.country[1]) {
return country[1] < o.country[1] ? -1 : 1;
}
+ if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) {
+ return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1;
+ }
if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
return smallestScreenWidthDp < o.smallestScreenWidthDp ? -1 : 1;
}
@@ -1558,6 +1561,13 @@ bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const {
}
}
+ if (screenLayout || o.screenLayout) {
+ if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0) {
+ if (!(screenLayout & MASK_LAYOUTDIR)) return false;
+ if (!(o.screenLayout & MASK_LAYOUTDIR)) return true;
+ }
+ }
+
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
if (!smallestScreenWidthDp) return false;
@@ -1683,6 +1693,15 @@ bool ResTable_config::isBetterThan(const ResTable_config& o,
}
}
+ if (screenLayout || o.screenLayout) {
+ if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0
+ && (requested->screenLayout & MASK_LAYOUTDIR)) {
+ int myLayoutDir = screenLayout & MASK_LAYOUTDIR;
+ int oLayoutDir = o.screenLayout & MASK_LAYOUTDIR;
+ return (myLayoutDir > oLayoutDir);
+ }
+ }
+
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
// The configuration closest to the actual size is best.
// We assume that larger configs have already been filtered
@@ -1906,6 +1925,12 @@ bool ResTable_config::match(const ResTable_config& settings) const {
}
}
if (screenConfig != 0) {
+ const int layoutDir = screenLayout&MASK_LAYOUTDIR;
+ const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR;
+ if (layoutDir != 0 && layoutDir != setLayoutDir) {
+ return false;
+ }
+
const int screenSize = screenLayout&MASK_SCREENSIZE;
const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE;
// Any screen sizes for larger screens than the setting do not
@@ -2032,6 +2057,21 @@ String8 ResTable_config::toString() const {
if (res.size() > 0) res.append("-");
res.append(country, 2);
}
+ if ((screenLayout&MASK_LAYOUTDIR) != 0) {
+ if (res.size() > 0) res.append("-");
+ switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) {
+ case ResTable_config::LAYOUTDIR_LTR:
+ res.append("ldltr");
+ break;
+ case ResTable_config::LAYOUTDIR_RTL:
+ res.append("ldrtl");
+ break;
+ default:
+ res.appendFormat("layoutDir=%d",
+ dtohs(screenLayout&ResTable_config::MASK_LAYOUTDIR));
+ break;
+ }
+ }
if (smallestScreenWidthDp != 0) {
if (res.size() > 0) res.append("-");
res.appendFormat("sw%ddp", dtohs(smallestScreenWidthDp));
diff --git a/libs/androidfw/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp
index 09d4d7d985a9..2c9f65067cbf 100644
--- a/libs/androidfw/tests/ObbFile_test.cpp
+++ b/libs/androidfw/tests/ObbFile_test.cpp
@@ -22,6 +22,8 @@
#include <gtest/gtest.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
@@ -43,7 +45,7 @@ protected:
mFileName = new char[totalLen];
snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME);
- int fd = ::open(mFileName, O_CREAT | O_TRUNC);
+ int fd = ::open(mFileName, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd < 0) {
FAIL() << "Couldn't create " << mFileName << " for tests";
}
diff --git a/libs/diskusage/dirsize.c b/libs/diskusage/dirsize.c
index 45e7b2a17790..6703783b5bbb 100644
--- a/libs/diskusage/dirsize.c
+++ b/libs/diskusage/dirsize.c
@@ -49,6 +49,9 @@ int64_t calculate_dir_size(int dfd)
while ((de = readdir(d))) {
const char *name = de->d_name;
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+ size += stat_size(&s);
+ }
if (de->d_type == DT_DIR) {
int subfd;
@@ -64,10 +67,6 @@ int64_t calculate_dir_size(int dfd)
if (subfd >= 0) {
size += calculate_dir_size(subfd);
}
- } else {
- if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
- size += stat_size(&s);
- }
}
}
closedir(d);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 5ec3983cf728..549edd2bf7b3 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -6,17 +6,22 @@ include $(CLEAR_VARS)
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SRC_FILES:= \
utils/SortedListImpl.cpp \
+ font/CacheTexture.cpp \
+ font/Font.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
DisplayListLogBuffer.cpp \
DisplayListRenderer.cpp \
+ Dither.cpp \
FboCache.cpp \
GradientCache.cpp \
+ Layer.cpp \
LayerCache.cpp \
LayerRenderer.cpp \
Matrix.cpp \
OpenGLRenderer.cpp \
+ PathRenderer.cpp \
Patch.cpp \
PatchCache.cpp \
PathCache.cpp \
@@ -27,9 +32,10 @@ ifeq ($(USE_OPENGL_RENDERER),true)
SkiaColorFilter.cpp \
SkiaShader.cpp \
Snapshot.cpp \
+ Stencil.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
-
+
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
$(LOCAL_PATH)/../../include/utils \
@@ -40,12 +46,19 @@ ifeq ($(USE_OPENGL_RENDERER),true)
external/skia/include/utils
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DGL_GLEXT_PROTOTYPES
- LOCAL_CFLAGS += -fvisibility=hidden
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
-
+
+ ifndef HWUI_COMPILE_SYMBOLS
+ LOCAL_CFLAGS += -fvisibility=hidden
+ endif
+
+ ifdef HWUI_COMPILE_FOR_PERF
+ LOCAL_CFLAGS += -fno-omit-frame-pointer -marm -mapcs
+ endif
+
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index f2108203b9ba..7853ae4168b9 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -49,15 +49,13 @@ namespace uirenderer {
Caches::Caches(): Singleton<Caches>(), mInitialized(false) {
init();
+ initFont();
initExtensions();
initConstraints();
+ initProperties();
mDebugLevel = readDebugLevel();
ALOGD("Enabling debug mode %d", mDebugLevel);
-
-#if RENDER_LAYERS_AS_REGIONS
- INIT_LOGD("Layers will be composited as regions");
-#endif
}
void Caches::init() {
@@ -70,10 +68,13 @@ void Caches::init() {
mCurrentBuffer = meshBuffer;
mCurrentIndicesBuffer = 0;
mCurrentPositionPointer = this;
+ mCurrentPositionStride = 0;
mCurrentTexCoordsPointer = this;
mTexCoordsArrayEnabled = false;
+ glDisable(GL_SCISSOR_TEST);
+ scissorEnabled = false;
mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
glActiveTexture(gTextureUnits[0]);
@@ -86,9 +87,15 @@ void Caches::init() {
lastDstMode = GL_ZERO;
currentProgram = NULL;
+ mFunctorsCount = 0;
+
mInitialized = true;
}
+void Caches::initFont() {
+ fontRenderer = GammaFontRenderer::createRenderer();
+}
+
void Caches::initExtensions() {
if (extensions.hasDebugMarker()) {
eventMark = glInsertEventMarkerEXT;
@@ -119,6 +126,23 @@ void Caches::initConstraints() {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
+void Caches::initProperties() {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_DEBUG_LAYERS_UPDATES, property, NULL) > 0) {
+ INIT_LOGD(" Layers updates debug enabled: %s", property);
+ debugLayersUpdates = !strcmp(property, "true");
+ } else {
+ debugLayersUpdates = false;
+ }
+
+ if (property_get(PROPERTY_DEBUG_OVERDRAW, property, NULL) > 0) {
+ INIT_LOGD(" Overdraw debug enabled: %s", property);
+ debugOverdraw = !strcmp(property, "true");
+ } else {
+ debugOverdraw = false;
+ }
+}
+
void Caches::terminate() {
if (!mInitialized) return;
@@ -169,8 +193,8 @@ void Caches::dumpMemoryUsage(String8 &log) {
arcShapeCache.getSize(), arcShapeCache.getMaxSize());
log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
dropShadowCache.getMaxSize());
- for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
- const uint32_t size = fontRenderer.getFontRendererSize(i);
+ for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
+ const uint32_t size = fontRenderer->getFontRendererSize(i);
log.appendFormat(" FontRenderer %d %8d / %8d\n", i, size, size);
}
log.appendFormat("Other:\n");
@@ -190,8 +214,8 @@ void Caches::dumpMemoryUsage(String8 &log) {
total += ovalShapeCache.getSize();
total += rectShapeCache.getSize();
total += arcShapeCache.getSize();
- for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
- total += fontRenderer.getFontRendererSize(i);
+ for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
+ total += fontRenderer->getFontRendererSize(i);
}
log.appendFormat("Total memory usage:\n");
@@ -206,21 +230,29 @@ void Caches::clearGarbage() {
textureCache.clearGarbage();
pathCache.clearGarbage();
- Mutex::Autolock _l(mGarbageLock);
+ Vector<DisplayList*> displayLists;
+ Vector<Layer*> layers;
- size_t count = mLayerGarbage.size();
- for (size_t i = 0; i < count; i++) {
- Layer* layer = mLayerGarbage.itemAt(i);
- LayerRenderer::destroyLayer(layer);
+ { // scope for the lock
+ Mutex::Autolock _l(mGarbageLock);
+ displayLists = mDisplayListGarbage;
+ layers = mLayerGarbage;
+ mDisplayListGarbage.clear();
+ mLayerGarbage.clear();
}
- mLayerGarbage.clear();
- count = mDisplayListGarbage.size();
+ size_t count = displayLists.size();
for (size_t i = 0; i < count; i++) {
- DisplayList* displayList = mDisplayListGarbage.itemAt(i);
+ DisplayList* displayList = displayLists.itemAt(i);
delete displayList;
}
- mDisplayListGarbage.clear();
+
+ count = layers.size();
+ for (size_t i = 0; i < count; i++) {
+ Layer* layer = layers.itemAt(i);
+ delete layer;
+ }
+ layers.clear();
}
void Caches::deleteLayerDeferred(Layer* layer) {
@@ -236,18 +268,17 @@ void Caches::deleteDisplayListDeferred(DisplayList* displayList) {
void Caches::flush(FlushMode mode) {
FLUSH_LOGD("Flushing caches (mode %d)", mode);
- clearGarbage();
-
switch (mode) {
case kFlushMode_Full:
textureCache.clear();
patchCache.clear();
dropShadowCache.clear();
gradientCache.clear();
- fontRenderer.clear();
+ fontRenderer->clear();
+ dither.clear();
// fall through
case kFlushMode_Moderate:
- fontRenderer.flush();
+ fontRenderer->flush();
textureCache.flush();
pathCache.clear();
roundRectShapeCache.clear();
@@ -260,6 +291,8 @@ void Caches::flush(FlushMode mode) {
layerCache.clear();
break;
}
+
+ clearGarbage();
}
///////////////////////////////////////////////////////////////////////////////
@@ -306,15 +339,22 @@ bool Caches::unbindIndicesBuffer() {
return false;
}
-void Caches::bindPositionVertexPointer(bool force, GLuint slot, GLvoid* vertices, GLsizei stride) {
- if (force || vertices != mCurrentPositionPointer) {
+///////////////////////////////////////////////////////////////////////////////
+// Meshes and textures
+///////////////////////////////////////////////////////////////////////////////
+
+void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride) {
+ if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
+ GLuint slot = currentProgram->position;
glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
mCurrentPositionPointer = vertices;
+ mCurrentPositionStride = stride;
}
}
-void Caches::bindTexCoordsVertexPointer(bool force, GLuint slot, GLvoid* vertices) {
+void Caches::bindTexCoordsVertexPointer(bool force, GLvoid* vertices) {
if (force || vertices != mCurrentTexCoordsPointer) {
+ GLuint slot = currentProgram->texCoords;
glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, gMeshStride, vertices);
mCurrentTexCoordsPointer = vertices;
}
@@ -351,14 +391,52 @@ void Caches::activeTexture(GLuint textureUnit) {
}
}
-void Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
- if (x != mScissorX || y != mScissorY || width != mScissorWidth || height != mScissorHeight) {
+///////////////////////////////////////////////////////////////////////////////
+// Scissor
+///////////////////////////////////////////////////////////////////////////////
+
+bool Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
+ if (scissorEnabled && (x != mScissorX || y != mScissorY ||
+ width != mScissorWidth || height != mScissorHeight)) {
+
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+
glScissor(x, y, width, height);
mScissorX = x;
mScissorY = y;
mScissorWidth = width;
mScissorHeight = height;
+
+ return true;
+ }
+ return false;
+}
+
+bool Caches::enableScissor() {
+ if (!scissorEnabled) {
+ glEnable(GL_SCISSOR_TEST);
+ scissorEnabled = true;
+ return true;
+ }
+ return false;
+}
+
+bool Caches::disableScissor() {
+ if (scissorEnabled) {
+ glDisable(GL_SCISSOR_TEST);
+ scissorEnabled = false;
+ return true;
+ }
+ return false;
+}
+
+void Caches::setScissorEnabled(bool enabled) {
+ if (scissorEnabled != enabled) {
+ if (enabled) glEnable(GL_SCISSOR_TEST);
+ else glDisable(GL_SCISSOR_TEST);
+ scissorEnabled = enabled;
}
}
@@ -366,6 +444,42 @@ void Caches::resetScissor() {
mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
}
+///////////////////////////////////////////////////////////////////////////////
+// Tiling
+///////////////////////////////////////////////////////////////////////////////
+
+void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool opaque) {
+ if (extensions.hasTiledRendering() && !debugOverdraw) {
+ glStartTilingQCOM(x, y, width, height, (opaque ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
+ }
+}
+
+void Caches::endTiling() {
+ if (extensions.hasTiledRendering() && !debugOverdraw) {
+ glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
+ }
+}
+
+bool Caches::hasRegisteredFunctors() {
+ return mFunctorsCount > 0;
+}
+
+void Caches::registerFunctors(uint32_t functorCount) {
+ mFunctorsCount += functorCount;
+}
+
+void Caches::unregisterFunctors(uint32_t functorCount) {
+ if (functorCount > mFunctorsCount) {
+ mFunctorsCount = 0;
+ } else {
+ mFunctorsCount -= functorCount;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Regions
+///////////////////////////////////////////////////////////////////////////////
+
TextureVertex* Caches::getRegionMesh() {
// Create the mesh, 2 triangles and 4 vertices per rectangle in the region
if (!mRegionMesh) {
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 58361c9568f1..ad1ff6fa56be 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -38,6 +38,8 @@
#include "TextDropShadowCache.h"
#include "FboCache.h"
#include "ResourceCache.h"
+#include "Stencil.h"
+#include "Dither.h"
namespace android {
namespace uirenderer {
@@ -66,6 +68,7 @@ static const GLsizei gVertexStride = sizeof(Vertex);
static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
static const GLsizei gAAVertexStride = sizeof(AAVertex);
static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
+static const GLsizei gVertexAlphaOffset = 2 * sizeof(float);
static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
static const GLsizei gMeshCount = 4;
@@ -170,14 +173,13 @@ public:
* Binds an attrib to the specified float vertex pointer.
* Assumes a stride of gMeshStride and a size of 2.
*/
- void bindPositionVertexPointer(bool force, GLuint slot, GLvoid* vertices,
- GLsizei stride = gMeshStride);
+ void bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride = gMeshStride);
/**
* Binds an attrib to the specified float vertex pointer.
* Assumes a stride of gMeshStride and a size of 2.
*/
- void bindTexCoordsVertexPointer(bool force, GLuint slot, GLvoid* vertices);
+ void bindTexCoordsVertexPointer(bool force, GLvoid* vertices);
/**
* Resets the vertex pointers.
@@ -197,13 +199,20 @@ public:
/**
* Sets the scissor for the current surface.
*/
- void setScissor(GLint x, GLint y, GLint width, GLint height);
+ bool setScissor(GLint x, GLint y, GLint width, GLint height);
/**
* Resets the scissor state.
*/
void resetScissor();
+ bool enableScissor();
+ bool disableScissor();
+ void setScissorEnabled(bool enabled);
+
+ void startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool opaque);
+ void endTiling();
+
/**
* Returns the mesh used to draw regions. Calling this method will
* bind a VBO of type GL_ELEMENT_ARRAY_BUFFER that contains the
@@ -217,10 +226,15 @@ public:
void dumpMemoryUsage();
void dumpMemoryUsage(String8& log);
+ bool hasRegisteredFunctors();
+ void registerFunctors(uint32_t functorCount);
+ void unregisterFunctors(uint32_t functorCount);
+
bool blend;
GLenum lastSrcMode;
GLenum lastDstMode;
Program* currentProgram;
+ bool scissorEnabled;
// VBO to draw with
GLuint meshBuffer;
@@ -230,6 +244,8 @@ public:
// Misc
GLint maxTextureSize;
+ bool debugLayersUpdates;
+ bool debugOverdraw;
TextureCache textureCache;
LayerCache layerCache;
@@ -244,9 +260,15 @@ public:
PatchCache patchCache;
TextDropShadowCache dropShadowCache;
FboCache fboCache;
- GammaFontRenderer fontRenderer;
ResourceCache resourceCache;
+ GammaFontRenderer* fontRenderer;
+
+ Dither dither;
+#if STENCIL_BUFFER_SIZE
+ Stencil stencil;
+#endif
+
// Debug methods
PFNGLINSERTEVENTMARKEREXTPROC eventMark;
PFNGLPUSHGROUPMARKEREXTPROC startMark;
@@ -256,8 +278,10 @@ public:
PFNGLGETOBJECTLABELEXTPROC getLabel;
private:
+ void initFont();
void initExtensions();
void initConstraints();
+ void initProperties();
static void eventMarkNull(GLsizei length, const GLchar* marker) { }
static void startMarkNull(GLsizei length, const GLchar* marker) { }
@@ -274,6 +298,7 @@ private:
GLuint mCurrentBuffer;
GLuint mCurrentIndicesBuffer;
void* mCurrentPositionPointer;
+ GLsizei mCurrentPositionStride;
void* mCurrentTexCoordsPointer;
bool mTexCoordsArrayEnabled;
@@ -295,6 +320,8 @@ private:
DebugLevel mDebugLevel;
bool mInitialized;
+
+ uint32_t mFunctorsCount;
}; // class Caches
}; // namespace uirenderer
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 55a860ebc69b..6795ac3205d1 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -68,6 +68,9 @@
// Turn on to dump display list state
#define DEBUG_DISPLAY_LIST 0
+// Turn on to insert an event marker for each display list op
+#define DEBUG_DISPLAY_LIST_OPS_AS_EVENTS 0
+
#if DEBUG_INIT
#define INIT_LOGD(...) ALOGD(__VA_ARGS__)
#else
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 0c89014dfd66..589d5c2eb985 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -61,9 +61,9 @@ const char* DisplayList::OP_NAMES[] = {
"DrawPath",
"DrawLines",
"DrawPoints",
- "DrawText",
"DrawTextOnPath",
"DrawPosText",
+ "DrawText",
"ResetShader",
"SetupShader",
"ResetColorFilter",
@@ -105,35 +105,6 @@ DisplayList::~DisplayList() {
clearResources();
}
-void DisplayList::initProperties() {
- mLeft = 0;
- mTop = 0;
- mRight = 0;
- mBottom = 0;
- mClipChildren = true;
- mAlpha = 1;
- mMultipliedAlpha = 255;
- mHasOverlappingRendering = true;
- mTranslationX = 0;
- mTranslationY = 0;
- mRotation = 0;
- mRotationX = 0;
- mRotationY= 0;
- mScaleX = 1;
- mScaleY = 1;
- mPivotX = 0;
- mPivotY = 0;
- mCameraDistance = 0;
- mMatrixDirty = false;
- mMatrixFlags = 0;
- mPrevWidth = -1;
- mPrevHeight = -1;
- mWidth = 0;
- mHeight = 0;
- mPivotExplicitlySet = false;
- mCaching = false;
-}
-
void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {
if (displayList) {
DISPLAY_LIST_LOGD("Deferring display list destruction");
@@ -143,12 +114,14 @@ void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {
void DisplayList::clearResources() {
sk_free((void*) mReader.base());
+ mReader.setMemory(NULL, 0);
delete mTransformMatrix;
delete mTransformCamera;
delete mTransformMatrix3D;
delete mStaticMatrix;
delete mAnimationMatrix;
+
mTransformMatrix = NULL;
mTransformCamera = NULL;
mTransformMatrix3D = NULL;
@@ -156,102 +129,135 @@ void DisplayList::clearResources() {
mAnimationMatrix = NULL;
Caches& caches = Caches::getInstance();
+ caches.unregisterFunctors(mFunctorCount);
+ caches.resourceCache.lock();
for (size_t i = 0; i < mBitmapResources.size(); i++) {
- caches.resourceCache.decrementRefcount(mBitmapResources.itemAt(i));
+ caches.resourceCache.decrementRefcountLocked(mBitmapResources.itemAt(i));
}
- mBitmapResources.clear();
for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) {
SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i);
- caches.resourceCache.decrementRefcount(bitmap);
- caches.resourceCache.destructor(bitmap);
+ caches.resourceCache.decrementRefcountLocked(bitmap);
+ caches.resourceCache.destructorLocked(bitmap);
}
- mOwnedBitmapResources.clear();
for (size_t i = 0; i < mFilterResources.size(); i++) {
- caches.resourceCache.decrementRefcount(mFilterResources.itemAt(i));
+ caches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
}
- mFilterResources.clear();
for (size_t i = 0; i < mShaders.size(); i++) {
- caches.resourceCache.decrementRefcount(mShaders.itemAt(i));
- caches.resourceCache.destructor(mShaders.itemAt(i));
+ caches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
+ caches.resourceCache.destructorLocked(mShaders.itemAt(i));
+ }
+
+ for (size_t i = 0; i < mSourcePaths.size(); i++) {
+ caches.resourceCache.decrementRefcountLocked(mSourcePaths.itemAt(i));
+ }
+
+ for (size_t i = 0; i < mLayers.size(); i++) {
+ caches.resourceCache.decrementRefcountLocked(mLayers.itemAt(i));
}
- mShaders.clear();
+
+ caches.resourceCache.unlock();
for (size_t i = 0; i < mPaints.size(); i++) {
delete mPaints.itemAt(i);
}
- mPaints.clear();
for (size_t i = 0; i < mPaths.size(); i++) {
SkPath* path = mPaths.itemAt(i);
caches.pathCache.remove(path);
delete path;
}
- mPaths.clear();
-
- for (size_t i = 0; i < mSourcePaths.size(); i++) {
- caches.resourceCache.decrementRefcount(mSourcePaths.itemAt(i));
- }
- mSourcePaths.clear();
for (size_t i = 0; i < mMatrices.size(); i++) {
delete mMatrices.itemAt(i);
}
+
+ mBitmapResources.clear();
+ mOwnedBitmapResources.clear();
+ mFilterResources.clear();
+ mShaders.clear();
+ mSourcePaths.clear();
+ mPaints.clear();
+ mPaths.clear();
mMatrices.clear();
+ mLayers.clear();
}
-void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) {
- const SkWriter32& writer = recorder.writeStream();
+void DisplayList::reset() {
+ clearResources();
init();
+}
- if (writer.size() == 0) {
- return;
- }
+void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) {
if (reusing) {
// re-using display list - clear out previous allocations
clearResources();
}
- initProperties();
+
+ init();
+
+ const SkWriter32& writer = recorder.writeStream();
+ if (writer.size() == 0) {
+ return;
+ }
mSize = writer.size();
void* buffer = sk_malloc_throw(mSize);
writer.flatten(buffer);
mReader.setMemory(buffer, mSize);
+ mFunctorCount = recorder.getFunctorCount();
+
Caches& caches = Caches::getInstance();
+ caches.registerFunctors(mFunctorCount);
+ caches.resourceCache.lock();
const Vector<SkBitmap*>& bitmapResources = recorder.getBitmapResources();
for (size_t i = 0; i < bitmapResources.size(); i++) {
SkBitmap* resource = bitmapResources.itemAt(i);
mBitmapResources.add(resource);
- caches.resourceCache.incrementRefcount(resource);
+ caches.resourceCache.incrementRefcountLocked(resource);
}
const Vector<SkBitmap*> &ownedBitmapResources = recorder.getOwnedBitmapResources();
for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
SkBitmap* resource = ownedBitmapResources.itemAt(i);
mOwnedBitmapResources.add(resource);
- caches.resourceCache.incrementRefcount(resource);
+ caches.resourceCache.incrementRefcountLocked(resource);
}
const Vector<SkiaColorFilter*>& filterResources = recorder.getFilterResources();
for (size_t i = 0; i < filterResources.size(); i++) {
SkiaColorFilter* resource = filterResources.itemAt(i);
mFilterResources.add(resource);
- caches.resourceCache.incrementRefcount(resource);
+ caches.resourceCache.incrementRefcountLocked(resource);
}
const Vector<SkiaShader*>& shaders = recorder.getShaders();
for (size_t i = 0; i < shaders.size(); i++) {
SkiaShader* resource = shaders.itemAt(i);
mShaders.add(resource);
- caches.resourceCache.incrementRefcount(resource);
+ caches.resourceCache.incrementRefcountLocked(resource);
}
+ const SortedVector<SkPath*>& sourcePaths = recorder.getSourcePaths();
+ for (size_t i = 0; i < sourcePaths.size(); i++) {
+ mSourcePaths.add(sourcePaths.itemAt(i));
+ caches.resourceCache.incrementRefcountLocked(sourcePaths.itemAt(i));
+ }
+
+ const Vector<Layer*>& layers = recorder.getLayers();
+ for (size_t i = 0; i < layers.size(); i++) {
+ mLayers.add(layers.itemAt(i));
+ caches.resourceCache.incrementRefcountLocked(layers.itemAt(i));
+ }
+
+ caches.resourceCache.unlock();
+
const Vector<SkPaint*>& paints = recorder.getPaints();
for (size_t i = 0; i < paints.size(); i++) {
mPaints.add(paints.itemAt(i));
@@ -262,12 +268,6 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde
mPaths.add(paths.itemAt(i));
}
- const SortedVector<SkPath*>& sourcePaths = recorder.getSourcePaths();
- for (size_t i = 0; i < sourcePaths.size(); i++) {
- mSourcePaths.add(sourcePaths.itemAt(i));
- caches.resourceCache.incrementRefcount(sourcePaths.itemAt(i));
- }
-
const Vector<SkMatrix*>& matrices = recorder.getMatrices();
for (size_t i = 0; i < matrices.size(); i++) {
mMatrices.add(matrices.itemAt(i));
@@ -277,6 +277,33 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde
void DisplayList::init() {
mSize = 0;
mIsRenderable = true;
+ mFunctorCount = 0;
+ mLeft = 0;
+ mTop = 0;
+ mRight = 0;
+ mBottom = 0;
+ mClipChildren = true;
+ mAlpha = 1;
+ mMultipliedAlpha = 255;
+ mHasOverlappingRendering = true;
+ mTranslationX = 0;
+ mTranslationY = 0;
+ mRotation = 0;
+ mRotationX = 0;
+ mRotationY= 0;
+ mScaleX = 1;
+ mScaleY = 1;
+ mPivotX = 0;
+ mPivotY = 0;
+ mCameraDistance = 0;
+ mMatrixDirty = false;
+ mMatrixFlags = 0;
+ mPrevWidth = -1;
+ mPrevHeight = -1;
+ mWidth = 0;
+ mHeight = 0;
+ mPivotExplicitlySet = false;
+ mCaching = false;
}
size_t DisplayList::getSize() {
@@ -486,7 +513,8 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float top = getFloat();
float right = getFloat();
float bottom = getFloat();
- SkPaint* paint = getPaint(renderer);
+ int alpha = getInt();
+ SkXfermode::Mode mode = (SkXfermode::Mode) getInt();
ALOGD("%s%s %.2f, %.2f, %.2f, %.2f", (char*) indent, OP_NAMES[op],
left, top, right, bottom);
}
@@ -571,17 +599,6 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
}
break;
- case DrawText: {
- getText(&text);
- int32_t count = getInt();
- float x = getFloat();
- float y = getFloat();
- SkPaint* paint = getPaint(renderer);
- float length = getFloat();
- ALOGD("%s%s %s, %d, %d, %.2f, %.2f, %p, %.2f", (char*) indent, OP_NAMES[op],
- text.text(), text.length(), count, x, y, paint, length);
- }
- break;
case DrawTextOnPath: {
getText(&text);
int32_t count = getInt();
@@ -602,6 +619,20 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
ALOGD("%s%s %s, %d, %d, %p", (char*) indent, OP_NAMES[op],
text.text(), text.length(), count, paint);
}
+ break;
+ case DrawText: {
+ getText(&text);
+ int32_t count = getInt();
+ float x = getFloat();
+ float y = getFloat();
+ int32_t positionsCount = 0;
+ float* positions = getFloats(positionsCount);
+ SkPaint* paint = getPaint(renderer);
+ float length = getFloat();
+ ALOGD("%s%s %s, %d, %d, %p", (char*) indent, OP_NAMES[op],
+ text.text(), text.length(), count, paint);
+ }
+ break;
case ResetShader: {
ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
}
@@ -851,11 +882,13 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
#endif
renderer.startMark(mName.string());
+
int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
DISPLAY_LIST_LOGD("%s%s %d %d", indent, "Save",
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
setViewProperties(renderer, level);
- if (renderer.quickReject(0, 0, mWidth, mHeight)) {
+
+ if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, "RestoreToCount", restoreTo);
renderer.restoreToCount(restoreTo);
renderer.endMark();
@@ -864,6 +897,7 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
int saveCount = renderer.getSaveCount() - 1;
+
while (!mReader.eof()) {
int op = mReader.readInt();
if (op & OP_MAY_BE_SKIPPED_MASK) {
@@ -879,6 +913,10 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
}
logBuffer.writeCommand(level, op);
+#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
+ Caches::getInstance().eventMark(strlen(OP_NAMES[op]), OP_NAMES[op]);
+#endif
+
switch (op) {
case DrawGLFunction: {
Functor *functor = (Functor *) getInt();
@@ -993,29 +1031,39 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
}
break;
case DrawLayer: {
+ int oldAlpha = -1;
Layer* layer = (Layer*) getInt();
float x = getFloat();
float y = getFloat();
SkPaint* paint = getPaint(renderer);
- if (mCaching) {
- paint->setAlpha(mMultipliedAlpha);
+ if (mCaching && mMultipliedAlpha < 255) {
+ oldAlpha = layer->getAlpha();
+ layer->setAlpha(mMultipliedAlpha);
}
DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
layer, x, y, paint);
drawGlStatus |= renderer.drawLayer(layer, x, y, paint);
+ if (oldAlpha >= 0) {
+ layer->setAlpha(oldAlpha);
+ }
}
break;
case DrawBitmap: {
+ int oldAlpha = -1;
SkBitmap* bitmap = getBitmap();
float x = getFloat();
float y = getFloat();
SkPaint* paint = getPaint(renderer);
- if (mCaching) {
+ if (mCaching && mMultipliedAlpha < 255) {
+ oldAlpha = paint->getAlpha();
paint->setAlpha(mMultipliedAlpha);
}
DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
bitmap, x, y, paint);
drawGlStatus |= renderer.drawBitmap(bitmap, x, y, paint);
+ if (oldAlpha >= 0) {
+ paint->setAlpha(oldAlpha);
+ }
}
break;
case DrawBitmapMatrix: {
@@ -1089,11 +1137,14 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
float top = getFloat();
float right = getFloat();
float bottom = getFloat();
- SkPaint* paint = getPaint(renderer);
+
+ int alpha = getInt();
+ SkXfermode::Mode mode = (SkXfermode::Mode) getInt();
DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
drawGlStatus |= renderer.drawPatch(bitmap, xDivs, yDivs, colors,
- xDivsCount, yDivsCount, numColors, left, top, right, bottom, paint);
+ xDivsCount, yDivsCount, numColors, left, top, right, bottom,
+ alpha, mode);
}
break;
case DrawColor: {
@@ -1185,19 +1236,6 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
drawGlStatus |= renderer.drawPoints(points, count, paint);
}
break;
- case DrawText: {
- getText(&text);
- int32_t count = getInt();
- float x = getFloat();
- float y = getFloat();
- SkPaint* paint = getPaint(renderer);
- float length = getFloat();
- DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p, %.2f", (char*) indent,
- OP_NAMES[op], text.text(), text.length(), count, x, y, paint, length);
- drawGlStatus |= renderer.drawText(text.text(), text.length(), count, x, y,
- paint, length);
- }
- break;
case DrawTextOnPath: {
getText(&text);
int32_t count = getInt();
@@ -1223,6 +1261,21 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
positions, paint);
}
break;
+ case DrawText: {
+ getText(&text);
+ int32_t count = getInt();
+ float x = getFloat();
+ float y = getFloat();
+ int32_t positionsCount = 0;
+ float* positions = getFloats(positionsCount);
+ SkPaint* paint = getPaint(renderer);
+ float length = getFloat();
+ DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p, %.2f", (char*) indent,
+ OP_NAMES[op], text.text(), text.length(), count, x, y, paint, length);
+ drawGlStatus |= renderer.drawText(text.text(), text.length(), count,
+ x, y, positions, paint, length);
+ }
+ break;
case ResetShader: {
DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
renderer.resetShader();
@@ -1293,8 +1346,10 @@ status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flag
// Base structure
///////////////////////////////////////////////////////////////////////////////
-DisplayListRenderer::DisplayListRenderer() : mWriter(MIN_WRITER_SIZE),
- mTranslateX(0.0f), mTranslateY(0.0f), mHasTranslate(false), mHasDrawOps(false) {
+DisplayListRenderer::DisplayListRenderer():
+ mCaches(Caches::getInstance()), mWriter(MIN_WRITER_SIZE),
+ mTranslateX(0.0f), mTranslateY(0.0f), mHasTranslate(false),
+ mHasDrawOps(false), mFunctorCount(0) {
}
DisplayListRenderer::~DisplayListRenderer() {
@@ -1304,34 +1359,42 @@ DisplayListRenderer::~DisplayListRenderer() {
void DisplayListRenderer::reset() {
mWriter.reset();
- Caches& caches = Caches::getInstance();
+ mCaches.resourceCache.lock();
+
for (size_t i = 0; i < mBitmapResources.size(); i++) {
- caches.resourceCache.decrementRefcount(mBitmapResources.itemAt(i));
+ mCaches.resourceCache.decrementRefcountLocked(mBitmapResources.itemAt(i));
}
- mBitmapResources.clear();
for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) {
- SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i);
- caches.resourceCache.decrementRefcount(bitmap);
+ mCaches.resourceCache.decrementRefcountLocked(mOwnedBitmapResources.itemAt(i));
}
- mOwnedBitmapResources.clear();
for (size_t i = 0; i < mFilterResources.size(); i++) {
- caches.resourceCache.decrementRefcount(mFilterResources.itemAt(i));
+ mCaches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
}
- mFilterResources.clear();
for (size_t i = 0; i < mShaders.size(); i++) {
- caches.resourceCache.decrementRefcount(mShaders.itemAt(i));
+ mCaches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
}
- mShaders.clear();
- mShaderMap.clear();
for (size_t i = 0; i < mSourcePaths.size(); i++) {
- caches.resourceCache.decrementRefcount(mSourcePaths.itemAt(i));
+ mCaches.resourceCache.decrementRefcountLocked(mSourcePaths.itemAt(i));
+ }
+
+ for (size_t i = 0; i < mLayers.size(); i++) {
+ mCaches.resourceCache.decrementRefcountLocked(mLayers.itemAt(i));
}
+
+ mCaches.resourceCache.unlock();
+
+ mBitmapResources.clear();
+ mOwnedBitmapResources.clear();
+ mFilterResources.clear();
mSourcePaths.clear();
+ mShaders.clear();
+ mShaderMap.clear();
+
mPaints.clear();
mPaintMap.clear();
@@ -1340,7 +1403,10 @@ void DisplayListRenderer::reset() {
mMatrices.clear();
+ mLayers.clear();
+
mHasDrawOps = false;
+ mFunctorCount = 0;
}
///////////////////////////////////////////////////////////////////////////////
@@ -1373,8 +1439,12 @@ int DisplayListRenderer::prepareDirty(float left, float top,
mSnapshot = new Snapshot(mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
mSaveCount = 1;
+
mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight);
+ mDirtyClip = opaque;
+
mRestoreSaveCount = -1;
+
return DrawGlInfo::kStatusDone; // No invalidate needed at record-time
}
@@ -1393,6 +1463,7 @@ status_t DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty)
// Ignore dirty during recording, it matters only when we replay
addOp(DisplayList::DrawGLFunction);
addInt((int) functor);
+ mFunctorCount++;
return DrawGlInfo::kStatusDone; // No invalidate needed at record-time
}
@@ -1496,14 +1567,15 @@ status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList,
status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) {
addOp(DisplayList::DrawLayer);
- addInt((int) layer);
+ addLayer(layer);
addPoint(x, y);
addPaint(paint);
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
- const bool reject = quickReject(left, top, left + bitmap->width(), top + bitmap->height());
+ const bool reject = quickRejectNoScissor(left, top,
+ left + bitmap->width(), top + bitmap->height());
uint32_t* location = addOp(DisplayList::DrawBitmap, reject);
addBitmap(bitmap);
addPoint(left, top);
@@ -1517,7 +1589,7 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkP
const mat4 transform(*matrix);
transform.mapRect(r);
- const bool reject = quickReject(r.left, r.top, r.right, r.bottom);
+ const bool reject = quickRejectNoScissor(r.left, r.top, r.right, r.bottom);
uint32_t* location = addOp(DisplayList::DrawBitmapMatrix, reject);
addBitmap(bitmap);
addMatrix(matrix);
@@ -1529,7 +1601,7 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkP
status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, SkPaint* paint) {
- const bool reject = quickReject(dstLeft, dstTop, dstRight, dstBottom);
+ const bool reject = quickRejectNoScissor(dstLeft, dstTop, dstRight, dstBottom);
uint32_t* location = addOp(DisplayList::DrawBitmapRect, reject);
addBitmap(bitmap);
addBounds(srcLeft, srcTop, srcRight, srcBottom);
@@ -1541,7 +1613,8 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float
status_t DisplayListRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top,
SkPaint* paint) {
- const bool reject = quickReject(left, top, left + bitmap->width(), top + bitmap->height());
+ const bool reject = quickRejectNoScissor(left, top,
+ left + bitmap->width(), top + bitmap->height());
uint32_t* location = addOp(DisplayList::DrawBitmapData, reject);
addBitmapData(bitmap);
addPoint(left, top);
@@ -1570,14 +1643,19 @@ status_t DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, in
status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs,
const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height,
int8_t numColors, float left, float top, float right, float bottom, SkPaint* paint) {
- const bool reject = quickReject(left, top, right, bottom);
+ int alpha;
+ SkXfermode::Mode mode;
+ OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+
+ const bool reject = quickRejectNoScissor(left, top, right, bottom);
uint32_t* location = addOp(DisplayList::DrawPatch, reject);
addBitmap(bitmap);
addInts(xDivs, width);
addInts(yDivs, height);
addUInts(colors, numColors);
addBounds(left, top, right, bottom);
- addPaint(paint);
+ addInt(alpha);
+ addInt(mode);
addSkip(location);
return DrawGlInfo::kStatusDone;
}
@@ -1592,7 +1670,7 @@ status_t DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) {
status_t DisplayListRenderer::drawRect(float left, float top, float right, float bottom,
SkPaint* paint) {
const bool reject = paint->getStyle() == SkPaint::kFill_Style &&
- quickReject(left, top, right, bottom);
+ quickRejectNoScissor(left, top, right, bottom);
uint32_t* location = addOp(DisplayList::DrawRect, reject);
addBounds(left, top, right, bottom);
addPaint(paint);
@@ -1603,7 +1681,7 @@ status_t DisplayListRenderer::drawRect(float left, float top, float right, float
status_t DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, SkPaint* paint) {
const bool reject = paint->getStyle() == SkPaint::kFill_Style &&
- quickReject(left, top, right, bottom);
+ quickRejectNoScissor(left, top, right, bottom);
uint32_t* location = addOp(DisplayList::DrawRoundRect, reject);
addBounds(left, top, right, bottom);
addPoint(rx, ry);
@@ -1646,7 +1724,7 @@ status_t DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) {
left -= offset;
top -= offset;
- const bool reject = quickReject(left, top, left + width, top + height);
+ const bool reject = quickRejectNoScissor(left, top, left + width, top + height);
uint32_t* location = addOp(DisplayList::DrawPath, reject);
addPath(path);
addPaint(paint);
@@ -1668,8 +1746,38 @@ status_t DisplayListRenderer::drawPoints(float* points, int count, SkPaint* pain
return DrawGlInfo::kStatusDone;
}
+status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
+ SkPath* path, float hOffset, float vOffset, SkPaint* paint) {
+ if (!text || count <= 0) return DrawGlInfo::kStatusDone;
+ addOp(DisplayList::DrawTextOnPath);
+ addText(text, bytesCount);
+ addInt(count);
+ addPath(path);
+ addFloat(hOffset);
+ addFloat(vOffset);
+ paint->setAntiAlias(true);
+ SkPaint* addedPaint = addPaint(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
+ fontRenderer.precache(addedPaint, text, count);
+ return DrawGlInfo::kStatusDone;
+}
+
+status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count,
+ const float* positions, SkPaint* paint) {
+ if (!text || count <= 0) return DrawGlInfo::kStatusDone;
+ addOp(DisplayList::DrawPosText);
+ addText(text, bytesCount);
+ addInt(count);
+ addFloats(positions, count * 2);
+ paint->setAntiAlias(true);
+ SkPaint* addedPaint = addPaint(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
+ fontRenderer.precache(addedPaint, text, count);
+ return DrawGlInfo::kStatusDone;
+}
+
status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, SkPaint* paint, float length) {
+ float x, float y, const float* positions, SkPaint* paint, float length) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
// TODO: We should probably make a copy of the paint instead of modifying
@@ -1686,45 +1794,25 @@ status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int cou
if (CC_LIKELY(paint->getTextAlign() == SkPaint::kLeft_Align)) {
SkPaint::FontMetrics metrics;
paint->getFontMetrics(&metrics, 0.0f);
- reject = quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom);
+ reject = quickRejectNoScissor(x, y + metrics.fTop, x + length, y + metrics.fBottom);
}
uint32_t* location = addOp(DisplayList::DrawText, reject);
addText(text, bytesCount);
addInt(count);
- addPoint(x, y);
- addPaint(paint);
+ addFloat(x);
+ addFloat(y);
+ addFloats(positions, count * 2);
+ SkPaint* addedPaint = addPaint(paint);
+ if (!reject) {
+ FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
+ fontRenderer.precache(addedPaint, text, count);
+ }
addFloat(length);
addSkip(location);
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
- SkPath* path, float hOffset, float vOffset, SkPaint* paint) {
- if (!text || count <= 0) return DrawGlInfo::kStatusDone;
- addOp(DisplayList::DrawTextOnPath);
- addText(text, bytesCount);
- addInt(count);
- addPath(path);
- addFloat(hOffset);
- addFloat(vOffset);
- paint->setAntiAlias(true);
- addPaint(paint);
- return DrawGlInfo::kStatusDone;
-}
-
-status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count,
- const float* positions, SkPaint* paint) {
- if (!text || count <= 0) return DrawGlInfo::kStatusDone;
- addOp(DisplayList::DrawPosText);
- addText(text, bytesCount);
- addInt(count);
- addFloats(positions, count * 2);
- paint->setAntiAlias(true);
- addPaint(paint);
- return DrawGlInfo::kStatusDone;
-}
-
void DisplayListRenderer::resetShader() {
addOp(DisplayList::ResetShader);
}
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 6b4c6b20d863..2610055ecd06 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -103,9 +103,9 @@ public:
DrawPath,
DrawLines,
DrawPoints,
- DrawText,
DrawTextOnPath,
DrawPosText,
+ DrawText,
ResetShader,
SetupShader,
ResetColorFilter,
@@ -137,6 +137,8 @@ public:
void output(OpenGLRenderer& renderer, uint32_t level = 0);
+ ANDROID_API void reset();
+
void setRenderable(bool renderable) {
mIsRenderable = renderable;
}
@@ -399,7 +401,6 @@ public:
private:
void init();
- void initProperties();
void clearResources();
@@ -496,12 +497,14 @@ private:
SortedVector<SkPath*> mSourcePaths;
Vector<SkMatrix*> mMatrices;
Vector<SkiaShader*> mShaders;
+ Vector<Layer*> mLayers;
mutable SkFlattenableReadBuffer mReader;
size_t mSize;
bool mIsRenderable;
+ uint32_t mFunctorCount;
String8 mName;
@@ -599,12 +602,12 @@ public:
virtual status_t drawPath(SkPath* path, SkPaint* paint);
virtual status_t drawLines(float* points, int count, SkPaint* paint);
virtual status_t drawPoints(float* points, int count, SkPaint* paint);
- virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- SkPaint* paint, float length = -1.0f);
virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
float hOffset, float vOffset, SkPaint* paint);
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
+ virtual status_t drawText(const char* text, int bytesCount, int count,
+ float x, float y, const float* positions, SkPaint* paint, float length);
virtual void resetShader();
virtual void setupShader(SkiaShader* shader);
@@ -652,10 +655,18 @@ public:
return mSourcePaths;
}
+ const Vector<Layer*>& getLayers() const {
+ return mLayers;
+ }
+
const Vector<SkMatrix*>& getMatrices() const {
return mMatrices;
}
+ uint32_t getFunctorCount() const {
+ return mFunctorCount;
+ }
+
private:
void insertRestoreToCount() {
if (mRestoreSaveCount >= 0) {
@@ -763,17 +774,17 @@ private:
mPaths.add(pathCopy);
}
if (mSourcePaths.indexOf(path) < 0) {
- Caches::getInstance().resourceCache.incrementRefcount(path);
+ mCaches.resourceCache.incrementRefcount(path);
mSourcePaths.add(path);
}
addInt((int) pathCopy);
}
- inline void addPaint(SkPaint* paint) {
+ inline SkPaint* addPaint(SkPaint* paint) {
if (!paint) {
addInt((int) NULL);
- return;
+ return paint;
}
SkPaint* paintCopy = mPaintMap.valueFor(paint);
@@ -785,6 +796,8 @@ private:
}
addInt((int) paintCopy);
+
+ return paintCopy;
}
inline void addDisplayList(DisplayList* displayList) {
@@ -802,6 +815,12 @@ private:
mMatrices.add(copy);
}
+ inline void addLayer(Layer* layer) {
+ addInt((int) layer);
+ mLayers.add(layer);
+ mCaches.resourceCache.incrementRefcount(layer);
+ }
+
inline void addBitmap(SkBitmap* bitmap) {
// Note that this assumes the bitmap is immutable. There are cases this won't handle
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
@@ -809,13 +828,13 @@ private:
// which doesn't seem worth the extra cycles for this unlikely case.
addInt((int) bitmap);
mBitmapResources.add(bitmap);
- Caches::getInstance().resourceCache.incrementRefcount(bitmap);
+ mCaches.resourceCache.incrementRefcount(bitmap);
}
void addBitmapData(SkBitmap* bitmap) {
addInt((int) bitmap);
mOwnedBitmapResources.add(bitmap);
- Caches::getInstance().resourceCache.incrementRefcount(bitmap);
+ mCaches.resourceCache.incrementRefcount(bitmap);
}
inline void addShader(SkiaShader* shader) {
@@ -831,7 +850,7 @@ private:
// replaceValueFor() performs an add if the entry doesn't exist
mShaderMap.replaceValueFor(shader, shaderCopy);
mShaders.add(shaderCopy);
- Caches::getInstance().resourceCache.incrementRefcount(shaderCopy);
+ mCaches.resourceCache.incrementRefcount(shaderCopy);
}
addInt((int) shaderCopy);
@@ -840,7 +859,7 @@ private:
inline void addColorFilter(SkiaColorFilter* colorFilter) {
addInt((int) colorFilter);
mFilterResources.add(colorFilter);
- Caches::getInstance().resourceCache.incrementRefcount(colorFilter);
+ mCaches.resourceCache.incrementRefcount(colorFilter);
}
Vector<SkBitmap*> mBitmapResources;
@@ -860,17 +879,22 @@ private:
Vector<SkMatrix*> mMatrices;
- SkWriter32 mWriter;
+ Vector<Layer*> mLayers;
+
uint32_t mBufferSize;
int mRestoreSaveCount;
+ Caches& mCaches;
+ SkWriter32 mWriter;
+
float mTranslateX;
float mTranslateY;
bool mHasTranslate;
-
bool mHasDrawOps;
+ uint32_t mFunctorCount;
+
friend class DisplayList;
}; // class DisplayListRenderer
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
new file mode 100755
index 000000000000..e80b325e6e32
--- /dev/null
+++ b/libs/hwui/Dither.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include "Caches.h"
+#include "Dither.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Must be a power of two
+#define DITHER_KERNEL_SIZE 4
+
+///////////////////////////////////////////////////////////////////////////////
+// Lifecycle
+///////////////////////////////////////////////////////////////////////////////
+
+void Dither::bindDitherTexture() {
+ if (!mInitialized) {
+ const uint8_t pattern[] = {
+ 0, 8, 2, 10,
+ 12, 4, 14, 6,
+ 3, 11, 1, 9,
+ 15, 7, 13, 5
+ };
+
+ glGenTextures(1, &mDitherTexture);
+ glBindTexture(GL_TEXTURE_2D, mDitherTexture);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
+
+ mInitialized = true;
+ } else {
+ glBindTexture(GL_TEXTURE_2D, mDitherTexture);
+ }
+}
+
+void Dither::clear() {
+ if (mInitialized) {
+ glDeleteTextures(1, &mDitherTexture);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Program management
+///////////////////////////////////////////////////////////////////////////////
+
+void Dither::setupProgram(Program* program, GLuint* textureUnit) {
+ GLuint textureSlot = (*textureUnit)++;
+ Caches::getInstance().activeTexture(textureSlot);
+
+ bindDitherTexture();
+
+ float ditherSize = 1.0f / DITHER_KERNEL_SIZE;
+ glUniform1i(program->getUniform("ditherSampler"), textureSlot);
+ glUniform1f(program->getUniform("ditherSize"), ditherSize);
+ glUniform1f(program->getUniform("ditherSizeSquared"), ditherSize * ditherSize);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
new file mode 100755
index 000000000000..34cf9bf6a4b7
--- /dev/null
+++ b/libs/hwui/Dither.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 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_DITHER_H
+#define ANDROID_HWUI_DITHER_H
+
+#include <GLES2/gl2.h>
+
+#include "Program.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Handles dithering for programs.
+ */
+class Dither {
+public:
+ Dither(): mInitialized(false), mDitherTexture(0) { }
+
+ void clear();
+ void setupProgram(Program* program, GLuint* textureUnit);
+
+private:
+ void bindDitherTexture();
+
+ bool mInitialized;
+ GLuint mDitherTexture;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DITHER_H
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 6b174d6877a6..bdaa3cc06aa8 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -39,9 +39,6 @@ namespace uirenderer {
#define EXT_LOGD(...)
#endif
-// Vendor strings
-#define VENDOR_IMG "Imagination Technologies"
-
///////////////////////////////////////////////////////////////////////////////
// Classes
///////////////////////////////////////////////////////////////////////////////
@@ -68,23 +65,21 @@ public:
mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer");
mHasDebugMarker = hasExtension("GL_EXT_debug_marker");
mHasDebugLabel = hasExtension("GL_EXT_debug_label");
+ mHasTiledRendering = hasExtension("GL_QCOM_tiled_rendering");
- const char* vendor = (const char*) glGetString(GL_VENDOR);
- EXT_LOGD("Vendor: %s", vendor);
- mNeedsHighpTexCoords = strcmp(vendor, VENDOR_IMG) == 0;
+ mExtensions = strdup(buffer);
+ }
- // We don't need to copy the string, the OpenGL ES spec
- // guarantees the result of glGetString to point to a
- // static string as long as our OpenGL context is valid
- mExtensions = buffer;
+ ~Extensions() {
+ free(mExtensions);
}
inline bool hasNPot() const { return mHasNPot; }
inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
- inline bool needsHighpTexCoords() const { return mNeedsHighpTexCoords; }
inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; }
inline bool hasDebugMarker() const { return mHasDebugMarker; }
inline bool hasDebugLabel() const { return mHasDebugLabel; }
+ inline bool hasTiledRendering() const { return mHasTiledRendering; }
bool hasExtension(const char* extension) const {
const String8 s(extension);
@@ -98,14 +93,14 @@ public:
private:
SortedVector<String8> mExtensionList;
- const char* mExtensions;
+ char* mExtensions;
bool mHasNPot;
- bool mNeedsHighpTexCoords;
bool mHasFramebufferFetch;
bool mHasDiscardFramebuffer;
bool mHasDebugMarker;
bool mHasDebugLabel;
+ bool mHasTiledRendering;
}; // class Extensions
}; // namespace uirenderer
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index b51b1e11b929..4e97c8862d7f 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -25,436 +25,12 @@
#include "Caches.h"
#include "Debug.h"
#include "FontRenderer.h"
-#include "Caches.h"
+#include "Rect.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define DEFAULT_TEXT_CACHE_WIDTH 1024
-#define DEFAULT_TEXT_CACHE_HEIGHT 256
-#define MAX_TEXT_CACHE_WIDTH 2048
-#define TEXTURE_BORDER_SIZE 2
-
-#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheTextureLine
-///////////////////////////////////////////////////////////////////////////////
-
-bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
- if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
- return false;
- }
-
- if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE < mMaxWidth) {
- *retOriginX = mCurrentCol + 1;
- *retOriginY = mCurrentRow + 1;
- mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE;
- mDirty = true;
- return true;
- }
-
- return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX,
- SkPaint::Style style, uint32_t strokeWidth) :
- mState(state), mFontId(fontId), mFontSize(fontSize),
- mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
- mStyle(style), mStrokeWidth(mStrokeWidth) {
-}
-
-
-Font::~Font() {
- for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
- if (mState->mActiveFonts[ct] == this) {
- mState->mActiveFonts.removeAt(ct);
- break;
- }
- }
-
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- delete mCachedGlyphs.valueAt(i);
- }
-}
-
-void Font::invalidateTextureCache(CacheTextureLine *cacheLine) {
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
- if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) {
- cachedGlyph->mIsValid = false;
- }
- }
-}
-
-void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
- int width = (int) glyph->mBitmapWidth;
- int height = (int) glyph->mBitmapHeight;
-
- if (bounds->bottom > nPenY) {
- bounds->bottom = nPenY;
- }
- if (bounds->left > nPenX) {
- bounds->left = nPenX;
- }
- if (bounds->right < nPenX + width) {
- bounds->right = nPenX + width;
- }
- if (bounds->top < nPenY + height) {
- bounds->top = nPenY + height;
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
-
- float u1 = glyph->mBitmapMinU;
- float u2 = glyph->mBitmapMaxU;
- float v1 = glyph->mBitmapMinV;
- float v2 = glyph->mBitmapMaxV;
-
- int width = (int) glyph->mBitmapWidth;
- int height = (int) glyph->mBitmapHeight;
-
- mState->appendMeshQuad(nPenX, nPenY, u1, v2,
- nPenX + width, nPenY, u2, v2,
- nPenX + width, nPenY - height, u2, v1,
- nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
-}
-
-void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
- uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
- uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
-
- CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
- uint32_t cacheWidth = cacheTexture->mWidth;
- const uint8_t* cacheBuffer = cacheTexture->mTexture;
-
- uint32_t cacheX = 0, cacheY = 0;
- int32_t bX = 0, bY = 0;
- for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
- for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
-#if DEBUG_FONT_RENDERER
- if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
- ALOGE("Skipping invalid index");
- continue;
- }
-#endif
- uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
- bitmap[bY * bitmapW + bX] = tempCol;
- }
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
- const float halfWidth = glyph->mBitmapWidth * 0.5f;
- const float height = glyph->mBitmapHeight;
-
- vOffset += glyph->mBitmapTop + height;
-
- SkPoint destination[4];
- measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
-
- // Move along the tangent and offset by the normal
- destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
- -tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
- tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[2].set(destination[1].fX + tangent->fY * height,
- destination[1].fY - tangent->fX * height);
- destination[3].set(destination[0].fX + tangent->fY * height,
- destination[0].fY - tangent->fX * height);
-
- const float u1 = glyph->mBitmapMinU;
- const float u2 = glyph->mBitmapMaxU;
- const float v1 = glyph->mBitmapMinV;
- const float v2 = glyph->mBitmapMaxV;
-
- mState->appendRotatedMeshQuad(
- position->fX + destination[0].fX,
- position->fY + destination[0].fY, u1, v2,
- position->fX + destination[1].fX,
- position->fY + destination[1].fY, u2, v2,
- position->fX + destination[2].fX,
- position->fY + destination[2].fY, u2, v1,
- position->fX + destination[3].fX,
- position->fY + destination[3].fY, u1, v1,
- glyph->mCachedTextureLine->mCacheTexture);
-}
-
-CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
- CachedGlyphInfo* cachedGlyph = NULL;
- ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
- if (index >= 0) {
- cachedGlyph = mCachedGlyphs.valueAt(index);
- } else {
- cachedGlyph = cacheGlyph(paint, textUnit);
- }
-
- // Is the glyph still in texture cache?
- if (!cachedGlyph->mIsValid) {
- const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
- updateGlyphCache(paint, skiaGlyph, cachedGlyph);
- }
-
- return cachedGlyph;
-}
-
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
- if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
- render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
- bitmapW, bitmapH, NULL, NULL);
- } else {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
- 0, 0, NULL, NULL);
- }
-}
-
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, const float* positions) {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
- 0, 0, NULL, positions);
-}
-
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
- return;
- }
-
- text += start;
-
- int glyphsCount = 0;
- SkFixed prevRsbDelta = 0;
-
- float penX = 0.0f;
-
- SkPoint position;
- SkVector tangent;
-
- SkPathMeasure measure(*path, false);
- float pathLength = SkScalarToFloat(measure.getLength());
-
- if (paint->getTextAlign() != SkPaint::kLeft_Align) {
- float textWidth = SkScalarToFloat(paint->measureText(text, len));
- float pathOffset = pathLength;
- if (paint->getTextAlign() == SkPaint::kCenter_Align) {
- textWidth *= 0.5f;
- pathOffset *= 0.5f;
- }
- penX += pathOffset - textWidth;
- }
-
- while (glyphsCount < numGlyphs && penX < pathLength) {
- glyph_t glyph = GET_GLYPH(text);
-
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
- prevRsbDelta = cachedGlyph->mRsbDelta;
-
- if (cachedGlyph->mIsValid) {
- drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
- }
-
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
- glyphsCount++;
- }
-}
-
-void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds) {
- if (bounds == NULL) {
- ALOGE("No return rectangle provided to measure text");
- return;
- }
- bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
-}
-
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
- return;
- }
-
- static RenderGlyph gRenderGlyph[] = {
- &android::uirenderer::Font::drawCachedGlyph,
- &android::uirenderer::Font::drawCachedGlyphBitmap,
- &android::uirenderer::Font::measureCachedGlyph
- };
- RenderGlyph render = gRenderGlyph[mode];
-
- text += start;
- int glyphsCount = 0;
-
- if (CC_LIKELY(positions == NULL)) {
- SkFixed prevRsbDelta = 0;
-
- float penX = x + 0.5f;
- int penY = y;
-
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
- prevRsbDelta = cachedGlyph->mRsbDelta;
-
- // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
- if (cachedGlyph->mIsValid) {
- (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
- bitmap, bitmapW, bitmapH, bounds, positions);
- }
-
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
- glyphsCount++;
- }
- } else {
- const SkPaint::Align align = paint->getTextAlign();
-
- // This is for renderPosText()
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-
- // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
- if (cachedGlyph->mIsValid) {
- int penX = x + positions[(glyphsCount << 1)];
- int penY = y + positions[(glyphsCount << 1) + 1];
-
- switch (align) {
- case SkPaint::kRight_Align:
- penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
- penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
- break;
- case SkPaint::kCenter_Align:
- penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
- penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
- default:
- break;
- }
-
- (*this.*render)(cachedGlyph, penX, penY,
- bitmap, bitmapW, bitmapH, bounds, positions);
- }
-
- glyphsCount++;
- }
- }
-}
-
-void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
- glyph->mAdvanceX = skiaGlyph.fAdvanceX;
- glyph->mAdvanceY = skiaGlyph.fAdvanceY;
- glyph->mBitmapLeft = skiaGlyph.fLeft;
- glyph->mBitmapTop = skiaGlyph.fTop;
- glyph->mLsbDelta = skiaGlyph.fLsbDelta;
- glyph->mRsbDelta = skiaGlyph.fRsbDelta;
-
- uint32_t startX = 0;
- uint32_t startY = 0;
-
- // Get the bitmap for the glyph
- paint->findImage(skiaGlyph);
- mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
-
- if (!glyph->mIsValid) {
- return;
- }
-
- uint32_t endX = startX + skiaGlyph.fWidth;
- uint32_t endY = startY + skiaGlyph.fHeight;
-
- glyph->mStartX = startX;
- glyph->mStartY = startY;
- glyph->mBitmapWidth = skiaGlyph.fWidth;
- glyph->mBitmapHeight = skiaGlyph.fHeight;
-
- uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
- uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
-
- glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
- glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
- glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
- glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
-
- mState->mUploadTexture = true;
-}
-
-CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
- CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
- mCachedGlyphs.add(glyph, newGlyph);
-
- const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
- newGlyph->mGlyphIndex = skiaGlyph.fID;
- newGlyph->mIsValid = false;
-
- updateGlyphCache(paint, skiaGlyph, newGlyph);
-
- return newGlyph;
-}
-
-Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX,
- SkPaint::Style style, uint32_t strokeWidth) {
- Vector<Font*> &activeFonts = state->mActiveFonts;
-
- for (uint32_t i = 0; i < activeFonts.size(); i++) {
- Font* font = activeFonts[i];
- if (font->mFontId == fontId && font->mFontSize == fontSize &&
- font->mFlags == flags && font->mItalicStyle == italicStyle &&
- font->mScaleX == scaleX && font->mStyle == style &&
- (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
- return font;
- }
- }
-
- Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
- scaleX, style, strokeWidth);
- activeFonts.push(newFont);
- return newFont;
-}
-
-///////////////////////////////////////////////////////////////////////////////
// FontRenderer
///////////////////////////////////////////////////////////////////////////////
@@ -470,63 +46,65 @@ FontRenderer::FontRenderer() {
mMaxNumberOfQuads = 1024;
mCurrentQuadIndex = 0;
- mTextMeshPtr = NULL;
+ mTextMesh = NULL;
mCurrentCacheTexture = NULL;
mLastCacheTexture = NULL;
- mCacheTextureSmall = NULL;
- mCacheTexture128 = NULL;
- mCacheTexture256 = NULL;
- mCacheTexture512 = NULL;
mLinearFiltering = false;
mIndexBufferID = 0;
- mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
- mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
+ mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
+ mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
+ mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
+ mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Setting text cache width to %s pixels", property);
- }
+ if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
mSmallCacheWidth = atoi(property);
- } else {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
- }
}
- if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Setting text cache width to %s pixels", property);
- }
+ if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
mSmallCacheHeight = atoi(property);
- } else {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
- }
+ }
+
+ if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
+ mLargeCacheWidth = atoi(property);
+ }
+
+ if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
+ mLargeCacheHeight = atoi(property);
+ }
+
+ uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
+ mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
+ mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
+ mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
+ mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
+
+ if (sLogFontRendererCreate) {
+ INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
+ mSmallCacheWidth, mSmallCacheHeight,
+ mLargeCacheWidth, mLargeCacheHeight >> 1,
+ mLargeCacheWidth, mLargeCacheHeight >> 1,
+ mLargeCacheWidth, mLargeCacheHeight);
}
sLogFontRendererCreate = false;
}
FontRenderer::~FontRenderer() {
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- delete mCacheLines[i];
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ delete mCacheTextures[i];
}
- mCacheLines.clear();
+ mCacheTextures.clear();
if (mInitialized) {
// Unbinding the buffer shouldn't be necessary but it crashes with some drivers
Caches::getInstance().unbindIndicesBuffer();
glDeleteBuffers(1, &mIndexBufferID);
- delete[] mTextMeshPtr;
- delete mCacheTextureSmall;
- delete mCacheTexture128;
- delete mCacheTexture256;
- delete mCacheTexture512;
+ delete[] mTextMesh;
}
Vector<Font*> fontsToDereference = mActiveFonts;
@@ -545,80 +123,57 @@ void FontRenderer::flushAllAndInvalidate() {
mActiveFonts[i]->invalidateTextureCache();
}
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- mCacheLines[i]->mCurrentCol = 0;
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ mCacheTextures[i]->init();
}
-}
-void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
- if (cacheTexture && cacheTexture->mTexture) {
- glDeleteTextures(1, &cacheTexture->mTextureId);
- delete[] cacheTexture->mTexture;
- cacheTexture->mTexture = NULL;
- cacheTexture->mTextureId = 0;
+#if DEBUG_FONT_RENDERER
+ uint16_t totalGlyphs = 0;
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ totalGlyphs += mCacheTextures[i]->getGlyphCount();
+ // Erase caches, just as a debugging facility
+ if (mCacheTextures[i]->getTexture()) {
+ memset(mCacheTextures[i]->getTexture(), 0,
+ mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
+ }
}
+ ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
+#endif
}
void FontRenderer::flushLargeCaches() {
- if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
- (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
- (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
- // Typical case; no large glyph caches allocated
- return;
- }
-
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- CacheTextureLine* cacheLine = mCacheLines[i];
- if ((cacheLine->mCacheTexture == mCacheTexture128 ||
- cacheLine->mCacheTexture == mCacheTexture256 ||
- cacheLine->mCacheTexture == mCacheTexture512) &&
- cacheLine->mCacheTexture->mTexture != NULL) {
- cacheLine->mCurrentCol = 0;
- for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
- mActiveFonts[i]->invalidateTextureCache(cacheLine);
+ // Start from 1; don't deallocate smallest/default texture
+ for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
+ CacheTexture* cacheTexture = mCacheTextures[i];
+ if (cacheTexture->getTexture()) {
+ cacheTexture->init();
+ for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
+ mActiveFonts[j]->invalidateTextureCache(cacheTexture);
}
+ cacheTexture->releaseTexture();
}
}
-
- deallocateTextureMemory(mCacheTexture128);
- deallocateTextureMemory(mCacheTexture256);
- deallocateTextureMemory(mCacheTexture512);
}
-void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
- int width = cacheTexture->mWidth;
- int height = cacheTexture->mHeight;
-
- cacheTexture->mTexture = new uint8_t[width * height];
-#if DEBUG_FONT_RENDERER
- memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
-#endif
-
- if (!cacheTexture->mTextureId) {
- glGenTextures(1, &cacheTexture->mTextureId);
+CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
+ uint32_t* startX, uint32_t* startY) {
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
+ return mCacheTextures[i];
+ }
}
-
- Caches::getInstance().activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- // Initialize texture dimensions
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, 0);
-
- const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ // Could not fit glyph into current cache textures
+ return NULL;
}
void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
- uint32_t* retOriginX, uint32_t* retOriginY) {
+ uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
+ checkInit();
cachedGlyph->mIsValid = false;
// If the glyph is too tall, don't cache it
- if (mCacheLines.size() != 0 && (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight)) {
- ALOGE("Font size to large to fit in cache. width, height = %i, %i",
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
+ mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
+ ALOGE("Font size too large to fit in cache. width, height = %i, %i",
(int) glyph.fWidth, (int) glyph.fHeight);
return;
}
@@ -627,36 +182,23 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
uint32_t startX = 0;
uint32_t startY = 0;
- bool bitmapFit = false;
- CacheTextureLine *cacheLine;
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
- if (bitmapFit) {
- cacheLine = mCacheLines[i];
- break;
- }
- }
-
- // If the new glyph didn't fit, flush the state so far and invalidate everything
- if (!bitmapFit) {
- flushAllAndInvalidate();
+ CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
- // Try to fit it again
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
- if (bitmapFit) {
- cacheLine = mCacheLines[i];
- break;
- }
+ if (!cacheTexture) {
+ if (!precaching) {
+ // If the new glyph didn't fit and we are not just trying to precache it,
+ // clear out the cache and try again
+ flushAllAndInvalidate();
+ cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
}
- // if we still don't fit, something is wrong and we shouldn't draw
- if (!bitmapFit) {
+ if (!cacheTexture) {
+ // either the glyph didn't fit or we're precaching and will cache it when we draw
return;
}
}
- cachedGlyph->mCachedTextureLine = cacheLine;
+ cachedGlyph->mCacheTexture = cacheTexture;
*retOriginX = startX;
*retOriginY = startY;
@@ -664,23 +206,44 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
uint32_t endX = startX + glyph.fWidth;
uint32_t endY = startY + glyph.fHeight;
- uint32_t cacheWidth = cacheLine->mMaxWidth;
+ uint32_t cacheWidth = cacheTexture->getWidth();
- CacheTexture* cacheTexture = cacheLine->mCacheTexture;
- if (!cacheTexture->mTexture) {
+ if (!cacheTexture->getTexture()) {
+ Caches::getInstance().activeTexture(0);
// Large-glyph texture memory is allocated only as needed
- allocateTextureMemory(cacheTexture);
+ cacheTexture->allocateTexture();
}
- uint8_t* cacheBuffer = cacheTexture->mTexture;
+ uint8_t* cacheBuffer = cacheTexture->getTexture();
uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
unsigned int stride = glyph.rowBytes();
uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
- for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
- for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
- uint8_t tempCol = bitmapBuffer[bY * stride + bX];
- cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
+
+ for (cacheX = startX - TEXTURE_BORDER_SIZE; cacheX < endX + TEXTURE_BORDER_SIZE; cacheX++) {
+ cacheBuffer[(startY - TEXTURE_BORDER_SIZE) * cacheWidth + cacheX] = 0;
+ cacheBuffer[(endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + cacheX] = 0;
+ }
+
+ for (cacheY = startY - TEXTURE_BORDER_SIZE + 1;
+ cacheY < endY + TEXTURE_BORDER_SIZE - 1; cacheY++) {
+ cacheBuffer[cacheY * cacheWidth + startX - TEXTURE_BORDER_SIZE] = 0;
+ cacheBuffer[cacheY * cacheWidth + endX + TEXTURE_BORDER_SIZE - 1] = 0;
+ }
+
+ if (mGammaTable) {
+ for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
+ for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
+ uint8_t tempCol = bitmapBuffer[bY * stride + bX];
+ cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
+ }
+ }
+ } else {
+ for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
+ for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
+ uint8_t tempCol = bitmapBuffer[bY * stride + bX];
+ cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
+ }
}
}
@@ -688,68 +251,28 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
}
CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
- uint8_t* textureMemory = NULL;
- CacheTexture* cacheTexture = new CacheTexture(textureMemory, width, height);
+ CacheTexture* cacheTexture = new CacheTexture(width, height);
if (allocate) {
- allocateTextureMemory(cacheTexture);
+ Caches::getInstance().activeTexture(0);
+ cacheTexture->allocateTexture();
}
return cacheTexture;
}
void FontRenderer::initTextTexture() {
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- delete mCacheLines[i];
- }
- mCacheLines.clear();
-
- if (mCacheTextureSmall) {
- delete mCacheTextureSmall;
- delete mCacheTexture128;
- delete mCacheTexture256;
- delete mCacheTexture512;
- }
-
- // Next, use other, separate caches for large glyphs.
- uint16_t maxWidth = 0;
- if (Caches::hasInstance()) {
- maxWidth = Caches::getInstance().maxTextureSize;
- }
-
- if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
- maxWidth = MAX_TEXT_CACHE_WIDTH;
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ delete mCacheTextures[i];
}
-
- mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
- mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
- mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
- mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
- mCurrentCacheTexture = mCacheTextureSmall;
+ mCacheTextures.clear();
mUploadTexture = false;
- // Split up our default cache texture into lines of certain widths
- int nextLine = 0;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
- nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
- nextLine, 0, mCacheTextureSmall));
-
- // The first cache is split into 2 lines of height 128, the rest have just one cache line.
- mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
- mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
- mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
- mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
+ mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
+ mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
+ mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
+ mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
+ mCurrentCacheTexture = mCacheTextures[0];
}
// Avoid having to reallocate memory and render quad by quad
@@ -782,7 +305,7 @@ void FontRenderer::initVertexArrayBuffers() {
uint32_t uvSize = 2;
uint32_t vertsPerQuad = 4;
uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
- mTextMeshPtr = new float[vertexBufferSize];
+ mTextMesh = new float[vertexBufferSize];
}
// We don't want to allocate anything unless we actually draw text
@@ -804,37 +327,38 @@ void FontRenderer::checkTextureUpdate() {
Caches& caches = Caches::getInstance();
GLuint lastTextureId = 0;
- // Iterate over all the cache lines and see which ones need to be updated
- for (uint32_t i = 0; i < mCacheLines.size(); i++) {
- CacheTextureLine* cl = mCacheLines[i];
- if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
- CacheTexture* cacheTexture = cl->mCacheTexture;
- uint32_t xOffset = 0;
- uint32_t yOffset = cl->mCurrentRow;
- uint32_t width = cl->mMaxWidth;
- uint32_t height = cl->mMaxHeight;
- void* textureData = cacheTexture->mTexture + (yOffset * width);
-
- if (cacheTexture->mTextureId != lastTextureId) {
+ // Iterate over all the cache textures and see which ones need to be updated
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ CacheTexture* cacheTexture = mCacheTextures[i];
+ if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
+ // Can't copy inner rect; glTexSubimage expects pointer to deal with entire buffer
+ // of data. So expand the dirty rect to the encompassing horizontal stripe.
+ const Rect* dirtyRect = cacheTexture->getDirtyRect();
+ uint32_t x = 0;
+ uint32_t y = dirtyRect->top;
+ uint32_t width = cacheTexture->getWidth();
+ uint32_t height = dirtyRect->getHeight();
+ void* textureData = cacheTexture->getTexture() + y * width;
+
+ if (cacheTexture->getTextureId() != lastTextureId) {
+ lastTextureId = cacheTexture->getTextureId();
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
- lastTextureId = cacheTexture->mTextureId;
+ glBindTexture(GL_TEXTURE_2D, lastTextureId);
}
- glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
+#if DEBUG_FONT_RENDERER
+ ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
+ i, x, y, width, height);
+#endif
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
-
- cl->mDirty = false;
+ cacheTexture->setDirty(false);
}
}
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
- if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
- const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
- }
+ glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->getTextureId());
+
+ mCurrentCacheTexture->setLinearFiltering(mLinearFiltering, false);
mLastCacheTexture = mCurrentCacheTexture;
mUploadTexture = false;
@@ -846,13 +370,12 @@ void FontRenderer::issueDrawCommand() {
Caches& caches = Caches::getInstance();
caches.bindIndicesBuffer(mIndexBufferID);
if (!mDrawn) {
- float* buffer = mTextMeshPtr;
+ float* buffer = mTextMesh;
int offset = 2;
bool force = caches.unbindMeshBuffer();
- caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
- caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
- buffer + offset);
+ caches.bindPositionVertexPointer(force, buffer);
+ caches.bindTexCoordsVertexPointer(force, buffer + offset);
}
glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
@@ -875,7 +398,7 @@ void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
const uint32_t vertsPerQuad = 4;
const uint32_t floatsPerVert = 4;
- float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
+ float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
(*currentPos++) = x1;
(*currentPos++) = y1;
@@ -943,43 +466,7 @@ void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
}
}
-uint32_t FontRenderer::getRemainingCacheCapacity() {
- uint32_t remainingCapacity = 0;
- float totalPixels = 0;
-
- //avoid divide by zero if the size is 0
- if (mCacheLines.size() == 0) {
- return 0;
- }
- for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
- remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
- totalPixels += mCacheLines[i]->mMaxWidth;
- }
- remainingCapacity = (remainingCapacity * 100) / totalPixels;
- return remainingCapacity;
-}
-
-void FontRenderer::precacheLatin(SkPaint* paint) {
- // Remaining capacity is measured in %
- uint32_t remainingCapacity = getRemainingCacheCapacity();
- uint32_t precacheIndex = 0;
-
- // We store a string with letters in a rough frequency of occurrence
- String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789");
-
- size_t size = l.size();
- uint16_t latin[size];
- paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin);
-
- while (remainingCapacity > 25 && precacheIndex < size) {
- mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex]));
- remainingCapacity = getRemainingCacheCapacity();
- precacheIndex++;
- }
-}
-
void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
- uint32_t currentNumFonts = mActiveFonts.size();
int flags = 0;
if (paint->isFakeBoldText()) {
flags |= Font::kFakeBold;
@@ -995,16 +482,10 @@ void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
scaleX, style, strokeWidth);
- const float maxPrecacheFontSize = 40.0f;
- bool isNewFont = currentNumFonts != mActiveFonts.size();
-
- if (isNewFont && fontSize <= maxPrecacheFontSize) {
- precacheLatin(paint);
- }
}
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, const float* positions) {
checkInit();
if (!mCurrentFont) {
@@ -1022,7 +503,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
mBounds = NULL;
Rect bounds;
- mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
+ mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
@@ -1036,7 +517,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
int penY = radius - bounds.bottom;
mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
- dataBuffer, paddedWidth, paddedHeight);
+ Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
DropShadow image;
@@ -1067,6 +548,25 @@ void FontRenderer::finishRender() {
}
}
+void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
+ int flags = 0;
+ if (paint->isFakeBoldText()) {
+ flags |= Font::kFakeBold;
+ }
+ const float skewX = paint->getTextSkewX();
+ uint32_t italicStyle = *(uint32_t*) &skewX;
+ const float scaleXFloat = paint->getTextScaleX();
+ uint32_t scaleX = *(uint32_t*) &scaleXFloat;
+ SkPaint::Style style = paint->getStyle();
+ const float strokeWidthFloat = paint->getStrokeWidth();
+ uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
+ float fontSize = paint->getTextSize();
+ Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
+ fontSize, flags, italicStyle, scaleX, style, strokeWidth);
+
+ font->precache(paint, text, numGlyphs);
+}
+
bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
if (!mCurrentFont) {
@@ -1111,6 +611,19 @@ bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char
return mDrawn;
}
+void FontRenderer::removeFont(const Font* font) {
+ for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
+ if (mActiveFonts[ct] == font) {
+ mActiveFonts.removeAt(ct);
+ break;
+ }
+ }
+
+ if (mCurrentFont == font) {
+ mCurrentFont = NULL;
+ }
+}
+
void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
// Compute gaussian weights for the blur
// e is the euler's number
@@ -1198,7 +711,6 @@ void FontRenderer::verticalBlur(float* weights, int32_t radius,
float currentPixel = 0.0f;
for (int32_t y = 0; y < height; y ++) {
-
uint8_t* output = dest + y * width;
for (int32_t x = 0; x < width; x ++) {
@@ -1232,7 +744,7 @@ void FontRenderer::verticalBlur(float* weights, int32_t radius,
}
}
*output = (uint8_t) blurredPixel;
- output ++;
+ output++;
}
}
}
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 2ab680e0e379..405db09c1776 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,219 +17,22 @@
#ifndef ANDROID_HWUI_FONT_RENDERER_H
#define ANDROID_HWUI_FONT_RENDERER_H
-#include <utils/String8.h>
-#include <utils/String16.h>
#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-#include <SkScalerContext.h>
#include <SkPaint.h>
-#include <SkPathMeasure.h>
-#include <SkPoint.h>
#include <GLES2/gl2.h>
-#include "Rect.h"
+#include "font/FontUtil.h"
+#include "font/CacheTexture.h"
+#include "font/CachedGlyphInfo.h"
+#include "font/Font.h"
#include "Properties.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#if RENDER_TEXT_AS_GLYPHS
- typedef uint16_t glyph_t;
- #define TO_GLYPH(g) g
- #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
- #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) false
-#else
- typedef SkUnichar glyph_t;
- #define TO_GLYPH(g) ((SkUnichar) g)
- #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
- #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) glyph < 0
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Declarations
-///////////////////////////////////////////////////////////////////////////////
-
-class FontRenderer;
-
-class CacheTexture {
-public:
- CacheTexture() { }
- CacheTexture(uint8_t* texture, uint16_t width, uint16_t height) :
- mTexture(texture), mTextureId(0), mWidth(width), mHeight(height),
- mLinearFiltering(false) { }
- ~CacheTexture() {
- if (mTexture) {
- delete[] mTexture;
- }
- if (mTextureId) {
- glDeleteTextures(1, &mTextureId);
- }
- }
-
- uint8_t* mTexture;
- GLuint mTextureId;
- uint16_t mWidth;
- uint16_t mHeight;
- bool mLinearFiltering;
-};
-
-class CacheTextureLine {
-public:
- CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow,
- uint32_t currentCol, CacheTexture* cacheTexture):
- mMaxHeight(maxHeight),
- mMaxWidth(maxWidth),
- mCurrentRow(currentRow),
- mCurrentCol(currentCol),
- mDirty(false),
- mCacheTexture(cacheTexture) {
- }
-
- bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
-
- uint16_t mMaxHeight;
- uint16_t mMaxWidth;
- uint32_t mCurrentRow;
- uint32_t mCurrentCol;
- bool mDirty;
- CacheTexture* mCacheTexture;
-};
-
-struct CachedGlyphInfo {
- // Has the cache been invalidated?
- bool mIsValid;
- // Location of the cached glyph in the bitmap
- // in case we need to resize the texture or
- // render to bitmap
- uint32_t mStartX;
- uint32_t mStartY;
- uint32_t mBitmapWidth;
- uint32_t mBitmapHeight;
- // Also cache texture coords for the quad
- float mBitmapMinU;
- float mBitmapMinV;
- float mBitmapMaxU;
- float mBitmapMaxV;
- // Minimize how much we call freetype
- uint32_t mGlyphIndex;
- uint32_t mAdvanceX;
- uint32_t mAdvanceY;
- // Values below contain a glyph's origin in the bitmap
- int32_t mBitmapLeft;
- int32_t mBitmapTop;
- // Auto-kerning
- SkFixed mLsbDelta;
- SkFixed mRsbDelta;
- CacheTextureLine* mCachedTextureLine;
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Represents a font, defined by a Skia font id and a font size. A font is used
- * to generate glyphs and cache them in the FontState.
- */
-class Font {
-public:
- enum Style {
- kFakeBold = 1
- };
-
- ~Font();
-
- /**
- * Renders the specified string of text.
- * If bitmap is specified, it will be used as the render target
- */
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
- uint32_t bitmapW = 0, uint32_t bitmapH = 0);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, const float* positions);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset);
-
- /**
- * Creates a new font associated with the specified font state.
- */
- static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
- uint32_t strokeWidth);
-
-protected:
- friend class FontRenderer;
- typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
- uint32_t, uint32_t, Rect*, const float*);
-
- enum RenderMode {
- FRAMEBUFFER,
- BITMAP,
- MEASURE,
- };
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
-
- void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds);
-
- Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
- uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
-
- // Cache of glyphs
- DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
-
- void invalidateTextureCache(CacheTextureLine *cacheLine = NULL);
-
- CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph);
- void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph);
-
- void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
-
- CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit);
-
- static glyph_t nextGlyph(const uint16_t** srcPtr) {
- const uint16_t* src = *srcPtr;
- glyph_t g = *src++;
- *srcPtr = src;
- return g;
- }
-
- FontRenderer* mState;
- uint32_t mFontId;
- float mFontSize;
- int mFlags;
- uint32_t mItalicStyle;
- uint32_t mScaleX;
- SkPaint::Style mStyle;
- uint32_t mStrokeWidth;
-};
-
-///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
@@ -245,6 +48,9 @@ public:
}
void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
+
+ void precache(SkPaint* paint, const char* text, int numGlyphs);
+
// bounds is an out parameter
bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
@@ -274,42 +80,29 @@ 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, const float* positions);
GLuint getTexture(bool linearFiltering = false) {
checkInit();
- if (linearFiltering != mCurrentCacheTexture->mLinearFiltering) {
- mCurrentCacheTexture->mLinearFiltering = linearFiltering;
- mLinearFiltering = linearFiltering;
- const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
-
- glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- }
+ mCurrentCacheTexture->setLinearFiltering(linearFiltering);
+ mLinearFiltering = linearFiltering;
- return mCurrentCacheTexture->mTextureId;
+ return mCurrentCacheTexture->getTextureId();
}
uint32_t getCacheSize() const {
uint32_t size = 0;
- if (mCacheTextureSmall != NULL && mCacheTextureSmall->mTexture != NULL) {
- size += mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight;
- }
- if (mCacheTexture128 != NULL && mCacheTexture128->mTexture != NULL) {
- size += mCacheTexture128->mWidth * mCacheTexture128->mHeight;
- }
- if (mCacheTexture256 != NULL && mCacheTexture256->mTexture != NULL) {
- size += mCacheTexture256->mWidth * mCacheTexture256->mHeight;
- }
- if (mCacheTexture512 != NULL && mCacheTexture512->mTexture != NULL) {
- size += mCacheTexture512->mWidth * mCacheTexture512->mHeight;
+ for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
+ CacheTexture* cacheTexture = mCacheTextures[i];
+ if (cacheTexture && cacheTexture->getTexture()) {
+ size += cacheTexture->getWidth() * cacheTexture->getHeight();
+ }
}
return size;
}
-protected:
+private:
friend class Font;
const uint8_t* mGammaTable;
@@ -319,7 +112,8 @@ protected:
void initTextTexture();
CacheTexture* createCacheTexture(int width, int height, bool allocate);
void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
- uint32_t *retOriginX, uint32_t *retOriginY);
+ uint32_t *retOriginX, uint32_t *retOriginY, bool precaching);
+ CacheTexture* cacheBitmapInTexture(const SkGlyph& glyph, uint32_t* startX, uint32_t* startY);
void flushAllAndInvalidate();
void initVertexArrayBuffers();
@@ -328,8 +122,6 @@ protected:
void initRender(const Rect* clip, Rect* bounds);
void finishRender();
- void precacheLatin(SkPaint* paint);
-
void issueDrawCommand();
void appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
float x2, float y2, float u2, float v2,
@@ -344,27 +136,31 @@ protected:
float x3, float y3, float u3, float v3,
float x4, float y4, float u4, float v4, CacheTexture* texture);
+ void removeFont(const Font* font);
+
+ void checkTextureUpdate();
+
+ void setTextureDirty() {
+ mUploadTexture = true;
+ }
+
uint32_t mSmallCacheWidth;
uint32_t mSmallCacheHeight;
+ uint32_t mLargeCacheWidth;
+ uint32_t mLargeCacheHeight;
- Vector<CacheTextureLine*> mCacheLines;
- uint32_t getRemainingCacheCapacity();
+ Vector<CacheTexture*> mCacheTextures;
Font* mCurrentFont;
Vector<Font*> mActiveFonts;
CacheTexture* mCurrentCacheTexture;
CacheTexture* mLastCacheTexture;
- CacheTexture* mCacheTextureSmall;
- CacheTexture* mCacheTexture128;
- CacheTexture* mCacheTexture256;
- CacheTexture* mCacheTexture512;
- void checkTextureUpdate();
bool mUploadTexture;
// Pointer to vertex data to speed up frame to frame work
- float *mTextMeshPtr;
+ float* mTextMesh;
uint32_t mCurrentQuadIndex;
uint32_t mMaxNumberOfQuads;
@@ -378,12 +174,13 @@ protected:
bool mLinearFiltering;
- void computeGaussianWeights(float* weights, int32_t radius);
- void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
+ /** We should consider multi-threading this code or using Renderscript **/
+ static void computeGaussianWeights(float* weights, int32_t radius);
+ static void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
int32_t width, int32_t height);
- void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
+ static void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
int32_t width, int32_t height);
- void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
+ static void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
};
}; // namespace uirenderer
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
index 1be957f9cd21..bd0a4b39092b 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -24,20 +24,46 @@ namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
+// Utils
///////////////////////////////////////////////////////////////////////////////
-GammaFontRenderer::GammaFontRenderer() {
- INIT_LOGD("Creating gamma font renderer");
+static int luminance(const SkPaint* paint) {
+ uint32_t c = paint->getColor();
+ const int r = (c >> 16) & 0xFF;
+ const int g = (c >> 8) & 0xFF;
+ const int b = (c ) & 0xFF;
+ return (r * 2 + g * 5 + b) >> 3;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Base class GammaFontRenderer
+///////////////////////////////////////////////////////////////////////////////
+
+GammaFontRenderer* GammaFontRenderer::createRenderer() {
+ // Choose the best renderer
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_TEXT_GAMMA_METHOD, property, DEFAULT_TEXT_GAMMA_METHOD) > 0) {
+ if (!strcasecmp(property, "lookup")) {
+ return new LookupGammaFontRenderer();
+ } else if (!strcasecmp(property, "shader")) {
+ return new ShaderGammaFontRenderer(false);
+ } else if (!strcasecmp(property, "shader3")) {
+ return new ShaderGammaFontRenderer(true);
+ }
+ }
+ return new Lookup3GammaFontRenderer();
+}
+
+GammaFontRenderer::GammaFontRenderer() {
// Get the renderer properties
char property[PROPERTY_VALUE_MAX];
// Get the gamma
- float gamma = DEFAULT_TEXT_GAMMA;
+ mGamma = DEFAULT_TEXT_GAMMA;
if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
INIT_LOGD(" Setting text gamma to %s", property);
- gamma = atof(property);
+ mGamma = atof(property);
} else {
INIT_LOGD(" Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
}
@@ -61,18 +87,82 @@ GammaFontRenderer::GammaFontRenderer() {
INIT_LOGD(" Using default white black gamma threshold of %d",
DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
}
+}
+
+GammaFontRenderer::~GammaFontRenderer() {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Shader-based renderer
+///////////////////////////////////////////////////////////////////////////////
+
+ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma): GammaFontRenderer() {
+ INIT_LOGD("Creating shader gamma font renderer");
+ mRenderer = NULL;
+ mMultiGamma = multiGamma;
+}
+
+void ShaderGammaFontRenderer::describe(ProgramDescription& description,
+ const SkPaint* paint) const {
+ if (paint->getShader() == NULL) {
+ if (mMultiGamma) {
+ const int l = luminance(paint);
+
+ if (l <= mBlackThreshold) {
+ description.hasGammaCorrection = true;
+ description.gamma = mGamma;
+ } else if (l >= mWhiteThreshold) {
+ description.hasGammaCorrection = true;
+ description.gamma = 1.0f / mGamma;
+ }
+ } else {
+ description.hasGammaCorrection = true;
+ description.gamma = 1.0f / mGamma;
+ }
+ }
+}
+
+void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description,
+ Program* program) const {
+ if (description.hasGammaCorrection) {
+ glUniform1f(program->getUniform("gamma"), description.gamma);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Lookup-based renderer
+///////////////////////////////////////////////////////////////////////////////
+
+LookupGammaFontRenderer::LookupGammaFontRenderer(): GammaFontRenderer() {
+ INIT_LOGD("Creating lookup gamma font renderer");
// Compute the gamma tables
- const float blackGamma = gamma;
- const float whiteGamma = 1.0f / gamma;
+ const float gamma = 1.0f / mGamma;
for (uint32_t i = 0; i <= 255; i++) {
- mGammaTable[i] = i;
+ mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f));
+ }
+ mRenderer = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Lookup-based renderer, using 3 different correction tables
+///////////////////////////////////////////////////////////////////////////////
+
+Lookup3GammaFontRenderer::Lookup3GammaFontRenderer(): GammaFontRenderer() {
+ INIT_LOGD("Creating lookup3 gamma font renderer");
+
+ // Compute the gamma tables
+ const float blackGamma = mGamma;
+ const float whiteGamma = 1.0f / mGamma;
+
+ for (uint32_t i = 0; i <= 255; i++) {
const float v = i / 255.0f;
const float black = pow(v, blackGamma);
const float white = pow(v, whiteGamma);
+ mGammaTable[i] = i;
mGammaTable[256 + i] = uint8_t((float)::floor(black * 255.0f + 0.5f));
mGammaTable[512 + i] = uint8_t((float)::floor(white * 255.0f + 0.5f));
}
@@ -81,20 +171,20 @@ GammaFontRenderer::GammaFontRenderer() {
memset(mRenderersUsageCount, 0, sizeof(uint32_t) * kGammaCount);
}
-GammaFontRenderer::~GammaFontRenderer() {
+Lookup3GammaFontRenderer::~Lookup3GammaFontRenderer() {
for (int i = 0; i < kGammaCount; i++) {
delete mRenderers[i];
}
}
-void GammaFontRenderer::clear() {
+void Lookup3GammaFontRenderer::clear() {
for (int i = 0; i < kGammaCount; i++) {
delete mRenderers[i];
mRenderers[i] = NULL;
}
}
-void GammaFontRenderer::flush() {
+void Lookup3GammaFontRenderer::flush() {
int count = 0;
int min = -1;
uint32_t minCount = UINT_MAX;
@@ -122,7 +212,7 @@ void GammaFontRenderer::flush() {
}
}
-FontRenderer* GammaFontRenderer::getRenderer(Gamma gamma) {
+FontRenderer* Lookup3GammaFontRenderer::getRenderer(Gamma gamma) {
FontRenderer* renderer = mRenderers[gamma];
if (!renderer) {
renderer = new FontRenderer();
@@ -133,17 +223,13 @@ FontRenderer* GammaFontRenderer::getRenderer(Gamma gamma) {
return renderer;
}
-FontRenderer& GammaFontRenderer::getFontRenderer(const SkPaint* paint) {
+FontRenderer& Lookup3GammaFontRenderer::getFontRenderer(const SkPaint* paint) {
if (paint->getShader() == NULL) {
- uint32_t c = paint->getColor();
- const int r = (c >> 16) & 0xFF;
- const int g = (c >> 8) & 0xFF;
- const int b = (c ) & 0xFF;
- const int luminance = (r * 2 + g * 5 + b) >> 3;
+ const int l = luminance(paint);
- if (luminance <= mBlackThreshold) {
+ if (l <= mBlackThreshold) {
return *getRenderer(kGammaBlack);
- } else if (luminance >= mWhiteThreshold) {
+ } else if (l >= mWhiteThreshold) {
return *getRenderer(kGammaWhite);
}
}
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index 99f08f032769..5c1860ea5c8e 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -20,20 +20,132 @@
#include <SkPaint.h>
#include "FontRenderer.h"
+#include "Program.h"
namespace android {
namespace uirenderer {
-struct GammaFontRenderer {
+class GammaFontRenderer {
+public:
+ virtual ~GammaFontRenderer();
+
+ virtual void clear() = 0;
+ virtual void flush() = 0;
+
+ virtual FontRenderer& getFontRenderer(const SkPaint* paint) = 0;
+
+ virtual uint32_t getFontRendererCount() const = 0;
+ virtual uint32_t getFontRendererSize(uint32_t fontRenderer) const = 0;
+
+ virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0;
+ virtual void setupProgram(ProgramDescription& description, Program* program) const = 0;
+
+ static GammaFontRenderer* createRenderer();
+
+protected:
GammaFontRenderer();
- ~GammaFontRenderer();
- enum Gamma {
- kGammaDefault = 0,
- kGammaBlack = 1,
- kGammaWhite = 2,
- kGammaCount = 3
- };
+ int mBlackThreshold;
+ int mWhiteThreshold;
+
+ float mGamma;
+};
+
+class ShaderGammaFontRenderer: public GammaFontRenderer {
+public:
+ ~ShaderGammaFontRenderer() {
+ delete mRenderer;
+ }
+
+ void clear() {
+ delete mRenderer;
+ mRenderer = NULL;
+ }
+
+ void flush() {
+ if (mRenderer) {
+ mRenderer->flushLargeCaches();
+ }
+ }
+
+ FontRenderer& getFontRenderer(const SkPaint* paint) {
+ if (!mRenderer) {
+ mRenderer = new FontRenderer;
+ }
+ return *mRenderer;
+ }
+
+ uint32_t getFontRendererCount() const {
+ return 1;
+ }
+
+ uint32_t getFontRendererSize(uint32_t fontRenderer) const {
+ return mRenderer ? mRenderer->getCacheSize() : 0;
+ }
+
+ void describe(ProgramDescription& description, const SkPaint* paint) const;
+ void setupProgram(ProgramDescription& description, Program* program) const;
+
+private:
+ ShaderGammaFontRenderer(bool multiGamma);
+
+ FontRenderer* mRenderer;
+ bool mMultiGamma;
+
+ friend class GammaFontRenderer;
+};
+
+class LookupGammaFontRenderer: public GammaFontRenderer {
+public:
+ ~LookupGammaFontRenderer() {
+ delete mRenderer;
+ }
+
+ void clear() {
+ delete mRenderer;
+ mRenderer = NULL;
+ }
+
+ void flush() {
+ if (mRenderer) {
+ mRenderer->flushLargeCaches();
+ }
+ }
+
+ FontRenderer& getFontRenderer(const SkPaint* paint) {
+ if (!mRenderer) {
+ mRenderer = new FontRenderer;
+ mRenderer->setGammaTable(&mGammaTable[0]);
+ }
+ return *mRenderer;
+ }
+
+ uint32_t getFontRendererCount() const {
+ return 1;
+ }
+
+ uint32_t getFontRendererSize(uint32_t fontRenderer) const {
+ return mRenderer ? mRenderer->getCacheSize() : 0;
+ }
+
+ void describe(ProgramDescription& description, const SkPaint* paint) const {
+ }
+
+ void setupProgram(ProgramDescription& description, Program* program) const {
+ }
+
+private:
+ LookupGammaFontRenderer();
+
+ FontRenderer* mRenderer;
+ uint8_t mGammaTable[256];
+
+ friend class GammaFontRenderer;
+};
+
+class Lookup3GammaFontRenderer: public GammaFontRenderer {
+public:
+ ~Lookup3GammaFontRenderer();
void clear();
void flush();
@@ -53,16 +165,30 @@ struct GammaFontRenderer {
return renderer->getCacheSize();
}
+ void describe(ProgramDescription& description, const SkPaint* paint) const {
+ }
+
+ void setupProgram(ProgramDescription& description, Program* program) const {
+ }
+
private:
+ Lookup3GammaFontRenderer();
+
+ enum Gamma {
+ kGammaDefault = 0,
+ kGammaBlack = 1,
+ kGammaWhite = 2,
+ kGammaCount = 3
+ };
+
FontRenderer* getRenderer(Gamma gamma);
uint32_t mRenderersUsageCount[kGammaCount];
FontRenderer* mRenderers[kGammaCount];
- int mBlackThreshold;
- int mWhiteThreshold;
-
uint8_t mGammaTable[256 * kGammaCount];
+
+ friend class GammaFontRenderer;
};
}; // namespace uirenderer
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 3678788d5d50..2e4e3492df03 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -16,13 +16,9 @@
#define LOG_TAG "OpenGLRenderer"
-#include <GLES2/gl2.h>
-
-#include <SkCanvas.h>
-#include <SkGradientShader.h>
-
#include <utils/threads.h>
+#include "Caches.h"
#include "Debug.h"
#include "GradientCache.h"
#include "Properties.h"
@@ -31,6 +27,22 @@ namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define GRADIENT_TEXTURE_HEIGHT 2
+#define GRADIENT_BYTES_PER_PIXEL 4
+
+///////////////////////////////////////////////////////////////////////////////
+// Functions
+///////////////////////////////////////////////////////////////////////////////
+
+template<typename T>
+static inline T min(T a, T b) {
+ return a < b ? a : b;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
@@ -45,6 +57,8 @@ GradientCache::GradientCache():
INIT_LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
}
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+
mCache.setOnEntryRemovedListener(this);
}
@@ -83,7 +97,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) {
void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
if (texture) {
- const uint32_t size = texture->width * texture->height * 4;
+ const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
mSize -= size;
}
@@ -97,14 +111,13 @@ void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
// Caching
///////////////////////////////////////////////////////////////////////////////
-Texture* GradientCache::get(uint32_t* colors, float* positions,
- int count, SkShader::TileMode tileMode) {
+Texture* GradientCache::get(uint32_t* colors, float* positions, int count) {
- GradientCacheEntry gradient(colors, positions, count, tileMode);
+ GradientCacheEntry gradient(colors, positions, count);
Texture* texture = mCache.get(gradient);
if (!texture) {
- texture = addLinearGradient(gradient, colors, positions, count, tileMode);
+ texture = addLinearGradient(gradient, colors, positions, count);
}
return texture;
@@ -114,36 +127,45 @@ void GradientCache::clear() {
mCache.clear();
}
-Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
- uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) {
- SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1024, 1);
- bitmap.allocPixels();
- bitmap.eraseColor(0);
+void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
+ GradientInfo& info) {
+ uint32_t width = 256 * (count - 1);
- SkCanvas canvas(bitmap);
+ if (!Caches::getInstance().extensions.hasNPot()) {
+ width = 1 << (31 - __builtin_clz(width));
+ }
+
+ bool hasAlpha = false;
+ for (int i = 0; i < count; i++) {
+ if (((colors[i] >> 24) & 0xff) < 255) {
+ hasAlpha = true;
+ break;
+ }
+ }
- SkPoint points[2];
- points[0].set(0.0f, 0.0f);
- points[1].set(bitmap.width(), 0.0f);
+ info.width = min(width, uint32_t(mMaxTextureSize));
+ info.hasAlpha = hasAlpha;
+}
- SkShader* localShader = SkGradientShader::CreateLinear(points,
- reinterpret_cast<const SkColor*>(colors), positions, count, tileMode);
+Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
+ uint32_t* colors, float* positions, int count) {
- SkPaint p;
- p.setStyle(SkPaint::kStrokeAndFill_Style);
- p.setShader(localShader)->unref();
+ GradientInfo info;
+ getGradientInfo(colors, count, info);
- canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 1.0f, p);
+ Texture* texture = new Texture;
+ texture->width = info.width;
+ texture->height = GRADIENT_TEXTURE_HEIGHT;
+ texture->blend = info.hasAlpha;
+ texture->generation = 1;
// Asume the cache is always big enough
- const uint32_t size = bitmap.rowBytes() * bitmap.height();
+ const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
while (mSize + size > mMaxSize) {
mCache.removeOldest();
}
- Texture* texture = new Texture;
- generateTexture(&bitmap, texture);
+ generateTexture(colors, positions, count, texture);
mSize += size;
mCache.put(gradient, texture);
@@ -151,25 +173,69 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
return texture;
}
-void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) {
- SkAutoLockPixels autoLock(*bitmap);
- if (!bitmap->readyToDraw()) {
- ALOGE("Cannot generate texture from shader");
- return;
+void GradientCache::generateTexture(uint32_t* colors, float* positions,
+ int count, Texture* texture) {
+
+ const uint32_t width = texture->width;
+ const GLsizei rowBytes = width * GRADIENT_BYTES_PER_PIXEL;
+ uint32_t pixels[width * texture->height];
+
+ int currentPos = 1;
+
+ float startA = (colors[0] >> 24) & 0xff;
+ float startR = (colors[0] >> 16) & 0xff;
+ float startG = (colors[0] >> 8) & 0xff;
+ float startB = (colors[0] >> 0) & 0xff;
+
+ float endA = (colors[1] >> 24) & 0xff;
+ float endR = (colors[1] >> 16) & 0xff;
+ float endG = (colors[1] >> 8) & 0xff;
+ float endB = (colors[1] >> 0) & 0xff;
+
+ float start = positions[0];
+ float distance = positions[1] - start;
+
+ uint8_t* p = (uint8_t*) pixels;
+ for (uint32_t x = 0; x < width; x++) {
+ float pos = x / float(width - 1);
+ if (pos > positions[currentPos]) {
+ startA = endA;
+ startR = endR;
+ startG = endG;
+ startB = endB;
+ start = positions[currentPos];
+
+ currentPos++;
+
+ endA = (colors[currentPos] >> 24) & 0xff;
+ endR = (colors[currentPos] >> 16) & 0xff;
+ endG = (colors[currentPos] >> 8) & 0xff;
+ endB = (colors[currentPos] >> 0) & 0xff;
+ distance = positions[currentPos] - start;
+ }
+
+ float amount = (pos - start) / distance;
+ float oppAmount = 1.0f - amount;
+
+ const float alpha = startA * oppAmount + endA * amount;
+ const float a = alpha / 255.0f;
+ *p++ = uint8_t(a * (startR * oppAmount + endR * amount));
+ *p++ = uint8_t(a * (startG * oppAmount + endG * amount));
+ *p++ = uint8_t(a * (startB * oppAmount + endB * amount));
+ *p++ = uint8_t(alpha);
}
- texture->generation = bitmap->getGenerationID();
- texture->width = bitmap->width();
- texture->height = bitmap->height();
+ for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) {
+ memcpy(pixels + width * i, pixels, rowBytes);
+ }
glGenTextures(1, &texture->id);
glBindTexture(GL_TEXTURE_2D, texture->id);
- glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
+ glPixelStorei(GL_UNPACK_ALIGNMENT, GRADIENT_BYTES_PER_PIXEL);
- texture->blend = !bitmap->isOpaque();
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, pixels);
texture->setFilter(GL_LINEAR);
texture->setWrap(GL_CLAMP_TO_EDGE);
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index ac346846ba99..3b7c1fa92499 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HWUI_GRADIENT_CACHE_H
#define ANDROID_HWUI_GRADIENT_CACHE_H
+#include <GLES2/gl2.h>
+
#include <SkShader.h>
#include <utils/Mutex.h>
@@ -34,16 +36,14 @@ struct GradientCacheEntry {
count = 0;
colors = NULL;
positions = NULL;
- tileMode = SkShader::kClamp_TileMode;
}
- GradientCacheEntry(uint32_t* colors, float* positions, int count,
- SkShader::TileMode tileMode) {
- copy(colors, positions, count, tileMode);
+ GradientCacheEntry(uint32_t* colors, float* positions, int count) {
+ copy(colors, positions, count);
}
GradientCacheEntry(const GradientCacheEntry& entry) {
- copy(entry.colors, entry.positions, entry.count, entry.tileMode);
+ copy(entry.colors, entry.positions, entry.count);
}
~GradientCacheEntry() {
@@ -56,7 +56,7 @@ struct GradientCacheEntry {
delete[] colors;
delete[] positions;
- copy(entry.colors, entry.positions, entry.count, entry.tileMode);
+ copy(entry.colors, entry.positions, entry.count);
}
return *this;
@@ -65,13 +65,11 @@ struct GradientCacheEntry {
bool operator<(const GradientCacheEntry& r) const {
const GradientCacheEntry& rhs = (const GradientCacheEntry&) r;
LTE_INT(count) {
- LTE_INT(tileMode) {
- int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t));
- if (result< 0) return true;
- else if (result == 0) {
- result = memcmp(positions, rhs.positions, count * sizeof(float));
- if (result < 0) return true;
- }
+ int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t));
+ if (result< 0) return true;
+ else if (result == 0) {
+ result = memcmp(positions, rhs.positions, count * sizeof(float));
+ if (result < 0) return true;
}
}
return false;
@@ -84,11 +82,10 @@ struct GradientCacheEntry {
private:
- void copy(uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) {
+ void copy(uint32_t* colors, float* positions, int count) {
this->count = count;
this->colors = new uint32_t[count];
this->positions = new float[count];
- this->tileMode = tileMode;
memcpy(this->colors, colors, count * sizeof(uint32_t));
memcpy(this->positions, positions, count * sizeof(float));
@@ -116,8 +113,8 @@ public:
/**
* Returns the texture associated with the specified shader.
*/
- Texture* get(uint32_t* colors, float* positions,
- int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
+ Texture* get(uint32_t* colors, float* positions, int count);
+
/**
* Clears the cache. This causes all textures to be deleted.
*/
@@ -142,16 +139,24 @@ private:
* returned.
*/
Texture* addLinearGradient(GradientCacheEntry& gradient,
- uint32_t* colors, float* positions, int count,
- SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
+ uint32_t* colors, float* positions, int count);
+
+ void generateTexture(uint32_t* colors, float* positions, int count, Texture* texture);
- void generateTexture(SkBitmap* bitmap, Texture* texture);
+ struct GradientInfo {
+ uint32_t width;
+ bool hasAlpha;
+ };
+
+ void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);
GenerationCache<GradientCacheEntry, Texture*> mCache;
uint32_t mSize;
uint32_t mMaxSize;
+ GLint mMaxTextureSize;
+
Vector<SkShader*> mGarbage;
mutable Mutex mLock;
}; // class GradientCache
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
new file mode 100644
index 000000000000..882e4bbc4bb1
--- /dev/null
+++ b/libs/hwui/Layer.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 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 <utils/Log.h>
+
+#include "Layer.h"
+#include "LayerRenderer.h"
+#include "OpenGLRenderer.h"
+#include "Caches.h"
+
+namespace android {
+namespace uirenderer {
+
+Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
+ mesh = NULL;
+ meshIndices = NULL;
+ meshElementCount = 0;
+ cacheable = true;
+ textureLayer = false;
+ renderTarget = GL_TEXTURE_2D;
+ texture.width = layerWidth;
+ texture.height = layerHeight;
+ colorFilter = NULL;
+ deferredUpdateScheduled = false;
+ renderer = NULL;
+ displayList = NULL;
+ fbo = 0;
+ Caches::getInstance().resourceCache.incrementRefcount(this);
+}
+
+Layer::~Layer() {
+ if (mesh) delete mesh;
+ if (meshIndices) delete meshIndices;
+ if (colorFilter) Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
+ removeFbo();
+ deleteTexture();
+}
+
+void Layer::removeFbo() {
+ if (fbo) {
+ LayerRenderer::flushLayer(this);
+ Caches::getInstance().fboCache.put(fbo);
+ fbo = 0;
+ }
+}
+
+void Layer::setPaint(SkPaint* paint) {
+ OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+}
+
+void Layer::setColorFilter(SkiaColorFilter* filter) {
+ if (colorFilter) {
+ Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
+ }
+ colorFilter = filter;
+ if (colorFilter) {
+ Caches::getInstance().resourceCache.incrementRefcount(colorFilter);
+ }
+}
+
+
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index f2431771bd8b..69be3178ff9d 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -45,25 +45,10 @@ class DisplayList;
* A layer has dimensions and is backed by an OpenGL texture or FBO.
*/
struct Layer {
- Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
- mesh = NULL;
- meshIndices = NULL;
- meshElementCount = 0;
- cacheable = true;
- textureLayer = false;
- renderTarget = GL_TEXTURE_2D;
- texture.width = layerWidth;
- texture.height = layerHeight;
- colorFilter = NULL;
- deferredUpdateScheduled = false;
- renderer = NULL;
- displayList = NULL;
- }
-
- ~Layer() {
- if (mesh) delete mesh;
- if (meshIndices) delete meshIndices;
- }
+ Layer(const uint32_t layerWidth, const uint32_t layerHeight);
+ ~Layer();
+
+ void removeFbo();
/**
* Sets this layer's region to a rectangle. Computes the appropriate
@@ -106,6 +91,8 @@ struct Layer {
texture.height = height;
}
+ ANDROID_API void setPaint(SkPaint* paint);
+
inline void setBlend(bool blend) {
texture.blend = blend;
}
@@ -147,10 +134,6 @@ struct Layer {
return fbo;
}
- inline GLuint* getTexturePointer() {
- return &texture.id;
- }
-
inline GLuint getTexture() {
return texture.id;
}
@@ -191,20 +174,34 @@ struct Layer {
return colorFilter;
}
- inline void setColorFilter(SkiaColorFilter* filter) {
- colorFilter = filter;
- }
+ ANDROID_API void setColorFilter(SkiaColorFilter* filter);
inline void bindTexture() {
- glBindTexture(renderTarget, texture.id);
+ if (texture.id) {
+ glBindTexture(renderTarget, texture.id);
+ }
}
inline void generateTexture() {
- glGenTextures(1, &texture.id);
+ if (!texture.id) {
+ glGenTextures(1, &texture.id);
+ }
}
inline void deleteTexture() {
- if (texture.id) glDeleteTextures(1, &texture.id);
+ if (texture.id) {
+ glDeleteTextures(1, &texture.id);
+ texture.id = 0;
+ }
+ }
+
+ /**
+ * When the caller frees the texture itself, the caller
+ * must call this method to tell this layer that it lost
+ * the texture.
+ */
+ void clearTexture() {
+ texture.id = 0;
}
inline void deleteFbo() {
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index eea707edb457..ce74ceec1b27 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -69,15 +69,10 @@ void LayerCache::setMaxSize(uint32_t maxSize) {
void LayerCache::deleteLayer(Layer* layer) {
if (layer) {
- GLuint fbo = layer->getFbo();
- LAYER_LOGD("Destroying layer %dx%d, fbo %d", layer->getWidth(), layer->getHeight(), fbo);
-
+ LAYER_LOGD("Destroying layer %dx%d, fbo %d", layer->getWidth(), layer->getHeight(),
+ layer->getFbo());
mSize -= layer->getWidth() * layer->getHeight() * 4;
-
- if (fbo) Caches::getInstance().fboCache.put(fbo);
- layer->deleteTexture();
-
- delete layer;
+ Caches::getInstance().resourceCache.decrementRefcount(layer);
}
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 65f8c7c8b67c..f2e7f66ac503 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -37,6 +37,10 @@ LayerRenderer::LayerRenderer(Layer* layer): mLayer(layer) {
LayerRenderer::~LayerRenderer() {
}
+void LayerRenderer::setViewport(int width, int height) {
+ initViewport(width, height);
+}
+
int LayerRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) {
LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
@@ -45,7 +49,6 @@ int LayerRenderer::prepareDirty(float left, float top, float right, float bottom
const float width = mLayer->layer.getWidth();
const float height = mLayer->layer.getHeight();
-#if RENDER_LAYERS_AS_REGIONS
Rect dirty(left, top, right, bottom);
if (dirty.isEmpty() || (dirty.left <= 0 && dirty.top <= 0 &&
dirty.right >= width && dirty.bottom >= height)) {
@@ -58,9 +61,6 @@ int LayerRenderer::prepareDirty(float left, float top, float right, float bottom
}
return OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
-#else
- return OpenGLRenderer::prepareDirty(0.0f, 0.0f, width, height, opaque);
-#endif
}
void LayerRenderer::finish() {
@@ -78,6 +78,10 @@ GLint LayerRenderer::getTargetFbo() {
return mLayer->getFbo();
}
+bool LayerRenderer::suppressErrorChecks() {
+ return true;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Dirty region tracking
///////////////////////////////////////////////////////////////////////////////
@@ -87,14 +91,10 @@ bool LayerRenderer::hasLayer() {
}
Region* LayerRenderer::getRegion() {
-#if RENDER_LAYERS_AS_REGIONS
if (getSnapshot()->flags & Snapshot::kFlagFboTarget) {
return OpenGLRenderer::getRegion();
}
return &mLayer->region;
-#else
- return OpenGLRenderer::getRegion();
-#endif
}
// TODO: This implementation is flawed and can generate T-junctions
@@ -105,7 +105,6 @@ Region* LayerRenderer::getRegion() {
// In practice, T-junctions do not appear often so this has yet
// to be fixed.
void LayerRenderer::generateMesh() {
-#if RENDER_LAYERS_AS_REGIONS
if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
if (mLayer->mesh) {
delete mLayer->mesh;
@@ -172,7 +171,6 @@ void LayerRenderer::generateMesh() {
indices[index + 5] = quad + 3; // bottom-right
}
}
-#endif
}
///////////////////////////////////////////////////////////////////////////////
@@ -221,10 +219,8 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque
fbo, width, height);
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- caches.fboCache.put(fbo);
- layer->deleteTexture();
- delete layer;
+ Caches::getInstance().resourceCache.decrementRefcount(layer);
return NULL;
}
@@ -233,9 +229,8 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->getTexture(), 0);
- glDisable(GL_SCISSOR_TEST);
+ caches.disableScissor();
glClear(GL_COLOR_BUFFER_BIT);
- glEnable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
@@ -251,8 +246,7 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
layer->texCoords.set(0.0f, height / float(layer->getHeight()),
width / float(layer->getWidth()), 0.0f);
} else {
- layer->deleteTexture();
- delete layer;
+ Caches::getInstance().resourceCache.decrementRefcount(layer);
return false;
}
}
@@ -305,22 +299,15 @@ void LayerRenderer::destroyLayer(Layer* layer) {
LAYER_RENDERER_LOGD("Recycling layer, %dx%d fbo = %d",
layer->getWidth(), layer->getHeight(), layer->getFbo());
- GLuint fbo = layer->getFbo();
- if (fbo) {
- flushLayer(layer);
- Caches::getInstance().fboCache.put(fbo);
- layer->setFbo(0);
- }
-
if (!Caches::getInstance().layerCache.put(layer)) {
LAYER_RENDERER_LOGD(" Destroyed!");
- layer->deleteTexture();
- delete layer;
+ Caches::getInstance().resourceCache.decrementRefcount(layer);
} else {
LAYER_RENDERER_LOGD(" Cached!");
#if DEBUG_LAYER_RENDERER
Caches::getInstance().layerCache.dump();
#endif
+ layer->removeFbo();
layer->region.clear();
}
}
@@ -343,10 +330,10 @@ void LayerRenderer::flushLayer(Layer* layer) {
if (Caches::getInstance().extensions.hasDiscardFramebuffer()) {
GLuint previousFbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
-
- GLenum attachments = GL_COLOR_ATTACHMENT0;
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, &attachments);
+
+ const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };
+ glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
}
@@ -356,7 +343,7 @@ void LayerRenderer::flushLayer(Layer* layer) {
bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
Caches& caches = Caches::getInstance();
- if (layer && layer->isTextureLayer() && bitmap->width() <= caches.maxTextureSize &&
+ if (layer && bitmap->width() <= caches.maxTextureSize &&
bitmap->height() <= caches.maxTextureSize) {
GLuint fbo = caches.fboCache.get();
@@ -369,6 +356,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
GLuint texture;
GLuint previousFbo;
+ GLuint previousViewport[4];
GLenum format;
GLenum type;
@@ -398,11 +386,13 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
float alpha = layer->getAlpha();
SkXfermode::Mode mode = layer->getMode();
+ GLuint previousLayerFbo = layer->getFbo();
layer->setAlpha(255, SkXfermode::kSrc_Mode);
layer->setFbo(fbo);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
+ glGetIntegerv(GL_VIEWPORT, (GLint*) &previousViewport);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glGenTextures(1, &texture);
@@ -411,6 +401,8 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
caches.activeTexture(0);
glBindTexture(GL_TEXTURE_2D, texture);
+ glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -431,7 +423,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
bitmap->width(), bitmap->height(), !layer->isBlend());
- glDisable(GL_SCISSOR_TEST);
+ caches.disableScissor();
renderer.translate(0.0f, bitmap->height());
renderer.scale(1.0f, -1.0f);
@@ -460,8 +452,6 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
}
error:
- glEnable(GL_SCISSOR_TEST);
-
#if DEBUG_OPENGL
if (error != GL_NO_ERROR) {
ALOGD("GL error while copying layer into bitmap = 0x%x", error);
@@ -470,9 +460,11 @@ error:
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
layer->setAlpha(alpha, mode);
- layer->setFbo(0);
+ layer->setFbo(previousLayerFbo);
glDeleteTextures(1, &texture);
caches.fboCache.put(fbo);
+ glViewport(previousViewport[0], previousViewport[1],
+ previousViewport[2], previousViewport[3]);
return status;
}
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index 8f3a0a3111ee..acedbcc547c8 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -47,13 +47,10 @@ public:
ANDROID_API LayerRenderer(Layer* layer);
virtual ~LayerRenderer();
+ virtual void setViewport(int width, int height);
virtual int prepareDirty(float left, float top, float right, float bottom, bool opaque);
virtual void finish();
- virtual bool hasLayer();
- virtual Region* getRegion();
- virtual GLint getTargetFbo();
-
ANDROID_API static Layer* createTextureLayer(bool isOpaque);
ANDROID_API static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false);
ANDROID_API static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
@@ -61,9 +58,16 @@ public:
bool isOpaque, GLenum renderTarget, float* transform);
ANDROID_API static void destroyLayer(Layer* layer);
ANDROID_API static void destroyLayerDeferred(Layer* layer);
- ANDROID_API static void flushLayer(Layer* layer);
ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap);
+ static void flushLayer(Layer* layer);
+
+protected:
+ virtual bool hasLayer();
+ virtual Region* getRegion();
+ virtual GLint getTargetFbo();
+ virtual bool suppressErrorChecks();
+
private:
void generateMesh();
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 7348f4d834dd..a924362bf312 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -55,24 +55,29 @@ void Matrix4::loadIdentity() {
mSimpleMatrix = true;
}
-bool Matrix4::changesBounds() {
+bool Matrix4::changesBounds() const {
return !(data[0] == 1.0f && data[1] == 0.0f && data[2] == 0.0f && data[4] == 0.0f &&
data[5] == 1.0f && data[6] == 0.0f && data[8] == 0.0f && data[9] == 0.0f &&
data[10] == 1.0f);
}
-bool Matrix4::isPureTranslate() {
+bool Matrix4::isPureTranslate() const {
return mSimpleMatrix && data[kScaleX] == 1.0f && data[kScaleY] == 1.0f;
}
-bool Matrix4::isSimple() {
+bool Matrix4::isSimple() const {
return mSimpleMatrix;
}
-bool Matrix4::isIdentity() {
+bool Matrix4::isIdentity() const {
return mIsIdentity;
}
+bool Matrix4::isPerspective() const {
+ return data[kPerspective0] != 0.0f || data[kPerspective1] != 0.0f ||
+ data[kPerspective2] != 1.0f;
+}
+
void Matrix4::load(const float* v) {
memcpy(data, v, sizeof(data));
// TODO: Do something smarter here
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 22220a93c0ad..f86823d0a5b5 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -112,11 +112,12 @@ public:
multiply(u);
}
- bool isPureTranslate();
- bool isSimple();
- bool isIdentity();
+ bool isPureTranslate() const;
+ bool isSimple() const;
+ bool isIdentity() const;
+ bool isPerspective() const;
- bool changesBounds();
+ bool changesBounds() const;
void copyTo(float* v) const;
void copyTo(SkMatrix& v) const;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 9a90bfd1e0ff..7c23e4b373d6 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -33,6 +33,8 @@
#include "OpenGLRenderer.h"
#include "DisplayListRenderer.h"
+#include "PathRenderer.h"
+#include "Properties.h"
#include "Vector.h"
namespace android {
@@ -45,8 +47,7 @@ namespace uirenderer {
#define RAD_TO_DEG (180.0f / 3.14159265f)
#define MIN_ANGLE 0.001f
-// TODO: This should be set in properties
-#define ALPHA_THRESHOLD (0x7f / PANEL_BIT_DEPTH)
+#define ALPHA_THRESHOLD 0
#define FILTER(paint) (paint && paint->isFilterBitmap() ? GL_LINEAR : GL_NEAREST)
@@ -117,6 +118,8 @@ OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) {
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
mFirstSnapshot = new Snapshot;
+
+ mScissorOptimizationDisabled = false;
}
OpenGLRenderer::~OpenGLRenderer() {
@@ -124,31 +127,35 @@ OpenGLRenderer::~OpenGLRenderer() {
// GL APIs. All GL state should be kept in Caches.h
}
-///////////////////////////////////////////////////////////////////////////////
-// Debug
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::startMark(const char* name) const {
- mCaches.startMark(0, name);
-}
-
-void OpenGLRenderer::endMark() const {
- mCaches.endMark();
+void OpenGLRenderer::initProperties() {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_DISABLE_SCISSOR_OPTIMIZATION, property, "false")) {
+ mScissorOptimizationDisabled = !strcasecmp(property, "true");
+ INIT_LOGD(" Scissor optimization %s",
+ mScissorOptimizationDisabled ? "disabled" : "enabled");
+ } else {
+ INIT_LOGD(" Scissor optimization enabled");
+ }
}
///////////////////////////////////////////////////////////////////////////////
// Setup
///////////////////////////////////////////////////////////////////////////////
-uint32_t OpenGLRenderer::getStencilSize() {
- return STENCIL_BUFFER_SIZE;
-}
-
bool OpenGLRenderer::isDeferred() {
return false;
}
void OpenGLRenderer::setViewport(int width, int height) {
+ initViewport(width, height);
+
+ glDisable(GL_DITHER);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ glEnableVertexAttribArray(Program::kBindingPosition);
+}
+
+void OpenGLRenderer::initViewport(int width, int height) {
mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
mWidth = width;
@@ -156,12 +163,6 @@ void OpenGLRenderer::setViewport(int width, int height) {
mFirstSnapshot->height = height;
mFirstSnapshot->viewport.set(0, 0, width, height);
-
- glDisable(GL_DITHER);
- glEnable(GL_SCISSOR_TEST);
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
- glEnableVertexAttribArray(Program::kBindingPosition);
}
int OpenGLRenderer::prepare(bool opaque) {
@@ -179,9 +180,31 @@ int OpenGLRenderer::prepareDirty(float left, float top, float right, float botto
mSnapshot->setClip(left, top, right, bottom);
mDirtyClip = opaque;
+ updateLayers();
+
+ // If we know that we are going to redraw the entire framebuffer,
+ // perform a discard to let the driver know we don't need to preserve
+ // the back buffer for this frame.
+ if (mCaches.extensions.hasDiscardFramebuffer() &&
+ left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) {
+ const GLenum attachments[] = { getTargetFbo() == 0 ? GL_COLOR_EXT : GL_COLOR_ATTACHMENT0 };
+ glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
+ }
+
syncState();
+ // Functors break the tiling extension in pretty spectacular ways
+ // This ensures we don't use tiling when a functor is going to be
+ // invoked during the frame
+ mSuppressTiling = mCaches.hasRegisteredFunctors();
+
+ mTilingSnapshot = mSnapshot;
+ startTiling(mTilingSnapshot, true);
+
+ debugOverdraw(true, true);
+
if (!opaque) {
+ mCaches.enableScissor();
mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
glClear(GL_COLOR_BUFFER_BIT);
return DrawGlInfo::kStatusDrew;
@@ -202,34 +225,56 @@ void OpenGLRenderer::syncState() {
}
}
+void OpenGLRenderer::startTiling(const sp<Snapshot>& s, bool opaque) {
+ if (!mSuppressTiling) {
+ Rect* clip = mTilingSnapshot->clipRect;
+ if (s->flags & Snapshot::kFlagIsFboLayer) {
+ clip = s->clipRect;
+ }
+
+ mCaches.startTiling(clip->left, s->height - clip->bottom,
+ clip->right - clip->left, clip->bottom - clip->top, opaque);
+ }
+}
+
+void OpenGLRenderer::endTiling() {
+ if (!mSuppressTiling) mCaches.endTiling();
+}
+
void OpenGLRenderer::finish() {
+ renderOverdraw();
+ endTiling();
+
+ if (!suppressErrorChecks()) {
#if DEBUG_OPENGL
- GLenum status = GL_NO_ERROR;
- while ((status = glGetError()) != GL_NO_ERROR) {
- ALOGD("GL error from OpenGLRenderer: 0x%x", status);
- switch (status) {
- case GL_INVALID_ENUM:
- ALOGE(" GL_INVALID_ENUM");
- break;
- case GL_INVALID_VALUE:
- ALOGE(" GL_INVALID_VALUE");
- break;
- case GL_INVALID_OPERATION:
- ALOGE(" GL_INVALID_OPERATION");
- break;
- case GL_OUT_OF_MEMORY:
- ALOGE(" Out of memory!");
- break;
+ GLenum status = GL_NO_ERROR;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ ALOGD("GL error from OpenGLRenderer: 0x%x", status);
+ switch (status) {
+ case GL_INVALID_ENUM:
+ ALOGE(" GL_INVALID_ENUM");
+ break;
+ case GL_INVALID_VALUE:
+ ALOGE(" GL_INVALID_VALUE");
+ break;
+ case GL_INVALID_OPERATION:
+ ALOGE(" GL_INVALID_OPERATION");
+ break;
+ case GL_OUT_OF_MEMORY:
+ ALOGE(" Out of memory!");
+ break;
+ }
}
- }
#endif
+
#if DEBUG_MEMORY_USAGE
- mCaches.dumpMemoryUsage();
-#else
- if (mCaches.getDebugLevel() & kDebugMemory) {
mCaches.dumpMemoryUsage();
- }
+#else
+ if (mCaches.getDebugLevel() & kDebugMemory) {
+ mCaches.dumpMemoryUsage();
+ }
#endif
+ }
}
void OpenGLRenderer::interrupt() {
@@ -243,20 +288,23 @@ void OpenGLRenderer::interrupt() {
mCaches.unbindIndicesBuffer();
mCaches.resetVertexPointers();
mCaches.disbaleTexCoordsVertexArray();
+ debugOverdraw(false, false);
}
void OpenGLRenderer::resume() {
sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
-
glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
+ glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
+ debugOverdraw(true, false);
+
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- glEnable(GL_SCISSOR_TEST);
+ mCaches.scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
+ mCaches.enableScissor();
mCaches.resetScissor();
dirtyClip();
mCaches.activeTexture(0);
- glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
mCaches.blend = true;
glEnable(GL_BLEND);
@@ -264,6 +312,16 @@ void OpenGLRenderer::resume() {
glBlendEquation(GL_FUNC_ADD);
}
+void OpenGLRenderer::resumeAfterLayer() {
+ sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
+ glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
+ glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
+ debugOverdraw(true, false);
+
+ mCaches.resetScissor();
+ dirtyClip();
+}
+
void OpenGLRenderer::detachFunctor(Functor* functor) {
mFunctors.remove(functor);
}
@@ -303,10 +361,12 @@ status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
mFunctors.add(f);
}
}
+ // protect against functors binding to other buffers
+ mCaches.unbindMeshBuffer();
+ mCaches.unbindIndicesBuffer();
+ mCaches.activeTexture(0);
}
- mCaches.activeTexture(0);
-
return result;
}
@@ -314,6 +374,7 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
interrupt();
detachFunctor(functor);
+ mCaches.enableScissor();
if (mDirtyClip) {
setScissorFromClip();
}
@@ -321,13 +382,11 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
Rect clip(*mSnapshot->clipRect);
clip.snapToPixelBoundaries();
-#if RENDER_LAYERS_AS_REGIONS
// Since we don't know what the functor will draw, let's dirty
// tne entire clip region
if (hasLayer()) {
dirtyLayerUnchecked(clip, getRegion());
}
-#endif
DrawGlInfo info;
info.clipLeft = clip.left;
@@ -355,6 +414,124 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
}
///////////////////////////////////////////////////////////////////////////////
+// Debug
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::startMark(const char* name) const {
+ mCaches.startMark(0, name);
+}
+
+void OpenGLRenderer::endMark() const {
+ mCaches.endMark();
+}
+
+void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
+ if (mCaches.debugOverdraw && getTargetFbo() == 0) {
+ if (clear) {
+ mCaches.disableScissor();
+ mCaches.stencil.clear();
+ }
+ if (enable) {
+ mCaches.stencil.enableDebugWrite();
+ } else {
+ mCaches.stencil.disable();
+ }
+ }
+}
+
+void OpenGLRenderer::renderOverdraw() {
+ if (mCaches.debugOverdraw && getTargetFbo() == 0) {
+ const Rect* clip = mTilingSnapshot->clipRect;
+
+ mCaches.enableScissor();
+ mCaches.setScissor(clip->left, mTilingSnapshot->height - clip->bottom,
+ clip->right - clip->left, clip->bottom - clip->top);
+
+ mCaches.stencil.enableDebugTest(2);
+ drawColor(0x2f0000ff, SkXfermode::kSrcOver_Mode);
+ mCaches.stencil.enableDebugTest(3);
+ drawColor(0x2f00ff00, SkXfermode::kSrcOver_Mode);
+ mCaches.stencil.enableDebugTest(4);
+ drawColor(0x3fff0000, SkXfermode::kSrcOver_Mode);
+ mCaches.stencil.enableDebugTest(4, true);
+ drawColor(0x7fff0000, SkXfermode::kSrcOver_Mode);
+ mCaches.stencil.disable();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Layers
+///////////////////////////////////////////////////////////////////////////////
+
+bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
+ if (layer->deferredUpdateScheduled && layer->renderer && layer->displayList) {
+ OpenGLRenderer* renderer = layer->renderer;
+ Rect& dirty = layer->dirtyRect;
+
+ if (inFrame) {
+ endTiling();
+ debugOverdraw(false, false);
+ }
+
+ renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
+ renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, !layer->isBlend());
+ renderer->drawDisplayList(layer->displayList, dirty, DisplayList::kReplayFlag_ClipChildren);
+ renderer->finish();
+
+ if (inFrame) {
+ resumeAfterLayer();
+ startTiling(mSnapshot);
+ }
+
+ dirty.setEmpty();
+ layer->deferredUpdateScheduled = false;
+ layer->renderer = NULL;
+ layer->displayList = NULL;
+
+ return true;
+ }
+
+ return false;
+}
+
+void OpenGLRenderer::updateLayers() {
+ int count = mLayerUpdates.size();
+ if (count > 0) {
+ startMark("Layer Updates");
+
+ // Note: it is very important to update the layers in reverse order
+ for (int i = count - 1; i >= 0; i--) {
+ Layer* layer = mLayerUpdates.itemAt(i);
+ updateLayer(layer, false);
+ mCaches.resourceCache.decrementRefcount(layer);
+ }
+ mLayerUpdates.clear();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+ endMark();
+ }
+}
+
+void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
+ if (layer) {
+ mLayerUpdates.push_back(layer);
+ mCaches.resourceCache.incrementRefcount(layer);
+ }
+}
+
+void OpenGLRenderer::clearLayerUpdates() {
+ size_t count = mLayerUpdates.size();
+ if (count > 0) {
+ mCaches.resourceCache.lock();
+ for (size_t i = 0; i < count; i++) {
+ mCaches.resourceCache.decrementRefcountLocked(mLayerUpdates.itemAt(i));
+ }
+ mCaches.resourceCache.unlock();
+ mLayerUpdates.clear();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
// State management
///////////////////////////////////////////////////////////////////////////////
@@ -428,20 +605,12 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
if (p) {
alpha = p->getAlpha();
- if (!mCaches.extensions.hasFramebufferFetch()) {
- const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
- if (!isMode) {
- // Assume SRC_OVER
- mode = SkXfermode::kSrcOver_Mode;
- }
- } else {
- mode = getXfermode(p->getXfermode());
- }
+ mode = getXfermode(p->getXfermode());
} else {
mode = SkXfermode::kSrcOver_Mode;
}
- createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags, previousFbo);
+ createLayer(left, top, right, bottom, alpha, mode, flags, previousFbo);
}
return count;
@@ -449,7 +618,7 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
int alpha, int flags) {
- if (alpha >= 255 - ALPHA_THRESHOLD) {
+ if (alpha >= 255) {
return saveLayer(left, top, right, bottom, NULL, flags);
} else {
SkPaint paint;
@@ -509,44 +678,56 @@ int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bot
* buffer is left untouched until the first drawing operation. Only when
* something actually gets drawn are the layers regions cleared.
*/
-bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
- float right, float bottom, int alpha, SkXfermode::Mode mode,
- int flags, GLuint previousFbo) {
+bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
+ int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo) {
LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
// Window coordinates of the layer
+ Rect clip;
Rect bounds(left, top, right, bottom);
- if (!fboLayer) {
- mSnapshot->transform->mapRect(bounds);
-
- // Layers only make sense if they are in the framebuffer's bounds
- if (bounds.intersect(*snapshot->clipRect)) {
- // We cannot work with sub-pixels in this case
- bounds.snapToPixelBoundaries();
-
- // When the layer is not an FBO, we may use glCopyTexImage so we
- // need to make sure the layer does not extend outside the bounds
- // of the framebuffer
- if (!bounds.intersect(snapshot->previous->viewport)) {
- bounds.setEmpty();
- }
- } else {
+ Rect untransformedBounds(bounds);
+ mSnapshot->transform->mapRect(bounds);
+
+ // Layers only make sense if they are in the framebuffer's bounds
+ if (bounds.intersect(*mSnapshot->clipRect)) {
+ // We cannot work with sub-pixels in this case
+ bounds.snapToPixelBoundaries();
+
+ // When the layer is not an FBO, we may use glCopyTexImage so we
+ // need to make sure the layer does not extend outside the bounds
+ // of the framebuffer
+ if (!bounds.intersect(mSnapshot->previous->viewport)) {
bounds.setEmpty();
+ } else if (fboLayer) {
+ clip.set(bounds);
+ mat4 inverse;
+ inverse.loadInverse(*mSnapshot->transform);
+ inverse.mapRect(clip);
+ clip.snapToPixelBoundaries();
+ if (clip.intersect(untransformedBounds)) {
+ clip.translate(-left, -top);
+ bounds.set(untransformedBounds);
+ } else {
+ clip.setEmpty();
+ }
}
+ } else {
+ bounds.setEmpty();
}
if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
- bounds.getHeight() > mCaches.maxTextureSize) {
- snapshot->empty = fboLayer;
+ bounds.getHeight() > mCaches.maxTextureSize ||
+ (fboLayer && clip.isEmpty())) {
+ mSnapshot->empty = fboLayer;
} else {
- snapshot->invisible = snapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
+ mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
}
// Bail out if we won't draw in this snapshot
- if (snapshot->invisible || snapshot->empty) {
+ if (mSnapshot->invisible || mSnapshot->empty) {
return false;
}
@@ -564,23 +745,23 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
layer->setBlend(true);
// Save the layer in the snapshot
- snapshot->flags |= Snapshot::kFlagIsLayer;
- snapshot->layer = layer;
+ mSnapshot->flags |= Snapshot::kFlagIsLayer;
+ mSnapshot->layer = layer;
if (fboLayer) {
- return createFboLayer(layer, bounds, snapshot, previousFbo);
+ return createFboLayer(layer, bounds, clip, previousFbo);
} else {
// Copy the framebuffer into the layer
layer->bindTexture();
if (!bounds.isEmpty()) {
if (layer->isEmpty()) {
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
- bounds.left, snapshot->height - bounds.bottom,
+ bounds.left, mSnapshot->height - bounds.bottom,
layer->getWidth(), layer->getHeight(), 0);
layer->setEmpty(false);
} else {
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left,
- snapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight());
+ mSnapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight());
}
// Enqueue the buffer coordinates to clear the corresponding region later
@@ -591,38 +772,23 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
return true;
}
-bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot,
- GLuint previousFbo) {
+bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo) {
layer->setFbo(mCaches.fboCache.get());
-#if RENDER_LAYERS_AS_REGIONS
- snapshot->region = &snapshot->layer->region;
- snapshot->flags |= Snapshot::kFlagFboTarget;
-#endif
-
- Rect clip(bounds);
- snapshot->transform->mapRect(clip);
- clip.intersect(*snapshot->clipRect);
- clip.snapToPixelBoundaries();
- clip.intersect(snapshot->previous->viewport);
-
- mat4 inverse;
- inverse.loadInverse(*mSnapshot->transform);
+ mSnapshot->region = &mSnapshot->layer->region;
+ mSnapshot->flags |= Snapshot::kFlagFboTarget;
- inverse.mapRect(clip);
- clip.snapToPixelBoundaries();
- clip.intersect(bounds);
- clip.translate(-bounds.left, -bounds.top);
-
- snapshot->flags |= Snapshot::kFlagIsFboLayer;
- snapshot->fbo = layer->getFbo();
- snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
- snapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
- snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
- snapshot->height = bounds.getHeight();
- snapshot->flags |= Snapshot::kFlagDirtyOrtho;
- snapshot->orthoMatrix.load(mOrthoMatrix);
+ mSnapshot->flags |= Snapshot::kFlagIsFboLayer;
+ mSnapshot->fbo = layer->getFbo();
+ mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
+ mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
+ mSnapshot->height = bounds.getHeight();
+ mSnapshot->flags |= Snapshot::kFlagDirtyOrtho;
+ mSnapshot->orthoMatrix.load(mOrthoMatrix);
+ endTiling();
+ debugOverdraw(false, false);
// Bind texture to FBO
glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
layer->bindTexture();
@@ -636,22 +802,10 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> sna
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->getTexture(), 0);
-#if DEBUG_LAYERS_AS_REGIONS
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Framebuffer incomplete (GL error code 0x%x)", status);
-
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- layer->deleteTexture();
- mCaches.fboCache.put(layer->getFbo());
-
- delete layer;
-
- return false;
- }
-#endif
+ startTiling(mSnapshot);
// Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
+ mCaches.enableScissor();
mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
glClear(GL_COLOR_BUFFER_BIT);
@@ -677,11 +831,15 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
if (fboLayer) {
+ endTiling();
+
// Detach the texture from the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
-
// Unbind current FBO and restore previous one
glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
+ debugOverdraw(true, false);
+
+ startTiling(previous);
}
Layer* layer = current->layer;
@@ -730,8 +888,7 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
// Failing to add the layer to the cache should happen only if the layer is too large
if (!mCaches.layerCache.put(layer)) {
LAYER_LOGD("Deleting layer");
- layer->deleteTexture();
- delete layer;
+ Caches::getInstance().resourceCache.decrementRefcount(layer);
}
}
@@ -825,7 +982,6 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap)
}
void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
-#if RENDER_LAYERS_AS_REGIONS
if (layer->region.isRect()) {
layer->setRegionAsRect();
@@ -906,9 +1062,6 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
layer->region.clear();
}
-#else
- composeLayerRect(layer, rect);
-#endif
}
void OpenGLRenderer::drawRegionRects(const Region& region) {
@@ -939,27 +1092,22 @@ void OpenGLRenderer::drawRegionRects(const Region& region) {
void OpenGLRenderer::dirtyLayer(const float left, const float top,
const float right, const float bottom, const mat4 transform) {
-#if RENDER_LAYERS_AS_REGIONS
if (hasLayer()) {
Rect bounds(left, top, right, bottom);
transform.mapRect(bounds);
dirtyLayerUnchecked(bounds, getRegion());
}
-#endif
}
void OpenGLRenderer::dirtyLayer(const float left, const float top,
const float right, const float bottom) {
-#if RENDER_LAYERS_AS_REGIONS
if (hasLayer()) {
Rect bounds(left, top, right, bottom);
dirtyLayerUnchecked(bounds, getRegion());
}
-#endif
}
void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
-#if RENDER_LAYERS_AS_REGIONS
if (bounds.intersect(*mSnapshot->clipRect)) {
bounds.snapToPixelBoundaries();
android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
@@ -967,7 +1115,6 @@ void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
region->orSelf(dirty);
}
}
-#endif
}
void OpenGLRenderer::clearLayerRegions() {
@@ -982,7 +1129,7 @@ void OpenGLRenderer::clearLayerRegions() {
// The list contains bounds that have already been clipped
// against their initial clip rect, and the current clip
// is likely different so we need to disable clipping here
- glDisable(GL_SCISSOR_TEST);
+ bool scissorChanged = mCaches.disableScissor();
Vertex mesh[count * 6];
Vertex* vertex = mesh;
@@ -1010,7 +1157,7 @@ void OpenGLRenderer::clearLayerRegions() {
glDrawArrays(GL_TRIANGLES, 0, count * 6);
- glEnable(GL_SCISSOR_TEST);
+ if (scissorChanged) mCaches.enableScissor();
} else {
for (uint32_t i = 0; i < count; i++) {
delete mLayers.itemAt(i);
@@ -1067,17 +1214,17 @@ void OpenGLRenderer::setScissorFromClip() {
Rect clip(*mSnapshot->clipRect);
clip.snapToPixelBoundaries();
- mCaches.setScissor(clip.left, mSnapshot->height - clip.bottom,
- clip.getWidth(), clip.getHeight());
-
- mDirtyClip = false;
+ if (mCaches.setScissor(clip.left, mSnapshot->height - clip.bottom,
+ clip.getWidth(), clip.getHeight())) {
+ mDirtyClip = false;
+ }
}
const Rect& OpenGLRenderer::getClipBounds() {
return mSnapshot->getLocalClip();
}
-bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
+bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom) {
if (mSnapshot->isIgnored()) {
return true;
}
@@ -1092,6 +1239,51 @@ bool OpenGLRenderer::quickReject(float left, float top, float right, float botto
return !clipRect.intersects(r);
}
+bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom,
+ Rect& transformed, Rect& clip) {
+ if (mSnapshot->isIgnored()) {
+ return true;
+ }
+
+ transformed.set(left, top, right, bottom);
+ mSnapshot->transform->mapRect(transformed);
+ transformed.snapToPixelBoundaries();
+
+ clip.set(*mSnapshot->clipRect);
+ clip.snapToPixelBoundaries();
+
+ return !clip.intersects(transformed);
+}
+
+bool OpenGLRenderer::quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint) {
+ if (paint->getStyle() != SkPaint::kFill_Style) {
+ float outset = paint->getStrokeWidth() * 0.5f;
+ return quickReject(left - outset, top - outset, right + outset, bottom + outset);
+ } else {
+ return quickReject(left, top, right, bottom);
+ }
+}
+
+bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
+ if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
+ return true;
+ }
+
+ Rect r(left, top, right, bottom);
+ mSnapshot->transform->mapRect(r);
+ r.snapToPixelBoundaries();
+
+ Rect clipRect(*mSnapshot->clipRect);
+ clipRect.snapToPixelBoundaries();
+
+ bool rejected = !clipRect.intersects(r);
+ if (!isDeferred() && !rejected) {
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled || !clipRect.contains(r));
+ }
+
+ return rejected;
+}
+
bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
bool clipped = mSnapshot->clip(left, top, right, bottom, op);
if (clipped) {
@@ -1109,6 +1301,8 @@ Rect* OpenGLRenderer::getClipRect() {
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::setupDraw(bool clear) {
+ // TODO: It would be best if we could do this before quickReject()
+ // changes the scissor test state
if (clear) clearLayerRegions();
if (mDirtyClip) {
setScissorFromClip();
@@ -1134,10 +1328,14 @@ void OpenGLRenderer::setupDrawNoTexture() {
mCaches.disbaleTexCoordsVertexArray();
}
-void OpenGLRenderer::setupDrawAALine() {
+void OpenGLRenderer::setupDrawAA() {
mDescription.isAA = true;
}
+void OpenGLRenderer::setupDrawVertexShape() {
+ mDescription.isVertexShape = true;
+}
+
void OpenGLRenderer::setupDrawPoint(float pointSize) {
mDescription.isPoint = true;
mDescription.pointSize = pointSize;
@@ -1171,6 +1369,10 @@ void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) {
mSetShaderColor = mDescription.setAlpha8Color(mColorR, mColorG, mColorB, mColorA);
}
+void OpenGLRenderer::setupDrawTextGamma(const SkPaint* paint) {
+ mCaches.fontRenderer->describe(mDescription, paint);
+}
+
void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) {
mColorA = a;
mColorR = r;
@@ -1212,8 +1414,8 @@ void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool s
// When the blending mode is kClear_Mode, we need to use a modulate color
// argb=1,0,0,0
accountForClear(mode);
- chooseBlending(blend || (mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode,
- mDescription, swapSrcDst);
+ chooseBlending(blend || (mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()) ||
+ (mColorFilter && mColorFilter->blend()), mode, mDescription, swapSrcDst);
}
void OpenGLRenderer::setupDrawProgram() {
@@ -1298,9 +1500,13 @@ void OpenGLRenderer::setupDrawColorFilterUniforms() {
}
}
+void OpenGLRenderer::setupDrawTextGammaUniforms() {
+ mCaches.fontRenderer->setupProgram(mDescription, mCaches.currentProgram);
+}
+
void OpenGLRenderer::setupDrawSimpleMesh() {
bool force = mCaches.bindMeshBuffer();
- mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, 0);
+ mCaches.bindPositionVertexPointer(force, 0);
mCaches.unbindIndicesBuffer();
}
@@ -1333,9 +1539,9 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint v
force = mCaches.unbindMeshBuffer();
}
- mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, vertices);
+ mCaches.bindPositionVertexPointer(force, vertices);
if (mCaches.currentProgram->texCoords >= 0) {
- mCaches.bindTexCoordsVertexPointer(force, mCaches.currentProgram->texCoords, texCoords);
+ mCaches.bindTexCoordsVertexPointer(force, texCoords);
}
mCaches.unbindIndicesBuffer();
@@ -1343,16 +1549,15 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint v
void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords) {
bool force = mCaches.unbindMeshBuffer();
- mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, vertices);
+ mCaches.bindPositionVertexPointer(force, vertices);
if (mCaches.currentProgram->texCoords >= 0) {
- mCaches.bindTexCoordsVertexPointer(force, mCaches.currentProgram->texCoords, texCoords);
+ mCaches.bindTexCoordsVertexPointer(force, texCoords);
}
}
void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
bool force = mCaches.unbindMeshBuffer();
- mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
- vertices, gVertexStride);
+ mCaches.bindPositionVertexPointer(force, vertices, gVertexStride);
mCaches.unbindIndicesBuffer();
}
@@ -1370,8 +1575,7 @@ void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
GLvoid* lengthCoords, float boundaryWidthProportion, int& widthSlot, int& lengthSlot) {
bool force = mCaches.unbindMeshBuffer();
- mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
- vertices, gAAVertexStride);
+ mCaches.bindPositionVertexPointer(force, vertices, gAAVertexStride);
mCaches.resetTexCoordsVertexPointer();
mCaches.unbindIndicesBuffer();
@@ -1385,10 +1589,6 @@ void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth");
glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
-
- // Setting the inverse value saves computations per-fragment in the shader
- int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
- glUniform1f(inverseBoundaryWidthSlot, 1.0f / boundaryWidthProportion);
}
void OpenGLRenderer::finishDrawAALine(const int widthSlot, const int lengthSlot) {
@@ -1554,11 +1754,7 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
float right = FLT_MIN;
float bottom = FLT_MIN;
-#if RENDER_LAYERS_AS_REGIONS
const bool hasActiveLayer = hasLayer();
-#else
- const bool hasActiveLayer = false;
-#endif
// TODO: Support the colors array
TextureVertex mesh[count];
@@ -1589,7 +1785,6 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
TextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1);
TextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2);
-#if RENDER_LAYERS_AS_REGIONS
if (hasActiveLayer) {
// TODO: This could be optimized to avoid unnecessary ops
left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx])));
@@ -1597,15 +1792,12 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx])));
bottom = fmaxf(bottom, fmaxf(vertices[ay], fmaxf(vertices[by], vertices[cy])));
}
-#endif
}
}
-#if RENDER_LAYERS_AS_REGIONS
if (hasActiveLayer) {
dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
}
-#endif
drawTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f,
mode, texture->blend, &mesh[0].position[0], &mesh[0].texture[0],
@@ -1674,10 +1866,23 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
float left, float top, float right, float bottom, SkPaint* paint) {
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndModeDirect(paint, &alpha, &mode);
+
+ return drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors,
+ left, top, right, bottom, alpha, mode);
+}
+
+status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
+ const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode) {
if (quickReject(left, top, right, bottom)) {
return DrawGlInfo::kStatusDone;
}
+ alpha *= mSnapshot->alpha;
+
mCaches.activeTexture(0);
Texture* texture = mCaches.textureCache.get(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
@@ -1685,16 +1890,11 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const
texture->setWrap(GL_CLAMP_TO_EDGE, true);
texture->setFilter(GL_LINEAR, true);
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
const bool pureTranslate = mSnapshot->transform->isPureTranslate();
-#if RENDER_LAYERS_AS_REGIONS
// Mark the current layer dirty where we are going to draw the patch
if (hasLayer() && mesh->hasEmptyQuads) {
const float offsetX = left + mSnapshot->transform->getTranslateX();
@@ -1712,7 +1912,6 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const
}
}
}
-#endif
if (CC_LIKELY(pureTranslate)) {
const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
@@ -1734,83 +1933,67 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const
}
/**
- * This function uses a similar approach to that of AA lines in the drawLines() function.
- * We expand the rectangle by a half pixel in screen space on all sides, and use a fragment
- * shader to compute the translucency of the color, determined by whether a given pixel is
- * within that boundary region and how far into the region it is.
+ * Renders a convex path via tessellation. For AA paths, this function uses a similar approach to
+ * that of AA lines in the drawLines() function. We expand the convex path by a half pixel in
+ * screen space in all directions. However, instead of using a fragment shader to compute the
+ * translucency of the color from its position, we simply use a varying parameter to define how far
+ * a given pixel is from the edge. For non-AA paths, the expansion and alpha varying are not used.
+ *
+ * Doesn't yet support joins, caps, or path effects.
*/
-void OpenGLRenderer::drawAARect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode) {
- float inverseScaleX = 1.0f;
- float inverseScaleY = 1.0f;
- // The quad that we use needs to account for scaling.
- if (CC_UNLIKELY(!mSnapshot->transform->isPureTranslate())) {
- Matrix4 *mat = mSnapshot->transform;
- float m00 = mat->data[Matrix4::kScaleX];
- float m01 = mat->data[Matrix4::kSkewY];
- float m02 = mat->data[2];
- float m10 = mat->data[Matrix4::kSkewX];
- float m11 = mat->data[Matrix4::kScaleX];
- float m12 = mat->data[6];
- float scaleX = sqrt(m00 * m00 + m01 * m01);
- float scaleY = sqrt(m10 * m10 + m11 * m11);
- inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
- inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
+void OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) {
+ int color = paint->getColor();
+ SkPaint::Style style = paint->getStyle();
+ SkXfermode::Mode mode = getXfermode(paint->getXfermode());
+ bool isAA = paint->isAntiAlias();
+
+ VertexBuffer vertexBuffer;
+ // TODO: try clipping large paths to viewport
+ PathRenderer::convexPathVertices(path, paint, mSnapshot->transform, vertexBuffer);
+
+ if (!vertexBuffer.getSize()) {
+ // no vertices to draw
+ return;
}
setupDraw();
setupDrawNoTexture();
- setupDrawAALine();
+ if (isAA) setupDrawAA();
+ setupDrawVertexShape();
setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha);
setupDrawColorFilter();
setupDrawShader();
- setupDrawBlending(true, mode);
+ setupDrawBlending(isAA, mode);
setupDrawProgram();
- setupDrawModelViewIdentity(true);
+ setupDrawModelViewIdentity();
setupDrawColorUniforms();
setupDrawColorFilterUniforms();
setupDrawShaderIdentityUniforms();
- AAVertex rects[4];
- AAVertex* aaVertices = &rects[0];
- void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
- void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
+ void* vertices = vertexBuffer.getBuffer();
+ bool force = mCaches.unbindMeshBuffer();
+ mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride);
+ mCaches.resetTexCoordsVertexPointer();
+ mCaches.unbindIndicesBuffer();
- float boundarySizeX = .5 * inverseScaleX;
- float boundarySizeY = .5 * inverseScaleY;
+ int alphaSlot = -1;
+ if (isAA) {
+ void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset;
+ alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
- // Adjust the rect by the AA boundary padding
- left -= boundarySizeX;
- right += boundarySizeX;
- top -= boundarySizeY;
- bottom += boundarySizeY;
+ // TODO: avoid enable/disable in back to back uses of the alpha attribute
+ glEnableVertexAttribArray(alphaSlot);
+ glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
+ }
- float width = right - left;
- float height = bottom - top;
+ SkRect bounds = PathRenderer::computePathBounds(path, paint);
+ dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *mSnapshot->transform);
- int widthSlot;
- int lengthSlot;
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getSize());
- float boundaryWidthProportion = (width != 0) ? (2 * boundarySizeX) / width : 0;
- float boundaryHeightProportion = (height != 0) ? (2 * boundarySizeY) / height : 0;
- setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
- boundaryWidthProportion, widthSlot, lengthSlot);
-
- int boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
- int inverseBoundaryLengthSlot = mCaches.currentProgram->getUniform("inverseBoundaryLength");
- glUniform1f(boundaryLengthSlot, boundaryHeightProportion);
- glUniform1f(inverseBoundaryLengthSlot, (1.0f / boundaryHeightProportion));
-
- if (!quickReject(left, top, right, bottom)) {
- AAVertex::set(aaVertices++, left, bottom, 1, 1);
- AAVertex::set(aaVertices++, left, top, 1, 0);
- AAVertex::set(aaVertices++, right, bottom, 0, 1);
- AAVertex::set(aaVertices++, right, top, 0, 0);
- dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ if (isAA) {
+ glDisableVertexAttribArray(alphaSlot);
}
-
- finishDrawAALine(widthSlot, lengthSlot);
}
/**
@@ -1864,10 +2047,8 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
Matrix4 *mat = mSnapshot->transform;
float m00 = mat->data[Matrix4::kScaleX];
float m01 = mat->data[Matrix4::kSkewY];
- float m02 = mat->data[2];
float m10 = mat->data[Matrix4::kSkewX];
- float m11 = mat->data[Matrix4::kScaleX];
- float m12 = mat->data[6];
+ float m11 = mat->data[Matrix4::kScaleY];
float scaleX = sqrtf(m00 * m00 + m01 * m01);
float scaleY = sqrtf(m10 * m10 + m11 * m11);
@@ -1882,17 +2063,20 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
}
getAlphaAndMode(paint, &alpha, &mode);
+
+ mCaches.enableScissor();
+
setupDraw();
setupDrawNoTexture();
if (isAA) {
- setupDrawAALine();
+ setupDrawAA();
}
setupDrawColor(paint->getColor(), alpha);
setupDrawColorFilter();
setupDrawShader();
setupDrawBlending(isAA, mode);
setupDrawProgram();
- setupDrawModelViewIdentity(true);
+ setupDrawModelViewIdentity();
setupDrawColorUniforms();
setupDrawColorFilterUniforms();
setupDrawShaderIdentityUniforms();
@@ -1924,7 +2108,7 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
// This value is used in the fragment shader to determine how to fill fragments.
// We will need to calculate the actual width proportion on each segment for
// scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
- float boundaryWidthProportion = 1 / (2 * halfStrokeWidth);
+ float boundaryWidthProportion = .5 - 1 / (2 * halfStrokeWidth);
setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
boundaryWidthProportion, widthSlot, lengthSlot);
}
@@ -1933,9 +2117,7 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
Vertex* prevVertex = NULL;
int boundaryLengthSlot = -1;
- int inverseBoundaryLengthSlot = -1;
int boundaryWidthSlot = -1;
- int inverseBoundaryWidthSlot = -1;
for (int i = 0; i < count; i += 4) {
// a = start point, b = end point
@@ -1948,6 +2130,10 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
// Find the normal to the line
vec2 n = (b - a).copyNormalized() * halfStrokeWidth;
+ float x = n.x;
+ n.x = -n.y;
+ n.y = x;
+
if (isHairLine) {
if (isAA) {
float wideningFactor;
@@ -1972,14 +2158,10 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
float extendedNLength = extendedN.length();
// We need to set this value on the shader prior to drawing
- boundaryWidthProportion = extendedNLength / (halfStrokeWidth + extendedNLength);
+ boundaryWidthProportion = .5 - extendedNLength / (halfStrokeWidth + extendedNLength);
n += extendedN;
}
- float x = n.x;
- n.x = -n.y;
- n.y = x;
-
// aa lines expand the endpoint vertices to encompass the AA boundary
if (isAA) {
vec2 abVector = (b - a);
@@ -1990,9 +2172,9 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
abVector.x *= inverseScaleX;
abVector.y *= inverseScaleY;
float abLength = abVector.length();
- boundaryLengthProportion = abLength / (length + abLength);
+ boundaryLengthProportion = .5 - abLength / (length + abLength);
} else {
- boundaryLengthProportion = .5 / (length + 1);
+ boundaryLengthProportion = .5 - .5 / (length + 1);
}
abVector /= 2;
@@ -2012,7 +2194,7 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y)));
const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y)));
- if (!quickReject(left, top, right, bottom)) {
+ if (!quickRejectNoScissor(left, top, right, bottom)) {
if (!isAA) {
if (prevVertex != NULL) {
// Issue two repeat vertices to create degenerate triangles to bridge
@@ -2038,22 +2220,16 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
if (boundaryWidthSlot < 0) {
boundaryWidthSlot =
mCaches.currentProgram->getUniform("boundaryWidth");
- inverseBoundaryWidthSlot =
- mCaches.currentProgram->getUniform("inverseBoundaryWidth");
}
glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
- glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
}
if (boundaryLengthSlot < 0) {
boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
- inverseBoundaryLengthSlot =
- mCaches.currentProgram->getUniform("inverseBoundaryLength");
}
glUniform1f(boundaryLengthSlot, boundaryLengthProportion);
- glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLengthProportion));
if (prevAAVertex != NULL) {
// Issue two repeat vertices to create degenerate triangles to bridge
@@ -2117,6 +2293,10 @@ status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
TextureVertex pointsData[verticesCount];
TextureVertex* vertex = &pointsData[0];
+ // TODO: We should optimize this method to not generate vertices for points
+ // that lie outside of the clip.
+ mCaches.enableScissor();
+
setupDraw();
setupDrawNoTexture();
setupDrawPoint(strokeWidth);
@@ -2175,30 +2355,75 @@ status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* tex
}
status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+ float rx, float ry, SkPaint* p) {
+ if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p)) {
+ return DrawGlInfo::kStatusDone;
+ }
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect(
- right - left, bottom - top, rx, ry, paint);
- return drawShape(left, top, texture, paint);
+ if (p->getPathEffect() != 0) {
+ mCaches.activeTexture(0);
+ const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect(
+ right - left, bottom - top, rx, ry, p);
+ return drawShape(left, top, texture, p);
+ }
+
+ SkPath path;
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
+ float outset = p->getStrokeWidth() / 2;
+ rect.outset(outset, outset);
+ rx += outset;
+ ry += outset;
+ }
+ path.addRoundRect(rect, rx, ry);
+ drawConvexPath(path, p);
+
+ return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) {
+ if (mSnapshot->isIgnored() || quickRejectPreStroke(x - radius, y - radius,
+ x + radius, y + radius, p)) {
+ return DrawGlInfo::kStatusDone;
+ }
+ if (p->getPathEffect() != 0) {
+ mCaches.activeTexture(0);
+ const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, p);
+ return drawShape(x - radius, y - radius, texture, p);
+ }
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, paint);
- return drawShape(x - radius, y - radius, texture, paint);
+ SkPath path;
+ if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
+ path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
+ } else {
+ path.addCircle(x, y, radius);
+ }
+ drawConvexPath(path, p);
+
+ return DrawGlInfo::kStatusDrew;
}
status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
- SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+ SkPaint* p) {
+ if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p)) {
+ return DrawGlInfo::kStatusDone;
+ }
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, paint);
- return drawShape(left, top, texture, paint);
+ if (p->getPathEffect() != 0) {
+ mCaches.activeTexture(0);
+ const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, p);
+ return drawShape(left, top, texture, p);
+ }
+
+ SkPath path;
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
+ rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
+ }
+ path.addOval(rect);
+ drawConvexPath(path, p);
+
+ return DrawGlInfo::kStatusDrew;
}
status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
@@ -2215,45 +2440,84 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto
return drawShape(left, top, texture, paint);
}
-status_t OpenGLRenderer::drawRectAsShape(float left, float top, float right, float bottom,
- SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
-
- mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.rectShapeCache.getRect(right - left, bottom - top, paint);
- return drawShape(left, top, texture, paint);
-}
+// See SkPaintDefaults.h
+#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) {
- if (p->getStyle() != SkPaint::kFill_Style) {
- return drawRectAsShape(left, top, right, bottom, p);
- }
-
- if (quickReject(left, top, right, bottom)) {
+ if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p)) {
return DrawGlInfo::kStatusDone;
}
- SkXfermode::Mode mode;
- if (!mCaches.extensions.hasFramebufferFetch()) {
- const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
- if (!isMode) {
- // Assume SRC_OVER
- mode = SkXfermode::kSrcOver_Mode;
+ if (p->getStyle() != SkPaint::kFill_Style) {
+ // only fill style is supported by drawConvexPath, since others have to handle joins
+ if (p->getPathEffect() != 0 || p->getStrokeJoin() != SkPaint::kMiter_Join ||
+ p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
+ mCaches.activeTexture(0);
+ const PathTexture* texture =
+ mCaches.rectShapeCache.getRect(right - left, bottom - top, p);
+ return drawShape(left, top, texture, p);
}
- } else {
- mode = getXfermode(p->getXfermode());
+
+ SkPath path;
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
+ rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
+ }
+ path.addRect(rect);
+ drawConvexPath(path, p);
+
+ return DrawGlInfo::kStatusDrew;
}
- int color = p->getColor();
if (p->isAntiAlias() && !mSnapshot->transform->isSimple()) {
- drawAARect(left, top, right, bottom, color, mode);
+ SkPath path;
+ path.addRect(left, top, right, bottom);
+ drawConvexPath(path, p);
} else {
- drawColorRect(left, top, right, bottom, color, mode);
+ drawColorRect(left, top, right, bottom, p->getColor(), getXfermode(p->getXfermode()));
}
return DrawGlInfo::kStatusDrew;
}
+void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
+ const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
+ float x, float y) {
+ mCaches.activeTexture(0);
+
+ // NOTE: The drop shadow will not perform gamma correction
+ // if shader-based correction is enabled
+ mCaches.dropShadowCache.setFontRenderer(fontRenderer);
+ const ShadowTexture* shadow = mCaches.dropShadowCache.get(
+ paint, text, bytesCount, count, mShadowRadius, positions);
+ const AutoTexture autoCleanup(shadow);
+
+ const float sx = x - shadow->left + mShadowDx;
+ const float sy = y - shadow->top + mShadowDy;
+
+ const int shadowAlpha = ((mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
+ int shadowColor = mShadowColor;
+ if (mShader) {
+ shadowColor = 0xffffffff;
+ }
+
+ setupDraw();
+ setupDrawWithTexture(true);
+ setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
+ setupDrawColorFilter();
+ setupDrawShader();
+ setupDrawBlending(true, mode);
+ setupDrawProgram();
+ setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height);
+ setupDrawTexture(shadow->id);
+ setupDrawPureColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawShaderUniforms();
+ setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+}
+
status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint) {
if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
@@ -2274,7 +2538,7 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
}
- FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
paint->getTextSize());
@@ -2282,6 +2546,11 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
+ if (CC_UNLIKELY(mHasShadow)) {
+ drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
+ 0.0f, 0.0f);
+ }
+
// Pick the appropriate texture filtering
bool linearFilter = mSnapshot->transform->changesBounds();
if (pureTranslate && !linearFilter) {
@@ -2290,6 +2559,7 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
mCaches.activeTexture(0);
setupDraw();
+ setupDrawTextGamma(paint);
setupDrawDirtyRegionsDisabled();
setupDrawWithTexture(true);
setupDrawAlpha8Color(paint->getColor(), alpha);
@@ -2302,33 +2572,28 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();
setupDrawShaderUniforms(pureTranslate);
+ setupDrawTextGammaUniforms();
const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip();
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
-#if RENDER_LAYERS_AS_REGIONS
const bool hasActiveLayer = hasLayer();
-#else
- const bool hasActiveLayer = false;
-#endif
if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
positions, hasActiveLayer ? &bounds : NULL)) {
-#if RENDER_LAYERS_AS_REGIONS
if (hasActiveLayer) {
if (!pureTranslate) {
mSnapshot->transform->mapRect(bounds);
}
dirtyLayerUnchecked(bounds, getRegion());
}
-#endif
}
return DrawGlInfo::kStatusDrew;
}
status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, SkPaint* paint, float length) {
+ float x, float y, const float* positions, SkPaint* paint, float length) {
if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
(paint->getAlpha() * mSnapshot->alpha == 0 && paint->getXfermode() == NULL)) {
return DrawGlInfo::kStatusDone;
@@ -2361,10 +2626,11 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
}
#if DEBUG_GLYPHS
- ALOGD("OpenGLRenderer drawText() with FontID=%d", SkTypeface::UniqueID(paint->getTypeface()));
+ ALOGD("OpenGLRenderer drawText() with FontID=%d",
+ SkTypeface::UniqueID(paint->getTypeface()));
#endif
- FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
paint->getTextSize());
@@ -2373,37 +2639,8 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
getAlphaAndMode(paint, &alpha, &mode);
if (CC_UNLIKELY(mHasShadow)) {
- mCaches.activeTexture(0);
-
- mCaches.dropShadowCache.setFontRenderer(fontRenderer);
- const ShadowTexture* shadow = mCaches.dropShadowCache.get(
- paint, text, bytesCount, count, mShadowRadius);
- const AutoTexture autoCleanup(shadow);
-
- const float sx = oldX - shadow->left + mShadowDx;
- const float sy = oldY - shadow->top + mShadowDy;
-
- const int shadowAlpha = ((mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
- int shadowColor = mShadowColor;
- if (mShader) {
- shadowColor = 0xffffffff;
- }
-
- setupDraw();
- setupDrawWithTexture(true);
- setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(true, mode);
- setupDrawProgram();
- setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height);
- setupDrawTexture(shadow->id);
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawShaderUniforms();
- setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
+ oldX, oldY);
}
// Pick the appropriate texture filtering
@@ -2415,6 +2652,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
// The font renderer will always use texture unit 0
mCaches.activeTexture(0);
setupDraw();
+ setupDrawTextGamma(paint);
setupDrawDirtyRegionsDisabled();
setupDrawWithTexture(true);
setupDrawAlpha8Color(paint->getColor(), alpha);
@@ -2429,26 +2667,30 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();
setupDrawShaderUniforms(pureTranslate);
+ setupDrawTextGammaUniforms();
- const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip();
+ const Rect* clip = pureTranslate ? mSnapshot->clipRect :
+ (mSnapshot->hasPerspectiveTransform() ? NULL : &mSnapshot->getLocalClip());
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
-#if RENDER_LAYERS_AS_REGIONS
const bool hasActiveLayer = hasLayer();
-#else
- const bool hasActiveLayer = false;
-#endif
- if (fontRenderer.renderText(paint, clip, text, 0, bytesCount, count, x, y,
- hasActiveLayer ? &bounds : NULL)) {
-#if RENDER_LAYERS_AS_REGIONS
- if (hasActiveLayer) {
- if (!pureTranslate) {
- mSnapshot->transform->mapRect(bounds);
- }
- dirtyLayerUnchecked(bounds, getRegion());
+ bool status;
+ if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
+ SkPaint paintCopy(*paint);
+ paintCopy.setTextAlign(SkPaint::kLeft_Align);
+ status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
+ positions, hasActiveLayer ? &bounds : NULL);
+ } else {
+ status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
+ positions, hasActiveLayer ? &bounds : NULL);
+ }
+
+ if (status && hasActiveLayer) {
+ if (!pureTranslate) {
+ mSnapshot->transform->mapRect(bounds);
}
-#endif
+ dirtyLayerUnchecked(bounds, getRegion());
}
drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
@@ -2463,7 +2705,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
return DrawGlInfo::kStatusDone;
}
- FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
paint->getTextSize());
@@ -2473,6 +2715,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
mCaches.activeTexture(0);
setupDraw();
+ setupDrawTextGamma(paint);
setupDrawDirtyRegionsDisabled();
setupDrawWithTexture(true);
setupDrawAlpha8Color(paint->getColor(), alpha);
@@ -2485,24 +2728,19 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();
setupDrawShaderUniforms(false);
+ setupDrawTextGammaUniforms();
const Rect* clip = &mSnapshot->getLocalClip();
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
-#if RENDER_LAYERS_AS_REGIONS
const bool hasActiveLayer = hasLayer();
-#else
- const bool hasActiveLayer = false;
-#endif
if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
hOffset, vOffset, hasActiveLayer ? &bounds : NULL)) {
-#if RENDER_LAYERS_AS_REGIONS
if (hasActiveLayer) {
mSnapshot->transform->mapRect(bounds);
dirtyLayerUnchecked(bounds, getRegion());
}
-#endif
}
return DrawGlInfo::kStatusDrew;
@@ -2527,43 +2765,35 @@ status_t OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
}
status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) {
- if (!layer || quickReject(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight())) {
+ if (!layer) {
return DrawGlInfo::kStatusDone;
}
- if (layer->deferredUpdateScheduled && layer->renderer && layer->displayList) {
- OpenGLRenderer* renderer = layer->renderer;
- Rect& dirty = layer->dirtyRect;
+ Rect transformed;
+ Rect clip;
+ const bool rejected = quickRejectNoScissor(x, y,
+ x + layer->layer.getWidth(), y + layer->layer.getHeight(), transformed, clip);
- interrupt();
- renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
- renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, !layer->isBlend());
- renderer->drawDisplayList(layer->displayList, dirty, DisplayList::kReplayFlag_ClipChildren);
- renderer->finish();
- resume();
+ if (rejected) {
+ return DrawGlInfo::kStatusDone;
+ }
- dirty.setEmpty();
- layer->deferredUpdateScheduled = false;
- layer->renderer = NULL;
- layer->displayList = NULL;
+ bool debugLayerUpdate = false;
+ if (updateLayer(layer, true)) {
+ debugLayerUpdate = mCaches.debugLayersUpdates;
}
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled || !clip.contains(transformed));
mCaches.activeTexture(0);
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- layer->setAlpha(alpha, mode);
-
-#if RENDER_LAYERS_AS_REGIONS
if (CC_LIKELY(!layer->region.isEmpty())) {
+ SkiaColorFilter* oldFilter = mColorFilter;
+ mColorFilter = layer->getColorFilter();
+
if (layer->region.isRect()) {
composeLayerRect(layer, layer->regionRect);
} else if (layer->mesh) {
- const float a = alpha / 255.0f;
- const Rect& rect = layer->layer;
-
+ const float a = layer->getAlpha() / 255.0f;
setupDraw();
setupDrawWithTexture();
setupDrawColor(a, a, a, a);
@@ -2574,12 +2804,12 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* pain
setupDrawColorFilterUniforms();
setupDrawTexture(layer->getTexture());
if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) {
- x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
- y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
+ int tx = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
+ int ty = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
layer->setFilter(GL_NEAREST);
- setupDrawModelViewTranslate(x, y,
- x + layer->layer.getWidth(), y + layer->layer.getHeight(), true);
+ setupDrawModelViewTranslate(tx, ty,
+ tx + layer->layer.getWidth(), ty + layer->layer.getHeight(), true);
} else {
layer->setFilter(GL_LINEAR);
setupDrawModelViewTranslate(x, y,
@@ -2596,11 +2826,14 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* pain
drawRegionRects(layer->region);
#endif
}
+
+ mColorFilter = oldFilter;
+
+ if (debugLayerUpdate) {
+ drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(),
+ 0x7f00ff00, SkXfermode::kSrcOver_Mode);
+ }
}
-#else
- const Rect r(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight());
- composeLayerRect(layer, r);
-#endif
return DrawGlInfo::kStatusDrew;
}
@@ -2723,23 +2956,11 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float
underlineWidth = paintCopy.measureText(text, bytesCount);
}
- float offsetX = 0;
- switch (paintCopy.getTextAlign()) {
- case SkPaint::kCenter_Align:
- offsetX = underlineWidth * 0.5f;
- break;
- case SkPaint::kRight_Align:
- offsetX = underlineWidth;
- break;
- default:
- break;
- }
-
if (CC_LIKELY(underlineWidth > 0.0f)) {
const float textSize = paintCopy.getTextSize();
const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
- const float left = x - offsetX;
+ const float left = x;
float top = 0.0f;
int linesCount = 0;
@@ -2865,7 +3086,7 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
// the blending, turn blending off here
// If the blend mode cannot be implemented using shaders, fall
// back to the default SrcOver blend mode instead
- if CC_UNLIKELY((mode > SkXfermode::kScreen_Mode)) {
+ if (CC_UNLIKELY(mode > SkXfermode::kScreen_Mode)) {
if (CC_UNLIKELY(mCaches.extensions.hasFramebufferFetch())) {
description.framebufferMode = mode;
description.swapSrcDst = swapSrcDst;
@@ -2918,30 +3139,9 @@ void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, flo
}
void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
- if (paint) {
- *mode = getXfermode(paint->getXfermode());
-
- // Skia draws using the color's alpha channel if < 255
- // Otherwise, it uses the paint's alpha
- int color = paint->getColor();
- *alpha = (color >> 24) & 0xFF;
- if (*alpha == 255) {
- *alpha = paint->getAlpha();
- }
- } else {
- *mode = SkXfermode::kSrcOver_Mode;
- *alpha = 255;
- }
+ getAlphaAndModeDirect(paint, alpha, mode);
*alpha *= mSnapshot->alpha;
}
-SkXfermode::Mode OpenGLRenderer::getXfermode(SkXfermode* mode) {
- SkXfermode::Mode resultMode;
- if (!SkXfermode::AsMode(mode, &resultMode)) {
- resultMode = SkXfermode::kSrcOver_Mode;
- }
- return resultMode;
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 7aac87c3e691..a40d69ad2c91 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -63,16 +63,74 @@ public:
ANDROID_API OpenGLRenderer();
virtual ~OpenGLRenderer();
+ /**
+ * Read externally defined properties to control the behavior
+ * of the renderer.
+ */
+ ANDROID_API void initProperties();
+
+ /**
+ * Indicates whether this renderer executes drawing commands immediately.
+ * If this method returns true, the drawing commands will be executed
+ * later.
+ */
virtual bool isDeferred();
+ /**
+ * Sets the dimension of the underlying drawing surface. This method must
+ * be called at least once every time the drawing surface changes size.
+ *
+ * @param width The width in pixels of the underlysing surface
+ * @param height The height in pixels of the underlysing surface
+ */
virtual void setViewport(int width, int height);
+ /**
+ * Prepares the renderer to draw a frame. This method must be invoked
+ * at the beginning of each frame. When this method is invoked, the
+ * entire drawing surface is assumed to be redrawn.
+ *
+ * @param opaque If true, the target surface is considered opaque
+ * and will not be cleared. If false, the target surface
+ * will be cleared
+ */
ANDROID_API int prepare(bool opaque);
+
+ /**
+ * Prepares the renderer to draw a frame. This method must be invoked
+ * at the beginning of each frame. Only the specified rectangle of the
+ * frame is assumed to be dirty. A clip will automatically be set to
+ * the specified rectangle.
+ *
+ * @param left The left coordinate of the dirty rectangle
+ * @param top The top coordinate of the dirty rectangle
+ * @param right The right coordinate of the dirty rectangle
+ * @param bottom The bottom coordinate of the dirty rectangle
+ * @param opaque If true, the target surface is considered opaque
+ * and will not be cleared. If false, the target surface
+ * will be cleared in the specified dirty rectangle
+ */
virtual int prepareDirty(float left, float top, float right, float bottom, bool opaque);
+
+ /**
+ * Indicates the end of a frame. This method must be invoked whenever
+ * the caller is done rendering a frame.
+ */
virtual void finish();
- // These two calls must not be recorded in display lists
+ /**
+ * This method must be invoked before handing control over to a draw functor.
+ * See callDrawGLFunction() for instance.
+ *
+ * This command must not be recorded inside display lists.
+ */
virtual void interrupt();
+
+ /**
+ * This method must be invoked after getting control back from a draw functor.
+ *
+ * This command must not be recorded inside display lists.
+ */
virtual void resume();
ANDROID_API status_t invokeFunctors(Rect& dirty);
@@ -80,6 +138,9 @@ public:
ANDROID_API void attachFunctor(Functor* functor);
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
+ ANDROID_API void pushLayerUpdate(Layer* layer);
+ ANDROID_API void clearLayerUpdates();
+
ANDROID_API int getSaveCount() const;
virtual int save(int flags);
virtual void restore();
@@ -90,10 +151,6 @@ public:
virtual int saveLayerAlpha(float left, float top, float right, float bottom,
int alpha, int flags);
- void setAlpha(float alpha) {
- mSnapshot->alpha = alpha;
- }
-
virtual void translate(float dx, float dy);
virtual void rotate(float degrees);
virtual void scale(float sx, float sy);
@@ -105,6 +162,7 @@ public:
ANDROID_API const Rect& getClipBounds();
ANDROID_API bool quickReject(float left, float top, float right, float bottom);
+ bool quickRejectNoScissor(float left, float top, float right, float bottom);
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
virtual Rect* getClipRect();
@@ -123,6 +181,9 @@ public:
virtual status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
float left, float top, float right, float bottom, SkPaint* paint);
+ status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
+ const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode);
virtual status_t drawColor(int color, SkXfermode::Mode mode);
virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
virtual status_t drawRoundRect(float left, float top, float right, float bottom,
@@ -134,12 +195,12 @@ public:
virtual status_t drawPath(SkPath* path, SkPaint* paint);
virtual status_t drawLines(float* points, int count, SkPaint* paint);
virtual status_t drawPoints(float* points, int count, SkPaint* paint);
- virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- SkPaint* paint, float length = -1.0f);
virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path,
float hOffset, float vOffset, SkPaint* paint);
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
+ virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
+ const float* positions, SkPaint* paint, float length = -1.0f);
virtual void resetShader();
virtual void setupShader(SkiaShader* shader);
@@ -155,13 +216,65 @@ public:
SkPaint* filterPaint(SkPaint* paint);
- ANDROID_API static uint32_t getStencilSize();
+ /**
+ * Sets the alpha on the current snapshot. This alpha value will be modulated
+ * with other alpha values when drawing primitives.
+ */
+ void setAlpha(float alpha) {
+ mSnapshot->alpha = alpha;
+ }
+ /**
+ * Inserts a named group marker in the stream of GL commands. This marker
+ * can be used by tools to group commands into logical groups. A call to
+ * this method must always be followed later on by a call to endMark().
+ */
void startMark(const char* name) const;
+
+ /**
+ * Closes the last group marker opened by startMark().
+ */
void endMark() const;
+ /**
+ * Gets the alpha and xfermode out of a paint object. If the paint is null
+ * alpha will be 255 and the xfermode will be SRC_OVER. This method does
+ * not multiply the paint's alpha by the current snapshot's alpha.
+ *
+ * @param paint The paint to extract values from
+ * @param alpha Where to store the resulting alpha
+ * @param mode Where to store the resulting xfermode
+ */
+ static inline void getAlphaAndModeDirect(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
+ if (paint) {
+ *mode = getXfermode(paint->getXfermode());
+
+ // Skia draws using the color's alpha channel if < 255
+ // Otherwise, it uses the paint's alpha
+ int color = paint->getColor();
+ *alpha = (color >> 24) & 0xFF;
+ if (*alpha == 255) {
+ *alpha = paint->getAlpha();
+ }
+ } else {
+ *mode = SkXfermode::kSrcOver_Mode;
+ *alpha = 255;
+ }
+ }
+
protected:
/**
+ * Computes the projection matrix, initialize the first snapshot
+ * and stores the dimensions of the render target.
+ */
+ void initViewport(int width, int height);
+
+ /**
+ * Call this method after updating a layer during a drawing pass.
+ */
+ void resumeAfterLayer();
+
+ /**
* Compose the layer defined in the current snapshot with the layer
* defined by the previous snapshot.
*
@@ -213,6 +326,35 @@ protected:
*/
void drawTextureLayer(Layer* layer, const Rect& rect);
+ /**
+ * Gets the alpha and xfermode out of a paint object. If the paint is null
+ * alpha will be 255 and the xfermode will be SRC_OVER.
+ *
+ * @param paint The paint to extract values from
+ * @param alpha Where to store the resulting alpha
+ * @param mode Where to store the resulting xfermode
+ */
+ inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
+
+ /**
+ * Safely retrieves the mode from the specified xfermode. If the specified
+ * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode.
+ */
+ static inline SkXfermode::Mode getXfermode(SkXfermode* mode) {
+ SkXfermode::Mode resultMode;
+ if (!SkXfermode::AsMode(mode, &resultMode)) {
+ resultMode = SkXfermode::kSrcOver_Mode;
+ }
+ return resultMode;
+ }
+
+ /**
+ * Set to true to suppress error checks at the end of a frame.
+ */
+ virtual bool suppressErrorChecks() {
+ return false;
+ }
+
private:
/**
* Ensures the state of the renderer is the same as the state of
@@ -221,6 +363,19 @@ private:
void syncState();
/**
+ * Tells the GPU what part of the screen is about to be redrawn.
+ * This method needs to be invoked every time getTargetFbo() is
+ * bound again.
+ */
+ void startTiling(const sp<Snapshot>& snapshot, bool opaque = false);
+
+ /**
+ * Tells the GPU that we are done drawing the frame or that we
+ * are switching to another render target.
+ */
+ void endTiling();
+
+ /**
* Saves the current state of the renderer as a new snapshot.
* The new snapshot is saved in mSnapshot and the previous snapshot
* is linked from mSnapshot->previous.
@@ -245,6 +400,18 @@ private:
void setScissorFromClip();
/**
+ * Performs a quick reject but does not affect the scissor. Returns
+ * the transformed rect to test and the current clip.
+ */
+ bool quickRejectNoScissor(float left, float top, float right, float bottom,
+ Rect& transformed, Rect& clip);
+
+ /**
+ * Performs a quick reject but adjust the bounds to account for stroke width if necessary
+ */
+ bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint);
+
+ /**
* Creates a new layer stored in the specified snapshot.
*
* @param snapshot The snapshot associated with the new layer
@@ -259,7 +426,7 @@ private:
*
* @return True if the layer was successfully created, false otherwise
*/
- bool createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom,
+ bool createLayer(float left, float top, float right, float bottom,
int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo);
/**
@@ -270,8 +437,7 @@ private:
* @param bounds The bounds of the layer
* @param previousFbo The name of the current framebuffer
*/
- bool createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot,
- GLuint previousFbo);
+ bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo);
/**
* Compose the specified layer as a region.
@@ -320,7 +486,6 @@ private:
* @param color The rectangle's ARGB color, defined as a packed 32 bits word
* @param mode The Skia xfermode to use
* @param ignoreTransform True if the current transform should be ignored
- * @param ignoreBlending True if the blending is set by the caller
*/
void drawColorRect(float left, float top, float right, float bottom,
int color, SkXfermode::Mode mode, bool ignoreTransform = false);
@@ -339,19 +504,6 @@ private:
status_t drawShape(float left, float top, const PathTexture* texture, SkPaint* paint);
/**
- * Renders the rect defined by the specified bounds as a shape.
- * This will render the rect using a path texture, which is used to render
- * rects with stroke effects.
- *
- * @param left The left coordinate of the rect to draw
- * @param top The top coordinate of the rect to draw
- * @param right The right coordinate of the rect to draw
- * @param bottom The bottom coordinate of the rect to draw
- * @param p The paint to draw the rect with
- */
- status_t drawRectAsShape(float left, float top, float right, float bottom, SkPaint* p);
-
- /**
* Draws the specified texture as an alpha bitmap. Alpha bitmaps obey
* different compositing rules.
*
@@ -363,17 +515,12 @@ private:
void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint);
/**
- * Renders the rect defined by the specified bounds as an anti-aliased rect.
+ * Renders the convex hull defined by the specified path as a strip of polygons.
*
- * @param left The left coordinate of the rect to draw
- * @param top The top coordinate of the rect to draw
- * @param right The right coordinate of the rect to draw
- * @param bottom The bottom coordinate of the rect to draw
- * @param color The color of the rect
- * @param mode The blending mode to draw the rect
+ * @param path The hull of the path to draw
+ * @param paint The paint to render with
*/
- void drawAARect(float left, float top, float right, float bottom,
- int color, SkXfermode::Mode mode);
+ void drawConvexPath(const SkPath& path, SkPaint* paint);
/**
* Draws a textured rectangle with the specified texture. The specified coordinates
@@ -446,6 +593,24 @@ private:
void drawTextDecorations(const char* text, int bytesCount, float length,
float x, float y, SkPaint* paint);
+ /**
+ * Draws shadow layer on text (with optional positions).
+ *
+ * @param paint The paint to draw the shadow with
+ * @param text The text to draw
+ * @param bytesCount The number of bytes in the text
+ * @param count The number of glyphs in the text
+ * @param positions The x, y positions of individual glyphs (or NULL)
+ * @param fontRenderer The font renderer object
+ * @param alpha The alpha value for drawing the shadow
+ * @param mode The xfermode for drawing the shadow
+ * @param x The x coordinate where the shadow will be drawn
+ * @param y The y coordinate where the shadow will be drawn
+ */
+ void drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
+ const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
+ float x, float y);
+
/**
* Draws a path texture. Path textures are alpha8 bitmaps that need special
* compositing to apply colors/filters/etc.
@@ -455,7 +620,7 @@ private:
* @param y The y coordinate where the texture will be drawn
* @param paint The paint to draw the texture with
*/
- void drawPathTexture(const PathTexture* texture, 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
@@ -471,16 +636,6 @@ private:
void resetDrawTextureTexCoords(float u1, float v1, float u2, float v2);
/**
- * Gets the alpha and xfermode out of a paint object. If the paint is null
- * alpha will be 255 and the xfermode will be SRC_OVER.
- *
- * @param paint The paint to extract values from
- * @param alpha Where to store the resulting alpha
- * @param mode Where to store the resulting xfermode
- */
- inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
-
- /**
* Binds the specified texture. The texture unit must have been selected
* prior to calling this method.
*/
@@ -504,12 +659,6 @@ private:
bool swapSrcDst = false);
/**
- * Safely retrieves the mode from the specified xfermode. If the specified
- * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode.
- */
- inline SkXfermode::Mode getXfermode(SkXfermode* mode);
-
- /**
* Use the specified program with the current GL context. If the program is already
* in use, it will not be bound again. If it is not in use, the current program is
* marked unused and the specified program becomes used and becomes the new
@@ -525,18 +674,21 @@ private:
* Invoked before any drawing operation. This sets required state.
*/
void setupDraw(bool clear = true);
+
/**
* Various methods to setup OpenGL rendering.
*/
void setupDrawWithTexture(bool isAlpha8 = false);
void setupDrawWithExternalTexture();
void setupDrawNoTexture();
- void setupDrawAALine();
+ void setupDrawAA();
+ void setupDrawVertexShape();
void setupDrawPoint(float pointSize);
void setupDrawColor(int color);
void setupDrawColor(int color, int alpha);
void setupDrawColor(float r, float g, float b, float a);
void setupDrawAlpha8Color(int color, int alpha);
+ void setupDrawTextGamma(const SkPaint* paint);
void setupDrawShader();
void setupDrawColorFilter();
void setupDrawBlending(SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode,
@@ -561,6 +713,7 @@ private:
void setupDrawExternalTexture(GLuint texture);
void setupDrawTextureTransform();
void setupDrawTextureTransformUniforms(mat4& transform);
+ void setupDrawTextGammaUniforms();
void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords);
void setupDrawVertices(GLvoid* vertices);
@@ -570,8 +723,18 @@ private:
void finishDrawTexture();
void accountForClear(SkXfermode::Mode mode);
+ bool updateLayer(Layer* layer, bool inFrame);
+ void updateLayers();
+
+ /**
+ * Renders the specified region as a series of rectangles. This method
+ * is used for debugging only.
+ */
void drawRegionRects(const Region& region);
+ void debugOverdraw(bool enable, bool clear);
+ void renderOverdraw();
+
/**
* Should be invoked every time the glScissor is modified.
*/
@@ -594,6 +757,8 @@ private:
sp<Snapshot> mFirstSnapshot;
// Current state
sp<Snapshot> mSnapshot;
+ // State used to define the clipping region
+ sp<Snapshot> mTilingSnapshot;
// Shaders
SkiaShader* mShader;
@@ -624,6 +789,8 @@ private:
Vector<Rect*> mLayers;
// List of functors to invoke after a frame is drawn
SortedVector<Functor*> mFunctors;
+ // List of layers to update at the beginning of a frame
+ Vector<Layer*> mLayerUpdates;
// Indentity matrix
const mat4 mIdentity;
@@ -643,6 +810,15 @@ private:
GLuint mTextureUnit;
// Track dirty regions, true by default
bool mTrackDirtyRegions;
+ // Indicate whether we are drawing an opaque frame
+ bool mOpaqueFrame;
+
+ // See PROPERTY_DISABLE_SCISSOR_OPTIMIZATION in
+ // Properties.h
+ bool mScissorOptimizationDisabled;
+
+ // No-ops start/endTiling when set
+ bool mSuppressTiling;
friend class DisplayListRenderer;
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 27f530c48fa6..abc88fa3c054 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -108,9 +108,7 @@ bool Patch::matches(const int32_t* xDivs, const int32_t* yDivs, const uint32_t c
void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
float left, float top, float right, float bottom) {
-#if RENDER_LAYERS_AS_REGIONS
if (hasEmptyQuads) quads.clear();
-#endif
// Reset the vertices count here, we will count exactly how many
// vertices we actually need when generating the quads
@@ -268,6 +266,11 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f
const uint32_t oldQuadCount = quadCount;
quadCount++;
+ if (x1 < 0.0f) x1 = 0.0f;
+ if (x2 < 0.0f) x2 = 0.0f;
+ if (y1 < 0.0f) y1 = 0.0f;
+ if (y2 < 0.0f) y2 = 0.0f;
+
// Skip degenerate and transparent (empty) quads
if ((mColorKey >> oldQuadCount) & 0x1) {
#if DEBUG_PATCHES_EMPTY_VERTICES
@@ -278,13 +281,11 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f
return;
}
-#if RENDER_LAYERS_AS_REGIONS
// Record all non empty quads
if (hasEmptyQuads) {
Rect bounds(x1, y1, x2, y2);
quads.add(bounds);
}
-#endif
// Left triangle
TextureVertex::set(vertex++, x1, y1, u1, v1);
diff --git a/libs/hwui/PathRenderer.cpp b/libs/hwui/PathRenderer.cpp
new file mode 100644
index 000000000000..5b55c2b94589
--- /dev/null
+++ b/libs/hwui/PathRenderer.cpp
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2012 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 "PathRenderer"
+#define LOG_NDEBUG 1
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#define VERTEX_DEBUG 0
+
+#include <SkPath.h>
+#include <SkPaint.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "PathRenderer.h"
+#include "Matrix.h"
+#include "Vector.h"
+#include "Vertex.h"
+
+namespace android {
+namespace uirenderer {
+
+#define THRESHOLD 0.5f
+
+SkRect PathRenderer::computePathBounds(const SkPath& path, const SkPaint* paint) {
+ SkRect bounds = path.getBounds();
+ if (paint->getStyle() != SkPaint::kFill_Style) {
+ float outset = paint->getStrokeWidth() * 0.5f;
+ bounds.outset(outset, outset);
+ }
+ return bounds;
+}
+
+void computeInverseScales(const mat4 *transform, float &inverseScaleX, float& inverseScaleY) {
+ if (CC_UNLIKELY(!transform->isPureTranslate())) {
+ float m00 = transform->data[Matrix4::kScaleX];
+ float m01 = transform->data[Matrix4::kSkewY];
+ float m10 = transform->data[Matrix4::kSkewX];
+ float m11 = transform->data[Matrix4::kScaleY];
+ float scaleX = sqrt(m00 * m00 + m01 * m01);
+ float scaleY = sqrt(m10 * m10 + m11 * m11);
+ inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
+ inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
+ } else {
+ inverseScaleX = 1.0f;
+ inverseScaleY = 1.0f;
+ }
+}
+
+inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
+ Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
+}
+
+inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
+ AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
+}
+
+/**
+ * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
+ * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
+ * will be offset by 1.0
+ *
+ * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
+ * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
+ */
+inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
+ return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
+}
+
+void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
+ Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
+
+ int currentIndex = 0;
+ // zig zag between all previous points on the inside of the hull to create a
+ // triangle strip that fills the hull
+ int srcAindex = 0;
+ int srcBindex = perimeter.size() - 1;
+ while (srcAindex <= srcBindex) {
+ copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]);
+ if (srcAindex == srcBindex) break;
+ copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]);
+ srcAindex++;
+ srcBindex--;
+ }
+}
+
+void getStrokeVerticesFromPerimeter(const Vector<Vertex>& perimeter, float halfStrokeWidth,
+ VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
+ Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
+
+ int currentIndex = 0;
+ const Vertex* last = &(perimeter[perimeter.size() - 1]);
+ const Vertex* current = &(perimeter[0]);
+ vec2 lastNormal(current->position[1] - last->position[1],
+ last->position[0] - current->position[0]);
+ lastNormal.normalize();
+ for (unsigned int i = 0; i < perimeter.size(); i++) {
+ const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
+ vec2 nextNormal(next->position[1] - current->position[1],
+ current->position[0] - next->position[0]);
+ nextNormal.normalize();
+
+ vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ if (halfStrokeWidth == 0.0f) {
+ // hairline - compensate for scale
+ totalOffset.x *= 0.5f * inverseScaleX;
+ totalOffset.y *= 0.5f * inverseScaleY;
+ } else {
+ totalOffset *= halfStrokeWidth;
+ }
+
+ Vertex::set(&buffer[currentIndex++],
+ current->position[0] + totalOffset.x,
+ current->position[1] + totalOffset.y);
+
+ Vertex::set(&buffer[currentIndex++],
+ current->position[0] - totalOffset.x,
+ current->position[1] - totalOffset.y);
+
+ last = current;
+ current = next;
+ lastNormal = nextNormal;
+ }
+
+ // wrap around to beginning
+ copyVertex(&buffer[currentIndex++], &buffer[0]);
+ copyVertex(&buffer[currentIndex++], &buffer[1]);
+}
+
+void getFillVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer,
+ float inverseScaleX, float inverseScaleY) {
+ AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
+
+ // generate alpha points - fill Alpha vertex gaps in between each point with
+ // alpha 0 vertex, offset by a scaled normal.
+ int currentIndex = 0;
+ const Vertex* last = &(perimeter[perimeter.size() - 1]);
+ const Vertex* current = &(perimeter[0]);
+ vec2 lastNormal(current->position[1] - last->position[1],
+ last->position[0] - current->position[0]);
+ lastNormal.normalize();
+ for (unsigned int i = 0; i < perimeter.size(); i++) {
+ const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
+ vec2 nextNormal(next->position[1] - current->position[1],
+ current->position[0] - next->position[0]);
+ nextNormal.normalize();
+
+ // AA point offset from original point is that point's normal, such that each side is offset
+ // by .5 pixels
+ vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ totalOffset.x *= 0.5f * inverseScaleX;
+ totalOffset.y *= 0.5f * inverseScaleY;
+
+ AlphaVertex::set(&buffer[currentIndex++],
+ current->position[0] + totalOffset.x,
+ current->position[1] + totalOffset.y,
+ 0.0f);
+ AlphaVertex::set(&buffer[currentIndex++],
+ current->position[0] - totalOffset.x,
+ current->position[1] - totalOffset.y,
+ 1.0f);
+
+ last = current;
+ current = next;
+ lastNormal = nextNormal;
+ }
+
+ // wrap around to beginning
+ copyAlphaVertex(&buffer[currentIndex++], &buffer[0]);
+ copyAlphaVertex(&buffer[currentIndex++], &buffer[1]);
+
+ // zig zag between all previous points on the inside of the hull to create a
+ // triangle strip that fills the hull, repeating the first inner point to
+ // create degenerate tris to start inside path
+ int srcAindex = 0;
+ int srcBindex = perimeter.size() - 1;
+ while (srcAindex <= srcBindex) {
+ copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]);
+ if (srcAindex == srcBindex) break;
+ copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]);
+ srcAindex++;
+ srcBindex--;
+ }
+
+#if VERTEX_DEBUG
+ for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) {
+ ALOGD("point at %f %f", buffer[i].position[0], buffer[i].position[1]);
+ }
+#endif
+}
+
+void getStrokeVerticesFromPerimeterAA(const Vector<Vertex>& perimeter, float halfStrokeWidth,
+ VertexBuffer& vertexBuffer, float inverseScaleX, float inverseScaleY) {
+ AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
+
+ // avoid lines smaller than hairline since they break triangle based sampling. instead reducing
+ // alpha value (TODO: support different X/Y scale)
+ float maxAlpha = 1.0f;
+ if (halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
+ halfStrokeWidth * inverseScaleX < 1.0f) {
+ maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
+ halfStrokeWidth = 0.0f;
+ }
+
+ int offset = 2 * perimeter.size() + 3;
+ int currentAAOuterIndex = 0;
+ int currentStrokeIndex = offset;
+ int currentAAInnerIndex = offset * 2;
+
+ const Vertex* last = &(perimeter[perimeter.size() - 1]);
+ const Vertex* current = &(perimeter[0]);
+ vec2 lastNormal(current->position[1] - last->position[1],
+ last->position[0] - current->position[0]);
+ lastNormal.normalize();
+ for (unsigned int i = 0; i < perimeter.size(); i++) {
+ const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
+ vec2 nextNormal(next->position[1] - current->position[1],
+ current->position[0] - next->position[0]);
+ nextNormal.normalize();
+
+ vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ vec2 AAOffset = totalOffset;
+ AAOffset.x *= 0.5f * inverseScaleX;
+ AAOffset.y *= 0.5f * inverseScaleY;
+
+ vec2 innerOffset = totalOffset;
+ if (halfStrokeWidth == 0.0f) {
+ // hairline! - compensate for scale
+ innerOffset.x *= 0.5f * inverseScaleX;
+ innerOffset.y *= 0.5f * inverseScaleY;
+ } else {
+ innerOffset *= halfStrokeWidth;
+ }
+ vec2 outerOffset = innerOffset + AAOffset;
+ innerOffset -= AAOffset;
+
+ AlphaVertex::set(&buffer[currentAAOuterIndex++],
+ current->position[0] + outerOffset.x,
+ current->position[1] + outerOffset.y,
+ 0.0f);
+ AlphaVertex::set(&buffer[currentAAOuterIndex++],
+ current->position[0] + innerOffset.x,
+ current->position[1] + innerOffset.y,
+ maxAlpha);
+
+ AlphaVertex::set(&buffer[currentStrokeIndex++],
+ current->position[0] + innerOffset.x,
+ current->position[1] + innerOffset.y,
+ maxAlpha);
+ AlphaVertex::set(&buffer[currentStrokeIndex++],
+ current->position[0] - innerOffset.x,
+ current->position[1] - innerOffset.y,
+ maxAlpha);
+
+ AlphaVertex::set(&buffer[currentAAInnerIndex++],
+ current->position[0] - innerOffset.x,
+ current->position[1] - innerOffset.y,
+ maxAlpha);
+ AlphaVertex::set(&buffer[currentAAInnerIndex++],
+ current->position[0] - outerOffset.x,
+ current->position[1] - outerOffset.y,
+ 0.0f);
+
+ last = current;
+ current = next;
+ lastNormal = nextNormal;
+ }
+
+ // wrap each strip around to beginning, creating degenerate tris to bridge strips
+ copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]);
+ copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
+ copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]);
+
+ copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]);
+ copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
+ copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]);
+
+ copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]);
+ copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]);
+ // don't need to create last degenerate tri
+}
+
+void PathRenderer::convexPathVertices(const SkPath &path, const SkPaint* paint,
+ const mat4 *transform, VertexBuffer& vertexBuffer) {
+ ATRACE_CALL();
+
+ SkPaint::Style style = paint->getStyle();
+ bool isAA = paint->isAntiAlias();
+
+ float inverseScaleX, inverseScaleY;
+ computeInverseScales(transform, inverseScaleX, inverseScaleY);
+
+ Vector<Vertex> tempVertices;
+ float threshInvScaleX = inverseScaleX;
+ float threshInvScaleY = inverseScaleY;
+ if (style == SkPaint::kStroke_Style) {
+ // alter the bezier recursion threshold values we calculate in order to compensate for
+ // expansion done after the path vertices are found
+ SkRect bounds = path.getBounds();
+ if (!bounds.isEmpty()) {
+ threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
+ threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
+ }
+ }
+ convexPathPerimeterVertices(path, threshInvScaleX * threshInvScaleX,
+ threshInvScaleY * threshInvScaleY, tempVertices);
+
+ if (!tempVertices.size()) {
+ // path was empty, return without allocating vertex buffer
+ return;
+ }
+
+#if VERTEX_DEBUG
+ for (unsigned int i = 0; i < tempVertices.size(); i++) {
+ ALOGD("orig path: point at %f %f", tempVertices[i].position[0], tempVertices[i].position[1]);
+ }
+#endif
+
+ if (style == SkPaint::kStroke_Style) {
+ float halfStrokeWidth = paint->getStrokeWidth() * 0.5f;
+ if (!isAA) {
+ getStrokeVerticesFromPerimeter(tempVertices, halfStrokeWidth, vertexBuffer,
+ inverseScaleX, inverseScaleY);
+ } else {
+ getStrokeVerticesFromPerimeterAA(tempVertices, halfStrokeWidth, vertexBuffer,
+ inverseScaleX, inverseScaleY);
+ }
+ } else {
+ // For kStrokeAndFill style, the path should be adjusted externally, as it will be treated as a fill here.
+ if (!isAA) {
+ getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
+ } else {
+ getFillVerticesFromPerimeterAA(tempVertices, vertexBuffer, inverseScaleX, inverseScaleY);
+ }
+ }
+}
+
+
+void PathRenderer::convexPathPerimeterVertices(const SkPath& path,
+ float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+ ATRACE_CALL();
+
+ SkPath::Iter iter(path, true);
+ SkPoint pos;
+ SkPoint pts[4];
+ SkPath::Verb v;
+ Vertex* newVertex = 0;
+ while (SkPath::kDone_Verb != (v = iter.next(pts))) {
+ switch (v) {
+ case SkPath::kMove_Verb:
+ pos = pts[0];
+ ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
+ break;
+ case SkPath::kClose_Verb:
+ ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
+ break;
+ case SkPath::kLine_Verb:
+ ALOGV("kLine_Verb %f %f -> %f %f",
+ pts[0].x(), pts[0].y(),
+ pts[1].x(), pts[1].y());
+
+ // TODO: make this not yuck
+ outputVertices.push();
+ newVertex = &(outputVertices.editArray()[outputVertices.size() - 1]);
+ Vertex::set(newVertex, pts[1].x(), pts[1].y());
+ break;
+ case SkPath::kQuad_Verb:
+ ALOGV("kQuad_Verb");
+ recursiveQuadraticBezierVertices(
+ pts[0].x(), pts[0].y(),
+ pts[2].x(), pts[2].y(),
+ pts[1].x(), pts[1].y(),
+ sqrInvScaleX, sqrInvScaleY, outputVertices);
+ break;
+ case SkPath::kCubic_Verb:
+ ALOGV("kCubic_Verb");
+ recursiveCubicBezierVertices(
+ pts[0].x(), pts[0].y(),
+ pts[1].x(), pts[1].y(),
+ pts[3].x(), pts[3].y(),
+ pts[2].x(), pts[2].y(),
+ sqrInvScaleX, sqrInvScaleY, outputVertices);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void PathRenderer::recursiveCubicBezierVertices(
+ float p1x, float p1y, float c1x, float c1y,
+ float p2x, float p2y, float c2x, float c2y,
+ float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+ float dx = p2x - p1x;
+ float dy = p2y - p1y;
+ float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
+ float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
+ float d = d1 + d2;
+
+ // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
+
+ if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ // below thresh, draw line by adding endpoint
+ // TODO: make this not yuck
+ outputVertices.push();
+ Vertex* newVertex = &(outputVertices.editArray()[outputVertices.size() - 1]);
+ Vertex::set(newVertex, p2x, p2y);
+ } else {
+ float p1c1x = (p1x + c1x) * 0.5f;
+ float p1c1y = (p1y + c1y) * 0.5f;
+ float p2c2x = (p2x + c2x) * 0.5f;
+ float p2c2y = (p2y + c2y) * 0.5f;
+
+ float c1c2x = (c1x + c2x) * 0.5f;
+ float c1c2y = (c1y + c2y) * 0.5f;
+
+ float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
+ float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
+
+ float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
+ float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
+
+ float mx = (p1c1c2x + p2c1c2x) * 0.5f;
+ float my = (p1c1c2y + p2c1c2y) * 0.5f;
+
+ recursiveCubicBezierVertices(
+ p1x, p1y, p1c1x, p1c1y,
+ mx, my, p1c1c2x, p1c1c2y,
+ sqrInvScaleX, sqrInvScaleY, outputVertices);
+ recursiveCubicBezierVertices(
+ mx, my, p2c1c2x, p2c1c2y,
+ p2x, p2y, p2c2x, p2c2y,
+ sqrInvScaleX, sqrInvScaleY, outputVertices);
+ }
+}
+
+void PathRenderer::recursiveQuadraticBezierVertices(
+ float ax, float ay,
+ float bx, float by,
+ float cx, float cy,
+ float sqrInvScaleX, float sqrInvScaleY, Vector<Vertex>& outputVertices) {
+ float dx = bx - ax;
+ float dy = by - ay;
+ float d = (cx - bx) * dy - (cy - by) * dx;
+
+ if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ // below thresh, draw line by adding endpoint
+ // TODO: make this not yuck
+ outputVertices.push();
+ Vertex* newVertex = &(outputVertices.editArray()[outputVertices.size() - 1]);
+ Vertex::set(newVertex, bx, by);
+ } else {
+ float acx = (ax + cx) * 0.5f;
+ float bcx = (bx + cx) * 0.5f;
+ float acy = (ay + cy) * 0.5f;
+ float bcy = (by + cy) * 0.5f;
+
+ // midpoint
+ float mx = (acx + bcx) * 0.5f;
+ float my = (acy + bcy) * 0.5f;
+
+ recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
+ sqrInvScaleX, sqrInvScaleY, outputVertices);
+ recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
+ sqrInvScaleX, sqrInvScaleY, outputVertices);
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/PathRenderer.h b/libs/hwui/PathRenderer.h
new file mode 100644
index 000000000000..28a5b9031edb
--- /dev/null
+++ b/libs/hwui/PathRenderer.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012 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_PATH_RENDERER_H
+#define ANDROID_HWUI_PATH_RENDERER_H
+
+#include <utils/Vector.h>
+
+#include "Vertex.h"
+
+namespace android {
+namespace uirenderer {
+
+class Matrix4;
+typedef Matrix4 mat4;
+
+class VertexBuffer {
+public:
+ VertexBuffer():
+ mBuffer(0),
+ mSize(0),
+ mCleanupMethod(0)
+ {}
+
+ ~VertexBuffer() {
+ if (mCleanupMethod)
+ mCleanupMethod(mBuffer);
+ }
+
+ template <class TYPE>
+ TYPE* alloc(int size) {
+ mSize = size;
+ mBuffer = (void*)new TYPE[size];
+ mCleanupMethod = &(cleanup<TYPE>);
+
+ return (TYPE*)mBuffer;
+ }
+
+ void* getBuffer() { return mBuffer; }
+ unsigned int getSize() { return mSize; }
+
+private:
+ template <class TYPE>
+ static void cleanup(void* buffer) {
+ delete[] (TYPE*)buffer;
+ }
+
+ void* mBuffer;
+ unsigned int mSize;
+ void (*mCleanupMethod)(void*);
+};
+
+class PathRenderer {
+public:
+ static SkRect computePathBounds(const SkPath& path, const SkPaint* paint);
+
+ static void convexPathVertices(const SkPath& path, const SkPaint* paint,
+ const mat4 *transform, VertexBuffer& vertexBuffer);
+
+private:
+ static void convexPathPerimeterVertices(
+ const SkPath &path,
+ float sqrInvScaleX, float sqrInvScaleY,
+ Vector<Vertex> &outputVertices);
+
+/*
+ endpoints a & b,
+ control c
+ */
+ static void recursiveQuadraticBezierVertices(
+ float ax, float ay,
+ float bx, float by,
+ float cx, float cy,
+ float sqrInvScaleX, float sqrInvScaleY,
+ Vector<Vertex> &outputVertices);
+
+/*
+ endpoints p1, p2
+ control c1, c2
+ */
+ static void recursiveCubicBezierVertices(
+ float p1x, float p1y,
+ float c1x, float c1y,
+ float p2x, float p2y,
+ float c2x, float c2y,
+ float sqrInvScaleX, float sqrInvScaleY,
+ Vector<Vertex> &outputVertices);
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PATH_RENDERER_H
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 984461ca68d2..f0b5553a79a7 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -81,6 +81,7 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons
if (mInitialized) {
transform = addUniform("transform");
+ projection = addUniform("projection");
}
}
@@ -152,18 +153,20 @@ GLuint Program::buildShader(const char* source, GLenum type) {
void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
const mat4& transformMatrix, bool offset) {
- mat4 t(projectionMatrix);
+ mat4 p(projectionMatrix);
if (offset) {
// offset screenspace xy by an amount that compensates for typical precision
// issues in GPU hardware that tends to paint hor/vert lines in pixels shifted
// up and to the left.
// This offset value is based on an assumption that some hardware may use as
// little as 12.4 precision, so we offset by slightly more than 1/16.
- t.translate(.375, .375, 0);
+ p.translate(.375, .375, 0);
}
- t.multiply(transformMatrix);
+
+ mat4 t(transformMatrix);
t.multiply(modelViewMatrix);
+ glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]);
glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
}
@@ -178,7 +181,7 @@ void Program::setColor(const float r, const float g, const float b, const float
void Program::use() {
glUseProgram(mProgramId);
if (texCoords >= 0 && !mHasSampler) {
- glUniform1i(getUniform("sampler"), 0);
+ glUniform1i(getUniform("baseSampler"), 0);
mHasSampler = true;
}
mUse = true;
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index eb9ee7b4e64d..7e3aacf90260 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -41,8 +41,8 @@ namespace uirenderer {
#define PROGRAM_LOGD(...)
#endif
-#define COLOR_COMPONENT_THRESHOLD (1.0f - (0.5f / PANEL_BIT_DEPTH))
-#define COLOR_COMPONENT_INV_THRESHOLD (0.5f / PANEL_BIT_DEPTH)
+#define COLOR_COMPONENT_THRESHOLD 1.0f
+#define COLOR_COMPONENT_INV_THRESHOLD 0.0f
#define PROGRAM_KEY_TEXTURE 0x1
#define PROGRAM_KEY_A8_TEXTURE 0x2
@@ -77,6 +77,12 @@ namespace uirenderer {
#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
+#define PROGRAM_HAS_GAMMA_CORRECTION 40
+
+#define PROGRAM_IS_SIMPLE_GRADIENT 41
+
+#define PROGRAM_IS_VERTEX_SHAPE_SHIFT 42
+
///////////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////////
@@ -94,14 +100,14 @@ typedef uint64_t programid;
*/
struct ProgramDescription {
enum ColorModifier {
- kColorNone,
+ kColorNone = 0,
kColorMatrix,
kColorLighting,
kColorBlend
};
enum Gradient {
- kGradientLinear,
+ kGradientLinear = 0,
kGradientCircular,
kGradientSweep
};
@@ -124,9 +130,11 @@ struct ProgramDescription {
bool isBitmapNpot;
bool isAA;
+ bool isVertexShape;
bool hasGradient;
Gradient gradientType;
+ bool isSimpleGradient;
SkXfermode::Mode shadersMode;
@@ -146,6 +154,9 @@ struct ProgramDescription {
bool isPoint;
float pointSize;
+ bool hasGammaCorrection;
+ float gamma;
+
/**
* Resets this description. All fields are reset back to the default
* values they hold after building a new instance.
@@ -157,6 +168,7 @@ struct ProgramDescription {
hasTextureTransform = false;
isAA = false;
+ isVertexShape = false;
modulate = false;
@@ -165,6 +177,7 @@ struct ProgramDescription {
hasGradient = false;
gradientType = kGradientLinear;
+ isSimpleGradient = false;
shadersMode = SkXfermode::kClear_Mode;
@@ -180,6 +193,9 @@ struct ProgramDescription {
isPoint = false;
pointSize = 0.0f;
+
+ hasGammaCorrection = false;
+ gamma = 2.2f;
}
/**
@@ -188,8 +204,7 @@ struct ProgramDescription {
* be provided with a modulation color.
*/
bool setColor(const float r, const float g, const float b, const float a) {
- modulate = a < COLOR_COMPONENT_THRESHOLD || r < COLOR_COMPONENT_THRESHOLD ||
- g < COLOR_COMPONENT_THRESHOLD || b < COLOR_COMPONENT_THRESHOLD;
+ modulate = a < COLOR_COMPONENT_THRESHOLD;
return modulate;
}
@@ -246,6 +261,9 @@ struct ProgramDescription {
if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
+ if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
+ if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
+ if (isVertexShape) key |= programid(0x1) << PROGRAM_IS_VERTEX_SHAPE_SHIFT;
return key;
}
@@ -261,7 +279,7 @@ struct ProgramDescription {
}
private:
- inline uint32_t getEnumForWrap(GLenum wrap) const {
+ static inline uint32_t getEnumForWrap(GLenum wrap) {
switch (wrap) {
case GL_CLAMP_TO_EDGE:
return 0;
@@ -356,6 +374,11 @@ public:
*/
int transform;
+ /**
+ * Name of the projection uniform.
+ */
+ int projection;
+
protected:
/**
* Adds an attribute with the specified name.
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index a7f1277cf22c..f536adeff374 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -40,50 +40,61 @@ const char* gVS_Header_Attributes =
"attribute vec4 position;\n";
const char* gVS_Header_Attributes_TexCoords =
"attribute vec2 texCoords;\n";
-const char* gVS_Header_Attributes_AAParameters =
+const char* gVS_Header_Attributes_AALineParameters =
"attribute float vtxWidth;\n"
"attribute float vtxLength;\n";
+const char* gVS_Header_Attributes_AAVertexShapeParameters =
+ "attribute float vtxAlpha;\n";
const char* gVS_Header_Uniforms_TextureTransform =
"uniform mat4 mainTextureTransform;\n";
const char* gVS_Header_Uniforms =
+ "uniform mat4 projection;\n" \
"uniform mat4 transform;\n";
const char* gVS_Header_Uniforms_IsPoint =
"uniform mediump float pointSize;\n";
const char* gVS_Header_Uniforms_HasGradient[3] = {
// Linear
- "uniform mat4 screenSpace;\n",
+ "uniform mat4 screenSpace;\n"
+ "uniform float ditherSize;\n",
// Circular
- "uniform mat4 screenSpace;\n",
+ "uniform mat4 screenSpace;\n"
+ "uniform float ditherSize;\n",
// Sweep
"uniform mat4 screenSpace;\n"
+ "uniform float ditherSize;\n"
};
const char* gVS_Header_Uniforms_HasBitmap =
"uniform mat4 textureTransform;\n"
"uniform mediump vec2 textureDimension;\n";
const char* gVS_Header_Varyings_HasTexture =
"varying vec2 outTexCoords;\n";
-const char* gVS_Header_Varyings_IsAA =
+const char* gVS_Header_Varyings_IsAALine =
"varying float widthProportion;\n"
"varying float lengthProportion;\n";
-const char* gVS_Header_Varyings_HasBitmap[2] = {
- // Default precision
- "varying vec2 outBitmapTexCoords;\n",
- // High precision
- "varying highp vec2 outBitmapTexCoords;\n"
-};
-const char* gVS_Header_Varyings_PointHasBitmap[2] = {
- // Default precision
- "varying vec2 outPointBitmapTexCoords;\n",
- // High precision
- "varying highp vec2 outPointBitmapTexCoords;\n"
-};
-const char* gVS_Header_Varyings_HasGradient[3] = {
+const char* gVS_Header_Varyings_IsAAVertexShape =
+ "varying float alpha;\n";
+const char* gVS_Header_Varyings_HasBitmap =
+ "varying highp vec2 outBitmapTexCoords;\n";
+const char* gVS_Header_Varyings_PointHasBitmap =
+ "varying highp vec2 outPointBitmapTexCoords;\n";
+const char* gVS_Header_Varyings_HasGradient[6] = {
// Linear
- "varying vec2 linear;\n",
+ "varying highp vec2 linear;\n"
+ "varying vec2 ditherTexCoords;\n",
+ "varying float linear;\n"
+ "varying vec2 ditherTexCoords;\n",
+
// Circular
- "varying vec2 circular;\n",
+ "varying highp vec2 circular;\n"
+ "varying vec2 ditherTexCoords;\n",
+ "varying highp vec2 circular;\n"
+ "varying vec2 ditherTexCoords;\n",
+
// Sweep
- "varying vec2 sweep;\n"
+ "varying highp vec2 sweep;\n"
+ "varying vec2 ditherTexCoords;\n",
+ "varying highp vec2 sweep;\n"
+ "varying vec2 ditherTexCoords;\n",
};
const char* gVS_Main =
"\nvoid main(void) {\n";
@@ -91,25 +102,38 @@ const char* gVS_Main_OutTexCoords =
" outTexCoords = texCoords;\n";
const char* gVS_Main_OutTransformedTexCoords =
" outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n";
-const char* gVS_Main_OutGradient[3] = {
+const char* gVS_Main_OutGradient[6] = {
// Linear
- " linear = vec2((screenSpace * position).x, 0.5);\n",
+ " linear = vec2((screenSpace * position).x, 0.5);\n"
+ " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " linear = (screenSpace * position).x;\n"
+ " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+
// Circular
- " circular = (screenSpace * position).xy;\n",
+ " circular = (screenSpace * position).xy;\n"
+ " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " circular = (screenSpace * position).xy;\n"
+ " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+
// Sweep
" sweep = (screenSpace * position).xy;\n"
+ " ditherTexCoords = (transform * position).xy * ditherSize;\n",
+ " sweep = (screenSpace * position).xy;\n"
+ " ditherTexCoords = (transform * position).xy * ditherSize;\n",
};
const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
const char* gVS_Main_OutPointBitmapTexCoords =
" outPointBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
const char* gVS_Main_Position =
- " gl_Position = transform * position;\n";
+ " gl_Position = projection * transform * position;\n";
const char* gVS_Main_PointSize =
" gl_PointSize = pointSize;\n";
-const char* gVS_Main_AA =
+const char* gVS_Main_AALine =
" widthProportion = vtxWidth;\n"
" lengthProportion = vtxLength;\n";
+const char* gVS_Main_AAVertexShape =
+ " alpha = vtxAlpha;\n";
const char* gVS_Footer =
"}\n\n";
@@ -125,25 +149,34 @@ const char* gFS_Header =
"precision mediump float;\n\n";
const char* gFS_Uniforms_Color =
"uniform vec4 color;\n";
-const char* gFS_Uniforms_AA =
+const char* gFS_Uniforms_AALine =
"uniform float boundaryWidth;\n"
- "uniform float inverseBoundaryWidth;\n"
- "uniform float boundaryLength;\n"
- "uniform float inverseBoundaryLength;\n";
+ "uniform float boundaryLength;\n";
const char* gFS_Header_Uniforms_PointHasBitmap =
"uniform vec2 textureDimension;\n"
"uniform float pointSize;\n";
const char* gFS_Uniforms_TextureSampler =
- "uniform sampler2D sampler;\n";
+ "uniform sampler2D baseSampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler =
- "uniform samplerExternalOES sampler;\n";
-const char* gFS_Uniforms_GradientSampler[3] = {
+ "uniform samplerExternalOES baseSampler;\n";
+#define FS_UNIFORMS_DITHER \
+ "uniform float ditherSizeSquared;\n" \
+ "uniform sampler2D ditherSampler;\n"
+#define FS_UNIFORMS_GRADIENT \
+ "uniform vec4 startColor;\n" \
+ "uniform vec4 endColor;\n"
+const char* gFS_Uniforms_GradientSampler[6] = {
// Linear
- "uniform sampler2D gradientSampler;\n",
+ FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
+ FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT,
+
// Circular
- "uniform sampler2D gradientSampler;\n",
+ FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
+ FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT,
+
// Sweep
- "uniform sampler2D gradientSampler;\n"
+ FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
+ FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT
};
const char* gFS_Uniforms_BitmapSampler =
"uniform sampler2D bitmapSampler;\n";
@@ -159,14 +192,22 @@ const char* gFS_Uniforms_ColorOp[4] = {
// PorterDuff
"uniform vec4 colorBlend;\n"
};
+const char* gFS_Uniforms_Gamma =
+ "uniform float gamma;\n";
+
const char* gFS_Main =
"\nvoid main(void) {\n"
" lowp vec4 fragColor;\n";
const char* gFS_Main_PointBitmapTexCoords =
- " vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
+ " highp vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
"((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";
+#define FS_MAIN_DITHER \
+ "texture2D(ditherSampler, ditherTexCoords).a * ditherSizeSquared"
+const char* gFS_Main_AddDitherToGradient =
+ " gradientColor += " FS_MAIN_DITHER ";\n";
+
// Fast cases
const char* gFS_Fast_SingleColor =
"\nvoid main(void) {\n"
@@ -174,66 +215,87 @@ const char* gFS_Fast_SingleColor =
"}\n\n";
const char* gFS_Fast_SingleTexture =
"\nvoid main(void) {\n"
- " gl_FragColor = texture2D(sampler, outTexCoords);\n"
+ " gl_FragColor = texture2D(baseSampler, outTexCoords);\n"
"}\n\n";
const char* gFS_Fast_SingleModulateTexture =
"\nvoid main(void) {\n"
- " gl_FragColor = color.a * texture2D(sampler, outTexCoords);\n"
+ " gl_FragColor = color.a * texture2D(baseSampler, outTexCoords);\n"
"}\n\n";
const char* gFS_Fast_SingleA8Texture =
"\nvoid main(void) {\n"
- " gl_FragColor = texture2D(sampler, outTexCoords);\n"
+ " gl_FragColor = texture2D(baseSampler, outTexCoords);\n"
"}\n\n";
-const char* gFS_Fast_SingleModulateA8Texture =
+const char* gFS_Fast_SingleA8Texture_ApplyGamma =
"\nvoid main(void) {\n"
- " gl_FragColor = color * texture2D(sampler, outTexCoords).a;\n"
+ " gl_FragColor = vec4(0.0, 0.0, 0.0, pow(texture2D(baseSampler, outTexCoords).a, gamma));\n"
"}\n\n";
-const char* gFS_Fast_SingleGradient =
+const char* gFS_Fast_SingleModulateA8Texture =
"\nvoid main(void) {\n"
- " gl_FragColor = texture2D(gradientSampler, linear);\n"
+ " gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n"
"}\n\n";
-const char* gFS_Fast_SingleModulateGradient =
+const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
"\nvoid main(void) {\n"
- " gl_FragColor = color.a * texture2D(gradientSampler, linear);\n"
+ " gl_FragColor = color * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
"}\n\n";
+const char* gFS_Fast_SingleGradient[2] = {
+ "\nvoid main(void) {\n"
+ " gl_FragColor = " FS_MAIN_DITHER " + texture2D(gradientSampler, linear);\n"
+ "}\n\n",
+ "\nvoid main(void) {\n"
+ " gl_FragColor = " FS_MAIN_DITHER " + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
+ "}\n\n"
+};
+const char* gFS_Fast_SingleModulateGradient[2] = {
+ "\nvoid main(void) {\n"
+ " gl_FragColor = " FS_MAIN_DITHER " + color.a * texture2D(gradientSampler, linear);\n"
+ "}\n\n",
+ "\nvoid main(void) {\n"
+ " gl_FragColor = " FS_MAIN_DITHER " + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
+ "}\n\n"
+};
// General case
const char* gFS_Main_FetchColor =
" fragColor = color;\n";
const char* gFS_Main_ModulateColor =
" fragColor *= color.a;\n";
-const char* gFS_Main_AccountForAA =
- " if (widthProportion < boundaryWidth) {\n"
- " fragColor *= (widthProportion * inverseBoundaryWidth);\n"
- " } else if (widthProportion > (1.0 - boundaryWidth)) {\n"
- " fragColor *= ((1.0 - widthProportion) * inverseBoundaryWidth);\n"
- " }\n"
- " if (lengthProportion < boundaryLength) {\n"
- " fragColor *= (lengthProportion * inverseBoundaryLength);\n"
- " } else if (lengthProportion > (1.0 - boundaryLength)) {\n"
- " fragColor *= ((1.0 - lengthProportion) * inverseBoundaryLength);\n"
- " }\n";
+const char* gFS_Main_AccountForAALine =
+ " fragColor *= (1.0 - smoothstep(boundaryWidth, 0.5, abs(0.5 - widthProportion)))\n"
+ " * (1.0 - smoothstep(boundaryLength, 0.5, abs(0.5 - lengthProportion)));\n";
+const char* gFS_Main_AccountForAAVertexShape =
+ " fragColor *= alpha;\n";
+
const char* gFS_Main_FetchTexture[2] = {
// Don't modulate
- " fragColor = texture2D(sampler, outTexCoords);\n",
+ " fragColor = texture2D(baseSampler, outTexCoords);\n",
// Modulate
- " fragColor = color * texture2D(sampler, outTexCoords);\n"
+ " fragColor = color * texture2D(baseSampler, outTexCoords);\n"
};
-const char* gFS_Main_FetchA8Texture[2] = {
+const char* gFS_Main_FetchA8Texture[4] = {
// Don't modulate
- " fragColor = texture2D(sampler, outTexCoords);\n",
+ " fragColor = texture2D(baseSampler, outTexCoords);\n",
+ " fragColor = texture2D(baseSampler, outTexCoords);\n",
// Modulate
- " fragColor = color * texture2D(sampler, outTexCoords).a;\n"
+ " fragColor = color * texture2D(baseSampler, outTexCoords).a;\n",
+ " fragColor = color * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
-const char* gFS_Main_FetchGradient[3] = {
+const char* gFS_Main_FetchGradient[6] = {
// Linear
" vec4 gradientColor = texture2D(gradientSampler, linear);\n",
+
+ " vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
+
// Circular
- " float index = length(circular);\n"
- " vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n",
+ " vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n",
+
+ " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
+
// Sweep
- " float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
- " vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n"
+ " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
+ " vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n",
+
+ " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
+ " vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
};
const char* gFS_Main_FetchBitmap =
" vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
@@ -243,29 +305,38 @@ const char* gFS_Main_BlendShadersBG =
" fragColor = blendShaders(gradientColor, bitmapColor)";
const char* gFS_Main_BlendShadersGB =
" fragColor = blendShaders(bitmapColor, gradientColor)";
-const char* gFS_Main_BlendShaders_Modulate[3] = {
+const char* gFS_Main_BlendShaders_Modulate[6] = {
// Don't modulate
";\n",
+ ";\n",
// Modulate
- " * fragColor.a;\n",
+ " * color.a;\n",
+ " * color.a;\n",
// Modulate with alpha 8 texture
- " * texture2D(sampler, outTexCoords).a;\n"
+ " * texture2D(baseSampler, outTexCoords).a;\n",
+ " * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
-const char* gFS_Main_GradientShader_Modulate[3] = {
+const char* gFS_Main_GradientShader_Modulate[6] = {
// Don't modulate
" fragColor = gradientColor;\n",
+ " fragColor = gradientColor;\n",
// Modulate
- " fragColor = gradientColor * fragColor.a;\n",
+ " fragColor = gradientColor * color.a;\n",
+ " fragColor = gradientColor * color.a;\n",
// Modulate with alpha 8 texture
- " fragColor = gradientColor * texture2D(sampler, outTexCoords).a;\n"
+ " fragColor = gradientColor * texture2D(baseSampler, outTexCoords).a;\n",
+ " fragColor = gradientColor * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
-const char* gFS_Main_BitmapShader_Modulate[3] = {
+const char* gFS_Main_BitmapShader_Modulate[6] = {
// Don't modulate
" fragColor = bitmapColor;\n",
+ " fragColor = bitmapColor;\n",
// Modulate
- " fragColor = bitmapColor * fragColor.a;\n",
+ " fragColor = bitmapColor * color.a;\n",
+ " fragColor = bitmapColor * color.a;\n",
// Modulate with alpha 8 texture
- " fragColor = bitmapColor * texture2D(sampler, outTexCoords).a;\n"
+ " fragColor = bitmapColor * texture2D(baseSampler, outTexCoords).a;\n",
+ " fragColor = bitmapColor * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
const char* gFS_Main_FragColor =
" gl_FragColor = fragColor;\n";
@@ -277,7 +348,6 @@ const char* gFS_Main_ApplyColorOp[4] = {
// None
"",
// Matrix
- // TODO: Fix premultiplied alpha computations for color matrix
" fragColor *= colorMatrix;\n"
" fragColor += colorMatrixVector;\n"
" fragColor.rgb *= fragColor.a;\n",
@@ -388,8 +458,11 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr
String8 vertexShader = generateVertexShader(description);
String8 fragmentShader = generateFragmentShader(description);
- Program* program = new Program(description, vertexShader.string(), fragmentShader.string());
- return program;
+ return new Program(description, vertexShader.string(), fragmentShader.string());
+}
+
+static inline size_t gradientIndex(const ProgramDescription& description) {
+ return description.gradientType * 2 + description.isSimpleGradient;
}
String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
@@ -399,7 +472,11 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Header_Attributes_TexCoords);
}
if (description.isAA) {
- shader.append(gVS_Header_Attributes_AAParameters);
+ if (description.isVertexShape) {
+ shader.append(gVS_Header_Attributes_AAVertexShapeParameters);
+ } else {
+ shader.append(gVS_Header_Attributes_AALineParameters);
+ }
}
// Uniforms
shader.append(gVS_Header_Uniforms);
@@ -420,16 +497,19 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Header_Varyings_HasTexture);
}
if (description.isAA) {
- shader.append(gVS_Header_Varyings_IsAA);
+ if (description.isVertexShape) {
+ shader.append(gVS_Header_Varyings_IsAAVertexShape);
+ } else {
+ shader.append(gVS_Header_Varyings_IsAALine);
+ }
}
if (description.hasGradient) {
- shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
+ shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
}
if (description.hasBitmap) {
- int index = Caches::getInstance().extensions.needsHighpTexCoords() ? 1 : 0;
shader.append(description.isPoint ?
- gVS_Header_Varyings_PointHasBitmap[index] :
- gVS_Header_Varyings_HasBitmap[index]);
+ gVS_Header_Varyings_PointHasBitmap :
+ gVS_Header_Varyings_HasBitmap);
}
// Begin the shader
@@ -440,10 +520,11 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Main_OutTexCoords);
}
if (description.isAA) {
- shader.append(gVS_Main_AA);
- }
- if (description.hasGradient) {
- shader.append(gVS_Main_OutGradient[description.gradientType]);
+ if (description.isVertexShape) {
+ shader.append(gVS_Main_AAVertexShape);
+ } else {
+ shader.append(gVS_Main_AALine);
+ }
}
if (description.hasBitmap) {
shader.append(description.isPoint ?
@@ -455,6 +536,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
}
// Output transformed position
shader.append(gVS_Main_Position);
+ if (description.hasGradient) {
+ shader.append(gVS_Main_OutGradient[gradientIndex(description)]);
+ }
}
// End the shader
shader.append(gVS_Footer);
@@ -464,6 +548,14 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
return shader;
}
+static bool shaderOp(const ProgramDescription& description, String8& shader,
+ const int modulateOp, const char** snippets) {
+ int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
+ op = op * 2 + description.hasGammaCorrection;
+ shader.append(snippets[op]);
+ return description.hasAlpha8Texture;
+}
+
String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
String8 shader;
@@ -482,16 +574,19 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gVS_Header_Varyings_HasTexture);
}
if (description.isAA) {
- shader.append(gVS_Header_Varyings_IsAA);
+ if (description.isVertexShape) {
+ shader.append(gVS_Header_Varyings_IsAAVertexShape);
+ } else {
+ shader.append(gVS_Header_Varyings_IsAALine);
+ }
}
if (description.hasGradient) {
- shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
+ shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
}
if (description.hasBitmap) {
- int index = Caches::getInstance().extensions.needsHighpTexCoords() ? 1 : 0;
shader.append(description.isPoint ?
- gVS_Header_Varyings_PointHasBitmap[index] :
- gVS_Header_Varyings_HasBitmap[index]);
+ gVS_Header_Varyings_PointHasBitmap :
+ gVS_Header_Varyings_HasBitmap);
}
// Uniforms
@@ -508,19 +603,23 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
} else if (description.hasExternalTexture) {
shader.append(gFS_Uniforms_ExternalTextureSampler);
}
- if (description.isAA) {
- shader.append(gFS_Uniforms_AA);
+ if (description.isAA && !description.isVertexShape) {
+ shader.append(gFS_Uniforms_AALine);
}
if (description.hasGradient) {
- shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
+ shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]);
}
if (description.hasBitmap && description.isPoint) {
shader.append(gFS_Header_Uniforms_PointHasBitmap);
}
+ if (description.hasGammaCorrection) {
+ shader.append(gFS_Uniforms_Gamma);
+ }
// Optimization for common cases
if (!description.isAA && !blendFramebuffer &&
- description.colorOp == ProgramDescription::kColorNone && !description.isPoint) {
+ description.colorOp == ProgramDescription::kColorNone &&
+ !description.isPoint && !description.isVertexShape) {
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -544,16 +643,24 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
fast = true;
} else if (singleA8Texture) {
if (!description.modulate) {
- shader.append(gFS_Fast_SingleA8Texture);
+ if (description.hasGammaCorrection) {
+ shader.append(gFS_Fast_SingleA8Texture_ApplyGamma);
+ } else {
+ shader.append(gFS_Fast_SingleA8Texture);
+ }
} else {
- shader.append(gFS_Fast_SingleModulateA8Texture);
+ if (description.hasGammaCorrection) {
+ shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma);
+ } else {
+ shader.append(gFS_Fast_SingleModulateA8Texture);
+ }
}
fast = true;
} else if (singleGradient) {
if (!description.modulate) {
- shader.append(gFS_Fast_SingleGradient);
+ shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
} else {
- shader.append(gFS_Fast_SingleModulateGradient);
+ shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]);
}
fast = true;
}
@@ -594,21 +701,20 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasTexture || description.hasExternalTexture) {
if (description.hasAlpha8Texture) {
if (!description.hasGradient && !description.hasBitmap) {
- shader.append(gFS_Main_FetchA8Texture[modulateOp]);
+ shader.append(gFS_Main_FetchA8Texture[modulateOp * 2 +
+ description.hasGammaCorrection]);
}
} else {
shader.append(gFS_Main_FetchTexture[modulateOp]);
}
} else {
- if ((!description.hasGradient && !description.hasBitmap) || description.modulate) {
+ if (!description.hasGradient && !description.hasBitmap) {
shader.append(gFS_Main_FetchColor);
}
}
- if (description.isAA) {
- shader.append(gFS_Main_AccountForAA);
- }
if (description.hasGradient) {
- shader.append(gFS_Main_FetchGradient[description.gradientType]);
+ shader.append(gFS_Main_FetchGradient[gradientIndex(description)]);
+ shader.append(gFS_Main_AddDitherToGradient);
}
if (description.hasBitmap) {
if (description.isPoint) {
@@ -623,30 +729,38 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
bool applyModulate = false;
// Case when we have two shaders set
if (description.hasGradient && description.hasBitmap) {
- int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
if (description.isBitmapFirst) {
shader.append(gFS_Main_BlendShadersBG);
} else {
shader.append(gFS_Main_BlendShadersGB);
}
- shader.append(gFS_Main_BlendShaders_Modulate[op]);
- applyModulate = true;
+ applyModulate = shaderOp(description, shader, modulateOp,
+ gFS_Main_BlendShaders_Modulate);
} else {
if (description.hasGradient) {
- int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
- shader.append(gFS_Main_GradientShader_Modulate[op]);
- applyModulate = true;
+ applyModulate = shaderOp(description, shader, modulateOp,
+ gFS_Main_GradientShader_Modulate);
} else if (description.hasBitmap) {
- int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
- shader.append(gFS_Main_BitmapShader_Modulate[op]);
- applyModulate = true;
+ applyModulate = shaderOp(description, shader, modulateOp,
+ gFS_Main_BitmapShader_Modulate);
}
}
+
if (description.modulate && applyModulate) {
shader.append(gFS_Main_ModulateColor);
}
+
// Apply the color op if needed
shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
+
+ if (description.isAA) {
+ if (description.isVertexShape) {
+ shader.append(gFS_Main_AccountForAAVertexShape);
+ } else {
+ shader.append(gFS_Main_AccountForAALine);
+ }
+ }
+
// Output the fragment
if (!blendFramebuffer) {
shader.append(gFS_Main_FragColor);
@@ -676,13 +790,13 @@ void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::
}
void ProgramCache::generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT) {
- shader.append("\nvec2 wrap(vec2 texCoords) {\n");
+ shader.append("\nhighp vec2 wrap(highp vec2 texCoords) {\n");
if (wrapS == GL_MIRRORED_REPEAT) {
- shader.append(" float xMod2 = mod(texCoords.x, 2.0);\n");
+ shader.append(" highp float xMod2 = mod(texCoords.x, 2.0);\n");
shader.append(" if (xMod2 > 1.0) xMod2 = 2.0 - xMod2;\n");
}
if (wrapT == GL_MIRRORED_REPEAT) {
- shader.append(" float yMod2 = mod(texCoords.y, 2.0);\n");
+ shader.append(" highp float yMod2 = mod(texCoords.y, 2.0);\n");
shader.append(" if (yMod2 > 1.0) yMod2 = 2.0 - yMod2;\n");
}
shader.append(" return vec2(");
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 7854729be2da..1e8765bae9a2 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -25,9 +25,6 @@
* the OpenGLRenderer.
*/
-// If turned on, layers drawn inside FBOs are optimized with regions
-#define RENDER_LAYERS_AS_REGIONS 1
-
// If turned on, text is interpreted as glyphs instead of UTF-16
#define RENDER_TEXT_AS_GLYPHS 1
@@ -40,12 +37,13 @@
// Defines the size in bits of the stencil buffer
// Note: Only 1 bit is required for clipping but more bits are required
// to properly implement the winding fill rule when rasterizing paths
-#define STENCIL_BUFFER_SIZE 0
+#define STENCIL_BUFFER_SIZE 8
/**
- * Debug level for app developers.
+ * Debug level for app developers. The value is a numeric value defined
+ * by the DebugLevel enum below.
*/
-#define PROPERTY_DEBUG "hwui.debug_level"
+#define PROPERTY_DEBUG "debug.hwui.level"
/**
* Debug levels. Debug levels are used as flags.
@@ -57,6 +55,33 @@ enum DebugLevel {
kDebugMoreCaches = kDebugMemory | kDebugCaches
};
+/**
+ * Used to enable/disable layers update debugging. The accepted values are
+ * "true" and "false". The default value is "false".
+ */
+#define PROPERTY_DEBUG_LAYERS_UPDATES "debug.hwui.show_layers_updates"
+
+/**
+ * Used to enable/disable overdraw debugging. The accepted values are
+ * "true" and "false". The default value is "false".
+ */
+#define PROPERTY_DEBUG_OVERDRAW "debug.hwui.show_overdraw"
+
+/**
+ * Used to enable/disable scissor optimization. The accepted values are
+ * "true" and "false". The default value is "false".
+ *
+ * When scissor optimization is enabled, OpenGLRenderer will attempt to
+ * minimize the use of scissor by selectively enabling and disabling the
+ * GL scissor test.
+ * When the optimization is disabled, OpenGLRenderer will keep the GL
+ * scissor test enabled and change the scissor rect as needed.
+ * Some GPUs (for instance the SGX 540) perform better when changing
+ * the scissor rect often than when enabling/disabling the scissor test
+ * often.
+ */
+#define PROPERTY_DISABLE_SCISSOR_OPTIMIZATION "ro.hwui.disable_scissor_opt"
+
// These properties are defined in mega-bytes
#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
@@ -70,16 +95,35 @@ enum DebugLevel {
#define PROPERTY_TEXTURE_CACHE_FLUSH_RATE "ro.hwui.texture_cache_flush_rate"
// These properties are defined in pixels
-#define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width"
-#define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height"
+#define PROPERTY_TEXT_SMALL_CACHE_WIDTH "ro.hwui.text_small_cache_width"
+#define PROPERTY_TEXT_SMALL_CACHE_HEIGHT "ro.hwui.text_small_cache_height"
+#define PROPERTY_TEXT_LARGE_CACHE_WIDTH "ro.hwui.text_large_cache_width"
+#define PROPERTY_TEXT_LARGE_CACHE_HEIGHT "ro.hwui.text_large_cache_height"
+
+// Indicates whether gamma correction should be applied in the shaders
+// or in lookup tables. Accepted values:
+//
+// - "lookup3", correction based on lookup tables. Gamma correction
+// is different for black and white text (see thresholds below)
+//
+// - "lookup", correction based on a single lookup table
+//
+// - "shader3", correction applied by a GLSL shader. Gamma correction
+// is different for black and white text (see thresholds below)
+//
+// - "shader", correction applied by a GLSL shader
+//
+// See PROPERTY_TEXT_GAMMA, PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD and
+// PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD for more control.
+#define PROPERTY_TEXT_GAMMA_METHOD "hwui.text_gamma_correction"
+#define DEFAULT_TEXT_GAMMA_METHOD "lookup"
// Gamma (>= 1.0, <= 10.0)
-#define PROPERTY_TEXT_GAMMA "ro.text_gamma"
-#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "ro.text_gamma.black_threshold"
-#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "ro.text_gamma.white_threshold"
-
-// TODO: This should be set by a system property
-#define PANEL_BIT_DEPTH 20
+#define PROPERTY_TEXT_GAMMA "hwui.text_gamma"
+// Luminance threshold below which black gamma correction is applied. Range: [0..255]
+#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "hwui.text_gamma.black_threshold"
+// Lumincance threshold above which white gamma correction is applied. Range: [0..255]
+#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "hwui.text_gamma.white_threshold"
// Converts a number of mega-bytes into bytes
#define MB(s) s * 1024 * 1024
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 2ca4f50bb176..80f39ff90d1e 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -125,11 +125,11 @@ public:
return intersect(r.left, r.top, r.right, r.bottom);
}
- bool contains(float l, float t, float r, float b) {
+ inline bool contains(float l, float t, float r, float b) {
return l >= left && t >= top && r <= right && b <= bottom;
}
- bool contains(const Rect& r) {
+ inline bool contains(const Rect& r) {
return contains(r.left, r.top, r.right, r.bottom);
}
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index cf5f82205e03..81f7b94b31ee 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -38,7 +38,7 @@ void ResourceCache::logCache() {
ResourceCache::ResourceCache() {
Mutex::Autolock _l(mLock);
- mCache = new KeyedVector<void *, ResourceReference *>();
+ mCache = new KeyedVector<void*, ResourceReference*>();
}
ResourceCache::~ResourceCache() {
@@ -46,14 +46,17 @@ ResourceCache::~ResourceCache() {
delete mCache;
}
+void ResourceCache::lock() {
+ mLock.lock();
+}
+
+void ResourceCache::unlock() {
+ mLock.unlock();
+}
+
void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
Mutex::Autolock _l(mLock);
- ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
- if (ref == NULL || mCache->size() == 0) {
- ref = new ResourceReference(resourceType);
- mCache->add(resource, ref);
- }
- ref->refCount++;
+ incrementRefcountLocked(resource, resourceType);
}
void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) {
@@ -76,17 +79,47 @@ void ResourceCache::incrementRefcount(SkiaColorFilter* filterResource) {
incrementRefcount((void*) filterResource, kColorFilter);
}
+void ResourceCache::incrementRefcount(Layer* layerResource) {
+ incrementRefcount((void*) layerResource, kLayer);
+}
+
+void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) {
+ ssize_t index = mCache->indexOfKey(resource);
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
+ if (ref == NULL || mCache->size() == 0) {
+ ref = new ResourceReference(resourceType);
+ mCache->add(resource, ref);
+ }
+ ref->refCount++;
+}
+
+void ResourceCache::incrementRefcountLocked(SkBitmap* bitmapResource) {
+ SkSafeRef(bitmapResource->pixelRef());
+ SkSafeRef(bitmapResource->getColorTable());
+ incrementRefcountLocked((void*) bitmapResource, kBitmap);
+}
+
+void ResourceCache::incrementRefcountLocked(SkPath* pathResource) {
+ incrementRefcountLocked((void*) pathResource, kPath);
+}
+
+void ResourceCache::incrementRefcountLocked(SkiaShader* shaderResource) {
+ SkSafeRef(shaderResource->getSkShader());
+ incrementRefcountLocked((void*) shaderResource, kShader);
+}
+
+void ResourceCache::incrementRefcountLocked(SkiaColorFilter* filterResource) {
+ SkSafeRef(filterResource->getSkColorFilter());
+ incrementRefcountLocked((void*) filterResource, kColorFilter);
+}
+
+void ResourceCache::incrementRefcountLocked(Layer* layerResource) {
+ incrementRefcountLocked((void*) layerResource, kLayer);
+}
+
void ResourceCache::decrementRefcount(void* resource) {
Mutex::Autolock _l(mLock);
- ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
- if (ref == NULL) {
- // Should not get here - shouldn't get a call to decrement if we're not yet tracking it
- return;
- }
- ref->refCount--;
- if (ref->refCount == 0) {
- deleteResourceReference(resource, ref);
- }
+ decrementRefcountLocked(resource);
}
void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) {
@@ -109,27 +142,55 @@ void ResourceCache::decrementRefcount(SkiaColorFilter* filterResource) {
decrementRefcount((void*) filterResource);
}
-void ResourceCache::recycle(SkBitmap* resource) {
- Mutex::Autolock _l(mLock);
- if (mCache->indexOfKey(resource) < 0) {
- // not tracking this resource; just recycle the pixel data
- resource->setPixels(NULL, NULL);
- return;
- }
- ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+void ResourceCache::decrementRefcount(Layer* layerResource) {
+ decrementRefcount((void*) layerResource);
+}
+
+void ResourceCache::decrementRefcountLocked(void* resource) {
+ ssize_t index = mCache->indexOfKey(resource);
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
if (ref == NULL) {
- // Should not get here - shouldn't get a call to recycle if we're not yet tracking it
+ // Should not get here - shouldn't get a call to decrement if we're not yet tracking it
return;
}
- ref->recycled = true;
+ ref->refCount--;
if (ref->refCount == 0) {
- deleteResourceReference(resource, ref);
+ deleteResourceReferenceLocked(resource, ref);
}
}
+void ResourceCache::decrementRefcountLocked(SkBitmap* bitmapResource) {
+ SkSafeUnref(bitmapResource->pixelRef());
+ SkSafeUnref(bitmapResource->getColorTable());
+ decrementRefcountLocked((void*) bitmapResource);
+}
+
+void ResourceCache::decrementRefcountLocked(SkPath* pathResource) {
+ decrementRefcountLocked((void*) pathResource);
+}
+
+void ResourceCache::decrementRefcountLocked(SkiaShader* shaderResource) {
+ SkSafeUnref(shaderResource->getSkShader());
+ decrementRefcountLocked((void*) shaderResource);
+}
+
+void ResourceCache::decrementRefcountLocked(SkiaColorFilter* filterResource) {
+ SkSafeUnref(filterResource->getSkColorFilter());
+ decrementRefcountLocked((void*) filterResource);
+}
+
+void ResourceCache::decrementRefcountLocked(Layer* layerResource) {
+ decrementRefcountLocked((void*) layerResource);
+}
+
void ResourceCache::destructor(SkPath* resource) {
Mutex::Autolock _l(mLock);
- ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+ destructorLocked(resource);
+}
+
+void ResourceCache::destructorLocked(SkPath* resource) {
+ ssize_t index = mCache->indexOfKey(resource);
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
if (ref == NULL) {
// If we're not tracking this resource, just delete it
if (Caches::hasInstance()) {
@@ -140,13 +201,18 @@ void ResourceCache::destructor(SkPath* resource) {
}
ref->destroyed = true;
if (ref->refCount == 0) {
- deleteResourceReference(resource, ref);
+ deleteResourceReferenceLocked(resource, ref);
}
}
void ResourceCache::destructor(SkBitmap* resource) {
Mutex::Autolock _l(mLock);
- ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+ destructorLocked(resource);
+}
+
+void ResourceCache::destructorLocked(SkBitmap* resource) {
+ ssize_t index = mCache->indexOfKey(resource);
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
if (ref == NULL) {
// If we're not tracking this resource, just delete it
if (Caches::hasInstance()) {
@@ -157,13 +223,18 @@ void ResourceCache::destructor(SkBitmap* resource) {
}
ref->destroyed = true;
if (ref->refCount == 0) {
- deleteResourceReference(resource, ref);
+ deleteResourceReferenceLocked(resource, ref);
}
}
void ResourceCache::destructor(SkiaShader* resource) {
Mutex::Autolock _l(mLock);
- ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+ destructorLocked(resource);
+}
+
+void ResourceCache::destructorLocked(SkiaShader* resource) {
+ ssize_t index = mCache->indexOfKey(resource);
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
if (ref == NULL) {
// If we're not tracking this resource, just delete it
delete resource;
@@ -171,13 +242,18 @@ void ResourceCache::destructor(SkiaShader* resource) {
}
ref->destroyed = true;
if (ref->refCount == 0) {
- deleteResourceReference(resource, ref);
+ deleteResourceReferenceLocked(resource, ref);
}
}
void ResourceCache::destructor(SkiaColorFilter* resource) {
Mutex::Autolock _l(mLock);
- ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+ destructorLocked(resource);
+}
+
+void ResourceCache::destructorLocked(SkiaColorFilter* resource) {
+ ssize_t index = mCache->indexOfKey(resource);
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
if (ref == NULL) {
// If we're not tracking this resource, just delete it
delete resource;
@@ -185,7 +261,30 @@ void ResourceCache::destructor(SkiaColorFilter* resource) {
}
ref->destroyed = true;
if (ref->refCount == 0) {
- deleteResourceReference(resource, ref);
+ deleteResourceReferenceLocked(resource, ref);
+ }
+}
+
+void ResourceCache::recycle(SkBitmap* resource) {
+ Mutex::Autolock _l(mLock);
+ recycleLocked(resource);
+}
+
+void ResourceCache::recycleLocked(SkBitmap* resource) {
+ ssize_t index = mCache->indexOfKey(resource);
+ if (index < 0) {
+ // not tracking this resource; just recycle the pixel data
+ resource->setPixels(NULL, NULL);
+ return;
+ }
+ ResourceReference* ref = mCache->valueAt(index);
+ if (ref == NULL) {
+ // Should not get here - shouldn't get a call to recycle if we're not yet tracking it
+ return;
+ }
+ ref->recycled = true;
+ if (ref->refCount == 0) {
+ deleteResourceReferenceLocked(resource, ref);
}
}
@@ -193,11 +292,11 @@ void ResourceCache::destructor(SkiaColorFilter* resource) {
* This method should only be called while the mLock mutex is held (that mutex is grabbed
* by the various destructor() and recycle() methods which call this method).
*/
-void ResourceCache::deleteResourceReference(void* resource, ResourceReference* ref) {
+void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceReference* ref) {
if (ref->recycled && ref->resourceType == kBitmap) {
((SkBitmap*) resource)->setPixels(NULL, NULL);
}
- if (ref->destroyed) {
+ if (ref->destroyed || ref->resourceType == kLayer) {
switch (ref->resourceType) {
case kBitmap: {
SkBitmap* bitmap = (SkBitmap*) resource;
@@ -225,6 +324,11 @@ void ResourceCache::deleteResourceReference(void* resource, ResourceReference* r
delete filter;
}
break;
+ case kLayer: {
+ Layer* layer = (Layer*) resource;
+ Caches::getInstance().deleteLayerDeferred(layer);
+ }
+ break;
}
}
mCache->removeItem(resource);
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index 8cf466baa4e8..a80670ce36ed 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -23,6 +23,7 @@
#include <SkiaColorFilter.h>
#include <SkiaShader.h>
#include <utils/KeyedVector.h>
+#include "Layer.h"
namespace android {
namespace uirenderer {
@@ -35,6 +36,7 @@ enum ResourceType {
kShader,
kColorFilter,
kPath,
+ kLayer
};
class ResourceReference {
@@ -52,28 +54,63 @@ public:
};
class ANDROID_API ResourceCache {
- KeyedVector<void *, ResourceReference *>* mCache;
public:
ResourceCache();
~ResourceCache();
+
+ /**
+ * When using these two methods, make sure to only invoke the *Locked()
+ * variants of increment/decrementRefcount(), recyle() and destructor()
+ */
+ void lock();
+ void unlock();
+
void incrementRefcount(SkPath* resource);
void incrementRefcount(SkBitmap* resource);
void incrementRefcount(SkiaShader* resource);
void incrementRefcount(SkiaColorFilter* resource);
- void incrementRefcount(const void* resource, ResourceType resourceType);
- void decrementRefcount(void* resource);
+ void incrementRefcount(Layer* resource);
+
+ void incrementRefcountLocked(SkPath* resource);
+ void incrementRefcountLocked(SkBitmap* resource);
+ void incrementRefcountLocked(SkiaShader* resource);
+ void incrementRefcountLocked(SkiaColorFilter* resource);
+ void incrementRefcountLocked(Layer* resource);
+
void decrementRefcount(SkBitmap* resource);
void decrementRefcount(SkPath* resource);
void decrementRefcount(SkiaShader* resource);
void decrementRefcount(SkiaColorFilter* resource);
- void recycle(SkBitmap* resource);
+ void decrementRefcount(Layer* resource);
+
+ void decrementRefcountLocked(SkBitmap* resource);
+ void decrementRefcountLocked(SkPath* resource);
+ void decrementRefcountLocked(SkiaShader* resource);
+ void decrementRefcountLocked(SkiaColorFilter* resource);
+ void decrementRefcountLocked(Layer* resource);
+
void destructor(SkPath* resource);
void destructor(SkBitmap* resource);
void destructor(SkiaShader* resource);
void destructor(SkiaColorFilter* resource);
+
+ void destructorLocked(SkPath* resource);
+ void destructorLocked(SkBitmap* resource);
+ void destructorLocked(SkiaShader* resource);
+ void destructorLocked(SkiaColorFilter* resource);
+
+ void recycle(SkBitmap* resource);
+ void recycleLocked(SkBitmap* resource);
+
private:
- void deleteResourceReference(void* resource, ResourceReference* ref);
+ void deleteResourceReferenceLocked(void* resource, ResourceReference* ref);
+
void incrementRefcount(void* resource, ResourceType resourceType);
+ void incrementRefcountLocked(void* resource, ResourceType resourceType);
+
+ void decrementRefcount(void* resource);
+ void decrementRefcountLocked(void* resource);
+
void logCache();
/**
@@ -82,6 +119,8 @@ private:
* or a reference queue finalization thread.
*/
mutable Mutex mLock;
+
+ KeyedVector<void*, ResourceReference*>* mCache;
};
}; // namespace uirenderer
diff --git a/libs/hwui/SkiaColorFilter.cpp b/libs/hwui/SkiaColorFilter.cpp
index b86bbc5f8f4c..df918beb53ef 100644
--- a/libs/hwui/SkiaColorFilter.cpp
+++ b/libs/hwui/SkiaColorFilter.cpp
@@ -34,13 +34,16 @@ SkiaColorFilter::~SkiaColorFilter() {
// Color matrix filter
///////////////////////////////////////////////////////////////////////////////
-SkiaColorMatrixFilter::SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector):
+SkiaColorMatrixFilter::SkiaColorMatrixFilter(SkColorFilter* skFilter, float* matrix, float* vector):
SkiaColorFilter(skFilter, kColorMatrix, true), mMatrix(matrix), mVector(vector) {
// Skia uses the range [0..255] for the addition vector, but we need
// the [0..1] range to apply the vector in GLSL
for (int i = 0; i < 4; i++) {
mVector[i] /= 255.0f;
}
+
+ // TODO: We should be smarter about this
+ mBlend = true;
}
SkiaColorMatrixFilter::~SkiaColorMatrixFilter() {
@@ -62,7 +65,7 @@ void SkiaColorMatrixFilter::setupProgram(Program* program) {
// Lighting color filter
///////////////////////////////////////////////////////////////////////////////
-SkiaLightingFilter::SkiaLightingFilter(SkColorFilter *skFilter, int multiply, int add):
+SkiaLightingFilter::SkiaLightingFilter(SkColorFilter* skFilter, int multiply, int add):
SkiaColorFilter(skFilter, kLighting, true) {
mMulR = ((multiply >> 16) & 0xFF) / 255.0f;
mMulG = ((multiply >> 8) & 0xFF) / 255.0f;
@@ -71,6 +74,9 @@ SkiaLightingFilter::SkiaLightingFilter(SkColorFilter *skFilter, int multiply, in
mAddR = ((add >> 16) & 0xFF) / 255.0f;
mAddG = ((add >> 8) & 0xFF) / 255.0f;
mAddB = ((add ) & 0xFF) / 255.0f;
+
+ // A lighting filter always ignores alpha
+ mBlend = false;
}
void SkiaLightingFilter::describe(ProgramDescription& description, const Extensions& extensions) {
@@ -86,13 +92,16 @@ void SkiaLightingFilter::setupProgram(Program* program) {
// Blend color filter
///////////////////////////////////////////////////////////////////////////////
-SkiaBlendFilter::SkiaBlendFilter(SkColorFilter *skFilter, int color, SkXfermode::Mode mode):
+SkiaBlendFilter::SkiaBlendFilter(SkColorFilter* skFilter, int color, SkXfermode::Mode mode):
SkiaColorFilter(skFilter, kBlend, true), mMode(mode) {
const int alpha = (color >> 24) & 0xFF;
mA = alpha / 255.0f;
mR = mA * ((color >> 16) & 0xFF) / 255.0f;
mG = mA * ((color >> 8) & 0xFF) / 255.0f;
mB = mA * ((color ) & 0xFF) / 255.0f;
+
+ // TODO: We should do something smarter here
+ mBlend = true;
}
void SkiaBlendFilter::describe(ProgramDescription& description, const Extensions& extensions) {
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 66993a48e088..9013fd5f9fed 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -38,6 +38,22 @@ static const GLint gTileModes[] = {
GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode
};
+/**
+ * This function does not work for n == 0.
+ */
+static inline bool isPowerOfTwo(unsigned int n) {
+ return !(n & (n - 1));
+}
+
+static inline void bindUniformColor(int slot, uint32_t color) {
+ const float a = ((color >> 24) & 0xff) / 255.0f;
+ glUniform4f(slot,
+ a * ((color >> 16) & 0xff) / 255.0f,
+ a * ((color >> 8) & 0xff) / 255.0f,
+ a * ((color ) & 0xff) / 255.0f,
+ a);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Base shader
///////////////////////////////////////////////////////////////////////////////
@@ -139,10 +155,6 @@ void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
// Uniforms
bindTexture(texture, mWrapS, mWrapT);
- // Assume linear here; we should really check the transform in
- // ::updateTransforms() but we don't have the texture object
- // available at that point. The optimization is not worth the
- // effort for now.
texture->setFilter(GL_LINEAR);
glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
@@ -151,14 +163,6 @@ void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
}
-void SkiaBitmapShader::updateTransforms(Program* program, const mat4& modelView,
- const Snapshot& snapshot) {
- mat4 textureTransform;
- computeScreenSpaceMatrix(textureTransform, modelView);
- glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
- GL_FALSE, &textureTransform.data[0]);
-}
-
///////////////////////////////////////////////////////////////////////////////
// Linear gradient shader
///////////////////////////////////////////////////////////////////////////////
@@ -188,6 +192,8 @@ SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colo
mUnitMatrix.load(unitMatrix);
updateLocalMatrix(matrix);
+
+ mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
}
SkiaLinearGradientShader::~SkiaLinearGradientShader() {
@@ -206,6 +212,7 @@ SkiaShader* SkiaLinearGradientShader::copy() {
copy->mPositions = new float[mCount];
memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
copy->mCount = mCount;
+ copy->mIsSimple = mIsSimple;
return copy;
}
@@ -213,26 +220,27 @@ void SkiaLinearGradientShader::describe(ProgramDescription& description,
const Extensions& extensions) {
description.hasGradient = true;
description.gradientType = ProgramDescription::kGradientLinear;
+ description.isSimpleGradient = mIsSimple;
}
void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
const Snapshot& snapshot, GLuint* textureUnit) {
- GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
+ if (CC_UNLIKELY(!mIsSimple)) {
+ GLuint textureSlot = (*textureUnit)++;
+ Caches::getInstance().activeTexture(textureSlot);
- Texture* texture = mGradientCache->get(mColors, mPositions, mCount, mTileX);
+ Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
- mat4 screenSpace;
- computeScreenSpaceMatrix(screenSpace, modelView);
+ // Uniforms
+ bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
+ glUniform1i(program->getUniform("gradientSampler"), textureSlot);
+ } else {
+ bindUniformColor(program->getUniform("startColor"), mColors[0]);
+ bindUniformColor(program->getUniform("endColor"), mColors[1]);
+ }
- // Uniforms
- bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
- glUniform1i(program->getUniform("gradientSampler"), textureSlot);
- glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
-}
+ Caches::getInstance().dither.setupProgram(program, textureUnit);
-void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& modelView,
- const Snapshot& snapshot) {
mat4 screenSpace;
computeScreenSpaceMatrix(screenSpace, modelView);
glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
@@ -269,6 +277,7 @@ SkiaShader* SkiaCircularGradientShader::copy() {
copy->mPositions = new float[mCount];
memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
copy->mCount = mCount;
+ copy->mIsSimple = mIsSimple;
return copy;
}
@@ -276,6 +285,7 @@ void SkiaCircularGradientShader::describe(ProgramDescription& description,
const Extensions& extensions) {
description.hasGradient = true;
description.gradientType = ProgramDescription::kGradientCircular;
+ description.isSimpleGradient = mIsSimple;
}
///////////////////////////////////////////////////////////////////////////////
@@ -296,6 +306,8 @@ SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* col
mUnitMatrix.load(unitMatrix);
updateLocalMatrix(matrix);
+
+ mIsSimple = count == 2;
}
SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors,
@@ -303,6 +315,8 @@ SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, ui
SkMatrix* matrix, bool blend):
SkiaShader(type, key, tileMode, tileMode, matrix, blend),
mColors(colors), mPositions(positions), mCount(count) {
+
+ mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
}
SkiaSweepGradientShader::~SkiaSweepGradientShader() {
@@ -318,6 +332,7 @@ SkiaShader* SkiaSweepGradientShader::copy() {
copy->mPositions = new float[mCount];
memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
copy->mCount = mCount;
+ copy->mIsSimple = mIsSimple;
return copy;
}
@@ -325,26 +340,27 @@ void SkiaSweepGradientShader::describe(ProgramDescription& description,
const Extensions& extensions) {
description.hasGradient = true;
description.gradientType = ProgramDescription::kGradientSweep;
+ description.isSimpleGradient = mIsSimple;
}
void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
const Snapshot& snapshot, GLuint* textureUnit) {
- GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
+ if (CC_UNLIKELY(!mIsSimple)) {
+ GLuint textureSlot = (*textureUnit)++;
+ Caches::getInstance().activeTexture(textureSlot);
- Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
+ Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
- mat4 screenSpace;
- computeScreenSpaceMatrix(screenSpace, modelView);
+ // Uniforms
+ bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
+ glUniform1i(program->getUniform("gradientSampler"), textureSlot);
+ } else {
+ bindUniformColor(program->getUniform("startColor"), mColors[0]);
+ bindUniformColor(program->getUniform("endColor"), mColors[1]);
+ }
- // Uniforms
- bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
- glUniform1i(program->getUniform("gradientSampler"), textureSlot);
- glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
-}
+ Caches::getInstance().dither.setupProgram(program, textureUnit);
-void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& modelView,
- const Snapshot& snapshot) {
mat4 screenSpace;
computeScreenSpaceMatrix(screenSpace, modelView);
glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 2de9a93bd136..26875928cf98 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -82,10 +82,6 @@ struct SkiaShader {
mGradientCache = gradientCache;
}
- virtual void updateTransforms(Program* program, const mat4& modelView,
- const Snapshot& snapshot) {
- }
-
uint32_t getGenerationId() {
return mGenerationId;
}
@@ -148,19 +144,11 @@ struct SkiaBitmapShader: public SkiaShader {
void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
GLuint* textureUnit);
- void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
private:
SkiaBitmapShader() {
}
- /**
- * This method does not work for n == 0.
- */
- inline bool isPowerOfTwo(unsigned int n) {
- return !(n & (n - 1));
- }
-
SkBitmap* mBitmap;
Texture* mTexture;
GLenum mWrapS;
@@ -179,12 +167,12 @@ struct SkiaLinearGradientShader: public SkiaShader {
void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
GLuint* textureUnit);
- void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
private:
SkiaLinearGradientShader() {
}
+ bool mIsSimple;
float* mBounds;
uint32_t* mColors;
float* mPositions;
@@ -203,7 +191,6 @@ struct SkiaSweepGradientShader: public SkiaShader {
virtual void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
GLuint* textureUnit);
- void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
protected:
SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions,
@@ -211,6 +198,7 @@ protected:
SkiaSweepGradientShader() {
}
+ bool mIsSimple;
uint32_t* mColors;
float* mPositions;
int mCount;
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 5d5961a14004..fbc84554dcdf 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -57,7 +57,7 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags):
clipRect = &mClipRectRoot;
#if STENCIL_BUFFER_SIZE
if (s->clipRegion) {
- mClipRegionRoot.merge(*s->clipRegion);
+ mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op);
clipRegion = &mClipRegionRoot;
}
#endif
@@ -84,8 +84,7 @@ void Snapshot::ensureClipRegion() {
#if STENCIL_BUFFER_SIZE
if (!clipRegion) {
clipRegion = &mClipRegionRoot;
- android::Rect tmp(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
- clipRegion->set(tmp);
+ clipRegion->setRect(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
}
#endif
}
@@ -93,11 +92,11 @@ void Snapshot::ensureClipRegion() {
void Snapshot::copyClipRectFromRegion() {
#if STENCIL_BUFFER_SIZE
if (!clipRegion->isEmpty()) {
- android::Rect bounds(clipRegion->bounds());
- clipRect->set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ const SkIRect& bounds = clipRegion->getBounds();
+ clipRect->set(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
if (clipRegion->isRect()) {
- clipRegion->clear();
+ clipRegion->setEmpty();
clipRegion = NULL;
}
} else {
@@ -107,43 +106,11 @@ void Snapshot::copyClipRectFromRegion() {
#endif
}
-bool Snapshot::clipRegionOr(float left, float top, float right, float bottom) {
+bool Snapshot::clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op) {
#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->orSelf(tmp);
- copyClipRectFromRegion();
- return true;
-#else
- return false;
-#endif
-}
-
-bool Snapshot::clipRegionXor(float left, float top, float right, float bottom) {
-#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->xorSelf(tmp);
- copyClipRectFromRegion();
- return true;
-#else
- return false;
-#endif
-}
-
-bool Snapshot::clipRegionAnd(float left, float top, float right, float bottom) {
-#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->andSelf(tmp);
- copyClipRectFromRegion();
- return true;
-#else
- return false;
-#endif
-}
-
-bool Snapshot::clipRegionNand(float left, float top, float right, float bottom) {
-#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->subtractSelf(tmp);
+ SkIRect tmp;
+ tmp.set(left, top, right, bottom);
+ clipRegion->op(tmp, op);
copyClipRectFromRegion();
return true;
#else
@@ -161,14 +128,9 @@ bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
bool clipped = false;
switch (op) {
- case SkRegion::kDifference_Op: {
- ensureClipRegion();
- clipped = clipRegionNand(r.left, r.top, r.right, r.bottom);
- break;
- }
case SkRegion::kIntersect_Op: {
if (CC_UNLIKELY(clipRegion)) {
- clipped = clipRegionOr(r.left, r.top, r.right, r.bottom);
+ clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
} else {
clipped = clipRect->intersect(r);
if (!clipped) {
@@ -180,26 +142,22 @@ bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
}
case SkRegion::kUnion_Op: {
if (CC_UNLIKELY(clipRegion)) {
- clipped = clipRegionAnd(r.left, r.top, r.right, r.bottom);
+ clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op);
} else {
clipped = clipRect->unionWith(r);
}
break;
}
- case SkRegion::kXOR_Op: {
- ensureClipRegion();
- clipped = clipRegionXor(r.left, r.top, r.right, r.bottom);
- break;
- }
- case SkRegion::kReverseDifference_Op: {
- // TODO!!!!!!!
- break;
- }
case SkRegion::kReplace_Op: {
setClip(r.left, r.top, r.right, r.bottom);
clipped = true;
break;
}
+ default: {
+ ensureClipRegion();
+ clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, op);
+ break;
+ }
}
if (clipped) {
@@ -213,13 +171,17 @@ void Snapshot::setClip(float left, float top, float right, float bottom) {
clipRect->set(left, top, right, bottom);
#if STENCIL_BUFFER_SIZE
if (clipRegion) {
- clipRegion->clear();
+ clipRegion->setEmpty();
clipRegion = NULL;
}
#endif
flags |= Snapshot::kFlagClipSet;
}
+bool Snapshot::hasPerspectiveTransform() const {
+ return transform->isPerspective();
+}
+
const Rect& Snapshot::getLocalClip() {
mat4 inverse;
inverse.loadInverse(*transform);
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 30b03fc33db6..9c612ffb86f1 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -121,6 +121,11 @@ public:
bool isIgnored() const;
/**
+ * Indicates whether the current transform has perspective components.
+ */
+ bool hasPerspectiveTransform() const;
+
+ /**
* Dirty flags.
*/
int flags;
@@ -198,7 +203,7 @@ public:
*
* This field is used only if STENCIL_BUFFER_SIZE is > 0.
*/
- Region* clipRegion;
+ SkRegion* clipRegion;
/**
* The ancestor layer's dirty region.
@@ -223,17 +228,14 @@ private:
void ensureClipRegion();
void copyClipRectFromRegion();
- bool clipRegionOr(float left, float top, float right, float bottom);
- bool clipRegionXor(float left, float top, float right, float bottom);
- bool clipRegionAnd(float left, float top, float right, float bottom);
- bool clipRegionNand(float left, float top, float right, float bottom);
+ bool clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op);
mat4 mTransformRoot;
Rect mClipRectRoot;
Rect mLocalClip;
#if STENCIL_BUFFER_SIZE
- Region mClipRegionRoot;
+ SkRegion mClipRegionRoot;
#endif
}; // class Snapshot
diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/Stencil.cpp
new file mode 100644
index 000000000000..84df82b7b08f
--- /dev/null
+++ b/libs/hwui/Stencil.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <GLES2/gl2.h>
+
+#include "Properties.h"
+#include "Stencil.h"
+
+namespace android {
+namespace uirenderer {
+
+Stencil::Stencil(): mState(kDisabled) {
+}
+
+uint32_t Stencil::getStencilSize() {
+ return STENCIL_BUFFER_SIZE;
+}
+
+void Stencil::clear() {
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+}
+
+void Stencil::enableTest() {
+ if (mState != kTest) {
+ enable();
+ glStencilFunc(GL_EQUAL, 0x1, 0x1);
+ // We only want to test, let's keep everything
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ mState = kTest;
+ }
+}
+
+void Stencil::enableWrite() {
+ if (mState != kWrite) {
+ enable();
+ glStencilFunc(GL_ALWAYS, 0x1, 0x1);
+ // The test always passes so the first two values are meaningless
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ mState = kWrite;
+ }
+}
+
+void Stencil::enableDebugTest(GLint value, bool greater) {
+ enable();
+ glStencilFunc(greater ? GL_LESS : GL_EQUAL, value, 0xffffffff);
+ // We only want to test, let's keep everything
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ mState = kTest;
+}
+
+void Stencil::enableDebugWrite() {
+ if (mState != kWrite) {
+ enable();
+ glStencilFunc(GL_ALWAYS, 0x1, 0xffffffff);
+ // The test always passes so the first two values are meaningless
+ glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ mState = kWrite;
+ }
+}
+
+void Stencil::enable() {
+ if (mState == kDisabled) {
+ glEnable(GL_STENCIL_TEST);
+ }
+}
+
+void Stencil::disable() {
+ if (mState != kDisabled) {
+ glDisable(GL_STENCIL_TEST);
+ mState = kDisabled;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Stencil.h b/libs/hwui/Stencil.h
new file mode 100644
index 000000000000..2f8a66a54693
--- /dev/null
+++ b/libs/hwui/Stencil.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 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_STENCIL_H
+#define ANDROID_HWUI_STENCIL_H
+
+#ifndef LOG_TAG
+ #define LOG_TAG "OpenGLRenderer"
+#endif
+
+#include <cutils/compiler.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Stencil buffer management
+///////////////////////////////////////////////////////////////////////////////
+
+class ANDROID_API Stencil {
+public:
+ Stencil();
+
+ /**
+ * Returns the desired size for the stencil buffer. If the returned value
+ * is 0, then no stencil buffer is required.
+ */
+ ANDROID_API static uint32_t getStencilSize();
+
+ /**
+ * Clears the stencil buffer.
+ */
+ void clear();
+
+ /**
+ * Enables stencil test. When the stencil test is enabled the stencil
+ * buffer is not written into.
+ */
+ void enableTest();
+
+ /**
+ * Enables stencil write. When stencil write is enabled, the stencil
+ * test always succeeds and the value 0x1 is written in the stencil
+ * buffer for each fragment.
+ */
+ void enableWrite();
+
+ /**
+ * The test passes only when equal to the specified value.
+ */
+ void enableDebugTest(GLint value, bool greater = false);
+
+ /**
+ * Used for debugging. The stencil test always passes and increments.
+ */
+ void enableDebugWrite();
+
+ /**
+ * Disables stencil test and write.
+ */
+ void disable();
+
+ /**
+ * Indicates whether either test or write is enabled.
+ */
+ bool isEnabled() {
+ return mState != kDisabled;
+ }
+
+private:
+ void enable();
+
+ enum StencilState {
+ kDisabled,
+ kTest,
+ kWrite
+ };
+
+ StencilState mState;
+
+}; // class Stencil
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_STENCIL_H
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index bef137371d44..8426f5869189 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -102,13 +102,15 @@ void TextDropShadowCache::clear() {
}
ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len,
- int numGlyphs, uint32_t radius) {
- ShadowText entry(paint, radius, len, text);
+ int numGlyphs, uint32_t radius, const float* positions) {
+ ShadowText entry(paint, radius, len, text, positions);
ShadowTexture* texture = mCache.get(entry);
if (!texture) {
- FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(paint, text, 0,
- len, numGlyphs, radius);
+ SkPaint paintCopy(*paint);
+ paintCopy.setTextAlign(SkPaint::kLeft_Align);
+ FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, text, 0,
+ len, numGlyphs, radius, positions);
texture = new ShadowTexture;
texture->left = shadow.penX;
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index e2bdde1eca89..bae0c49349f6 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -35,8 +35,9 @@ struct ShadowText {
ShadowText(): radius(0), len(0), textSize(0.0f), typeface(NULL) {
}
- ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText):
- radius(radius), len(len) {
+ ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText,
+ const float* positions):
+ radius(radius), len(len), positions(positions) {
// TODO: Propagate this through the API, we should not cast here
text = (const char16_t*) srcText;
@@ -66,11 +67,18 @@ struct ShadowText {
uint32_t italicStyle;
uint32_t scaleX;
const char16_t* text;
+ const float* positions;
String16 str;
+ Vector<float> positionsCopy;
void copyTextLocally() {
str.setTo((const char16_t*) text, len >> 1);
text = str.string();
+ if (positions != NULL) {
+ positionsCopy.clear();
+ positionsCopy.appendArray(positions, len);
+ positions = positionsCopy.array();
+ }
}
bool operator<(const ShadowText& rhs) const {
@@ -81,7 +89,12 @@ struct ShadowText {
LTE_INT(flags) {
LTE_INT(italicStyle) {
LTE_INT(scaleX) {
- return memcmp(text, rhs.text, len) < 0;
+ int cmp = memcmp(text, rhs.text, len);
+ if (cmp < 0) return true;
+ if (cmp == 0 && rhs.positions != NULL) {
+ if (positions == NULL) return true;
+ return memcmp(positions, rhs.positions, len << 2) < 0;
+ }
}
}
}
@@ -117,7 +130,7 @@ public:
void operator()(ShadowText& text, ShadowTexture*& texture);
ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len,
- int numGlyphs, uint32_t radius);
+ int numGlyphs, uint32_t radius, const float* positions);
/**
* Clears the cache. This causes all textures to be deleted.
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 1adf2c77e7c3..03e2172a7aaf 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -38,6 +38,8 @@ struct Texture {
firstFilter = true;
firstWrap = true;
+
+ id = 0;
}
void setWrap(GLenum wrap, bool bindTexture = false, bool force = false,
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
new file mode 100644
index 000000000000..f65359217768
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <utils/Log.h>
+
+#include "Debug.h"
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheBlock
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
+ * order, except for the final block (the remainder space at the right, since we fill from the
+ * left).
+ */
+CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock* newBlock) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+ newBlock, newBlock->mX, newBlock->mY,
+ newBlock->mWidth, newBlock->mHeight);
+#endif
+
+ CacheBlock* currBlock = head;
+ CacheBlock* prevBlock = NULL;
+
+ while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
+ if (newBlock->mWidth < currBlock->mWidth) {
+ newBlock->mNext = currBlock;
+ newBlock->mPrev = prevBlock;
+ currBlock->mPrev = newBlock;
+
+ if (prevBlock) {
+ prevBlock->mNext = newBlock;
+ return head;
+ } else {
+ return newBlock;
+ }
+ }
+
+ prevBlock = currBlock;
+ currBlock = currBlock->mNext;
+ }
+
+ // new block larger than all others - insert at end (but before the remainder space, if there)
+ newBlock->mNext = currBlock;
+ newBlock->mPrev = prevBlock;
+
+ if (currBlock) {
+ currBlock->mPrev = newBlock;
+ }
+
+ if (prevBlock) {
+ prevBlock->mNext = newBlock;
+ return head;
+ } else {
+ return newBlock;
+ }
+}
+
+CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+ blockToRemove, blockToRemove->mX, blockToRemove->mY,
+ blockToRemove->mWidth, blockToRemove->mHeight);
+#endif
+
+ CacheBlock* newHead = head;
+ CacheBlock* nextBlock = blockToRemove->mNext;
+ CacheBlock* prevBlock = blockToRemove->mPrev;
+
+ if (prevBlock) {
+ prevBlock->mNext = nextBlock;
+ } else {
+ newHead = nextBlock;
+ }
+
+ if (nextBlock) {
+ nextBlock->mPrev = prevBlock;
+ }
+
+ delete blockToRemove;
+
+ return newHead;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheTexture
+///////////////////////////////////////////////////////////////////////////////
+
+bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) {
+ return false;
+ }
+
+ uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
+ uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
+
+ // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
+ // This columns for glyphs that are close but not necessarily exactly the same size. It trades
+ // off the loss of a few pixels for some glyphs against the ability to store more glyphs
+ // of varying sizes in one block.
+ uint16_t roundedUpW = (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
+
+ CacheBlock* cacheBlock = mCacheBlocks;
+ while (cacheBlock) {
+ // Store glyph in this block iff: it fits the block's remaining space and:
+ // it's the remainder space (mY == 0) or there's only enough height for this one glyph
+ // or it's within ROUNDING_SIZE of the block width
+ if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
+ (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
+ (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
+ if (cacheBlock->mHeight - glyphH < glyphH) {
+ // Only enough space for this glyph - don't bother rounding up the width
+ roundedUpW = glyphW;
+ }
+
+ *retOriginX = cacheBlock->mX;
+ *retOriginY = cacheBlock->mY;
+
+ // If this is the remainder space, create a new cache block for this column. Otherwise,
+ // adjust the info about this column.
+ if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
+ uint16_t oldX = cacheBlock->mX;
+ // Adjust remainder space dimensions
+ cacheBlock->mWidth -= roundedUpW;
+ cacheBlock->mX += roundedUpW;
+
+ if (mHeight - glyphH >= glyphH) {
+ // There's enough height left over to create a new CacheBlock
+ CacheBlock* newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
+ roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ newBlock, newBlock->mX, newBlock->mY,
+ newBlock->mWidth, newBlock->mHeight);
+#endif
+ mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
+ }
+ } else {
+ // Insert into current column and adjust column dimensions
+ cacheBlock->mY += glyphH;
+ cacheBlock->mHeight -= glyphH;
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ cacheBlock, cacheBlock->mX, cacheBlock->mY,
+ cacheBlock->mWidth, cacheBlock->mHeight);
+#endif
+ }
+
+ if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
+ // If remaining space in this block is too small to be useful, remove it
+ mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
+ }
+
+ mDirty = true;
+ const Rect r(*retOriginX - TEXTURE_BORDER_SIZE, *retOriginY - TEXTURE_BORDER_SIZE,
+ *retOriginX + glyphW, *retOriginY + glyphH);
+ mDirtyRect.unionWith(r);
+ mNumGlyphs++;
+
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: current block list:");
+ mCacheBlocks->output();
+#endif
+
+ return true;
+ }
+ cacheBlock = cacheBlock->mNext;
+ }
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
+#endif
+ return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
new file mode 100644
index 000000000000..fdd16232bc41
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2012 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_CACHE_TEXTURE_H
+#define ANDROID_HWUI_CACHE_TEXTURE_H
+
+#include <GLES2/gl2.h>
+
+#include <SkScalerContext.h>
+
+#include <utils/Log.h>
+
+#include "FontUtil.h"
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
+ * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
+ * When we add a glyph to the cache, we see if it fits within one of the existing columns that
+ * have already been started (this is the case if the glyph fits vertically as well as
+ * horizontally, and if its width is sufficiently close to the column width to avoid
+ * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
+ * glyph fits, we check the final node, which is the remaining space in the cache, creating
+ * a new column as appropriate.
+ *
+ * As columns fill up, we remove their CacheBlock from the list to avoid having to check
+ * small blocks in the future.
+ */
+struct CacheBlock {
+ uint16_t mX;
+ uint16_t mY;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ CacheBlock* mNext;
+ CacheBlock* mPrev;
+
+ CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
+ mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) {
+ }
+
+ static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock);
+
+ static CacheBlock* removeBlock(CacheBlock* head, CacheBlock* blockToRemove);
+
+ void output() {
+ CacheBlock* currBlock = this;
+ while (currBlock) {
+ ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
+ currBlock = currBlock->mNext;
+ }
+ }
+};
+
+class CacheTexture {
+public:
+ CacheTexture(uint16_t width, uint16_t height) :
+ mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
+ mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
+ mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ }
+
+ ~CacheTexture() {
+ releaseTexture();
+ reset();
+ }
+
+ void reset() {
+ // Delete existing cache blocks
+ while (mCacheBlocks != NULL) {
+ CacheBlock* tmpBlock = mCacheBlocks;
+ mCacheBlocks = mCacheBlocks->mNext;
+ delete tmpBlock;
+ }
+ mNumGlyphs = 0;
+ }
+
+ void init() {
+ // reset, then create a new remainder space to start again
+ reset();
+ mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ }
+
+ void releaseTexture() {
+ if (mTexture) {
+ delete[] mTexture;
+ mTexture = NULL;
+ }
+ if (mTextureId) {
+ glDeleteTextures(1, &mTextureId);
+ mTextureId = 0;
+ }
+ mDirty = false;
+ }
+
+ /**
+ * This method assumes that the proper texture unit is active.
+ */
+ void allocateTexture() {
+ if (!mTexture) {
+ mTexture = new uint8_t[mWidth * mHeight];
+ }
+
+ if (!mTextureId) {
+ glGenTextures(1, &mTextureId);
+
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ // Initialize texture dimensions
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mWidth, mHeight, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+
+ const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ }
+
+ bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
+
+ inline uint16_t getWidth() const {
+ return mWidth;
+ }
+
+ inline uint16_t getHeight() const {
+ return mHeight;
+ }
+
+ inline const Rect* getDirtyRect() const {
+ return &mDirtyRect;
+ }
+
+ inline uint8_t* getTexture() const {
+ return mTexture;
+ }
+
+ GLuint getTextureId() {
+ allocateTexture();
+ return mTextureId;
+ }
+
+ inline bool isDirty() const {
+ return mDirty;
+ }
+
+ inline void setDirty(bool dirty) {
+ mDirty = dirty;
+ if (!dirty) {
+ mDirtyRect.setEmpty();
+ }
+ }
+
+ inline bool getLinearFiltering() const {
+ return mLinearFiltering;
+ }
+
+ /**
+ * This method assumes that the proper texture unit is active.
+ */
+ void setLinearFiltering(bool linearFiltering, bool bind = true) {
+ if (linearFiltering != mLinearFiltering) {
+ mLinearFiltering = linearFiltering;
+
+ const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
+ if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+ }
+ }
+
+ inline uint16_t getGlyphCount() const {
+ return mNumGlyphs;
+ }
+
+private:
+ uint8_t* mTexture;
+ GLuint mTextureId;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ bool mLinearFiltering;
+ bool mDirty;
+ uint16_t mNumGlyphs;
+ CacheBlock* mCacheBlocks;
+ Rect mDirtyRect;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHE_TEXTURE_H
diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h
new file mode 100644
index 000000000000..6680a00bc3f9
--- /dev/null
+++ b/libs/hwui/font/CachedGlyphInfo.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 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_CACHED_GLYPH_INFO_H
+#define ANDROID_HWUI_CACHED_GLYPH_INFO_H
+
+#include <SkFixed.h>
+
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+struct CachedGlyphInfo {
+ // Has the cache been invalidated?
+ bool mIsValid;
+ // Location of the cached glyph in the bitmap
+ // in case we need to resize the texture or
+ // render to bitmap
+ uint32_t mStartX;
+ uint32_t mStartY;
+ uint32_t mBitmapWidth;
+ uint32_t mBitmapHeight;
+ // Also cache texture coords for the quad
+ float mBitmapMinU;
+ float mBitmapMinV;
+ float mBitmapMaxU;
+ float mBitmapMaxV;
+ // Minimize how much we call freetype
+ uint32_t mGlyphIndex;
+ uint32_t mAdvanceX;
+ uint32_t mAdvanceY;
+ // Values below contain a glyph's origin in the bitmap
+ int32_t mBitmapLeft;
+ int32_t mBitmapTop;
+ // Auto-kerning
+ SkFixed mLsbDelta;
+ SkFixed mRsbDelta;
+ CacheTexture* mCacheTexture;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHED_GLYPH_INFO_H
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
new file mode 100644
index 000000000000..7bfa63d850c1
--- /dev/null
+++ b/libs/hwui/font/Font.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2012 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 <cutils/compiler.h>
+
+#include <SkUtils.h>
+
+#include "Debug.h"
+#include "FontUtil.h"
+#include "Font.h"
+#include "FontRenderer.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX,
+ SkPaint::Style style, uint32_t strokeWidth) :
+ mState(state), mFontId(fontId), mFontSize(fontSize),
+ mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
+ mStyle(style), mStrokeWidth(mStrokeWidth) {
+}
+
+
+Font::~Font() {
+ mState->removeFont(this);
+
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ delete mCachedGlyphs.valueAt(i);
+ }
+}
+
+void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
+ if (!cacheTexture || cachedGlyph->mCacheTexture == cacheTexture) {
+ cachedGlyph->mIsValid = false;
+ }
+ }
+}
+
+void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) glyph->mBitmapHeight;
+
+ if (bounds->bottom > nPenY) {
+ bounds->bottom = nPenY;
+ }
+ if (bounds->left > nPenX) {
+ bounds->left = nPenX;
+ }
+ if (bounds->right < nPenX + width) {
+ bounds->right = nPenX + width;
+ }
+ if (bounds->top < nPenY + height) {
+ bounds->top = nPenY + height;
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
+
+ float u1 = glyph->mBitmapMinU;
+ float u2 = glyph->mBitmapMaxU;
+ float v1 = glyph->mBitmapMinV;
+ float v2 = glyph->mBitmapMaxV;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) glyph->mBitmapHeight;
+
+ mState->appendMeshQuad(nPenX, nPenY, u1, v2,
+ nPenX + width, nPenY, u2, v2,
+ nPenX + width, nPenY - height, u2, v1,
+ nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
+}
+
+void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop;
+
+ uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
+ uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
+
+ CacheTexture* cacheTexture = glyph->mCacheTexture;
+ uint32_t cacheWidth = cacheTexture->getWidth();
+ const uint8_t* cacheBuffer = cacheTexture->getTexture();
+
+ uint32_t cacheX = 0, cacheY = 0;
+ int32_t bX = 0, bY = 0;
+ for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
+ for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+#if DEBUG_FONT_RENDERER
+ if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
+ ALOGE("Skipping invalid index");
+ continue;
+ }
+#endif
+ uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
+ bitmap[bY * bitmapW + bX] = tempCol;
+ }
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+ SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
+ const float halfWidth = glyph->mBitmapWidth * 0.5f;
+ const float height = glyph->mBitmapHeight;
+
+ vOffset += glyph->mBitmapTop + height;
+
+ SkPoint destination[4];
+ measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
+
+ // Move along the tangent and offset by the normal
+ destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
+ -tangent->fY * halfWidth + tangent->fX * vOffset);
+ destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
+ tangent->fY * halfWidth + tangent->fX * vOffset);
+ destination[2].set(destination[1].fX + tangent->fY * height,
+ destination[1].fY - tangent->fX * height);
+ destination[3].set(destination[0].fX + tangent->fY * height,
+ destination[0].fY - tangent->fX * height);
+
+ const float u1 = glyph->mBitmapMinU;
+ const float u2 = glyph->mBitmapMaxU;
+ const float v1 = glyph->mBitmapMinV;
+ const float v2 = glyph->mBitmapMaxV;
+
+ mState->appendRotatedMeshQuad(
+ position->fX + destination[0].fX,
+ position->fY + destination[0].fY, u1, v2,
+ position->fX + destination[1].fX,
+ position->fY + destination[1].fY, u2, v2,
+ position->fX + destination[2].fX,
+ position->fY + destination[2].fY, u2, v1,
+ position->fX + destination[3].fX,
+ position->fY + destination[3].fY, u1, v1,
+ glyph->mCacheTexture);
+}
+
+CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
+ CachedGlyphInfo* cachedGlyph = NULL;
+ ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
+ if (index >= 0) {
+ cachedGlyph = mCachedGlyphs.valueAt(index);
+ } else {
+ cachedGlyph = cacheGlyph(paint, textUnit, precaching);
+ }
+
+ // Is the glyph still in texture cache?
+ if (!cachedGlyph->mIsValid) {
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
+ updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
+ }
+
+ return cachedGlyph;
+}
+
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+ if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
+ render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
+ bitmapW, bitmapH, NULL, NULL);
+ } else {
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+ 0, 0, NULL, NULL);
+ }
+}
+
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, const float* positions) {
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+ 0, 0, NULL, positions);
+}
+
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, SkPath* path, float hOffset, float vOffset) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ text += start;
+
+ int glyphsCount = 0;
+ SkFixed prevRsbDelta = 0;
+
+ float penX = 0.0f;
+
+ SkPoint position;
+ SkVector tangent;
+
+ SkPathMeasure measure(*path, false);
+ float pathLength = SkScalarToFloat(measure.getLength());
+
+ if (paint->getTextAlign() != SkPaint::kLeft_Align) {
+ float textWidth = SkScalarToFloat(paint->measureText(text, len));
+ float pathOffset = pathLength;
+ if (paint->getTextAlign() == SkPaint::kCenter_Align) {
+ textWidth *= 0.5f;
+ pathOffset *= 0.5f;
+ }
+ penX += pathOffset - textWidth;
+ }
+
+ while (glyphsCount < numGlyphs && penX < pathLength) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+ penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+ prevRsbDelta = cachedGlyph->mRsbDelta;
+
+ if (cachedGlyph->mIsValid) {
+ drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
+ }
+
+ penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+ glyphsCount++;
+ }
+}
+
+void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds, const float* positions) {
+ if (bounds == NULL) {
+ ALOGE("No return rectangle provided to measure text");
+ return;
+ }
+ bounds->set(1e6, -1e6, -1e6, 1e6);
+ render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
+}
+
+void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
+
+ if (numGlyphs == 0 || text == NULL) {
+ return;
+ }
+ int glyphsCount = 0;
+
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
+
+ glyphsCount++;
+ }
+}
+
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ static RenderGlyph gRenderGlyph[] = {
+ &android::uirenderer::Font::drawCachedGlyph,
+ &android::uirenderer::Font::drawCachedGlyphBitmap,
+ &android::uirenderer::Font::measureCachedGlyph
+ };
+ RenderGlyph render = gRenderGlyph[mode];
+
+ text += start;
+ int glyphsCount = 0;
+
+ if (CC_LIKELY(positions == NULL)) {
+ SkFixed prevRsbDelta = 0;
+
+ float penX = x + 0.5f;
+ int penY = y;
+
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+ penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+ prevRsbDelta = cachedGlyph->mRsbDelta;
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
+ bitmap, bitmapW, bitmapH, bounds, positions);
+ }
+
+ penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+ glyphsCount++;
+ }
+ } else {
+ const SkPaint::Align align = paint->getTextAlign();
+
+ // This is for renderPosText()
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ int penX = x + positions[(glyphsCount << 1)];
+ int penY = y + positions[(glyphsCount << 1) + 1];
+
+ switch (align) {
+ case SkPaint::kRight_Align:
+ penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
+ penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
+ break;
+ case SkPaint::kCenter_Align:
+ penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
+ penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
+ default:
+ break;
+ }
+
+ (*this.*render)(cachedGlyph, penX, penY,
+ bitmap, bitmapW, bitmapH, bounds, positions);
+ }
+
+ glyphsCount++;
+ }
+ }
+}
+
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+ bool precaching) {
+ glyph->mAdvanceX = skiaGlyph.fAdvanceX;
+ glyph->mAdvanceY = skiaGlyph.fAdvanceY;
+ glyph->mBitmapLeft = skiaGlyph.fLeft;
+ glyph->mBitmapTop = skiaGlyph.fTop;
+ glyph->mLsbDelta = skiaGlyph.fLsbDelta;
+ glyph->mRsbDelta = skiaGlyph.fRsbDelta;
+
+ uint32_t startX = 0;
+ uint32_t startY = 0;
+
+ // Get the bitmap for the glyph
+ paint->findImage(skiaGlyph);
+ mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
+
+ if (!glyph->mIsValid) {
+ return;
+ }
+
+ uint32_t endX = startX + skiaGlyph.fWidth;
+ uint32_t endY = startY + skiaGlyph.fHeight;
+
+ glyph->mStartX = startX;
+ glyph->mStartY = startY;
+ glyph->mBitmapWidth = skiaGlyph.fWidth;
+ glyph->mBitmapHeight = skiaGlyph.fHeight;
+
+ uint32_t cacheWidth = glyph->mCacheTexture->getWidth();
+ uint32_t cacheHeight = glyph->mCacheTexture->getHeight();
+
+ glyph->mBitmapMinU = startX / (float) cacheWidth;
+ glyph->mBitmapMinV = startY / (float) cacheHeight;
+ glyph->mBitmapMaxU = endX / (float) cacheWidth;
+ glyph->mBitmapMaxV = endY / (float) cacheHeight;
+
+ mState->setTextureDirty();
+}
+
+CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
+ CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
+ mCachedGlyphs.add(glyph, newGlyph);
+
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
+ newGlyph->mGlyphIndex = skiaGlyph.fID;
+ newGlyph->mIsValid = false;
+
+ updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
+
+ return newGlyph;
+}
+
+Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX,
+ SkPaint::Style style, uint32_t strokeWidth) {
+ Vector<Font*> &activeFonts = state->mActiveFonts;
+
+ for (uint32_t i = 0; i < activeFonts.size(); i++) {
+ Font* font = activeFonts[i];
+ if (font->mFontId == fontId && font->mFontSize == fontSize &&
+ font->mFlags == flags && font->mItalicStyle == italicStyle &&
+ font->mScaleX == scaleX && font->mStyle == style &&
+ (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
+ return font;
+ }
+ }
+
+ Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
+ scaleX, style, strokeWidth);
+ activeFonts.push(newFont);
+ return newFont;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
new file mode 100644
index 000000000000..7cab31e8a2ab
--- /dev/null
+++ b/libs/hwui/font/Font.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 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_FONT_H
+#define ANDROID_HWUI_FONT_H
+
+#include <utils/KeyedVector.h>
+
+#include <SkScalerContext.h>
+#include <SkPaint.h>
+#include <SkPathMeasure.h>
+
+#include "CachedGlyphInfo.h"
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+class FontRenderer;
+
+/**
+ * Represents a font, defined by a Skia font id and a font size. A font is used
+ * to generate glyphs and cache them in the FontState.
+ */
+class Font {
+public:
+ enum Style {
+ kFakeBold = 1
+ };
+
+ ~Font();
+
+ /**
+ * Renders the specified string of text.
+ * If bitmap is specified, it will be used as the render target
+ */
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
+ uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, const float* positions);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, SkPath* path, float hOffset, float vOffset);
+
+ /**
+ * Creates a new font associated with the specified font state.
+ */
+ static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
+ uint32_t strokeWidth);
+
+private:
+ friend class FontRenderer;
+ typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
+ uint32_t, uint32_t, Rect*, const float*);
+
+ enum RenderMode {
+ FRAMEBUFFER,
+ BITMAP,
+ MEASURE,
+ };
+
+ void precache(SkPaint* paint, const char* text, int numGlyphs);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
+
+ void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds, const float* positions);
+
+ Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
+ uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
+
+ // Cache of glyphs
+ DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
+
+ void invalidateTextureCache(CacheTexture* cacheTexture = NULL);
+
+ CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
+ void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+ bool precaching);
+
+ void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+ SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
+
+ CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
+
+ FontRenderer* mState;
+ uint32_t mFontId;
+ float mFontSize;
+ int mFlags;
+ uint32_t mItalicStyle;
+ uint32_t mScaleX;
+ SkPaint::Style mStyle;
+ uint32_t mStrokeWidth;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FONT_H
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
new file mode 100644
index 000000000000..12247baa876a
--- /dev/null
+++ b/libs/hwui/font/FontUtil.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 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_FONT_UTIL_H
+#define ANDROID_HWUI_FONT_UTIL_H
+
+#include <SkUtils.h>
+
+#include "Properties.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
+#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 256
+#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
+#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
+
+#define TEXTURE_BORDER_SIZE 1
+
+#define CACHE_BLOCK_ROUNDING_SIZE 4
+
+#if RENDER_TEXT_AS_GLYPHS
+ typedef uint16_t glyph_t;
+ #define TO_GLYPH(g) g
+ #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
+ #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) false
+
+ static glyph_t nextGlyph(const uint16_t** srcPtr) {
+ const uint16_t* src = *srcPtr;
+ glyph_t g = *src++;
+ *srcPtr = src;
+ return g;
+ }
+#else
+ typedef SkUnichar glyph_t;
+ #define TO_GLYPH(g) ((SkUnichar) g)
+ #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
+ #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) glyph < 0
+#endif
+
+#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+
+#endif // ANDROID_HWUI_FONT_UTIL_H
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 4ec8b25ff27e..5701678b3482 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -433,12 +433,13 @@ public:
reply.readExceptionCode();
}
- void mountObb(const String16& filename, const String16& key,
+ void mountObb(const String16& rawPath, const String16& canonicalPath, const String16& key,
const sp<IObbActionListener>& token, int32_t nonce)
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
- data.writeString16(filename);
+ data.writeString16(rawPath);
+ data.writeString16(canonicalPath);
data.writeString16(key);
data.writeStrongBinder(token->asBinder());
data.writeInt32(nonce);