diff options
Diffstat (limited to 'libs')
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); |