diff options
43 files changed, 670 insertions, 235 deletions
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 007e7b9..f80fbd8 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -129,6 +129,7 @@ public class LockPatternView extends View { private long[] mVibePattern; private int mAspect; + private final Matrix mArrowMatrix = new Matrix(); /** * Represents a cell in the 3 X 3 matrix of the unlock pattern view. @@ -923,7 +924,6 @@ public class LockPatternView extends View { // This assumes that the arrow image is drawn at 12:00 with it's top edge // coincident with the circle bitmap's top edge. Bitmap arrow = green ? mBitmapArrowGreenUp : mBitmapArrowRedUp; - Matrix matrix = new Matrix(); final int cellWidth = mBitmapCircleDefault.getWidth(); final int cellHeight = mBitmapCircleDefault.getHeight(); @@ -933,10 +933,10 @@ public class LockPatternView extends View { final float angle = (float) Math.toDegrees(theta) + 90.0f; // compose matrix - matrix.setTranslate(leftX + offsetX, topY + offsetY); // transform to cell position - matrix.preRotate(angle, cellWidth / 2.0f, cellHeight / 2.0f); // rotate about cell center - matrix.preTranslate((cellWidth - arrow.getWidth()) / 2.0f, 0.0f); // translate to 12:00 pos - canvas.drawBitmap(arrow, matrix, mPaint); + mArrowMatrix.setTranslate(leftX + offsetX, topY + offsetY); // transform to cell position + mArrowMatrix.preRotate(angle, cellWidth / 2.0f, cellHeight / 2.0f); // rotate about cell center + mArrowMatrix.preTranslate((cellWidth - arrow.getWidth()) / 2.0f, 0.0f); // translate to 12:00 pos + canvas.drawBitmap(arrow, mArrowMatrix, mPaint); } /** diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index d661f7b..29158e5 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -38,7 +38,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER LOCAL_MODULE_CLASS := SHARED_LIBRARIES - LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia + LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui LOCAL_MODULE := libhwui LOCAL_MODULE_TAGS := optional LOCAL_PRELINK_MODULE := false diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 0994d82..93c5b34 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -46,18 +46,21 @@ Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO), glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); mCurrentBuffer = meshBuffer; + mRegionMesh = NULL; } -/** - * Binds the VBO used to render simple textured quads. - */ +Caches::~Caches() { + delete[] mRegionMesh; +} + +/////////////////////////////////////////////////////////////////////////////// +// VBO +/////////////////////////////////////////////////////////////////////////////// + void Caches::bindMeshBuffer() { bindMeshBuffer(meshBuffer); } -/** - * Binds the specified VBO. - */ void Caches::bindMeshBuffer(const GLuint buffer) { if (mCurrentBuffer != buffer) { glBindBuffer(GL_ARRAY_BUFFER, buffer); @@ -65,9 +68,6 @@ void Caches::bindMeshBuffer(const GLuint buffer) { } } -/** - * Unbinds the VBO used to render simple textured quads. - */ void Caches::unbindMeshBuffer() { if (mCurrentBuffer) { glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -75,5 +75,35 @@ void Caches::unbindMeshBuffer() { } } +TextureVertex* Caches::getRegionMesh() { + // Create the mesh, 2 triangles and 4 vertices per rectangle in the region + if (!mRegionMesh) { + mRegionMesh = new TextureVertex[REGION_MESH_QUAD_COUNT * 4]; + + uint16_t* regionIndices = new uint16_t[REGION_MESH_QUAD_COUNT * 6]; + for (int i = 0; i < REGION_MESH_QUAD_COUNT; i++) { + uint16_t quad = i * 4; + int index = i * 6; + regionIndices[index ] = quad; // top-left + regionIndices[index + 1] = quad + 1; // top-right + regionIndices[index + 2] = quad + 2; // bottom-left + regionIndices[index + 3] = quad + 2; // bottom-left + regionIndices[index + 4] = quad + 1; // top-right + regionIndices[index + 5] = quad + 3; // bottom-right + } + + glGenBuffers(1, &mRegionMeshIndices); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mRegionMeshIndices); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t), + regionIndices, GL_STATIC_DRAW); + + delete[] regionIndices; + } else { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mRegionMeshIndices); + } + + return mRegionMesh; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index ca22867..c019fd1 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_CACHES_H -#define ANDROID_UI_CACHES_H +#ifndef ANDROID_HWUI_CACHES_H +#define ANDROID_HWUI_CACHES_H #ifndef LOG_TAG #define LOG_TAG "OpenGLRenderer" @@ -46,6 +46,8 @@ namespace uirenderer { #define REQUIRED_TEXTURE_UNITS_COUNT 3 +#define REGION_MESH_QUAD_COUNT 512 + // Generates simple and textured vertices #define FV(x, y, u, v) { { x, y }, { u, v } } @@ -77,6 +79,7 @@ struct CacheLogger { class Caches: public Singleton<Caches> { Caches(); + ~Caches(); friend class Singleton<Caches>; @@ -84,11 +87,33 @@ class Caches: public Singleton<Caches> { GLuint mCurrentBuffer; + // Used to render layers + TextureVertex* mRegionMesh; + GLuint mRegionMeshIndices; + public: + /** + * Binds the VBO used to render simple textured quads. + */ void bindMeshBuffer(); + + /** + * Binds the specified VBO if needed. + */ void bindMeshBuffer(const GLuint buffer); + + /** + * Unbinds the VBO used to render simple textured quads. + */ void unbindMeshBuffer(); + /** + * Returns the mesh used to draw regions. Calling this method will + * bind a VBO of type GL_ELEMENT_ARRAY_BUFFER that contains the + * indices for the region mesh. + */ + TextureVertex* getRegionMesh(); + bool blend; GLenum lastSrcMode; GLenum lastDstMode; @@ -118,7 +143,6 @@ public: }; // class Caches }; // namespace uirenderer - }; // namespace android -#endif // ANDROID_UI_CACHES_H +#endif // ANDROID_HWUI_CACHES_H diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 0dd9c5a..23ccef6 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -169,7 +169,8 @@ void DisplayList::replay(OpenGLRenderer& renderer) { int saveCount = renderer.getSaveCount() - 1; while (!mReader.eof()) { - switch (mReader.readInt()) { + int op = mReader.readInt(); + switch (op) { case AcquireContext: { renderer.acquireContext(); } @@ -195,6 +196,11 @@ void DisplayList::replay(OpenGLRenderer& renderer) { getPaint(), getInt()); } break; + case SaveLayerAlpha: { + renderer.saveLayerAlpha(getFloat(), getFloat(), getFloat(), getFloat(), + getInt(), getInt()); + } + break; case Translate: { renderer.translate(getFloat(), getFloat()); } @@ -400,6 +406,15 @@ int DisplayListRenderer::saveLayer(float left, float top, float right, float bot return OpenGLRenderer::save(flags); } +int DisplayListRenderer::saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int flags) { + addOp(DisplayList::SaveLayerAlpha); + addBounds(left, top, right, bottom); + addInt(alpha); + addInt(flags); + return OpenGLRenderer::save(flags); +} + void DisplayListRenderer::translate(float dx, float dy) { addOp(DisplayList::Translate); addPoint(dx, dy); diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 6636de6..fd69fab 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_DISPLAY_LIST_RENDERER_H -#define ANDROID_UI_DISPLAY_LIST_RENDERER_H +#ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H +#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H #include <SkChunkAlloc.h> #include <SkFlattenable.h> @@ -85,6 +85,7 @@ public: Restore, RestoreToCount, SaveLayer, + SaveLayerAlpha, Translate, Rotate, Scale, @@ -222,6 +223,8 @@ public: int saveLayer(float left, float top, float right, float bottom, SkPaint* p, int flags); + int saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int flags); void translate(float dx, float dy); void rotate(float degrees); @@ -411,4 +414,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_DISPLAY_LIST_RENDERER_H +#endif // ANDROID_HWUI_DISPLAY_LIST_RENDERER_H diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index d50d36e..eceb5c1 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_EXTENSIONS_H -#define ANDROID_UI_EXTENSIONS_H +#ifndef ANDROID_HWUI_EXTENSIONS_H +#define ANDROID_HWUI_EXTENSIONS_H #include <utils/SortedVector.h> #include <utils/String8.h> @@ -93,4 +93,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_EXTENSIONS_H +#endif // ANDROID_HWUI_EXTENSIONS_H diff --git a/libs/hwui/FboCache.h b/libs/hwui/FboCache.h index ec4afe9..ad6cc3e 100644 --- a/libs/hwui/FboCache.h +++ b/libs/hwui/FboCache.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_FBO_CACHE_H -#define ANDROID_UI_FBO_CACHE_H +#ifndef ANDROID_HWUI_FBO_CACHE_H +#define ANDROID_HWUI_FBO_CACHE_H #include <GLES2/gl2.h> @@ -76,4 +76,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_FBO_CACHE_H +#endif // ANDROID_HWUI_FBO_CACHE_H diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index e1a236c..5224689 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -556,6 +556,8 @@ void FontRenderer::issueDrawCommand() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); + + mDrawn = true; } void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, @@ -595,6 +597,13 @@ void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float mCurrentQuadIndex++; + if (mBounds) { + mBounds->left = fmin(mBounds->left, x1); + mBounds->top = fmin(mBounds->top, y3); + mBounds->right = fmax(mBounds->right, x3); + mBounds->bottom = fmax(mBounds->bottom, y1); + } + if (mCurrentQuadIndex == mMaxNumberOfQuads) { issueDrawCommand(); mCurrentQuadIndex = 0; @@ -674,22 +683,27 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch return image; } -void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) { +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) { checkInit(); if (!mCurrentFont) { LOGE("No font set"); - return; + return false; } + mDrawn = false; + mBounds = bounds; mClip = clip; mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y); + mBounds = NULL; if (mCurrentQuadIndex != 0) { issueDrawCommand(); mCurrentQuadIndex = 0; } + + return mDrawn; } void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index f10efad..a76cb86 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_FONT_RENDERER_H -#define ANDROID_UI_FONT_RENDERER_H +#ifndef ANDROID_HWUI_FONT_RENDERER_H +#define ANDROID_HWUI_FONT_RENDERER_H #include <utils/String8.h> #include <utils/String16.h> @@ -132,8 +132,8 @@ public: } void setFont(SkPaint* paint, uint32_t fontId, float fontSize); - void renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, - uint32_t len, int numGlyphs, int x, int y); + bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, + uint32_t len, int numGlyphs, int x, int y, Rect* bounds); struct DropShadow { DropShadow() { }; @@ -257,6 +257,8 @@ protected: uint32_t mIndexBufferID; const Rect* mClip; + Rect* mBounds; + bool mDrawn; bool mInitialized; @@ -273,4 +275,4 @@ protected: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_FONT_RENDERER_H +#endif // ANDROID_HWUI_FONT_RENDERER_H diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h index 5fa45cf..b59ae81 100644 --- a/libs/hwui/GammaFontRenderer.h +++ b/libs/hwui/GammaFontRenderer.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_GAMMA_FONT_RENDERER_H -#define ANDROID_UI_GAMMA_FONT_RENDERER_H +#ifndef ANDROID_HWUI_GAMMA_FONT_RENDERER_H +#define ANDROID_HWUI_GAMMA_FONT_RENDERER_H #include <SkPaint.h> @@ -45,4 +45,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_GAMMA_FONT_RENDERER_H +#endif // ANDROID_HWUI_GAMMA_FONT_RENDERER_H diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 48877f6..c9553f4 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_GRADIENT_CACHE_H -#define ANDROID_UI_GRADIENT_CACHE_H +#ifndef ANDROID_HWUI_GRADIENT_CACHE_H +#define ANDROID_HWUI_GRADIENT_CACHE_H #include <SkShader.h> @@ -93,4 +93,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_GRADIENT_CACHE_H +#endif // ANDROID_HWUI_GRADIENT_CACHE_H diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 2afe2fa..a780183 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -14,13 +14,15 @@ * limitations under the License. */ -#ifndef ANDROID_UI_LAYER_H -#define ANDROID_UI_LAYER_H +#ifndef ANDROID_HWUI_LAYER_H +#define ANDROID_HWUI_LAYER_H #include <sys/types.h> #include <GLES2/gl2.h> +#include <ui/Region.h> + #include <SkXfermode.h> #include "Rect.h" @@ -85,9 +87,15 @@ struct Layer { * Height of the layer texture. */ uint32_t height; + + /** + * Dirty region indicating what parts of the layer + * have been drawn. + */ + Region region; }; // struct Layer }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_LAYER_H +#endif // ANDROID_HWUI_LAYER_H diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index ae792ab..4df8ab3 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_LAYER_CACHE_H -#define ANDROID_UI_LAYER_CACHE_H +#ifndef ANDROID_HWUI_LAYER_CACHE_H +#define ANDROID_HWUI_LAYER_CACHE_H #include "Layer.h" #include "utils/SortedList.h" @@ -138,4 +138,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_LAYER_CACHE_H +#endif // ANDROID_HWUI_LAYER_CACHE_H diff --git a/libs/hwui/Line.h b/libs/hwui/Line.h index c529354..5c6f3d8 100644 --- a/libs/hwui/Line.h +++ b/libs/hwui/Line.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_LINE_H -#define ANDROID_UI_LINE_H +#ifndef ANDROID_HWUI_LINE_H +#define ANDROID_HWUI_LINE_H #include <GLES2/gl2.h> @@ -123,4 +123,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_LINE_H +#endif // ANDROID_HWUI_LINE_H diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index 83ea615..7462d5b 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -271,6 +271,19 @@ void Matrix4::mapRect(Rect& r) const { MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]); MUL_ADD_STORE(r.top, data[kScaleY], data[kTranslateY]); MUL_ADD_STORE(r.bottom, data[kScaleY], data[kTranslateY]); + + if (r.left > r.right) { + float x = r.left; + r.left = r.right; + r.right = x; + } + + if (r.top > r.bottom) { + float y = r.top; + r.top = r.bottom; + r.bottom = y; + } + return; } diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index fe81159..d678bd0 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_MATRIX_H -#define ANDROID_UI_MATRIX_H +#ifndef ANDROID_HWUI_MATRIX_H +#define ANDROID_HWUI_MATRIX_H #include <SkMatrix.h> @@ -137,4 +137,4 @@ typedef Matrix4 mat4; }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_MATRIX_H +#endif // ANDROID_HWUI_MATRIX_H diff --git a/libs/hwui/OpenGLDebugRenderer.h b/libs/hwui/OpenGLDebugRenderer.h index ce6a4aa..7787ff1 100644 --- a/libs/hwui/OpenGLDebugRenderer.h +++ b/libs/hwui/OpenGLDebugRenderer.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_OPENGL_DEBUG_RENDERER_H -#define ANDROID_UI_OPENGL_DEBUG_RENDERER_H +#ifndef ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H +#define ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H #include "OpenGLRenderer.h" @@ -66,4 +66,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_OPENGL_DEBUG_RENDERER_H +#endif // ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index b167131..0f0316f 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -26,6 +26,8 @@ #include <utils/Log.h> #include <utils/StopWatch.h> +#include <ui/Rect.h> + #include "OpenGLRenderer.h" namespace android { @@ -285,7 +287,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 == 0xff) { + if (alpha >= 255 - ALPHA_THRESHOLD) { return saveLayer(left, top, right, bottom, NULL, flags); } else { SkPaint paint; @@ -377,7 +379,6 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, bounds.getHeight() > mCaches.maxTextureSize) { snapshot->invisible = true; } else { - // TODO: Should take the mode into account snapshot->invisible = snapshot->previous->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer); } @@ -387,8 +388,7 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, return false; } - glActiveTexture(GL_TEXTURE0); - + glActiveTexture(gTextureUnits[0]); Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight()); if (!layer) { return false; @@ -405,56 +405,7 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, snapshot->layer = layer; if (fboLayer) { - layer->fbo = mCaches.fboCache.get(); - - snapshot->flags |= Snapshot::kFlagIsFboLayer; - snapshot->fbo = layer->fbo; - snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); - snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); - snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); - snapshot->height = bounds.getHeight(); - snapshot->flags |= Snapshot::kFlagDirtyOrtho; - snapshot->orthoMatrix.load(mOrthoMatrix); - - // Bind texture to FBO - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - glBindTexture(GL_TEXTURE_2D, layer->texture); - - // Initialize the texture if needed - if (layer->empty) { - layer->empty = false; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - } - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->texture, 0); - -#if DEBUG_LAYERS - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - LOGE("Framebuffer incomplete (GL error code 0x%x)", status); - - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteTextures(1, &layer->texture); - mCaches.fboCache.put(layer->fbo); - - delete layer; - - return false; - } -#endif - - // Clear the FBO - glScissor(0.0f, 0.0f, bounds.getWidth() + 1.0f, bounds.getHeight() + 1.0f); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - - dirtyClip(); - - // Change the ortho projection - glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + return createFboLayer(layer, bounds, snapshot, previousFbo); } else { // Copy the framebuffer into the layer glBindTexture(GL_TEXTURE_2D, layer->texture); @@ -475,6 +426,82 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, return true; } +bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot, + GLuint previousFbo) { + layer->fbo = 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); + + inverse.mapRect(clip); + clip.snapToPixelBoundaries(); + clip.intersect(bounds); + + snapshot->flags |= Snapshot::kFlagIsFboLayer; + snapshot->fbo = layer->fbo; + snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); + //snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); + 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); + + // Bind texture to FBO + glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); + glBindTexture(GL_TEXTURE_2D, layer->texture); + + // Initialize the texture if needed + if (layer->empty) { + layer->empty = false; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + layer->texture, 0); + +#if DEBUG_LAYERS_AS_REGIONS + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LOGE("Framebuffer incomplete (GL error code 0x%x)", status); + + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + glDeleteTextures(1, &layer->texture); + mCaches.fboCache.put(layer->fbo); + + delete layer; + + return false; + } +#endif + + // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering + glScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f, + clip.getWidth() + 2.0f, clip.getHeight() + 2.0f); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + dirtyClip(); + + // Change the ortho projection + glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); + mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + + return true; +} + /** * Read the documentation of createLayer() before doing anything in this method. */ @@ -484,43 +511,37 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { return; } - const bool fboLayer = current->flags & SkCanvas::kClipToLayer_SaveFlag; + const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer; if (fboLayer) { // Unbind current FBO and restore previous one glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); } - // Restore the clip from the previous snapshot - Rect& clip(*previous->clipRect); - clip.snapToPixelBoundaries(); - glScissor(clip.left, previous->height - clip.bottom, clip.getWidth(), clip.getHeight()); - Layer* layer = current->layer; const Rect& rect = layer->layer; if (!fboLayer && layer->alpha < 255) { drawColorRect(rect.left, rect.top, rect.right, rect.bottom, layer->alpha << 24, SkXfermode::kDstIn_Mode, true); + // Required below, composeLayerRect() will divide by 255 + layer->alpha = 255; } - const Rect& texCoords = layer->texCoords; mCaches.unbindMeshBuffer(); - resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom); glActiveTexture(gTextureUnits[0]); + + // When the layer is stored in an FBO, we can save a bit of fillrate by + // drawing only the dirty region if (fboLayer) { - drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, - layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], - &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount); + dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform); + composeLayerRegion(layer, rect); } else { - drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, - 1.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], - &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, true, true); + dirtyLayer(rect.left, rect.top, rect.right, rect.bottom); + composeLayerRect(layer, rect, true); } - resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); - if (fboLayer) { // Detach the texture from the FBO glBindFramebuffer(GL_FRAMEBUFFER, current->fbo); @@ -541,6 +562,159 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { } } +void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) { + const Rect& texCoords = layer->texCoords; + resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom); + + drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, + layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], + &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap); + + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); +} + +void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { +#if RENDER_LAYERS_AS_REGIONS + if (layer->region.isRect()) { + composeLayerRect(layer, rect); + layer->region.clear(); + return; + } + + if (!layer->region.isEmpty()) { + size_t count; + const android::Rect* rects = layer->region.getArray(&count); + + setupDraw(); + + ProgramDescription description; + description.hasTexture = true; + + const float alpha = layer->alpha / 255.0f; + const bool setColor = description.setColor(alpha, alpha, alpha, alpha); + chooseBlending(layer->blend || layer->alpha < 255, layer->mode, description, false); + + useProgram(mCaches.programCache.get(description)); + + // Texture + bindTexture(layer->texture); + glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0); + + // Always premultiplied + if (setColor) { + mCaches.currentProgram->setColor(alpha, alpha, alpha, alpha); + } + + // Mesh + int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); + glEnableVertexAttribArray(texCoordsSlot); + + mModelView.loadIdentity(); + mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + + const float texX = 1.0f / float(layer->width); + const float texY = 1.0f / float(layer->height); + + TextureVertex* mesh = mCaches.getRegionMesh(); + GLsizei numQuads = 0; + + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, + gMeshStride, &mesh[0].position[0]); + glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, + gMeshStride, &mesh[0].texture[0]); + + for (size_t i = 0; i < count; i++) { + const android::Rect* r = &rects[i]; + + const float u1 = r->left * texX; + const float v1 = (rect.getHeight() - r->top) * texY; + const float u2 = r->right * texX; + const float v2 = (rect.getHeight() - r->bottom) * texY; + + // TODO: Reject quads outside of the clip + TextureVertex::set(mesh++, r->left, r->top, u1, v1); + TextureVertex::set(mesh++, r->right, r->top, u2, v1); + TextureVertex::set(mesh++, r->left, r->bottom, u1, v2); + TextureVertex::set(mesh++, r->right, r->bottom, u2, v2); + + numQuads++; + + if (numQuads >= REGION_MESH_QUAD_COUNT) { + glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL); + numQuads = 0; + mesh = mCaches.getRegionMesh(); + } + } + + if (numQuads > 0) { + glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(texCoordsSlot); + +#if DEBUG_LAYERS_AS_REGIONS + uint32_t colors[] = { + 0x7fff0000, 0x7f00ff00, + 0x7f0000ff, 0x7fff00ff, + }; + + int offset = 0; + int32_t top = rects[0].top; + int i = 0; + + for (size_t i = 0; i < count; i++) { + if (top != rects[i].top) { + offset ^= 0x2; + top = rects[i].top; + } + + Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom); + drawColorRect(r.left, r.top, r.right, r.bottom, colors[offset + (i & 0x1)], + SkXfermode::kSrcOver_Mode); + } +#endif + + layer->region.clear(); + } +#else + composeLayerRect(layer, rect); +#endif +} + +void OpenGLRenderer::dirtyLayer(const float left, const float top, + const float right, const float bottom, const mat4 transform) { +#if RENDER_LAYERS_AS_REGIONS + if ((mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region) { + Rect bounds(left, top, right, bottom); + transform.mapRect(bounds); + bounds.intersect(*mSnapshot->clipRect); + bounds.snapToPixelBoundaries(); + + android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom); + if (!dirty.isEmpty()) { + mSnapshot->region->orSelf(dirty); + } + } +#endif +} + +void OpenGLRenderer::dirtyLayer(const float left, const float top, + const float right, const float bottom) { +#if RENDER_LAYERS_AS_REGIONS + if ((mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region) { + Rect bounds(left, top, right, bottom); + bounds.intersect(*mSnapshot->clipRect); + bounds.snapToPixelBoundaries(); + + android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom); + if (!dirty.isEmpty()) { + mSnapshot->region->orSelf(dirty); + } + } +#endif +} + void OpenGLRenderer::setupDraw() { clearLayerRegions(); if (mDirtyClip) { @@ -551,21 +725,26 @@ void OpenGLRenderer::setupDraw() { void OpenGLRenderer::clearLayerRegions() { if (mLayers.size() == 0 || mSnapshot->invisible) return; + Rect clipRect(*mSnapshot->clipRect); + clipRect.snapToPixelBoundaries(); + for (uint32_t i = 0; i < mLayers.size(); i++) { Rect* bounds = mLayers.itemAt(i); - - // Clear the framebuffer where the layer will draw - glScissor(bounds->left, mSnapshot->height - bounds->bottom, - bounds->getWidth(), bounds->getHeight()); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); + if (clipRect.intersects(*bounds)) { + // Clear the framebuffer where the layer will draw + glScissor(bounds->left, mSnapshot->height - bounds->bottom, + bounds->getWidth(), bounds->getHeight()); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // Restore the clip + dirtyClip(); + } delete bounds; } - mLayers.clear(); - // Restore the clip - dirtyClip(); + mLayers.clear(); } /////////////////////////////////////////////////////////////////////////////// @@ -678,7 +857,12 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* pai if (!texture) return; const AutoTexture autoCleanup(texture); - drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint); + // This could be done in a cheaper way, all we need is pass the matrix + // to the vertex shader. The save/restore is a bit overkill. + save(SkCanvas::kMatrix_SaveFlag); + concatMatrix(matrix); + drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint); + restore(); } void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, @@ -738,11 +922,21 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors); if (mesh) { - // Specify right and bottom as +1.0f from left/top to prevent scaling since the - // patch mesh already defines the final size - drawTextureMesh(left, top, left + 1.0f, top + 1.0f, texture->id, alpha / 255.0f, + // Mark the current layer dirty where we are going to draw the patch + if ((mSnapshot->flags & Snapshot::kFlagFboTarget) && + mSnapshot->region && mesh->hasEmptyQuads) { + const size_t count = mesh->quads.size(); + for (size_t i = 0; i < count; i++) { + Rect bounds = mesh->quads.itemAt(i); + dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, + *mSnapshot->transform); + } + } + + drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset, - GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer); + GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer, + true, !mesh->hasEmptyQuads); } } @@ -801,6 +995,7 @@ void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { mModelView.scale(length, strokeWidth, 1.0f); } mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + // TODO: Add bounds to the layer's region if (mShader) { mShader->updateTransforms(mCaches.currentProgram, mModelView, *mSnapshot); @@ -904,13 +1099,34 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, // Assume that the modelView matrix does not force scales, rotates, etc. const bool linearFilter = mSnapshot->transform->changesBounds(); + + // Dimensions are set to (0,0), the layer (if any) won't be dirtied setupTextureAlpha8(fontRenderer.getTexture(linearFilter), 0, 0, textureUnit, x, y, r, g, b, a, mode, false, true, NULL, NULL); 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 + bool hasLayer = (mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region; +#else + bool hasLayer = false; +#endif mCaches.unbindMeshBuffer(); - fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y); + if (fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y, + hasLayer ? &bounds : NULL)) { +#if RENDER_LAYERS_AS_REGIONS + if (hasLayer) { + mSnapshot->transform->mapRect(bounds); + bounds.intersect(*mSnapshot->clipRect); + bounds.snapToPixelBoundaries(); + + android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom); + mSnapshot->region->orSelf(dirty); + } +#endif + } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); @@ -1081,7 +1297,12 @@ void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t } else { mModelView.loadIdentity(); } + mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + if (width > 0 && height > 0) { + dirtyLayer(x, y, x + width, y + height, *mSnapshot->transform); + } + if (setColor) { mCaches.currentProgram->setColor(r, g, b, a); } @@ -1214,9 +1435,11 @@ void OpenGLRenderer::setupColorRect(float left, float top, float right, float bo mModelView.scale(right - left, bottom - top, 1.0f); if (!ignoreTransform) { mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + dirtyLayer(left, top, right, bottom, *mSnapshot->transform); } else { mat4 identity; mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity); + dirtyLayer(left, top, right, bottom); } mCaches.currentProgram->setColor(r, g, b, a); @@ -1251,7 +1474,7 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom, GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool swapSrcDst, bool ignoreTransform, GLuint vbo) { + bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) { setupDraw(); ProgramDescription description; @@ -1262,16 +1485,20 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b } mModelView.loadTranslate(left, top, 0.0f); - mModelView.scale(right - left, bottom - top, 1.0f); + if (!ignoreScale) { + mModelView.scale(right - left, bottom - top, 1.0f); + } chooseBlending(blend || alpha < 1.0f, mode, description, swapSrcDst); useProgram(mCaches.programCache.get(description)); if (!ignoreTransform) { mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + if (dirty) dirtyLayer(left, top, right, bottom, *mSnapshot->transform); } else { - mat4 m; - mCaches.currentProgram->set(mOrthoMatrix, mModelView, m); + mat4 identity; + mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity); + if (dirty) dirtyLayer(left, top, right, bottom); } // Texture diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 3492d2c..2d612d4 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_OPENGL_RENDERER_H -#define ANDROID_UI_OPENGL_RENDERER_H +#ifndef ANDROID_HWUI_OPENGL_RENDERER_H +#define ANDROID_HWUI_OPENGL_RENDERER_H #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> @@ -50,6 +50,10 @@ namespace uirenderer { // Debug #define DEBUG_OPENGL 1 +// If turned on, layers drawn inside FBOs are optimized with regions +#define RENDER_LAYERS_AS_REGIONS 0 +#define DEBUG_LAYERS_AS_REGIONS 0 + /////////////////////////////////////////////////////////////////////////////// // Renderer /////////////////////////////////////////////////////////////////////////////// @@ -176,6 +180,34 @@ private: int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo); /** + * Creates a new layer stored in the specified snapshot as an FBO. + * + * @param layer The layer to store as an FBO + * @param snapshot The snapshot associated with the new layer + * @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); + + /** + * Compose the specified layer as a region. + * + * @param layer The layer to compose + * @param rect The layer's bounds + */ + void composeLayerRegion(Layer* layer, const Rect& rect); + + /** + * Compose the specified layer as a simple rectangle. + * + * @param layer The layer to compose + * @param rect The layer's bounds + * @param swap If true, the source and destination are swapped + */ + void composeLayerRect(Layer* layer, const Rect& rect, bool swap = false); + + /** * Clears all the regions corresponding to the current list of layers. * This method MUST be invoked before any drawing operation. */ @@ -192,7 +224,7 @@ 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 - * @paran ignoreBlending True if the blending is set by the caller + * @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); @@ -252,11 +284,14 @@ private: * @param swapSrcDst Whether or not the src and dst blending operations should be swapped * @param ignoreTransform True if the current transform should be ignored * @param vbo The VBO used to draw the mesh + * @param ignoreScale True if the model view matrix should not be scaled + * @param dirty True if calling this method should dirty the current layer */ void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, - bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0); + bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0, + bool ignoreScale = false, bool dirty = true); /** * Prepares the renderer to draw the specified shadow. The active texture @@ -411,6 +446,18 @@ private: mDirtyClip = true; } + /** + * Mark the layer as dirty at the specified coordinates. The coordinates + * are transformed with the supplied matrix. + */ + void dirtyLayer(const float left, const float top, const float right, const float bottom, + const mat4 transform); + + /** + * Mark the layer as dirty at the specified coordinates. + */ + void dirtyLayer(const float left, const float top, const float right, const float bottom); + // Dimensions of the drawing surface int mWidth, mHeight; @@ -462,4 +509,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_OPENGL_RENDERER_H +#endif // ANDROID_HWUI_OPENGL_RENDERER_H diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index 3d21431..9b2d476 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -34,6 +34,7 @@ Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQua // 2 triangles per patch, 3 vertices per triangle verticesCount = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3; mVertices = new TextureVertex[verticesCount]; + hasEmptyQuads = emptyQuads > 0; glGenBuffers(1, &meshBuffer); } @@ -51,6 +52,8 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, float left, float top, float right, float bottom, const int32_t* xDivs, const int32_t* yDivs, const uint32_t width, const uint32_t height, const uint32_t colorKey) { + if (hasEmptyQuads) quads.clear(); + const uint32_t xStretchCount = (width + 1) >> 1; const uint32_t yStretchCount = (height + 1) >> 1; @@ -118,7 +121,7 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, mVertices, GL_STATIC_DRAW); } -inline void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2, +void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2, const int32_t xDivs[], uint32_t xCount, float stretchX, float width, float bitmapWidth, uint32_t& quadCount, const uint32_t colorKey) { float previousStepX = 0.0f; @@ -150,12 +153,17 @@ inline void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2, quadCount, colorKey); } -inline void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, +void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, uint32_t& quadCount, const uint32_t colorKey) { if (((colorKey >> quadCount++) & 0x1) == 1) { return; } + if (hasEmptyQuads) { + Rect bounds(x1, y1, x2, y2); + quads.add(bounds); + } + // Left triangle TextureVertex::set(vertex++, x1, y1, u1, v1); TextureVertex::set(vertex++, x2, y1, u2, v1); diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h index ce898937..1e78b2f 100644 --- a/libs/hwui/Patch.h +++ b/libs/hwui/Patch.h @@ -14,13 +14,16 @@ * limitations under the License. */ -#ifndef ANDROID_UI_PATCH_H -#define ANDROID_UI_PATCH_H +#ifndef ANDROID_HWUI_PATCH_H +#define ANDROID_HWUI_PATCH_H #include <sys/types.h> #include <GLES2/gl2.h> +#include <utils/Vector.h> + +#include "Rect.h" #include "Vertex.h" #include "utils/Compare.h" @@ -96,15 +99,17 @@ struct Patch { GLuint meshBuffer; uint32_t verticesCount; + Vector<Rect> quads; + bool hasEmptyQuads; private: TextureVertex* mVertices; - static inline void generateRow(TextureVertex*& vertex, float y1, float y2, + void generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2, const int32_t xDivs[], uint32_t xCount, float stretchX, float width, float bitmapWidth, uint32_t& quadCount, const uint32_t colorKey); - static inline void generateQuad(TextureVertex*& vertex, + void generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, uint32_t& quadCount, const uint32_t colorKey); @@ -113,4 +118,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_PATCH_H +#endif // ANDROID_HWUI_PATCH_H diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h index f4eeb09..deba40d 100644 --- a/libs/hwui/PatchCache.h +++ b/libs/hwui/PatchCache.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_PATCH_CACHE_H -#define ANDROID_UI_PATCH_CACHE_H +#ifndef ANDROID_HWUI_PATCH_CACHE_H +#define ANDROID_HWUI_PATCH_CACHE_H #include <utils/KeyedVector.h> @@ -62,4 +62,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_PATCH_CACHE_H +#endif // ANDROID_HWUI_PATCH_CACHE_H diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 9a5fc45..db5ce08 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_PATH_CACHE_H -#define ANDROID_UI_PATH_CACHE_H +#ifndef ANDROID_HWUI_PATH_CACHE_H +#define ANDROID_HWUI_PATH_CACHE_H #include <SkBitmap.h> #include <SkPaint.h> @@ -170,4 +170,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_PATH_CACHE_H +#endif // ANDROID_HWUI_PATH_CACHE_H diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index 25674c6..baed5fd 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -22,12 +22,6 @@ namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// -// Shaders -/////////////////////////////////////////////////////////////////////////////// - -#define SHADER_SOURCE(name, source) const char* name = #source - -/////////////////////////////////////////////////////////////////////////////// // Base program /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index 026097c..5981662 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_PROGRAM_H -#define ANDROID_UI_PROGRAM_H +#ifndef ANDROID_HWUI_PROGRAM_H +#define ANDROID_HWUI_PROGRAM_H #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> @@ -131,4 +131,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_PROGRAM_H +#endif // ANDROID_HWUI_PROGRAM_H diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 9cb13b3..186e869 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_PROGRAM_CACHE_H -#define ANDROID_UI_PROGRAM_CACHE_H +#ifndef ANDROID_HWUI_PROGRAM_CACHE_H +#define ANDROID_HWUI_PROGRAM_CACHE_H #include <utils/KeyedVector.h> #include <utils/Log.h> @@ -258,4 +258,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_PROGRAM_CACHE_H +#endif // ANDROID_HWUI_PROGRAM_CACHE_H diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index db3cb4d..813392b 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_PROPERTIES_H -#define ANDROID_UI_PROPERTIES_H +#ifndef ANDROID_HWUI_PROPERTIES_H +#define ANDROID_HWUI_PROPERTIES_H #include <cutils/properties.h> @@ -45,15 +45,15 @@ #define MB(s) s * 1024 * 1024 #define DEFAULT_TEXTURE_CACHE_SIZE 20.0f -#define DEFAULT_LAYER_CACHE_SIZE 6.0f +#define DEFAULT_LAYER_CACHE_SIZE 8.0f #define DEFAULT_PATH_CACHE_SIZE 4.0f #define DEFAULT_PATCH_CACHE_SIZE 512 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f -#define DEFAULT_FBO_CACHE_SIZE 25 +#define DEFAULT_FBO_CACHE_SIZE 12 #define DEFAULT_TEXT_GAMMA 1.4f #define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64 #define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192 -#endif // ANDROID_UI_PROPERTIES_H +#endif // ANDROID_HWUI_PROPERTIES_H diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index 8f3655c..71951b7 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -14,8 +14,10 @@ * limitations under the License. */ -#ifndef ANDROID_UI_RECT_H -#define ANDROID_UI_RECT_H +#ifndef ANDROID_HWUI_RECT_H +#define ANDROID_HWUI_RECT_H + +#include <cmath> #include <utils/Log.h> @@ -32,25 +34,35 @@ struct Rect { float right; float bottom; - Rect(): + // Used by Region + typedef float value_type; + + inline Rect(): left(0), top(0), right(0), bottom(0) { } - Rect(float left, float top, float right, float bottom): + inline Rect(float left, float top, float right, float bottom): left(left), top(top), right(right), bottom(bottom) { } - Rect(const Rect& r) { + inline Rect(float width, float height): + left(0.0f), + top(0.0f), + right(width), + bottom(height) { + } + + inline Rect(const Rect& r) { set(r); } - Rect(Rect& r) { + inline Rect(Rect& r) { set(r); } @@ -72,22 +84,26 @@ struct Rect { return memcmp(&a, &b, sizeof(a)); } - bool isEmpty() const { + inline void clear() { + left = top = right = bottom = 0.0f; + } + + inline bool isEmpty() const { return left >= right || top >= bottom; } - void setEmpty() { - memset(this, 0, sizeof(*this)); + inline void setEmpty() { + left = top = right = bottom = 0.0f; } - void set(float left, float top, float right, float bottom) { + inline void set(float left, float top, float right, float bottom) { this->left = left; this->right = right; this->top = top; this->bottom = bottom; } - void set(const Rect& r) { + inline void set(const Rect& r) { set(r.left, r.top, r.right, r.bottom); } @@ -148,6 +164,13 @@ struct Rect { return false; } + void translate(float dx, float dy) { + left += dx; + right += dx; + top += dy; + bottom += dy; + } + void snapToPixelBoundaries() { left = floorf(left + 0.5f); top = floorf(top + 0.5f); @@ -164,4 +187,4 @@ struct Rect { }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_RECT_H +#endif // ANDROID_HWUI_RECT_H diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h index d9b3718..2256fd1 100644 --- a/libs/hwui/ResourceCache.h +++ b/libs/hwui/ResourceCache.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_RESOURCE_CACHE_H -#define ANDROID_UI_RESOURCE_CACHE_H +#ifndef ANDROID_HWUI_RESOURCE_CACHE_H +#define ANDROID_HWUI_RESOURCE_CACHE_H #include <SkBitmap.h> #include <SkiaColorFilter.h> @@ -75,4 +75,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_RESOURCE_CACHE_H +#endif // ANDROID_HWUI_RESOURCE_CACHE_H diff --git a/libs/hwui/SkiaColorFilter.h b/libs/hwui/SkiaColorFilter.h index 17f49f9..bf45e13 100644 --- a/libs/hwui/SkiaColorFilter.h +++ b/libs/hwui/SkiaColorFilter.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_SKIA_COLOR_FILTER_H -#define ANDROID_UI_SKIA_COLOR_FILTER_H +#ifndef ANDROID_HWUI_SKIA_COLOR_FILTER_H +#define ANDROID_HWUI_SKIA_COLOR_FILTER_H #include <GLES2/gl2.h> #include <SkColorFilter.h> @@ -123,4 +123,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_SKIA_COLOR_FILTER_H +#endif // ANDROID_HWUI_SKIA_COLOR_FILTER_H diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index 4cd1b8b..1d884ab 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_SKIA_SHADER_H -#define ANDROID_UI_SKIA_SHADER_H +#ifndef ANDROID_HWUI_SKIA_SHADER_H +#define ANDROID_HWUI_SKIA_SHADER_H #include <SkShader.h> #include <SkXfermode.h> @@ -216,4 +216,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_SKIA_SHADER_H +#endif // ANDROID_HWUI_SKIA_SHADER_H diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 2da1950..9f78063 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -14,16 +14,16 @@ * limitations under the License. */ -#ifndef ANDROID_UI_SNAPSHOT_H -#define ANDROID_UI_SNAPSHOT_H +#ifndef ANDROID_HWUI_SNAPSHOT_H +#define ANDROID_HWUI_SNAPSHOT_H #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <utils/RefBase.h> +#include <ui/Region.h> #include <SkCanvas.h> -#include <SkRegion.h> #include "Layer.h" #include "Matrix.h" @@ -46,6 +46,7 @@ public: Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), invisible(false) { transform = &mTransformRoot; clipRect = &mClipRectRoot; + region = NULL; } /** @@ -75,6 +76,13 @@ public: } else { flags |= Snapshot::kFlagDirtyLocalClip; } + + if (s->flags & Snapshot::kFlagFboTarget) { + flags |= Snapshot::kFlagFboTarget; + region = s->region; + } else { + region = NULL; + } } /** @@ -105,6 +113,11 @@ public: * Indicates that this snapshot has changed the ortho matrix. */ kFlagDirtyOrtho = 0x10, + /** + * Indicates that this snapshot or an ancestor snapshot is + * an FBO layer. + */ + kFlagFboTarget = 0x20 }; /** @@ -243,6 +256,11 @@ public: */ Rect* clipRect; + /** + * The ancestor layer's dirty region.. + */ + Region* region; + private: mat4 mTransformRoot; Rect mClipRectRoot; @@ -253,4 +271,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_SNAPSHOT_H +#endif // ANDROID_HWUI_SNAPSHOT_H diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h index 16e2814..adf09e2 100644 --- a/libs/hwui/TextDropShadowCache.h +++ b/libs/hwui/TextDropShadowCache.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H -#define ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H +#ifndef ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H +#define ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H #include <GLES2/gl2.h> @@ -148,4 +148,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H +#endif // ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index 755074d..4922bb3 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_TEXTURE_H -#define ANDROID_UI_TEXTURE_H +#ifndef ANDROID_HWUI_TEXTURE_H +#define ANDROID_HWUI_TEXTURE_H #include <GLES2/gl2.h> @@ -86,4 +86,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_TEXTURE_H +#endif // ANDROID_HWUI_TEXTURE_H diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index d860953..0c7948f 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -200,9 +200,13 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege texture->blend = !bitmap->isOpaque(); break; case SkBitmap::kIndex8_Config: - uploadPalettedTexture(resize, bitmap, texture->width, texture->height); + uploadLoFiTexture(resize, bitmap, texture->width, texture->height); texture->blend = false; break; + case SkBitmap::kARGB_4444_Config: + uploadLoFiTexture(resize, bitmap, texture->width, texture->height); + texture->blend = true; + break; default: LOGW("Unsupported bitmap config: %d", bitmap->getConfig()); break; @@ -215,7 +219,7 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } -void TextureCache::uploadPalettedTexture(bool resize, SkBitmap* bitmap, +void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height) { SkBitmap rgbaBitmap; rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 6718597..d9d2387 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_TEXTURE_CACHE_H -#define ANDROID_UI_TEXTURE_CACHE_H +#ifndef ANDROID_HWUI_TEXTURE_CACHE_H +#define ANDROID_HWUI_TEXTURE_CACHE_H #include <SkBitmap.h> @@ -93,7 +93,7 @@ private: */ void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false); - void uploadPalettedTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height); + void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height); void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height, GLenum type, const GLvoid * data); @@ -115,4 +115,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_TEXTURE_CACHE_H +#endif // ANDROID_HWUI_TEXTURE_CACHE_H diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index 1f54086..bbf4d4a 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_VERTEX_H -#define ANDROID_UI_VERTEX_H +#ifndef ANDROID_HWUI_VERTEX_H +#define ANDROID_HWUI_VERTEX_H namespace android { namespace uirenderer { @@ -43,4 +43,4 @@ struct TextureVertex { }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_VERTEX_H +#endif // ANDROID_HWUI_VERTEX_H diff --git a/libs/hwui/utils/Compare.h b/libs/hwui/utils/Compare.h index 5ea0fc9..6531e78 100644 --- a/libs/hwui/utils/Compare.h +++ b/libs/hwui/utils/Compare.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_COMPARE_H -#define ANDROID_UI_COMPARE_H +#ifndef ANDROID_HWUI_COMPARE_H +#define ANDROID_HWUI_COMPARE_H #include <cmath> @@ -37,4 +37,4 @@ if (a < rhs.a) return true; \ if (a == rhs.a) -#endif // ANDROID_UI_COMPARE_H +#endif // ANDROID_HWUI_COMPARE_H diff --git a/libs/hwui/utils/GenerationCache.h b/libs/hwui/utils/GenerationCache.h index 5cea30f..2e76236 100644 --- a/libs/hwui/utils/GenerationCache.h +++ b/libs/hwui/utils/GenerationCache.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_GENERATION_CACHE_H -#define ANDROID_UI_GENERATION_CACHE_H +#ifndef ANDROID_HWUI_GENERATION_CACHE_H +#define ANDROID_HWUI_GENERATION_CACHE_H #include <utils/KeyedVector.h> #include <utils/RefBase.h> @@ -242,4 +242,4 @@ void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) { }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_GENERATION_CACHE_H +#endif // ANDROID_HWUI_GENERATION_CACHE_H diff --git a/libs/hwui/utils/SortedList.h b/libs/hwui/utils/SortedList.h index 68f5e9d..2fa890a 100644 --- a/libs/hwui/utils/SortedList.h +++ b/libs/hwui/utils/SortedList.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_SORTED_LIST_H -#define ANDROID_UI_SORTED_LIST_H +#ifndef ANDROID_HWUI_SORTED_LIST_H +#define ANDROID_HWUI_SORTED_LIST_H #include <stdint.h> #include <sys/types.h> @@ -239,4 +239,4 @@ int SortedList<TYPE>::do_compare(const void* lhs, const void* rhs) const { }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_SORTED_LIST_H +#endif // ANDROID_HWUI_SORTED_LIST_H diff --git a/libs/hwui/utils/SortedListImpl.h b/libs/hwui/utils/SortedListImpl.h index 7da09ef..dc385b5 100644 --- a/libs/hwui/utils/SortedListImpl.h +++ b/libs/hwui/utils/SortedListImpl.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_UI_SORTED_LIST_IMPL_H -#define ANDROID_UI_SORTED_LIST_IMPL_H +#ifndef ANDROID_HWUI_SORTED_LIST_IMPL_H +#define ANDROID_HWUI_SORTED_LIST_IMPL_H #include <utils/VectorImpl.h> @@ -62,4 +62,4 @@ private: }; // namespace uirenderer }; // namespace android -#endif // ANDROID_UI_SORTED_LIST_IMPL_H +#endif // ANDROID_HWUI_SORTED_LIST_IMPL_H diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index 12db908..1994f6a 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -289,7 +289,7 @@ private: void flushSpan() { bool merge = false; if (tail-head == ssize_t(span.size())) { - Rect const* p = cur; + Rect const* p = span.editArray(); Rect const* q = head; if (p->top == q->bottom) { merge = true; |